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/MemFault.cpp
Views: 1401
// Copyright (C) 2020 PPSSPP Project12// 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 "ppsspp_config.h"1819#include <cstdint>20#include <unordered_set>21#include <mutex>22#include <sstream>2324#include "Common/StringUtils.h"25#include "Common/MachineContext.h"2627#if PPSSPP_ARCH(AMD64) || PPSSPP_ARCH(X86)28#include "Common/x64Analyzer.h"2930#elif PPSSPP_ARCH(ARM64)31#include "Core/Util/DisArm64.h"32#elif PPSSPP_ARCH(ARM)33#include "ext/disarm.h"34#endif3536#include "Common/Log.h"37#include "Core/Config.h"38#include "Core/Core.h"39#include "Core/MemFault.h"40#include "Core/MemMap.h"41#include "Core/MIPS/JitCommon/JitCommon.h"42#include "Core/Debugger/SymbolMap.h"4344// Stack walking stuff45#include "Core/MIPS/MIPSStackWalk.h"46#include "Core/MIPS/MIPSDebugInterface.h"47#include "Core/HLE/sceKernelThread.h"4849namespace Memory {5051static int64_t g_numReportedBadAccesses = 0;52const uint8_t *g_lastCrashAddress;53MemoryExceptionType g_lastMemoryExceptionType;54static bool inCrashHandler = false;5556std::unordered_set<const uint8_t *> g_ignoredAddresses;5758void MemFault_Init() {59g_numReportedBadAccesses = 0;60g_lastCrashAddress = nullptr;61g_lastMemoryExceptionType = MemoryExceptionType::NONE;62g_ignoredAddresses.clear();63}6465bool MemFault_MayBeResumable() {66return g_lastCrashAddress != nullptr;67}6869void MemFault_IgnoreLastCrash() {70g_ignoredAddresses.insert(g_lastCrashAddress);71}7273#ifdef MACHINE_CONTEXT_SUPPORTED7475static bool DisassembleNativeAt(const uint8_t *codePtr, int instructionSize, std::string *dest) {76#if PPSSPP_ARCH(AMD64) || PPSSPP_ARCH(X86)77auto lines = DisassembleX86(codePtr, instructionSize);78if (!lines.empty()) {79*dest = lines[0];80return true;81}82#elif PPSSPP_ARCH(ARM64)83auto lines = DisassembleArm64(codePtr, instructionSize);84if (!lines.empty()) {85*dest = lines[0];86return true;87}88#elif PPSSPP_ARCH(ARM)89auto lines = DisassembleArm2(codePtr, instructionSize);90if (!lines.empty()) {91*dest = lines[0];92return true;93}94#elif PPSSPP_ARCH(RISCV64)95auto lines = DisassembleRV64(codePtr, instructionSize);96if (!lines.empty()) {97*dest = lines[0];98return true;99}100#endif101return false;102}103104bool HandleFault(uintptr_t hostAddress, void *ctx) {105if (inCrashHandler)106return false;107inCrashHandler = true;108109SContext *context = (SContext *)ctx;110const uint8_t *codePtr = (uint8_t *)(context->CTX_PC);111112std::lock_guard<std::recursive_mutex> guard(MIPSComp::jitLock);113114// We set this later if we think it can be resumed from.115g_lastCrashAddress = nullptr;116117// TODO: Check that codePtr is within the current JIT space.118bool inJitSpace = MIPSComp::jit && MIPSComp::jit->CodeInRange(codePtr);119if (!inJitSpace) {120// This is a crash in non-jitted code. Not something we want to handle here, ignore.121// Actually, we could handle crashes from the IR interpreter here, although recovering the call stack122// might be tricky...123inCrashHandler = false;124return false;125}126127uintptr_t baseAddress = (uintptr_t)base;128#ifdef MASKED_PSP_MEMORY129const uintptr_t addressSpaceSize = 0x40000000ULL;130#else131const uintptr_t addressSpaceSize = 0x100000000ULL;132#endif133134// Check whether hostAddress is within the PSP memory space, which (likely) means it was a guest executable that did the bad access.135bool invalidHostAddress = hostAddress == (uintptr_t)0xFFFFFFFFFFFFFFFFULL;136if (hostAddress < baseAddress || hostAddress >= baseAddress + addressSpaceSize) {137// Host address outside - this was a different kind of crash.138if (!invalidHostAddress) {139inCrashHandler = false;140return false;141}142}143144// OK, a guest executable did a bad access. Let's handle it.145146uint32_t guestAddress = invalidHostAddress ? 0xFFFFFFFFUL : (uint32_t)(hostAddress - baseAddress);147148// TODO: Share the struct between the various analyzers, that will allow us to share most of149// the implementations here.150bool success = false;151152MemoryExceptionType type = MemoryExceptionType::NONE;153154int instructionSize = 4;155#if PPSSPP_ARCH(AMD64) || PPSSPP_ARCH(X86)156// X86, X86-64. Variable instruction size so need to analyze the mov instruction in detail.157instructionSize = 15;158159// To ignore the access, we need to disassemble the instruction and modify context->CTX_PC160LSInstructionInfo info{};161success = X86AnalyzeMOV(codePtr, info);162if (success)163instructionSize = info.instructionSize;164#elif PPSSPP_ARCH(ARM64)165uint32_t word;166memcpy(&word, codePtr, 4);167// To ignore the access, we need to disassemble the instruction and modify context->CTX_PC168Arm64LSInstructionInfo info{};169success = Arm64AnalyzeLoadStore((uint64_t)codePtr, word, &info);170#elif PPSSPP_ARCH(ARM)171uint32_t word;172memcpy(&word, codePtr, 4);173// To ignore the access, we need to disassemble the instruction and modify context->CTX_PC174ArmLSInstructionInfo info{};175success = ArmAnalyzeLoadStore((uint32_t)codePtr, word, &info);176#elif PPSSPP_ARCH(RISCV64)177// TODO: Put in a disassembler.178struct RiscVLSInstructionInfo {179int instructionSize;180bool isIntegerLoadStore;181bool isFPLoadStore;182int size;183bool isMemoryWrite;184};185186uint32_t word;187memcpy(&word, codePtr, 4);188189RiscVLSInstructionInfo info{};190// Compressed instructions have low bits 00, 01, or 10.191info.instructionSize = (word & 3) == 3 ? 4 : 2;192instructionSize = info.instructionSize;193194success = true;195switch (word & 0x7F) {196case 3:197info.isIntegerLoadStore = true;198info.size = 1 << ((word >> 12) & 3);199break;200case 7:201info.isFPLoadStore = true;202info.size = 1 << ((word >> 12) & 3);203break;204case 35:205info.isIntegerLoadStore = true;206info.isMemoryWrite = true;207info.size = 1 << ((word >> 12) & 3);208break;209case 39:210info.isFPLoadStore = true;211info.isMemoryWrite = true;212info.size = 1 << ((word >> 12) & 3);213break;214default:215// Compressed instruction.216switch (word & 0x6003) {217case 0x4000:218case 0x4002:219case 0x6000:220case 0x6002:221info.isIntegerLoadStore = true;222info.size = (word & 0x2000) != 0 ? 8 : 4;223info.isMemoryWrite = (word & 0x8000) != 0;224break;225case 0x2000:226case 0x2002:227info.isFPLoadStore = true;228info.size = 8;229info.isMemoryWrite = (word & 0x8000) != 0;230break;231default:232// Not a read or a write.233success = false;234break;235}236break;237}238#endif239240if (MIPSComp::jit && MIPSComp::jit->IsAtDispatchFetch(codePtr)) {241u32 targetAddr = currentMIPS->pc; // bad approximation242// TODO: Do the other archs and platforms.243#if PPSSPP_ARCH(AMD64) && PPSSPP_PLATFORM(WINDOWS)244// We know which register the address is in, look in Asm.cpp.245targetAddr = (uint32_t)context->Rax;246#endif247Core_ExecException(targetAddr, currentMIPS->pc, ExecExceptionType::JUMP);248// Redirect execution to a crash handler that will switch to CoreState::CORE_RUNTIME_ERROR immediately.249uintptr_t crashHandler = (uintptr_t)MIPSComp::jit->GetCrashHandler();250if (crashHandler != 0) {251context->CTX_PC = crashHandler;252ERROR_LOG(Log::MemMap, "Bad execution access detected, halting: %08x (last known pc %08x, host: %p)", targetAddr, currentMIPS->pc, (void *)hostAddress);253inCrashHandler = false;254return true;255}256257type = MemoryExceptionType::UNKNOWN;258} else if (success) {259if (info.isMemoryWrite) {260type = MemoryExceptionType::WRITE_WORD;261} else {262type = MemoryExceptionType::READ_WORD;263}264} else {265type = MemoryExceptionType::UNKNOWN;266}267268g_lastMemoryExceptionType = type;269270bool handled = true;271if (success && (g_Config.bIgnoreBadMemAccess || g_ignoredAddresses.find(codePtr) != g_ignoredAddresses.end())) {272if (!info.isMemoryWrite) {273// It was a read. Fill the destination register with 0.274// TODO275}276// Move on to the next instruction. Note that handling bad accesses like this is pretty slow.277context->CTX_PC += info.instructionSize;278g_numReportedBadAccesses++;279if (g_numReportedBadAccesses < 100) {280ERROR_LOG(Log::MemMap, "Bad memory access detected and ignored: %08x (%p)", guestAddress, (void *)hostAddress);281}282} else {283std::string infoString = "";284std::string temp;285if (MIPSComp::jit && MIPSComp::jit->DescribeCodePtr(codePtr, temp)) {286infoString += temp + "\n";287}288temp.clear();289if (DisassembleNativeAt(codePtr, instructionSize, &temp)) {290infoString += temp + "\n";291}292293// Either bIgnoreBadMemAccess is off, or we failed recovery analysis.294// We can't ignore this memory access.295uint32_t approximatePC = currentMIPS->pc;296// TODO: Determine access size from the disassembled native instruction. We have some partial info already,297// just need to clean it up.298Core_MemoryExceptionInfo(guestAddress, 0, approximatePC, type, infoString, true);299300// There's a small chance we can resume from this type of crash.301g_lastCrashAddress = codePtr;302303// Redirect execution to a crash handler that will switch to CoreState::CORE_RUNTIME_ERROR immediately.304uintptr_t crashHandler = 0;305if (MIPSComp::jit)306crashHandler = (uintptr_t)MIPSComp::jit->GetCrashHandler();307if (crashHandler != 0)308context->CTX_PC = crashHandler;309else310handled = false;311ERROR_LOG(Log::MemMap, "Bad memory access detected! %08x (%p) Stopping emulation. Info:\n%s", guestAddress, (void *)hostAddress, infoString.c_str());312}313314inCrashHandler = false;315return handled;316}317318#else319320bool HandleFault(uintptr_t hostAddress, void *ctx) {321ERROR_LOG(Log::MemMap, "Exception handling not supported");322return false;323}324325#endif326327} // namespace Memory328329std::vector<MIPSStackWalk::StackFrame> WalkCurrentStack(int threadID) {330DebugInterface *cpuDebug = currentDebugMIPS;331332auto threads = GetThreadsInfo();333uint32_t entry = cpuDebug->GetPC();334uint32_t stackTop = 0;335for (const DebugThreadInfo &th : threads) {336if ((threadID == -1 && th.isCurrent) || th.id == threadID) {337entry = th.entrypoint;338stackTop = th.initialStack;339break;340}341}342343uint32_t ra = cpuDebug->GetRegValue(0, MIPS_REG_RA);344uint32_t sp = cpuDebug->GetRegValue(0, MIPS_REG_SP);345return MIPSStackWalk::Walk(cpuDebug->GetPC(), ra, sp, entry, stackTop);346}347348std::string FormatStackTrace(const std::vector<MIPSStackWalk::StackFrame> &frames) {349std::stringstream str;350for (const auto &frame : frames) {351std::string desc = g_symbolMap->GetDescription(frame.entry);352str << StringFromFormat("%s (%08x+%03x, pc: %08x sp: %08x)\n", desc.c_str(), frame.entry, frame.pc - frame.entry, frame.pc, frame.sp);353}354return str.str();355}356357358