mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-09-01 18:25:19 +02:00
Add a VSH-only allowlist for real-loading flash0 modules, sceVshCommonUtil stub and sceVshBridge
Load and start VSH's kernel modules before booting vshmain.prx A few flash0 modules (vshbridge.prx, paf.prx, common_gui.prx, common_util.prx) should run for real once we know we're actually booting the VSH rather than a game, since our fakes are unlikely to be good substitutes for the genuine thing. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PSNaZnHCjmryS3ziVN9gZU
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
0e2dfad02d
commit
04a8948ded
@@ -1089,6 +1089,64 @@ enum : u32 {
|
||||
ELF_MAGIC = 0x464c457f,
|
||||
};
|
||||
|
||||
// Set once we've seen a PSP_MODULE_VSH_MODE module load (i.e. we're booting the VSH rather
|
||||
// than a game), and reset on the next __KernelLoadExec. See ShouldHLEModuleForLoad below.
|
||||
static bool g_runningVSH = false;
|
||||
|
||||
// A few flash0 modules (VSH's own bridge/UI/utility libraries) should only ever be genuinely
|
||||
// loaded - rather than faked via any HLE implementation we may have for them - once we know
|
||||
// we're actually running the VSH. A regular game never legitimately loads these, so this only
|
||||
// matters for VSH boots; mirrors JPCSP's moduleFileNamesVshOnly.
|
||||
static bool IsVshOnlyModuleName(std::string_view modname) {
|
||||
static const char *const vshOnlyModules[] = {
|
||||
"sceVshBridge_Driver",
|
||||
"scePaf_Module",
|
||||
"sceVshCommonGui_Module",
|
||||
"sceVshCommonUtil_Module",
|
||||
};
|
||||
for (const char *name : vshOnlyModules) {
|
||||
if (equalsNoCase(modname, name))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool ShouldHLEModuleForLoad(std::string_view modname, bool *wasDisabledManually = nullptr) {
|
||||
if (g_runningVSH && IsVshOnlyModuleName(modname)) {
|
||||
if (wasDisabledManually)
|
||||
*wasDisabledManually = false;
|
||||
return false;
|
||||
}
|
||||
return ShouldHLEModule(modname, wasDisabledManually);
|
||||
}
|
||||
|
||||
// vsh_module (vshmain.prx) doesn't import sceKernelLoadModule at all - only StartModule/
|
||||
// StopModule/UnloadModule - so unlike a game's auxiliary PRXes, it never loads paf.prx/
|
||||
// common_gui.prx/common_util.prx/vshbridge.prx itself. On real hardware these are loaded and
|
||||
// started as part of the kernel's own boot sequence, before any user module runs (matches
|
||||
// JPCSP's moduleFileNamesToBeLoaded/moduleFileNamesVshOnly). We don't emulate that sequence,
|
||||
// so approximate it here: load and start them ourselves right before starting the VSH itself.
|
||||
static void LoadAndStartVshKernelModules() {
|
||||
static const char *const vshKernelModulePaths[] = {
|
||||
"flash0:/kd/vshbridge.prx",
|
||||
"flash0:/vsh/module/paf.prx",
|
||||
"flash0:/vsh/module/common_gui.prx",
|
||||
"flash0:/vsh/module/common_util.prx",
|
||||
};
|
||||
for (const char *path : vshKernelModulePaths) {
|
||||
std::string error_string;
|
||||
SceUID moduleId = KernelLoadModule(path, &error_string);
|
||||
if (moduleId < 0) {
|
||||
WARN_LOG(Log::sceModule, "LoadAndStartVshKernelModules: failed to load %s: %s", path, error_string.c_str());
|
||||
continue;
|
||||
}
|
||||
int result = __KernelStartModule(moduleId, 0, 0, 0, nullptr, nullptr);
|
||||
if (result < 0) {
|
||||
WARN_LOG(Log::sceModule, "LoadAndStartVshKernelModules: failed to start %s (uid=%d)", path, moduleId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// filename is only used for dumping/metadata.
|
||||
static PSPModule *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 loadAddress, bool fromTop, std::string *error_string, u32 *magic, std::string_view filename, u32 &error) {
|
||||
// The magic reads below need four bytes, and the ~SCE branch another four after that. Everything
|
||||
@@ -1135,7 +1193,7 @@ static PSPModule *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 load
|
||||
devkitVersion = head->devkitversion;
|
||||
|
||||
bool wasDisabled;
|
||||
if (ShouldHLEModule(head->modname, &wasDisabled)) {
|
||||
if (ShouldHLEModuleForLoad(head->modname, &wasDisabled)) {
|
||||
int ver = (head->module_ver_hi << 8) | head->module_ver_lo;
|
||||
INFO_LOG(Log::sceModule, "Loading module %s with version %04x, devkit %08x, crc %x", head->modname, ver, head->devkitversion, module->crc);
|
||||
|
||||
@@ -1362,20 +1420,21 @@ static PSPModule *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 load
|
||||
|
||||
modinfo = (const PspModuleInfo *)Memory::GetPointerUnchecked(modinfoaddr);
|
||||
|
||||
// OK, even if it's an ELF module, it might be one we shouldn't fully load and execute!
|
||||
// This is seen with mpeg.prx in Tony Hawk's Underground 2, see #20568.
|
||||
if (ShouldHLEModule(modinfo->name)) {
|
||||
// We load it, but at least we don't run any part of it.
|
||||
module->isFake = true;
|
||||
}
|
||||
|
||||
module->nm.nsegment = reader.GetNumSegments();
|
||||
module->nm.attribute = modinfo->moduleAttrs;
|
||||
if ((module->nm.attribute & PSP_MODULE_VSH_MODE) != 0) {
|
||||
// Used by the PSP's Visual Shell (VSH/XMB) and modules it loads, such as vshmain.prx.
|
||||
// We don't do anything special with this yet, just recognizing it for now.
|
||||
g_runningVSH = true;
|
||||
INFO_LOG(Log::sceModule, "VSH mode module detected: %s", modinfo->name);
|
||||
}
|
||||
|
||||
// OK, even if it's an ELF module, it might be one we shouldn't fully load and execute!
|
||||
// This is seen with mpeg.prx in Tony Hawk's Underground 2, see #20568.
|
||||
if (ShouldHLEModuleForLoad(modinfo->name)) {
|
||||
// We load it, but at least we don't run any part of it.
|
||||
module->isFake = true;
|
||||
}
|
||||
|
||||
module->nm.version[0] = modinfo->moduleVersion & 0xFF;
|
||||
module->nm.version[1] = modinfo->moduleVersion >> 8;
|
||||
module->nm.data_size = 0;
|
||||
@@ -1697,7 +1756,7 @@ static PSPModule *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 load
|
||||
|
||||
delete [] newptr;
|
||||
|
||||
if (!reportedModule && ShouldHLEModule(modinfo->name)) {
|
||||
if (!reportedModule && ShouldHLEModuleForLoad(modinfo->name)) {
|
||||
INFO_LOG(Log::sceModule, "Loading module %s with version %04x, devkit %08x", modinfo->name, modinfo->moduleVersion, devkitVersion);
|
||||
|
||||
if (!strcmp(modinfo->name, "sceMpeg_library")) {
|
||||
@@ -1837,6 +1896,7 @@ bool KernelModuleIsKernelMode(SceUID uid) {
|
||||
|
||||
void __KernelLoadReset() {
|
||||
// Wipe kernel here, loadexec should reset the entire system
|
||||
g_runningVSH = false;
|
||||
if (__KernelIsRunning()) {
|
||||
u32 error;
|
||||
while (!loadedModules.empty()) {
|
||||
@@ -1937,6 +1997,10 @@ bool __KernelLoadExec(MIPSState *mips, const char *filename, u32 paramPtr, std::
|
||||
if (module->nm.module_start_thread_stacksize != 0)
|
||||
option.stacksize = module->nm.module_start_thread_stacksize;
|
||||
|
||||
if (g_runningVSH) {
|
||||
LoadAndStartVshKernelModules();
|
||||
}
|
||||
|
||||
INFO_LOG(Log::System, "Starting modules...");
|
||||
if (paramPtr)
|
||||
__KernelStartModule(module, param.args, (const char*)param_argp, &option);
|
||||
|
||||
Reference in New Issue
Block a user