ps5: collect type of process and firmware

add
initial gc and dce ioctls implementation
This commit is contained in:
DH 2024-11-20 23:11:55 +03:00
parent ffe2837915
commit b6556e83ee
11 changed files with 297 additions and 41 deletions

View file

@ -48,6 +48,12 @@ struct UmtxChain {
uint notify_n(const UmtxKey &key, sint count);
};
enum class FwType : std::uint8_t {
Unknown,
Ps4,
Ps5,
};
class alignas(__STDCPP_DEFAULT_NEW_ALIGNMENT__) KernelContext final {
public:
KernelContext();
@ -192,7 +198,10 @@ public:
shared_mutex regMgrMtx;
kmap<std::uint32_t, std::uint32_t> regMgrInt;
std::vector<std::tuple<std::uint8_t*, size_t>> dialogs{};
std::vector<std::tuple<std::uint8_t *, size_t>> dialogs{};
FwType fwType = FwType::Unknown;
bool isDevKit = false;
private:
shared_mutex m_heap_mtx;

View file

@ -15,6 +15,12 @@ namespace orbis {
struct Thread;
struct Process;
enum class DynType : std::uint8_t {
None,
Ps4,
Ps5,
};
struct ModuleNeeded {
utils::kstring name;
std::uint16_t version;
@ -98,6 +104,8 @@ struct Module final {
uint16_t flags{};
uint64_t entryPoint{};
DynType dynType = DynType::None;
uint32_t phNum{};
uint64_t phdrAddress{};

View file

@ -44,6 +44,12 @@ struct NamedMemoryRange {
}
};
enum class ProcessType : std::uint8_t {
FreeBsd,
Ps4,
Ps5,
};
struct Process final {
KernelContext *context = nullptr;
pid_t pid = -1;
@ -54,6 +60,7 @@ struct Process final {
Process *parentProcess = nullptr;
shared_mutex mtx;
int vmId = -1;
ProcessType type = ProcessType::FreeBsd;
void (*onSysEnter)(Thread *thread, int id, uint64_t *args,
int argsCount) = nullptr;
void (*onSysExit)(Thread *thread, int id, uint64_t *args, int argsCount,

View file

@ -189,6 +189,10 @@ static SysResult keventChange(KQueue *kq, KEvent &change, Thread *thread) {
// hp3d idle
nodeIt->triggered = true;
kq->cv.notify_all(kq->mtx);
} else if (g_context.fwType == FwType::Ps5 &&
change.filter == kEvFiltGraphicsCore && change.ident == 0) {
nodeIt->triggered = true;
kq->cv.notify_all(kq->mtx);
}
return {};

View file

@ -202,6 +202,18 @@ SysResult kern_sysctl(Thread *thread, ptr<sint> name, uint namelen,
}
}
if (name[0] == kern && name[1] == proc && name[2] == 55) {
if (g_context.fwType != FwType::Ps5) {
return orbis::ErrorCode::INVAL;
}
if (oldlenp && old && *oldlenp == 4) {
return uwrite<uint32_t>(ptr<uint32_t>(old),
thread->tproc->type == ProcessType::Ps5 ? 1
: 0);
}
}
if (name[0] == kern && name[1] == proc && name[2] == 36) {
Process *process = thread->tproc;
if (process->pid != name[3]) {

View file

@ -256,6 +256,11 @@ static void initDceMemory(DceDevice *device) {
device->dmemOffset = start;
}
static orbis::ErrorCode dce_mmap(orbis::File *file, void **address,
std::uint64_t size, std::int32_t prot,
std::int32_t flags, std::int64_t offset,
orbis::Thread *thread);
static orbis::ErrorCode dce_ioctl(orbis::File *file, std::uint64_t request,
void *argp, orbis::Thread *thread) {
auto device = static_cast<DceDevice *>(file->device.get());
@ -263,6 +268,35 @@ static orbis::ErrorCode dce_ioctl(orbis::File *file, std::uint64_t request,
auto gpu = amdgpu::DeviceCtl{orbis::g_context.gpuDevice};
auto &gpuCtx = gpu.getContext();
// std::this_thread::sleep_for(std::chrono::seconds(5));
if (orbis::g_context.fwType == orbis::FwType::Ps5) {
if (request == 0x80308217) {
auto args = reinterpret_cast<FlipControlArgs *>(argp);
if (args->id == 9) {
ORBIS_LOG_NOTICE("dce: FlipControl allocate", args->id, args->padding,
args->arg2, args->ptr, args->size, args->arg5,
args->arg6);
void *address;
ORBIS_RET_ON_ERROR(
dce_mmap(file, &address, vm::kPageSize,
vm::kMapProtCpuReadWrite | vm::kMapProtGpuAll,
vm::kMapFlagShared, 0, thread));
*(void **)args->ptr = address;
*(std::uint64_t *)args->arg5 = vm::kPageSize;
return {};
}
ORBIS_LOG_FATAL("dce: unimplemented 0x80308217 request", args->id,
args->padding, args->arg2, args->ptr, args->size,
args->arg5, args->arg6);
}
}
if (request == 0xc0308203) {
// returns:
// PERM

View file

@ -77,6 +77,136 @@ static orbis::ErrorCode gc_ioctl(orbis::File *file, std::uint64_t request,
*reinterpret_cast<void **>(argp) = device->submitArea;
break;
case 0xc004812e: {
if (orbis::g_context.fwType != orbis::FwType::Ps5) {
return orbis::ErrorCode::INVAL;
}
struct Args {
std::uint16_t unk;
std::uint16_t flag;
};
auto args = reinterpret_cast<Args *>(argp);
args->unk = 0;
args->flag = 0;
return {};
}
case 0xc0488131: {
if (orbis::g_context.fwType != orbis::FwType::Ps5) {
return orbis::ErrorCode::INVAL;
}
// ps5 submit header
struct Args {
orbis::uint32_t unk0;
orbis::uint32_t contextControl[3];
orbis::uint32_t cmds[3 * 4];
orbis::uint64_t status;
};
static_assert(sizeof(Args) == 72);
auto args = reinterpret_cast<Args *>(argp);
// ORBIS_LOG_ERROR("gc submit header", args->status, args->unk0,
// args->contextControl[0], args->contextControl[1],
// args->contextControl[2]);
// for (std::size_t i = 0; i < 3; ++i) {
// auto offset = i * 4;
// ORBIS_LOG_ERROR("gc submit header cmd", i, args->cmds[offset],
// args->cmds[offset + 1], args->cmds[offset + 2],
// args->cmds[offset + 3]);
// }
// thread->where();
if (auto gpu = amdgpu::DeviceCtl{orbis::g_context.gpuDevice}) {
if (args->contextControl[0]) {
gpu.submitGfxCommand(gcFile->gfxPipe,
orbis::g_currentThread->tproc->vmId,
{args->contextControl, 3});
}
for (std::size_t i = 0; i < 3; ++i) {
auto offset = i * 4;
if (args->cmds[offset] == 0) {
continue;
}
ORBIS_LOG_ERROR("submit header", i, args->status, args->cmds[offset],
args->cmds[offset + 1], args->cmds[offset + 2],
args->cmds[offset + 3]);
gpu.submitGfxCommand(gcFile->gfxPipe,
orbis::g_currentThread->tproc->vmId,
{args->cmds + offset, 4});
}
gpu.waitForIdle();
args->status = 0;
} else {
return orbis::ErrorCode::BUSY;
}
return {};
}
case 0xc0188132: {
if (orbis::g_context.fwType != orbis::FwType::Ps5) {
return orbis::ErrorCode::INVAL;
}
struct Submit {
std::uint64_t unk0;
std::uint64_t unk1;
std::uint64_t address;
std::uint64_t size;
};
struct Args {
orbis::uint32_t unk0; // ringId?
orbis::uint32_t count;
orbis::ptr<Submit> submits;
orbis::uint32_t status;
orbis::uint32_t padding;
};
static_assert(sizeof(Args) == 24);
auto args = reinterpret_cast<Args *>(argp);
// ORBIS_LOG_ERROR("gc submit", args->unk0, args->count, args->submits,
// args->status, args->padding);
// for (std::size_t i = 0; i < args->count / 2; ++i) {
// ORBIS_LOG_ERROR("gc submit cmd", i, args->submits[i].address,
// args->submits[i].size, args->submits[i].unk0,
// args->submits[i].unk1);
// }
if (auto gpu = amdgpu::DeviceCtl{orbis::g_context.gpuDevice}) {
for (unsigned i = 0; i < args->count / 2; ++i) {
auto addressLo = static_cast<std::uint32_t>(args->submits[i].address);
auto addressHi = static_cast<std::uint32_t>(args->submits[i].address >> 32);
auto size = static_cast<std::uint32_t>(args->submits[i].size);
gpu.submitGfxCommand(gcFile->gfxPipe,
orbis::g_currentThread->tproc->vmId, {
{0xc0023f00, addressLo, addressHi, size}
});
}
gpu.waitForIdle();
} else {
return orbis::ErrorCode::BUSY;
}
args->status = 0;
return {};
}
case 0xc0108102: { // submit?
struct Args {
orbis::uint32_t arg0;

View file

@ -47,8 +47,13 @@ static orbis::ErrorCode icc_power_ioctl(orbis::File *file,
return {};
}
orbis::ErrorCode icc_stat(orbis::File *file, orbis::Stat *sb, orbis::Thread *thread) {
return{};
}
static const orbis::FileOps fileOps = {
.ioctl = icc_power_ioctl,
.stat = icc_stat,
};
orbis::ErrorCode IccPowerDevice::open(orbis::Ref<orbis::File> *file,

View file

@ -663,6 +663,9 @@ Ref<orbis::Module> rx::linker::loadModule(std::span<std::byte> image,
std::unordered_map<std::uint64_t, std::size_t> idToModuleIndex;
std::unordered_map<std::uint64_t, std::size_t> idToLibraryIndex;
bool hasPs4Dyn = false;
bool hasPs5Dyn = false;
for (auto dyn : dyns) {
if (dyn.d_tag == kElfDynamicTypeSceModuleInfo ||
dyn.d_tag == kElfDynamicTypeSceModuleInfo1) {
@ -686,6 +689,11 @@ Ref<orbis::Module> rx::linker::loadModule(std::span<std::byte> image,
if (dyn.d_tag == kElfDynamicTypeSceNeededModule ||
dyn.d_tag == kElfDynamicTypeSceNeededModule1) {
if (dyn.d_tag == kElfDynamicTypeSceNeededModule) {
hasPs4Dyn = true;
} else {
hasPs5Dyn = true;
}
auto [it, inserted] = idToModuleIndex.try_emplace(
dyn.d_un.d_val >> 48, result->neededModules.size());
@ -713,6 +721,13 @@ Ref<orbis::Module> rx::linker::loadModule(std::span<std::byte> image,
lib.name = strtab + static_cast<std::uint32_t>(dyn.d_un.d_val);
lib.isExport = dyn.d_tag == kElfDynamicTypeSceExportLib ||
dyn.d_tag == kElfDynamicTypeSceExportLib1;
if (dyn.d_tag == kElfDynamicTypeSceExportLib ||
dyn.d_tag == kElfDynamicTypeSceImportLib) {
hasPs4Dyn = true;
} else {
hasPs5Dyn = true;
}
} else if (dyn.d_tag == kElfDynamicTypeSceExportLibAttr ||
dyn.d_tag == kElfDynamicTypeSceImportLibAttr) {
auto [it, inserted] = idToLibraryIndex.try_emplace(
@ -787,6 +802,16 @@ Ref<orbis::Module> rx::linker::loadModule(std::span<std::byte> image,
}
}
if (hasPs4Dyn && hasPs5Dyn) {
std::fprintf(stderr, "unexpected import type\n");
std::abort();
}
if (hasPs4Dyn) {
result->dynType = orbis::DynType::Ps4;
} else if (hasPs5Dyn) {
result->dynType = orbis::DynType::Ps5;
}
if (symtab != nullptr && symtabSize > 0) {
auto sceSymtab = std::span(symtab, symtabSize);

View file

@ -322,7 +322,7 @@ static void onSysExit(orbis::Thread *thread, int id, uint64_t *args,
funlockfile(stderr);
}
static void ps4InitDev() {
static void guestInitDev() {
auto dmem1 = createDmemCharacterDevice(1);
orbis::g_context.dmemDevice = dmem1;
@ -431,6 +431,11 @@ static void ps4InitDev() {
vfs::addDevice("cayman/reg", createCaymanRegCharacterDevice());
vfs::addDevice("hctrl", createHidCharacterDevice());
if (orbis::g_context.fwType == orbis::FwType::Ps5) {
vfs::addDevice("iccnvs4", createIccPowerCharacterDevice());
vfs::addDevice("ajmi", createAjmCharacterDevice());
}
// mbus->emitEvent({
// .system = 2,
// .eventId = 1,
@ -454,7 +459,7 @@ static void ps4InitDev() {
orbis::g_context.blockpoolDevice = createBlockPoolDevice();
}
static void ps4InitFd(orbis::Thread *mainThread) {
static void guestInitFd(orbis::Thread *mainThread) {
orbis::Ref<orbis::File> stdinFile;
orbis::Ref<orbis::File> stdoutFile;
orbis::Ref<orbis::File> stderrFile;
@ -486,9 +491,9 @@ struct ExecEnv {
std::uint64_t interpBase;
};
int ps4Exec(orbis::Thread *mainThread, ExecEnv execEnv,
orbis::utils::Ref<orbis::Module> executableModule,
std::span<std::string> argv, std::span<std::string> envp) {
int guestExec(orbis::Thread *mainThread, ExecEnv execEnv,
orbis::utils::Ref<orbis::Module> executableModule,
std::span<std::string> argv, std::span<std::string> envp) {
const auto stackEndAddress = 0x7'ffff'c000ull;
const auto stackSize = 0x40000 * 32;
auto stackStartAddress = stackEndAddress - stackSize;
@ -518,6 +523,12 @@ int ps4Exec(orbis::Thread *mainThread, ExecEnv execEnv,
envpOffsets.push_back(0);
if (executableModule->dynType == orbis::DynType::Ps4) {
mainThread->tproc->type = orbis::ProcessType::Ps4;
} else if (executableModule->dynType == orbis::DynType::Ps5) {
mainThread->tproc->type = orbis::ProcessType::Ps5;
}
// clang-format off
std::uint64_t auxv[] = {
AT_ENTRY, executableModule->entryPoint,
@ -565,7 +576,7 @@ int ps4Exec(orbis::Thread *mainThread, ExecEnv execEnv,
std::abort();
}
struct Ps4ProcessParam {
struct ProcessParam {
orbis::size_t size;
orbis::uint32_t magic;
orbis::uint32_t version;
@ -578,23 +589,17 @@ struct Ps4ProcessParam {
orbis::ptr<void> libcParam;
};
ExecEnv ps4CreateExecEnv(orbis::Thread *mainThread,
const orbis::Ref<orbis::Module> &executableModule,
bool isSystem) {
ExecEnv guestCreateExecEnv(orbis::Thread *mainThread,
const orbis::Ref<orbis::Module> &executableModule,
bool isSystem) {
std::uint64_t interpBase = 0;
std::uint64_t entryPoint = executableModule->entryPoint;
if (mainThread->tproc->processParam != nullptr &&
mainThread->tproc->processParamSize >= sizeof(Ps4ProcessParam)) {
mainThread->tproc->processParamSize >= sizeof(ProcessParam)) {
auto processParam =
reinterpret_cast<std::byte *>(mainThread->tproc->processParam);
auto sdkVersion = processParam //
+ sizeof(uint64_t) // size
+ sizeof(uint32_t) // magic
+ sizeof(uint32_t); // entryCount
mainThread->tproc->sdkVersion = *(uint32_t *)sdkVersion;
reinterpret_cast<ProcessParam *>(mainThread->tproc->processParam);
mainThread->tproc->sdkVersion = processParam->sdkVersion;
}
if (orbis::g_context.sdkVersion == 0 && mainThread->tproc->sdkVersion != 0) {
@ -604,8 +609,7 @@ ExecEnv ps4CreateExecEnv(orbis::Thread *mainThread,
mainThread->tproc->sdkVersion = orbis::g_context.sdkVersion;
}
if (executableModule->type == rx::linker::kElfTypeExec ||
executableModule->type == rx::linker::kElfTypeSceExec) {
if (executableModule->dynType == orbis::DynType::None) {
return {.entryPoint = entryPoint, .interpBase = interpBase};
}
@ -671,6 +675,14 @@ ExecEnv ps4CreateExecEnv(orbis::Thread *mainThread,
std::printf("fw sdk version: %x\n", orbis::g_context.fwSdkVersion);
}
if (orbis::g_context.fwType == orbis::FwType::Unknown) {
if (libkernel->dynType == orbis::DynType::Ps4) {
orbis::g_context.fwType = orbis::FwType::Ps4;
} else {
orbis::g_context.fwType = orbis::FwType::Ps5;
}
}
libkernel->id = mainThread->tproc->modulesMap.insert(libkernel);
interpBase = reinterpret_cast<std::uint64_t>(libkernel->base);
entryPoint = libkernel->entryPoint;
@ -678,11 +690,12 @@ ExecEnv ps4CreateExecEnv(orbis::Thread *mainThread,
return {.entryPoint = entryPoint, .interpBase = interpBase};
}
int ps4Exec(orbis::Thread *mainThread,
orbis::utils::Ref<orbis::Module> executableModule,
std::span<std::string> argv, std::span<std::string> envp) {
auto execEnv = ps4CreateExecEnv(mainThread, executableModule, true);
return ps4Exec(mainThread, execEnv, std::move(executableModule), argv, envp);
int guestExec(orbis::Thread *mainThread,
orbis::utils::Ref<orbis::Module> executableModule,
std::span<std::string> argv, std::span<std::string> envp) {
auto execEnv = guestCreateExecEnv(mainThread, executableModule, true);
return guestExec(mainThread, execEnv, std::move(executableModule), argv,
envp);
}
static void usage(const char *argv0) {
@ -774,7 +787,7 @@ static orbis::SysResult launchDaemon(orbis::Thread *thread, std::string path,
rx::thread::initialize();
rx::thread::setupThisThread();
ps4InitFd(newThread);
guestInitFd(newThread);
orbis::Ref<orbis::File> socket;
createSocket(&socket, "", 1, 1, 0);
@ -808,7 +821,7 @@ static orbis::SysResult launchDaemon(orbis::Thread *thread, std::string path,
rx::thread::initialize();
rx::thread::setupSignalStack();
rx::thread::setupThisThread();
ps4Exec(thread, executableModule, argv, envv);
guestExec(thread, executableModule, argv, envv);
});
thread->handle.join();
@ -968,6 +981,7 @@ int main(int argc, const char *argv[]) {
if (guestArgv.empty()) {
guestArgv.emplace_back("/mini-syscore.elf");
isSystem = true;
asRoot = true;
}
rx::thread::initialize();
@ -1083,10 +1097,10 @@ int main(int argc, const char *argv[]) {
return 1;
}
ps4InitDev();
ps4InitFd(mainThread);
guestInitDev();
guestInitFd(mainThread);
auto execEnv = ps4CreateExecEnv(mainThread, executableModule, isSystem);
auto execEnv = guestCreateExecEnv(mainThread, executableModule, isSystem);
// data transfer mode
// 0 - normal
@ -1135,7 +1149,8 @@ int main(int argc, const char *argv[]) {
ipmi::createSysCoreObjects(initProcess);
ipmi::createGnmCompositorObjects(initProcess);
ipmi::createShellCoreObjects(initProcess);
if (enableAudioIpmi) {
if (enableAudioIpmi || orbis::g_context.fwType == orbis::FwType::Ps5) {
ipmi::createAudioSystemObjects(initProcess);
}
@ -1152,7 +1167,7 @@ int main(int argc, const char *argv[]) {
initProcess->cwd = "/app0/";
if (!enableAudioIpmi) {
if (orbis::g_context.fwType != orbis::FwType::Ps5 && !enableAudioIpmi) {
launchDaemon(mainThread, "/system/sys/orbis_audiod.elf",
{"/system/sys/orbis_audiod.elf"}, {},
{
@ -1196,14 +1211,20 @@ int main(int argc, const char *argv[]) {
}
}
if (orbis::g_context.fwType == orbis::FwType::Ps5 && !isSystem) {
ipmi::createIpmiServer(initProcess, "SceSysAvControlIpc");
ipmi::createShm("SceAvControl", 0xa02, 0x1a4, 4096);
ipmi::createEventFlag("SceAvControlEvf", 0x121, 0);
}
mainThread->hostTid = ::gettid();
mainThread->nativeHandle = pthread_self();
orbis::g_currentThread = mainThread;
rx::thread::setupSignalStack();
rx::thread::setupThisThread();
status =
ps4Exec(mainThread, execEnv, std::move(executableModule), guestArgv, {});
status = guestExec(mainThread, execEnv, std::move(executableModule),
guestArgv, {});
vm::deinitialize();
rx::thread::deinitialize();

View file

@ -42,9 +42,9 @@ extern bool allowMonoDebug;
extern "C" void __register_frame(const void *);
void setupSigHandlers();
int ps4Exec(orbis::Thread *mainThread,
orbis::utils::Ref<orbis::Module> executableModule,
std::span<std::string> argv, std::span<std::string> envp);
int guestExec(orbis::Thread *mainThread,
orbis::utils::Ref<orbis::Module> executableModule,
std::span<std::string> argv, std::span<std::string> envp);
namespace {
static std::pair<SysResult, Ref<Module>>
@ -722,7 +722,7 @@ SysResult processNeeded(Thread *thread) {
}
}
for (auto needed : allNeeded) {
for (const auto &needed : allNeeded) {
auto [result, neededModule] =
loadPrx(thread, needed, false, loadedObjects, loadedModules, needed);
@ -736,7 +736,7 @@ SysResult processNeeded(Thread *thread) {
module->importedModules.clear();
module->importedModules.reserve(module->neededModules.size());
for (auto mod : module->neededModules) {
for (const auto &mod : module->neededModules) {
if (auto it = loadedModules.find(std::string_view(mod.name));
it != loadedModules.end()) {
module->importedModules.emplace_back(it->second);
@ -787,6 +787,7 @@ SysResult fork(Thread *thread, slong flags) {
process->parentProcess = thread->tproc;
process->authInfo = thread->tproc->authInfo;
process->sdkVersion = thread->tproc->sdkVersion;
process->type = thread->tproc->type;
for (auto [id, mod] : thread->tproc->modulesMap) {
if (!process->modulesMap.insert(id, mod)) {
std::abort();
@ -919,7 +920,7 @@ SysResult execve(Thread *thread, ptr<char> fname, ptr<ptr<char>> argv,
thread->nativeHandle = pthread_self();
orbis::g_currentThread = thread;
ps4Exec(thread, executableModule, _argv, _envv);
guestExec(thread, executableModule, _argv, _envv);
}).join();
std::abort();
}