rpcsx/kernel/orbis/src/ipmi.cpp
DH 9fc036d9a5 orbis: initial physical memory emulation support (not used)
avoid unconditional linux specific types/api usage
2025-11-09 21:38:23 +03:00

1405 lines
41 KiB
C++

#include "ipmi.hpp"
#include "KernelContext.hpp"
#include "thread/Process.hpp"
#include "thread/Thread.hpp"
#include "utils/Logs.hpp"
#include <chrono>
#include <span>
orbis::ErrorCode orbis::ipmiCreateClient(Process *proc, void *clientImpl,
const char *name,
const IpmiCreateClientConfig &config,
rx::Ref<IpmiClient> &result) {
auto client = knew<IpmiClient>(name);
if (client == nullptr) {
return ErrorCode::NOMEM;
}
client->clientImpl = clientImpl;
client->name = name;
client->process = proc;
client->userData = config.userData;
client->eventFlags.resize(32);
client->messageQueues.resize(32);
result = client;
return {};
}
orbis::ErrorCode orbis::ipmiCreateServer(Process *proc, void *serverImpl,
const char *name,
const IpmiCreateServerConfig &config,
rx::Ref<IpmiServer> &result) {
auto [server, errorCode] = g_context->createIpmiServer(name);
ORBIS_RET_ON_ERROR(errorCode);
server->serverImpl = serverImpl;
server->userData = config.userData;
server->eventHandler = config.eventHandler;
server->pid = proc->pid;
result = server;
return {};
}
orbis::ErrorCode orbis::ipmiCreateSession(Thread *thread, void *sessionImpl,
ptr<void> userData,
rx::Ref<IpmiSession> &result) {
std::unique_lock ipmiMapLock(g_context->ipmiMap.mutex);
for (auto [kid, obj] : g_context->ipmiMap) {
auto server = dynamic_cast<IpmiServer *>(obj);
if (server == nullptr) {
continue;
}
std::lock_guard serverLock(server->mutex);
for (auto &conReq : server->connectionRequests) {
if (conReq.serverTid != thread->tid ||
conReq.client->session != nullptr ||
conReq.client->name != server->name) {
continue;
}
std::lock_guard clientLock(conReq.client->mutex);
if (conReq.client->session != nullptr) {
continue;
}
auto session = knew<IpmiSession>();
if (session == nullptr) {
return ErrorCode::NOMEM;
}
result = session;
session->sessionImpl = sessionImpl;
session->userData = userData;
session->client = conReq.client;
session->server = server;
conReq.client->session = session;
conReq.client->sessionCv.notify_one(conReq.client->mutex);
return {};
}
}
ORBIS_LOG_ERROR(__FUNCTION__, ": connection request not found");
return ErrorCode::INVAL;
}
orbis::SysResult orbis::sysIpmiCreateClient(Thread *thread, ptr<uint> result,
ptr<void> params,
uint64_t paramsSz) {
if (paramsSz != sizeof(IpmiCreateClientParams)) {
return ErrorCode::INVAL;
}
IpmiCreateClientParams _params;
IpmiCreateClientConfig _config;
char _name[25];
rx::Ref<IpmiClient> client;
ORBIS_RET_ON_ERROR(uread(_params, ptr<IpmiCreateClientParams>(params)));
ORBIS_RET_ON_ERROR(uread(_config, _params.config));
ORBIS_RET_ON_ERROR(ureadString(_name, sizeof(_name), _params.name));
ORBIS_RET_ON_ERROR(ipmiCreateClient(thread->tproc, _params.clientImpl, _name,
_config, client));
auto kid = g_context->ipmiMap.insert(std::move(client));
if (kid == -1) {
return ErrorCode::MFILE;
}
ORBIS_LOG_ERROR(__FUNCTION__, kid, _name);
return uwrite<uint>(result, kid);
}
orbis::SysResult orbis::sysIpmiCreateServer(Thread *thread, ptr<uint> result,
ptr<void> params,
uint64_t paramsSz) {
struct IpmiCreateServerParams {
ptr<void> serverImpl;
ptr<const char> name;
ptr<IpmiCreateServerConfig> config;
};
static_assert(sizeof(IpmiCreateServerParams) == 0x18);
if (paramsSz != sizeof(IpmiCreateServerParams)) {
return ErrorCode::INVAL;
}
IpmiCreateServerParams _params;
IpmiCreateServerConfig _config;
char _name[25];
rx::Ref<IpmiServer> server;
ORBIS_RET_ON_ERROR(uread(_params, ptr<IpmiCreateServerParams>(params)));
ORBIS_RET_ON_ERROR(uread(_config, _params.config));
ORBIS_RET_ON_ERROR(ureadString(_name, sizeof(_name), _params.name));
ORBIS_RET_ON_ERROR(ipmiCreateServer(thread->tproc, _params.serverImpl, _name,
_config, server));
auto kid = g_context->ipmiMap.insert(std::move(server));
if (kid == -1) {
return ErrorCode::MFILE;
}
ORBIS_LOG_ERROR(__FUNCTION__, kid, _name);
return uwrite<uint>(result, kid);
}
orbis::SysResult orbis::sysIpmiCreateSession(Thread *thread, ptr<uint> result,
ptr<void> params,
uint64_t paramsSz) {
struct IpmiSessionUserData {
uint64_t size;
ptr<void> data;
};
static_assert(sizeof(IpmiSessionUserData) == 0x10);
struct IpmiCreateSessionParams {
ptr<void> sessionImpl;
ptr<IpmiSessionUserData> userData;
};
static_assert(sizeof(IpmiCreateSessionParams) == 0x10);
if (paramsSz != sizeof(IpmiCreateSessionParams)) {
return ErrorCode::INVAL;
}
IpmiCreateSessionParams _params;
IpmiSessionUserData _userData;
rx::Ref<IpmiSession> session;
ORBIS_RET_ON_ERROR(uread(_params, ptr<IpmiCreateSessionParams>(params)));
ORBIS_RET_ON_ERROR(uread(_userData, _params.userData));
if (_userData.size != sizeof(IpmiSessionUserData)) {
return ErrorCode::INVAL;
}
ORBIS_RET_ON_ERROR(
ipmiCreateSession(thread, _params.sessionImpl, _userData.data, session));
auto kid = g_context->ipmiMap.insert(std::move(session));
if (kid == -1) {
return ErrorCode::MFILE;
}
ORBIS_LOG_ERROR(__FUNCTION__, kid);
return uwrite<uint>(result, kid);
}
orbis::SysResult orbis::sysIpmiDestroyClient(Thread *thread, ptr<uint> result,
uint kid, ptr<void> params,
uint64_t paramsSz) {
ORBIS_LOG_TODO(__FUNCTION__);
return uwrite<uint>(result, 0);
}
orbis::SysResult orbis::sysIpmiDestroyServer(Thread *thread, ptr<uint> result,
uint kid, ptr<void> params,
uint64_t paramsSz) {
ORBIS_LOG_TODO(__FUNCTION__);
return uwrite<uint>(result, 0);
}
orbis::SysResult orbis::sysIpmiDestroySession(Thread *thread, ptr<uint> result,
uint kid, ptr<void> params,
uint64_t paramsSz) {
ORBIS_LOG_TODO(__FUNCTION__);
return uwrite<uint>(result, 0);
}
orbis::SysResult orbis::sysIpmiServerReceivePacket(Thread *thread,
ptr<uint> result, uint kid,
ptr<void> params,
uint64_t paramsSz) {
struct IpmiServerReceivePacketParams {
ptr<void> buffer;
uint64_t bufferSize;
ptr<IpmiServer::IpmiPacketInfo> receivePacketInfo;
ptr<uint> unk;
};
if (paramsSz != sizeof(IpmiServerReceivePacketParams)) {
return orbis::ErrorCode::INVAL;
}
IpmiServerReceivePacketParams _params;
ORBIS_RET_ON_ERROR(
uread(_params, ptr<IpmiServerReceivePacketParams>(params)));
auto server = g_context->ipmiMap.get(kid).cast<IpmiServer>();
if (server == nullptr) {
return ErrorCode::INVAL;
}
IpmiServer::Packet _packet;
{
std::lock_guard lock(server->mutex);
while (server->packets.empty()) {
orbis::scoped_unblock unblock;
server->receiveCv.wait(server->mutex);
}
_packet = std::move(server->packets.front());
server->packets.pop_front();
}
if (_packet.info.type == 0x1) {
// on connection packet
for (auto &conn : server->connectionRequests) {
if (conn.clientTid == _packet.info.inputSize) {
conn.serverTid = thread->tid;
_packet.info.inputSize = 0;
break;
}
}
} else if ((_packet.info.type & ~(0x10 | 0x8000)) == 0x41) {
auto syncMessage = (IpmiSyncMessageHeader *)_packet.message.data();
ORBIS_LOG_ERROR(__FUNCTION__, server->name, syncMessage->methodId,
syncMessage->numInData, syncMessage->numOutData,
syncMessage->pid);
} else if ((_packet.info.type & ~0x10) == 0x43) {
auto asyncMessage = (IpmiAsyncMessageHeader *)_packet.message.data();
ORBIS_LOG_ERROR(__FUNCTION__, server->name, asyncMessage->methodId,
asyncMessage->numInData, asyncMessage->pid);
}
if (_params.bufferSize < _packet.message.size()) {
ORBIS_LOG_ERROR(__FUNCTION__, "too small buffer", _params.bufferSize,
_packet.message.size());
return ErrorCode::INVAL;
}
server->tidToClientTid[thread->tid] = _packet.clientTid;
ORBIS_RET_ON_ERROR(uwriteRaw((ptr<std::byte>)_params.buffer,
_packet.message.data(), _packet.message.size()));
_params.bufferSize = _packet.message.size();
_packet.info.eventHandler = server->eventHandler;
ORBIS_RET_ON_ERROR(uwrite(_params.receivePacketInfo, _packet.info));
ORBIS_RET_ON_ERROR(
uwrite(ptr<IpmiServerReceivePacketParams>(params), _params));
return uwrite<uint>(result, 0);
}
orbis::SysResult orbis::sysIpmiSendConnectResult(Thread *thread,
ptr<uint> result, uint kid,
ptr<void> params,
uint64_t paramsSz) {
if (paramsSz != sizeof(sint)) {
return ErrorCode::INVAL;
}
ORBIS_LOG_NOTICE(__FUNCTION__, kid);
auto ipmiObject = g_context->ipmiMap.get(kid);
if (ipmiObject == nullptr) {
return ErrorCode::INVAL;
}
rx::Ref<IpmiClient> client;
if (auto result = ipmiObject.cast<IpmiSession>()) {
client = result->client;
} else if (auto result = ipmiObject.cast<IpmiClient>()) {
client = result;
} else if (auto result = ipmiObject.cast<IpmiServer>()) {
for (auto &request : result->connectionRequests) {
if (request.serverTid == thread->tid) {
client = request.client;
break;
}
}
}
if (client == nullptr) {
ORBIS_LOG_FATAL(__FUNCTION__);
std::abort();
}
sint status;
ORBIS_RET_ON_ERROR(uread(status, ptr<sint>(params)));
ORBIS_LOG_NOTICE(__FUNCTION__, kid, status);
std::lock_guard lock(client->mutex);
client->connectionStatus = status;
client->connectCv.notify_all(client->mutex);
client->sessionCv.notify_all(client->mutex);
return uwrite(result, 0u);
}
orbis::SysResult orbis::sysIpmiSessionRespondSync(Thread *thread,
ptr<uint> result, uint kid,
ptr<void> params,
uint64_t paramsSz) {
struct IpmiRespondParams {
sint errorCode;
uint32_t bufferCount;
ptr<IpmiBufferInfo> buffers;
uint32_t flags;
uint32_t padding;
};
static_assert(sizeof(IpmiRespondParams) == 0x18);
if (paramsSz != sizeof(IpmiRespondParams)) {
return ErrorCode::INVAL;
}
auto session = g_context->ipmiMap.get(kid).cast<IpmiSession>();
if (session == nullptr) {
return ErrorCode::INVAL;
}
IpmiRespondParams _params;
ORBIS_RET_ON_ERROR(uread(_params, ptr<IpmiRespondParams>(params)));
kvector<kvector<std::byte>> buffers;
// if ((_params.flags & 1) || _params.bufferCount != 1) {
auto count = _params.bufferCount;
buffers.reserve(count);
for (uint32_t i = 0; i < count; ++i) {
IpmiBufferInfo _buffer;
ORBIS_RET_ON_ERROR(uread(_buffer, _params.buffers + i));
auto &bufferData = buffers.emplace_back();
bufferData.resize(_buffer.size);
ORBIS_RET_ON_ERROR(ureadRaw(bufferData.data(), _buffer.data, _buffer.size));
}
// }
std::lock_guard lock(session->mutex);
std::uint32_t clientTid;
{
std::lock_guard serverLock(session->server->mutex);
clientTid = session->server->tidToClientTid.at(thread->tid);
}
ORBIS_LOG_ERROR(__FUNCTION__, session->client->name, _params.errorCode);
if (_params.errorCode != 0) {
ORBIS_LOG_ERROR(__FUNCTION__, session->client->name, _params.errorCode);
thread->where();
}
session->syncResponses.push_front({
.errorCode = _params.errorCode,
.callerTid = clientTid,
.data = std::move(buffers),
});
session->responseCv.notify_all(session->mutex);
return uwrite(result, 0u);
}
orbis::SysResult orbis::sysIpmiClientInvokeAsyncMethod(Thread *thread,
ptr<uint> result,
uint kid,
ptr<void> params,
uint64_t paramsSz) {
struct IpmiAsyncCallParams {
uint32_t method;
uint32_t evfIndex;
uint64_t evfValue;
uint32_t numInData;
uint32_t padding1;
ptr<IpmiDataInfo> pInData;
ptr<sint> pResult;
uint32_t flags;
};
static_assert(sizeof(IpmiAsyncCallParams) == 0x30);
if (paramsSz != sizeof(IpmiAsyncCallParams)) {
return ErrorCode::INVAL;
}
auto client = g_context->ipmiMap.get(kid).cast<IpmiClient>();
if (client == nullptr) {
return ErrorCode::INVAL;
}
IpmiAsyncCallParams _params;
ORBIS_RET_ON_ERROR(uread(_params, (ptr<IpmiAsyncCallParams>)params));
if (_params.flags > 1) {
return ErrorCode::INVAL;
}
std::lock_guard clientLock(client->mutex);
auto session = client->session;
if (session == nullptr) {
return ErrorCode::INVAL;
}
std::lock_guard sessionLock(session->mutex);
auto server = session->server;
if (server == nullptr) {
return ErrorCode::INVAL;
}
{
std::lock_guard serverLock(server->mutex);
std::size_t inSize = 0;
for (auto &data : std::span(_params.pInData, _params.numInData)) {
inSize += data.size;
}
auto size = sizeof(IpmiAsyncMessageHeader) + inSize +
_params.numInData * sizeof(uint32_t);
kvector<std::byte> message(size);
auto msg = new (message.data()) IpmiAsyncMessageHeader;
msg->sessionImpl = session->sessionImpl;
msg->pid = thread->tproc->pid;
msg->methodId = _params.method;
msg->numInData = _params.numInData;
auto bufLoc = std::bit_cast<char *>(msg + 1);
for (auto &data : std::span(_params.pInData, _params.numInData)) {
*std::bit_cast<uint32_t *>(bufLoc) = data.size;
bufLoc += sizeof(uint32_t);
ORBIS_RET_ON_ERROR(ureadRaw(bufLoc, data.data, data.size));
bufLoc += data.size;
}
uint type = 0x43;
if ((_params.flags & 1) == 0) {
type |= 0x10;
}
server->packets.push_back(
{{.type = type, .clientKid = kid}, 0, session, std::move(message)});
server->receiveCv.notify_one(server->mutex);
}
if (_params.evfIndex != -1 && _params.evfValue != 0) {
client->eventFlags[_params.evfIndex].set(_params.evfValue);
}
ORBIS_RET_ON_ERROR(uwrite(_params.pResult, 0));
return uwrite(result, 0u);
}
orbis::SysResult orbis::sysImpiSessionRespondAsync(Thread *thread,
ptr<uint> result, uint kid,
ptr<void> params,
uint64_t paramsSz) {
struct IpmiAsyncRespondParams {
uint method;
uint pid;
sint result;
uint32_t numOutData;
ptr<IpmiDataInfo> pOutData;
uint32_t unk2; // == 1
};
static_assert(sizeof(IpmiAsyncRespondParams) == 0x20);
if (paramsSz != sizeof(IpmiAsyncRespondParams)) {
return ErrorCode::INVAL;
}
auto session = g_context->ipmiMap.get(kid).cast<IpmiSession>();
if (session == nullptr) {
return ErrorCode::INVAL;
}
auto client = session->client;
if (client == nullptr) {
return ErrorCode::INVAL;
}
IpmiAsyncRespondParams _params;
ORBIS_RET_ON_ERROR(uread(_params, (ptr<IpmiAsyncRespondParams>)params));
kvector<kvector<std::byte>> outData;
outData.reserve(_params.numOutData);
for (auto data : std::span(_params.pOutData, _params.numOutData)) {
auto &elem = outData.emplace_back();
elem.resize(data.size);
ORBIS_RET_ON_ERROR(ureadRaw(elem.data(), data.data, data.size));
}
{
std::lock_guard clientLock(client->mutex);
client->asyncResponses.push_back({
.methodId = _params.method,
.errorCode = _params.result,
.data = std::move(outData),
});
}
client->asyncResponseCv.notify_all(client->mutex);
return uwrite(result, 0u);
}
orbis::SysResult orbis::sysIpmiClientTryGetResult(Thread *thread,
ptr<uint> result, uint kid,
ptr<void> params,
uint64_t paramsSz) {
struct IpmiTryGetResultParams {
uint32_t method;
uint32_t unk;
ptr<sint> pResult;
uint32_t numOutData;
uint32_t padding;
ptr<IpmiBufferInfo> pOutData;
uint64_t padding2;
};
static_assert(sizeof(IpmiTryGetResultParams) == 0x28);
if (paramsSz != sizeof(IpmiTryGetResultParams)) {
return ErrorCode::INVAL;
}
IpmiTryGetResultParams _params;
ORBIS_RET_ON_ERROR(uread(_params, (ptr<IpmiTryGetResultParams>)params));
auto client = g_context->ipmiMap.get(kid).cast<IpmiClient>();
if (client == nullptr) {
return ErrorCode::INVAL;
}
while (true) {
std::lock_guard clientLock(client->mutex);
for (auto it = client->asyncResponses.begin();
it != client->asyncResponses.end(); ++it) {
if (it->methodId != _params.method) {
continue;
}
auto response = std::move(*it);
client->asyncResponses.erase(it);
ORBIS_RET_ON_ERROR(uwrite(_params.pResult, it->errorCode));
if (response.data.size() != _params.numOutData) {
ORBIS_LOG_ERROR(__FUNCTION__, "responses count mismatch",
response.data.size(), _params.numOutData);
}
for (std::size_t i = 0; i < response.data.size(); ++i) {
if (response.data.size() > _params.numOutData) {
ORBIS_LOG_ERROR(__FUNCTION__, "too many responses",
response.data.size(), _params.numOutData);
break;
}
IpmiBufferInfo _outData;
ORBIS_RET_ON_ERROR(uread(_outData, _params.pOutData + i));
auto &data = response.data[i];
if (_outData.capacity < data.size()) {
ORBIS_LOG_ERROR(__FUNCTION__, "too big response", _outData.capacity,
data.size());
continue;
}
_outData.size = data.size();
ORBIS_RET_ON_ERROR(uwriteRaw(_outData.data, data.data(), data.size()));
ORBIS_RET_ON_ERROR(uwrite(_params.pOutData + i, _outData));
}
return uwrite(result, 0u);
}
orbis::scoped_unblock unblock;
client->asyncResponseCv.wait(client->mutex);
}
// return uwrite(result, 0x80020000 + static_cast<int>(ErrorCode::AGAIN));
}
orbis::SysResult orbis::sysIpmiClientGetMessage(Thread *thread,
ptr<uint> result, uint kid,
ptr<void> params,
uint64_t paramsSz) {
struct SceIpmiClientGetArgs {
uint32_t queueIndex;
uint32_t padding;
ptr<std::byte> message;
ptr<uint64_t> pSize;
uint64_t maxSize;
ptr<uint> pTimeout;
};
static_assert(sizeof(SceIpmiClientGetArgs) == 0x28);
if (paramsSz != sizeof(SceIpmiClientGetArgs)) {
return ErrorCode::INVAL;
}
auto client = g_context->ipmiMap.get(kid).cast<IpmiClient>();
if (client == nullptr) {
return ErrorCode::INVAL;
}
SceIpmiClientGetArgs _params;
ORBIS_RET_ON_ERROR(uread(_params, ptr<SceIpmiClientGetArgs>(params)));
std::lock_guard lock(client->mutex);
if (_params.queueIndex >= client->messageQueues.size()) {
return ErrorCode::INVAL;
}
auto &queue = client->messageQueues[_params.queueIndex];
using clock = std::chrono::high_resolution_clock;
clock::time_point timeoutPoint = clock::time_point::max();
if (_params.pTimeout != nullptr) {
std::uint32_t timeout{};
ORBIS_RET_ON_ERROR(uread(timeout, _params.pTimeout));
timeoutPoint = clock::now() + std::chrono::microseconds(timeout);
}
if (queue.messages.empty()) {
if (timeoutPoint != clock::time_point::max()) {
while (true) {
auto now = clock::now();
if (now >= timeoutPoint) {
ORBIS_RET_ON_ERROR(uwrite(_params.pTimeout, 0u));
return uwrite<uint>(
result, 0x80020000 + static_cast<int>(ErrorCode::TIMEDOUT));
}
auto waitTime = std::chrono::duration_cast<std::chrono::microseconds>(
timeoutPoint - now);
{
orbis::scoped_unblock unblock;
queue.messageCv.wait(client->mutex, waitTime.count());
}
if (!queue.messages.empty()) {
now = clock::now();
if (now >= timeoutPoint) {
ORBIS_RET_ON_ERROR(uwrite(_params.pTimeout, 0u));
} else {
std::uint32_t restTime =
std::chrono::duration_cast<std::chrono::microseconds>(
timeoutPoint - now)
.count();
ORBIS_RET_ON_ERROR(uwrite(_params.pTimeout, restTime));
}
break;
}
}
} else {
while (queue.messages.empty()) {
orbis::scoped_unblock unblock;
queue.messageCv.wait(client->mutex);
}
}
}
auto &message = queue.messages.front();
if (_params.maxSize < message.size()) {
ORBIS_LOG_ERROR(__FUNCTION__, "too small buffer");
return uwrite<uint>(result,
0x80020000 + static_cast<int>(ErrorCode::INVAL));
}
ORBIS_RET_ON_ERROR(uwrite(_params.pSize, message.size()));
ORBIS_RET_ON_ERROR(
uwriteRaw(_params.message, message.data(), message.size()));
queue.messages.pop_front();
return uwrite<uint>(result, 0);
}
orbis::SysResult orbis::sysIpmiClientTryGetMessage(Thread *thread,
ptr<uint> result, uint kid,
ptr<void> params,
uint64_t paramsSz) {
struct SceIpmiClientTryGetArgs {
uint32_t queueIndex;
uint32_t padding;
ptr<std::byte> message;
ptr<uint64_t> pSize;
uint64_t maxSize;
};
static_assert(sizeof(SceIpmiClientTryGetArgs) == 0x20);
if (paramsSz != sizeof(SceIpmiClientTryGetArgs)) {
return ErrorCode::INVAL;
}
auto client = g_context->ipmiMap.get(kid).cast<IpmiClient>();
if (client == nullptr) {
return ErrorCode::INVAL;
}
SceIpmiClientTryGetArgs _params;
ORBIS_RET_ON_ERROR(uread(_params, ptr<SceIpmiClientTryGetArgs>(params)));
std::lock_guard lock(client->mutex);
if (_params.queueIndex >= client->messageQueues.size()) {
return ErrorCode::INVAL;
}
auto &queue = client->messageQueues[_params.queueIndex];
if (queue.messages.empty()) {
return uwrite<uint>(result,
0x80020000 + static_cast<int>(ErrorCode::AGAIN));
}
auto &message = queue.messages.front();
if (_params.maxSize < message.size()) {
ORBIS_LOG_ERROR(__FUNCTION__, "too small buffer");
return uwrite<uint>(result,
0x80020000 + static_cast<int>(ErrorCode::INVAL));
}
ORBIS_RET_ON_ERROR(uwrite(_params.pSize, message.size()));
ORBIS_RET_ON_ERROR(
uwriteRaw(_params.message, message.data(), message.size()));
queue.messages.pop_front();
return uwrite<uint>(result, 0);
}
orbis::SysResult orbis::sysIpmiSessionTrySendMessage(Thread *thread,
ptr<uint> result, uint kid,
ptr<void> params,
uint64_t paramsSz) {
struct SceIpmiClientTrySendArgs {
uint32_t queueIndex;
uint32_t padding;
ptr<std::byte> message;
uint64_t size;
};
static_assert(sizeof(SceIpmiClientTrySendArgs) == 0x18);
if (paramsSz != sizeof(SceIpmiClientTrySendArgs)) {
return ErrorCode::INVAL;
}
auto session = g_context->ipmiMap.get(kid).cast<IpmiSession>();
if (session == nullptr) {
return ErrorCode::INVAL;
}
SceIpmiClientTrySendArgs _params;
ORBIS_RET_ON_ERROR(uread(_params, ptr<SceIpmiClientTrySendArgs>(params)));
std::lock_guard lock(session->mutex);
if (session->client == nullptr) {
return ErrorCode::INVAL;
}
auto client = session->client;
std::lock_guard lockClient(client->mutex);
if (_params.queueIndex >= client->messageQueues.size()) {
return ErrorCode::INVAL;
}
auto &queue = client->messageQueues[_params.queueIndex];
auto &message = queue.messages.emplace_back();
message.resize(_params.size);
ORBIS_RET_ON_ERROR(ureadRaw(message.data(), _params.message, _params.size));
queue.messageCv.notify_all(client->mutex);
return uwrite<uint>(result, 0);
}
orbis::SysResult orbis::sysIpmiClientDisconnect(Thread *thread,
ptr<uint> result, uint kid,
ptr<void> params,
uint64_t paramsSz) {
struct SceIpmiClientDisconnectArgs {
ptr<sint> status;
};
if (paramsSz != sizeof(SceIpmiClientDisconnectArgs)) {
return ErrorCode::INVAL;
}
auto client = g_context->ipmiMap.get(kid).cast<IpmiClient>();
if (client == nullptr) {
return ErrorCode::INVAL;
}
SceIpmiClientDisconnectArgs _params;
ORBIS_RET_ON_ERROR(uread(_params, ptr<SceIpmiClientDisconnectArgs>(params)));
ORBIS_LOG_ERROR(__FUNCTION__, client->name, _params.status);
ORBIS_RET_ON_ERROR(uwrite(_params.status, 0));
return uwrite<uint>(result, 0);
}
orbis::SysResult orbis::sysIpmiSessionGetClientPid(Thread *thread,
ptr<uint> result, uint kid,
ptr<void> params,
uint64_t paramsSz) {
struct IpmiGetClientPidParams {
ptr<uint32_t> pid;
};
if (paramsSz != sizeof(IpmiGetClientPidParams)) {
return ErrorCode::INVAL;
}
auto session = g_context->ipmiMap.get(kid).cast<IpmiSession>();
if (session == nullptr) {
return ErrorCode::INVAL;
}
IpmiGetClientPidParams _params;
ORBIS_RET_ON_ERROR(uread(_params, ptr<IpmiGetClientPidParams>(params)));
ORBIS_RET_ON_ERROR(
uwrite<uint32_t>(_params.pid, session->client->process->pid));
return uwrite<uint>(result, 0);
}
orbis::SysResult
orbis::sysIpmiClientInvokeSyncMethod(Thread *thread, ptr<uint> result, uint kid,
ptr<void> params, uint64_t paramsSz) {
if (paramsSz != sizeof(IpmiSyncCallParams)) {
return ErrorCode::INVAL;
}
IpmiSyncCallParams _params;
ORBIS_RET_ON_ERROR(uread(_params, (ptr<IpmiSyncCallParams>)params));
if (_params.flags > 1) {
return ErrorCode::INVAL;
}
auto client = g_context->ipmiMap.get(kid).cast<IpmiClient>();
if (client == nullptr) {
return ErrorCode::INVAL;
}
auto session = client->session;
if (session == nullptr) {
return ErrorCode::INVAL;
}
std::lock_guard sessionLock(session->mutex);
auto server = session->server;
if (server == nullptr) {
return ErrorCode::INVAL;
}
{
std::lock_guard serverLock(server->mutex);
std::size_t inSize = 0;
for (auto &data : std::span(_params.pInData, _params.numInData)) {
inSize += data.size;
}
auto headerSize = sizeof(IpmiSyncMessageHeader) + inSize +
_params.numInData * sizeof(uint32_t);
auto size = headerSize + _params.numOutData * sizeof(uint);
kvector<std::byte> message(size);
auto msg = new (message.data()) IpmiSyncMessageHeader;
msg->sessionImpl = session->sessionImpl;
msg->pid = thread->tproc->pid;
msg->methodId = _params.method;
msg->numInData = _params.numInData;
msg->numOutData = _params.numOutData;
auto bufLoc = std::bit_cast<char *>(msg + 1);
for (auto &data : std::span(_params.pInData, _params.numInData)) {
*std::bit_cast<uint32_t *>(bufLoc) = data.size;
bufLoc += sizeof(uint32_t);
ORBIS_RET_ON_ERROR(ureadRaw(bufLoc, data.data, data.size));
bufLoc += data.size;
}
for (auto &data : std::span(_params.pOutData, _params.numOutData)) {
*std::bit_cast<uint32_t *>(bufLoc) = data.capacity;
bufLoc += sizeof(uint32_t);
}
uint type = 0x41;
if ((_params.flags & 1) == 0) {
type |= 0x10;
}
if (server->pid == thread->tproc->pid) {
type |= 0x8000;
}
server->packets.push_back(
{{.inputSize = headerSize, .type = type, .clientKid = kid},
thread->tid,
session,
std::move(message)});
server->receiveCv.notify_one(server->mutex);
}
IpmiSession::SyncResponse response;
while (true) {
{
orbis::scoped_unblock unblock;
session->responseCv.wait(session->mutex);
}
bool found = false;
for (auto it = session->syncResponses.begin();
it != session->syncResponses.end(); ++it) {
if (it->callerTid != thread->tid) {
continue;
}
response = std::move(*it);
session->syncResponses.erase(it);
found = true;
break;
}
if (found) {
break;
}
}
ORBIS_RET_ON_ERROR(uwrite(_params.pResult, response.errorCode));
if (response.data.size() != _params.numOutData) {
ORBIS_LOG_ERROR(__FUNCTION__, "responses amount mismatch",
response.data.size(), _params.numOutData);
}
for (std::size_t i = 0; i < response.data.size(); ++i) {
if (response.data.size() > _params.numOutData) {
ORBIS_LOG_ERROR(__FUNCTION__, "too many responses", response.data.size(),
_params.numOutData);
break;
}
IpmiBufferInfo _outData;
ORBIS_RET_ON_ERROR(uread(_outData, _params.pOutData + i));
auto &data = response.data[i];
if (_outData.capacity < data.size()) {
ORBIS_LOG_ERROR(__FUNCTION__, "too big response", _outData.capacity,
data.size());
continue;
}
// ORBIS_LOG_ERROR(__FUNCTION__, server->name, i, _outData.data,
// _outData.capacity,
// data.size());
_outData.size = data.size();
ORBIS_RET_ON_ERROR(uwriteRaw(_outData.data, data.data(), data.size()));
ORBIS_RET_ON_ERROR(uwrite(_params.pOutData + i, _outData));
}
return uwrite<uint>(result, 0);
}
orbis::SysResult orbis::sysIpmiClientConnect(Thread *thread, ptr<uint> result,
uint kid, ptr<void> params,
uint64_t paramsSz) {
if (paramsSz != sizeof(IpmiClientConnectParams)) {
return ErrorCode::INVAL;
}
auto client = g_context->ipmiMap.get(kid).cast<IpmiClient>();
if (client == nullptr) {
return ErrorCode::INVAL;
}
if (client->session != nullptr) {
return ErrorCode::EXIST;
}
IpmiClientConnectParams _params;
ORBIS_RET_ON_ERROR(uread(_params, ptr<IpmiClientConnectParams>(params)));
auto server = g_context->findIpmiServer(client->name);
if (server == nullptr) {
return SysResult::notAnError(ErrorCode::NOENT);
}
std::lock_guard clientLock(client->mutex);
decltype(server->connectionRequests)::iterator requestIt;
{
std::lock_guard serverLock(server->mutex);
for (auto &connReq : server->connectionRequests) {
if (connReq.client == client) {
return ErrorCode::EXIST;
}
}
server->connectionRequests.push_front({
.client = client,
.clientTid = thread->tid,
.clientPid = thread->tproc->pid,
});
requestIt = server->connectionRequests.begin();
struct QueueStats {
uint maxOutstanding;
uint unk;
ulong inDataSizeHardLimit;
ulong outDataSizeHardLimit;
};
struct ConnectMessageHeader {
uint32_t clientPid;
uint32_t clientKid;
QueueStats sync;
QueueStats async;
uint numEventFlag;
uint unk1;
uint numMsgQueue;
uint unk2;
ulong msgQueueSize[32];
ulong memorySize;
};
static_assert(sizeof(ConnectMessageHeader) == 0x150);
kvector<std::byte> message{
sizeof(ConnectMessageHeader) + sizeof(uint) +
std::max<std::size_t>(_params.userDataLen, 0x10)};
auto header = new (message.data()) ConnectMessageHeader{};
header->clientPid = thread->tproc->pid;
header->clientKid = kid;
header->sync.maxOutstanding = 1;
header->sync.inDataSizeHardLimit = 0x10000;
header->sync.outDataSizeHardLimit = 0x10000;
header->async.maxOutstanding = 8;
header->async.inDataSizeHardLimit = 0x10000;
header->async.outDataSizeHardLimit = 0x10000;
header->numEventFlag = client->eventFlags.size();
header->numMsgQueue = client->messageQueues.size();
for (auto &size : header->msgQueueSize) {
size = 0x10000;
}
if (_params.userDataLen != 0) {
auto bufLoc = std::bit_cast<char *>(header + 1);
*std::bit_cast<uint *>(bufLoc) = _params.userDataLen;
ORBIS_RET_ON_ERROR(ureadRaw(bufLoc + sizeof(uint), _params.userData,
_params.userDataLen));
}
server->packets.push_back({{
.inputSize = static_cast<ulong>(thread->tid),
.type = 1,
.clientKid = kid,
},
0,
nullptr,
std::move(message)});
server->receiveCv.notify_one(server->mutex);
}
while (client->session == nullptr && !client->connectionStatus) {
orbis::scoped_unblock unblock;
client->sessionCv.wait(client->mutex);
}
while (!client->connectionStatus) {
orbis::scoped_unblock unblock;
client->connectCv.wait(client->mutex);
}
ORBIS_RET_ON_ERROR(uwrite(_params.status, *client->connectionStatus));
{
std::lock_guard serverLock(server->mutex);
server->connectionRequests.erase(requestIt);
}
return uwrite(result, 0u);
}
orbis::SysResult orbis::sysIpmiSessionGetClientAppId(Thread *thread,
ptr<uint> result, uint kid,
ptr<void> params,
uint64_t paramsSz) {
struct IpmiGetUserDataParam {
ptr<uint> data;
};
if (paramsSz != sizeof(IpmiGetUserDataParam)) {
return ErrorCode::INVAL;
}
auto session = g_context->ipmiMap.get(kid).cast<IpmiSession>();
if (session == nullptr) {
return ErrorCode::INVAL;
}
IpmiGetUserDataParam _params;
ORBIS_RET_ON_ERROR(uread(_params, ptr<IpmiGetUserDataParam>(params)));
ORBIS_RET_ON_ERROR(
uwrite(_params.data, session->client->process->appInfo.appId));
return uwrite<uint>(result, 0);
}
orbis::SysResult orbis::sysIpmiSessionGetUserData(Thread *thread,
ptr<uint> result, uint kid,
ptr<void> params,
uint64_t paramsSz) {
struct IpmiGetUserDataParam {
ptr<ptr<void>> data;
};
if (paramsSz != sizeof(IpmiGetUserDataParam)) {
return ErrorCode::INVAL;
}
auto session = g_context->ipmiMap.get(kid).cast<IpmiSession>();
if (session == nullptr) {
return ErrorCode::INVAL;
}
IpmiGetUserDataParam _params;
ORBIS_RET_ON_ERROR(uread(_params, ptr<IpmiGetUserDataParam>(params)));
ORBIS_RET_ON_ERROR(uwrite(_params.data, session->userData));
return uwrite<uint>(result, 0);
}
orbis::SysResult orbis::sysIpmiServerGetName(Thread *thread, ptr<uint> result,
uint kid, ptr<void> params,
uint64_t paramsSz) {
struct IpmiGetServerNameParams {
ptr<char> name;
};
if (paramsSz != sizeof(IpmiGetServerNameParams)) {
return ErrorCode::INVAL;
}
auto server = g_context->ipmiMap.get(kid).cast<IpmiServer>();
if (server == nullptr) {
return ErrorCode::INVAL;
}
IpmiGetServerNameParams _param;
ORBIS_RET_ON_ERROR(uread(_param, ptr<IpmiGetServerNameParams>(params)));
ORBIS_RET_ON_ERROR(
uwriteRaw(_param.name, server->name.c_str(), server->name.size() + 1));
return uwrite<uint>(result, 0);
}
orbis::SysResult orbis::sysIpmiClientGetName(Thread *thread, ptr<uint> result,
uint kid, ptr<void> params,
uint64_t paramsSz) {
struct IpmiGetClientNameParams {
ptr<char> name;
};
if (paramsSz != sizeof(IpmiGetClientNameParams)) {
return ErrorCode::INVAL;
}
auto client = g_context->ipmiMap.get(kid).cast<IpmiClient>();
if (client == nullptr) {
return ErrorCode::INVAL;
}
IpmiGetClientNameParams _param;
ORBIS_RET_ON_ERROR(uread(_param, ptr<IpmiGetClientNameParams>(params)));
ORBIS_RET_ON_ERROR(
uwriteRaw(_param.name, client->name.c_str(), client->name.size() + 1));
return uwrite<uint>(result, 0);
}
orbis::SysResult orbis::sysIpmiClientWaitEventFlag(Thread *thread,
ptr<uint> result, uint kid,
ptr<void> params,
uint64_t paramsSz) {
struct IpmiWaitEventFlagParam {
uint32_t index;
uint32_t padding0;
uint64_t patternSet;
uint32_t mode;
uint32_t padding1;
ptr<uint64_t> pPatternSet;
ptr<uint32_t> pTimeout;
};
static_assert(sizeof(IpmiWaitEventFlagParam) == 0x28);
if (paramsSz != sizeof(IpmiWaitEventFlagParam)) {
return ErrorCode::INVAL;
}
IpmiWaitEventFlagParam _params;
ORBIS_RET_ON_ERROR(uread(_params, ptr<IpmiWaitEventFlagParam>(params)));
auto client = g_context->ipmiMap.get(kid).cast<IpmiClient>();
if (client == nullptr) {
return ErrorCode::INVAL;
}
if (client->eventFlags.size() <= _params.index) {
return ErrorCode::INVAL;
}
std::uint32_t resultTimeout{};
if (_params.pTimeout != nullptr) {
ORBIS_RET_ON_ERROR(uread(resultTimeout, _params.pTimeout));
}
auto &evf = client->eventFlags[_params.index];
ErrorCode waitResult;
{
orbis::scoped_unblock unblock;
waitResult = evf.wait(thread, _params.mode, _params.patternSet,
_params.pTimeout != 0 ? &resultTimeout : nullptr);
}
if (_params.pPatternSet != nullptr) {
ORBIS_RET_ON_ERROR(uwrite(_params.pPatternSet, thread->evfResultPattern));
}
ORBIS_RET_ON_ERROR(uwrite(result, 0u));
if (_params.pTimeout != nullptr) {
ORBIS_RET_ON_ERROR(uwrite(_params.pTimeout, resultTimeout));
}
if (waitResult == ErrorCode::TIMEDOUT) {
return SysResult::notAnError(ErrorCode::TIMEDOUT);
}
return waitResult;
}
orbis::SysResult orbis::sysIpmiClientPollEventFlag(Thread *thread,
ptr<uint> result, uint kid,
ptr<void> params,
uint64_t paramsSz) {
struct IpmiPollEventFlagParam {
uint32_t index;
uint32_t padding0;
uint64_t patternSet;
uint32_t mode;
uint32_t padding1;
ptr<uint64_t> pPatternSet;
};
static_assert(sizeof(IpmiPollEventFlagParam) == 0x20);
if (paramsSz != sizeof(IpmiPollEventFlagParam)) {
return ErrorCode::INVAL;
}
IpmiPollEventFlagParam _params;
ORBIS_RET_ON_ERROR(uread(_params, ptr<IpmiPollEventFlagParam>(params)));
auto client = g_context->ipmiMap.get(kid).cast<IpmiClient>();
if (client == nullptr) {
return ErrorCode::INVAL;
}
if (client->eventFlags.size() <= _params.index) {
return ErrorCode::INVAL;
}
uint64_t patternSet;
ORBIS_RET_ON_ERROR(uread(patternSet, _params.pPatternSet));
auto &evf = client->eventFlags[_params.index];
auto waitResult = evf.tryWait(thread, _params.mode, patternSet);
ORBIS_RET_ON_ERROR(uread(patternSet, _params.pPatternSet));
ORBIS_RET_ON_ERROR(uwrite(_params.pPatternSet, thread->evfResultPattern));
ORBIS_RET_ON_ERROR(uwrite(result, 0u));
return SysResult::notAnError(waitResult);
}
orbis::SysResult orbis::sysIpmiSessionSetEventFlag(Thread *thread,
ptr<uint> result, uint kid,
ptr<void> params,
uint64_t paramsSz) {
struct IpmiSetEventFlagParam {
uint32_t index;
uint32_t padding;
uint64_t patternSet;
};
static_assert(sizeof(IpmiSetEventFlagParam) == 0x10);
if (paramsSz != sizeof(IpmiSetEventFlagParam)) {
return ErrorCode::INVAL;
}
IpmiSetEventFlagParam _params;
ORBIS_RET_ON_ERROR(uread(_params, ptr<IpmiSetEventFlagParam>(params)));
auto session = g_context->ipmiMap.get(kid).cast<IpmiSession>();
if (session == nullptr) {
return ErrorCode::INVAL;
}
auto client = session->client;
if (client == nullptr) {
return ErrorCode::INVAL;
}
if (client->eventFlags.size() <= _params.index) {
return ErrorCode::INVAL;
}
auto &evf = client->eventFlags[_params.index];
evf.set(_params.patternSet);
return uwrite<uint>(result, 0);
}