[libs] Update AGC and fiber exports

This commit is contained in:
ParantezTech
2026-06-28 23:46:44 +03:00
parent 0e922a73ee
commit 418d46beb5
2 changed files with 366 additions and 194 deletions
+187 -4
View File
@@ -30,6 +30,7 @@ public static class AgcExports
private const uint RUcRegsIndirect = 0x13;
private const uint RAcquireMem = 0x14;
private const uint RFlip = 0x17;
private const uint RReleaseMem = 0x18;
private const uint SpiShaderPgmLoPs = 0x8;
private const uint SpiShaderPgmHiPs = 0x9;
private const uint SpiShaderPgmLoEs = 0xC8;
@@ -47,8 +48,11 @@ public static class AgcExports
private const uint Gen5TextureType2D = 9;
private const ulong VideoOutPixelFormatA8R8G8B8Srgb = 0x80000000;
private const ulong VideoOutPixelFormatA8B8G8R8Srgb = 0x80002200;
private const ulong VideoOutPixelFormatB8G8R8A8Unorm = 0x8100000000000000;
private const ulong VideoOutPixelFormatR8G8B8A8Unorm = 0x8100000022000000;
private const uint RegisterDefaultsVersion7 = 7;
private const uint RegisterDefaultsVersion8 = 8;
private const uint RegisterDefaultsVersion10 = 10;
private const int RegisterDefaultsSize = 0x40;
private const int RegisterDefaultBlockSize = 16 * 8;
@@ -497,6 +501,64 @@ public static class AgcExports
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(
Nid = "TRO721eVt4g",
ExportName = "sceAgcDcbResetQueue",
@@ -825,6 +887,50 @@ public static class AgcExports
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(
Nid = "UglJIZjGssM",
ExportName = "sceAgcDriverSubmitDcb",
@@ -871,6 +977,20 @@ public static class AgcExports
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)
{
if (commandAddress == 0 || dwordCount == 0 || dwordCount > 1_000_000)
@@ -912,6 +1032,26 @@ public static class AgcExports
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 &&
length >= 5 &&
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(
CpuContext ctx,
SubmittedDcbState state,
@@ -1152,7 +1324,11 @@ public static class AgcExports
destination.Width > 8192 ||
destination.Height > 8192 ||
destination.TilingMode != 0 ||
destination.PixelFormat is not (VideoOutPixelFormatA8R8G8B8Srgb or VideoOutPixelFormatA8B8G8R8Srgb))
destination.PixelFormat is not (
VideoOutPixelFormatA8R8G8B8Srgb or
VideoOutPixelFormatA8B8G8R8Srgb or
VideoOutPixelFormatB8G8R8A8Unorm or
VideoOutPixelFormatR8G8B8A8Unorm))
{
return false;
}
@@ -1189,7 +1365,9 @@ public static class AgcExports
}
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++)
{
var sourceY = (uint)(((ulong)y * source.Height) / destination.Height);
@@ -1493,7 +1671,10 @@ public static class AgcExports
private static bool IsSupportedRegisterDefaultsVersion(uint version)
{
return version is RegisterDefaultsVersion7 or RegisterDefaultsVersion8;
return version is
RegisterDefaultsVersion7 or
RegisterDefaultsVersion8 or
RegisterDefaultsVersion10;
}
private static bool TryGetRegisterDefaultsAllocation(
@@ -1696,7 +1877,9 @@ public static class AgcExports
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;
}
+179 -190
View File
@@ -48,15 +48,10 @@ public static class FiberExports
[ThreadStatic]
private static ulong _currentFiberAddress;
[ThreadStatic]
private static bool _fiberReturnRequested;
[ThreadStatic]
private static ulong _fiberReturnArgument;
private static readonly object _fiberGate = 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, FiberRunSession> _runSessions = new();
[SysAbiExport(
Nid = "hVYD7Ou2pCQ",
@@ -146,6 +141,7 @@ public static class FiberExports
}
_continuations.TryRemove(fiber, out _);
_returnTargets.TryRemove(fiber, out _);
_stackRanges.TryRemove(fiber, out _);
_ = TryWriteUInt32(ctx, fiber + FiberStateOffset, FiberStateTerminated);
return SetReturn(ctx, 0);
@@ -231,43 +227,75 @@ public static class FiberExports
public static int FiberReturnToThread(CpuContext ctx)
{
var fiberAddress = ResolveCurrentFiberAddress(ctx);
var inferredFiber = false;
if (_currentFiberAddress == 0 && fiberAddress != 0)
{
inferredFiber = true;
}
if (fiberAddress == 0)
{
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];
if (argOnRunAddress != 0 && !TryWriteUInt64(ctx, argOnRunAddress, 0))
{
return SetReturn(ctx, FiberErrorInvalid);
}
_fiberReturnRequested = true;
_fiberReturnArgument = ctx[CpuRegister.Rdi];
if (_runSessions.TryGetValue(fiberAddress, out var session))
{
session.SetReturn(ctx[CpuRegister.Rdi]);
}
if (GuestThreadExecution.TryGetCurrentImportCallFrame(out var frame))
GuestCpuContinuation transferTarget;
ulong previousFiber;
lock (_fiberGate)
{
_continuations[fiberAddress] = new FiberContinuation(
CaptureContinuation(ctx, frame.ReturnRip, frame.ResumeRsp, frame.ReturnSlotAddress),
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}");
}
else
{
TraceFiber($"yield-no-frame{(inferredFiber ? "-inferred" : string.Empty)} fiber=0x{fiberAddress:X16} arg_out=0x{argOnRunAddress:X16}");
if (!_returnTargets.TryRemove(fiberAddress, out var returnTarget))
{
_continuations.TryRemove(fiberAddress, out _);
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);
}
@@ -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 switchingFromFiber = isSwitch && previousFiber != 0 && previousFiber != fiber;
if (isSwitch && previousFiber == 0)
if ((isSwitch && previousFiber == 0) ||
(!isSwitch && previousFiber != 0))
{
return SetReturn(ctx, FiberErrorPermission);
}
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))
if (previousFiber == fiber)
{
return SetReturn(ctx, FiberErrorState);
}
FiberContinuation? previousContinuation = null;
GuestImportCallFrame switchFrame = default;
if (switchingFromFiber)
if (GuestThreadExecution.Scheduler is not { SupportsGuestContextTransfer: true } ||
!GuestThreadExecution.TryGetCurrentImportCallFrame(out var frame))
{
if (!GuestThreadExecution.TryGetCurrentImportCallFrame(out switchFrame))
return SetReturn(ctx, FiberErrorPermission);
}
GuestCpuContinuation transferTarget;
var resumed = false;
lock (_fiberGate)
{
if (!TryReadFiberFields(ctx, fiber, out fields))
{
if (ownsSession)
{
_runSessions.TryRemove(fiber, out _);
}
TraceFiber($"switch-no-frame from=0x{previousFiber:X16} to=0x{fiber:X16} arg_out=0x{outArgumentAddress:X16}");
return SetReturn(ctx, FiberErrorPermission);
return SetReturn(ctx, FiberErrorInvalid);
}
if (fields.State != FiberStateIdle)
{
TraceFiber($"run-state-error reason={reason} fiber=0x{fiber:X16} state=0x{fields.State:X8}");
return SetReturn(ctx, FiberErrorState);
}
previousContinuation = new FiberContinuation(
CaptureContinuation(ctx, switchFrame.ReturnRip, switchFrame.ResumeRsp, switchFrame.ReturnSlotAddress),
FiberContinuation targetContinuation;
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);
}
if (!TryWriteUInt32(ctx, fiber + FiberStateOffset, FiberStateRun))
{
if (ownsSession)
if (previousFiber != 0)
{
_runSessions.TryRemove(fiber, out _);
}
return SetReturn(ctx, FiberErrorInvalid);
}
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))
if (!TryReadUInt32(ctx, previousFiber + FiberStateOffset, out var previousState) ||
previousState != FiberStateRun ||
!TryWriteUInt32(ctx, previousFiber + FiberStateOffset, FiberStateIdle))
{
callbackOk = false;
callbackError = $"failed to write resumed argOnRun to 0x{continuation.ArgOnRunAddress:X16}";
}
else
{
callbackOk = scheduler.TryCallGuestContinuation(
ctx,
continuation.Context,
reason,
out callbackError);
return SetReturn(ctx, FiberErrorState);
}
_continuations[previousFiber] = callerContinuation;
_returnTargets[fiber] = new FiberReturnTarget(previousFiber, null);
}
else
{
callbackOk = scheduler.TryCallGuestFunction(
ctx,
fields.Entry,
fields.ArgOnInitialize,
argOnRun,
fields.ContextAddress,
fields.ContextSize,
reason,
out callbackError);
_returnTargets[fiber] = new FiberReturnTarget(0, callerContinuation);
}
}
finally
{
GuestThreadExecution.RestoreFiber(previousExecutionFiber);
if (!TryWriteUInt32(ctx, fiber + FiberStateOffset, FiberStateRun))
{
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;
var returnArgument = _fiberReturnArgument;
if (!returnRequested && session.TryGetReturn(out var sessionReturnArgument))
{
returnRequested = true;
returnArgument = sessionReturnArgument;
}
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}");
_currentFiberAddress = fiber;
_ = GuestThreadExecution.EnterFiber(fiber);
GuestThreadExecution.RequestCurrentContextTransfer(transferTarget);
TraceFiber(
$"transfer reason={reason} from=0x{previousFiber:X16} to=0x{fiber:X16} resume={resumed} " +
$"rip=0x{transferTarget.Rip:X16} rsp=0x{transferTarget.Rsp:X16} arg=0x{argOnRun:X16}");
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(
CpuContext ctx,
ulong resumeRip,
@@ -1007,30 +1017,9 @@ public static class FiberExports
GuestCpuContinuation Context,
ulong ArgOnRunAddress);
private sealed class FiberRunSession
{
private int _returnRequested;
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 FiberReturnTarget(
ulong PreviousFiber,
FiberContinuation? ThreadContinuation);
private readonly record struct FiberStackRange(ulong Start, ulong Size)
{