mirror of
https://github.com/par274/sharpemu.git
synced 2026-08-02 16:09:47 +08:00
[libs] Update AGC and fiber exports
This commit is contained in:
@@ -30,6 +30,7 @@ public static class AgcExports
|
|||||||
private const uint RUcRegsIndirect = 0x13;
|
private const uint RUcRegsIndirect = 0x13;
|
||||||
private const uint RAcquireMem = 0x14;
|
private const uint RAcquireMem = 0x14;
|
||||||
private const uint RFlip = 0x17;
|
private const uint RFlip = 0x17;
|
||||||
|
private const uint RReleaseMem = 0x18;
|
||||||
private const uint SpiShaderPgmLoPs = 0x8;
|
private const uint SpiShaderPgmLoPs = 0x8;
|
||||||
private const uint SpiShaderPgmHiPs = 0x9;
|
private const uint SpiShaderPgmHiPs = 0x9;
|
||||||
private const uint SpiShaderPgmLoEs = 0xC8;
|
private const uint SpiShaderPgmLoEs = 0xC8;
|
||||||
@@ -47,8 +48,11 @@ public static class AgcExports
|
|||||||
private const uint Gen5TextureType2D = 9;
|
private const uint Gen5TextureType2D = 9;
|
||||||
private const ulong VideoOutPixelFormatA8R8G8B8Srgb = 0x80000000;
|
private const ulong VideoOutPixelFormatA8R8G8B8Srgb = 0x80000000;
|
||||||
private const ulong VideoOutPixelFormatA8B8G8R8Srgb = 0x80002200;
|
private const ulong VideoOutPixelFormatA8B8G8R8Srgb = 0x80002200;
|
||||||
|
private const ulong VideoOutPixelFormatB8G8R8A8Unorm = 0x8100000000000000;
|
||||||
|
private const ulong VideoOutPixelFormatR8G8B8A8Unorm = 0x8100000022000000;
|
||||||
private const uint RegisterDefaultsVersion7 = 7;
|
private const uint RegisterDefaultsVersion7 = 7;
|
||||||
private const uint RegisterDefaultsVersion8 = 8;
|
private const uint RegisterDefaultsVersion8 = 8;
|
||||||
|
private const uint RegisterDefaultsVersion10 = 10;
|
||||||
private const int RegisterDefaultsSize = 0x40;
|
private const int RegisterDefaultsSize = 0x40;
|
||||||
private const int RegisterDefaultBlockSize = 16 * 8;
|
private const int RegisterDefaultBlockSize = 16 * 8;
|
||||||
|
|
||||||
@@ -497,6 +501,64 @@ public static class AgcExports
|
|||||||
return ReturnPointer(ctx, commandAddress);
|
return ReturnPointer(ctx, commandAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[SysAbiExport(
|
||||||
|
Nid = "wr23dPKyWc0",
|
||||||
|
ExportName = "sceAgcCbReleaseMem",
|
||||||
|
Target = Generation.Gen5,
|
||||||
|
LibraryName = "libSceAgc")]
|
||||||
|
public static int CbReleaseMem(CpuContext ctx)
|
||||||
|
{
|
||||||
|
var commandBufferAddress = ctx[CpuRegister.Rdi];
|
||||||
|
var action = (uint)(ctx[CpuRegister.Rsi] & 0xFF);
|
||||||
|
var gcrControl = (uint)(ctx[CpuRegister.Rdx] & 0xFFFF);
|
||||||
|
var destination = (uint)(ctx[CpuRegister.Rcx] & 0xFF);
|
||||||
|
var cachePolicy = (uint)(ctx[CpuRegister.R8] & 0xFF);
|
||||||
|
var destinationAddress = ctx[CpuRegister.R9];
|
||||||
|
var stackAddress = ctx[CpuRegister.Rsp];
|
||||||
|
if (!TryReadUInt64(ctx, stackAddress + 8, out var dataSelectionRaw) ||
|
||||||
|
!TryReadUInt64(ctx, stackAddress + 16, out var data) ||
|
||||||
|
!TryReadUInt64(ctx, stackAddress + 24, out var gdsOffsetRaw) ||
|
||||||
|
!TryReadUInt64(ctx, stackAddress + 32, out var gdsSizeRaw) ||
|
||||||
|
!TryReadUInt64(ctx, stackAddress + 40, out var interruptRaw) ||
|
||||||
|
!TryReadUInt64(ctx, stackAddress + 48, out var interruptContextIdRaw))
|
||||||
|
{
|
||||||
|
return ReturnPointer(ctx, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
var dataSelection = (uint)(dataSelectionRaw & 0xFF);
|
||||||
|
var gdsOffset = (uint)(gdsOffsetRaw & 0xFFFF);
|
||||||
|
var gdsSize = (uint)(gdsSizeRaw & 0xFFFF);
|
||||||
|
var interrupt = (uint)(interruptRaw & 0xFF);
|
||||||
|
var interruptContextId = (uint)interruptContextIdRaw;
|
||||||
|
if (commandBufferAddress == 0 ||
|
||||||
|
destination != 1 ||
|
||||||
|
dataSelection is not (2 or 3) ||
|
||||||
|
gdsOffset != 0 ||
|
||||||
|
gdsSize != 1 ||
|
||||||
|
interrupt != 0 ||
|
||||||
|
interruptContextId != 0)
|
||||||
|
{
|
||||||
|
return ReturnPointer(ctx, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TryAllocateCommandDwords(ctx, commandBufferAddress, 7, out var commandAddress) ||
|
||||||
|
!TryWriteUInt32(ctx, commandAddress, Pm4(7, ItNop, RReleaseMem)) ||
|
||||||
|
!TryWriteUInt32(ctx, commandAddress + 4, action | (cachePolicy << 8)) ||
|
||||||
|
!TryWriteUInt32(ctx, commandAddress + 8, gcrControl | (dataSelection << 16)) ||
|
||||||
|
!TryWriteUInt32(ctx, commandAddress + 12, (uint)destinationAddress) ||
|
||||||
|
!TryWriteUInt32(ctx, commandAddress + 16, (uint)(destinationAddress >> 32)) ||
|
||||||
|
!TryWriteUInt32(ctx, commandAddress + 20, (uint)data) ||
|
||||||
|
!TryWriteUInt32(ctx, commandAddress + 24, (uint)(data >> 32)))
|
||||||
|
{
|
||||||
|
return ReturnPointer(ctx, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TraceAgc(
|
||||||
|
$"agc.cb_release_mem buf=0x{commandBufferAddress:X16} cmd=0x{commandAddress:X16} " +
|
||||||
|
$"action=0x{action:X2} gcr=0x{gcrControl:X4} dst=0x{destinationAddress:X16} data_sel={dataSelection} data=0x{data:X16}");
|
||||||
|
return ReturnPointer(ctx, commandAddress);
|
||||||
|
}
|
||||||
|
|
||||||
[SysAbiExport(
|
[SysAbiExport(
|
||||||
Nid = "TRO721eVt4g",
|
Nid = "TRO721eVt4g",
|
||||||
ExportName = "sceAgcDcbResetQueue",
|
ExportName = "sceAgcDcbResetQueue",
|
||||||
@@ -825,6 +887,50 @@ public static class AgcExports
|
|||||||
return ReturnPointer(ctx, commandAddress);
|
return ReturnPointer(ctx, commandAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[SysAbiExport(
|
||||||
|
Nid = "w2rJhmD+dsE",
|
||||||
|
ExportName = "sceAgcDriverAddEqEvent",
|
||||||
|
Target = Generation.Gen5,
|
||||||
|
LibraryName = "libSceAgcDriver")]
|
||||||
|
public static int DriverAddEqEvent(CpuContext ctx)
|
||||||
|
{
|
||||||
|
var equeue = ctx[CpuRegister.Rdi];
|
||||||
|
var eventId = ctx[CpuRegister.Rsi];
|
||||||
|
var userData = ctx[CpuRegister.Rdx];
|
||||||
|
if (!KernelEventQueueCompatExports.RegisterEvent(
|
||||||
|
equeue,
|
||||||
|
eventId,
|
||||||
|
KernelEventQueueCompatExports.KernelEventFilterGraphics,
|
||||||
|
userData))
|
||||||
|
{
|
||||||
|
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
TraceAgc($"agc.driver_add_eq_event eq=0x{equeue:X16} id=0x{eventId:X16} udata=0x{userData:X16}");
|
||||||
|
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
[SysAbiExport(
|
||||||
|
Nid = "DL2RXaXOy88",
|
||||||
|
ExportName = "sceAgcDriverDeleteEqEvent",
|
||||||
|
Target = Generation.Gen5,
|
||||||
|
LibraryName = "libSceAgcDriver")]
|
||||||
|
public static int DriverDeleteEqEvent(CpuContext ctx)
|
||||||
|
{
|
||||||
|
var equeue = ctx[CpuRegister.Rdi];
|
||||||
|
var eventId = ctx[CpuRegister.Rsi];
|
||||||
|
if (!KernelEventQueueCompatExports.DeleteRegisteredEvent(
|
||||||
|
equeue,
|
||||||
|
eventId,
|
||||||
|
KernelEventQueueCompatExports.KernelEventFilterGraphics))
|
||||||
|
{
|
||||||
|
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
TraceAgc($"agc.driver_delete_eq_event eq=0x{equeue:X16} id=0x{eventId:X16}");
|
||||||
|
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK);
|
||||||
|
}
|
||||||
|
|
||||||
[SysAbiExport(
|
[SysAbiExport(
|
||||||
Nid = "UglJIZjGssM",
|
Nid = "UglJIZjGssM",
|
||||||
ExportName = "sceAgcDriverSubmitDcb",
|
ExportName = "sceAgcDriverSubmitDcb",
|
||||||
@@ -871,6 +977,20 @@ public static class AgcExports
|
|||||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[SysAbiExport(
|
||||||
|
Nid = "qj7QZpgr9Uw",
|
||||||
|
ExportName = "sceAgcUnknownQj7QZpgr9Uw",
|
||||||
|
Target = Generation.Gen5,
|
||||||
|
LibraryName = "libSceAgc")]
|
||||||
|
public static int UnknownQj7QZpgr9Uw(CpuContext ctx)
|
||||||
|
{
|
||||||
|
TraceAgc(
|
||||||
|
$"agc.unknown_qj7 rdi=0x{ctx[CpuRegister.Rdi]:X16} rsi=0x{ctx[CpuRegister.Rsi]:X16} " +
|
||||||
|
$"rdx=0x{ctx[CpuRegister.Rdx]:X16} rcx=0x{ctx[CpuRegister.Rcx]:X16}");
|
||||||
|
ctx[CpuRegister.Rax] = 0;
|
||||||
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||||
|
}
|
||||||
|
|
||||||
private static void ParseSubmittedDcb(CpuContext ctx, ulong commandAddress, uint dwordCount, bool tracePackets)
|
private static void ParseSubmittedDcb(CpuContext ctx, ulong commandAddress, uint dwordCount, bool tracePackets)
|
||||||
{
|
{
|
||||||
if (commandAddress == 0 || dwordCount == 0 || dwordCount > 1_000_000)
|
if (commandAddress == 0 || dwordCount == 0 || dwordCount > 1_000_000)
|
||||||
@@ -912,6 +1032,26 @@ public static class AgcExports
|
|||||||
|
|
||||||
ApplySubmittedRegisters(ctx, state, currentAddress, length, op, register);
|
ApplySubmittedRegisters(ctx, state, currentAddress, length, op, register);
|
||||||
|
|
||||||
|
if (op == ItEventWrite &&
|
||||||
|
length >= 2 &&
|
||||||
|
TryReadUInt32(ctx, currentAddress + sizeof(uint), out var eventTypeRaw))
|
||||||
|
{
|
||||||
|
var eventType = eventTypeRaw & 0x3Fu;
|
||||||
|
var triggered = KernelEventQueueCompatExports.TriggerRegisteredEvents(
|
||||||
|
eventType,
|
||||||
|
KernelEventQueueCompatExports.KernelEventFilterGraphics,
|
||||||
|
eventType);
|
||||||
|
if (tracePackets)
|
||||||
|
{
|
||||||
|
TraceAgc($"agc.dcb.event type=0x{eventType:X2} queues={triggered}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (op == ItNop && register == RReleaseMem && length >= 7)
|
||||||
|
{
|
||||||
|
ApplySubmittedReleaseMem(ctx, currentAddress, tracePackets);
|
||||||
|
}
|
||||||
|
|
||||||
if (op == ItDrawIndexOffset2 &&
|
if (op == ItDrawIndexOffset2 &&
|
||||||
length >= 5 &&
|
length >= 5 &&
|
||||||
TryReadUInt32(ctx, currentAddress + 4, out var indexCount) &&
|
TryReadUInt32(ctx, currentAddress + 4, out var indexCount) &&
|
||||||
@@ -972,6 +1112,38 @@ public static class AgcExports
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void ApplySubmittedReleaseMem(
|
||||||
|
CpuContext ctx,
|
||||||
|
ulong packetAddress,
|
||||||
|
bool tracePacket)
|
||||||
|
{
|
||||||
|
if (!TryReadUInt32(ctx, packetAddress + 8, out var control) ||
|
||||||
|
!TryReadUInt32(ctx, packetAddress + 12, out var destinationLo) ||
|
||||||
|
!TryReadUInt32(ctx, packetAddress + 16, out var destinationHi) ||
|
||||||
|
!TryReadUInt32(ctx, packetAddress + 20, out var dataLo) ||
|
||||||
|
!TryReadUInt32(ctx, packetAddress + 24, out var dataHi))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var dataSelection = (control >> 16) & 0xFFu;
|
||||||
|
var destinationAddress = ((ulong)destinationHi << 32) | destinationLo;
|
||||||
|
var data = ((ulong)dataHi << 32) | dataLo;
|
||||||
|
var wroteData = dataSelection switch
|
||||||
|
{
|
||||||
|
2 => TryWriteUInt32(ctx, destinationAddress, dataLo),
|
||||||
|
3 => ctx.TryWriteUInt64(destinationAddress, data),
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (tracePacket)
|
||||||
|
{
|
||||||
|
TraceAgc(
|
||||||
|
$"agc.dcb.release_mem dst=0x{destinationAddress:X16} data_sel={dataSelection} " +
|
||||||
|
$"data=0x{data:X16} wrote={wroteData}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void ApplySubmittedRegisters(
|
private static void ApplySubmittedRegisters(
|
||||||
CpuContext ctx,
|
CpuContext ctx,
|
||||||
SubmittedDcbState state,
|
SubmittedDcbState state,
|
||||||
@@ -1152,7 +1324,11 @@ public static class AgcExports
|
|||||||
destination.Width > 8192 ||
|
destination.Width > 8192 ||
|
||||||
destination.Height > 8192 ||
|
destination.Height > 8192 ||
|
||||||
destination.TilingMode != 0 ||
|
destination.TilingMode != 0 ||
|
||||||
destination.PixelFormat is not (VideoOutPixelFormatA8R8G8B8Srgb or VideoOutPixelFormatA8B8G8R8Srgb))
|
destination.PixelFormat is not (
|
||||||
|
VideoOutPixelFormatA8R8G8B8Srgb or
|
||||||
|
VideoOutPixelFormatA8B8G8R8Srgb or
|
||||||
|
VideoOutPixelFormatB8G8R8A8Unorm or
|
||||||
|
VideoOutPixelFormatR8G8B8A8Unorm))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1189,7 +1365,9 @@ public static class AgcExports
|
|||||||
}
|
}
|
||||||
|
|
||||||
var destinationRow = new byte[checked((int)destinationPitch * 4)];
|
var destinationRow = new byte[checked((int)destinationPitch * 4)];
|
||||||
var rgbaDestination = destination.PixelFormat == VideoOutPixelFormatA8B8G8R8Srgb;
|
var rgbaDestination = destination.PixelFormat is
|
||||||
|
VideoOutPixelFormatA8B8G8R8Srgb or
|
||||||
|
VideoOutPixelFormatR8G8B8A8Unorm;
|
||||||
for (uint y = 0; y < destination.Height; y++)
|
for (uint y = 0; y < destination.Height; y++)
|
||||||
{
|
{
|
||||||
var sourceY = (uint)(((ulong)y * source.Height) / destination.Height);
|
var sourceY = (uint)(((ulong)y * source.Height) / destination.Height);
|
||||||
@@ -1493,7 +1671,10 @@ public static class AgcExports
|
|||||||
|
|
||||||
private static bool IsSupportedRegisterDefaultsVersion(uint version)
|
private static bool IsSupportedRegisterDefaultsVersion(uint version)
|
||||||
{
|
{
|
||||||
return version is RegisterDefaultsVersion7 or RegisterDefaultsVersion8;
|
return version is
|
||||||
|
RegisterDefaultsVersion7 or
|
||||||
|
RegisterDefaultsVersion8 or
|
||||||
|
RegisterDefaultsVersion10;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool TryGetRegisterDefaultsAllocation(
|
private static bool TryGetRegisterDefaultsAllocation(
|
||||||
@@ -1696,7 +1877,9 @@ public static class AgcExports
|
|||||||
|
|
||||||
private static void TraceCreateShader(ulong destinationAddress, ulong headerAddress, ulong codeAddress, string detail)
|
private static void TraceCreateShader(ulong destinationAddress, ulong headerAddress, ulong codeAddress, string detail)
|
||||||
{
|
{
|
||||||
if (!string.Equals(Environment.GetEnvironmentVariable("SHARPEMU_LOG_AGC"), "1", StringComparison.Ordinal))
|
var isOk = string.Equals(detail, "ok", StringComparison.Ordinal);
|
||||||
|
if (isOk &&
|
||||||
|
!string.Equals(Environment.GetEnvironmentVariable("SHARPEMU_LOG_AGC"), "1", StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,15 +48,10 @@ public static class FiberExports
|
|||||||
[ThreadStatic]
|
[ThreadStatic]
|
||||||
private static ulong _currentFiberAddress;
|
private static ulong _currentFiberAddress;
|
||||||
|
|
||||||
[ThreadStatic]
|
private static readonly object _fiberGate = new();
|
||||||
private static bool _fiberReturnRequested;
|
|
||||||
|
|
||||||
[ThreadStatic]
|
|
||||||
private static ulong _fiberReturnArgument;
|
|
||||||
|
|
||||||
private static readonly ConcurrentDictionary<ulong, FiberContinuation> _continuations = new();
|
private static readonly ConcurrentDictionary<ulong, FiberContinuation> _continuations = new();
|
||||||
|
private static readonly ConcurrentDictionary<ulong, FiberReturnTarget> _returnTargets = new();
|
||||||
private static readonly ConcurrentDictionary<ulong, FiberStackRange> _stackRanges = new();
|
private static readonly ConcurrentDictionary<ulong, FiberStackRange> _stackRanges = new();
|
||||||
private static readonly ConcurrentDictionary<ulong, FiberRunSession> _runSessions = new();
|
|
||||||
|
|
||||||
[SysAbiExport(
|
[SysAbiExport(
|
||||||
Nid = "hVYD7Ou2pCQ",
|
Nid = "hVYD7Ou2pCQ",
|
||||||
@@ -146,6 +141,7 @@ public static class FiberExports
|
|||||||
}
|
}
|
||||||
|
|
||||||
_continuations.TryRemove(fiber, out _);
|
_continuations.TryRemove(fiber, out _);
|
||||||
|
_returnTargets.TryRemove(fiber, out _);
|
||||||
_stackRanges.TryRemove(fiber, out _);
|
_stackRanges.TryRemove(fiber, out _);
|
||||||
_ = TryWriteUInt32(ctx, fiber + FiberStateOffset, FiberStateTerminated);
|
_ = TryWriteUInt32(ctx, fiber + FiberStateOffset, FiberStateTerminated);
|
||||||
return SetReturn(ctx, 0);
|
return SetReturn(ctx, 0);
|
||||||
@@ -231,43 +227,75 @@ public static class FiberExports
|
|||||||
public static int FiberReturnToThread(CpuContext ctx)
|
public static int FiberReturnToThread(CpuContext ctx)
|
||||||
{
|
{
|
||||||
var fiberAddress = ResolveCurrentFiberAddress(ctx);
|
var fiberAddress = ResolveCurrentFiberAddress(ctx);
|
||||||
var inferredFiber = false;
|
|
||||||
if (_currentFiberAddress == 0 && fiberAddress != 0)
|
|
||||||
{
|
|
||||||
inferredFiber = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fiberAddress == 0)
|
if (fiberAddress == 0)
|
||||||
{
|
{
|
||||||
return SetReturn(ctx, FiberErrorPermission);
|
return SetReturn(ctx, FiberErrorPermission);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (GuestThreadExecution.Scheduler is not { SupportsGuestContextTransfer: true } ||
|
||||||
|
!GuestThreadExecution.TryGetCurrentImportCallFrame(out var frame))
|
||||||
|
{
|
||||||
|
return SetReturn(ctx, FiberErrorPermission);
|
||||||
|
}
|
||||||
|
|
||||||
|
var returnArgument = ctx[CpuRegister.Rdi];
|
||||||
var argOnRunAddress = ctx[CpuRegister.Rsi];
|
var argOnRunAddress = ctx[CpuRegister.Rsi];
|
||||||
if (argOnRunAddress != 0 && !TryWriteUInt64(ctx, argOnRunAddress, 0))
|
if (argOnRunAddress != 0 && !TryWriteUInt64(ctx, argOnRunAddress, 0))
|
||||||
{
|
{
|
||||||
return SetReturn(ctx, FiberErrorInvalid);
|
return SetReturn(ctx, FiberErrorInvalid);
|
||||||
}
|
}
|
||||||
|
|
||||||
_fiberReturnRequested = true;
|
GuestCpuContinuation transferTarget;
|
||||||
_fiberReturnArgument = ctx[CpuRegister.Rdi];
|
ulong previousFiber;
|
||||||
if (_runSessions.TryGetValue(fiberAddress, out var session))
|
lock (_fiberGate)
|
||||||
{
|
|
||||||
session.SetReturn(ctx[CpuRegister.Rdi]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GuestThreadExecution.TryGetCurrentImportCallFrame(out var frame))
|
|
||||||
{
|
{
|
||||||
_continuations[fiberAddress] = new FiberContinuation(
|
_continuations[fiberAddress] = new FiberContinuation(
|
||||||
CaptureContinuation(ctx, frame.ReturnRip, frame.ResumeRsp, frame.ReturnSlotAddress),
|
CaptureContinuation(ctx, frame.ReturnRip, frame.ResumeRsp, frame.ReturnSlotAddress),
|
||||||
argOnRunAddress);
|
argOnRunAddress);
|
||||||
TraceFiber($"yield{(inferredFiber ? "-inferred" : string.Empty)} fiber=0x{fiberAddress:X16} resume=0x{frame.ReturnRip:X16} rsp=0x{frame.ResumeRsp:X16} return_slot=0x{frame.ReturnSlotAddress:X16} arg_out=0x{argOnRunAddress:X16}");
|
|
||||||
}
|
if (!_returnTargets.TryRemove(fiberAddress, out var returnTarget))
|
||||||
else
|
{
|
||||||
{
|
_continuations.TryRemove(fiberAddress, out _);
|
||||||
TraceFiber($"yield-no-frame{(inferredFiber ? "-inferred" : string.Empty)} fiber=0x{fiberAddress:X16} arg_out=0x{argOnRunAddress:X16}");
|
return SetReturn(ctx, FiberErrorPermission);
|
||||||
|
}
|
||||||
|
|
||||||
|
previousFiber = returnTarget.PreviousFiber;
|
||||||
|
if (previousFiber != 0)
|
||||||
|
{
|
||||||
|
if (!_continuations.TryRemove(previousFiber, out var previousContinuation) ||
|
||||||
|
!TryWriteResumeArgument(ctx, previousContinuation, returnArgument) ||
|
||||||
|
!TryWriteUInt32(ctx, previousFiber + FiberStateOffset, FiberStateRun))
|
||||||
|
{
|
||||||
|
_continuations.TryRemove(fiberAddress, out _);
|
||||||
|
return SetReturn(ctx, FiberErrorState);
|
||||||
|
}
|
||||||
|
|
||||||
|
transferTarget = previousContinuation.Context with { Rax = 0 };
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!returnTarget.ThreadContinuation.HasValue ||
|
||||||
|
!TryWriteResumeArgument(ctx, returnTarget.ThreadContinuation.Value, returnArgument))
|
||||||
|
{
|
||||||
|
_continuations.TryRemove(fiberAddress, out _);
|
||||||
|
return SetReturn(ctx, FiberErrorState);
|
||||||
|
}
|
||||||
|
|
||||||
|
transferTarget = returnTarget.ThreadContinuation.Value.Context with { Rax = 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TryWriteUInt32(ctx, fiberAddress + FiberStateOffset, FiberStateIdle))
|
||||||
|
{
|
||||||
|
return SetReturn(ctx, FiberErrorInvalid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GuestThreadExecution.RequestCurrentEntryExit("sceFiberReturnToThread", 0);
|
_currentFiberAddress = previousFiber;
|
||||||
|
_ = GuestThreadExecution.EnterFiber(previousFiber);
|
||||||
|
GuestThreadExecution.RequestCurrentContextTransfer(transferTarget);
|
||||||
|
TraceFiber(
|
||||||
|
$"return fiber=0x{fiberAddress:X16} to=0x{previousFiber:X16} " +
|
||||||
|
$"resume=0x{transferTarget.Rip:X16} rsp=0x{transferTarget.Rsp:X16} arg=0x{returnArgument:X16}");
|
||||||
return SetReturn(ctx, 0);
|
return SetReturn(ctx, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -536,170 +564,152 @@ public static class FiberExports
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fields.State != FiberStateIdle)
|
|
||||||
{
|
|
||||||
TraceFiber($"run-state-error reason={reason} fiber=0x{fiber:X16} state=0x{fields.State:X8}");
|
|
||||||
return SetReturn(ctx, FiberErrorState);
|
|
||||||
}
|
|
||||||
|
|
||||||
var previousFiber = ResolveCurrentFiberAddress(ctx);
|
var previousFiber = ResolveCurrentFiberAddress(ctx);
|
||||||
var switchingFromFiber = isSwitch && previousFiber != 0 && previousFiber != fiber;
|
if ((isSwitch && previousFiber == 0) ||
|
||||||
if (isSwitch && previousFiber == 0)
|
(!isSwitch && previousFiber != 0))
|
||||||
{
|
{
|
||||||
return SetReturn(ctx, FiberErrorPermission);
|
return SetReturn(ctx, FiberErrorPermission);
|
||||||
}
|
}
|
||||||
|
if (previousFiber == fiber)
|
||||||
var scheduler = GuestThreadExecution.Scheduler;
|
|
||||||
if (scheduler is null)
|
|
||||||
{
|
|
||||||
return SetReturn(ctx, FiberErrorPermission);
|
|
||||||
}
|
|
||||||
|
|
||||||
var session = new FiberRunSession();
|
|
||||||
var ownsSession = _runSessions.TryAdd(fiber, session);
|
|
||||||
if (!ownsSession && !_runSessions.TryGetValue(fiber, out session))
|
|
||||||
{
|
{
|
||||||
return SetReturn(ctx, FiberErrorState);
|
return SetReturn(ctx, FiberErrorState);
|
||||||
}
|
}
|
||||||
|
if (GuestThreadExecution.Scheduler is not { SupportsGuestContextTransfer: true } ||
|
||||||
FiberContinuation? previousContinuation = null;
|
!GuestThreadExecution.TryGetCurrentImportCallFrame(out var frame))
|
||||||
GuestImportCallFrame switchFrame = default;
|
|
||||||
if (switchingFromFiber)
|
|
||||||
{
|
{
|
||||||
if (!GuestThreadExecution.TryGetCurrentImportCallFrame(out switchFrame))
|
return SetReturn(ctx, FiberErrorPermission);
|
||||||
|
}
|
||||||
|
|
||||||
|
GuestCpuContinuation transferTarget;
|
||||||
|
var resumed = false;
|
||||||
|
lock (_fiberGate)
|
||||||
|
{
|
||||||
|
if (!TryReadFiberFields(ctx, fiber, out fields))
|
||||||
{
|
{
|
||||||
if (ownsSession)
|
return SetReturn(ctx, FiberErrorInvalid);
|
||||||
{
|
}
|
||||||
_runSessions.TryRemove(fiber, out _);
|
if (fields.State != FiberStateIdle)
|
||||||
}
|
{
|
||||||
TraceFiber($"switch-no-frame from=0x{previousFiber:X16} to=0x{fiber:X16} arg_out=0x{outArgumentAddress:X16}");
|
TraceFiber($"run-state-error reason={reason} fiber=0x{fiber:X16} state=0x{fields.State:X8}");
|
||||||
return SetReturn(ctx, FiberErrorPermission);
|
return SetReturn(ctx, FiberErrorState);
|
||||||
}
|
}
|
||||||
|
|
||||||
previousContinuation = new FiberContinuation(
|
FiberContinuation targetContinuation;
|
||||||
CaptureContinuation(ctx, switchFrame.ReturnRip, switchFrame.ResumeRsp, switchFrame.ReturnSlotAddress),
|
if (_continuations.TryGetValue(fiber, out var savedContinuation))
|
||||||
|
{
|
||||||
|
targetContinuation = savedContinuation;
|
||||||
|
resumed = true;
|
||||||
|
}
|
||||||
|
else if (!TryCreateInitialContinuation(ctx, fields, argOnRun, out targetContinuation))
|
||||||
|
{
|
||||||
|
return SetReturn(ctx, FiberErrorInvalid);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resumed && !TryWriteResumeArgument(ctx, targetContinuation, argOnRun))
|
||||||
|
{
|
||||||
|
return SetReturn(ctx, FiberErrorInvalid);
|
||||||
|
}
|
||||||
|
|
||||||
|
var callerContinuation = new FiberContinuation(
|
||||||
|
CaptureContinuation(ctx, frame.ReturnRip, frame.ResumeRsp, frame.ReturnSlotAddress),
|
||||||
outArgumentAddress);
|
outArgumentAddress);
|
||||||
}
|
|
||||||
|
|
||||||
if (!TryWriteUInt32(ctx, fiber + FiberStateOffset, FiberStateRun))
|
if (previousFiber != 0)
|
||||||
{
|
|
||||||
if (ownsSession)
|
|
||||||
{
|
{
|
||||||
_runSessions.TryRemove(fiber, out _);
|
if (!TryReadUInt32(ctx, previousFiber + FiberStateOffset, out var previousState) ||
|
||||||
}
|
previousState != FiberStateRun ||
|
||||||
return SetReturn(ctx, FiberErrorInvalid);
|
!TryWriteUInt32(ctx, previousFiber + FiberStateOffset, FiberStateIdle))
|
||||||
}
|
|
||||||
|
|
||||||
if (switchingFromFiber && !TryWriteUInt32(ctx, previousFiber + FiberStateOffset, FiberStateIdle))
|
|
||||||
{
|
|
||||||
_ = TryWriteUInt32(ctx, fiber + FiberStateOffset, FiberStateIdle);
|
|
||||||
if (ownsSession)
|
|
||||||
{
|
|
||||||
_runSessions.TryRemove(fiber, out _);
|
|
||||||
}
|
|
||||||
return SetReturn(ctx, FiberErrorInvalid);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (previousContinuation.HasValue)
|
|
||||||
{
|
|
||||||
_continuations[previousFiber] = previousContinuation.Value;
|
|
||||||
TraceFiber($"switch-save from=0x{previousFiber:X16} to=0x{fiber:X16} resume=0x{switchFrame.ReturnRip:X16} rsp=0x{switchFrame.ResumeRsp:X16} return_slot=0x{switchFrame.ReturnSlotAddress:X16} arg_out=0x{outArgumentAddress:X16}");
|
|
||||||
}
|
|
||||||
|
|
||||||
var previousReturnRequested = _fiberReturnRequested;
|
|
||||||
var previousReturnArgument = _fiberReturnArgument;
|
|
||||||
_currentFiberAddress = fiber;
|
|
||||||
_fiberReturnRequested = false;
|
|
||||||
_fiberReturnArgument = 0;
|
|
||||||
var previousExecutionFiber = GuestThreadExecution.EnterFiber(fiber);
|
|
||||||
TraceFiber($"run-enter reason={reason} fiber=0x{fiber:X16} prev=0x{previousFiber:X16} thread=0x{GuestThreadExecution.CurrentGuestThreadHandle:X16} managed={Environment.CurrentManagedThreadId} stack=0x{fields.ContextAddress:X16}");
|
|
||||||
|
|
||||||
var hasContinuation = _continuations.TryGetValue(fiber, out var continuation);
|
|
||||||
if (hasContinuation)
|
|
||||||
{
|
|
||||||
_continuations.TryRemove(fiber, out _);
|
|
||||||
}
|
|
||||||
|
|
||||||
TraceFiber(hasContinuation
|
|
||||||
? $"run-dispatch reason={reason} fiber=0x{fiber:X16} resume=1 rip=0x{continuation.Context.Rip:X16} rsp=0x{continuation.Context.Rsp:X16} return_slot=0x{continuation.Context.ReturnSlotAddress:X16} arg_out=0x{continuation.ArgOnRunAddress:X16}"
|
|
||||||
: $"run-dispatch reason={reason} fiber=0x{fiber:X16} resume=0 rip=0x{fields.Entry:X16} rsp=0x{fields.ContextAddress + fields.ContextSize:X16} arg_out=0x{outArgumentAddress:X16}");
|
|
||||||
bool callbackOk;
|
|
||||||
string? callbackError;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (hasContinuation)
|
|
||||||
{
|
|
||||||
if (continuation.ArgOnRunAddress != 0 &&
|
|
||||||
!TryWriteUInt64(ctx, continuation.ArgOnRunAddress, argOnRun))
|
|
||||||
{
|
{
|
||||||
callbackOk = false;
|
return SetReturn(ctx, FiberErrorState);
|
||||||
callbackError = $"failed to write resumed argOnRun to 0x{continuation.ArgOnRunAddress:X16}";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
callbackOk = scheduler.TryCallGuestContinuation(
|
|
||||||
ctx,
|
|
||||||
continuation.Context,
|
|
||||||
reason,
|
|
||||||
out callbackError);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_continuations[previousFiber] = callerContinuation;
|
||||||
|
_returnTargets[fiber] = new FiberReturnTarget(previousFiber, null);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
callbackOk = scheduler.TryCallGuestFunction(
|
_returnTargets[fiber] = new FiberReturnTarget(0, callerContinuation);
|
||||||
ctx,
|
|
||||||
fields.Entry,
|
|
||||||
fields.ArgOnInitialize,
|
|
||||||
argOnRun,
|
|
||||||
fields.ContextAddress,
|
|
||||||
fields.ContextSize,
|
|
||||||
reason,
|
|
||||||
out callbackError);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
finally
|
if (!TryWriteUInt32(ctx, fiber + FiberStateOffset, FiberStateRun))
|
||||||
{
|
{
|
||||||
GuestThreadExecution.RestoreFiber(previousExecutionFiber);
|
if (previousFiber != 0)
|
||||||
|
{
|
||||||
|
_continuations.TryRemove(previousFiber, out _);
|
||||||
|
_ = TryWriteUInt32(ctx, previousFiber + FiberStateOffset, FiberStateRun);
|
||||||
|
}
|
||||||
|
_returnTargets.TryRemove(fiber, out _);
|
||||||
|
return SetReturn(ctx, FiberErrorInvalid);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resumed)
|
||||||
|
{
|
||||||
|
_continuations.TryRemove(fiber, out _);
|
||||||
|
}
|
||||||
|
|
||||||
|
transferTarget = targetContinuation.Context with { Rax = 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
var returnRequested = _fiberReturnRequested;
|
_currentFiberAddress = fiber;
|
||||||
var returnArgument = _fiberReturnArgument;
|
_ = GuestThreadExecution.EnterFiber(fiber);
|
||||||
if (!returnRequested && session.TryGetReturn(out var sessionReturnArgument))
|
GuestThreadExecution.RequestCurrentContextTransfer(transferTarget);
|
||||||
{
|
TraceFiber(
|
||||||
returnRequested = true;
|
$"transfer reason={reason} from=0x{previousFiber:X16} to=0x{fiber:X16} resume={resumed} " +
|
||||||
returnArgument = sessionReturnArgument;
|
$"rip=0x{transferTarget.Rip:X16} rsp=0x{transferTarget.Rsp:X16} arg=0x{argOnRun:X16}");
|
||||||
}
|
|
||||||
|
|
||||||
if (ownsSession)
|
|
||||||
{
|
|
||||||
_runSessions.TryRemove(fiber, out _);
|
|
||||||
}
|
|
||||||
|
|
||||||
_currentFiberAddress = previousFiber;
|
|
||||||
_fiberReturnRequested = previousReturnRequested;
|
|
||||||
_fiberReturnArgument = previousReturnArgument;
|
|
||||||
|
|
||||||
_ = TryWriteUInt32(ctx, fiber + FiberStateOffset, FiberStateIdle);
|
|
||||||
if (switchingFromFiber)
|
|
||||||
{
|
|
||||||
_ = TryWriteUInt32(ctx, previousFiber + FiberStateOffset, FiberStateRun);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!callbackOk)
|
|
||||||
{
|
|
||||||
TraceFiber($"run-failed fiber=0x{fiber:X16} entry=0x{fields.Entry:X16} error={callbackError}");
|
|
||||||
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_CPU_TRAP);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (outArgumentAddress != 0 && !TryWriteUInt64(ctx, outArgumentAddress, returnArgument))
|
|
||||||
{
|
|
||||||
return SetReturn(ctx, FiberErrorInvalid);
|
|
||||||
}
|
|
||||||
|
|
||||||
TraceFiber($"run fiber=0x{fiber:X16} entry=0x{fields.Entry:X16} resume={hasContinuation} arg=0x{argOnRun:X16} ret=0x{returnArgument:X16}");
|
|
||||||
return SetReturn(ctx, 0);
|
return SetReturn(ctx, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool TryCreateInitialContinuation(
|
||||||
|
CpuContext ctx,
|
||||||
|
FiberFields fields,
|
||||||
|
ulong argOnRun,
|
||||||
|
out FiberContinuation continuation)
|
||||||
|
{
|
||||||
|
continuation = default;
|
||||||
|
if (fields.ContextAddress == 0 || fields.ContextSize < FiberContextMinimumSize)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var stackEnd = fields.ContextAddress + fields.ContextSize;
|
||||||
|
var entryRsp = (stackEnd & ~15UL) - sizeof(ulong);
|
||||||
|
if (!TryWriteUInt64(ctx, entryRsp, 0))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
continuation = new FiberContinuation(
|
||||||
|
new GuestCpuContinuation(
|
||||||
|
fields.Entry,
|
||||||
|
entryRsp,
|
||||||
|
entryRsp,
|
||||||
|
ctx.Rflags == 0 ? 0x202UL : ctx.Rflags,
|
||||||
|
ctx.FsBase,
|
||||||
|
ctx.GsBase,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
argOnRun,
|
||||||
|
fields.ArgOnInitialize,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0),
|
||||||
|
0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryWriteResumeArgument(
|
||||||
|
CpuContext ctx,
|
||||||
|
FiberContinuation continuation,
|
||||||
|
ulong argument) =>
|
||||||
|
continuation.ArgOnRunAddress == 0 ||
|
||||||
|
TryWriteUInt64(ctx, continuation.ArgOnRunAddress, argument);
|
||||||
|
|
||||||
private static GuestCpuContinuation CaptureContinuation(
|
private static GuestCpuContinuation CaptureContinuation(
|
||||||
CpuContext ctx,
|
CpuContext ctx,
|
||||||
ulong resumeRip,
|
ulong resumeRip,
|
||||||
@@ -1007,30 +1017,9 @@ public static class FiberExports
|
|||||||
GuestCpuContinuation Context,
|
GuestCpuContinuation Context,
|
||||||
ulong ArgOnRunAddress);
|
ulong ArgOnRunAddress);
|
||||||
|
|
||||||
private sealed class FiberRunSession
|
private readonly record struct FiberReturnTarget(
|
||||||
{
|
ulong PreviousFiber,
|
||||||
private int _returnRequested;
|
FiberContinuation? ThreadContinuation);
|
||||||
|
|
||||||
private ulong _returnArgument;
|
|
||||||
|
|
||||||
public void SetReturn(ulong argument)
|
|
||||||
{
|
|
||||||
_returnArgument = argument;
|
|
||||||
Volatile.Write(ref _returnRequested, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TryGetReturn(out ulong argument)
|
|
||||||
{
|
|
||||||
if (Volatile.Read(ref _returnRequested) == 0)
|
|
||||||
{
|
|
||||||
argument = 0;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
argument = _returnArgument;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly record struct FiberStackRange(ulong Start, ulong Size)
|
private readonly record struct FiberStackRange(ulong Start, ulong Size)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user