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/MIPS/x86/JitSafeMem.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 "ppsspp_config.h"1819#if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)2021#include "Core/Config.h"22#include "Core/Debugger/Breakpoints.h"23#include "Core/MemMap.h"24#include "Core/MIPS/JitCommon/JitCommon.h"25#include "Core/MIPS/MIPSAnalyst.h"26#include "Core/MIPS/x86/Jit.h"27#include "Core/MIPS/x86/JitSafeMem.h"28#include "Core/System.h"2930namespace MIPSComp31{32using namespace Gen;33using namespace X64JitConstants;3435JitSafeMem::JitSafeMem(Jit *jit, MIPSGPReg raddr, s32 offset, u32 alignMask)36: jit_(jit), raddr_(raddr), offset_(offset), needsCheck_(false), needsSkip_(false), alignMask_(alignMask)37{38// Mask out the kernel RAM bit, because we'll end up with a negative offset to MEMBASEREG.39if (jit_->gpr.IsImm(raddr_))40iaddr_ = (jit_->gpr.GetImm(raddr_) + offset_) & 0x7FFFFFFF;41else42iaddr_ = (u32) -1;4344fast_ = g_Config.bFastMemory || raddr == MIPS_REG_SP;4546// If raddr_ is going to get loaded soon, load it now for more optimal code.47// We assume that it was already locked.48const int LOOKAHEAD_OPS = 3;49if (!jit_->gpr.R(raddr_).IsImm() && MIPSAnalyst::IsRegisterUsed(raddr_, jit_->GetCompilerPC() + 4, LOOKAHEAD_OPS))50jit_->gpr.MapReg(raddr_, true, false);51}5253bool JitSafeMem::PrepareWrite(OpArg &dest, int size)54{55size_ = size;56// If it's an immediate, we can do the write if valid.57if (iaddr_ != (u32) -1)58{59if (ImmValid())60{61u32 addr = (iaddr_ & alignMask_);62#ifdef MASKED_PSP_MEMORY63addr &= Memory::MEMVIEW32_MASK;64#endif6566#if PPSSPP_ARCH(32BIT)67dest = M(Memory::base + addr); // 32-bit only68#else69dest = MDisp(MEMBASEREG, addr);70#endif71return true;72}73else74return false;75}76// Otherwise, we always can do the write (conditionally.)77else78dest = PrepareMemoryOpArg(MEM_WRITE);79return true;80}8182bool JitSafeMem::PrepareRead(OpArg &src, int size)83{84size_ = size;85if (iaddr_ != (u32) -1)86{87if (ImmValid())88{89u32 addr = (iaddr_ & alignMask_);90#ifdef MASKED_PSP_MEMORY91addr &= Memory::MEMVIEW32_MASK;92#endif9394#if PPSSPP_ARCH(32BIT)95src = M(Memory::base + addr); // 32-bit only96#else97src = MDisp(MEMBASEREG, addr);98#endif99return true;100}101else102return false;103}104else105src = PrepareMemoryOpArg(MEM_READ);106return true;107}108109OpArg JitSafeMem::NextFastAddress(int suboffset)110{111if (iaddr_ != (u32) -1)112{113u32 addr = (iaddr_ + suboffset) & alignMask_;114#ifdef MASKED_PSP_MEMORY115addr &= Memory::MEMVIEW32_MASK;116#endif117118#if PPSSPP_ARCH(32BIT)119return M(Memory::base + addr); // 32-bit only120#else121return MDisp(MEMBASEREG, addr);122#endif123}124125_dbg_assert_msg_((suboffset & alignMask_) == suboffset, "suboffset must be aligned");126127#if PPSSPP_ARCH(32BIT)128return MDisp(xaddr_, (u32) Memory::base + offset_ + suboffset);129#else130return MComplex(MEMBASEREG, xaddr_, SCALE_1, offset_ + suboffset);131#endif132}133134OpArg JitSafeMem::PrepareMemoryOpArg(MemoryOpType type)135{136// We may not even need to move into EAX as a temporary.137bool needTemp = alignMask_ != 0xFFFFFFFF;138139#ifdef MASKED_PSP_MEMORY140bool needMask = true; // raddr_ != MIPS_REG_SP; // Commented out this speedhack due to low impact141// We always mask on 32 bit in fast memory mode.142needTemp = needTemp || (fast_ && needMask);143#endif144145if (jit_->gpr.R(raddr_).IsSimpleReg() && !needTemp)146{147jit_->gpr.MapReg(raddr_, true, false);148xaddr_ = jit_->gpr.RX(raddr_);149}150else151{152jit_->MOV(32, R(EAX), jit_->gpr.R(raddr_));153xaddr_ = EAX;154}155156if (!fast_)157{158// Is it in physical ram?159jit_->CMP(32, R(xaddr_), Imm32(PSP_GetKernelMemoryBase() - offset_));160tooLow_ = jit_->J_CC(CC_B);161jit_->CMP(32, R(xaddr_), Imm32(PSP_GetUserMemoryEnd() - offset_ - (size_ - 1)));162tooHigh_ = jit_->J_CC(CC_AE);163164// We may need to jump back up here.165safe_ = jit_->GetCodePtr();166}167else168{169#ifdef MASKED_PSP_MEMORY170if (needMask) {171jit_->AND(32, R(EAX), Imm32(Memory::MEMVIEW32_MASK));172}173#endif174}175176// TODO: This could be more optimal, but the common case is that we want xaddr_ not to include offset_.177// Since we need to align them after add, we add and subtract.178if (alignMask_ != 0xFFFFFFFF)179{180jit_->ADD(32, R(xaddr_), Imm32(offset_));181jit_->AND(32, R(xaddr_), Imm32(alignMask_));182jit_->SUB(32, R(xaddr_), Imm32(offset_));183}184185#if PPSSPP_ARCH(32BIT)186return MDisp(xaddr_, (u32) Memory::base + offset_);187#else188return MComplex(MEMBASEREG, xaddr_, SCALE_1, offset_);189#endif190}191192void JitSafeMem::PrepareSlowAccess()193{194// Skip the fast path (which the caller wrote just now.)195skip_ = jit_->J(true);196needsSkip_ = true;197jit_->SetJumpTarget(tooLow_);198jit_->SetJumpTarget(tooHigh_);199200// Might also be the scratchpad.201jit_->CMP(32, R(xaddr_), Imm32(PSP_GetScratchpadMemoryBase() - offset_));202FixupBranch tooLow = jit_->J_CC(CC_B);203jit_->CMP(32, R(xaddr_), Imm32(PSP_GetScratchpadMemoryEnd() - offset_ - (size_ - 1)));204jit_->J_CC(CC_B, safe_);205jit_->SetJumpTarget(tooLow);206}207208bool JitSafeMem::PrepareSlowWrite()209{210// If it's immediate, we only need a slow write on invalid.211if (iaddr_ != (u32) -1)212return !fast_ && !ImmValid();213214if (!fast_)215{216PrepareSlowAccess();217return true;218}219else220return false;221}222223void JitSafeMem::DoSlowWrite(const void *safeFunc, const OpArg &src, int suboffset) {224_dbg_assert_msg_(safeFunc != nullptr, "Safe func cannot be null");225226if (iaddr_ != (u32) -1)227jit_->MOV(32, R(EAX), Imm32((iaddr_ + suboffset) & alignMask_));228else229{230jit_->LEA(32, EAX, MDisp(xaddr_, offset_ + suboffset));231if (alignMask_ != 0xFFFFFFFF)232jit_->AND(32, R(EAX), Imm32(alignMask_));233}234235#if PPSSPP_ARCH(32BIT)236jit_->PUSH(EDX);237#endif238if (!src.IsSimpleReg(EDX)) {239jit_->MOV(32, R(EDX), src);240}241if (!g_Config.bIgnoreBadMemAccess) {242jit_->MOV(32, MIPSSTATE_VAR(pc), Imm32(jit_->GetCompilerPC()));243}244// This is a special jit-ABI'd function.245if (jit_->CanCALLDirect(safeFunc)) {246jit_->CALL(safeFunc);247} else {248// We can't safely flush a reg, but this shouldn't be normal.249IndirectCALL(safeFunc);250}251#if PPSSPP_ARCH(32BIT)252jit_->POP(EDX);253#endif254needsCheck_ = true;255}256257bool JitSafeMem::PrepareSlowRead(const void *safeFunc) {258_dbg_assert_msg_(safeFunc != nullptr, "Safe func cannot be null");259if (!fast_) {260if (iaddr_ != (u32) -1) {261// No slow read necessary.262if (ImmValid())263return false;264jit_->MOV(32, R(EAX), Imm32(iaddr_ & alignMask_));265} else {266PrepareSlowAccess();267jit_->LEA(32, EAX, MDisp(xaddr_, offset_));268if (alignMask_ != 0xFFFFFFFF)269jit_->AND(32, R(EAX), Imm32(alignMask_));270}271272if (!g_Config.bIgnoreBadMemAccess) {273jit_->MOV(32, MIPSSTATE_VAR(pc), Imm32(jit_->GetCompilerPC()));274}275// This is a special jit-ABI'd function.276if (jit_->CanCALLDirect(safeFunc)) {277jit_->CALL(safeFunc);278} else {279// We can't safely flush a reg, but this shouldn't be normal.280IndirectCALL(safeFunc);281}282needsCheck_ = true;283return true;284}285else286return false;287}288289void JitSafeMem::NextSlowRead(const void *safeFunc, int suboffset) {290_dbg_assert_msg_(safeFunc != nullptr, "Safe func cannot be null");291_dbg_assert_msg_(!fast_, "NextSlowRead() called in fast memory mode?");292293// For simplicity, do nothing for 0. We already read in PrepareSlowRead().294if (suboffset == 0)295return;296297if (jit_->gpr.IsImm(raddr_))298{299_dbg_assert_msg_(!Memory::IsValidAddress(iaddr_ + suboffset), "NextSlowRead() for an invalid immediate address?");300301jit_->MOV(32, R(EAX), Imm32((iaddr_ + suboffset) & alignMask_));302}303// For GPR, if xaddr_ was the dest register, this will be wrong. Don't use in GPR.304else305{306jit_->LEA(32, EAX, MDisp(xaddr_, offset_ + suboffset));307if (alignMask_ != 0xFFFFFFFF)308jit_->AND(32, R(EAX), Imm32(alignMask_));309}310311if (!g_Config.bIgnoreBadMemAccess) {312jit_->MOV(32, MIPSSTATE_VAR(pc), Imm32(jit_->GetCompilerPC()));313}314// This is a special jit-ABI'd function.315if (jit_->CanCALLDirect(safeFunc)) {316jit_->CALL(safeFunc);317} else {318// We can't safely flush a reg, but this shouldn't be normal.319IndirectCALL(safeFunc);320}321}322323bool JitSafeMem::ImmValid()324{325return iaddr_ != (u32) -1 && Memory::IsValidAddress(iaddr_) && Memory::IsValidAddress(iaddr_ + size_ - 1);326}327328void JitSafeMem::IndirectCALL(const void *safeFunc) {329#if PPSSPP_ARCH(32BIT)330jit_->PUSH(ECX);331jit_->SUB(PTRBITS, R(ESP), Imm8(16 - 4));332jit_->MOV(PTRBITS, R(ECX), ImmPtr(safeFunc));333jit_->CALLptr(R(RCX));334jit_->ADD(PTRBITS, R(ESP), Imm8(16 - 4));335jit_->POP(ECX);336#else337jit_->PUSH(RCX);338jit_->SUB(PTRBITS, R(RSP), Imm8(8));339jit_->MOV(PTRBITS, R(RCX), ImmPtr(safeFunc));340jit_->CALLptr(R(RCX));341jit_->ADD(64, R(RSP), Imm8(8));342jit_->POP(RCX);343#endif344}345346void JitSafeMem::Finish()347{348// Memory::Read_U32/etc. may have tripped coreState.349if (needsCheck_ && !g_Config.bIgnoreBadMemAccess)350jit_->js.afterOp |= JitState::AFTER_CORE_STATE;351if (needsSkip_)352jit_->SetJumpTarget(skip_);353for (auto it = skipChecks_.begin(), end = skipChecks_.end(); it != end; ++it)354jit_->SetJumpTarget(*it);355}356357static const int FUNCS_ARENA_SIZE = 512 * 1024;358359void JitSafeMemFuncs::Init(ThunkManager *thunks) {360using namespace Gen;361362AllocCodeSpace(FUNCS_ARENA_SIZE);363thunks_ = thunks;364365BeginWrite(1024);366readU32 = GetCodePtr();367CreateReadFunc(32, (const void *)&Memory::Read_U32);368readU16 = GetCodePtr();369CreateReadFunc(16, (const void *)&Memory::Read_U16);370readU8 = GetCodePtr();371CreateReadFunc(8, (const void *)&Memory::Read_U8);372373writeU32 = GetCodePtr();374CreateWriteFunc(32, (const void *)&Memory::Write_U32);375writeU16 = GetCodePtr();376CreateWriteFunc(16, (const void *)&Memory::Write_U16);377writeU8 = GetCodePtr();378CreateWriteFunc(8, (const void *)&Memory::Write_U8);379EndWrite();380}381382void JitSafeMemFuncs::Shutdown() {383ResetCodePtr(0);384FreeCodeSpace();385386readU32 = nullptr;387readU16 = nullptr;388readU8 = nullptr;389writeU32 = nullptr;390writeU16 = nullptr;391writeU8 = nullptr;392}393394// Mini ABI:395// Read funcs take address in EAX, return in RAX.396// Write funcs take address in EAX, data in RDX.397// On x86-32, Write funcs also have an extra 4 bytes on the stack.398399void JitSafeMemFuncs::CreateReadFunc(int bits, const void *fallbackFunc) {400CheckDirectEAX();401402// Since we were CALLed, we need to align the stack before calling C++.403#if PPSSPP_ARCH(32BIT)404SUB(32, R(ESP), Imm8(16 - 4));405ABI_CallFunctionA(thunks_->ProtectFunction(fallbackFunc, 1), R(EAX));406ADD(32, R(ESP), Imm8(16 - 4));407#else408SUB(64, R(RSP), Imm8(0x28));409ABI_CallFunctionA(thunks_->ProtectFunction(fallbackFunc, 1), R(EAX));410ADD(64, R(RSP), Imm8(0x28));411#endif412413RET();414415StartDirectAccess();416417#if PPSSPP_ARCH(32BIT)418MOVZX(32, bits, EAX, MDisp(EAX, (u32)Memory::base));419#else420MOVZX(32, bits, EAX, MRegSum(MEMBASEREG, EAX));421#endif422423RET();424}425426void JitSafeMemFuncs::CreateWriteFunc(int bits, const void *fallbackFunc) {427CheckDirectEAX();428429// Since we were CALLed, we need to align the stack before calling C++.430#if PPSSPP_ARCH(32BIT)431// 4 for return, 4 for saved reg on stack.432SUB(32, R(ESP), Imm8(16 - 4 - 4));433ABI_CallFunctionAA(thunks_->ProtectFunction(fallbackFunc, 2), R(EDX), R(EAX));434ADD(32, R(ESP), Imm8(16 - 4 - 4));435#else436SUB(64, R(RSP), Imm8(0x28));437ABI_CallFunctionAA(thunks_->ProtectFunction(fallbackFunc, 2), R(EDX), R(EAX));438ADD(64, R(RSP), Imm8(0x28));439#endif440441RET();442443StartDirectAccess();444445#if PPSSPP_ARCH(32BIT)446MOV(bits, MDisp(EAX, (u32)Memory::base), R(EDX));447#else448MOV(bits, MRegSum(MEMBASEREG, EAX), R(EDX));449#endif450451RET();452}453454void JitSafeMemFuncs::CheckDirectEAX() {455// Clear any cache/kernel bits.456AND(32, R(EAX), Imm32(0x3FFFFFFF));457458CMP(32, R(EAX), Imm32(PSP_GetUserMemoryEnd()));459FixupBranch tooHighRAM = J_CC(CC_AE);460CMP(32, R(EAX), Imm32(PSP_GetKernelMemoryBase()));461skips_.push_back(J_CC(CC_AE));462463CMP(32, R(EAX), Imm32(PSP_GetVidMemEnd()));464FixupBranch tooHighVid = J_CC(CC_AE);465CMP(32, R(EAX), Imm32(PSP_GetVidMemBase()));466skips_.push_back(J_CC(CC_AE));467468CMP(32, R(EAX), Imm32(PSP_GetScratchpadMemoryEnd()));469FixupBranch tooHighScratch = J_CC(CC_AE);470CMP(32, R(EAX), Imm32(PSP_GetScratchpadMemoryBase()));471skips_.push_back(J_CC(CC_AE));472473SetJumpTarget(tooHighRAM);474SetJumpTarget(tooHighVid);475SetJumpTarget(tooHighScratch);476}477478void JitSafeMemFuncs::StartDirectAccess() {479for (auto it = skips_.begin(), end = skips_.end(); it != end; ++it) {480SetJumpTarget(*it);481}482skips_.clear();483}484485};486487#endif // PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)488489490