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/Debugger/WebSocket/MemorySubscriber.cpp
Views: 1401
// Copyright (c) 2018- 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 <algorithm>18#include <cstring>19#include <mutex>20#include "Common/Data/Encoding/Base64.h"21#include "Common/StringUtils.h"22#include "Core/Core.h"23#include "Core/Debugger/WebSocket/MemorySubscriber.h"24#include "Core/Debugger/WebSocket/WebSocketUtils.h"25#include "Core/HLE/ReplaceTables.h"26#include "Core/MemMap.h"27#include "Core/MIPS/MIPSDebugInterface.h"28#include "Core/Reporting.h"29#include "Core/System.h"3031DebuggerSubscriber *WebSocketMemoryInit(DebuggerEventHandlerMap &map) {32// No need to bind or alloc state, these are all global.33map["memory.read_u8"] = &WebSocketMemoryReadU8;34map["memory.read_u16"] = &WebSocketMemoryReadU16;35map["memory.read_u32"] = &WebSocketMemoryReadU32;36map["memory.read"] = &WebSocketMemoryRead;37map["memory.readString"] = &WebSocketMemoryReadString;38map["memory.write_u8"] = &WebSocketMemoryWriteU8;39map["memory.write_u16"] = &WebSocketMemoryWriteU16;40map["memory.write_u32"] = &WebSocketMemoryWriteU32;41map["memory.write"] = &WebSocketMemoryWrite;4243return nullptr;44}4546struct AutoDisabledReplacements {47AutoDisabledReplacements() {}48AutoDisabledReplacements(AutoDisabledReplacements &&other);49AutoDisabledReplacements(const AutoDisabledReplacements &) = delete;50AutoDisabledReplacements &operator =(const AutoDisabledReplacements &) = delete;51~AutoDisabledReplacements();5253Memory::MemoryInitedLock *lock = nullptr;54std::map<u32, u32> replacements;55std::vector<u32> emuhacks;56bool saved = false;57bool wasStepping = false;58};5960// Important: Only use keepReplacements when reading, not writing.61static AutoDisabledReplacements LockMemoryAndCPU(uint32_t addr, bool keepReplacements) {62AutoDisabledReplacements result;63if (Core_IsStepping()) {64result.wasStepping = true;65} else {66Core_EnableStepping(true, "memory.access", addr);67Core_WaitInactive();68}6970result.lock = new Memory::MemoryInitedLock();71if (!keepReplacements) {72result.saved = true;73// Okay, save so we can restore later.74result.replacements = SaveAndClearReplacements();75std::lock_guard<std::recursive_mutex> guard(MIPSComp::jitLock);76if (MIPSComp::jit)77result.emuhacks = MIPSComp::jit->SaveAndClearEmuHackOps();78}79return result;80}8182AutoDisabledReplacements::AutoDisabledReplacements(AutoDisabledReplacements &&other) {83lock = other.lock;84other.lock = nullptr;85replacements = std::move(other.replacements);86emuhacks = std::move(other.emuhacks);87saved = other.saved;88other.saved = false;89wasStepping = other.wasStepping;90other.wasStepping = true;91}9293AutoDisabledReplacements::~AutoDisabledReplacements() {94if (saved) {95std::lock_guard<std::recursive_mutex> guard(MIPSComp::jitLock);96if (MIPSComp::jit)97MIPSComp::jit->RestoreSavedEmuHackOps(emuhacks);98RestoreSavedReplacements(replacements);99}100if (!wasStepping)101Core_EnableStepping(false);102delete lock;103}104105// Read a byte from memory (memory.read_u8)106//107// Parameters:108// - address: unsigned integer109//110// Response (same event name):111// - value: unsigned integer112void WebSocketMemoryReadU8(DebuggerRequest &req) {113uint32_t addr;114if (!req.ParamU32("address", &addr, false)) {115return;116}117118auto memLock = LockMemoryAndCPU(addr, true);119if (!currentDebugMIPS->isAlive() || !Memory::IsActive())120return req.Fail("CPU not started");121122if (!Memory::IsValidAddress(addr)) {123req.Fail("Invalid address");124return;125}126127JsonWriter &json = req.Respond();128json.writeUint("value", Memory::Read_U8(addr));129}130131// Read two bytes from memory (memory.read_u16)132//133// Parameters:134// - address: unsigned integer135//136// Response (same event name):137// - value: unsigned integer138void WebSocketMemoryReadU16(DebuggerRequest &req) {139uint32_t addr;140if (!req.ParamU32("address", &addr, false)) {141return;142}143144auto memLock = LockMemoryAndCPU(addr, true);145if (!currentDebugMIPS->isAlive() || !Memory::IsActive())146return req.Fail("CPU not started");147148if (!Memory::IsValidAddress(addr)) {149req.Fail("Invalid address");150return;151}152153JsonWriter &json = req.Respond();154json.writeUint("value", Memory::Read_U16(addr));155}156157// Read four bytes from memory (memory.read_u32)158//159// Parameters:160// - address: unsigned integer161//162// Response (same event name):163// - value: unsigned integer164void WebSocketMemoryReadU32(DebuggerRequest &req) {165uint32_t addr;166if (!req.ParamU32("address", &addr, false)) {167return;168}169170auto memLock = LockMemoryAndCPU(addr, true);171if (!currentDebugMIPS->isAlive() || !Memory::IsActive())172return req.Fail("CPU not started");173174if (!Memory::IsValidAddress(addr)) {175req.Fail("Invalid address");176return;177}178179JsonWriter &json = req.Respond();180json.writeUint("value", Memory::Read_U32(addr));181}182183// Read bytes from memory (memory.read)184//185// Parameters:186// - address: unsigned integer address for the start of the memory range.187// - size: unsigned integer specifying size of memory range.188// - replacements: optional, false to ignore PPSSPP replacements in MIPS code.189//190// Response (same event name):191// - base64: base64 encode of binary data.192void WebSocketMemoryRead(DebuggerRequest &req) {193uint32_t addr;194if (!req.ParamU32("address", &addr))195return;196uint32_t size;197if (!req.ParamU32("size", &size))198return;199bool replacements = true;200if (!req.ParamBool("replacements", &replacements, DebuggerParamType::OPTIONAL))201return;202203auto memLock = LockMemoryAndCPU(addr, replacements);204if (!currentDebugMIPS->isAlive() || !Memory::IsActive())205return req.Fail("CPU not started");206207if (!Memory::IsValidAddress(addr))208return req.Fail("Invalid address");209else if (!Memory::IsValidRange(addr, size))210return req.Fail("Invalid size");211212JsonWriter &json = req.Respond();213// Start a value without any actual data yet...214json.writeRaw("base64", "");215req.Flush();216217// Now we'll write it directly to the stream.218req.ws->AddFragment(false, "\"");219// 65535 is an "even" number of base64 characters.220static const size_t CHUNK_SIZE = 65535;221for (size_t i = 0; i < size; i += CHUNK_SIZE) {222size_t left = std::min(size - i, CHUNK_SIZE);223req.ws->AddFragment(false, Base64Encode(Memory::GetPointerUnchecked(addr) + i, left));224}225req.ws->AddFragment(false, "\"");226}227228// Read a NUL terminated string from memory (memory.readString)229//230// Parameters:231// - address: unsigned integer address for the start of the memory range.232// - type: optional, 'utf-8' (default) or 'base64'.233//234// Response (same event name) for 'utf8':235// - value: string value read.236//237// Response (same event name) for 'base64':238// - base64: base64 encode of binary data, not including NUL.239void WebSocketMemoryReadString(DebuggerRequest &req) {240uint32_t addr;241if (!req.ParamU32("address", &addr))242return;243244auto memLock = LockMemoryAndCPU(addr, true);245if (!currentDebugMIPS->isAlive() || !Memory::IsActive())246return req.Fail("CPU not started");247248std::string type = "utf-8";249if (!req.ParamString("type", &type, DebuggerParamType::OPTIONAL))250return;251if (type != "utf-8" && type != "base64")252return req.Fail("Invalid type, must be either utf-8 or base64");253254if (!Memory::IsValidAddress(addr))255return req.Fail("Invalid address");256257// Let's try to avoid crashing and get a safe length.258const uint8_t *p = Memory::GetPointerUnchecked(addr);259size_t longest = Memory::ValidSize(addr, Memory::g_MemorySize);260size_t len = strnlen((const char *)p, longest);261262JsonWriter &json = req.Respond();263if (type == "utf-8") {264json.writeString("value", std::string((const char *)p, len));265} else if (type == "base64") {266json.writeString("base64", Base64Encode(p, len));267}268}269270// Write a byte to memory (memory.write_u8)271//272// Parameters:273// - address: unsigned integer274// - value: unsigned integer275//276// Response (same event name):277// - value: new value, unsigned integer278void WebSocketMemoryWriteU8(DebuggerRequest &req) {279uint32_t addr, val;280if (!req.ParamU32("address", &addr, false)) {281return;282}283if (!req.ParamU32("value", &val, false)) {284return;285}286287auto memLock = LockMemoryAndCPU(addr, true);288if (!currentDebugMIPS->isAlive() || !Memory::IsActive())289return req.Fail("CPU not started");290291if (!Memory::IsValidAddress(addr)) {292req.Fail("Invalid address");293return;294}295currentMIPS->InvalidateICache(addr, 1);296Memory::Write_U8(val, addr);297Reporting::NotifyDebugger();298299JsonWriter &json = req.Respond();300json.writeUint("value", Memory::Read_U8(addr));301}302303// Write two bytes to memory (memory.write_u16)304//305// Parameters:306// - address: unsigned integer307// - value: unsigned integer308//309// Response (same event name):310// - value: new value, unsigned integer311void WebSocketMemoryWriteU16(DebuggerRequest &req) {312uint32_t addr, val;313if (!req.ParamU32("address", &addr, false)) {314return;315}316if (!req.ParamU32("value", &val, false)) {317return;318}319320auto memLock = LockMemoryAndCPU(addr, true);321if (!currentDebugMIPS->isAlive() || !Memory::IsActive())322return req.Fail("CPU not started");323324if (!Memory::IsValidAddress(addr)) {325req.Fail("Invalid address");326return;327}328currentMIPS->InvalidateICache(addr, 2);329Memory::Write_U16(val, addr);330Reporting::NotifyDebugger();331332JsonWriter &json = req.Respond();333json.writeUint("value", Memory::Read_U16(addr));334}335336// Write four bytes to memory (memory.write_u32)337//338// Parameters:339// - address: unsigned integer340// - value: unsigned integer341//342// Response (same event name):343// - value: new value, unsigned integer344void WebSocketMemoryWriteU32(DebuggerRequest &req) {345uint32_t addr, val;346if (!req.ParamU32("address", &addr, false)) {347return;348}349if (!req.ParamU32("value", &val, false)) {350return;351}352353auto memLock = LockMemoryAndCPU(addr, true);354if (!currentDebugMIPS->isAlive() || !Memory::IsActive())355return req.Fail("CPU not started");356357if (!Memory::IsValidAddress(addr)) {358req.Fail("Invalid address");359return;360}361currentMIPS->InvalidateICache(addr, 4);362Memory::Write_U32(val, addr);363Reporting::NotifyDebugger();364365JsonWriter &json = req.Respond();366json.writeUint("value", Memory::Read_U32(addr));367}368369// Write bytes to memory (memory.write)370//371// Parameters:372// - address: unsigned integer address for the start of the memory range.373// - base64: data to write, encoded as base64 string374//375// Response (same event name) with no extra data.376void WebSocketMemoryWrite(DebuggerRequest &req) {377uint32_t addr;378if (!req.ParamU32("address", &addr))379return;380std::string encoded;381if (!req.ParamString("base64", &encoded))382return;383384auto memLock = LockMemoryAndCPU(addr, true);385if (!currentDebugMIPS->isAlive() || !Memory::IsActive())386return req.Fail("CPU not started");387388std::vector<uint8_t> value = Base64Decode(&encoded[0], encoded.size());389uint32_t size = (uint32_t)value.size();390391if (!Memory::IsValidAddress(addr))392return req.Fail("Invalid address");393else if (value.size() != (size_t)size || !Memory::IsValidRange(addr, size))394return req.Fail("Invalid size");395396currentMIPS->InvalidateICache(addr, size);397Memory::MemcpyUnchecked(addr, &value[0], size);398Reporting::NotifyDebugger();399req.Respond();400}401402403