[orbis-kernel] ipmi: implement more operations

Implement receive packet, connect, connect result, invoke sync method, get client pid, respond sync
This commit is contained in:
DH 2023-11-11 16:59:58 +03:00
parent 60c3077cd6
commit c62c853814
3 changed files with 488 additions and 21 deletions

View File

@ -55,6 +55,7 @@ struct IpmiClient : RcBase {
ptr<void> userData;
Ref<IpmiSession> 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<MessageResponse> 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<uint> result, uint kid,
SysResult sysIpmiServerReceivePacket(Thread *thread, ptr<uint> result, uint kid,
ptr<void> params, uint64_t paramsSz);
SysResult sysIpmiSessionConnectResult(Thread *thread, ptr<uint> result,
SysResult sysIpmiSendConnectResult(Thread *thread, ptr<uint> result,
uint kid, ptr<void> params,
uint64_t paramsSz);
SysResult sysIpmiSessionRespondSync(Thread *thread, ptr<uint> result, uint kid,
@ -128,6 +132,8 @@ SysResult sysIpmiClientInvokeSyncMethod(Thread *thread, ptr<uint> result,
uint64_t paramsSz);
SysResult sysIpmiClientConnect(Thread *thread, ptr<uint> result, uint kid,
ptr<void> params, uint64_t paramsSz);
SysResult sysIpmiSessionGetUserData(Thread *thread, ptr<uint> result, uint kid,
ptr<void> params, uint64_t paramsSz);
SysResult sysIpmiServerGetName(Thread *thread, ptr<uint> result, uint kid,
ptr<void> params, uint64_t paramsSz);
} // namespace orbis

View File

@ -1,6 +1,24 @@
#include "ipmi.hpp"
#include "KernelContext.hpp"
#include "thread/Process.hpp"
#include "utils/Logs.hpp"
namespace orbis {
struct IpmiBufferInfo {
ptr<void> data;
uint64_t size;
};
static_assert(sizeof(IpmiBufferInfo) == 0x10);
struct IpmiDataInfo {
ptr<void> 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<uint> result,
ptr<void> params,
uint64_t paramsSz) {
struct IpmiCreateClientParams {
orbis::ptr<void> clientImpl;
orbis::ptr<const char> name;
orbis::ptr<void> config; // FIXME: describe
ptr<void> clientImpl;
ptr<const char> name;
ptr<void> config; // FIXME: describe
};
static_assert(sizeof(IpmiCreateClientParams) == 0x18);
@ -105,6 +124,7 @@ orbis::SysResult orbis::sysIpmiCreateClient(Thread *thread, ptr<uint> result,
return ErrorCode::MFILE;
}
ORBIS_LOG_ERROR(__FUNCTION__, kid, _name);
return uwrite<uint>(result, kid);
}
@ -112,9 +132,9 @@ orbis::SysResult orbis::sysIpmiCreateServer(Thread *thread, ptr<uint> result,
ptr<void> params,
uint64_t paramsSz) {
struct IpmiCreateServerParams {
orbis::ptr<void> serverImpl;
orbis::ptr<const char> name;
orbis::ptr<IpmiCreateServerConfig> config;
ptr<void> serverImpl;
ptr<const char> name;
ptr<IpmiCreateServerConfig> config;
};
static_assert(sizeof(IpmiCreateServerParams) == 0x18);
@ -139,6 +159,7 @@ orbis::SysResult orbis::sysIpmiCreateServer(Thread *thread, ptr<uint> result,
return ErrorCode::MFILE;
}
ORBIS_LOG_ERROR(__FUNCTION__, kid, _name);
return uwrite<uint>(result, kid);
}
@ -183,6 +204,7 @@ orbis::SysResult orbis::sysIpmiCreateSession(Thread *thread, ptr<uint> result,
return ErrorCode::MFILE;
}
ORBIS_LOG_ERROR(__FUNCTION__, kid);
return uwrite<uint>(result, kid);
}
@ -206,38 +228,474 @@ orbis::SysResult orbis::sysIpmiServerReceivePacket(Thread *thread,
ptr<uint> result, uint kid,
ptr<void> params,
uint64_t paramsSz) {
return ErrorCode::NOSYS;
struct IpmiServerReceivePacketParams {
ptr<void> buffer;
uint64_t bufferSize;
ptr<IpmiServer::IpmiPacketInfo> receivePacketInfo;
ptr<uint> unk;
};
IpmiServerReceivePacketParams _params;
ORBIS_RET_ON_ERROR(
uread(_params, ptr<IpmiServerReceivePacketParams>(params)));
auto server = thread->tproc->ipmiMap.get(kid).cast<IpmiServer>();
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<void>)conn.clientTid == _packet.info.userData) {
conn.serverTid = thread->tid;
_packet.info.userData = nullptr;
break;
}
}
}
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::sysIpmiSessionConnectResult(Thread *thread,
ptr<uint> result, uint kid,
ptr<void> params,
uint64_t paramsSz) {
return ErrorCode::NOSYS;
orbis::SysResult orbis::sysIpmiSendConnectResult(Thread *thread,
ptr<uint> result, uint kid,
ptr<void> 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<sint>(params)));
ORBIS_LOG_NOTICE(__FUNCTION__, kid, status);
return uwrite(result, 0u);
}
orbis::SysResult orbis::sysIpmiSessionRespondSync(Thread *thread,
ptr<uint> result, uint kid,
ptr<void> params,
uint64_t paramsSz) {
return ErrorCode::NOSYS;
struct IpmiRespondParams {
sint errorCode;
uint32_t unk1;
ptr<IpmiBufferInfo> 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<IpmiSession>();
if (session == nullptr) {
return ErrorCode::INVAL;
}
IpmiRespondParams _params;
ORBIS_RET_ON_ERROR(uread(_params, ptr<IpmiRespondParams>(params)));
if (_params.bufferCount > 1) {
ORBIS_LOG_ERROR(__FUNCTION__, "unexpected buffers count");
return ErrorCode::INVAL;
}
ORBIS_LOG_ERROR(__FUNCTION__, session->client->name);
kvector<std::byte> 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<uint> result, uint kid,
ptr<void> params,
uint64_t paramsSz) {
return ErrorCode::NOSYS;
struct IpmiGetClientPidParams {
ptr<uint32_t> pid;
};
if (paramsSz != sizeof(IpmiGetClientPidParams)) {
return ErrorCode::INVAL;
}
auto session = thread->tproc->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->pid));
return uwrite<uint>(result, 0);
}
orbis::SysResult
orbis::sysIpmiClientInvokeSyncMethod(Thread *thread, ptr<uint> result, uint kid,
ptr<void> params, uint64_t paramsSz) {
return ErrorCode::NOSYS;
struct IpmiSyncCallParams {
uint32_t method;
uint32_t numInData;
uint32_t numOutData;
uint32_t unk;
ptr<IpmiBufferInfo> pInData;
ptr<IpmiDataInfo> pOutData;
ptr<sint> pResult;
uint32_t flags;
};
static_assert(sizeof(IpmiSyncCallParams) == 0x30);
struct MessageHeader {
ptr<void> 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<IpmiSyncCallParams>)params));
if (_params.flags > 1) {
return ErrorCode::INVAL;
}
auto client = thread->tproc->ipmiMap.get(kid).cast<IpmiClient>();
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<std::byte> 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<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.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<uint>(result, 0);
}
orbis::SysResult orbis::sysIpmiClientConnect(Thread *thread, ptr<uint> result,
uint kid, ptr<void> params,
uint64_t paramsSz) {
return ErrorCode::NOSYS;
struct IpmiClientConnectParams {
ptr<void> arg0;
ptr<void> arg1;
ptr<sint> status;
ptr<void> arg3;
};
static_assert(sizeof(IpmiClientConnectParams) == 0x20);
if (paramsSz != sizeof(IpmiClientConnectParams)) {
return ErrorCode::INVAL;
}
auto client = thread->tproc->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)));
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<std::byte> message{sizeof(ConnectMessageHeader) +
sizeof(ConnectFields)};
auto header = new (message.data()) ConnectMessageHeader{};
header->pid = thread->tproc->pid;
server->packets.push_back(
{{
.userData = (ptr<void>)static_cast<ulong>(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<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 =
dynamic_cast<IpmiSession *>(thread->tproc->ipmiMap.get(kid).get());
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) {
return ErrorCode::NOSYS;
struct IpmiGetServerNameParams {
ptr<char> name;
};
if (paramsSz != sizeof(IpmiGetServerNameParams)) {
return ErrorCode::INVAL;
}
auto server = thread->tproc->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);
}

View File

@ -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<uint> result, ptr<void> 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);
}