CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Core/HLE/HLE.cpp
Views: 1401
// Copyright (c) 2012- PPSSPP Project.12// This program is free software: you can redistribute it and/or modify3// it under the terms of the GNU General Public License as published by4// the Free Software Foundation, version 2.0 or later versions.56// This program is distributed in the hope that it will be useful,7// but WITHOUT ANY WARRANTY; without even the implied warranty of8// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9// GNU General Public License 2.0 for more details.1011// A copy of the GPL 2.0 should have been included with the program.12// If not, see http://www.gnu.org/licenses/1314// Official git repository and contact information can be found at15// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.1617#include <cstdarg>18#include <map>19#include <vector>20#include <string>2122#include "Common/Profiler/Profiler.h"2324#include "Common/Log.h"25#include "Common/Serialize/SerializeFuncs.h"26#include "Common/TimeUtil.h"27#include "Core/Config.h"28#include "Core/Core.h"29#include "Core/CoreTiming.h"30#include "Core/MemMapHelpers.h"31#include "Core/Reporting.h"32#include "Core/System.h"33#include "Core/MIPS/MIPS.h"34#include "Core/MIPS/MIPSCodeUtils.h"35#include "Core/HLE/HLETables.h"36#include "Core/HLE/sceIo.h"37#include "Core/HLE/sceAudio.h"38#include "Core/HLE/sceKernelMemory.h"39#include "Core/HLE/sceKernelThread.h"40#include "Core/HLE/sceKernelInterrupt.h"41#include "Core/HLE/HLE.h"4243enum44{45// Do nothing after the syscall.46HLE_AFTER_NOTHING = 0x00,47// Reschedule immediately after the syscall.48HLE_AFTER_RESCHED = 0x01,49// Call current thread's callbacks after the syscall.50HLE_AFTER_CURRENT_CALLBACKS = 0x02,51// Reschedule and process current thread's callbacks after the syscall.52HLE_AFTER_RESCHED_CALLBACKS = 0x08,53// Run interrupts (and probably reschedule) after the syscall.54HLE_AFTER_RUN_INTERRUPTS = 0x10,55// Switch to CORE_STEPPING after the syscall (for debugging.)56HLE_AFTER_DEBUG_BREAK = 0x20,57// Don't fill temp regs with 0xDEADBEEF.58HLE_AFTER_SKIP_DEADBEEF = 0x40,59// Execute pending mips calls.60HLE_AFTER_QUEUED_CALLS = 0x80,61};6263static std::vector<HLEModule> moduleDB;64static int delayedResultEvent = -1;65static int hleAfterSyscall = HLE_AFTER_NOTHING;66static const char *hleAfterSyscallReschedReason;67static const HLEFunction *latestSyscall = nullptr;68static uint32_t latestSyscallPC = 0;69static int idleOp;7071struct HLEMipsCallInfo {72u32 func;73PSPAction *action;74std::vector<u32> args;75};7677struct HLEMipsCallStack {78u32_le nextOff;79union {80struct {81u32_le func;82u32_le actionIndex;83u32_le argc;84};85struct {86u32_le ra;87u32_le v0;88u32_le v1;89};90};91};9293// No need to save state, always flushed at a syscall end.94static std::vector<HLEMipsCallInfo> enqueuedMipsCalls;95// Does need to be saved, referenced by the stack and owned.96static std::vector<PSPAction *> mipsCallActions;9798void hleDelayResultFinish(u64 userdata, int cycleslate)99{100u32 error;101SceUID threadID = (SceUID) userdata;102SceUID verify = __KernelGetWaitID(threadID, WAITTYPE_HLEDELAY, error);103// The top 32 bits of userdata are the top 32 bits of the 64 bit result.104// We can't just put it all in userdata because we need to know the threadID...105u64 result = (userdata & 0xFFFFFFFF00000000ULL) | __KernelGetWaitValue(threadID, error);106107if (error == 0 && verify == 1)108{109__KernelResumeThreadFromWait(threadID, result);110__KernelReSchedule("woke from hle delay");111}112else113WARN_LOG(Log::HLE, "Someone else woke up HLE-blocked thread %d?", threadID);114}115116void HLEInit() {117RegisterAllModules();118delayedResultEvent = CoreTiming::RegisterEvent("HLEDelayedResult", hleDelayResultFinish);119idleOp = GetSyscallOp("FakeSysCalls", NID_IDLE);120}121122void HLEDoState(PointerWrap &p) {123auto s = p.Section("HLE", 1, 2);124if (!s)125return;126127// Can't be inside a syscall, reset this so errors aren't misleading.128latestSyscall = nullptr;129latestSyscallPC = 0;130Do(p, delayedResultEvent);131CoreTiming::RestoreRegisterEvent(delayedResultEvent, "HLEDelayedResult", hleDelayResultFinish);132133if (s >= 2) {134int actions = (int)mipsCallActions.size();135Do(p, actions);136if (actions != (int)mipsCallActions.size()) {137mipsCallActions.resize(actions);138}139140for (auto &action : mipsCallActions) {141int actionTypeID = action != nullptr ? action->actionTypeID : -1;142Do(p, actionTypeID);143if (actionTypeID != -1) {144if (p.mode == p.MODE_READ)145action = __KernelCreateAction(actionTypeID);146action->DoState(p);147}148}149}150}151152void HLEShutdown() {153hleAfterSyscall = HLE_AFTER_NOTHING;154latestSyscall = nullptr;155latestSyscallPC = 0;156moduleDB.clear();157enqueuedMipsCalls.clear();158for (auto p : mipsCallActions) {159delete p;160}161mipsCallActions.clear();162}163164void RegisterModule(const char *name, int numFunctions, const HLEFunction *funcTable)165{166HLEModule module = {name, numFunctions, funcTable};167moduleDB.push_back(module);168}169170int GetModuleIndex(const char *moduleName)171{172for (size_t i = 0; i < moduleDB.size(); i++)173if (strcmp(moduleName, moduleDB[i].name) == 0)174return (int)i;175return -1;176}177178int GetFuncIndex(int moduleIndex, u32 nib)179{180const HLEModule &module = moduleDB[moduleIndex];181for (int i = 0; i < module.numFunctions; i++)182{183if (module.funcTable[i].ID == nib)184return i;185}186return -1;187}188189u32 GetNibByName(const char *moduleName, const char *function)190{191int moduleIndex = GetModuleIndex(moduleName);192if (moduleIndex == -1)193return -1;194195const HLEModule &module = moduleDB[moduleIndex];196for (int i = 0; i < module.numFunctions; i++)197{198if (!strcmp(module.funcTable[i].name, function))199return module.funcTable[i].ID;200}201return -1;202}203204const HLEFunction *GetFunc(const char *moduleName, u32 nib)205{206int moduleIndex = GetModuleIndex(moduleName);207if (moduleIndex != -1)208{209int idx = GetFuncIndex(moduleIndex, nib);210if (idx != -1)211return &(moduleDB[moduleIndex].funcTable[idx]);212}213return 0;214}215216const char *GetFuncName(const char *moduleName, u32 nib)217{218_dbg_assert_msg_(moduleName != nullptr, "Invalid module name.");219220const HLEFunction *func = GetFunc(moduleName,nib);221if (func)222return func->name;223224static char temp[256];225snprintf(temp, sizeof(temp), "[UNK: 0x%08x]", nib);226return temp;227}228229u32 GetSyscallOp(const char *moduleName, u32 nib) {230// Special case to hook up bad imports.231if (moduleName == NULL) {232return (0x03FFFFCC); // invalid syscall233}234235int modindex = GetModuleIndex(moduleName);236if (modindex != -1) {237int funcindex = GetFuncIndex(modindex, nib);238if (funcindex != -1) {239return (0x0000000c | (modindex<<18) | (funcindex<<6));240} else {241INFO_LOG(Log::HLE, "Syscall (%s, %08x) unknown", moduleName, nib);242return (0x0003FFCC | (modindex<<18)); // invalid syscall243}244}245else246{247ERROR_LOG(Log::HLE, "Unknown module %s!", moduleName);248return (0x03FFFFCC); // invalid syscall249}250}251252bool FuncImportIsSyscall(const char *module, u32 nib)253{254return GetFunc(module, nib) != NULL;255}256257void WriteFuncStub(u32 stubAddr, u32 symAddr)258{259// Note that this should be J not JAL, as otherwise control will return to the stub..260Memory::Write_U32(MIPS_MAKE_J(symAddr), stubAddr);261// Note: doing that, we can't trace external module calls, so maybe something else should be done to debug more efficiently262// Perhaps a syscall here (and verify support in jit), marking the module by uid (debugIdentifier)?263Memory::Write_U32(MIPS_MAKE_NOP(), stubAddr + 4);264}265266void WriteFuncMissingStub(u32 stubAddr, u32 nid)267{268// Write a trap so we notice this func if it's called before resolving.269Memory::Write_U32(MIPS_MAKE_JR_RA(), stubAddr); // jr ra270Memory::Write_U32(GetSyscallOp(NULL, nid), stubAddr + 4);271}272273bool WriteSyscall(const char *moduleName, u32 nib, u32 address)274{275if (nib == 0)276{277WARN_LOG_REPORT(Log::HLE, "Wrote patched out nid=0 syscall (%s)", moduleName);278Memory::Write_U32(MIPS_MAKE_JR_RA(), address); //patched out?279Memory::Write_U32(MIPS_MAKE_NOP(), address+4); //patched out?280return true;281}282int modindex = GetModuleIndex(moduleName);283if (modindex != -1)284{285Memory::Write_U32(MIPS_MAKE_JR_RA(), address); // jr ra286Memory::Write_U32(GetSyscallOp(moduleName, nib), address + 4);287return true;288}289else290{291ERROR_LOG_REPORT(Log::HLE, "Unable to write unknown syscall: %s/%08x", moduleName, nib);292return false;293}294}295296const char *GetFuncName(int moduleIndex, int func)297{298if (moduleIndex >= 0 && moduleIndex < (int)moduleDB.size())299{300const HLEModule &module = moduleDB[moduleIndex];301if (func >= 0 && func < module.numFunctions)302{303return module.funcTable[func].name;304}305}306return "[unknown]";307}308309void hleCheckCurrentCallbacks()310{311hleAfterSyscall |= HLE_AFTER_CURRENT_CALLBACKS;312}313314void hleReSchedule(const char *reason)315{316#ifdef _DEBUG317_dbg_assert_msg_(reason != nullptr && strlen(reason) < 256, "hleReSchedule: Invalid or too long reason.");318#endif319320hleAfterSyscall |= HLE_AFTER_RESCHED;321322if (!reason)323hleAfterSyscallReschedReason = "Invalid reason";324else325hleAfterSyscallReschedReason = reason;326}327328void hleReSchedule(bool callbacks, const char *reason)329{330hleReSchedule(reason);331if (callbacks)332hleAfterSyscall |= HLE_AFTER_RESCHED_CALLBACKS;333}334335void hleRunInterrupts()336{337hleAfterSyscall |= HLE_AFTER_RUN_INTERRUPTS;338}339340void hleDebugBreak()341{342hleAfterSyscall |= HLE_AFTER_DEBUG_BREAK;343}344345void hleSkipDeadbeef()346{347hleAfterSyscall |= HLE_AFTER_SKIP_DEADBEEF;348}349350// Pauses execution after an HLE call.351bool hleExecuteDebugBreak(const HLEFunction &func)352{353const u32 NID_SUSPEND_INTR = 0x092968F4, NID_RESUME_INTR = 0x5F10D406;354355// Never break on these, they're noise.356u32 blacklistedNIDs[] = {NID_SUSPEND_INTR, NID_RESUME_INTR, NID_IDLE};357for (size_t i = 0; i < ARRAY_SIZE(blacklistedNIDs); ++i)358{359if (func.ID == blacklistedNIDs[i])360return false;361}362363Core_EnableStepping(true, "hle.step", latestSyscallPC);364return true;365}366367u32 hleDelayResult(u32 result, const char *reason, int usec) {368if (!__KernelIsDispatchEnabled()) {369WARN_LOG(Log::HLE, "%s: Dispatch disabled, not delaying HLE result (right thing to do?)", latestSyscall ? latestSyscall->name : "?");370} else {371SceUID thread = __KernelGetCurThread();372if (KernelIsThreadWaiting(thread))373ERROR_LOG(Log::HLE, "%s: Delaying a thread that's already waiting", latestSyscall ? latestSyscall->name : "?");374CoreTiming::ScheduleEvent(usToCycles(usec), delayedResultEvent, thread);375__KernelWaitCurThread(WAITTYPE_HLEDELAY, 1, result, 0, false, reason);376}377return result;378}379380u64 hleDelayResult(u64 result, const char *reason, int usec) {381if (!__KernelIsDispatchEnabled()) {382WARN_LOG(Log::HLE, "%s: Dispatch disabled, not delaying HLE result (right thing to do?)", latestSyscall ? latestSyscall->name : "?");383} else {384SceUID thread = __KernelGetCurThread();385if (KernelIsThreadWaiting(thread))386ERROR_LOG(Log::HLE, "%s: Delaying a thread that's already waiting", latestSyscall ? latestSyscall->name : "?");387u64 param = (result & 0xFFFFFFFF00000000) | thread;388CoreTiming::ScheduleEvent(usToCycles(usec), delayedResultEvent, param);389__KernelWaitCurThread(WAITTYPE_HLEDELAY, 1, (u32)result, 0, false, reason);390}391return result;392}393394void hleEatCycles(int cycles) {395// Maybe this should Idle, at least for larger delays? Could that cause issues?396currentMIPS->downcount -= cycles;397}398399void hleEatMicro(int usec) {400hleEatCycles((int) usToCycles(usec));401}402403bool hleIsKernelMode() {404return latestSyscall && (latestSyscall->flags & HLE_KERNEL_SYSCALL) != 0;405}406407void hleEnqueueCall(u32 func, int argc, const u32 *argv, PSPAction *afterAction) {408std::vector<u32> args;409args.resize(argc);410memcpy(args.data(), argv, argc * sizeof(u32));411412enqueuedMipsCalls.push_back({ func, afterAction, args });413414hleAfterSyscall |= HLE_AFTER_QUEUED_CALLS;415}416417void hleFlushCalls() {418u32 &sp = currentMIPS->r[MIPS_REG_SP];419PSPPointer<HLEMipsCallStack> stackData;420VERBOSE_LOG(Log::HLE, "Flushing %d HLE mips calls from %s, sp=%08x", (int)enqueuedMipsCalls.size(), latestSyscall ? latestSyscall->name : "?", sp);421422// First, we'll add a marker for the final return.423sp -= sizeof(HLEMipsCallStack);424stackData.ptr = sp;425stackData->nextOff = 0xFFFFFFFF;426stackData->ra = currentMIPS->pc;427stackData->v0 = currentMIPS->r[MIPS_REG_V0];428stackData->v1 = currentMIPS->r[MIPS_REG_V1];429430// Now we'll set up the first in the chain.431currentMIPS->pc = enqueuedMipsCalls[0].func;432currentMIPS->r[MIPS_REG_RA] = HLEMipsCallReturnAddress();433for (int i = 0; i < (int)enqueuedMipsCalls[0].args.size(); i++) {434currentMIPS->r[MIPS_REG_A0 + i] = enqueuedMipsCalls[0].args[i];435}436437// For stack info, process the first enqueued call last, so we run it first.438// We don't actually need to store 0's args, but keep it consistent.439for (int i = (int)enqueuedMipsCalls.size() - 1; i >= 0; --i) {440auto &info = enqueuedMipsCalls[i];441u32 stackRequired = (int)info.args.size() * sizeof(u32) + sizeof(HLEMipsCallStack);442u32 stackAligned = (stackRequired + 0xF) & ~0xF;443444sp -= stackAligned;445stackData.ptr = sp;446stackData->nextOff = stackAligned;447stackData->func = info.func;448if (info.action) {449stackData->actionIndex = (int)mipsCallActions.size();450mipsCallActions.push_back(info.action);451} else {452stackData->actionIndex = 0xFFFFFFFF;453}454stackData->argc = (int)info.args.size();455for (int j = 0; j < (int)info.args.size(); ++j) {456Memory::Write_U32(info.args[j], sp + sizeof(HLEMipsCallStack) + j * sizeof(u32));457}458}459enqueuedMipsCalls.clear();460461DEBUG_LOG(Log::HLE, "Executing HLE mips call at %08x, sp=%08x", currentMIPS->pc, sp);462}463464void HLEReturnFromMipsCall() {465u32 &sp = currentMIPS->r[MIPS_REG_SP];466PSPPointer<HLEMipsCallStack> stackData;467468// At this point, we may have another mips call to run, or be at the end...469stackData.ptr = sp;470471if ((stackData->nextOff & 0x0000000F) != 0 || !Memory::IsValidAddress(sp + stackData->nextOff)) {472ERROR_LOG(Log::HLE, "Corrupt stack on HLE mips call return: %08x", stackData->nextOff);473Core_UpdateState(CORE_RUNTIME_ERROR);474return;475}476477if (stackData->actionIndex != 0xFFFFFFFF && stackData->actionIndex < (u32)mipsCallActions.size()) {478PSPAction *&action = mipsCallActions[stackData->actionIndex];479VERBOSE_LOG(Log::HLE, "Executing action for HLE mips call at %08x, sp=%08x", stackData->func, sp);480481// Search for the saved v0/v1 values, to preserve the PSPAction API...482PSPPointer<HLEMipsCallStack> finalMarker = stackData;483while ((finalMarker->nextOff & 0x0000000F) == 0 && Memory::IsValidAddress(finalMarker.ptr + finalMarker->nextOff)) {484finalMarker.ptr += finalMarker->nextOff;485}486487if (finalMarker->nextOff != 0xFFFFFFFF) {488ERROR_LOG(Log::HLE, "Corrupt stack on HLE mips call return action: %08x", finalMarker->nextOff);489Core_UpdateState(CORE_RUNTIME_ERROR);490return;491}492493MipsCall mc;494mc.savedV0 = finalMarker->v0;495mc.savedV1 = finalMarker->v1;496action->run(mc);497finalMarker->v0 = mc.savedV0;498finalMarker->v1 = mc.savedV1;499500delete action;501action = nullptr;502503// Note: the action could actually enqueue more, adding another layer on stack after this.504}505506sp += stackData->nextOff;507stackData.ptr = sp;508509if (stackData->nextOff == 0xFFFFFFFF) {510// We're done. Grab the HLE result's v0/v1 and return from the syscall.511currentMIPS->pc = stackData->ra;512currentMIPS->r[MIPS_REG_V0] = stackData->v0;513currentMIPS->r[MIPS_REG_V1] = stackData->v1;514515sp += sizeof(HLEMipsCallStack);516517bool canClear = true;518for (auto p : mipsCallActions) {519canClear = canClear && p == nullptr;520}521if (canClear) {522mipsCallActions.clear();523}524525VERBOSE_LOG(Log::HLE, "Finished HLE mips calls, v0=%08x, sp=%08x", currentMIPS->r[MIPS_REG_V0], sp);526return;527}528529// Alright, we have another to call.530hleSkipDeadbeef();531currentMIPS->pc = stackData->func;532currentMIPS->r[MIPS_REG_RA] = HLEMipsCallReturnAddress();533for (int i = 0; i < (int)stackData->argc; i++) {534currentMIPS->r[MIPS_REG_A0 + i] = Memory::Read_U32(sp + sizeof(HLEMipsCallStack) + i * sizeof(u32));535}536DEBUG_LOG(Log::HLE, "Executing next HLE mips call at %08x, sp=%08x", currentMIPS->pc, sp);537}538539const static u32 deadbeefRegs[12] = {0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF};540inline static void SetDeadbeefRegs()541{542// Not exactly the same, but any time a syscall happens, it should clear ll.543currentMIPS->llBit = 0;544545if (g_Config.bSkipDeadbeefFilling)546return;547548currentMIPS->r[MIPS_REG_COMPILER_SCRATCH] = 0xDEADBEEF;549// Set all the arguments and temp regs.550memcpy(¤tMIPS->r[MIPS_REG_A0], deadbeefRegs, sizeof(deadbeefRegs));551currentMIPS->r[MIPS_REG_T8] = 0xDEADBEEF;552currentMIPS->r[MIPS_REG_T9] = 0xDEADBEEF;553554currentMIPS->lo = 0xDEADBEEF;555currentMIPS->hi = 0xDEADBEEF;556}557558inline void hleFinishSyscall(const HLEFunction &info)559{560if ((hleAfterSyscall & HLE_AFTER_SKIP_DEADBEEF) == 0)561SetDeadbeefRegs();562563if ((hleAfterSyscall & HLE_AFTER_QUEUED_CALLS) != 0)564hleFlushCalls();565if ((hleAfterSyscall & HLE_AFTER_CURRENT_CALLBACKS) != 0 && (hleAfterSyscall & HLE_AFTER_RESCHED_CALLBACKS) == 0)566__KernelForceCallbacks();567568if ((hleAfterSyscall & HLE_AFTER_RUN_INTERRUPTS) != 0)569__RunOnePendingInterrupt();570571if ((hleAfterSyscall & HLE_AFTER_RESCHED_CALLBACKS) != 0)572__KernelReSchedule(true, hleAfterSyscallReschedReason);573else if ((hleAfterSyscall & HLE_AFTER_RESCHED) != 0)574__KernelReSchedule(hleAfterSyscallReschedReason);575576if ((hleAfterSyscall & HLE_AFTER_DEBUG_BREAK) != 0)577{578if (!hleExecuteDebugBreak(info))579{580// We'll do it next syscall.581hleAfterSyscall = HLE_AFTER_DEBUG_BREAK;582hleAfterSyscallReschedReason = 0;583return;584}585}586587hleAfterSyscall = HLE_AFTER_NOTHING;588hleAfterSyscallReschedReason = 0;589}590591static void updateSyscallStats(int modulenum, int funcnum, double total)592{593const char *name = moduleDB[modulenum].funcTable[funcnum].name;594// Ignore this one, especially for msInSyscalls (although that ignores CoreTiming events.)595if (0 == strcmp(name, "_sceKernelIdle"))596return;597598if (total > kernelStats.slowestSyscallTime)599{600kernelStats.slowestSyscallTime = total;601kernelStats.slowestSyscallName = name;602}603kernelStats.msInSyscalls += total;604605KernelStatsSyscall statCall(modulenum, funcnum);606auto summedStat = kernelStats.summedMsInSyscalls.find(statCall);607if (summedStat == kernelStats.summedMsInSyscalls.end())608{609kernelStats.summedMsInSyscalls[statCall] = total;610if (total > kernelStats.summedSlowestSyscallTime)611{612kernelStats.summedSlowestSyscallTime = total;613kernelStats.summedSlowestSyscallName = name;614}615}616else617{618double newTotal = kernelStats.summedMsInSyscalls[statCall] += total;619if (newTotal > kernelStats.summedSlowestSyscallTime)620{621kernelStats.summedSlowestSyscallTime = newTotal;622kernelStats.summedSlowestSyscallName = name;623}624}625}626627inline void CallSyscallWithFlags(const HLEFunction *info)628{629latestSyscall = info;630latestSyscallPC = currentMIPS->pc;631const u32 flags = info->flags;632633if (flags & HLE_CLEAR_STACK_BYTES) {634u32 stackStart = __KernelGetCurThreadStackStart();635if (currentMIPS->r[MIPS_REG_SP] - info->stackBytesToClear >= stackStart) {636Memory::Memset(currentMIPS->r[MIPS_REG_SP] - info->stackBytesToClear, 0, info->stackBytesToClear, "HLEStackClear");637}638}639640if ((flags & HLE_NOT_DISPATCH_SUSPENDED) && !__KernelIsDispatchEnabled()) {641RETURN(hleLogDebug(Log::HLE, SCE_KERNEL_ERROR_CAN_NOT_WAIT, "dispatch suspended"));642} else if ((flags & HLE_NOT_IN_INTERRUPT) && __IsInInterrupt()) {643RETURN(hleLogDebug(Log::HLE, SCE_KERNEL_ERROR_ILLEGAL_CONTEXT, "in interrupt"));644} else {645info->func();646}647648if (hleAfterSyscall != HLE_AFTER_NOTHING)649hleFinishSyscall(*info);650else651SetDeadbeefRegs();652}653654inline void CallSyscallWithoutFlags(const HLEFunction *info)655{656latestSyscall = info;657latestSyscallPC = currentMIPS->pc;658info->func();659660if (hleAfterSyscall != HLE_AFTER_NOTHING)661hleFinishSyscall(*info);662else663SetDeadbeefRegs();664}665666const HLEFunction *GetSyscallFuncPointer(MIPSOpcode op)667{668u32 callno = (op >> 6) & 0xFFFFF; //20 bits669int funcnum = callno & 0xFFF;670int modulenum = (callno & 0xFF000) >> 12;671if (funcnum == 0xfff) {672ERROR_LOG(Log::HLE, "Unknown syscall: Module: %s (module: %d func: %d)", modulenum > (int)moduleDB.size() ? "(unknown)" : moduleDB[modulenum].name, modulenum, funcnum);673return NULL;674}675if (modulenum >= (int)moduleDB.size()) {676ERROR_LOG(Log::HLE, "Syscall had bad module number %d - probably executing garbage", modulenum);677return NULL;678}679if (funcnum >= moduleDB[modulenum].numFunctions) {680ERROR_LOG(Log::HLE, "Syscall had bad function number %d in module %d - probably executing garbage", funcnum, modulenum);681return NULL;682}683return &moduleDB[modulenum].funcTable[funcnum];684}685686void *GetQuickSyscallFunc(MIPSOpcode op) {687if (coreCollectDebugStats)688return nullptr;689690const HLEFunction *info = GetSyscallFuncPointer(op);691if (!info || !info->func)692return nullptr;693DEBUG_LOG(Log::HLE, "Compiling syscall to %s", info->name);694695// TODO: Do this with a flag?696if (op == idleOp)697return (void *)info->func;698if (info->flags != 0)699return (void *)&CallSyscallWithFlags;700return (void *)&CallSyscallWithoutFlags;701}702703static double hleSteppingTime = 0.0;704void hleSetSteppingTime(double t) {705hleSteppingTime += t;706}707708static double hleFlipTime = 0.0;709void hleSetFlipTime(double t) {710hleFlipTime = t;711}712713void CallSyscall(MIPSOpcode op)714{715PROFILE_THIS_SCOPE("syscall");716double start = 0.0; // need to initialize to fix the race condition where coreCollectDebugStats is enabled in the middle of this func.717if (coreCollectDebugStats) {718start = time_now_d();719}720721const HLEFunction *info = GetSyscallFuncPointer(op);722if (!info) {723RETURN(SCE_KERNEL_ERROR_LIBRARY_NOT_YET_LINKED);724return;725}726727if (info->func) {728if (op == idleOp)729info->func();730else if (info->flags != 0)731CallSyscallWithFlags(info);732else733CallSyscallWithoutFlags(info);734}735else {736RETURN(SCE_KERNEL_ERROR_LIBRARY_NOT_YET_LINKED);737ERROR_LOG_REPORT(Log::HLE, "Unimplemented HLE function %s", info->name ? info->name : "(\?\?\?)");738}739740if (coreCollectDebugStats) {741u32 callno = (op >> 6) & 0xFFFFF; //20 bits742int funcnum = callno & 0xFFF;743int modulenum = (callno & 0xFF000) >> 12;744double total = time_now_d() - start - hleSteppingTime;745if (total >= hleFlipTime)746total -= hleFlipTime;747_dbg_assert_msg_(total >= 0.0, "Time spent in syscall became negative");748hleSteppingTime = 0.0;749hleFlipTime = 0.0;750updateSyscallStats(modulenum, funcnum, total);751}752}753754size_t hleFormatLogArgs(char *message, size_t sz, const char *argmask) {755char *p = message;756size_t used = 0;757758#define APPEND_FMT(...) do { \759if (used < sz) { \760size_t c = snprintf(p, sz - used, __VA_ARGS__); \761used += c; \762p += c; \763} \764} while (false)765766int reg = 0;767int regf = 0;768for (size_t i = 0, n = strlen(argmask); i < n; ++i, ++reg) {769u32 regval;770if (reg < 8) {771regval = PARAM(reg);772} else {773u32 sp = currentMIPS->r[MIPS_REG_SP];774// Goes upward on stack.775// NOTE: Currently we only support > 8 for 32-bit integer args.776regval = Memory::Read_U32(sp + (reg - 8) * 4);777}778779switch (argmask[i]) {780case 'p':781if (Memory::IsValidAddress(regval)) {782APPEND_FMT("%08x[%08x]", regval, Memory::Read_U32(regval));783} else {784APPEND_FMT("%08x[invalid]", regval);785}786break;787788case 'P':789if (Memory::IsValidAddress(regval)) {790APPEND_FMT("%08x[%016llx]", regval, Memory::Read_U64(regval));791} else {792APPEND_FMT("%08x[invalid]", regval);793}794break;795796case 's':797if (Memory::IsValidAddress(regval)) {798const char *s = Memory::GetCharPointer(regval);799const int safeLen = Memory::ValidSize(regval, 128);800if (strnlen(s, safeLen) >= safeLen) {801APPEND_FMT("%.*s...", safeLen, Memory::GetCharPointer(regval));802} else {803APPEND_FMT("%.*s", safeLen, Memory::GetCharPointer(regval));804}805} else {806APPEND_FMT("(invalid)");807}808break;809810case 'x':811APPEND_FMT("%08x", regval);812break;813814case 'i':815APPEND_FMT("%d", regval);816break;817818case 'X':819case 'I':820// 64-bit regs are always aligned.821if ((reg & 1))822++reg;823APPEND_FMT("%016llx", PARAM64(reg));824++reg;825break;826827case 'f':828APPEND_FMT("%f", PARAMF(regf++));829// This doesn't consume a gp reg.830--reg;831break;832833// TODO: Double? Does it ever happen?834835default:836_dbg_assert_msg_(false, "Invalid argmask character: %c", argmask[i]);837APPEND_FMT(" -- invalid arg format: %c -- %08x", argmask[i], regval);838break;839}840if (i + 1 < n) {841APPEND_FMT(", ");842}843}844845if (used > sz) {846message[sz - 1] = '\0';847} else {848message[used] = '\0';849}850851#undef APPEND_FMT852return used;853}854855void hleDoLogInternal(Log t, LogLevel level, u64 res, const char *file, int line, const char *reportTag, char retmask, const char *reason, const char *formatted_reason) {856char formatted_args[4096];857const char *funcName = "?";858u32 funcFlags = 0;859if (latestSyscall) {860_dbg_assert_(latestSyscall->argmask != nullptr);861hleFormatLogArgs(formatted_args, sizeof(formatted_args), latestSyscall->argmask);862863// This acts as an override (for error returns which are usually hex.)864if (retmask == '\0')865retmask = latestSyscall->retmask;866867funcName = latestSyscall->name;868funcFlags = latestSyscall->flags;869} else {870strcpy(formatted_args, "?");871}872873const char *fmt;874if (retmask == 'x') {875fmt = "%s%08llx=%s(%s)%s";876// Truncate the high bits of the result (from any sign extension.)877res = (u32)res;878} else if (retmask == 'i' || retmask == 'I') {879fmt = "%s%lld=%s(%s)%s";880} else if (retmask == 'f') {881// TODO: For now, floats are just shown as bits.882fmt = "%s%08x=%s(%s)%s";883} else {884_dbg_assert_msg_(false, "Invalid return format: %c", retmask);885fmt = "%s%08llx=%s(%s)%s";886}887888const char *kernelFlag = (funcFlags & HLE_KERNEL_SYSCALL) != 0 ? "K " : "";889GenericLog(level, t, file, line, fmt, kernelFlag, res, funcName, formatted_args, formatted_reason);890891if (reportTag != nullptr) {892// A blank string means always log, not just once.893if (reportTag[0] == '\0' || Reporting::ShouldLogNTimes(reportTag, 1)) {894// Here we want the original key, so that different args, etc. group together.895std::string key = std::string(kernelFlag) + std::string("%08x=") + funcName + "(%s)";896if (reason != nullptr)897key += std::string(": ") + reason;898899char formatted_message[8192];900snprintf(formatted_message, sizeof(formatted_message), fmt, kernelFlag, res, funcName, formatted_args, formatted_reason);901Reporting::ReportMessageFormatted(key.c_str(), formatted_message);902}903}904}905906907