mirror of
https://github.com/RPCSX/rpcsx.git
synced 2025-12-06 07:12:14 +01:00
1091 lines
39 KiB
C++
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);
|
|
}
|