rpcsx/rpcsx/ipmi.cpp
2024-11-14 11:09:13 +03:00

1091 lines
39 KiB
C++

#include "ipmi.hpp"
#include "AudioOut.hpp"
#include "io-device.hpp"
#include "orbis/KernelContext.hpp"
#include "orbis/osem.hpp"
#include "orbis/utils/Logs.hpp"
#include "rx/hexdump.hpp"
#include "rx/mem.hpp"
#include "rx/watchdog.hpp"
#include "vfs.hpp"
#include "vm.hpp"
#include <cstdint>
#include <fcntl.h>
#include <filesystem>
#include <format>
#include <print>
#include <sys/mman.h>
#include <sys/stat.h>
ipmi::IpmiClient ipmi::audioIpmiClient;
template <typename T = std::byte> struct GuestAlloc {
orbis::ptr<T> guestAddress;
GuestAlloc(std::size_t size) {
if (size == 0) {
guestAddress = nullptr;
} else {
guestAddress = orbis::ptr<T>(
vm::map(nullptr, size, vm::kMapProtCpuRead | vm::kMapProtCpuWrite,
vm::kMapFlagPrivate | vm::kMapFlagAnonymous));
}
}
GuestAlloc() : GuestAlloc(sizeof(T)) {}
GuestAlloc(const T &data) : GuestAlloc() {
if (orbis::uwrite(guestAddress, data) != orbis::ErrorCode{}) {
std::abort();
}
}
GuestAlloc(const void *data, std::size_t size) : GuestAlloc(size) {
if (orbis::uwriteRaw(guestAddress, data, size) != orbis::ErrorCode{}) {
std::abort();
}
}
GuestAlloc(const GuestAlloc &) = delete;
GuestAlloc(GuestAlloc &&other) noexcept : guestAddress(other.guestAddress) {
other.guestAddress = 0;
}
GuestAlloc &operator=(GuestAlloc &&other) noexcept {
std::swap(guestAddress, other.guestAddress);
}
~GuestAlloc() {
if (guestAddress != 0) {
vm::unmap(guestAddress, sizeof(T));
}
}
operator orbis::ptr<T>() { return guestAddress; }
T *operator->() { return guestAddress; }
operator T &() { return *guestAddress; }
};
orbis::sint ipmi::IpmiClient::sendSyncMessageRaw(
std::uint32_t method, const std::vector<std::vector<std::byte>> &inData,
std::vector<std::vector<std::byte>> &outBuf) {
GuestAlloc<orbis::sint> serverResult;
GuestAlloc<orbis::IpmiDataInfo> guestInDataArray{sizeof(orbis::IpmiDataInfo) *
inData.size()};
GuestAlloc<orbis::IpmiBufferInfo> guestOutBufArray{
sizeof(orbis::IpmiBufferInfo) * outBuf.size()};
std::vector<GuestAlloc<std::byte>> guestAllocs;
guestAllocs.reserve(inData.size() + outBuf.size());
for (auto &data : inData) {
auto pointer =
guestAllocs.emplace_back(data.data(), data.size()).guestAddress;
guestInDataArray.guestAddress[&data - inData.data()] = {
.data = pointer, .size = data.size()};
}
for (auto &buf : outBuf) {
auto pointer =
guestAllocs.emplace_back(buf.data(), buf.size()).guestAddress;
guestOutBufArray.guestAddress[&buf - outBuf.data()] = {
.data = pointer, .capacity = buf.size()};
}
GuestAlloc params = orbis::IpmiSyncCallParams{
.method = method,
.numInData = static_cast<orbis::uint32_t>(inData.size()),
.numOutData = static_cast<orbis::uint32_t>(outBuf.size()),
.pInData = guestInDataArray,
.pOutData = guestOutBufArray,
.pResult = serverResult,
.flags = (inData.size() >= 1 || outBuf.size() >= 1) ? 1u : 0u,
};
GuestAlloc<orbis::uint> errorCode;
orbis::sysIpmiClientInvokeSyncMethod(thread, errorCode, kid, params,
sizeof(orbis::IpmiSyncCallParams));
for (auto &buf : outBuf) {
auto size = guestOutBufArray.guestAddress[inData.data() - &buf].size;
buf.resize(size);
}
return serverResult;
}
ipmi::IpmiClient ipmi::createIpmiClient(orbis::Thread *thread,
const char *name) {
orbis::Ref<orbis::IpmiClient> client;
GuestAlloc config = orbis::IpmiCreateClientConfig{
.size = sizeof(orbis::IpmiCreateClientConfig),
};
orbis::uint kid;
{
GuestAlloc<char> guestName{name, std::strlen(name)};
GuestAlloc params = orbis::IpmiCreateClientParams{
.name = guestName,
.config = config,
};
GuestAlloc<orbis::uint> result;
GuestAlloc<orbis::uint> guestKid;
orbis::sysIpmiCreateClient(thread, guestKid, params,
sizeof(orbis::IpmiCreateClientParams));
kid = guestKid;
}
{
GuestAlloc<orbis::sint> status;
GuestAlloc params = orbis::IpmiClientConnectParams{.status = status};
GuestAlloc<orbis::uint> result;
while (true) {
auto errc = orbis::sysIpmiClientConnect(
thread, result, kid, params, sizeof(orbis::IpmiClientConnectParams));
if (errc.value() == 0) {
break;
}
std::this_thread::sleep_for(std::chrono::microseconds(300));
}
}
return {.clientImpl = std::move(client), .kid = kid, .thread = thread};
}
orbis::Semaphore *ipmi::createSemaphore(std::string_view name, uint32_t attrs,
uint64_t initCount, uint64_t maxCount) {
auto result =
orbis::g_context
.createSemaphore(orbis::kstring(name), attrs, initCount, maxCount)
.first;
std::memcpy(result->name, name.data(), name.size());
result->name[name.size()] = 0;
return result;
}
orbis::EventFlag *ipmi::createEventFlag(std::string_view name, uint32_t attrs,
uint64_t initPattern) {
return orbis::g_context
.createEventFlag(orbis::kstring(name), attrs, initPattern)
.first;
}
void ipmi::createShm(const char *name, uint32_t flags, uint32_t mode,
uint64_t size) {
orbis::Ref<orbis::File> shm;
auto shmDevice = orbis::g_context.shmDevice.staticCast<IoDevice>();
shmDevice->open(&shm, name, flags, mode, nullptr);
shm->ops->truncate(shm.get(), size, nullptr);
}
orbis::ErrorCode
ipmi::IpmiServer::handle(orbis::IpmiSession *session,
orbis::IpmiAsyncMessageHeader *message) {
std::vector<std::span<std::byte>> inData;
std::vector<std::vector<std::byte>> outData;
auto bufLoc = std::bit_cast<std::byte *>(message + 1);
for (unsigned i = 0; i < message->numInData; ++i) {
auto size = *std::bit_cast<orbis::uint *>(bufLoc);
bufLoc += sizeof(orbis::uint);
inData.push_back({bufLoc, size});
bufLoc += size;
}
orbis::IpmiClient::AsyncResponse response;
response.methodId = message->methodId + 1;
response.errorCode = 0;
orbis::ErrorCode result{};
if (auto it = asyncMethods.find(message->methodId);
it != asyncMethods.end()) {
auto &handler = it->second;
result = handler(*session, response.errorCode, outData, inData);
} else {
std::println(stderr, "Unimplemented async method {}::{:x}(inBufCount={})",
session->server->name, unsigned(message->methodId),
unsigned(message->numInData));
for (auto in : inData) {
std::println(stderr, "in {}", in.size());
rx::hexdump(in);
}
}
for (auto out : outData) {
response.data.push_back({out.data(), out.data() + out.size()});
}
std::lock_guard clientLock(session->client->mutex);
session->client->asyncResponses.push_front(std::move(response));
std::fprintf(stderr, "%s:%x: sending async response\n",
session->client->name.c_str(), message->methodId);
session->client->asyncResponseCv.notify_all(session->client->mutex);
return result;
}
orbis::ErrorCode
ipmi::IpmiServer::handle(orbis::IpmiSession *session,
orbis::IpmiServer::Packet &packet,
orbis::IpmiSyncMessageHeader *message) {
auto bufLoc = std::bit_cast<std::byte *>(message + 1);
std::vector<std::span<std::byte>> inData;
std::vector<std::vector<std::byte>> outData;
for (unsigned i = 0; i < message->numInData; ++i) {
auto size = *std::bit_cast<orbis::uint *>(bufLoc);
bufLoc += sizeof(orbis::uint);
inData.emplace_back(bufLoc, size);
bufLoc += size;
}
for (unsigned i = 0; i < message->numOutData; ++i) {
auto size = *std::bit_cast<orbis::uint *>(bufLoc);
bufLoc += sizeof(orbis::uint);
outData.emplace_back(size);
}
orbis::IpmiSession::SyncResponse response;
response.errorCode = 0;
orbis::ErrorCode result{};
if (auto it = syncMethods.find(message->methodId); it != syncMethods.end()) {
auto &handler = it->second;
result = handler(*session, response.errorCode, outData, inData);
} else {
std::println(
stderr,
"Unimplemented sync method {}::{:x}(inBufCount={}, outBufCount={})",
session->server->name, unsigned(message->methodId),
unsigned(message->numInData), unsigned(message->numOutData));
for (auto in : inData) {
std::println(stderr, "in {}", in.size());
rx::hexdump(in);
}
for (auto &out : outData) {
std::println(stderr, "out {:x}", out.size());
}
for (auto out : outData) {
std::memset(out.data(), 0, out.size());
}
// TODO:
// response.errorCode = message->numOutData == 0 ||
// (message->numOutData == 1 && outData[0].empty())
// ? 0
// : -1,
}
response.callerTid = packet.clientTid;
for (auto out : outData) {
response.data.push_back({out.data(), out.data() + out.size()});
}
std::lock_guard lock(session->mutex);
session->syncResponses.push_front(std::move(response));
session->responseCv.notify_all(session->mutex);
return result;
}
ipmi::IpmiServer &ipmi::createIpmiServer(orbis::Process *process,
const char *name) {
orbis::IpmiCreateServerConfig config{};
orbis::Ref<orbis::IpmiServer> serverImpl;
orbis::ipmiCreateServer(process, nullptr, name, config, serverImpl);
auto server = std::make_shared<ipmi::IpmiServer>();
server->serverImpl = serverImpl;
std::thread{[server, serverImpl, name] {
pthread_setname_np(pthread_self(), name);
while (true) {
orbis::IpmiServer::Packet packet;
{
std::lock_guard lock(serverImpl->mutex);
while (serverImpl->packets.empty()) {
serverImpl->receiveCv.wait(serverImpl->mutex);
}
packet = std::move(serverImpl->packets.front());
serverImpl->packets.pop_front();
}
if (packet.info.type == 1) {
std::lock_guard serverLock(serverImpl->mutex);
for (auto it = serverImpl->connectionRequests.begin();
it != serverImpl->connectionRequests.end(); ++it) {
auto &conReq = *it;
std::lock_guard clientLock(conReq.client->mutex);
if (conReq.client->session != nullptr) {
continue;
}
auto session = orbis::knew<orbis::IpmiSession>();
if (session == nullptr) {
break;
}
session->client = conReq.client;
session->server = serverImpl;
conReq.client->session = session;
for (auto &message : server->messages) {
conReq.client->messageQueues[0].messages.push_back(
orbis::kvector<std::byte>(message.data(),
message.data() + message.size()));
}
conReq.client->connectionStatus = 0;
conReq.client->sessionCv.notify_all(conReq.client->mutex);
conReq.client->connectCv.notify_all(conReq.client->mutex);
break;
}
continue;
}
if ((packet.info.type & ~0x8010) == 0x41) {
auto msgHeader = std::bit_cast<orbis::IpmiSyncMessageHeader *>(
packet.message.data());
auto process = orbis::g_context.findProcessById(msgHeader->pid);
if (process == nullptr) {
continue;
}
auto client = orbis::g_context.ipmiMap.get(packet.info.clientKid)
.cast<orbis::IpmiClient>();
if (client == nullptr) {
continue;
}
auto session = client->session;
if (session == nullptr) {
continue;
}
server->handle(client->session.get(), packet, msgHeader);
packet = {};
continue;
}
if ((packet.info.type & ~0x10) == 0x43) {
auto msgHeader = (orbis::IpmiAsyncMessageHeader *)packet.message.data();
auto process = orbis::g_context.findProcessById(msgHeader->pid);
if (process == nullptr) {
continue;
}
auto client = orbis::g_context.ipmiMap.get(packet.info.clientKid)
.cast<orbis::IpmiClient>();
if (client == nullptr) {
continue;
}
auto session = client->session;
if (session == nullptr) {
continue;
}
server->handle(client->session.get(), msgHeader);
continue;
}
std::println(stderr, "IPMI: Unhandled packet {}::{}", serverImpl->name,
packet.info.type);
}
}}.detach();
return *server;
}
void ipmi::createMiniSysCoreObjects(orbis::Process *) {
createEventFlag("SceBootStatusFlags", 0x121, ~0ull);
}
void ipmi::createSysAvControlObjects(orbis::Process *process) {
createIpmiServer(process, "SceAvSettingIpc");
createIpmiServer(process, "SceAvCaptureIpc");
createEventFlag("SceAvCaptureIpc", 0x121, 0);
createEventFlag("SceAvSettingEvf", 0x121, 0xffff00000000);
createShm("/SceAvSetting", 0xa02, 0x1a4, 4096);
}
struct SceSysAudioSystemThreadArgs {
uint32_t threadId;
};
struct SceSysAudioSystemPortAndThreadArgs {
uint32_t audioPort;
uint32_t threadId;
};
void ipmi::createAudioSystemObjects(orbis::Process *process) {
auto audioOut = orbis::Ref<AudioOut>(orbis::knew<AudioOut>());
createIpmiServer(process, "SceSysAudioSystemIpc")
.addSyncMethod<SceSysAudioSystemThreadArgs>(
0x12340000,
[=](const auto &args) -> std::int32_t {
ORBIS_LOG_TODO("IPMI: SceSysAudioSystemCreateControl",
args.threadId);
audioOut->channelInfo.idControl = args.threadId;
return 0;
})
.addSyncMethod<SceSysAudioSystemThreadArgs>(
0x1234000f,
[=](const auto &args) -> std::int32_t {
ORBIS_LOG_TODO("IPMI: SceSysAudioSystemOpenMixFlag", args.threadId);
// very bad
char buffer[32];
std::snprintf(buffer, sizeof(buffer), "sceAudioOutMix%x",
args.threadId);
auto [eventFlag, inserted] =
orbis::g_context.createEventFlag(buffer, 0x100, 0);
if (!inserted) {
return 17; // FIXME: verify
}
audioOut->channelInfo.evf = eventFlag;
return 0;
})
.addSyncMethod<SceSysAudioSystemPortAndThreadArgs>(
0x12340001,
[=](const auto &args) -> std::int32_t {
ORBIS_LOG_TODO("IPMI: SceSysAudioSystemOpenPort", args.threadId,
args.audioPort);
audioOut->channelInfo.port = args.audioPort;
audioOut->channelInfo.channel = args.threadId;
return 0;
})
.addSyncMethod<SceSysAudioSystemPortAndThreadArgs>(
0x12340002,
[=](const auto &args) -> std::int32_t {
ORBIS_LOG_TODO("IPMI: SceSysAudioSystemStartListening",
args.threadId, args.audioPort);
audioOut->start();
return 0;
})
.addSyncMethod<SceSysAudioSystemPortAndThreadArgs>(
0x12340006, [=](const auto &args) -> std::int32_t {
ORBIS_LOG_TODO("IPMI: SceSysAudioSystemStopListening",
args.audioPort, args.threadId);
// TODO: implement
return 0;
});
}
struct SceMbusIpcAddHandleByUserIdMethodArgs {
orbis::uint32_t deviceType; // 0 - pad, 1 - aout, 2 - ain, 4 - camera, 6 - kb,
// 7 - mouse, 8 - vr
orbis::uint32_t deviceId;
orbis::uint32_t userId;
orbis::uint32_t type;
orbis::uint32_t index;
orbis::uint32_t reserved;
orbis::uint32_t pid;
};
static_assert(sizeof(SceMbusIpcAddHandleByUserIdMethodArgs) == 0x1c);
struct SceUserServiceEvent {
std::uint32_t eventType; // 0 - login, 1 - logout
std::uint32_t user;
};
void ipmi::createSysCoreObjects(orbis::Process *process) {
createIpmiServer(process, "SceMbusIpc")
.addSyncMethod<SceMbusIpcAddHandleByUserIdMethodArgs>(
0xce110007, [](const auto &args) -> std::int32_t {
ORBIS_LOG_TODO("IPMI: SceMbusIpcAddHandleByUserId", args.deviceType,
args.deviceId, args.userId, args.type, args.index,
args.reserved, args.pid);
if (args.deviceType == 1) {
struct HandleA {
int32_t pid;
int32_t port;
int32_t unk0 = 0x20100000;
int32_t unk1 = 1;
} handleA;
handleA.pid = args.pid;
handleA.port = args.deviceId;
audioIpmiClient.sendSyncMessage(0x1234000a, handleA);
struct HandleC {
int32_t pid;
int32_t port;
int32_t unk0 = 1;
int32_t unk1 = 0;
int32_t unk2 = 1;
int32_t unk3 = 0;
int32_t unk4 = 0;
int32_t unk5 = 0;
int32_t unk6 = 0;
int32_t unk7 = 1;
int32_t unk8 = 0;
} handleC;
handleC.pid = args.pid;
handleC.port = args.deviceId;
audioIpmiClient.sendSyncMessage(0x1234000c, handleC);
}
return 0;
});
createIpmiServer(process, "SceSysCoreApp");
createIpmiServer(process, "SceSysCoreApp2");
createIpmiServer(process, "SceMDBG0SRV");
createSemaphore("SceSysCoreProcSpawnSema", 0x101, 0, 1);
createSemaphore("SceTraceMemorySem", 0x100, 1, 1);
createSemaphore("SceSysCoreEventSemaphore", 0x101, 0, 0x2d2);
createSemaphore("SceSysCoreProcSema", 0x101, 0, 1);
createSemaphore("AppmgrCoredumpHandlingEventSema", 0x101, 0, 4);
createEventFlag("SceMdbgVrTriggerDump", 0x121, 0);
}
void ipmi::createGnmCompositorObjects(orbis::Process *) {
createEventFlag("SceCompositorCrashEventFlags", 0x122, 0);
createEventFlag("SceCompositorEventflag", 0x122, 0);
createEventFlag("SceCompositorResetStatusEVF", 0x122, 0);
createShm("/tmp/SceHmd/Vr2d_shm_pass", 0xa02, 0x1b6, 16384);
}
void ipmi::createShellCoreObjects(orbis::Process *process) {
auto fmtHex = [](auto value, bool upperCase = false) {
if (upperCase) {
return std::format("{:X}", value);
}
return std::format("{:x}", value);
};
createIpmiServer(process, "SceSystemLoggerService");
createIpmiServer(process, "SceLoginMgrServer");
createIpmiServer(process, "SceLncService")
.addSyncMethod(orbis::g_context.fwSdkVersion > 0x6000000 ? 0x30013
: 0x30010,
[](void *out, std::uint64_t &size) -> std::int32_t {
struct SceLncServiceAppStatus {
std::uint32_t unk0;
std::uint32_t unk1;
std::uint32_t unk2;
};
if (size < sizeof(SceLncServiceAppStatus)) {
return -1;
}
*(SceLncServiceAppStatus *)out = {
.unk0 = 1u,
.unk1 = 0u,
.unk2 = 5u,
};
size = sizeof(SceLncServiceAppStatus);
return 0;
})
.addSyncMethodStub(
orbis::g_context.fwSdkVersion > 0x6000000 ? 0x30033 : 0x3002e,
[]() -> std::int32_t {
auto commonDialog = std::get<0>(orbis::g_context.dialogs.front());
auto currentDialogId =
*reinterpret_cast<std::int16_t *>(commonDialog + 4);
auto currentDialog = std::get<0>(orbis::g_context.dialogs.back());
if (currentDialogId == 5) {
std::int32_t titleSize = 8192;
std::int32_t buttonNameSize = 64;
std::string dialogTitle(
reinterpret_cast<char *>(currentDialog + 0x4e4), titleSize);
std::string buttonOk(
reinterpret_cast<char *>(currentDialog + 0x2510),
buttonNameSize);
std::string buttonCancel(
reinterpret_cast<char *>(currentDialog + 0x2550),
buttonNameSize);
auto buttonType =
*reinterpret_cast<std::uint8_t *>(currentDialog + 0x488);
ORBIS_LOG_TODO("Activate message dialog", dialogTitle.data(),
buttonOk.data(), buttonCancel.data(),
(std::int16_t)buttonType);
// ignore dialogs without buttons
if (buttonType != 2 && buttonType != 5 && buttonType != 6) {
*reinterpret_cast<std::uint8_t *>(currentDialog + 0x18) =
1; // finished state
*reinterpret_cast<std::int32_t *>(currentDialog + 0x30) =
0; // result code
*reinterpret_cast<std::uint8_t *>(currentDialog + 0x24ec) =
1; // pressed button type
}
} else {
ORBIS_LOG_TODO("Activate unsupported dialog", currentDialogId);
}
return 0;
})
.addSyncMethod(
orbis::g_context.fwSdkVersion > 0x6000000 ? 0x30044 : 0x3003f,
[=](std::vector<std::vector<std::byte>>,
const std::vector<std::span<std::byte>> &inData) -> std::int32_t {
struct InitDialogArgs {
char *name;
size_t len;
};
auto args = (InitDialogArgs *)inData.data();
// maybe it's not necessary, but they add 1 to len
std::string realName(args->name, args->len - 1);
auto hostPath = rx::getShmGuestPath(realName);
ORBIS_LOG_TODO("Register dialog", inData.data(), inData.size(),
realName.data());
int shmFd =
::open(hostPath.c_str(), O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
if (shmFd == -1) {
perror("shm_open");
std::abort();
}
struct stat controlStat;
if (::fstat(shmFd, &controlStat)) {
perror("fstat");
std::abort();
}
auto shmAddress = reinterpret_cast<std::uint8_t *>(
rx::mem::map(nullptr, controlStat.st_size,
PROT_READ | PROT_WRITE, MAP_SHARED, shmFd));
if (shmAddress == MAP_FAILED) {
perror("mmap");
std::abort();
}
orbis::g_context.dialogs.emplace_back(shmAddress,
controlStat.st_size);
return 0;
})
.addSyncMethod(
orbis::g_context.fwSdkVersion > 0x6000000 ? 0x30045 : 0x30040,
[=](std::vector<std::vector<std::byte>>,
const std::vector<std::span<std::byte>> &inData) -> std::int32_t {
if (!orbis::g_context.dialogs.empty()) {
auto currentDialogAddr =
std::get<0>(orbis::g_context.dialogs.back());
auto currentDialogSize =
std::get<1>(orbis::g_context.dialogs.back());
ORBIS_LOG_TODO("Unmap shm after unlinking", currentDialogAddr,
currentDialogSize);
rx::mem::unmap(currentDialogAddr, currentDialogSize);
orbis::g_context.dialogs.pop_back();
}
return 0;
});
createIpmiServer(process, "SceAppMessaging");
createIpmiServer(process, "SceShellCoreUtil");
createIpmiServer(process, "SceNetCtl");
createIpmiServer(process, "SceNpMgrIpc")
.addSyncMethod(
0,
[=](void *out, std::uint64_t &size) -> std::int32_t {
std::string_view result = "SceNpMgrEvf";
if (size < result.size() + 1) {
return 0x8002'0000 + static_cast<int>(orbis::ErrorCode::INVAL);
}
std::strncpy((char *)out, result.data(), result.size() + 1);
size = result.size() + 1;
orbis::g_context.createEventFlag(orbis::kstring(result), 0x200, 0);
return 0;
})
.addSyncMethodStub(0xd);
createIpmiServer(process, "SceNpService")
.addSyncMethod<std::uint32_t>(
0, [=](void *, std::uint64_t &, std::uint32_t) { return 0; })
.addSyncMethod(0xa0001,
[=](void *out, std::uint64_t &size) -> std::int32_t {
if (size < 1) {
return 0x8002'0000 +
static_cast<int>(orbis::ErrorCode::INVAL);
}
size = 1;
*reinterpret_cast<std::uint8_t *>(out) = 1;
return 0;
})
.addSyncMethod(0xa0002,
[=](void *out, std::uint64_t &size) -> std::int32_t {
if (size < 1) {
return 0x8002'0000 +
static_cast<int>(orbis::ErrorCode::INVAL);
}
size = 1;
*reinterpret_cast<std::uint8_t *>(out) = 1;
return 0;
})
.addSyncMethod<std::uint32_t, std::uint32_t>(
0xd0000, // sceNpTpipIpcClientGetShmIndex
[=](std::uint32_t &shmIndex, std::uint32_t) -> std::int32_t {
shmIndex = 0;
return 0;
});
createIpmiServer(process, "SceNpTrophyIpc")
.addSyncMethod(2,
[](std::vector<std::vector<std::byte>> &out,
const std::vector<std::span<std::byte>> &) {
if (out.size() != 1 ||
out[0].size() < sizeof(std::uint32_t)) {
return orbis::ErrorCode::INVAL;
}
out = {toBytes<std::uint32_t>(0)};
return orbis::ErrorCode{};
})
.addAsyncMethod(0x30040,
[](orbis::IpmiSession &session,
std::vector<std::vector<std::byte>> &,
const std::vector<std::span<std::byte>> &) {
session.client->eventFlags[0].set(1);
return orbis::ErrorCode{};
})
.addSyncMethod(0x90000,
[](std::vector<std::vector<std::byte>> &out,
const std::vector<std::span<std::byte>> &) {
if (out.size() != 1 ||
out[0].size() < sizeof(std::uint32_t)) {
return orbis::ErrorCode::INVAL;
}
out = {toBytes<std::uint32_t>(1)};
return orbis::ErrorCode{};
})
.addSyncMethod(0x90003,
[](std::vector<std::vector<std::byte>> &out,
const std::vector<std::span<std::byte>> &) {
if (out.size() != 1 ||
out[0].size() < sizeof(std::uint32_t)) {
return orbis::ErrorCode::INVAL;
}
out = {toBytes<std::uint32_t>(1)};
return orbis::ErrorCode{};
})
.addAsyncMethod(0x90024,
[](orbis::IpmiSession &,
std::vector<std::vector<std::byte>> &out,
const std::vector<std::span<std::byte>> &) {
out.push_back(toBytes<std::uint32_t>(0));
// session.client->eventFlags[0].set(1);
return orbis::ErrorCode{};
})
.addAsyncMethod(0x90026, [](orbis::IpmiSession &session,
std::vector<std::vector<std::byte>> &,
const std::vector<std::span<std::byte>> &) {
session.client->eventFlags[0].set(1);
return orbis::ErrorCode{};
});
createIpmiServer(process, "SceNpUdsIpc");
createIpmiServer(process, "SceLibNpRifMgrIpc");
createIpmiServer(process, "SceNpPartner001");
createIpmiServer(process, "SceNpPartnerSubs");
createIpmiServer(process, "SceNpGameIntent");
createIpmiServer(process, "SceBgft");
createIpmiServer(process, "SceCntMgrService");
createIpmiServer(process, "ScePlayGo");
createIpmiServer(process, "SceCompAppProxyUtil");
createIpmiServer(process, "SceShareSpIpcService");
createIpmiServer(process, "SceRnpsAppMgr");
createIpmiServer(process, "SceUpdateService");
createIpmiServer(process, "ScePatchChecker");
createIpmiServer(process, "SceMorpheusUpdService");
createIpmiServer(process, "ScePsmSharedDmem");
auto saveDataSem =
createSemaphore("SceSaveData0000000000000001", 0x101, 0, 1);
auto saveDataSem_0 =
createSemaphore("SceSaveData0000000000000001_0", 0x101, 0, 1);
createShm("SceSaveData0000000000000001_0", 0x202, 0x1b6, 0x40000);
createShm("SceSaveDataI0000000000000001", 0x202, 0x1b6, 43008);
createShm("SceSaveDataI0000000000000001_0", 0x202, 0x1b6, 43008);
createShm("SceNpPlusLogger", 0x202, 0x1b6, 0x40000);
auto ruiEvf = createEventFlag("SceSaveDataMemoryRUI00000010", 0x120,
0x1000000100010000);
createIpmiServer(process, "SceSaveData")
.addSyncMethod(0x12340000,
[=](void *, std::uint64_t &) -> std::int32_t {
ruiEvf->set(~0ull);
{
saveDataSem->value++;
saveDataSem->cond.notify_one(saveDataSem->mtx);
}
{
saveDataSem_0->value++;
saveDataSem_0->cond.notify_one(saveDataSem_0->mtx);
}
return 0;
})
.addSyncMethod(
0x12340001,
[](std::vector<std::vector<std::byte>> &outData,
const std::vector<std::span<std::byte>> &inData) -> std::int32_t {
std::println(stderr, "SceSaveData: 0x12340001");
if (inData.size() != 2 || outData.size() != 2) {
return 0x8002000 +
static_cast<std::uint32_t>(orbis::ErrorCode::INVAL);
}
if (inData[0].size() != sizeof(orbis::uint64_t) ||
outData[1].size() != sizeof(orbis::uint64_t)) {
return 0x8002000 +
static_cast<std::uint32_t>(orbis::ErrorCode::INVAL);
}
auto outputLen =
*reinterpret_cast<orbis::uint64_t *>(inData[0].data());
if (outputLen != outData[0].size()) {
return 0x8002000 +
static_cast<std::uint32_t>(orbis::ErrorCode::INVAL);
}
struct Request {
orbis::uint32_t unk0;
orbis::uint32_t id;
orbis::uint32_t unk1[31];
};
static_assert(sizeof(Request) == 132);
if (inData[1].size() != sizeof(Request)) {
return 0x8002000 +
static_cast<std::uint32_t>(orbis::ErrorCode::INVAL);
}
auto request = reinterpret_cast<Request *>(inData[1].data());
std::println(stderr, "SceSaveData: 0x12340001, message {}",
request->id);
for (std::size_t index = 0; auto &in : inData) {
std::println(stderr, "in {} - {}", index++, in.size());
rx::hexdump(in);
}
for (std::size_t index = 0; auto &out : outData) {
std::println(stderr, "out {} - {}", index++, out.size());
}
if (request->id == 2) {
return 0;
}
if (request->id == 3) {
struct MountInfo {
std::uint64_t blocks;
std::uint64_t freeBlocks;
};
std::memset(outData[0].data(), 0xff, outData[0].size());
auto info = (MountInfo *)outData[0].data();
info->blocks = 1024 * 32;
info->freeBlocks = 1024 * 16;
return 0;
}
if (request->id == 4) {
return 0;
}
if (request->id == 6) {
struct Entry {
char string[32];
};
struct SearchResults {
std::uint32_t totalCount;
std::uint32_t count;
Entry entries[];
};
std::uint32_t fillOffset = 4 + sizeof(Entry) + 1024;
std::memset(outData[0].data() + fillOffset, 0xff,
outData[0].size() - fillOffset);
auto results = (SearchResults *)outData[0].data();
std::vector<std::string> searchResults;
searchResults.emplace_back("TEST");
results->totalCount = searchResults.size();
Entry *entries = results->entries;
results->count = searchResults.size();
for (auto &str : searchResults) {
std::strncpy(entries->string, str.data(),
sizeof(entries->string));
entries->string[std::size(entries->string) - 1] = 0;
entries++;
}
return 0;
}
if (request->id == 7) {
return 0;
}
if (request->id == 8) {
return 0;
}
if (request->id == 9) {
return 0;
}
if (request->id == 10) {
return 0;
}
if (request->id == 1 || request->id == 60) {
{
auto [dev, devPath] = vfs::get("/app0");
if (auto hostFs = dev.cast<HostFsDevice>()) {
std::error_code ec;
auto saveDir = hostFs->hostPath + "/.rpcsx/savedata/";
if (!std::filesystem::exists(saveDir)) {
return 0x8002'0000 +
static_cast<int>(orbis::ErrorCode::NOENT);
}
}
}
// umount
std::string_view result = "/savedata";
if (outData[0].size() < result.size() + 1) {
return 0x8002'0000 + static_cast<int>(orbis::ErrorCode::INVAL);
}
std::strncpy((char *)outData[0].data(), result.data(),
result.size() + 1);
outData[0].resize(result.size() + 1);
orbis::g_context.createEventFlag(orbis::kstring(result), 0x200,
0);
outData[1] = toBytes<orbis::uint64_t>(0);
return 0;
}
return 0x8002000 +
static_cast<std::uint32_t>(orbis::ErrorCode::INVAL);
})
.addSyncMethod(0x12340002, [](void *, std::uint64_t &) -> std::int32_t {
{
auto [dev, devPath] = vfs::get("/app0");
if (auto hostFs = dev.cast<HostFsDevice>()) {
std::error_code ec;
auto saveDir = hostFs->hostPath + "/.rpcsx/savedata/";
std::filesystem::create_directories(saveDir, ec);
vfs::mount("/savedata/", createHostIoDevice(saveDir, "/savedata/"));
}
}
return 0;
});
createIpmiServer(process, "SceStickerCoreServer");
createIpmiServer(process, "SceDbRecoveryShellCore");
createIpmiServer(process, "SceUserService")
.sendMsg(SceUserServiceEvent{.eventType = 0, .user = 1})
.addSyncMethod(0x30011,
[](void *ptr, std::uint64_t &size) -> std::int32_t {
if (size < sizeof(orbis::uint32_t)) {
return 0x8000'0000;
}
*(orbis::uint32_t *)ptr = 1;
size = sizeof(orbis::uint32_t);
return 0;
});
createIpmiServer(process, "SceDbPreparationServer");
createIpmiServer(process, "SceScreenShot");
createIpmiServer(process, "SceAppDbIpc");
createIpmiServer(process, "SceAppInst");
createIpmiServer(process, "SceAppContent")
.addSyncMethod<orbis::uint32_t, orbis::uint32_t>(
0x20001,
[](orbis::uint32_t &out, orbis::uint32_t param) -> std::int32_t {
switch (param) {
case 0: // sku
out = 3;
return 0;
case 1: // user defined param 0
case 2: // user defined param 1
case 3: // user defined param 2
case 4: // user defined param 3
ORBIS_LOG_ERROR("SceAppContent: get user defined param");
out = 0;
return 0;
}
return 0x8002000 + static_cast<std::uint32_t>(orbis::ErrorCode::INVAL);
});
createIpmiServer(process, "SceNpEntAccess");
createIpmiServer(process, "SceMwIPMIServer");
createIpmiServer(process, "SceAutoMounterIpc");
createIpmiServer(process, "SceBackupRestoreUtil");
createIpmiServer(process, "SceDataTransfer");
createIpmiServer(process, "SceEventService");
createIpmiServer(process, "SceShareFactoryUtil");
createIpmiServer(process, "SceCloudConnectManager");
createIpmiServer(process, "SceHubAppUtil");
createIpmiServer(process, "SceTcIPMIServer");
createSemaphore("SceLncSuspendBlock00000001", 0x101, 1, 1);
createSemaphore("SceAppMessaging00000001", 0x100, 1, 0x7fffffff);
createEventFlag("SceAutoMountUsbMass", 0x120, 0);
createEventFlag("SceLoginMgrUtilityEventFlag", 0x112, 0);
createEventFlag("SceLoginMgrSharePlayEventFlag", 0x112, 0);
createEventFlag("SceLoginMgrServerHmdConnect", 0x112, 0);
createEventFlag("SceLoginMgrServerDialogRequest", 0x112, 0);
createEventFlag("SceLoginMgrServerDialogResponse", 0x112, 0);
createEventFlag("SceGameLiveStreamingSpectator", 0x120, 0x8000000000000000);
createEventFlag("SceGameLiveStreamingUserId", 0x120, 0x8000000000000000);
createEventFlag("SceGameLiveStreamingMsgCount", 0x120, 0x8000000000000000);
createEventFlag("SceGameLiveStreamingBCCtrl", 0x120, 0);
createEventFlag("SceGameLiveStreamingEvntArg", 0x120, 0);
createEventFlag("SceLncUtilSystemStatus", 0x120, 0);
createEventFlag("SceShellCoreUtilRunLevel", 0x100, 0);
createEventFlag("SceSystemStateMgrInfo", 0x120, 0x10000000a);
createEventFlag("SceSystemStateMgrStatus", 0x120, 0);
createEventFlag("SceAppInstallerEventFlag", 0x120, 0);
createEventFlag("SceShellCoreUtilPowerControl", 0x120, 0x400000);
createEventFlag("SceShellCoreUtilAppFocus", 0x120, 1);
createEventFlag("SceShellCoreUtilCtrlFocus", 0x120, 0);
createEventFlag("SceShellCoreUtilUIStatus", 0x120, 0x20001);
createEventFlag("SceShellCoreUtilDevIdxBehavior", 0x120, 0);
createEventFlag("SceNpMgrVshReq", 0x121, 0);
createEventFlag("SceNpIdMapperVshReq", 0x121, 0);
createEventFlag("SceRtcUtilTzdataUpdateFlag", 0x120, 0);
createEventFlag("SceDataTransfer", 0x120, 0);
createEventFlag("SceLncUtilAppStatus00000000", 0x100, 0);
createEventFlag("SceLncUtilAppStatus1", 0x100, 0);
createEventFlag("SceAppMessaging1", 0x120, 1);
createEventFlag("SceShellCoreUtil1", 0x120, 0x3f8c);
createEventFlag("SceNpScoreIpc_" + fmtHex(process->pid), 0x120, 0);
createEventFlag("/vmicDdEvfAin", 0x120, 0);
createSemaphore("SceAppMessaging1", 0x101, 1, 0x7fffffff);
createSemaphore("SceLncSuspendBlock1", 0x101, 1, 10000);
createShm("SceGlsSharedMemory", 0x202, 0x1a4, 262144);
createShm("SceShellCoreUtil", 0x202, 0x1a4, 16384);
createShm("SceNpTpip", 0x202, 0x1ff, 43008);
createShm("vmicDdShmAin", 0x202, 0x1b6, 43008);
createSemaphore("SceNpTpip 0", 0x101, 0, 1);
}