diff --git a/Core/CMakeLists.txt b/Core/CMakeLists.txt index 3c902b2ffa..7eafa34695 100644 --- a/Core/CMakeLists.txt +++ b/Core/CMakeLists.txt @@ -588,6 +588,8 @@ add_library(Core STATIC HLE/sceUtility.h HLE/sceVaudio.cpp HLE/sceVaudio.h + HLE/sceVshBridge.cpp + HLE/sceVshBridge.h HLE/scePspNpDrm_user.cpp HLE/scePspNpDrm_user.h HLE/sceNp.cpp diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 3c7f560423..322278e8fc 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -667,6 +667,7 @@ + @@ -1176,6 +1177,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index d6790ae2a7..da1dbed56d 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -387,6 +387,9 @@ HLE\Libraries + + HLE\Libraries + Dialog @@ -1704,6 +1707,9 @@ HLE\Libraries + + HLE\Libraries + Dialog diff --git a/Core/HLE/HLETables.cpp b/Core/HLE/HLETables.cpp index 373c92236f..679b8ff2cd 100644 --- a/Core/HLE/HLETables.cpp +++ b/Core/HLE/HLETables.cpp @@ -78,6 +78,7 @@ #include "sceUsbMic.h" #include "sceUtility.h" #include "sceVaudio.h" +#include "sceVshBridge.h" #include "sceMt19937.h" #include "sceSha256.h" #include "sceAdler.h" @@ -315,6 +316,7 @@ void RegisterAllModules() { Register_sceImpose_driver(); Register_sceHprm_driver(); Register_sceChkreg(); + Register_sceVshBridge(); // add new modules here. // Not ready to enable this due to apparent softlocks in Patapon 3. diff --git a/Core/HLE/sceCtrl.cpp b/Core/HLE/sceCtrl.cpp index d9b53ebaff..4384b2b762 100644 --- a/Core/HLE/sceCtrl.cpp +++ b/Core/HLE/sceCtrl.cpp @@ -495,7 +495,7 @@ static int sceCtrlGetIdleCancelThreshold(u32 idleResetPtr, u32 idleBackPtr) return hleLogDebug(Log::sceCtrl, 0); } -static int sceCtrlReadBufferPositive(u32 ctrlDataPtr, u32 nBufs) +int sceCtrlReadBufferPositive(u32 ctrlDataPtr, u32 nBufs) { int done = __CtrlReadBuffer(ctrlDataPtr, nBufs, false, false); hleEatCycles(330); diff --git a/Core/HLE/sceCtrl.h b/Core/HLE/sceCtrl.h index 522cc26611..f37325fae6 100644 --- a/Core/HLE/sceCtrl.h +++ b/Core/HLE/sceCtrl.h @@ -87,6 +87,9 @@ bool __CtrlGetRapidFire(); // For use by internal UI like MsgDialog u32 __CtrlPeekButtons(); + +// Exposed so sceVshBridge can reuse it directly for vshCtrlReadBufferPositive, matching JPCSP. +int sceCtrlReadBufferPositive(u32 ctrlDataPtr, u32 nBufs); u32 __CtrlPeekButtonsVisual(); // also incorporates rapid-fire void __CtrlPeekAnalog(int stick, float *x, float *y); u32 __CtrlReadLatch(); diff --git a/Core/HLE/sceKernelModule.cpp b/Core/HLE/sceKernelModule.cpp index 02a01c5cff..88b2545824 100644 --- a/Core/HLE/sceKernelModule.cpp +++ b/Core/HLE/sceKernelModule.cpp @@ -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); diff --git a/Core/HLE/sceKernelModule.h b/Core/HLE/sceKernelModule.h index 59d8fefddf..cc9cfb099a 100644 --- a/Core/HLE/sceKernelModule.h +++ b/Core/HLE/sceKernelModule.h @@ -237,6 +237,9 @@ u32 __KernelGetModuleGP(SceUID module); bool KernelModuleIsKernelMode(SceUID module); bool __KernelLoadGEDump(std::string_view base_filename, std::string *error_string); bool __KernelLoadExec(MIPSState *mips, const char *filename, u32 paramPtr, std::string *error_string); +// Exposed so sceVshBridge can reuse it directly for vshKernelLoadModuleBufferVSH, matching JPCSP. +SceUID sceKernelLoadModuleBufferUsbWlan(u32 size, u32 bufPtr, u32 flags, u32 lmoptionPtr); +// Exposed for HLE.cpp's "Unknown syscall" diagnostic - see the definition for details. bool KernelFindImportByStubAddr(u32 stubAddr, std::string *importModuleName, u32 *nid, std::string *importingModuleName); // Describes which loaded module (and section within it) an address falls in, e.g. "EBOOT.BIN.text+1234". // Returns an empty string if the address isn't inside any currently loaded module. diff --git a/Core/HLE/sceVshBridge.cpp b/Core/HLE/sceVshBridge.cpp new file mode 100644 index 0000000000..4575f7dc74 --- /dev/null +++ b/Core/HLE/sceVshBridge.cpp @@ -0,0 +1,48 @@ +// Copyright (c) 2012- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "Core/HLE/HLE.h" +#include "Core/HLE/FunctionWrappers.h" +#include "Core/HLE/sceVshBridge.h" +#include "Core/HLE/sceCtrl.h" +#include "Core/HLE/sceKernel.h" +#include "Core/HLE/sceKernelModule.h" + +// sceVshBridge is the HLE surface of flash0:/kd/vshbridge.prx, a kernel-mode module that +// exposes a bunch of otherwise-kernel-only functionality (controller reads, LoadExec +// variants, audio/ME/display bits, MagicGate memory stick audio, etc.) to the VSH, which +// mostly runs in user mode. +// +// Unlike sceVshCommonUtil/sceVshCommonGui, we intentionally only implement the handful of +// NIDs that genuinely need to trap into PPSSPP's own host-level code (controller input, +// process control, our own module-loading machinery) - the same reasoning that applies to +// every other kernel syscall PPSSPP HLEs for regular games. We now load and start the real +// vshbridge.prx as part of booting the VSH (see LoadAndStartVshKernelModules in +// sceKernelModule.cpp), so registering a stub for every other NID here would silently discard +// vshbridge.prx's real, working exports in favor of a placeholder that just logs an error - +// Core/HLE/sceKernelModule.cpp's ExportFuncSymbol() ignores a module's real export if we claim +// to already have HLE for that NID. Found this the hard way with sceVshCommonUtil first. + +const HLEFunction sceVshBridge[] = { + {0XC9626587, &WrapI_UUUU, "vshKernelLoadModuleBufferVSH", 'i', "xxxx"}, + {0XC6395C03, &WrapI_UU, "vshCtrlReadBufferPositive", 'i', "xx" }, + {0X9929DDA5, &WrapV_V, "vshKernelExitVSH", 'v', "" }, +}; + +void Register_sceVshBridge() { + RegisterHLEModule("sceVshBridge", ARRAY_SIZE(sceVshBridge), sceVshBridge); +} diff --git a/Core/HLE/sceVshBridge.h b/Core/HLE/sceVshBridge.h new file mode 100644 index 0000000000..cea6702375 --- /dev/null +++ b/Core/HLE/sceVshBridge.h @@ -0,0 +1,20 @@ +// Copyright (c) 2012- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +void Register_sceVshBridge(); diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 654217ce62..8bd3e6952d 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -781,6 +781,7 @@ EXEC_AND_LIB_FILES := \ $(SRC)/Core/HLE/sceUsbMic.cpp \ $(SRC)/Core/HLE/sceUtility.cpp \ $(SRC)/Core/HLE/sceVaudio.cpp \ + $(SRC)/Core/HLE/sceVshBridge.cpp \ $(SRC)/Core/HLE/scePspNpDrm_user.cpp \ $(SRC)/Core/HLE/sceGameUpdate.cpp \ $(SRC)/Core/HLE/sceNp.cpp \ diff --git a/headless/Headless.cpp b/headless/Headless.cpp index 68e2068e8c..79e9ea0f92 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -137,6 +137,7 @@ std::string NativeLoadSecret(std::string_view nameOfSecret) { int printUsage(const CommandLineOptions &options, const char *progname, const char *reason) { options.PrintUsage(progname, reason); fprintf(stderr, " --ir use ir interpreter\n"); + fprintf(stderr, " --flash0=DIR use DIR as flash0: instead of the bundled assets (e.g. a real dump)\n"); return 1; } diff --git a/libretro/Makefile.common b/libretro/Makefile.common index fdf991d907..c88a3d0235 100644 --- a/libretro/Makefile.common +++ b/libretro/Makefile.common @@ -832,6 +832,7 @@ SOURCES_CXX += \ $(COREDIR)/HLE/sceUsbCam.cpp \ $(COREDIR)/HLE/sceUtility.cpp \ $(COREDIR)/HLE/sceVaudio.cpp \ + $(COREDIR)/HLE/sceVshBridge.cpp \ $(COREDIR)/HLE/scePspNpDrm_user.cpp \ $(COREDIR)/HLE/sceNp.cpp \ $(COREDIR)/HLE/sceNp2.cpp \