diff --git a/Core/HLE/HLE.cpp b/Core/HLE/HLE.cpp index 3d6535d603..33c5ed1f3f 100644 --- a/Core/HLE/HLE.cpp +++ b/Core/HLE/HLE.cpp @@ -20,6 +20,8 @@ #include #include +#include "Common/Math/CrossSIMD.h" + #include "Common/Profiler/Profiler.h" #include "Common/Log.h" @@ -67,8 +69,14 @@ static std::vector moduleDB; static int delayedResultEvent = -1; static int hleAfterSyscall = HLE_AFTER_NOTHING; static const char *hleAfterSyscallReschedReason; -static const HLEFunction *latestSyscall = nullptr; -static uint32_t latestSyscallPC = 0; + +#define MAX_SYSCALL_RECURSION 16 + +// Keep track of syscalls in flight. Note that they can call each other! But they'll have to use hleCall<>. +static const HLEFunction *g_stack[MAX_SYSCALL_RECURSION]; +u32 g_syscallPC; +int g_stackSize; + static int idleOp; // Split syscall support. NOTE: This needs to be saved in DoState somehow! @@ -105,8 +113,7 @@ static std::vector enqueuedMipsCalls; // Does need to be saved, referenced by the stack and owned. static std::vector mipsCallActions; -void hleDelayResultFinish(u64 userdata, int cycleslate) -{ +void hleDelayResultFinish(u64 userdata, int cycleslate) { u32 error; SceUID threadID = (SceUID) userdata; SceUID verify = __KernelGetWaitID(threadID, WAITTYPE_HLEDELAY, error); @@ -114,8 +121,7 @@ void hleDelayResultFinish(u64 userdata, int cycleslate) // We can't just put it all in userdata because we need to know the threadID... u64 result = (userdata & 0xFFFFFFFF00000000ULL) | __KernelGetWaitValue(threadID, error); - if (error == 0 && verify == 1) - { + if (error == 0 && verify == 1) { __KernelResumeThreadFromWait(threadID, result); __KernelReSchedule("woke from hle delay"); } @@ -134,9 +140,12 @@ void HLEDoState(PointerWrap &p) { if (!s) return; - // Can't be inside a syscall, reset this so errors aren't misleading. - latestSyscall = nullptr; - latestSyscallPC = 0; + // Can't be inside a syscall when saving state, reset this so errors aren't misleading. + if (g_stackSize) { + ERROR_LOG(Log::HLE, "Can't save state while in a HLE syscall"); + } + + g_stackSize = 0; Do(p, delayedResultEvent); CoreTiming::RestoreRegisterEvent(delayedResultEvent, "HLEDelayedResult", hleDelayResultFinish); @@ -161,8 +170,7 @@ void HLEDoState(PointerWrap &p) { void HLEShutdown() { hleAfterSyscall = HLE_AFTER_NOTHING; - latestSyscall = nullptr; - latestSyscallPC = 0; + g_stackSize = 0; moduleDB.clear(); enqueuedMipsCalls.clear(); for (auto p : mipsCallActions) { @@ -385,17 +393,20 @@ static bool hleExecuteDebugBreak(const HLEFunction *func) { } INFO_LOG(Log::CPU, "Broke after syscall: %s", func->name); - Core_Break("hle.step", latestSyscallPC); + Core_Break("hle.step", g_syscallPC); return true; } u32 hleDelayResult(u32 result, const char *reason, int usec) { + _dbg_assert_(g_stackSize > 0); + _dbg_assert_(g_stackSize == 1); + if (!__KernelIsDispatchEnabled()) { - WARN_LOG(Log::HLE, "%s: Dispatch disabled, not delaying HLE result (right thing to do?)", latestSyscall ? latestSyscall->name : "?"); + WARN_LOG(Log::HLE, "%s: Dispatch disabled, not delaying HLE result (right thing to do?)", g_stackSize ? g_stack[0]->name : "?"); } else { SceUID thread = __KernelGetCurThread(); if (KernelIsThreadWaiting(thread)) - ERROR_LOG(Log::HLE, "%s: Delaying a thread that's already waiting", latestSyscall ? latestSyscall->name : "?"); + ERROR_LOG(Log::HLE, "%s: Delaying a thread that's already waiting", g_stackSize ? g_stack[0]->name : "?"); CoreTiming::ScheduleEvent(usToCycles(usec), delayedResultEvent, thread); __KernelWaitCurThread(WAITTYPE_HLEDELAY, 1, result, 0, false, reason); } @@ -403,12 +414,16 @@ u32 hleDelayResult(u32 result, const char *reason, int usec) { } u64 hleDelayResult(u64 result, const char *reason, int usec) { + _dbg_assert_(g_stackSize > 0); + _dbg_assert_(g_stackSize == 1); + if (!__KernelIsDispatchEnabled()) { - WARN_LOG(Log::HLE, "%s: Dispatch disabled, not delaying HLE result (right thing to do?)", latestSyscall ? latestSyscall->name : "?"); + WARN_LOG(Log::HLE, "%s: Dispatch disabled, not delaying HLE result (right thing to do?)", g_stackSize ? g_stack[0]->name : "?"); } else { + // TODO: Defer this, so you can call this multiple times, in case of syscalls calling syscalls? Although, return values are tricky. SceUID thread = __KernelGetCurThread(); if (KernelIsThreadWaiting(thread)) - ERROR_LOG(Log::HLE, "%s: Delaying a thread that's already waiting", latestSyscall ? latestSyscall->name : "?"); + ERROR_LOG(Log::HLE, "%s: Delaying a thread that's already waiting", g_stackSize ? g_stack[0]->name : "?"); u64 param = (result & 0xFFFFFFFF00000000) | thread; CoreTiming::ScheduleEvent(usToCycles(usec), delayedResultEvent, param); __KernelWaitCurThread(WAITTYPE_HLEDELAY, 1, (u32)result, 0, false, reason); @@ -434,7 +449,7 @@ void hleEatMicro(int usec) { } bool hleIsKernelMode() { - return latestSyscall && (latestSyscall->flags & HLE_KERNEL_SYSCALL) != 0; + return g_stackSize && (g_stack[0]->flags & HLE_KERNEL_SYSCALL) != 0; } void hleEnqueueCall(u32 func, int argc, const u32 *argv, PSPAction *afterAction) { @@ -450,7 +465,8 @@ void hleEnqueueCall(u32 func, int argc, const u32 *argv, PSPAction *afterAction) void hleFlushCalls() { u32 &sp = currentMIPS->r[MIPS_REG_SP]; PSPPointer stackData; - VERBOSE_LOG(Log::HLE, "Flushing %d HLE mips calls from %s, sp=%08x", (int)enqueuedMipsCalls.size(), latestSyscall ? latestSyscall->name : "?", sp); + _dbg_assert_(g_stackSize > 0); + VERBOSE_LOG(Log::HLE, "Flushing %d HLE mips calls from %s, sp=%08x", (int)enqueuedMipsCalls.size(), g_stackSize ? g_stack[0]->name : "?", sp); // First, we'll add a marker for the final return. sp -= sizeof(HLEMipsCallStack); @@ -579,7 +595,7 @@ inline static void SetDeadbeefRegs() return; currentMIPS->r[MIPS_REG_COMPILER_SCRATCH] = 0xDEADBEEF; - // Set all the arguments and temp regs. + // Set all the arguments and temp regs. TODO: Use SIMD to just do three writes. memcpy(¤tMIPS->r[MIPS_REG_A0], deadbeefRegs, sizeof(deadbeefRegs)); currentMIPS->r[MIPS_REG_T8] = 0xDEADBEEF; currentMIPS->r[MIPS_REG_T9] = 0xDEADBEEF; @@ -678,10 +694,12 @@ static void updateSyscallStats(int modulenum, int funcnum, double total) } } -inline void CallSyscallWithFlags(const HLEFunction *info) -{ - latestSyscall = info; - latestSyscallPC = currentMIPS->pc; +static void CallSyscallWithFlags(const HLEFunction *info) { + _dbg_assert_(g_stackSize == 0); + + g_stack[g_stackSize++] = info; + g_syscallPC = currentMIPS->pc; + const u32 flags = info->flags; if (flags & HLE_CLEAR_STACK_BYTES) { @@ -699,25 +717,37 @@ inline void CallSyscallWithFlags(const HLEFunction *info) info->func(); } + // Now, g_stackSize should be back to 0. + // _dbg_assert_(g_stackSize == 0); + if (hleAfterSyscall != HLE_AFTER_NOTHING) hleFinishSyscall(info); else SetDeadbeefRegs(); + + g_stackSize = 0; } -inline void CallSyscallWithoutFlags(const HLEFunction *info) -{ - latestSyscall = info; - latestSyscallPC = currentMIPS->pc; +static void CallSyscallWithoutFlags(const HLEFunction *info) { + _dbg_assert_(g_stackSize == 0); + + g_stack[g_stackSize++] = info; + g_syscallPC = currentMIPS->pc; + info->func(); + // Now, g_stackSize should be back to 0. + // _dbg_assert_(g_stackSize == 0); + if (hleAfterSyscall != HLE_AFTER_NOTHING) hleFinishSyscall(info); else SetDeadbeefRegs(); + + g_stackSize = 0; } -const HLEFunction *GetSyscallFuncPointer(MIPSOpcode op) +const HLEFunction *GetSyscallFuncPointer(MIPSOpcode op) { u32 callno = (op >> 6) & 0xFFFFF; //20 bits int funcnum = callno & 0xFFF; @@ -768,6 +798,7 @@ void CallSyscall(MIPSOpcode op) { const HLEFunction *info = GetSyscallFuncPointer(op); if (!info) { + // We haven't incremented the stack yet. RETURN(SCE_KERNEL_ERROR_LIBRARY_NOT_YET_LINKED); return; } @@ -779,8 +810,8 @@ void CallSyscall(MIPSOpcode op) { CallSyscallWithFlags(info); else CallSyscallWithoutFlags(info); - } - else { + } else { + // We haven't incremented the stack yet. RETURN(SCE_KERNEL_ERROR_LIBRARY_NOT_YET_LINKED); ERROR_LOG_REPORT(Log::HLE, "Unimplemented HLE function %s", info->name ? info->name : "(\?\?\?)"); } @@ -900,20 +931,35 @@ size_t hleFormatLogArgs(char *message, size_t sz, const char *argmask) { return used; } +void hleLeave() { + _dbg_assert_(g_stackSize > 0); + if (g_stackSize > 0) { + g_stackSize--; + } // else warn? +} + void hleDoLogInternal(Log t, LogLevel level, u64 res, const char *file, int line, const char *reportTag, char retmask, const char *reason, const char *formatted_reason) { char formatted_args[4096]; const char *funcName = "?"; u32 funcFlags = 0; - if (latestSyscall) { - _dbg_assert_(latestSyscall->argmask != nullptr); - hleFormatLogArgs(formatted_args, sizeof(formatted_args), latestSyscall->argmask); + + if (!g_stackSize) { + ERROR_LOG(Log::HLE, "HLE function stack mismatch!"); + return; + } + + const HLEFunction *hleFunc = g_stack[g_stackSize - 1]; + + if (g_stackSize) { + _dbg_assert_(hleFunc->argmask != nullptr); + hleFormatLogArgs(formatted_args, sizeof(formatted_args), hleFunc->argmask); // This acts as an override (for error returns which are usually hex.) if (retmask == '\0') - retmask = latestSyscall->retmask; + retmask = hleFunc->retmask; - funcName = latestSyscall->name; - funcFlags = latestSyscall->flags; + funcName = hleFunc->name; + funcFlags = hleFunc->flags; } else { strcpy(formatted_args, "?"); } diff --git a/Core/HLE/HLE.h b/Core/HLE/HLE.h index d68ce68cdc..cc00d5c4b6 100644 --- a/Core/HLE/HLE.h +++ b/Core/HLE/HLE.h @@ -92,7 +92,7 @@ struct Syscall { #define PARAM(n) currentMIPS->r[MIPS_REG_A0 + n] #define PARAM64(n) (currentMIPS->r[MIPS_REG_A0 + n] | ((u64)currentMIPS->r[MIPS_REG_A0 + n + 1] << 32)) #define PARAMF(n) currentMIPS->f[12 + n] -#define RETURN(n) currentMIPS->r[MIPS_REG_V0] = n +#define RETURN(n) currentMIPS->r[MIPS_REG_V0] = n; #define RETURN64(n) {u64 RETURN64_tmp = n; currentMIPS->r[MIPS_REG_V0] = RETURN64_tmp & 0xFFFFFFFF; currentMIPS->r[MIPS_REG_V1] = RETURN64_tmp >> 32;} #define RETURNF(fl) currentMIPS->f[0] = fl @@ -131,6 +131,10 @@ u64 hleDelayResult(u64 result, const char *reason, int usec); void hleEatCycles(int cycles); void hleEatMicro(int usec); +// Don't manually call this, it's called by the various syscall return macros. +// This should only be called once per syscall! +void hleLeave(); + void hleCoreTimingForceCheck(); // Causes the syscall to not fully execute immediately, instead give the Ge a chance to @@ -169,13 +173,16 @@ void *GetQuickSyscallFunc(MIPSOpcode op); void hleDoLogInternal(Log t, LogLevel level, u64 res, const char *file, int line, const char *reportTag, char retmask, const char *reason, const char *formatted_reason); -template +template [[nodiscard]] #ifdef __GNUC__ __attribute__((format(printf, 8, 9))) #endif T hleDoLog(Log t, LogLevel level, T res, const char *file, int line, const char *reportTag, char retmask, const char *reasonFmt, ...) { if ((int)level > MAX_LOGLEVEL || !GenericLogEnabled(level, t)) { + if (leave) { + hleLeave(); + } return res; } @@ -198,13 +205,19 @@ T hleDoLog(Log t, LogLevel level, T res, const char *file, int line, const char fmtRes = (s64)res; } hleDoLogInternal(t, level, fmtRes, file, line, reportTag, retmask, reasonFmt, formatted_reason); + if (leave) { + hleLeave(); + } return res; } -template +template [[nodiscard]] T hleDoLog(Log t, LogLevel level, T res, const char *file, int line, const char *reportTag, char retmask) { if (((int)level > MAX_LOGLEVEL || !GenericLogEnabled(level, t)) && !reportTag) { + if (leave) { + hleLeave(); + } return res; } @@ -216,16 +229,22 @@ T hleDoLog(Log t, LogLevel level, T res, const char *file, int line, const char fmtRes = (s64)res; } hleDoLogInternal(t, level, fmtRes, file, line, reportTag, retmask, nullptr, ""); + if (leave) { + hleLeave(); + } return res; } -// These will become important later. +// These unwind the log stack. template [[nodiscard]] inline T hleNoLog(T t) { + hleLeave(); return t; } -inline void hleNoLogVoid() {} +inline void hleNoLogVoid() { + hleLeave(); +} // This is just a quick way to force logging to be more visible for one file. #ifdef HLE_LOG_FORCE @@ -244,7 +263,7 @@ inline void hleNoLogVoid() {} // IMPORTANT: These *must* only be used directly in HLE functions. They cannot be used by utility functions // called by them. Use regular ERROR_LOG etc for those. -#define hleLogHelper(t, level, res, retmask, ...) hleDoLog(t, level, res, __FILE__, __LINE__, nullptr, retmask, ##__VA_ARGS__) +#define hleLogHelper(t, level, res, retmask, ...) hleDoLog(t, level, res, __FILE__, __LINE__, nullptr, retmask, ##__VA_ARGS__) #define hleLogError(t, res, ...) hleLogHelper(t, LogLevel::LERROR, res, 'x', ##__VA_ARGS__) #define hleLogWarning(t, res, ...) hleLogHelper(t, LogLevel::LWARNING, res, 'x', ##__VA_ARGS__) #define hleLogVerbose(t, res, ...) hleLogHelper(t, HLE_LOG_LVERBOSE, res, 'x', ##__VA_ARGS__) @@ -262,6 +281,6 @@ inline void hleNoLogVoid() {} #define hleLogSuccessVerboseX(t, res, ...) hleLogHelper(t, HLE_LOG_LVERBOSE, res, 'x', ##__VA_ARGS__) #define hleLogSuccessVerboseI(t, res, ...) hleLogHelper(t, HLE_LOG_LVERBOSE, res, 'i', ##__VA_ARGS__) -#define hleReportError(t, res, ...) hleDoLog(t, LogLevel::LERROR, res, __FILE__, __LINE__, "", 'x', ##__VA_ARGS__) -#define hleReportWarning(t, res, ...) hleDoLog(t, LogLevel::LWARNING, res, __FILE__, __LINE__, "", 'x', ##__VA_ARGS__) -#define hleReportDebug(t, res, ...) hleDoLog(t, HLE_LOG_LDEBUG, res, __FILE__, __LINE__, "", 'x', ##__VA_ARGS__) +#define hleReportError(t, res, ...) hleDoLog(t, LogLevel::LERROR, res, __FILE__, __LINE__, "", 'x', ##__VA_ARGS__) +#define hleReportWarning(t, res, ...) hleDoLog(t, LogLevel::LWARNING, res, __FILE__, __LINE__, "", 'x', ##__VA_ARGS__) +#define hleReportDebug(t, res, ...) hleDoLog(t, HLE_LOG_LDEBUG, res, __FILE__, __LINE__, "", 'x', ##__VA_ARGS__)