diff --git a/orbis-kernel/include/orbis/ipmi.hpp b/orbis-kernel/include/orbis/ipmi.hpp index 631f4da..18830ea 100644 --- a/orbis-kernel/include/orbis/ipmi.hpp +++ b/orbis-kernel/include/orbis/ipmi.hpp @@ -55,6 +55,7 @@ struct IpmiClient : RcBase { ptr userData; Ref session; shared_mutex mutex; + shared_cv sessionCv; sint pid; explicit IpmiClient(kstring name) : name(std::move(name)) {} @@ -74,7 +75,10 @@ struct IpmiSession : RcBase { shared_cv responseCv; kdeque messageResponses; EventFlag evf{0, 0}; - bool connection = false; // TODO: implement more states + shared_cv connectCv; + bool expectedOutput = false; // TODO: verify + bool connected = false; // TODO: implement more states + sint connectionStatus{0}; }; struct IpmiCreateServerConfig { @@ -116,7 +120,7 @@ SysResult sysIpmiDestroySession(Thread *thread, ptr result, uint kid, SysResult sysIpmiServerReceivePacket(Thread *thread, ptr result, uint kid, ptr params, uint64_t paramsSz); -SysResult sysIpmiSessionConnectResult(Thread *thread, ptr result, +SysResult sysIpmiSendConnectResult(Thread *thread, ptr result, uint kid, ptr params, uint64_t paramsSz); SysResult sysIpmiSessionRespondSync(Thread *thread, ptr result, uint kid, @@ -128,6 +132,8 @@ SysResult sysIpmiClientInvokeSyncMethod(Thread *thread, ptr result, uint64_t paramsSz); SysResult sysIpmiClientConnect(Thread *thread, ptr result, uint kid, ptr params, uint64_t paramsSz); +SysResult sysIpmiSessionGetUserData(Thread *thread, ptr result, uint kid, + ptr params, uint64_t paramsSz); SysResult sysIpmiServerGetName(Thread *thread, ptr result, uint kid, ptr params, uint64_t paramsSz); } // namespace orbis diff --git a/orbis-kernel/src/ipmi.cpp b/orbis-kernel/src/ipmi.cpp index c7215d1..774edf1 100644 --- a/orbis-kernel/src/ipmi.cpp +++ b/orbis-kernel/src/ipmi.cpp @@ -1,6 +1,24 @@ #include "ipmi.hpp" #include "KernelContext.hpp" #include "thread/Process.hpp" +#include "utils/Logs.hpp" + +namespace orbis { +struct IpmiBufferInfo { + ptr data; + uint64_t size; +}; + +static_assert(sizeof(IpmiBufferInfo) == 0x10); + +struct IpmiDataInfo { + ptr data; + uint64_t size; + uint64_t capacity; //? +}; + +static_assert(sizeof(IpmiDataInfo) == 0x18); +} // namespace orbis orbis::ErrorCode orbis::ipmiCreateClient(Thread *thread, void *clientImpl, const char *name, void *config, @@ -66,6 +84,7 @@ orbis::ErrorCode orbis::ipmiCreateSession(Thread *thread, void *sessionImpl, session->client = conReq.client; session->server = server; conReq.client->session = session; + conReq.client->sessionCv.notify_all(conReq.client->mutex); return {}; } @@ -79,9 +98,9 @@ orbis::SysResult orbis::sysIpmiCreateClient(Thread *thread, ptr result, ptr params, uint64_t paramsSz) { struct IpmiCreateClientParams { - orbis::ptr clientImpl; - orbis::ptr name; - orbis::ptr config; // FIXME: describe + ptr clientImpl; + ptr name; + ptr config; // FIXME: describe }; static_assert(sizeof(IpmiCreateClientParams) == 0x18); @@ -105,6 +124,7 @@ orbis::SysResult orbis::sysIpmiCreateClient(Thread *thread, ptr result, return ErrorCode::MFILE; } + ORBIS_LOG_ERROR(__FUNCTION__, kid, _name); return uwrite(result, kid); } @@ -112,9 +132,9 @@ orbis::SysResult orbis::sysIpmiCreateServer(Thread *thread, ptr result, ptr params, uint64_t paramsSz) { struct IpmiCreateServerParams { - orbis::ptr serverImpl; - orbis::ptr name; - orbis::ptr config; + ptr serverImpl; + ptr name; + ptr config; }; static_assert(sizeof(IpmiCreateServerParams) == 0x18); @@ -139,6 +159,7 @@ orbis::SysResult orbis::sysIpmiCreateServer(Thread *thread, ptr result, return ErrorCode::MFILE; } + ORBIS_LOG_ERROR(__FUNCTION__, kid, _name); return uwrite(result, kid); } @@ -183,6 +204,7 @@ orbis::SysResult orbis::sysIpmiCreateSession(Thread *thread, ptr result, return ErrorCode::MFILE; } + ORBIS_LOG_ERROR(__FUNCTION__, kid); return uwrite(result, kid); } @@ -206,38 +228,474 @@ orbis::SysResult orbis::sysIpmiServerReceivePacket(Thread *thread, ptr result, uint kid, ptr params, uint64_t paramsSz) { - return ErrorCode::NOSYS; + struct IpmiServerReceivePacketParams { + ptr buffer; + uint64_t bufferSize; + ptr receivePacketInfo; + ptr unk; + }; + + IpmiServerReceivePacketParams _params; + + ORBIS_RET_ON_ERROR( + uread(_params, ptr(params))); + + auto server = thread->tproc->ipmiMap.get(kid).cast(); + + if (server == nullptr) { + return ErrorCode::INVAL; + } + + IpmiServer::Packet _packet; + + ORBIS_LOG_ERROR(__FUNCTION__, server->name, ": waiting for packet"); + { + std::lock_guard lock(server->mutex); + while (server->packets.empty()) { + server->receiveCv.wait(server->mutex); + } + + _packet = std::move(server->packets.front()); + server->packets.pop_front(); + } + ORBIS_LOG_ERROR(__FUNCTION__, server->name, ": got packet"); + + if (_packet.info.type == 0x1) { + // on connection packet + + for (auto &conn : server->connectionRequests) { + if ((ptr)conn.clientTid == _packet.info.userData) { + conn.serverTid = thread->tid; + _packet.info.userData = nullptr; + break; + } + } + } + + ORBIS_RET_ON_ERROR(uwriteRaw((ptr)_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(params), _params)); + + return uwrite(result, 0); } -orbis::SysResult orbis::sysIpmiSessionConnectResult(Thread *thread, - ptr result, uint kid, - ptr params, - uint64_t paramsSz) { - return ErrorCode::NOSYS; +orbis::SysResult orbis::sysIpmiSendConnectResult(Thread *thread, + ptr result, uint kid, + ptr params, + uint64_t paramsSz) { + if (paramsSz != sizeof(sint)) { + ORBIS_LOG_ERROR(__FUNCTION__, "wrong param size"); + return ErrorCode::INVAL; + } + + sint status; + ORBIS_RET_ON_ERROR(uread(status, ptr(params))); + + ORBIS_LOG_NOTICE(__FUNCTION__, kid, status); + return uwrite(result, 0u); } orbis::SysResult orbis::sysIpmiSessionRespondSync(Thread *thread, ptr result, uint kid, ptr params, uint64_t paramsSz) { - return ErrorCode::NOSYS; + struct IpmiRespondParams { + sint errorCode; + uint32_t unk1; + ptr buffers; + uint32_t bufferCount; + uint32_t padding; + }; + + static_assert(sizeof(IpmiRespondParams) == 0x18); + if (paramsSz != sizeof(IpmiRespondParams)) { + return ErrorCode::INVAL; + } + + auto session = thread->tproc->ipmiMap.get(kid).cast(); + + if (session == nullptr) { + return ErrorCode::INVAL; + } + + IpmiRespondParams _params; + ORBIS_RET_ON_ERROR(uread(_params, ptr(params))); + + if (_params.bufferCount > 1) { + ORBIS_LOG_ERROR(__FUNCTION__, "unexpected buffers count"); + return ErrorCode::INVAL; + } + + ORBIS_LOG_ERROR(__FUNCTION__, session->client->name); + + kvector data; + + if (_params.errorCode == 0 && _params.bufferCount > 0 && + session->expectedOutput) { + IpmiBufferInfo _buffer; + ORBIS_RET_ON_ERROR(uread(_buffer, _params.buffers)); + + data.resize(_buffer.size); + ORBIS_RET_ON_ERROR(ureadRaw(data.data(), _buffer.data, _buffer.size)); + } + + std::lock_guard lock(session->mutex); + + session->messageResponses.push_front({ + .errorCode = _params.errorCode, + .data = std::move(data), + }); + + session->responseCv.notify_one(session->mutex); + return uwrite(result, 0u); } + orbis::SysResult orbis::sysIpmiSessionGetClientPid(Thread *thread, ptr result, uint kid, ptr params, uint64_t paramsSz) { - return ErrorCode::NOSYS; + struct IpmiGetClientPidParams { + ptr pid; + }; + + if (paramsSz != sizeof(IpmiGetClientPidParams)) { + return ErrorCode::INVAL; + } + + auto session = thread->tproc->ipmiMap.get(kid).cast(); + + if (session == nullptr) { + return ErrorCode::INVAL; + } + + IpmiGetClientPidParams _params; + ORBIS_RET_ON_ERROR(uread(_params, ptr(params))); + ORBIS_RET_ON_ERROR(uwrite(_params.pid, session->client->pid)); + return uwrite(result, 0); } orbis::SysResult orbis::sysIpmiClientInvokeSyncMethod(Thread *thread, ptr result, uint kid, ptr params, uint64_t paramsSz) { - return ErrorCode::NOSYS; + struct IpmiSyncCallParams { + uint32_t method; + uint32_t numInData; + uint32_t numOutData; + uint32_t unk; + ptr pInData; + ptr pOutData; + ptr pResult; + uint32_t flags; + }; + + static_assert(sizeof(IpmiSyncCallParams) == 0x30); + + struct MessageHeader { + ptr sessionImpl; + uint pid; + uint methodId; + uint numInData; + uint numOutData; + }; + + static_assert(sizeof(MessageHeader) == 0x18); + + if (paramsSz != sizeof(IpmiSyncCallParams)) { + return ErrorCode::INVAL; + } + + IpmiSyncCallParams _params; + ORBIS_RET_ON_ERROR(uread(_params, (ptr)params)); + + if (_params.flags > 1) { + return ErrorCode::INVAL; + } + + auto client = thread->tproc->ipmiMap.get(kid).cast(); + + if (client == nullptr) { + return ErrorCode::INVAL; + } + + std::lock_guard clientLock(client->mutex); + auto session = client->session; + + if (session == nullptr) { + ORBIS_LOG_TODO(__FUNCTION__, "waiting for connection", client->name, + _params.method); + + while (session == nullptr) { + client->sessionCv.wait(client->mutex); + session = client->session; + } + } + + ORBIS_LOG_ERROR(__FUNCTION__, client->name, "sync call", _params.method); + + std::lock_guard sessionLock(session->mutex); + auto server = session->server; + + if (server == nullptr) { + return ErrorCode::INVAL; + } + + { + std::lock_guard serverLock(server->mutex); + + // ORBIS_LOG_TODO("IPMI: invokeSyncMethod", client->name, _params.method, + // _params.numInData, _params.unk, _params.numOutData, + // _params.pInData, _params.pOutData, _params.pResult, + // _params.flags); + + std::size_t inSize = 0; + for (auto &data : std::span(_params.pInData, _params.numInData)) { + inSize += data.size; + } + + std::size_t outSize = 0; + for (auto &data : std::span(_params.pOutData, _params.numOutData)) { + outSize += data.size; + } + + auto size = sizeof(MessageHeader) + inSize + outSize + + _params.numInData * sizeof(uint32_t) + + _params.numOutData * sizeof(uint32_t); + + kvector message(size); + auto msg = new (message.data()) MessageHeader; + msg->sessionImpl = session->sessionImpl; + msg->pid = thread->tproc->pid; + msg->methodId = _params.method; + msg->numInData = _params.numInData; + msg->numOutData = _params.numOutData; + + ORBIS_LOG_TODO("IPMI: sync call", client->name, _params.method, + thread->tproc->pid); + thread->where(); + + auto bufLoc = std::bit_cast(msg + 1); + + for (auto &data : std::span(_params.pInData, _params.numInData)) { + *std::bit_cast(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(bufLoc) = data.size; + bufLoc += sizeof(uint32_t) + data.size; + } + + uint type = 0x41; + + if (_params.numInData == 1 && _params.numOutData == 1 && + server->pid == thread->tproc->pid) { + type |= 0x10; + } + + if ((_params.flags & 1) == 0) { + type |= 0x8000; + } + + session->expectedOutput = _params.numOutData > 0; + server->packets.push_back( + {{.type = type, .clientKid = kid}, std::move(message)}); + server->receiveCv.notify_one(server->mutex); + } + + while (session->messageResponses.empty()) { + session->responseCv.wait(session->mutex); + } + + auto response = std::move(session->messageResponses.front()); + session->messageResponses.pop_front(); + + ORBIS_RET_ON_ERROR(uwrite(_params.pResult, response.errorCode)); + if (_params.numOutData > 0 && _params.pOutData->size < response.data.size()) { + return ErrorCode::INVAL; + } + + if (_params.numOutData && _params.pOutData->size) { + ORBIS_RET_ON_ERROR(uwriteRaw(_params.pOutData->data, response.data.data(), + response.data.size())); + _params.pOutData->size = response.data.size(); + } + + ORBIS_LOG_TODO(__FUNCTION__, "sync message response", client->name, _params.method, + response.errorCode, response.data.size()); + return uwrite(result, 0); } + orbis::SysResult orbis::sysIpmiClientConnect(Thread *thread, ptr result, uint kid, ptr params, uint64_t paramsSz) { - return ErrorCode::NOSYS; + struct IpmiClientConnectParams { + ptr arg0; + ptr arg1; + ptr status; + ptr arg3; + }; + + static_assert(sizeof(IpmiClientConnectParams) == 0x20); + + if (paramsSz != sizeof(IpmiClientConnectParams)) { + return ErrorCode::INVAL; + } + + auto client = thread->tproc->ipmiMap.get(kid).cast(); + + if (client == nullptr) { + return ErrorCode::INVAL; + } + + if (client->session != nullptr) { + return ErrorCode::EXIST; + } + + IpmiClientConnectParams _params; + ORBIS_RET_ON_ERROR(uread(_params, ptr(params))); + + ORBIS_LOG_ERROR(__FUNCTION__, client->name, "connect"); + + auto server = g_context.findIpmiServer(client->name); + + if (server == nullptr) { + ORBIS_LOG_ERROR(__FUNCTION__, "waiting for server", client->name); + + while (server == nullptr) { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + server = g_context.findIpmiServer(client->name); + } + } + + 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 pid; + uint32_t unk0; + QueueStats sync; + QueueStats async; + uint numEventFlag; + uint unk1; + uint numMsgQueue; + uint unk2; + ulong msgQueueSize[32]; + ulong memorySize; + }; + + static_assert(sizeof(ConnectMessageHeader) == 0x150); + + struct ConnectFields { + uint unk0; + uint unk1; + }; + + kvector message{sizeof(ConnectMessageHeader) + + sizeof(ConnectFields)}; + auto header = new (message.data()) ConnectMessageHeader{}; + header->pid = thread->tproc->pid; + + server->packets.push_back( + {{ + .userData = (ptr)static_cast(thread->tid), + .type = 1, + .clientKid = kid, + }, + std::move(message)}); + + server->receiveCv.notify_one(server->mutex); + } + + ORBIS_LOG_ERROR(__FUNCTION__, client->name, "connect: packet sent"); + + while (client->session == nullptr) { + client->sessionCv.wait(client->mutex); + } + + ORBIS_LOG_ERROR(__FUNCTION__, client->name, "connect: session created"); + ORBIS_RET_ON_ERROR(uwrite(_params.status, 0)); // TODO + + { + std::lock_guard serverLock(server->mutex); + server->connectionRequests.erase(requestIt); + } + return uwrite(result, 0u); } + +orbis::SysResult orbis::sysIpmiSessionGetUserData(Thread *thread, + ptr result, uint kid, + ptr params, + uint64_t paramsSz) { + struct IpmiGetUserDataParam { + ptr> data; + }; + + if (paramsSz != sizeof(IpmiGetUserDataParam)) { + return ErrorCode::INVAL; + } + + auto session = + dynamic_cast(thread->tproc->ipmiMap.get(kid).get()); + + if (session == nullptr) { + return ErrorCode::INVAL; + } + + IpmiGetUserDataParam _params; + ORBIS_RET_ON_ERROR(uread(_params, ptr(params))); + ORBIS_RET_ON_ERROR(uwrite(_params.data, session->userData)); + return uwrite(result, 0); +} + orbis::SysResult orbis::sysIpmiServerGetName(Thread *thread, ptr result, uint kid, ptr params, uint64_t paramsSz) { - return ErrorCode::NOSYS; + struct IpmiGetServerNameParams { + ptr name; + }; + + if (paramsSz != sizeof(IpmiGetServerNameParams)) { + return ErrorCode::INVAL; + } + + auto server = thread->tproc->ipmiMap.get(kid).cast(); + + if (server == nullptr) { + return ErrorCode::INVAL; + } + + IpmiGetServerNameParams _param; + ORBIS_RET_ON_ERROR(uread(_param, ptr(params))); + ORBIS_RET_ON_ERROR( + uwriteRaw(_param.name, server->name.c_str(), server->name.size() + 1)); + + return uwrite(result, 0); } diff --git a/orbis-kernel/src/sys/sys_sce.cpp b/orbis-kernel/src/sys/sys_sce.cpp index 5761aa9..3d76e82 100644 --- a/orbis-kernel/src/sys/sys_sce.cpp +++ b/orbis-kernel/src/sys/sys_sce.cpp @@ -1027,7 +1027,8 @@ orbis::SysResult orbis::sys_suspend_system(Thread *thread /* TODO */) { orbis::SysResult orbis::sys_ipmimgr_call(Thread *thread, uint op, uint kid, ptr result, ptr params, uint64_t paramsSz) { - ORBIS_LOG_TODO(__FUNCTION__, thread->tid, op, kid, result, params, paramsSz); + // ORBIS_LOG_TODO(__FUNCTION__, thread->tid, op, kid, result, params, paramsSz); + switch (op) { case 0: return sysIpmiCreateServer(thread, result, params, paramsSz); @@ -1046,7 +1047,7 @@ orbis::SysResult orbis::sys_ipmimgr_call(Thread *thread, uint op, uint kid, case 0x201: return sysIpmiServerReceivePacket(thread, result, kid, params, paramsSz); case 0x212: - return sysIpmiSessionConnectResult(thread, result, kid, params, paramsSz); + return sysIpmiSendConnectResult(thread, result, kid, params, paramsSz); case 0x232: return sysIpmiSessionRespondSync(thread, result, kid, params, paramsSz); case 0x302: @@ -1055,6 +1056,8 @@ orbis::SysResult orbis::sys_ipmimgr_call(Thread *thread, uint op, uint kid, return sysIpmiClientInvokeSyncMethod(thread, result, kid, params, paramsSz); case 0x400: return sysIpmiClientConnect(thread, result, kid, params, paramsSz); + case 0x468: + return sysIpmiSessionGetUserData(thread, result, kid, params, paramsSz); case 0x46a: return sysIpmiServerGetName(thread, result, kid, params, paramsSz); }