mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-19 09:26:16 +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 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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user