mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-15 15:36:11 +08:00
A dozen changes; new HLEs, AV fixes, ELF loader fixes, new return codes, etc.
This commit is contained in:
@@ -10,6 +10,10 @@ namespace SharpEmu.Libs.CxxAbi;
|
||||
|
||||
public static class CxaGuardExports
|
||||
{
|
||||
private const ulong GuardCompleteValue = 0x0000_0000_0000_0001;
|
||||
private const ulong GuardPendingValue = 0x0000_0000_0000_0100;
|
||||
private const ulong GuardStateMask = 0x0000_0000_0000_FFFF;
|
||||
|
||||
private sealed class GuardState
|
||||
{
|
||||
public int OwnerThreadId { get; set; }
|
||||
@@ -36,13 +40,13 @@ public static class CxaGuardExports
|
||||
var spinner = new SpinWait();
|
||||
while (true)
|
||||
{
|
||||
if (!TryReadGuardInitialized(ctx, guardPtr, out var initialized))
|
||||
if (!TryReadGuardState(ctx, guardPtr, out _, out var initialized, out var inProgress))
|
||||
{
|
||||
ctx[CpuRegister.Rax] = 0;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
|
||||
LogGuardState(ctx, "guard_acquire", guardPtr, initialized);
|
||||
LogGuardState(ctx, "guard_acquire", guardPtr, initialized, inProgress);
|
||||
|
||||
if (initialized)
|
||||
{
|
||||
@@ -58,6 +62,13 @@ public static class CxaGuardExports
|
||||
};
|
||||
if (_inProgress.TryAdd(guardPtr, newState))
|
||||
{
|
||||
if (!TryWriteGuardState(ctx, guardPtr, GuardPendingValue))
|
||||
{
|
||||
_inProgress.TryRemove(guardPtr, out _);
|
||||
ctx[CpuRegister.Rax] = 0;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
|
||||
ctx[CpuRegister.Rax] = 1;
|
||||
LogGuardResult("guard_acquire", guardPtr, result: 1, initialized, inProgress: true, ownerThreadId: currentThreadId);
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
@@ -117,14 +128,14 @@ public static class CxaGuardExports
|
||||
}
|
||||
}
|
||||
|
||||
if (!TryWriteGuardInitialized(ctx, guardPtr, initialized: true))
|
||||
if (!TryWriteGuardState(ctx, guardPtr, GuardCompleteValue))
|
||||
{
|
||||
ctx[CpuRegister.Rax] = 0;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
|
||||
_inProgress.TryRemove(guardPtr, out _);
|
||||
LogGuardState(ctx, "guard_release", guardPtr, initialized: true);
|
||||
LogGuardState(ctx, "guard_release", guardPtr, initialized: true, inProgress: false);
|
||||
|
||||
ctx[CpuRegister.Rax] = 0;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
@@ -152,60 +163,50 @@ public static class CxaGuardExports
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
_ = TryWriteGuardInitialized(ctx, guardPtr, initialized: false);
|
||||
_ = TryWriteGuardState(ctx, guardPtr, 0);
|
||||
_inProgress.TryRemove(guardPtr, out _);
|
||||
LogGuardState(ctx, "guard_abort", guardPtr, initialized: false);
|
||||
LogGuardState(ctx, "guard_abort", guardPtr, initialized: false, inProgress: false);
|
||||
|
||||
ctx[CpuRegister.Rax] = 0;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
private static bool TryReadGuardInitialized(CpuContext ctx, ulong guardPtr, out bool initialized)
|
||||
private static bool TryReadGuardState(CpuContext ctx, ulong guardPtr, out ulong word, out bool initialized, out bool inProgress)
|
||||
{
|
||||
word = 0;
|
||||
initialized = false;
|
||||
|
||||
var aligned = guardPtr & ~7UL;
|
||||
var shift = (int)((guardPtr & 7UL) * 8);
|
||||
|
||||
if (!ctx.TryReadUInt64(aligned, out var word))
|
||||
inProgress = false;
|
||||
if (!ctx.TryReadUInt64(guardPtr, out word))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var b0 = (byte)((word >> shift) & 0xFF);
|
||||
initialized = (b0 & 0x01) != 0;
|
||||
initialized = (word & GuardCompleteValue) != 0;
|
||||
inProgress = (word & 0x0000_0000_0000_FF00) != 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryWriteGuardInitialized(CpuContext ctx, ulong guardPtr, bool initialized)
|
||||
private static bool TryWriteGuardState(CpuContext ctx, ulong guardPtr, ulong stateValue)
|
||||
{
|
||||
var aligned = guardPtr & ~7UL;
|
||||
var shift = (int)((guardPtr & 7UL) * 8);
|
||||
var mask = 0xFFUL << shift;
|
||||
|
||||
if (!ctx.TryReadUInt64(aligned, out var word))
|
||||
if (!ctx.TryReadUInt64(guardPtr, out var word))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var b0 = (byte)((word >> shift) & 0xFF);
|
||||
b0 = initialized ? (byte)(b0 | 0x01) : (byte)(b0 & ~0x01);
|
||||
|
||||
var newWord = (word & ~mask) | ((ulong)b0 << shift);
|
||||
return ctx.TryWriteUInt64(aligned, newWord);
|
||||
var newWord = (word & ~GuardStateMask) | (stateValue & GuardStateMask);
|
||||
return ctx.TryWriteUInt64(guardPtr, newWord);
|
||||
}
|
||||
|
||||
private static void LogGuardState(CpuContext ctx, string op, ulong guardPtr, bool initialized)
|
||||
private static void LogGuardState(CpuContext ctx, string op, ulong guardPtr, bool initialized, bool inProgress)
|
||||
{
|
||||
if (!string.Equals(Environment.GetEnvironmentVariable("SHARPEMU_LOG_GUARDS"), "1", StringComparison.Ordinal))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var aligned = guardPtr & ~7UL;
|
||||
var readable = ctx.TryReadUInt64(aligned, out var word);
|
||||
var readable = ctx.TryReadUInt64(guardPtr, out var word);
|
||||
Console.Error.WriteLine(
|
||||
$"[LOADER][TRACE] {op}: guard=0x{guardPtr:X16} aligned=0x{aligned:X16} init={initialized} word={(readable ? $"0x{word:X16}" : "<unreadable>")}");
|
||||
$"[LOADER][TRACE] {op}: guard=0x{guardPtr:X16} init={initialized} in_progress={inProgress} word={(readable ? $"0x{word:X16}" : "<unreadable>")}");
|
||||
}
|
||||
|
||||
private static void LogGuardResult(string op, ulong guardPtr, int result, bool initialized, bool inProgress, int ownerThreadId)
|
||||
|
||||
@@ -11,6 +11,9 @@ public static class KernelExports
|
||||
private static int _nextFileDescriptor = 2;
|
||||
private static readonly object _cxaGate = new();
|
||||
private static readonly List<CxaDestructorEntry> _cxaDestructors = new();
|
||||
private static readonly object _coredumpGate = new();
|
||||
private static ulong _coredumpHandler;
|
||||
private static ulong _coredumpHandlerContext;
|
||||
|
||||
private readonly record struct CxaDestructorEntry(
|
||||
ulong Function,
|
||||
@@ -28,6 +31,23 @@ public static class KernelExports
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "8zLSfEfW5AU",
|
||||
ExportName = "sceCoredumpRegisterCoredumpHandler",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libSceCoredump")]
|
||||
public static int CoredumpRegisterHandler(CpuContext ctx)
|
||||
{
|
||||
lock (_coredumpGate)
|
||||
{
|
||||
_coredumpHandler = ctx[CpuRegister.Rdi];
|
||||
_coredumpHandlerContext = ctx[CpuRegister.Rsi];
|
||||
}
|
||||
|
||||
ctx[CpuRegister.Rax] = 0;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "uMei1W9uyNo",
|
||||
ExportName = "exit",
|
||||
|
||||
@@ -21,6 +21,19 @@ public static class KernelMemoryCompatExports
|
||||
private const int O_CREAT = 0x0200;
|
||||
private const int O_TRUNC = 0x0400;
|
||||
private const int O_DIRECTORY = 0x00020000;
|
||||
private const int OrbisKernelMapFixed = 0x0010;
|
||||
private const int OrbisKernelMapOpMapDirect = 0;
|
||||
private const int OrbisKernelMapOpUnmap = 1;
|
||||
private const int OrbisKernelMapOpProtect = 2;
|
||||
private const int OrbisKernelMapOpMapFlexible = 3;
|
||||
private const int OrbisKernelMapOpTypeProtect = 4;
|
||||
private const int OrbisKernelBatchMapEntrySize = 32;
|
||||
private const int OrbisKernelBatchMapEntryStartOffset = 0;
|
||||
private const int OrbisKernelBatchMapEntryOffsetOffset = 8;
|
||||
private const int OrbisKernelBatchMapEntryLengthOffset = 16;
|
||||
private const int OrbisKernelBatchMapEntryProtectionOffset = 24;
|
||||
private const int OrbisKernelBatchMapEntryTypeOffset = 25;
|
||||
private const int OrbisKernelBatchMapEntryOperationOffset = 28;
|
||||
private const int SeekSet = 0;
|
||||
private const int SeekCur = 1;
|
||||
private const int SeekEnd = 2;
|
||||
@@ -87,6 +100,7 @@ public static class KernelMemoryCompatExports
|
||||
private readonly record struct DirectAllocation(ulong Start, ulong Length, int MemoryType);
|
||||
private readonly record struct LibcHeapAllocation(nint BaseAddress, nuint Size, nuint Alignment);
|
||||
private readonly record struct MappedRegion(ulong Address, ulong Length, int Protection, bool IsFlexible, ulong DirectStart);
|
||||
private readonly record struct BatchMapEntry(ulong Start, ulong Offset, ulong Length, byte Protection, byte Type, int Operation);
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "8zTFvBIAIN8",
|
||||
@@ -993,17 +1007,11 @@ public static class KernelMemoryCompatExports
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
var candidate = AlignUp(Math.Max(searchStart, _nextPhysicalAddress), alignment);
|
||||
if (candidate >= searchEnd)
|
||||
if (!TryFindAvailableDirectMemorySpanLocked(searchStart, searchEnd, alignment, out var candidate, out var rangeAvailable))
|
||||
{
|
||||
candidate = AlignUp(searchStart, alignment);
|
||||
if (candidate >= searchEnd)
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
|
||||
}
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
var rangeAvailable = searchEnd - candidate;
|
||||
if (!ctx.TryWriteUInt64(outAddress, candidate) || !ctx.TryWriteUInt64(outSize, rangeAvailable))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
@@ -1070,7 +1078,17 @@ public static class KernelMemoryCompatExports
|
||||
var outAddress = ctx[CpuRegister.R9];
|
||||
|
||||
if (length == 0 || outAddress == 0)
|
||||
{
|
||||
TraceDirectMemoryCall(
|
||||
ctx,
|
||||
"allocate_direct",
|
||||
length,
|
||||
alignment,
|
||||
memoryType,
|
||||
outAddress,
|
||||
result: OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
var limit = DirectMemorySizeBytes;
|
||||
ulong searchStart;
|
||||
@@ -1104,41 +1122,48 @@ public static class KernelMemoryCompatExports
|
||||
}
|
||||
|
||||
var align = alignment == 0 ? 0x1000UL : alignment;
|
||||
var alignedStart = AlignUp(searchStart, align);
|
||||
|
||||
ulong selectedAddress;
|
||||
lock (_memoryGate)
|
||||
{
|
||||
selectedAddress = AlignUp(Math.Max(alignedStart, _nextPhysicalAddress), align);
|
||||
|
||||
if (!TryAdd(selectedAddress, length, out var endAddr) ||
|
||||
endAddr > searchEnd ||
|
||||
endAddr > limit)
|
||||
if (!TryAllocateDirectMemoryLocked(searchStart, searchEnd, length, align, memoryType, out selectedAddress))
|
||||
{
|
||||
selectedAddress = alignedStart;
|
||||
|
||||
if (!TryAdd(selectedAddress, length, out endAddr) ||
|
||||
endAddr > searchEnd ||
|
||||
endAddr > limit)
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
|
||||
}
|
||||
TraceDirectMemoryCall(
|
||||
ctx,
|
||||
"allocate_direct",
|
||||
length,
|
||||
align,
|
||||
memoryType,
|
||||
outAddress,
|
||||
result: OrbisGen2Result.ORBIS_GEN2_ERROR_TRY_AGAIN);
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_TRY_AGAIN;
|
||||
}
|
||||
|
||||
_directAllocations[selectedAddress] = new DirectAllocation(selectedAddress, length, memoryType);
|
||||
_nextPhysicalAddress = selectedAddress + length;
|
||||
}
|
||||
|
||||
if (!ctx.TryWriteUInt64(outAddress, selectedAddress))
|
||||
{
|
||||
TraceDirectMemoryCall(
|
||||
ctx,
|
||||
"allocate_direct",
|
||||
length,
|
||||
align,
|
||||
memoryType,
|
||||
outAddress,
|
||||
selectedAddress,
|
||||
OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
|
||||
TraceDirectMemoryCall(
|
||||
ctx,
|
||||
"allocate_direct",
|
||||
length,
|
||||
align,
|
||||
memoryType,
|
||||
outAddress,
|
||||
selectedAddress,
|
||||
OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
|
||||
static bool TryAdd(ulong a, ulong b, out ulong sum)
|
||||
{
|
||||
sum = a + b;
|
||||
return sum >= a;
|
||||
}
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -1154,26 +1179,59 @@ public static class KernelMemoryCompatExports
|
||||
var outAddress = ctx[CpuRegister.Rcx];
|
||||
if (outAddress == 0 || length == 0)
|
||||
{
|
||||
TraceDirectMemoryCall(
|
||||
ctx,
|
||||
"allocate_main_direct",
|
||||
length,
|
||||
alignment,
|
||||
memoryType,
|
||||
outAddress,
|
||||
result: OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
var aligned = AlignUp(_nextPhysicalAddress, alignment == 0 ? 0x1000UL : alignment);
|
||||
var effectiveAlignment = alignment == 0 ? 0x1000UL : alignment;
|
||||
ulong aligned;
|
||||
lock (_memoryGate)
|
||||
{
|
||||
if (aligned + length > DirectMemorySizeBytes)
|
||||
if (!TryAllocateDirectMemoryLocked(0, DirectMemorySizeBytes, length, effectiveAlignment, memoryType, out aligned))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
|
||||
TraceDirectMemoryCall(
|
||||
ctx,
|
||||
"allocate_main_direct",
|
||||
length,
|
||||
effectiveAlignment,
|
||||
memoryType,
|
||||
outAddress,
|
||||
result: OrbisGen2Result.ORBIS_GEN2_ERROR_TRY_AGAIN);
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_TRY_AGAIN;
|
||||
}
|
||||
|
||||
_directAllocations[aligned] = new DirectAllocation(aligned, length, memoryType);
|
||||
_nextPhysicalAddress = aligned + length;
|
||||
}
|
||||
|
||||
if (!ctx.TryWriteUInt64(outAddress, aligned))
|
||||
{
|
||||
TraceDirectMemoryCall(
|
||||
ctx,
|
||||
"allocate_main_direct",
|
||||
length,
|
||||
effectiveAlignment,
|
||||
memoryType,
|
||||
outAddress,
|
||||
aligned,
|
||||
OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
|
||||
TraceDirectMemoryCall(
|
||||
ctx,
|
||||
"allocate_main_direct",
|
||||
length,
|
||||
effectiveAlignment,
|
||||
memoryType,
|
||||
outAddress,
|
||||
aligned,
|
||||
OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
@@ -1199,6 +1257,7 @@ public static class KernelMemoryCompatExports
|
||||
}
|
||||
|
||||
_directAllocations.Remove(start);
|
||||
_nextPhysicalAddress = GetDirectMemoryHighWaterMarkLocked();
|
||||
}
|
||||
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
@@ -1362,6 +1421,26 @@ public static class KernelMemoryCompatExports
|
||||
return KernelMapNamedFlexibleMemory(ctx);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "2SKEx6bSq-4",
|
||||
ExportName = "sceKernelBatchMap",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libKernel")]
|
||||
public static int KernelBatchMap(CpuContext ctx)
|
||||
{
|
||||
return KernelBatchMapCore(ctx, OrbisKernelMapFixed);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "kBJzF8x4SyE",
|
||||
ExportName = "sceKernelBatchMap2",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libKernel")]
|
||||
public static int KernelBatchMap2(CpuContext ctx)
|
||||
{
|
||||
return KernelBatchMapCore(ctx, unchecked((int)ctx[CpuRegister.Rcx]));
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "cQke9UuBQOk",
|
||||
ExportName = "sceKernelMunmap",
|
||||
@@ -1494,12 +1573,37 @@ public static class KernelMemoryCompatExports
|
||||
|
||||
lock (_memoryGate)
|
||||
{
|
||||
if (!_mappedRegions.TryGetValue(address, out var region) || region.Length != length)
|
||||
if (!TryApplyMappedRegionProtectionLocked(address, length, protection))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
_mappedRegions[address] = region with { Protection = protection };
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "9bfdLIyuwCY",
|
||||
ExportName = "sceKernelMtypeprotect",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libKernel")]
|
||||
public static int KernelMtypeprotect(CpuContext ctx)
|
||||
{
|
||||
var address = ctx[CpuRegister.Rdi];
|
||||
var length = ctx[CpuRegister.Rsi];
|
||||
var memoryType = unchecked((int)ctx[CpuRegister.Rdx]);
|
||||
var protection = unchecked((int)ctx[CpuRegister.Rcx]);
|
||||
if (address == 0 || length == 0)
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
lock (_memoryGate)
|
||||
{
|
||||
if (!TryApplyMappedRegionProtectionLocked(address, length, protection, memoryType))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
@@ -2460,6 +2564,364 @@ public static class KernelMemoryCompatExports
|
||||
return TryWriteCompat(ctx, address, bytes);
|
||||
}
|
||||
|
||||
private static int KernelBatchMapCore(CpuContext ctx, int flags)
|
||||
{
|
||||
var entriesAddress = ctx[CpuRegister.Rdi];
|
||||
var entryCount = unchecked((int)ctx[CpuRegister.Rsi]);
|
||||
var processedOutAddress = ctx[CpuRegister.Rdx];
|
||||
var processedCount = 0;
|
||||
var result = (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
|
||||
for (var index = 0; index < entryCount; index++)
|
||||
{
|
||||
var entryAddress = entriesAddress + (ulong)(index * OrbisKernelBatchMapEntrySize);
|
||||
if (!TryReadBatchMapEntry(ctx, entryAddress, out var entry) ||
|
||||
entry.Length == 0 ||
|
||||
entry.Operation < OrbisKernelMapOpMapDirect ||
|
||||
entry.Operation > OrbisKernelMapOpTypeProtect)
|
||||
{
|
||||
result = (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
|
||||
break;
|
||||
}
|
||||
|
||||
result = entry.Operation switch
|
||||
{
|
||||
OrbisKernelMapOpMapDirect => InvokeKernelMemoryOperation(
|
||||
ctx,
|
||||
KernelMapDirectMemory,
|
||||
entryAddress + OrbisKernelBatchMapEntryStartOffset,
|
||||
entry.Length,
|
||||
entry.Protection,
|
||||
unchecked((ulong)(uint)flags),
|
||||
entry.Offset,
|
||||
0),
|
||||
OrbisKernelMapOpUnmap => InvokeKernelMemoryOperation(
|
||||
ctx,
|
||||
KernelMunmap,
|
||||
entry.Start,
|
||||
entry.Length),
|
||||
OrbisKernelMapOpProtect => InvokeKernelMemoryOperation(
|
||||
ctx,
|
||||
KernelMprotect,
|
||||
entry.Start,
|
||||
entry.Length,
|
||||
entry.Protection),
|
||||
OrbisKernelMapOpMapFlexible => InvokeKernelMemoryOperation(
|
||||
ctx,
|
||||
KernelMapNamedFlexibleMemory,
|
||||
entryAddress + OrbisKernelBatchMapEntryStartOffset,
|
||||
entry.Length,
|
||||
entry.Protection,
|
||||
unchecked((ulong)(uint)flags)),
|
||||
OrbisKernelMapOpTypeProtect => InvokeKernelMemoryOperation(
|
||||
ctx,
|
||||
KernelMtypeprotect,
|
||||
entry.Start,
|
||||
entry.Length,
|
||||
entry.Type,
|
||||
entry.Protection),
|
||||
_ => (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT,
|
||||
};
|
||||
|
||||
if (result != (int)OrbisGen2Result.ORBIS_GEN2_OK)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
processedCount++;
|
||||
}
|
||||
|
||||
if (processedOutAddress != 0 && !TryWriteInt32(ctx, processedOutAddress, processedCount))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int InvokeKernelMemoryOperation(
|
||||
CpuContext ctx,
|
||||
Func<CpuContext, int> operation,
|
||||
ulong rdi = 0,
|
||||
ulong rsi = 0,
|
||||
ulong rdx = 0,
|
||||
ulong rcx = 0,
|
||||
ulong r8 = 0,
|
||||
ulong r9 = 0)
|
||||
{
|
||||
var savedRdi = ctx[CpuRegister.Rdi];
|
||||
var savedRsi = ctx[CpuRegister.Rsi];
|
||||
var savedRdx = ctx[CpuRegister.Rdx];
|
||||
var savedRcx = ctx[CpuRegister.Rcx];
|
||||
var savedR8 = ctx[CpuRegister.R8];
|
||||
var savedR9 = ctx[CpuRegister.R9];
|
||||
|
||||
ctx[CpuRegister.Rdi] = rdi;
|
||||
ctx[CpuRegister.Rsi] = rsi;
|
||||
ctx[CpuRegister.Rdx] = rdx;
|
||||
ctx[CpuRegister.Rcx] = rcx;
|
||||
ctx[CpuRegister.R8] = r8;
|
||||
ctx[CpuRegister.R9] = r9;
|
||||
|
||||
try
|
||||
{
|
||||
return operation(ctx);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ctx[CpuRegister.Rdi] = savedRdi;
|
||||
ctx[CpuRegister.Rsi] = savedRsi;
|
||||
ctx[CpuRegister.Rdx] = savedRdx;
|
||||
ctx[CpuRegister.Rcx] = savedRcx;
|
||||
ctx[CpuRegister.R8] = savedR8;
|
||||
ctx[CpuRegister.R9] = savedR9;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryReadBatchMapEntry(CpuContext ctx, ulong entryAddress, out BatchMapEntry entry)
|
||||
{
|
||||
entry = default;
|
||||
if (!ctx.TryReadUInt64(entryAddress + OrbisKernelBatchMapEntryStartOffset, out var start) ||
|
||||
!ctx.TryReadUInt64(entryAddress + OrbisKernelBatchMapEntryOffsetOffset, out var offset) ||
|
||||
!ctx.TryReadUInt64(entryAddress + OrbisKernelBatchMapEntryLengthOffset, out var length))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Span<byte> protection = stackalloc byte[1];
|
||||
Span<byte> memoryType = stackalloc byte[1];
|
||||
if (!TryReadCompat(ctx, entryAddress + OrbisKernelBatchMapEntryProtectionOffset, protection) ||
|
||||
!TryReadCompat(ctx, entryAddress + OrbisKernelBatchMapEntryTypeOffset, memoryType) ||
|
||||
!TryReadUInt32Compat(ctx, entryAddress + OrbisKernelBatchMapEntryOperationOffset, out var operation))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
entry = new BatchMapEntry(start, offset, length, protection[0], memoryType[0], unchecked((int)operation));
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryApplyMappedRegionProtectionLocked(
|
||||
ulong address,
|
||||
ulong length,
|
||||
int protection,
|
||||
int? memoryType = null)
|
||||
{
|
||||
if (!_mappedRegions.TryGetValue(address, out var region) || region.Length != length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_mappedRegions[address] = region with { Protection = protection };
|
||||
|
||||
if (memoryType.HasValue &&
|
||||
region.DirectStart != 0 &&
|
||||
_directAllocations.TryGetValue(region.DirectStart, out var allocation))
|
||||
{
|
||||
_directAllocations[region.DirectStart] = allocation with { MemoryType = memoryType.Value };
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void TraceDirectMemoryCall(
|
||||
CpuContext ctx,
|
||||
string operation,
|
||||
ulong length,
|
||||
ulong alignment,
|
||||
int memoryType,
|
||||
ulong outAddress,
|
||||
ulong selectedAddress = 0,
|
||||
OrbisGen2Result? result = null)
|
||||
{
|
||||
if (!string.Equals(Environment.GetEnvironmentVariable("SHARPEMU_LOG_DIRECT_MEMORY"), "1", StringComparison.Ordinal))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var returnRip = 0UL;
|
||||
var stackPointer = ctx[CpuRegister.Rsp];
|
||||
if (stackPointer != 0)
|
||||
{
|
||||
_ = ctx.TryReadUInt64(stackPointer, out returnRip);
|
||||
}
|
||||
|
||||
Console.Error.WriteLine(
|
||||
$"[LOADER][TRACE] {operation}: ret=0x{returnRip:X16} len=0x{length:X16} align=0x{alignment:X16} type=0x{memoryType:X8} out=0x{outAddress:X16} selected=0x{selectedAddress:X16} result={result?.ToString() ?? "<pending>"}");
|
||||
}
|
||||
|
||||
private static bool TryAllocateDirectMemoryLocked(
|
||||
ulong searchStart,
|
||||
ulong searchEnd,
|
||||
ulong length,
|
||||
ulong alignment,
|
||||
int memoryType,
|
||||
out ulong selectedAddress)
|
||||
{
|
||||
selectedAddress = 0;
|
||||
if (length == 0 || searchStart >= searchEnd)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var effectiveAlignment = alignment == 0 ? 0x1000UL : alignment;
|
||||
if (!TryFindAllocatableDirectMemoryRangeLocked(searchStart, searchEnd, length, effectiveAlignment, out var freePosition) ||
|
||||
!TryAddU64(freePosition, length, out var endAddress))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_directAllocations[freePosition] = new DirectAllocation(freePosition, length, memoryType);
|
||||
_nextPhysicalAddress = endAddress;
|
||||
selectedAddress = freePosition;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryFindAllocatableDirectMemoryRangeLocked(
|
||||
ulong searchStart,
|
||||
ulong searchEnd,
|
||||
ulong length,
|
||||
ulong alignment,
|
||||
out ulong selectedAddress)
|
||||
{
|
||||
selectedAddress = 0;
|
||||
if (length == 0 || searchStart >= searchEnd)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var effectiveEnd = Math.Min(searchEnd, DirectMemorySizeBytes);
|
||||
var candidate = AlignUp(searchStart, alignment);
|
||||
if (candidate >= effectiveEnd)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var allocations = new List<DirectAllocation>(_directAllocations.Values);
|
||||
allocations.Sort(static (left, right) => left.Start.CompareTo(right.Start));
|
||||
|
||||
foreach (var allocation in allocations)
|
||||
{
|
||||
if (!TryAddU64(allocation.Start, allocation.Length, out var allocationEnd))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (allocationEnd <= candidate)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var gapEnd = Math.Min(allocation.Start, effectiveEnd);
|
||||
if (candidate < gapEnd &&
|
||||
TryAddU64(candidate, length, out var candidateEnd) &&
|
||||
candidateEnd <= gapEnd)
|
||||
{
|
||||
selectedAddress = candidate;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (allocation.Start >= effectiveEnd)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
candidate = AlignUp(Math.Max(candidate, allocationEnd), alignment);
|
||||
if (candidate >= effectiveEnd)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!TryAddU64(candidate, length, out var endAddress) || endAddress > effectiveEnd)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
selectedAddress = candidate;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryFindAvailableDirectMemorySpanLocked(
|
||||
ulong searchStart,
|
||||
ulong searchEnd,
|
||||
ulong alignment,
|
||||
out ulong spanStart,
|
||||
out ulong spanLength)
|
||||
{
|
||||
spanStart = 0;
|
||||
spanLength = 0;
|
||||
if (searchStart >= searchEnd)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var effectiveEnd = Math.Min(searchEnd, DirectMemorySizeBytes);
|
||||
var candidate = AlignUp(searchStart, alignment);
|
||||
if (candidate >= effectiveEnd)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var allocations = new List<DirectAllocation>(_directAllocations.Values);
|
||||
allocations.Sort(static (left, right) => left.Start.CompareTo(right.Start));
|
||||
|
||||
foreach (var allocation in allocations)
|
||||
{
|
||||
if (!TryAddU64(allocation.Start, allocation.Length, out var allocationEnd))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (allocationEnd <= candidate)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var gapEnd = Math.Min(allocation.Start, effectiveEnd);
|
||||
if (candidate < gapEnd)
|
||||
{
|
||||
spanStart = candidate;
|
||||
spanLength = gapEnd - candidate;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (allocation.Start >= effectiveEnd)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
candidate = AlignUp(Math.Max(candidate, allocationEnd), alignment);
|
||||
if (candidate >= effectiveEnd)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
spanStart = candidate;
|
||||
spanLength = effectiveEnd - candidate;
|
||||
return spanLength != 0;
|
||||
}
|
||||
|
||||
private static ulong GetDirectMemoryHighWaterMarkLocked()
|
||||
{
|
||||
ulong highWaterMark = 0;
|
||||
foreach (var allocation in _directAllocations.Values)
|
||||
{
|
||||
if (!TryAddU64(allocation.Start, allocation.Length, out var endAddress))
|
||||
{
|
||||
return DirectMemorySizeBytes;
|
||||
}
|
||||
|
||||
if (endAddress > highWaterMark)
|
||||
{
|
||||
highWaterMark = endAddress;
|
||||
}
|
||||
}
|
||||
|
||||
return Math.Min(highWaterMark, DirectMemorySizeBytes);
|
||||
}
|
||||
|
||||
private static bool TryReadHostMemory(ulong address, Span<byte> destination)
|
||||
{
|
||||
if (destination.IsEmpty || !IsHostRangeAccessible(address, (ulong)destination.Length, writeAccess: false))
|
||||
@@ -2808,4 +3270,10 @@ public static class KernelMemoryCompatExports
|
||||
var mask = alignment - 1;
|
||||
return (value + mask) & ~mask;
|
||||
}
|
||||
|
||||
private static bool TryAddU64(ulong left, ulong right, out ulong sum)
|
||||
{
|
||||
sum = left + right;
|
||||
return sum >= left;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ public static class KernelPthreadCompatExports
|
||||
private const int MutexTypeNormal = 4;
|
||||
private const ulong SyntheticMutexHandleBase = 0x00006000_0000_0000;
|
||||
private const ulong SyntheticMutexAttrHandleBase = 0x00006001_0000_0000;
|
||||
private const ulong SyntheticCondHandleBase = 0x00006002_0000_0000;
|
||||
|
||||
private static readonly object _stateGate = new();
|
||||
private static readonly Dictionary<ulong, PthreadMutexState> _mutexStates = new();
|
||||
@@ -22,6 +23,7 @@ public static class KernelPthreadCompatExports
|
||||
private static readonly HashSet<ulong> _condAttrStates = new();
|
||||
private static long _nextSyntheticMutexHandleId = 1;
|
||||
private static long _nextSyntheticMutexAttrHandleId = 1;
|
||||
private static long _nextSyntheticCondHandleId = 1;
|
||||
|
||||
private sealed class PthreadMutexState
|
||||
{
|
||||
@@ -228,14 +230,14 @@ public static class KernelPthreadCompatExports
|
||||
ExportName = "scePthreadCondInit",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libKernel")]
|
||||
public static int PthreadCondInit(CpuContext ctx) => PthreadCondInitCore(ctx[CpuRegister.Rdi]);
|
||||
public static int PthreadCondInit(CpuContext ctx) => PthreadCondInitCore(ctx, ctx[CpuRegister.Rdi]);
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "g+PZd2hiacg",
|
||||
ExportName = "scePthreadCondDestroy",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libKernel")]
|
||||
public static int PthreadCondDestroy(CpuContext ctx) => PthreadCondDestroyCore(ctx[CpuRegister.Rdi]);
|
||||
public static int PthreadCondDestroy(CpuContext ctx) => PthreadCondDestroyCore(ctx, ctx[CpuRegister.Rdi]);
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "WKAXJ4XBPQ4",
|
||||
@@ -370,6 +372,7 @@ public static class KernelPthreadCompatExports
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
_ = ctx.TryWriteUInt64(mutexAddress, 0);
|
||||
state.Semaphore.Dispose();
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
@@ -381,20 +384,10 @@ public static class KernelPthreadCompatExports
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
var resolvedAddress = ResolveMutexHandle(ctx, mutexAddress);
|
||||
PthreadMutexState state;
|
||||
lock (_stateGate)
|
||||
if (!TryResolveMutexState(ctx, mutexAddress, createIfZero: true, out var resolvedAddress, out var state))
|
||||
{
|
||||
if (!_mutexStates.TryGetValue(resolvedAddress, out state!))
|
||||
{
|
||||
state = new PthreadMutexState();
|
||||
_mutexStates[resolvedAddress] = state;
|
||||
}
|
||||
|
||||
if (resolvedAddress != mutexAddress)
|
||||
{
|
||||
_mutexStates[mutexAddress] = state;
|
||||
}
|
||||
TracePthreadMutex(ctx, tryOnly ? "trylock" : "lock", mutexAddress, resolvedAddress, null, KernelPthreadState.GetCurrentThreadHandle(), (int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND);
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
var currentThreadId = KernelPthreadState.GetCurrentThreadHandle();
|
||||
@@ -449,14 +442,7 @@ public static class KernelPthreadCompatExports
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
var resolvedAddress = ResolveMutexHandle(ctx, mutexAddress);
|
||||
PthreadMutexState? state;
|
||||
lock (_stateGate)
|
||||
{
|
||||
_mutexStates.TryGetValue(resolvedAddress, out state);
|
||||
}
|
||||
|
||||
if (state is null)
|
||||
if (!TryResolveMutexState(ctx, mutexAddress, createIfZero: true, out var resolvedAddress, out var state))
|
||||
{
|
||||
TracePthreadMutex(ctx, "unlock", mutexAddress, resolvedAddress, null, KernelPthreadState.GetCurrentThreadHandle(), (int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND);
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
|
||||
@@ -620,6 +606,65 @@ public static class KernelPthreadCompatExports
|
||||
return mutexAddress;
|
||||
}
|
||||
|
||||
private static bool TryResolveMutexState(CpuContext ctx, ulong mutexAddress, bool createIfZero, out ulong resolvedAddress, out PthreadMutexState? state)
|
||||
{
|
||||
resolvedAddress = 0;
|
||||
state = null;
|
||||
if (mutexAddress == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
lock (_stateGate)
|
||||
{
|
||||
if (_mutexStates.TryGetValue(mutexAddress, out state))
|
||||
{
|
||||
resolvedAddress = mutexAddress;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ctx.TryReadUInt64(mutexAddress, out var pointedHandle))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pointedHandle != 0)
|
||||
{
|
||||
lock (_stateGate)
|
||||
{
|
||||
if (_mutexStates.TryGetValue(pointedHandle, out state))
|
||||
{
|
||||
_mutexStates[mutexAddress] = state;
|
||||
resolvedAddress = pointedHandle;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
resolvedAddress = pointedHandle;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!createIfZero)
|
||||
{
|
||||
resolvedAddress = mutexAddress;
|
||||
return false;
|
||||
}
|
||||
|
||||
var createdState = new PthreadMutexState();
|
||||
var syntheticHandle = AllocateSyntheticHandle(SyntheticMutexHandleBase, ref _nextSyntheticMutexHandleId);
|
||||
lock (_stateGate)
|
||||
{
|
||||
_mutexStates[mutexAddress] = createdState;
|
||||
_mutexStates[syntheticHandle] = createdState;
|
||||
}
|
||||
|
||||
_ = ctx.TryWriteUInt64(mutexAddress, syntheticHandle);
|
||||
resolvedAddress = syntheticHandle;
|
||||
state = createdState;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static ulong ResolveMutexAttrHandle(CpuContext ctx, ulong attrAddress)
|
||||
{
|
||||
if (attrAddress == 0)
|
||||
@@ -665,39 +710,137 @@ public static class KernelPthreadCompatExports
|
||||
}
|
||||
}
|
||||
|
||||
private static ulong ResolveCondHandle(CpuContext ctx, ulong condAddress)
|
||||
{
|
||||
if (condAddress == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
lock (_stateGate)
|
||||
{
|
||||
if (_condStates.ContainsKey(condAddress))
|
||||
{
|
||||
return condAddress;
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx.TryReadUInt64(condAddress, out var pointedHandle) && pointedHandle != 0)
|
||||
{
|
||||
lock (_stateGate)
|
||||
{
|
||||
if (_condStates.ContainsKey(pointedHandle))
|
||||
{
|
||||
return pointedHandle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return condAddress;
|
||||
}
|
||||
|
||||
private static bool TryResolveCondState(CpuContext? ctx, ulong condAddress, bool createIfZero, out ulong resolvedAddress, out PthreadCondState? state)
|
||||
{
|
||||
resolvedAddress = 0;
|
||||
state = null;
|
||||
if (condAddress == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
lock (_stateGate)
|
||||
{
|
||||
if (_condStates.TryGetValue(condAddress, out state))
|
||||
{
|
||||
resolvedAddress = condAddress;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx is null || !ctx.TryReadUInt64(condAddress, out var pointedHandle))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pointedHandle != 0)
|
||||
{
|
||||
lock (_stateGate)
|
||||
{
|
||||
if (_condStates.TryGetValue(pointedHandle, out state))
|
||||
{
|
||||
_condStates[condAddress] = state;
|
||||
resolvedAddress = pointedHandle;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
resolvedAddress = pointedHandle;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!createIfZero)
|
||||
{
|
||||
resolvedAddress = condAddress;
|
||||
return false;
|
||||
}
|
||||
|
||||
var createdState = new PthreadCondState();
|
||||
var syntheticHandle = AllocateSyntheticHandle(SyntheticCondHandleBase, ref _nextSyntheticCondHandleId);
|
||||
lock (_stateGate)
|
||||
{
|
||||
_condStates[condAddress] = createdState;
|
||||
_condStates[syntheticHandle] = createdState;
|
||||
}
|
||||
|
||||
_ = ctx.TryWriteUInt64(condAddress, syntheticHandle);
|
||||
resolvedAddress = syntheticHandle;
|
||||
state = createdState;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static ulong AllocateSyntheticHandle(ulong baseAddress, ref long nextId)
|
||||
{
|
||||
var id = unchecked((ulong)Interlocked.Increment(ref nextId));
|
||||
return baseAddress + (id << 4);
|
||||
}
|
||||
|
||||
private static int PthreadCondInitCore(ulong condAddress)
|
||||
private static int PthreadCondInitCore(CpuContext ctx, ulong condAddress)
|
||||
{
|
||||
if (condAddress == 0)
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
var syntheticHandle = AllocateSyntheticHandle(SyntheticCondHandleBase, ref _nextSyntheticCondHandleId);
|
||||
lock (_stateGate)
|
||||
{
|
||||
_condStates[condAddress] = new PthreadCondState();
|
||||
var state = new PthreadCondState();
|
||||
_condStates[condAddress] = state;
|
||||
_condStates[syntheticHandle] = state;
|
||||
}
|
||||
|
||||
_ = ctx.TryWriteUInt64(condAddress, syntheticHandle);
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
private static int PthreadCondDestroyCore(ulong condAddress)
|
||||
private static int PthreadCondDestroyCore(CpuContext ctx, ulong condAddress)
|
||||
{
|
||||
if (condAddress == 0)
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
var resolvedAddress = ResolveCondHandle(ctx, condAddress);
|
||||
lock (_stateGate)
|
||||
{
|
||||
_condStates.Remove(condAddress);
|
||||
_condStates.Remove(resolvedAddress);
|
||||
if (resolvedAddress != condAddress)
|
||||
{
|
||||
_condStates.Remove(condAddress);
|
||||
}
|
||||
}
|
||||
|
||||
_ = ctx.TryWriteUInt64(condAddress, 0);
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
@@ -708,14 +851,9 @@ public static class KernelPthreadCompatExports
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
PthreadCondState state;
|
||||
lock (_stateGate)
|
||||
if (!TryResolveCondState(ctx, condAddress, createIfZero: true, out _, out var state))
|
||||
{
|
||||
if (!_condStates.TryGetValue(condAddress, out state!))
|
||||
{
|
||||
state = new PthreadCondState();
|
||||
_condStates[condAddress] = state;
|
||||
}
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
var waitResult = (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
@@ -770,14 +908,9 @@ public static class KernelPthreadCompatExports
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
PthreadCondState state;
|
||||
lock (_stateGate)
|
||||
if (!TryResolveCondState(null, condAddress, createIfZero: false, out _, out var state))
|
||||
{
|
||||
if (!_condStates.TryGetValue(condAddress, out state!))
|
||||
{
|
||||
state = new PthreadCondState();
|
||||
_condStates[condAddress] = state;
|
||||
}
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
lock (state.SyncRoot)
|
||||
|
||||
@@ -18,6 +18,7 @@ public static class KernelPthreadExtendedCompatExports
|
||||
private const int DefaultInheritSched = 0;
|
||||
private const int DefaultSchedPolicy = 0;
|
||||
private const int DefaultSchedPriority = 0;
|
||||
private const ulong SyntheticRwlockHandleBase = 0x00006003_0000_0000;
|
||||
|
||||
private static readonly object _stateGate = new();
|
||||
private static readonly Dictionary<ulong, ThreadState> _threadStates = new();
|
||||
@@ -25,6 +26,7 @@ public static class KernelPthreadExtendedCompatExports
|
||||
private static readonly Dictionary<ulong, ReaderWriterLockSlim> _rwlockStates = new();
|
||||
private static readonly Dictionary<int, TlsKeyState> _tlsKeys = new();
|
||||
private static int _nextTlsKey = 1;
|
||||
private static long _nextSyntheticRwlockHandleId = 1;
|
||||
|
||||
[ThreadStatic]
|
||||
private static Dictionary<int, ulong>? _threadLocalSpecific;
|
||||
@@ -591,20 +593,32 @@ public static class KernelPthreadExtendedCompatExports
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
var syntheticHandle = AllocateSyntheticHandle(SyntheticRwlockHandleBase, ref _nextSyntheticRwlockHandleId);
|
||||
lock (_stateGate)
|
||||
{
|
||||
if (_rwlockStates.Remove(rwlockAddress, out var existing))
|
||||
var resolvedAddress = ResolveRwlockHandle(ctx, rwlockAddress);
|
||||
if (_rwlockStates.Remove(resolvedAddress, out var existing))
|
||||
{
|
||||
existing.Dispose();
|
||||
}
|
||||
|
||||
_rwlockStates[rwlockAddress] = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
|
||||
var rwlock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
|
||||
_rwlockStates[rwlockAddress] = rwlock;
|
||||
_rwlockStates[syntheticHandle] = rwlock;
|
||||
}
|
||||
|
||||
_ = ctx.TryWriteUInt64(rwlockAddress, syntheticHandle);
|
||||
ctx[CpuRegister.Rax] = 0;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "ytQULN-nhL4",
|
||||
ExportName = "pthread_rwlock_init",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libKernel")]
|
||||
public static int PosixPthreadRwlockInit(CpuContext ctx) => PthreadRwlockInit(ctx);
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "BB+kb08Tl9A",
|
||||
ExportName = "scePthreadRwlockDestroy",
|
||||
@@ -618,10 +632,15 @@ public static class KernelPthreadExtendedCompatExports
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
var resolvedAddress = ResolveRwlockHandle(ctx, rwlockAddress);
|
||||
ReaderWriterLockSlim? state;
|
||||
lock (_stateGate)
|
||||
{
|
||||
_rwlockStates.Remove(rwlockAddress, out state);
|
||||
_rwlockStates.Remove(resolvedAddress, out state);
|
||||
if (resolvedAddress != rwlockAddress)
|
||||
{
|
||||
_rwlockStates.Remove(rwlockAddress);
|
||||
}
|
||||
}
|
||||
|
||||
if (state is null)
|
||||
@@ -629,24 +648,46 @@ public static class KernelPthreadExtendedCompatExports
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
_ = ctx.TryWriteUInt64(rwlockAddress, 0);
|
||||
state.Dispose();
|
||||
ctx[CpuRegister.Rax] = 0;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "1471ajPzxh0",
|
||||
ExportName = "pthread_rwlock_destroy",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libKernel")]
|
||||
public static int PosixPthreadRwlockDestroy(CpuContext ctx) => PthreadRwlockDestroy(ctx);
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "Ox9i0c7L5w0",
|
||||
ExportName = "scePthreadRwlockRdlock",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libKernel")]
|
||||
public static int PthreadRwlockRdlock(CpuContext ctx) => PthreadRwlockLockCore(ctx[CpuRegister.Rdi], write: false);
|
||||
public static int PthreadRwlockRdlock(CpuContext ctx) => PthreadRwlockLockCore(ctx, ctx[CpuRegister.Rdi], write: false);
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "iGjsr1WAtI0",
|
||||
ExportName = "pthread_rwlock_rdlock",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libKernel")]
|
||||
public static int PosixPthreadRwlockRdlock(CpuContext ctx) => PthreadRwlockRdlock(ctx);
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "mqdNorrB+gI",
|
||||
ExportName = "scePthreadRwlockWrlock",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libKernel")]
|
||||
public static int PthreadRwlockWrlock(CpuContext ctx) => PthreadRwlockLockCore(ctx[CpuRegister.Rdi], write: true);
|
||||
public static int PthreadRwlockWrlock(CpuContext ctx) => PthreadRwlockLockCore(ctx, ctx[CpuRegister.Rdi], write: true);
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "sIlRvQqsN2Y",
|
||||
ExportName = "pthread_rwlock_wrlock",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libKernel")]
|
||||
public static int PosixPthreadRwlockWrlock(CpuContext ctx) => PthreadRwlockWrlock(ctx);
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "+L98PIbGttk",
|
||||
@@ -661,13 +702,7 @@ public static class KernelPthreadExtendedCompatExports
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
ReaderWriterLockSlim? rwlock;
|
||||
lock (_stateGate)
|
||||
{
|
||||
_rwlockStates.TryGetValue(rwlockAddress, out rwlock);
|
||||
}
|
||||
|
||||
if (rwlock is null)
|
||||
if (!TryResolveRwlockState(ctx, rwlockAddress, createIfZero: true, out _, out var rwlock))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
|
||||
}
|
||||
@@ -695,6 +730,13 @@ public static class KernelPthreadExtendedCompatExports
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "EgmLo6EWgso",
|
||||
ExportName = "pthread_rwlock_unlock",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libKernel")]
|
||||
public static int PosixPthreadRwlockUnlock(CpuContext ctx) => PthreadRwlockUnlock(ctx);
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "mqULNdimTn0",
|
||||
ExportName = "pthread_key_create",
|
||||
@@ -730,6 +772,13 @@ public static class KernelPthreadExtendedCompatExports
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "geDaqgH9lTg",
|
||||
ExportName = "scePthreadKeyCreate",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libKernel")]
|
||||
public static int OrbisPthreadKeyCreate(CpuContext ctx) => PosixPthreadKeyCreate(ctx);
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "6BpEZuDT7YI",
|
||||
ExportName = "pthread_key_delete",
|
||||
@@ -751,6 +800,13 @@ public static class KernelPthreadExtendedCompatExports
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "PrdHuuDekhY",
|
||||
ExportName = "scePthreadKeyDelete",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libKernel")]
|
||||
public static int OrbisPthreadKeyDelete(CpuContext ctx) => PosixPthreadKeyDelete(ctx);
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "WrOLvHU0yQM",
|
||||
ExportName = "pthread_setspecific",
|
||||
@@ -774,6 +830,13 @@ public static class KernelPthreadExtendedCompatExports
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "+BzXYkqYeLE",
|
||||
ExportName = "scePthreadSetspecific",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libKernel")]
|
||||
public static int OrbisPthreadSetspecific(CpuContext ctx) => PosixPthreadSetspecific(ctx);
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "0-KXaS70xy4",
|
||||
ExportName = "pthread_getspecific",
|
||||
@@ -798,21 +861,23 @@ public static class KernelPthreadExtendedCompatExports
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
private static int PthreadRwlockLockCore(ulong rwlockAddress, bool write)
|
||||
[SysAbiExport(
|
||||
Nid = "eoht7mQOCmo",
|
||||
ExportName = "scePthreadGetspecific",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libKernel")]
|
||||
public static int OrbisPthreadGetspecific(CpuContext ctx) => PosixPthreadGetspecific(ctx);
|
||||
|
||||
private static int PthreadRwlockLockCore(CpuContext ctx, ulong rwlockAddress, bool write)
|
||||
{
|
||||
if (rwlockAddress == 0)
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
ReaderWriterLockSlim rwlock;
|
||||
lock (_stateGate)
|
||||
if (!TryResolveRwlockState(ctx, rwlockAddress, createIfZero: true, out _, out var rwlock))
|
||||
{
|
||||
if (!_rwlockStates.TryGetValue(rwlockAddress, out rwlock!))
|
||||
{
|
||||
rwlock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
|
||||
_rwlockStates[rwlockAddress] = rwlock;
|
||||
}
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
try
|
||||
@@ -834,6 +899,100 @@ public static class KernelPthreadExtendedCompatExports
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
private static ulong ResolveRwlockHandle(CpuContext ctx, ulong rwlockAddress)
|
||||
{
|
||||
if (rwlockAddress == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
lock (_stateGate)
|
||||
{
|
||||
if (_rwlockStates.ContainsKey(rwlockAddress))
|
||||
{
|
||||
return rwlockAddress;
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx.TryReadUInt64(rwlockAddress, out var pointedHandle) && pointedHandle != 0)
|
||||
{
|
||||
lock (_stateGate)
|
||||
{
|
||||
if (_rwlockStates.ContainsKey(pointedHandle))
|
||||
{
|
||||
return pointedHandle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rwlockAddress;
|
||||
}
|
||||
|
||||
private static bool TryResolveRwlockState(CpuContext ctx, ulong rwlockAddress, bool createIfZero, out ulong resolvedAddress, out ReaderWriterLockSlim? rwlock)
|
||||
{
|
||||
resolvedAddress = 0;
|
||||
rwlock = null;
|
||||
if (rwlockAddress == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
lock (_stateGate)
|
||||
{
|
||||
if (_rwlockStates.TryGetValue(rwlockAddress, out rwlock))
|
||||
{
|
||||
resolvedAddress = rwlockAddress;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ctx.TryReadUInt64(rwlockAddress, out var pointedHandle))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pointedHandle != 0)
|
||||
{
|
||||
lock (_stateGate)
|
||||
{
|
||||
if (_rwlockStates.TryGetValue(pointedHandle, out rwlock))
|
||||
{
|
||||
_rwlockStates[rwlockAddress] = rwlock;
|
||||
resolvedAddress = pointedHandle;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
resolvedAddress = pointedHandle;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!createIfZero)
|
||||
{
|
||||
resolvedAddress = rwlockAddress;
|
||||
return false;
|
||||
}
|
||||
|
||||
var createdRwlock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
|
||||
var syntheticHandle = AllocateSyntheticHandle(SyntheticRwlockHandleBase, ref _nextSyntheticRwlockHandleId);
|
||||
lock (_stateGate)
|
||||
{
|
||||
_rwlockStates[rwlockAddress] = createdRwlock;
|
||||
_rwlockStates[syntheticHandle] = createdRwlock;
|
||||
}
|
||||
|
||||
_ = ctx.TryWriteUInt64(rwlockAddress, syntheticHandle);
|
||||
resolvedAddress = syntheticHandle;
|
||||
rwlock = createdRwlock;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static ulong AllocateSyntheticHandle(ulong baseAddress, ref long nextId)
|
||||
{
|
||||
var id = unchecked((ulong)Interlocked.Increment(ref nextId));
|
||||
return baseAddress + (id << 4);
|
||||
}
|
||||
|
||||
private static ThreadState GetOrCreateThreadStateLocked(ulong thread)
|
||||
{
|
||||
if (_threadStates.TryGetValue(thread, out var state))
|
||||
|
||||
@@ -39,7 +39,10 @@ public static class KernelRuntimeCompatExports
|
||||
private static readonly RdtscDelegate? _rdtscReader = CreateRdtscReader();
|
||||
private static readonly ulong _kernelTscFrequency = ResolveKernelTscFrequency();
|
||||
private static readonly ulong _stackChkGuardValue = 0xC0DEC0DECAFEBABEUL;
|
||||
private static readonly nint _stackChkGuardObjectAddress = AllocateStackChkGuardObject();
|
||||
private static readonly nint _stackChkGuardObjectAddress =
|
||||
HleDataSymbols.TryGetAddress("f7uOxY9mM1U", out var stackChkGuardAddress)
|
||||
? unchecked((nint)stackChkGuardAddress)
|
||||
: AllocateStackChkGuardObject();
|
||||
private static ulong _applicationHeapApiAddress;
|
||||
private static ulong _processProcParamAddress;
|
||||
private static ulong _nextReservedVirtualBase = 0x6000_0000_0UL;
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
// Copyright (C) 2026 SharpEmu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using SharpEmu.HLE;
|
||||
|
||||
namespace SharpEmu.Libs.LibcInternal;
|
||||
|
||||
public static class LibcInternalExports
|
||||
{
|
||||
private const ulong HeapTraceInfoSize = 32;
|
||||
private const int HeapTraceTableEntryCount = 64;
|
||||
private const int HeapTraceMaskOffset = 0;
|
||||
private const int HeapTraceTableOffset = HeapTraceMaskOffset + sizeof(ulong);
|
||||
private const int HeapTraceStorageSize = HeapTraceTableOffset + (HeapTraceTableEntryCount * sizeof(ulong));
|
||||
|
||||
private static readonly object _heapTraceGate = new();
|
||||
private static nint _heapTraceStorage;
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "NWtTN10cJzE",
|
||||
ExportName = "LibcHeapGetTraceInfo",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "LibcInternalExt")]
|
||||
public static int LibcHeapGetTraceInfo(CpuContext ctx)
|
||||
{
|
||||
var infoAddress = ctx[CpuRegister.Rdi];
|
||||
if (infoAddress == 0 || !ctx.TryReadUInt64(infoAddress, out var size) || size != HeapTraceInfoSize)
|
||||
{
|
||||
ctx[CpuRegister.Rax] = 0;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
var storage = EnsureHeapTraceStorage();
|
||||
if (storage == 0)
|
||||
{
|
||||
ctx[CpuRegister.Rax] = 0;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
|
||||
var maskAddress = unchecked((ulong)(storage + HeapTraceMaskOffset));
|
||||
var tableAddress = unchecked((ulong)(storage + HeapTraceTableOffset));
|
||||
if (!ctx.TryWriteUInt64(infoAddress + 16, maskAddress) ||
|
||||
!ctx.TryWriteUInt64(infoAddress + 24, tableAddress))
|
||||
{
|
||||
ctx[CpuRegister.Rax] = 0;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
|
||||
ctx[CpuRegister.Rax] = 0;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
private static nint EnsureHeapTraceStorage()
|
||||
{
|
||||
lock (_heapTraceGate)
|
||||
{
|
||||
if (_heapTraceStorage != 0)
|
||||
{
|
||||
return _heapTraceStorage;
|
||||
}
|
||||
|
||||
var storage = Marshal.AllocHGlobal(HeapTraceStorageSize);
|
||||
if (storage == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsafe
|
||||
{
|
||||
NativeMemory.Clear((void*)storage, (nuint)HeapTraceStorageSize);
|
||||
}
|
||||
|
||||
_heapTraceStorage = storage;
|
||||
return storage;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
// Copyright (C) 2026 SharpEmu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
using SharpEmu.HLE;
|
||||
using System.Buffers.Binary;
|
||||
|
||||
namespace SharpEmu.Libs.Rtc;
|
||||
|
||||
public static class RtcExports
|
||||
{
|
||||
[SysAbiExport(
|
||||
Nid = "ZPD1YOKI+Kw",
|
||||
ExportName = "sceRtcGetCurrentClockLocalTime",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libSceRtc")]
|
||||
public static int RtcGetCurrentClockLocalTime(CpuContext ctx)
|
||||
{
|
||||
var timeAddress = ctx[CpuRegister.Rdi];
|
||||
if (timeAddress == 0)
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
var now = DateTimeOffset.Now;
|
||||
Span<byte> rtcDateTime = stackalloc byte[16];
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(rtcDateTime[0..2], checked((ushort)now.Year));
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(rtcDateTime[2..4], checked((ushort)now.Month));
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(rtcDateTime[4..6], checked((ushort)now.Day));
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(rtcDateTime[6..8], checked((ushort)now.Hour));
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(rtcDateTime[8..10], checked((ushort)now.Minute));
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(rtcDateTime[10..12], checked((ushort)now.Second));
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(
|
||||
rtcDateTime[12..16],
|
||||
checked((uint)((now.Ticks % TimeSpan.TicksPerSecond) / 10)));
|
||||
|
||||
if (!ctx.Memory.TryWrite(timeAddress, rtcDateTime))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
|
||||
ctx[CpuRegister.Rax] = 0;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "8w-H19ip48I",
|
||||
ExportName = "sceRtcGetTick",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libSceRtc")]
|
||||
public static int RtcGetTick(CpuContext ctx)
|
||||
{
|
||||
var dateTimeAddress = ctx[CpuRegister.Rdi];
|
||||
var tickAddress = ctx[CpuRegister.Rsi];
|
||||
if (dateTimeAddress == 0 || tickAddress == 0)
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (!TryReadRtcDateTime(ctx, dateTimeAddress, out var rtcDateTime))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
|
||||
ulong tickValue;
|
||||
try
|
||||
{
|
||||
var baseDateTime = new DateTime(
|
||||
rtcDateTime.Year,
|
||||
rtcDateTime.Month,
|
||||
rtcDateTime.Day,
|
||||
rtcDateTime.Hour,
|
||||
rtcDateTime.Minute,
|
||||
rtcDateTime.Second,
|
||||
DateTimeKind.Utc);
|
||||
tickValue = checked((ulong)((baseDateTime.Ticks / 10) + rtcDateTime.Microsecond));
|
||||
}
|
||||
catch (ArgumentOutOfRangeException)
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (!ctx.TryWriteUInt64(tickAddress, tickValue))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
|
||||
ctx[CpuRegister.Rax] = 0;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
private static bool TryReadRtcDateTime(CpuContext ctx, ulong address, out RtcDateTime rtcDateTime)
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[16];
|
||||
if (!ctx.Memory.TryRead(address, buffer))
|
||||
{
|
||||
rtcDateTime = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
rtcDateTime = new RtcDateTime(
|
||||
BinaryPrimitives.ReadUInt16LittleEndian(buffer[0..2]),
|
||||
BinaryPrimitives.ReadUInt16LittleEndian(buffer[2..4]),
|
||||
BinaryPrimitives.ReadUInt16LittleEndian(buffer[4..6]),
|
||||
BinaryPrimitives.ReadUInt16LittleEndian(buffer[6..8]),
|
||||
BinaryPrimitives.ReadUInt16LittleEndian(buffer[8..10]),
|
||||
BinaryPrimitives.ReadUInt16LittleEndian(buffer[10..12]),
|
||||
BinaryPrimitives.ReadUInt32LittleEndian(buffer[12..16]));
|
||||
return true;
|
||||
}
|
||||
|
||||
private readonly record struct RtcDateTime(
|
||||
ushort Year,
|
||||
ushort Month,
|
||||
ushort Day,
|
||||
ushort Hour,
|
||||
ushort Minute,
|
||||
ushort Second,
|
||||
uint Microsecond);
|
||||
}
|
||||
Reference in New Issue
Block a user