[kernel] Add thread and event primitives

This commit is contained in:
ParantezTech
2026-06-21 23:18:42 +03:00
parent 2a52ccfe6a
commit 5512da9e12
5 changed files with 1668 additions and 49 deletions
+961
View File
@@ -0,0 +1,961 @@
// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
using System.Buffers.Binary;
using System.Collections.Concurrent;
using System.Text;
using SharpEmu.HLE;
namespace SharpEmu.Libs.Fiber;
public static class FiberExports
{
private const int MaxNameLength = 31;
private const int FiberInfoSize = 128;
private const int FiberContextMinimumSize = 512;
private const uint FiberSignature0 = 0xDEF1649C;
private const uint FiberSignature1 = 0xB37592A0;
private const uint FiberOptSignature = 0xBB40E64D;
private const ulong FiberStackSignature = 0x7149F2CA7149F2CAUL;
private const ulong FiberStackSizeCheck = 0xDEADBEEFDEADBEEFUL;
private const uint FiberStateRun = 1;
private const uint FiberStateIdle = 2;
private const uint FiberStateTerminated = 3;
private const uint FiberFlagContextSizeCheck = 0x10;
private const int FiberErrorNull = unchecked((int)0x80590001);
private const int FiberErrorAlignment = unchecked((int)0x80590002);
private const int FiberErrorRange = unchecked((int)0x80590003);
private const int FiberErrorInvalid = unchecked((int)0x80590004);
private const int FiberErrorPermission = unchecked((int)0x80590005);
private const int FiberErrorState = unchecked((int)0x80590006);
private const int FiberMagicStartOffset = 0;
private const int FiberStateOffset = 4;
private const int FiberEntryOffset = 8;
private const int FiberArgOnInitializeOffset = 16;
private const int FiberContextAddressOffset = 24;
private const int FiberContextSizeOffset = 32;
private const int FiberNameOffset = 40;
private const int FiberContextPointerOffset = 72;
private const int FiberFlagsOffset = 80;
private const int FiberContextStartOffset = 88;
private const int FiberContextEndOffset = 96;
private const int FiberMagicEndOffset = 104;
private static int _contextSizeCheck;
[ThreadStatic]
private static ulong _currentFiberAddress;
[ThreadStatic]
private static bool _fiberReturnRequested;
[ThreadStatic]
private static ulong _fiberReturnArgument;
private static readonly ConcurrentDictionary<ulong, FiberContinuation> _continuations = new();
private static readonly ConcurrentDictionary<ulong, FiberStackRange> _stackRanges = new();
private static readonly ConcurrentDictionary<ulong, FiberRunSession> _runSessions = new();
[SysAbiExport(
Nid = "hVYD7Ou2pCQ",
ExportName = "_sceFiberInitializeImpl",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libSceFiber")]
public static int FiberInitialize(CpuContext ctx)
{
var optParam = ReadStackArg64(ctx, 0);
return FiberInitializeCore(
ctx,
ctx[CpuRegister.Rdi],
ctx[CpuRegister.Rsi],
ctx[CpuRegister.Rdx],
ctx[CpuRegister.Rcx],
ctx[CpuRegister.R8],
ctx[CpuRegister.R9],
optParam,
flags: 0);
}
[SysAbiExport(
Nid = "7+OJIpko9RY",
ExportName = "_sceFiberInitializeWithInternalOptionImpl",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libSceFiber")]
public static int FiberInitializeWithInternalOption(CpuContext ctx)
{
var optParam = ReadStackArg64(ctx, 0);
var flags = unchecked((uint)ReadStackArg64(ctx, 1));
return FiberInitializeCore(
ctx,
ctx[CpuRegister.Rdi],
ctx[CpuRegister.Rsi],
ctx[CpuRegister.Rdx],
ctx[CpuRegister.Rcx],
ctx[CpuRegister.R8],
ctx[CpuRegister.R9],
optParam,
flags);
}
[SysAbiExport(
Nid = "asjUJJ+aa8s",
ExportName = "sceFiberOptParamInitialize",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libSceFiber")]
public static int FiberOptParamInitialize(CpuContext ctx)
{
var optParam = ctx[CpuRegister.Rdi];
if (optParam == 0)
{
return SetReturn(ctx, FiberErrorNull);
}
if ((optParam & 7) != 0)
{
return SetReturn(ctx, FiberErrorAlignment);
}
return TryWriteUInt32(ctx, optParam, FiberOptSignature)
? SetReturn(ctx, 0)
: SetReturn(ctx, FiberErrorInvalid);
}
[SysAbiExport(
Nid = "JeNX5F-NzQU",
ExportName = "sceFiberFinalize",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libSceFiber")]
public static int FiberFinalize(CpuContext ctx)
{
var fiber = ctx[CpuRegister.Rdi];
if (!TryValidateFiber(ctx, fiber, out var error))
{
return SetReturn(ctx, error);
}
if (!TryReadUInt32(ctx, fiber + FiberStateOffset, out var state))
{
return SetReturn(ctx, FiberErrorInvalid);
}
if (state != FiberStateIdle)
{
return SetReturn(ctx, FiberErrorState);
}
_continuations.TryRemove(fiber, out _);
_stackRanges.TryRemove(fiber, out _);
_ = TryWriteUInt32(ctx, fiber + FiberStateOffset, FiberStateTerminated);
return SetReturn(ctx, 0);
}
[SysAbiExport(
Nid = "a0LLrZWac0M",
ExportName = "sceFiberRun",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libSceFiber")]
public static int FiberRun(CpuContext ctx)
{
return FiberRunCore(
ctx,
ctx[CpuRegister.Rdi],
ctx[CpuRegister.Rsi],
ctx[CpuRegister.Rdx],
attachContextAddress: 0,
attachContextSize: 0,
reason: "sceFiberRun",
isSwitch: false);
}
[SysAbiExport(
Nid = "avfGJ94g36Q",
ExportName = "_sceFiberAttachContextAndRun",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libSceFiber")]
public static int FiberAttachContextAndRun(CpuContext ctx)
{
return FiberRunCore(
ctx,
ctx[CpuRegister.Rdi],
ctx[CpuRegister.Rcx],
ctx[CpuRegister.R8],
attachContextAddress: ctx[CpuRegister.Rsi],
attachContextSize: ctx[CpuRegister.Rdx],
reason: "_sceFiberAttachContextAndRun",
isSwitch: false);
}
[SysAbiExport(
Nid = "PFT2S-tJ7Uk",
ExportName = "sceFiberSwitch",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libSceFiber")]
public static int FiberSwitch(CpuContext ctx)
{
return FiberRunCore(
ctx,
ctx[CpuRegister.Rdi],
ctx[CpuRegister.Rsi],
ctx[CpuRegister.Rdx],
attachContextAddress: 0,
attachContextSize: 0,
reason: "sceFiberSwitch",
isSwitch: true);
}
[SysAbiExport(
Nid = "ZqhZFuzKT6U",
ExportName = "_sceFiberAttachContextAndSwitch",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libSceFiber")]
public static int FiberAttachContextAndSwitch(CpuContext ctx)
{
return FiberRunCore(
ctx,
ctx[CpuRegister.Rdi],
ctx[CpuRegister.Rcx],
ctx[CpuRegister.R8],
attachContextAddress: ctx[CpuRegister.Rsi],
attachContextSize: ctx[CpuRegister.Rdx],
reason: "_sceFiberAttachContextAndSwitch",
isSwitch: true);
}
[SysAbiExport(
Nid = "B0ZX2hx9DMw",
ExportName = "sceFiberReturnToThread",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libSceFiber")]
public static int FiberReturnToThread(CpuContext ctx)
{
var fiberAddress = _currentFiberAddress;
var inferredFiber = false;
if (fiberAddress == 0 && TryFindFiberByStack(ctx, out fiberAddress))
{
inferredFiber = true;
}
if (fiberAddress == 0)
{
return SetReturn(ctx, FiberErrorPermission);
}
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))
{
_continuations[fiberAddress] = new FiberContinuation(
CaptureContinuation(ctx, frame.ReturnRip, frame.ResumeRsp),
argOnRunAddress);
TraceFiber($"yield{(inferredFiber ? "-inferred" : string.Empty)} fiber=0x{fiberAddress:X16} resume=0x{frame.ReturnRip:X16} rsp=0x{frame.ResumeRsp:X16} arg_out=0x{argOnRunAddress:X16}");
}
else
{
TraceFiber($"yield-no-frame{(inferredFiber ? "-inferred" : string.Empty)} fiber=0x{fiberAddress:X16} arg_out=0x{argOnRunAddress:X16}");
}
GuestThreadExecution.RequestCurrentEntryExit("sceFiberReturnToThread", 0);
return SetReturn(ctx, 0);
}
[SysAbiExport(
Nid = "p+zLIOg27zU",
ExportName = "sceFiberGetSelf",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libSceFiber")]
public static int FiberGetSelf(CpuContext ctx)
{
var outAddress = ctx[CpuRegister.Rdi];
if (outAddress == 0)
{
return SetReturn(ctx, FiberErrorNull);
}
if (_currentFiberAddress == 0)
{
return SetReturn(ctx, FiberErrorPermission);
}
return TryWriteUInt64(ctx, outAddress, _currentFiberAddress)
? SetReturn(ctx, 0)
: SetReturn(ctx, FiberErrorInvalid);
}
[SysAbiExport(
Nid = "uq2Y5BFz0PE",
ExportName = "sceFiberGetInfo",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libSceFiber")]
public static int FiberGetInfo(CpuContext ctx)
{
var fiber = ctx[CpuRegister.Rdi];
var info = ctx[CpuRegister.Rsi];
if (info == 0)
{
return SetReturn(ctx, FiberErrorNull);
}
if (!TryValidateFiber(ctx, fiber, out var error))
{
return SetReturn(ctx, error);
}
if (!TryReadUInt64(ctx, info, out var size) || size != FiberInfoSize)
{
return SetReturn(ctx, FiberErrorInvalid);
}
if (!TryReadFiberFields(ctx, fiber, out var fields))
{
return SetReturn(ctx, FiberErrorInvalid);
}
if (!TryWriteUInt64(ctx, info + 8, fields.Entry) ||
!TryWriteUInt64(ctx, info + 16, fields.ArgOnInitialize) ||
!TryWriteUInt64(ctx, info + 24, fields.ContextAddress) ||
!TryWriteUInt64(ctx, info + 32, fields.ContextSize) ||
!TryWriteName(ctx, info + 40, fields.Name) ||
!TryWriteUInt64(ctx, info + 72, ulong.MaxValue))
{
return SetReturn(ctx, FiberErrorInvalid);
}
return SetReturn(ctx, 0);
}
[SysAbiExport(
Nid = "JzyT91ucGDc",
ExportName = "sceFiberRename",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libSceFiber")]
public static int FiberRename(CpuContext ctx)
{
var fiber = ctx[CpuRegister.Rdi];
var nameAddress = ctx[CpuRegister.Rsi];
if (!TryValidateFiber(ctx, fiber, out var error))
{
return SetReturn(ctx, error);
}
if (nameAddress == 0)
{
return SetReturn(ctx, FiberErrorNull);
}
if (!TryReadNullTerminatedUtf8(ctx, nameAddress, MaxNameLength + 1, out var name))
{
return SetReturn(ctx, FiberErrorInvalid);
}
return TryWriteName(ctx, fiber + FiberNameOffset, name)
? SetReturn(ctx, 0)
: SetReturn(ctx, FiberErrorInvalid);
}
[SysAbiExport(
Nid = "Lcqty+QNWFc",
ExportName = "sceFiberStartContextSizeCheck",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libSceFiber")]
public static int FiberStartContextSizeCheck(CpuContext ctx)
{
if (ctx[CpuRegister.Rdi] != 0)
{
return SetReturn(ctx, FiberErrorInvalid);
}
return Interlocked.Exchange(ref _contextSizeCheck, 1) == 0
? SetReturn(ctx, 0)
: SetReturn(ctx, FiberErrorState);
}
[SysAbiExport(
Nid = "Kj4nXMpnM8Y",
ExportName = "sceFiberStopContextSizeCheck",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libSceFiber")]
public static int FiberStopContextSizeCheck(CpuContext ctx)
{
return Interlocked.Exchange(ref _contextSizeCheck, 0) == 1
? SetReturn(ctx, 0)
: SetReturn(ctx, FiberErrorState);
}
[SysAbiExport(
Nid = "0dy4JtMUcMQ",
ExportName = "_sceFiberGetThreadFramePointerAddress",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libSceFiber")]
public static int FiberGetThreadFramePointerAddress(CpuContext ctx)
{
var outAddress = ctx[CpuRegister.Rdi];
if (outAddress == 0)
{
return SetReturn(ctx, FiberErrorNull);
}
if (_currentFiberAddress == 0)
{
return SetReturn(ctx, FiberErrorPermission);
}
return TryWriteUInt64(ctx, outAddress, ctx[CpuRegister.Rbp])
? SetReturn(ctx, 0)
: SetReturn(ctx, FiberErrorInvalid);
}
private static int FiberInitializeCore(
CpuContext ctx,
ulong fiber,
ulong nameAddress,
ulong entry,
ulong argOnInitialize,
ulong contextAddress,
ulong contextSize,
ulong optParam,
uint flags)
{
if (fiber == 0 || nameAddress == 0 || entry == 0)
{
return SetReturn(ctx, FiberErrorNull);
}
if ((fiber & 7) != 0 ||
(contextAddress & 15) != 0 ||
(optParam & 7) != 0)
{
return SetReturn(ctx, FiberErrorAlignment);
}
if (contextSize != 0 && contextSize < FiberContextMinimumSize)
{
return SetReturn(ctx, FiberErrorRange);
}
if ((contextSize & 15) != 0 ||
(contextAddress == 0 && contextSize != 0) ||
(contextAddress != 0 && contextSize == 0))
{
return SetReturn(ctx, FiberErrorInvalid);
}
if (optParam != 0 &&
(!TryReadUInt32(ctx, optParam, out var optMagic) || optMagic != FiberOptSignature))
{
return SetReturn(ctx, FiberErrorInvalid);
}
if (!TryReadNullTerminatedUtf8(ctx, nameAddress, MaxNameLength + 1, out var name))
{
return SetReturn(ctx, FiberErrorInvalid);
}
if (Volatile.Read(ref _contextSizeCheck) != 0)
{
flags |= FiberFlagContextSizeCheck;
}
if (!TryWriteUInt32(ctx, fiber + FiberMagicStartOffset, FiberSignature0) ||
!TryWriteUInt32(ctx, fiber + FiberStateOffset, FiberStateIdle) ||
!TryWriteUInt64(ctx, fiber + FiberEntryOffset, entry) ||
!TryWriteUInt64(ctx, fiber + FiberArgOnInitializeOffset, argOnInitialize) ||
!TryWriteUInt64(ctx, fiber + FiberContextAddressOffset, contextAddress) ||
!TryWriteUInt64(ctx, fiber + FiberContextSizeOffset, contextSize) ||
!TryWriteName(ctx, fiber + FiberNameOffset, name) ||
!TryWriteUInt64(ctx, fiber + FiberContextPointerOffset, 0) ||
!TryWriteUInt32(ctx, fiber + FiberFlagsOffset, flags) ||
!TryWriteUInt64(ctx, fiber + FiberContextStartOffset, contextAddress) ||
!TryWriteUInt64(ctx, fiber + FiberContextEndOffset, contextAddress == 0 ? 0 : contextAddress + contextSize) ||
!TryWriteUInt32(ctx, fiber + FiberMagicEndOffset, FiberSignature1))
{
return SetReturn(ctx, FiberErrorInvalid);
}
if (contextAddress != 0)
{
if (!TryWriteUInt64(ctx, contextAddress, FiberStackSignature))
{
return SetReturn(ctx, FiberErrorInvalid);
}
if ((flags & FiberFlagContextSizeCheck) != 0)
{
FillContextSizeCheck(ctx, contextAddress + sizeof(ulong), contextSize - sizeof(ulong));
}
}
if (contextAddress != 0 && contextSize != 0)
{
_stackRanges[fiber] = new FiberStackRange(contextAddress, contextSize);
}
TraceFiber($"init fiber=0x{fiber:X16} entry=0x{entry:X16} ctx=0x{contextAddress:X16} size=0x{contextSize:X} name='{name}'");
return SetReturn(ctx, 0);
}
private static int FiberRunCore(
CpuContext ctx,
ulong fiber,
ulong argOnRun,
ulong outArgumentAddress,
ulong attachContextAddress,
ulong attachContextSize,
string reason,
bool isSwitch)
{
if (!TryValidateFiber(ctx, fiber, out var error))
{
return SetReturn(ctx, error);
}
if (!TryReadFiberFields(ctx, fiber, out var fields))
{
return SetReturn(ctx, FiberErrorInvalid);
}
if (attachContextAddress != 0 || attachContextSize != 0)
{
var attachResult = AttachContext(ctx, fiber, attachContextAddress, attachContextSize, ref fields);
if (attachResult != 0)
{
return SetReturn(ctx, attachResult);
}
}
if (fields.State != FiberStateIdle)
{
return SetReturn(ctx, FiberErrorState);
}
var previousFiber = _currentFiberAddress;
var switchingFromFiber = isSwitch && previousFiber != 0 && previousFiber != fiber;
if (isSwitch && previousFiber == 0)
{
return SetReturn(ctx, FiberErrorPermission);
}
var scheduler = GuestThreadExecution.Scheduler;
if (scheduler is null)
{
return SetReturn(ctx, FiberErrorPermission);
}
if (!TryWriteUInt32(ctx, fiber + FiberStateOffset, FiberStateRun))
{
return SetReturn(ctx, FiberErrorInvalid);
}
if (switchingFromFiber && !TryWriteUInt32(ctx, previousFiber + FiberStateOffset, FiberStateIdle))
{
_ = TryWriteUInt32(ctx, fiber + FiberStateOffset, FiberStateIdle);
return SetReturn(ctx, FiberErrorInvalid);
}
var previousReturnRequested = _fiberReturnRequested;
var previousReturnArgument = _fiberReturnArgument;
var session = new FiberRunSession();
_runSessions[fiber] = session;
_currentFiberAddress = fiber;
_fiberReturnRequested = false;
_fiberReturnArgument = 0;
var hasContinuation = _continuations.TryGetValue(fiber, out var continuation);
bool callbackOk;
string? callbackError;
if (hasContinuation)
{
if (continuation.ArgOnRunAddress != 0 &&
!TryWriteUInt64(ctx, continuation.ArgOnRunAddress, argOnRun))
{
callbackOk = false;
callbackError = $"failed to write resumed argOnRun to 0x{continuation.ArgOnRunAddress:X16}";
}
else
{
callbackOk = scheduler.TryCallGuestContinuation(
ctx,
continuation.Context,
reason,
out callbackError);
}
}
else
{
callbackOk = scheduler.TryCallGuestFunction(
ctx,
fields.Entry,
fields.ArgOnInitialize,
argOnRun,
fields.ContextAddress,
fields.ContextSize,
reason,
out callbackError);
}
var returnRequested = _fiberReturnRequested;
var returnArgument = _fiberReturnArgument;
if (!returnRequested && session.TryGetReturn(out var sessionReturnArgument))
{
returnRequested = true;
returnArgument = sessionReturnArgument;
}
if (!returnRequested)
{
_continuations.TryRemove(fiber, out _);
}
_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);
}
private static GuestCpuContinuation CaptureContinuation(CpuContext ctx, ulong resumeRip, ulong resumeRsp) =>
new(
resumeRip,
resumeRsp,
ctx.Rflags == 0 ? 0x202UL : ctx.Rflags,
ctx.FsBase,
ctx.GsBase,
0,
ctx[CpuRegister.Rcx],
ctx[CpuRegister.Rdx],
ctx[CpuRegister.Rbx],
ctx[CpuRegister.Rbp],
ctx[CpuRegister.Rsi],
ctx[CpuRegister.Rdi],
ctx[CpuRegister.R8],
ctx[CpuRegister.R9],
ctx[CpuRegister.R12],
ctx[CpuRegister.R13],
ctx[CpuRegister.R14],
ctx[CpuRegister.R15]);
private static int AttachContext(
CpuContext ctx,
ulong fiber,
ulong contextAddress,
ulong contextSize,
ref FiberFields fields)
{
if ((contextAddress & 15) != 0)
{
return FiberErrorAlignment;
}
if (contextSize != 0 && contextSize < FiberContextMinimumSize)
{
return FiberErrorRange;
}
if ((contextSize & 15) != 0 ||
contextAddress == 0 ||
contextSize == 0 ||
fields.ContextAddress != 0)
{
return FiberErrorInvalid;
}
if (!TryWriteUInt64(ctx, fiber + FiberContextAddressOffset, contextAddress) ||
!TryWriteUInt64(ctx, fiber + FiberContextSizeOffset, contextSize) ||
!TryWriteUInt64(ctx, fiber + FiberContextStartOffset, contextAddress) ||
!TryWriteUInt64(ctx, fiber + FiberContextEndOffset, contextAddress + contextSize) ||
!TryWriteUInt64(ctx, contextAddress, FiberStackSignature))
{
return FiberErrorInvalid;
}
fields = fields with
{
ContextAddress = contextAddress,
ContextSize = contextSize,
};
_stackRanges[fiber] = new FiberStackRange(contextAddress, contextSize);
return 0;
}
private static bool TryFindFiberByStack(CpuContext ctx, out ulong fiber)
{
if (TryFindFiberByStackAddress(ctx[CpuRegister.Rsp], out fiber))
{
return true;
}
return TryFindFiberByStackAddress(ctx[CpuRegister.Rbp], out fiber);
}
private static bool TryFindFiberByStackAddress(ulong address, out ulong fiber)
{
if (address != 0)
{
foreach (var (candidate, range) in _stackRanges)
{
if (range.Contains(address))
{
fiber = candidate;
return true;
}
}
}
fiber = 0;
return false;
}
private static bool TryValidateFiber(CpuContext ctx, ulong fiber, out int error)
{
if (fiber == 0)
{
error = FiberErrorNull;
return false;
}
if ((fiber & 7) != 0)
{
error = FiberErrorAlignment;
return false;
}
if (!TryReadUInt32(ctx, fiber + FiberMagicStartOffset, out var magicStart) ||
!TryReadUInt32(ctx, fiber + FiberMagicEndOffset, out var magicEnd) ||
magicStart != FiberSignature0 ||
magicEnd != FiberSignature1)
{
error = FiberErrorInvalid;
return false;
}
error = 0;
return true;
}
private static bool TryReadFiberFields(CpuContext ctx, ulong fiber, out FiberFields fields)
{
fields = default;
if (!TryReadUInt32(ctx, fiber + FiberStateOffset, out var state) ||
!TryReadUInt64(ctx, fiber + FiberEntryOffset, out var entry) ||
!TryReadUInt64(ctx, fiber + FiberArgOnInitializeOffset, out var argOnInitialize) ||
!TryReadUInt64(ctx, fiber + FiberContextAddressOffset, out var contextAddress) ||
!TryReadUInt64(ctx, fiber + FiberContextSizeOffset, out var contextSize) ||
!TryReadUInt32(ctx, fiber + FiberFlagsOffset, out var flags) ||
!TryReadInlineName(ctx, fiber + FiberNameOffset, out var name))
{
return false;
}
fields = new FiberFields(
state,
entry,
argOnInitialize,
contextAddress,
contextSize,
flags,
name);
return true;
}
private static void FillContextSizeCheck(CpuContext ctx, ulong address, ulong size)
{
Span<byte> value = stackalloc byte[sizeof(ulong)];
BinaryPrimitives.WriteUInt64LittleEndian(value, FiberStackSizeCheck);
var end = address + size;
for (var current = address; current + sizeof(ulong) <= end; current += sizeof(ulong))
{
_ = ctx.Memory.TryWrite(current, value);
}
}
private static ulong ReadStackArg64(CpuContext ctx, int index)
{
if (ctx.TryReadUInt64(ctx[CpuRegister.Rsp] + sizeof(ulong) + ((ulong)index * sizeof(ulong)), out var value))
{
return value;
}
return 0;
}
private static bool TryReadUInt32(CpuContext ctx, ulong address, out uint value)
{
Span<byte> buffer = stackalloc byte[sizeof(uint)];
if (!ctx.Memory.TryRead(address, buffer))
{
value = 0;
return false;
}
value = BinaryPrimitives.ReadUInt32LittleEndian(buffer);
return true;
}
private static bool TryWriteUInt32(CpuContext ctx, ulong address, uint value)
{
Span<byte> buffer = stackalloc byte[sizeof(uint)];
BinaryPrimitives.WriteUInt32LittleEndian(buffer, value);
return ctx.Memory.TryWrite(address, buffer);
}
private static bool TryReadUInt64(CpuContext ctx, ulong address, out ulong value)
{
Span<byte> buffer = stackalloc byte[sizeof(ulong)];
if (!ctx.Memory.TryRead(address, buffer))
{
value = 0;
return false;
}
value = BinaryPrimitives.ReadUInt64LittleEndian(buffer);
return true;
}
private static bool TryWriteUInt64(CpuContext ctx, ulong address, ulong value)
{
Span<byte> buffer = stackalloc byte[sizeof(ulong)];
BinaryPrimitives.WriteUInt64LittleEndian(buffer, value);
return ctx.Memory.TryWrite(address, buffer);
}
private static bool TryWriteName(CpuContext ctx, ulong address, string name)
{
Span<byte> buffer = stackalloc byte[MaxNameLength + 1];
var bytes = Encoding.UTF8.GetBytes(name);
bytes.AsSpan(0, Math.Min(bytes.Length, MaxNameLength)).CopyTo(buffer);
return ctx.Memory.TryWrite(address, buffer);
}
private static bool TryReadInlineName(CpuContext ctx, ulong address, out string value)
{
Span<byte> buffer = stackalloc byte[MaxNameLength + 1];
if (!ctx.Memory.TryRead(address, buffer))
{
value = string.Empty;
return false;
}
var length = buffer.IndexOf((byte)0);
if (length < 0)
{
length = buffer.Length;
}
value = Encoding.UTF8.GetString(buffer[..length]);
return true;
}
private static bool TryReadNullTerminatedUtf8(CpuContext ctx, ulong address, int capacity, out string value)
{
var bytes = new byte[capacity];
for (var index = 0; index < bytes.Length; index++)
{
Span<byte> current = stackalloc byte[1];
if (!ctx.Memory.TryRead(address + (ulong)index, current))
{
value = string.Empty;
return false;
}
if (current[0] == 0)
{
value = Encoding.UTF8.GetString(bytes, 0, index);
return true;
}
bytes[index] = current[0];
}
value = Encoding.UTF8.GetString(bytes);
return true;
}
private static int SetReturn(CpuContext ctx, int result)
{
ctx[CpuRegister.Rax] = unchecked((ulong)result);
return result;
}
private static void TraceFiber(string message)
{
if (string.Equals(Environment.GetEnvironmentVariable("SHARPEMU_LOG_FIBER"), "1", StringComparison.Ordinal))
{
Console.Error.WriteLine($"[LOADER][TRACE] fiber.{message}");
}
}
private readonly record struct FiberFields(
uint State,
ulong Entry,
ulong ArgOnInitialize,
ulong ContextAddress,
ulong ContextSize,
uint Flags,
string Name);
private readonly record struct FiberContinuation(
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 FiberStackRange(ulong Start, ulong Size)
{
public bool Contains(ulong address) =>
Size != 0 && address >= Start && address < Start + Size;
}
}