Files
PS2Recomp/ps2xRuntime/src/lib/Kernel/Syscalls/RPC.cpp
T
Ranieri ee149581aa Refactor IOP system (#170)
* feat: implement memory card IOP

* feat: IOP trace

* feat: move IOP logic to ps2xIOP
refactor: small refactor on audio api on runtime
2026-07-15 01:44:06 -03:00

1043 lines
35 KiB
C++

#include "Common.h"
#include "RPC.h"
#include "../../ps2_iop_transport.h"
namespace ps2_syscalls
{
namespace
{
SifRpcDebugEvent makeRpcDebugEvent(const char *op, R5900Context *ctx)
{
SifRpcDebugEvent event{};
event.op = op;
event.pc = ctx ? ctx->pc : 0u;
event.ra = ctx ? getRegU32(ctx, 31) : 0u;
event.threadId = static_cast<uint32_t>(g_currentThreadId);
return event;
}
void pushSifRpcDebugEventLocked(SifRpcDebugEvent event)
{
event.seq = ++g_sif_rpc_debug_next_seq;
g_sif_rpc_debug_history[event.seq % kSifRpcDebugHistoryCount] = event;
}
void pushSifRpcDebugEvent(SifRpcDebugEvent event)
{
std::lock_guard<std::mutex> lock(g_rpc_mutex);
pushSifRpcDebugEventLocked(event);
}
void fillRpcDebugPreview(const uint8_t *rdram, uint32_t addr, uint32_t size, uint8_t *preview, uint32_t &previewSize)
{
previewSize = 0u;
if (!rdram || !addr || !preview || size == 0u)
{
return;
}
const uint32_t count = std::min<uint32_t>(size, static_cast<uint32_t>(kSifRpcDebugPreviewBytes));
for (uint32_t i = 0; i < count; ++i)
{
const uint8_t *ptr = getConstMemPtr(rdram, addr + i);
if (!ptr)
{
break;
}
preview[i] = *ptr;
++previewSize;
}
}
std::string formatRpcTraceBytes(const uint8_t *bytes, uint32_t count)
{
std::string out;
if (!bytes || count == 0u)
{
return out;
}
char item[4] = {};
for (uint32_t i = 0; i < count; ++i)
{
std::snprintf(item, sizeof(item), "%02X", bytes[i]);
if (!out.empty())
{
out.push_back(' ');
}
out += item;
}
return out;
}
#ifndef PS2X_ENABLE_IOP_RPC_TRACE
#define PS2X_ENABLE_IOP_RPC_TRACE 1
#endif
#if PS2X_ENABLE_IOP_RPC_TRACE
std::string loadedIopModuleTraceSummary()
{
std::lock_guard<std::mutex> lock(g_sif_module_mutex);
std::string out;
uint32_t count = 0u;
for (const auto &entry : g_sif_modules_by_id)
{
const SifModuleRecord &module = entry.second;
if (!module.loaded)
{
continue;
}
if (!out.empty())
{
out += "; ";
}
out += module.pathKey.empty() ? module.path : module.pathKey;
++count;
if (count >= 6u)
{
break;
}
}
return out;
}
void logUnhandledRpcTrace(const SifRpcDebugEvent &event)
{
static std::unordered_set<uint64_t> loggedSignatures;
if (std::strcmp(event.op ? event.op : "", "CallRpc") != 0)
{
return;
}
uint64_t signature = 1469598103934665603ull;
auto mixSignature = [&](uint32_t value)
{
signature ^= static_cast<uint64_t>(value);
signature *= 1099511628211ull;
};
mixSignature(event.sid);
mixSignature(event.rpcNum);
mixSignature(event.sendSize);
mixSignature(event.recvSize);
if (!loggedSignatures.insert(signature).second || loggedSignatures.size() > 128u)
{
return;
}
const std::string modules = loadedIopModuleTraceSummary();
std::cerr << "[IOP/RPC trace:unhandled]"
<< " sid=0x" << std::hex << event.sid
<< " rpc=0x" << event.rpcNum
<< " pc=0x" << event.pc
<< " ra=0x" << event.ra
<< " send=0x" << event.sendBuf << "/" << std::dec << event.sendSize
<< " recv=0x" << std::hex << event.recvBuf << "/" << std::dec << event.recvSize
<< " sendBytes=[" << formatRpcTraceBytes(event.sendPreview, event.sendPreviewSize) << "]"
<< " loadedModules=[" << modules << "]"
<< std::dec << std::endl;
}
#endif
bool signalRpcCompletionSema(uint32_t semaId)
{
if (semaId == 0u || semaId > 0xFFFFu)
{
return false;
}
auto sema = lookupSemaInfo(static_cast<int>(semaId));
if (!sema)
{
return false;
}
bool signaled = false;
{
std::lock_guard<std::mutex> lock(sema->m);
if (!sema->deleted && sema->count < sema->maxCount)
{
sema->count++;
signaled = true;
}
}
if (signaled)
{
sema->cv.notify_one();
}
return signaled;
}
} // namespace
void SifStopModule(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const int32_t moduleId = static_cast<int32_t>(getRegU32(ctx, 4)); // $a0
const uint32_t resultAddr = getRegU32(ctx, 7); // $a3 (int* result, optional)
uint32_t refsLeft = 0;
const bool knownModule = trackSifModuleStop(moduleId, &refsLeft);
const int32_t ret = knownModule ? 0 : -1;
if (resultAddr != 0)
{
int32_t *hostResult = reinterpret_cast<int32_t *>(getMemPtr(rdram, resultAddr));
if (hostResult)
{
*hostResult = knownModule ? 0 : -1;
}
}
if (knownModule)
{
std::string modulePath;
{
std::lock_guard<std::mutex> lock(g_sif_module_mutex);
auto it = g_sif_modules_by_id.find(moduleId);
if (it != g_sif_modules_by_id.end())
{
modulePath = it->second.path;
}
}
logSifModuleAction("stop", moduleId, modulePath, refsLeft);
}
setReturnS32(ctx, ret);
}
void SifLoadModule(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
const uint32_t pathAddr = getRegU32(ctx, 4); // $a0
const std::string modulePath = readGuestCStringBounded(rdram, pathAddr, kMaxSifModulePathBytes);
if (modulePath.empty())
{
setReturnS32(ctx, -1);
return;
}
const int32_t moduleId = trackSifModuleLoad(modulePath);
if (moduleId <= 0)
{
setReturnS32(ctx, -1);
return;
}
uint32_t refs = 0;
{
std::lock_guard<std::mutex> lock(g_sif_module_mutex);
auto it = g_sif_modules_by_id.find(moduleId);
if (it != g_sif_modules_by_id.end())
{
refs = it->second.refCount;
}
}
logSifModuleAction("load", moduleId, modulePath, refs);
setReturnS32(ctx, moduleId);
}
void SifInitRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
std::lock_guard<std::mutex> lock(g_rpc_mutex);
if (runtime)
{
PS2IopTransport::reset(runtime);
}
if (!g_rpc_initialized)
{
g_rpc_servers.clear();
g_rpc_clients.clear();
g_rpc_next_id = 1;
g_rpc_packet_index = 0;
g_rpc_server_index = 0;
g_rpc_active_queue = 0;
g_sif_rpc_debug_next_seq = 0;
for (size_t i = 0; i < kSifRpcDebugHistoryCount; ++i)
{
g_sif_rpc_debug_history[i] = SifRpcDebugEvent{};
}
g_rpc_initialized = true;
RUNTIME_LOG("[SifInitRpc] Initialized");
}
SifRpcDebugEvent event = makeRpcDebugEvent("InitRpc", ctx);
event.result = 0;
pushSifRpcDebugEventLocked(event);
setReturnS32(ctx, 0);
}
void SifBindRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
uint32_t clientPtr = getRegU32(ctx, 4);
uint32_t rpcId = getRegU32(ctx, 5);
uint32_t mode = getRegU32(ctx, 6);
t_SifRpcClientData *client = reinterpret_cast<t_SifRpcClientData *>(getMemPtr(rdram, clientPtr));
if (!client)
{
SifRpcDebugEvent event = makeRpcDebugEvent("BindRpc", ctx);
event.clientPtr = clientPtr;
event.sid = rpcId;
event.mode = mode;
event.flags = kSifRpcDebugFlagMissingClient;
event.result = -1;
pushSifRpcDebugEvent(event);
setReturnS32(ctx, -1);
return;
}
client->command = 0;
client->buf = 0;
client->cbuf = 0;
client->end_function = 0;
client->end_param = 0;
client->server = 0;
client->hdr.pkt_addr = 0;
client->hdr.sema_id = -1;
client->hdr.mode = mode;
uint32_t serverPtr = 0;
{
std::lock_guard<std::mutex> lock(g_rpc_mutex);
client->hdr.rpc_id = g_rpc_next_id++;
auto it = g_rpc_servers.find(rpcId);
if (it != g_rpc_servers.end())
{
serverPtr = it->second.sd_ptr;
}
g_rpc_clients[clientPtr] = {};
g_rpc_clients[clientPtr].sid = rpcId;
}
if (!serverPtr)
{
// Allocate a dummy server so bind loops can proceed.
serverPtr = rpcAllocServerAddr(rdram);
if (serverPtr)
{
t_SifRpcServerData *dummy = reinterpret_cast<t_SifRpcServerData *>(getMemPtr(rdram, serverPtr));
if (dummy)
{
std::memset(dummy, 0, sizeof(*dummy));
dummy->sid = static_cast<int>(rpcId);
}
std::lock_guard<std::mutex> lock(g_rpc_mutex);
g_rpc_servers[rpcId] = {rpcId, serverPtr};
}
}
if (serverPtr)
{
t_SifRpcServerData *sd = reinterpret_cast<t_SifRpcServerData *>(getMemPtr(rdram, serverPtr));
client->server = serverPtr;
client->buf = sd ? sd->buf : 0;
client->cbuf = sd ? sd->cbuf : 0;
}
else
{
client->server = 0;
client->buf = 0;
client->cbuf = 0;
}
SifRpcDebugEvent event = makeRpcDebugEvent("BindRpc", ctx);
event.clientPtr = clientPtr;
event.serverPtr = serverPtr;
event.sid = rpcId;
event.mode = mode;
event.result = 0;
pushSifRpcDebugEvent(event);
setReturnS32(ctx, 0);
}
void SifCallRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
std::lock_guard<std::recursive_mutex> rpcCallLock(g_sif_call_rpc_mutex);
const uint32_t clientPtr = getRegU32(ctx, 4);
const uint32_t rpcNum = getRegU32(ctx, 5);
const uint32_t mode = getRegU32(ctx, 6);
const uint32_t sendBuf = getRegU32(ctx, 7);
const uint32_t stackPointer = getRegU32(ctx, 29);
const uint32_t sendSizeRegisters = getRegU32(ctx, 8);
const uint32_t receiveBufferRegisters = getRegU32(ctx, 9);
const uint32_t receiveSizeRegisters = getRegU32(ctx, 10);
const uint32_t endFunctionRegisters = getRegU32(ctx, 11);
uint32_t endParameterRegisters = 0u;
(void)readStackU32(rdram, stackPointer, 0x0u, endParameterRegisters);
uint32_t sendSizeStack = 0u;
uint32_t receiveBufferStack = 0u;
uint32_t receiveSizeStack = 0u;
uint32_t endFunctionStack = 0u;
uint32_t endParameterStack = 0u;
(void)readStackU32(rdram, stackPointer, 0x10u, sendSizeStack);
(void)readStackU32(rdram, stackPointer, 0x14u, receiveBufferStack);
(void)readStackU32(rdram, stackPointer, 0x18u, receiveSizeStack);
(void)readStackU32(rdram, stackPointer, 0x1Cu, endFunctionStack);
(void)readStackU32(rdram, stackPointer, 0x20u, endParameterStack);
const auto looksLikeGuestPointer = [](uint32_t value)
{
if (value == 0u)
{
return true;
}
const uint32_t normalized = value & 0x1FFFFFFFu;
return normalized >= 0x10000u && normalized < PS2_RAM_SIZE;
};
const auto looksLikeSize = [](uint32_t value)
{
return value <= 0x02000000u;
};
const auto plausiblePack = [&](uint32_t sendSize,
uint32_t receiveBuffer,
uint32_t receiveSize,
uint32_t endFunction)
{
return looksLikeSize(sendSize) &&
looksLikeGuestPointer(receiveBuffer) &&
looksLikeSize(receiveSize) &&
(endFunction == 0u || looksLikeGuestPointer(endFunction));
};
const bool registerPackPlausible =
plausiblePack(sendSizeRegisters,
receiveBufferRegisters,
receiveSizeRegisters,
endFunctionRegisters);
const bool stackPackPlausible =
plausiblePack(sendSizeStack,
receiveBufferStack,
receiveSizeStack,
endFunctionStack);
uint32_t sidHint = 0u;
{
std::lock_guard<std::mutex> lock(g_rpc_mutex);
const auto clientIt = g_rpc_clients.find(clientPtr);
if (clientIt != g_rpc_clients.end())
{
sidHint = clientIt->second.sid;
}
}
ps2x::iop::RpcAbi selectedAbi = ps2x::iop::RpcAbi::RuntimeDefault;
{
ps2x::iop::RpcAbiRequest request{};
request.boundSid = sidHint;
request.function = rpcNum;
request.registers = {
sendSizeRegisters,
receiveBufferRegisters,
receiveSizeRegisters,
endFunctionRegisters,
endParameterRegisters,
registerPackPlausible,
};
request.stack = {
sendSizeStack,
receiveBufferStack,
receiveSizeStack,
endFunctionStack,
endParameterStack,
stackPackPlausible,
};
selectedAbi = PS2IopTransport::selectRpcAbi(runtime, request);
}
bool useRegisterConvention = selectedAbi != ps2x::iop::RpcAbi::Stack;
if (selectedAbi == ps2x::iop::RpcAbi::RuntimeDefault && !registerPackPlausible && stackPackPlausible)
{
const bool registersHaveCallback = endFunctionRegisters != 0u && looksLikeGuestPointer(endFunctionRegisters);
const bool stackHasCallback = endFunctionStack != 0u && looksLikeGuestPointer(endFunctionStack);
if (!(registersHaveCallback && !stackHasCallback))
{
useRegisterConvention = false;
}
}
else if (selectedAbi == ps2x::iop::RpcAbi::Registers)
{
useRegisterConvention = true;
}
const uint32_t sendSize = useRegisterConvention ? sendSizeRegisters : sendSizeStack;
const uint32_t receiveBuffer = useRegisterConvention ? receiveBufferRegisters : receiveBufferStack;
const uint32_t receiveSize = useRegisterConvention ? receiveSizeRegisters : receiveSizeStack;
const uint32_t endFunction = useRegisterConvention ? endFunctionRegisters : endFunctionStack;
const uint32_t endParameter = useRegisterConvention ? endParameterRegisters : endParameterStack;
auto *client = reinterpret_cast<t_SifRpcClientData *>(getMemPtr(rdram, clientPtr));
if (!client)
{
SifRpcDebugEvent event = makeRpcDebugEvent("CallRpc", ctx);
event.clientPtr = clientPtr;
event.sid = sidHint;
event.rpcNum = rpcNum;
event.mode = mode;
event.sendBuf = sendBuf;
event.sendSize = sendSize;
event.recvBuf = receiveBuffer;
event.recvSize = receiveSize;
event.endFunc = endFunction;
event.endParam = endParameter;
event.flags = kSifRpcDebugFlagMissingClient | ((mode & kSifRpcModeNowait) ? kSifRpcDebugFlagNowait : 0u);
event.result = -1;
pushSifRpcDebugEvent(event);
setReturnS32(ctx, -1);
return;
}
client->command = rpcNum;
client->end_function = endFunction;
client->end_param = endParameter;
client->hdr.mode = mode;
uint32_t sid = 0u;
{
std::lock_guard<std::mutex> lock(g_rpc_mutex);
auto &state = g_rpc_clients[clientPtr];
state.busy = true;
state.last_rpc = rpcNum;
sid = state.sid;
if (sid != 0u)
{
const auto serverIt = g_rpc_servers.find(sid);
if (serverIt != g_rpc_servers.end() &&
serverIt->second.sd_ptr != 0u)
{
client->server = serverIt->second.sd_ptr;
}
}
}
const uint32_t serverPtr = client->server;
auto *server = serverPtr
? reinterpret_cast<t_SifRpcServerData *>(getMemPtr(rdram, serverPtr))
: nullptr;
if (server)
{
server->client = clientPtr;
server->pkt_addr = client->hdr.pkt_addr;
server->rpc_number = rpcNum;
server->size = static_cast<int>(sendSize);
server->recvbuf = receiveBuffer;
server->rsize = static_cast<int>(receiveSize);
server->rmode = ((mode & kSifRpcModeNowait) && endFunction == 0u) ? 0 : 1;
server->rid = 0;
if (server->buf != 0u && sendBuf != 0u && sendSize != 0u)
{
rpcCopyToRdram(rdram, server->buf, sendBuf, sendSize);
}
}
uint32_t resultPointer = 0u;
bool handled = false;
bool handledByIop = false;
bool serverDispatched = false;
bool callbackCompleted = false;
bool copiedFallback = false;
bool zeroedFallback = false;
ps2x::iop::RpcResult iopResult{};
const auto completionSemaphore = [&]()
{
uint32_t semaphore = static_cast<uint32_t>(client->hdr.sema_id);
if (semaphore == 0xFFFFFFFFu || semaphore == 0u)
{
semaphore = endParameter;
}
return semaphore;
};
{
ps2x::iop::RpcRequest request{};
request.clientAddress = clientPtr;
request.serverAddress = serverPtr;
request.serverFunction = server ? server->func : 0u;
request.serverBuffer = server ? server->buf : 0u;
request.sid = sid;
request.function = rpcNum;
request.mode = mode;
request.send = {sendBuf, sendSize};
request.receive = {receiveBuffer, receiveSize};
request.endFunction = endFunction;
request.endParameter = endParameter;
iopResult = PS2IopTransport::handleRpc(runtime, rdram, ctx, request);
handled = iopResult.handled;
handledByIop = iopResult.handled;
resultPointer = iopResult.resultAddress;
if (iopResult.signalNowaitCompletion &&
(mode & kSifRpcModeNowait) != 0u)
{
(void)signalRpcCompletionSema(completionSemaphore());
}
if (iopResult.signalCompletion)
{
(void)signalRpcCompletionSema(completionSemaphore());
}
}
if (server && server->func != 0u && iopResult.serverDispatchPolicy != ps2x::iop::ServerDispatchPolicy::Suppress)
{
uint32_t serverResult = 0u;
serverDispatched = rpcInvokeFunction(rdram,
ctx,
runtime,
server->func,
rpcNum,
server->buf,
sendSize,
0u,
&serverResult);
if (serverDispatched)
{
handled = true;
resultPointer = serverResult;
if (resultPointer == 0u && server->buf != 0u)
{
resultPointer = server->buf;
}
if (resultPointer == 0u)
{
resultPointer = receiveBuffer;
}
}
}
if (receiveBuffer != 0u && receiveSize != 0u)
{
if (handled && resultPointer != 0u && resultPointer != receiveBuffer)
{
rpcCopyToRdram(rdram,
receiveBuffer,
resultPointer,
receiveSize);
}
else if (!handled && sendBuf != 0u && sendSize != 0u && sendBuf != receiveBuffer)
{
const uint32_t copySize = std::min(sendSize, receiveSize);
rpcCopyToRdram(rdram, receiveBuffer, sendBuf, copySize);
copiedFallback = true;
}
else if (!handled)
{
rpcZeroRdram(rdram, receiveBuffer, receiveSize);
zeroedFallback = true;
}
}
if (endFunction != 0u)
{
if (iopResult.callbackPolicy == ps2x::iop::CallbackPolicy::Suppress)
{
callbackCompleted = true;
}
else
{
callbackCompleted = rpcInvokeFunction(rdram,
ctx,
runtime,
endFunction,
endParameter,
0u,
0u,
0u,
nullptr);
if (!callbackCompleted && endFunction >= 0x10000u)
{
const uint32_t normalizedEndFunction = endFunction - 0x10000u;
if (runtime->hasFunction(normalizedEndFunction))
{
callbackCompleted = rpcInvokeFunction(
rdram,
ctx,
runtime,
normalizedEndFunction,
endParameter,
0u,
0u,
0u,
nullptr);
}
}
}
if (!callbackCompleted)
{
const bool signaled = signalRpcCompletionSema(completionSemaphore());
static uint32_t unresolvedCallbackWarnings = 0u;
if (unresolvedCallbackWarnings < 32u)
{
std::cerr
<< "[SifCallRpc] unresolved end callback endFunc=0x"
<< std::hex << endFunction
<< " semaId=0x" << completionSemaphore()
<< " fallbackSignal=" << std::dec
<< (signaled ? 1 : 0) << std::endl;
++unresolvedCallbackWarnings;
}
}
}
{
std::lock_guard<std::mutex> lock(g_rpc_mutex);
g_rpc_clients[clientPtr].busy = false;
}
SifRpcDebugEvent event = makeRpcDebugEvent("CallRpc", ctx);
event.clientPtr = clientPtr;
event.serverPtr = serverPtr;
event.sid = sid;
event.rpcNum = rpcNum;
event.mode = mode;
event.sendBuf = sendBuf;
event.sendSize = sendSize;
event.recvBuf = receiveBuffer;
event.recvSize = receiveSize;
event.resultPtr = resultPointer;
event.endFunc = endFunction;
event.endParam = endParameter;
event.semaId = static_cast<uint32_t>(client->hdr.sema_id);
event.flags =
((mode & kSifRpcModeNowait) ? kSifRpcDebugFlagNowait : 0u) |
(handledByIop ? kSifRpcDebugFlagHandledByHle : 0u) |
(callbackCompleted ? kSifRpcDebugFlagCallback : 0u) |
(serverDispatched ? kSifRpcDebugFlagServerDispatch : 0u) |
(!handled ? kSifRpcDebugFlagUnhandled : 0u) |
(copiedFallback ? kSifRpcDebugFlagFallbackCopy : 0u) |
(zeroedFallback ? kSifRpcDebugFlagFallbackZero : 0u);
fillRpcDebugPreview(rdram,
sendBuf,
sendSize,
event.sendPreview,
event.sendPreviewSize);
fillRpcDebugPreview(rdram,
receiveBuffer,
receiveSize,
event.recvPreview,
event.recvPreviewSize);
event.result = 0;
#if PS2X_ENABLE_IOP_RPC_TRACE
if ((event.flags & kSifRpcDebugFlagUnhandled) != 0u)
{
logUnhandledRpcTrace(event);
}
#endif
pushSifRpcDebugEvent(event);
setReturnS32(ctx, 0);
}
void SifRegisterRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
uint32_t sdPtr = getRegU32(ctx, 4);
uint32_t sid = getRegU32(ctx, 5);
uint32_t func = getRegU32(ctx, 6);
uint32_t buf = getRegU32(ctx, 7);
// stack args: cfunc, cbuf, qd...
uint32_t sp = getRegU32(ctx, 29);
uint32_t cfunc = 0;
uint32_t cbuf = 0;
uint32_t qd = 0;
readStackU32(rdram, sp, 0x10, cfunc);
readStackU32(rdram, sp, 0x14, cbuf);
readStackU32(rdram, sp, 0x18, qd);
t_SifRpcServerData *sd = reinterpret_cast<t_SifRpcServerData *>(getMemPtr(rdram, sdPtr));
if (!sd)
{
SifRpcDebugEvent event = makeRpcDebugEvent("RegisterRpc", ctx);
event.serverPtr = sdPtr;
event.sid = sid;
event.sendBuf = buf;
event.recvBuf = cbuf;
event.resultPtr = qd;
event.endFunc = cfunc;
event.flags = kSifRpcDebugFlagMissingClient;
event.result = -1;
pushSifRpcDebugEvent(event);
setReturnS32(ctx, -1);
return;
}
sd->sid = static_cast<int>(sid);
sd->func = func;
sd->buf = buf;
sd->size = 0;
sd->cfunc = cfunc;
sd->cbuf = cbuf;
sd->size2 = 0;
sd->client = 0;
sd->pkt_addr = 0;
sd->rpc_number = 0;
sd->recvbuf = 0;
sd->rsize = 0;
sd->rmode = 0;
sd->rid = 0;
sd->base = qd;
sd->link = 0;
sd->next = 0;
{
std::lock_guard<std::mutex> lock(g_rpc_mutex);
if (qd)
{
t_SifRpcDataQueue *queue = reinterpret_cast<t_SifRpcDataQueue *>(getMemPtr(rdram, qd));
if (queue)
{
if (!queue->link)
{
queue->link = sdPtr;
}
else
{
uint32_t curPtr = queue->link;
for (int guard = 0; guard < 1024 && curPtr; ++guard)
{
t_SifRpcServerData *cur = reinterpret_cast<t_SifRpcServerData *>(getMemPtr(rdram, curPtr));
if (!cur)
break;
if (!cur->link)
{
cur->link = sdPtr;
break;
}
if (cur->link == sdPtr)
break;
curPtr = cur->link;
}
}
}
}
g_rpc_servers[sid] = {sid, sdPtr};
for (auto &entry : g_rpc_clients)
{
if (entry.second.sid == sid)
{
t_SifRpcClientData *cd = reinterpret_cast<t_SifRpcClientData *>(getMemPtr(rdram, entry.first));
if (cd)
{
cd->server = sdPtr;
cd->buf = sd->buf;
cd->cbuf = sd->cbuf;
}
}
}
}
RUNTIME_LOG("[SifRegisterRpc] sid=0x" << std::hex << sid << " sd=0x" << sdPtr << std::dec);
SifRpcDebugEvent event = makeRpcDebugEvent("RegisterRpc", ctx);
event.serverPtr = sdPtr;
event.sid = sid;
event.sendBuf = buf;
event.recvBuf = cbuf;
event.resultPtr = qd;
event.endFunc = cfunc;
event.result = 0;
pushSifRpcDebugEvent(event);
setReturnS32(ctx, 0);
}
void SifCheckStatRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
uint32_t clientPtr = getRegU32(ctx, 4);
std::lock_guard<std::mutex> lock(g_rpc_mutex);
auto it = g_rpc_clients.find(clientPtr);
if (it == g_rpc_clients.end())
{
setReturnS32(ctx, 0);
return;
}
setReturnS32(ctx, it->second.busy ? 1 : 0);
}
void SifSetRpcQueue(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
uint32_t qdPtr = getRegU32(ctx, 4);
int threadId = static_cast<int>(getRegU32(ctx, 5));
t_SifRpcDataQueue *qd = reinterpret_cast<t_SifRpcDataQueue *>(getMemPtr(rdram, qdPtr));
if (!qd)
{
setReturnS32(ctx, -1);
return;
}
qd->thread_id = threadId;
qd->active = 0;
qd->link = 0;
qd->start = 0;
qd->end = 0;
qd->next = 0;
{
std::lock_guard<std::mutex> lock(g_rpc_mutex);
if (!g_rpc_active_queue)
{
g_rpc_active_queue = qdPtr;
}
else
{
uint32_t curPtr = g_rpc_active_queue;
for (int guard = 0; guard < 1024 && curPtr; ++guard)
{
if (curPtr == qdPtr)
break;
t_SifRpcDataQueue *cur = reinterpret_cast<t_SifRpcDataQueue *>(getMemPtr(rdram, curPtr));
if (!cur)
break;
if (!cur->next)
{
cur->next = qdPtr;
break;
}
curPtr = cur->next;
}
}
}
setReturnS32(ctx, 0);
}
void SifRemoveRpcQueue(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
uint32_t qdPtr = getRegU32(ctx, 4);
if (!qdPtr)
{
setReturnU32(ctx, 0);
return;
}
std::lock_guard<std::mutex> lock(g_rpc_mutex);
if (!g_rpc_active_queue)
{
setReturnU32(ctx, 0);
return;
}
if (g_rpc_active_queue == qdPtr)
{
t_SifRpcDataQueue *qd = reinterpret_cast<t_SifRpcDataQueue *>(getMemPtr(rdram, qdPtr));
g_rpc_active_queue = qd ? qd->next : 0;
setReturnU32(ctx, qdPtr);
return;
}
uint32_t curPtr = g_rpc_active_queue;
for (int guard = 0; guard < 1024 && curPtr; ++guard)
{
t_SifRpcDataQueue *cur = reinterpret_cast<t_SifRpcDataQueue *>(getMemPtr(rdram, curPtr));
if (!cur)
break;
if (cur->next == qdPtr)
{
t_SifRpcDataQueue *rem = reinterpret_cast<t_SifRpcDataQueue *>(getMemPtr(rdram, qdPtr));
cur->next = rem ? rem->next : 0;
setReturnU32(ctx, qdPtr);
return;
}
curPtr = cur->next;
}
setReturnU32(ctx, 0);
}
void SifRemoveRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
uint32_t sdPtr = getRegU32(ctx, 4);
uint32_t qdPtr = getRegU32(ctx, 5);
t_SifRpcDataQueue *qd = reinterpret_cast<t_SifRpcDataQueue *>(getMemPtr(rdram, qdPtr));
if (!qd || !sdPtr)
{
setReturnU32(ctx, 0);
return;
}
std::lock_guard<std::mutex> lock(g_rpc_mutex);
if (qd->link == sdPtr)
{
t_SifRpcServerData *sd = reinterpret_cast<t_SifRpcServerData *>(getMemPtr(rdram, sdPtr));
qd->link = sd ? sd->link : 0;
if (sd)
sd->link = 0;
setReturnU32(ctx, sdPtr);
return;
}
uint32_t curPtr = qd->link;
for (int guard = 0; guard < 1024 && curPtr; ++guard)
{
t_SifRpcServerData *cur = reinterpret_cast<t_SifRpcServerData *>(getMemPtr(rdram, curPtr));
if (!cur)
break;
if (cur->link == sdPtr)
{
t_SifRpcServerData *sd = reinterpret_cast<t_SifRpcServerData *>(getMemPtr(rdram, sdPtr));
cur->link = sd ? sd->link : 0;
if (sd)
sd->link = 0;
setReturnU32(ctx, sdPtr);
return;
}
curPtr = cur->link;
}
setReturnU32(ctx, 0);
}
void sceSifCallRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
SifCallRpc(rdram, ctx, runtime);
}
void sceSifSendCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
uint32_t cid = getRegU32(ctx, 4);
uint32_t packetAddr = getRegU32(ctx, 5);
uint32_t packetSize = getRegU32(ctx, 6);
uint32_t srcExtra = getRegU32(ctx, 7);
uint32_t sp = getRegU32(ctx, 29);
uint32_t destExtra = 0;
uint32_t sizeExtra = 0;
readStackU32(rdram, sp, 0x10, destExtra);
readStackU32(rdram, sp, 0x14, sizeExtra);
if (sizeExtra > 0 && srcExtra && destExtra)
{
rpcCopyToRdram(rdram, destExtra, srcExtra, sizeExtra);
}
static int logCount = 0;
if (logCount < 5)
{
RUNTIME_LOG("[sceSifSendCmd] cid=0x" << std::hex << cid
<< " packet=0x" << packetAddr
<< " psize=0x" << packetSize
<< " extra=0x" << destExtra << std::dec << std::endl);
++logCount;
}
// Return non-zero on success.
setReturnS32(ctx, 1);
}
void sceRpcGetPacket(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
uint32_t queuePtr = getRegU32(ctx, 4);
setReturnS32(ctx, static_cast<int32_t>(queuePtr));
}
}