mirror of
https://github.com/azahar-emu/azahar.git
synced 2025-04-28 13:47:59 +03:00
Fix cheats and rpc server affecting the wrong processes (#956)
This commit is contained in:
parent
391f91f735
commit
eba3c2c08f
12 changed files with 363 additions and 67 deletions
61
dist/scripting/citra.py
vendored
61
dist/scripting/citra.py
vendored
|
@ -1,15 +1,21 @@
|
|||
# Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
# Licensed under GPLv2 or any later version
|
||||
# Refer to the license.txt file included.
|
||||
|
||||
import struct
|
||||
import random
|
||||
import enum
|
||||
import socket
|
||||
|
||||
CURRENT_REQUEST_VERSION = 1
|
||||
MAX_REQUEST_DATA_SIZE = 32
|
||||
MAX_PACKET_SIZE = 48
|
||||
MAX_REQUEST_DATA_SIZE = 1024
|
||||
MAX_PACKET_SIZE = 1024 + 0x10
|
||||
|
||||
class RequestType(enum.IntEnum):
|
||||
ReadMemory = 1,
|
||||
WriteMemory = 2
|
||||
WriteMemory = 2,
|
||||
ProcessList = 3,
|
||||
SetGetProcess = 4,
|
||||
|
||||
CITRA_PORT = 45987
|
||||
|
||||
|
@ -34,6 +40,55 @@ class Citra:
|
|||
return raw_reply[4*4:]
|
||||
return None
|
||||
|
||||
def process_list(self):
|
||||
processes = {}
|
||||
read_processes = 0
|
||||
while True:
|
||||
request_data = struct.pack("II", read_processes, 0x7FFFFFFF)
|
||||
request, request_id = self._generate_header(RequestType.ProcessList, len(request_data))
|
||||
request += request_data
|
||||
self.socket.sendto(request, (self.address, CITRA_PORT))
|
||||
|
||||
raw_reply = self.socket.recv(MAX_PACKET_SIZE)
|
||||
reply_data = self._read_and_validate_header(raw_reply, request_id, RequestType.ProcessList)
|
||||
|
||||
if reply_data:
|
||||
read_count = struct.unpack("I", reply_data[0:4])[0]
|
||||
reply_data = reply_data[4:]
|
||||
if read_count == 0:
|
||||
break
|
||||
read_processes += read_count
|
||||
for i in range(read_count):
|
||||
proc_data = reply_data[i * 0x14 : (i + 1) * 0x14]
|
||||
proc_id, title_id, proc_name = struct.unpack("<IQ8s", proc_data)
|
||||
proc_name = proc_name.rstrip(b"\x00").decode("ascii")
|
||||
processes[proc_id] = (title_id, proc_name)
|
||||
else:
|
||||
break
|
||||
return processes
|
||||
|
||||
def get_process(self):
|
||||
request_data = struct.pack("II", 0, 0)
|
||||
request, request_id = self._generate_header(RequestType.SetGetProcess, len(request_data))
|
||||
request += request_data
|
||||
self.socket.sendto(request, (self.address, CITRA_PORT))
|
||||
|
||||
raw_reply = self.socket.recv(MAX_PACKET_SIZE)
|
||||
reply_data = self._read_and_validate_header(raw_reply, request_id, RequestType.SetGetProcess)
|
||||
|
||||
if reply_data:
|
||||
return struct.unpack("I", reply_data)[0]
|
||||
else:
|
||||
return None
|
||||
|
||||
def set_process(self, process_id):
|
||||
request_data = struct.pack("II", 1, process_id)
|
||||
request, request_id = self._generate_header(RequestType.SetGetProcess, len(request_data))
|
||||
request += request_data
|
||||
self.socket.sendto(request, (self.address, CITRA_PORT))
|
||||
|
||||
self.socket.recv(MAX_PACKET_SIZE)
|
||||
|
||||
def read_memory(self, read_address, read_size):
|
||||
"""
|
||||
>>> c.read_memory(0x100000, 4)
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
// Copyright 2018 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
|
@ -14,7 +15,7 @@ namespace Cheats {
|
|||
class CheatBase {
|
||||
public:
|
||||
virtual ~CheatBase();
|
||||
virtual void Execute(Core::System& system) const = 0;
|
||||
virtual void Execute(Core::System& system, u32 process_id) const = 0;
|
||||
|
||||
virtual bool IsEnabled() const = 0;
|
||||
virtual void SetEnabled(bool enabled) = 0;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2018 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
|
@ -25,7 +25,8 @@ CheatEngine::~CheatEngine() {
|
|||
}
|
||||
}
|
||||
|
||||
void CheatEngine::Connect() {
|
||||
void CheatEngine::Connect(u32 process_id) {
|
||||
this->process_id = process_id;
|
||||
event = system.CoreTiming().RegisterEvent(
|
||||
"CheatCore::run_event",
|
||||
[this](u64 thread_id, s64 cycle_late) { RunCallback(thread_id, cycle_late); });
|
||||
|
@ -107,7 +108,7 @@ void CheatEngine::RunCallback([[maybe_unused]] std::uintptr_t user_data, s64 cyc
|
|||
std::shared_lock lock{cheats_list_mutex};
|
||||
for (const auto& cheat : cheats_list) {
|
||||
if (cheat->IsEnabled()) {
|
||||
cheat->Execute(system);
|
||||
cheat->Execute(system, process_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2018 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
|
@ -30,7 +30,7 @@ public:
|
|||
~CheatEngine();
|
||||
|
||||
/// Registers the cheat execution callback.
|
||||
void Connect();
|
||||
void Connect(u32 process_id);
|
||||
|
||||
/// Returns a span of the currently active cheats.
|
||||
std::span<const std::shared_ptr<CheatBase>> GetCheats() const;
|
||||
|
@ -50,6 +50,10 @@ public:
|
|||
/// Saves currently active cheats to file for the specified title id.
|
||||
void SaveCheatFile(u64 title_id) const;
|
||||
|
||||
u32 GetConnectedPID() const {
|
||||
return process_id;
|
||||
}
|
||||
|
||||
private:
|
||||
/// The cheat execution callback.
|
||||
void RunCallback(std::uintptr_t user_data, s64 cycles_late);
|
||||
|
@ -58,6 +62,7 @@ private:
|
|||
Core::System& system;
|
||||
Core::TimingEventType* event;
|
||||
std::optional<u64> loaded_title_id;
|
||||
u32 process_id = 0xFFFFFFFF;
|
||||
std::vector<std::shared_ptr<CheatBase>> cheats_list;
|
||||
mutable std::shared_mutex cheats_list_mutex;
|
||||
};
|
||||
|
|
|
@ -56,10 +56,10 @@ static inline std::enable_if_t<std::is_integral_v<T>> CompOp(const GatewayCheat:
|
|||
}
|
||||
}
|
||||
|
||||
static inline void LoadOffsetOp(Memory::MemorySystem& memory, const GatewayCheat::CheatLine& line,
|
||||
State& state) {
|
||||
static inline void LoadOffsetOp(Memory::MemorySystem& memory, const Kernel::Process& process,
|
||||
const GatewayCheat::CheatLine& line, State& state) {
|
||||
u32 addr = line.address + state.offset;
|
||||
state.offset = memory.Read32(addr);
|
||||
state.offset = memory.Read32(process, addr);
|
||||
}
|
||||
|
||||
static inline void LoopOp(const GatewayCheat::CheatLine& line, State& state) {
|
||||
|
@ -145,6 +145,7 @@ static inline void JokerOp(const GatewayCheat::CheatLine& line, State& state,
|
|||
}
|
||||
|
||||
static inline void PatchOp(const GatewayCheat::CheatLine& line, State& state, Core::System& system,
|
||||
const Kernel::Process& process,
|
||||
std::span<const GatewayCheat::CheatLine> cheat_lines) {
|
||||
if (state.if_flag > 0) {
|
||||
// Skip over the additional patch lines
|
||||
|
@ -166,7 +167,7 @@ static inline void PatchOp(const GatewayCheat::CheatLine& line, State& state, Co
|
|||
state.current_line_nr++;
|
||||
}
|
||||
first = !first;
|
||||
system.Memory().Write32(addr, tmp);
|
||||
system.Memory().Write32(process, addr, tmp);
|
||||
addr += 4;
|
||||
num_bytes -= 4;
|
||||
}
|
||||
|
@ -174,7 +175,7 @@ static inline void PatchOp(const GatewayCheat::CheatLine& line, State& state, Co
|
|||
u32 tmp = (first ? cheat_lines[state.current_line_nr].first
|
||||
: cheat_lines[state.current_line_nr].value) >>
|
||||
bit_offset;
|
||||
system.Memory().Write8(addr, static_cast<u8>(tmp));
|
||||
system.Memory().Write8(process, addr, static_cast<u8>(tmp));
|
||||
addr += 1;
|
||||
num_bytes -= 1;
|
||||
bit_offset += 8;
|
||||
|
@ -229,16 +230,27 @@ GatewayCheat::GatewayCheat(std::string name_, std::string code, std::string comm
|
|||
|
||||
GatewayCheat::~GatewayCheat() = default;
|
||||
|
||||
void GatewayCheat::Execute(Core::System& system) const {
|
||||
void GatewayCheat::Execute(Core::System& system, u32 process_id) const {
|
||||
State state;
|
||||
|
||||
Memory::MemorySystem& memory = system.Memory();
|
||||
auto Read8 = [&memory](VAddr addr) { return memory.Read8(addr); };
|
||||
auto Read16 = [&memory](VAddr addr) { return memory.Read16(addr); };
|
||||
auto Read32 = [&memory](VAddr addr) { return memory.Read32(addr); };
|
||||
auto Write8 = [&memory](VAddr addr, u8 value) { memory.Write8(addr, value); };
|
||||
auto Write16 = [&memory](VAddr addr, u16 value) { memory.Write16(addr, value); };
|
||||
auto Write32 = [&memory](VAddr addr, u32 value) { memory.Write32(addr, value); };
|
||||
std::shared_ptr<Kernel::Process> process = system.Kernel().GetProcessById(process_id);
|
||||
if (!process) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto Read8 = [&memory, &process](VAddr addr) { return memory.Read8(*process, addr); };
|
||||
auto Read16 = [&memory, &process](VAddr addr) { return memory.Read16(*process, addr); };
|
||||
auto Read32 = [&memory, &process](VAddr addr) { return memory.Read32(*process, addr); };
|
||||
auto Write8 = [&memory, &process](VAddr addr, u8 value) {
|
||||
memory.Write8(*process, addr, value);
|
||||
};
|
||||
auto Write16 = [&memory, &process](VAddr addr, u16 value) {
|
||||
memory.Write16(*process, addr, value);
|
||||
};
|
||||
auto Write32 = [&memory, &process](VAddr addr, u32 value) {
|
||||
memory.Write32(*process, addr, value);
|
||||
};
|
||||
|
||||
for (state.current_line_nr = 0; state.current_line_nr < cheat_lines.size();
|
||||
state.current_line_nr++) {
|
||||
|
@ -261,7 +273,7 @@ void GatewayCheat::Execute(Core::System& system) const {
|
|||
// EXXXXXXX YYYYYYYY
|
||||
// Copies YYYYYYYY bytes from (current code location + 8) to [XXXXXXXX + offset].
|
||||
// We need to call this here to skip the additional patch lines
|
||||
PatchOp(line, state, system, cheat_lines);
|
||||
PatchOp(line, state, system, *process, cheat_lines);
|
||||
break;
|
||||
case CheatType::Terminator:
|
||||
// D0000000 00000000 - ENDIF
|
||||
|
@ -336,7 +348,7 @@ void GatewayCheat::Execute(Core::System& system) const {
|
|||
break;
|
||||
case CheatType::LoadOffset:
|
||||
// BXXXXXXX 00000000 - offset = word[XXXXXXX+offset]
|
||||
LoadOffsetOp(system.Memory(), line, state);
|
||||
LoadOffsetOp(system.Memory(), *process, line, state);
|
||||
break;
|
||||
case CheatType::Loop: {
|
||||
// C0000000 YYYYYYYY - LOOP next block YYYYYYYY times
|
||||
|
@ -417,7 +429,7 @@ void GatewayCheat::Execute(Core::System& system) const {
|
|||
case CheatType::Patch: {
|
||||
// EXXXXXXX YYYYYYYY
|
||||
// Copies YYYYYYYY bytes from (current code location + 8) to [XXXXXXXX + offset].
|
||||
PatchOp(line, state, system, cheat_lines);
|
||||
PatchOp(line, state, system, *process, cheat_lines);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2018 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
|
@ -59,7 +59,7 @@ public:
|
|||
GatewayCheat(std::string name, std::string code, std::string comments);
|
||||
~GatewayCheat();
|
||||
|
||||
void Execute(Core::System& system) const override;
|
||||
void Execute(Core::System& system, u32 process_id) const override;
|
||||
|
||||
bool IsEnabled() const override;
|
||||
void SetEnabled(bool enabled) override;
|
||||
|
|
|
@ -394,7 +394,7 @@ System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::st
|
|||
}
|
||||
|
||||
cheat_engine.LoadCheatFile(title_id);
|
||||
cheat_engine.Connect();
|
||||
cheat_engine.Connect(process->process_id);
|
||||
|
||||
perf_stats = std::make_unique<PerfStats>(title_id);
|
||||
|
||||
|
@ -814,9 +814,11 @@ void System::serialize(Archive& ar, const unsigned int file_version) {
|
|||
|
||||
// This needs to be set from somewhere - might as well be here!
|
||||
if (Archive::is_loading::value) {
|
||||
u32 cheats_pid;
|
||||
ar & cheats_pid;
|
||||
timing->UnlockEventQueue();
|
||||
memory->SetDSP(*dsp_core);
|
||||
cheat_engine.Connect();
|
||||
cheat_engine.Connect(cheats_pid);
|
||||
gpu->Sync();
|
||||
|
||||
// Re-register gpu callback, because gsp service changed after service_manager got
|
||||
|
@ -824,6 +826,9 @@ void System::serialize(Archive& ar, const unsigned int file_version) {
|
|||
auto gsp = service_manager->GetService<Service::GSP::GSP_GPU>("gsp::Gpu");
|
||||
gpu->SetInterruptHandler(
|
||||
[gsp](Service::GSP::InterruptId interrupt_id) { gsp->SignalInterrupt(interrupt_id); });
|
||||
} else {
|
||||
u32 cheats_pid = cheat_engine.GetConnectedPID();
|
||||
ar & cheats_pid;
|
||||
}
|
||||
|
||||
save_state_status = SaveStateStatus::NONE;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2015 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
|
@ -448,8 +448,8 @@ void MemorySystem::UnregisterPageTable(std::shared_ptr<PageTable> page_table) {
|
|||
}
|
||||
|
||||
template <typename T>
|
||||
T MemorySystem::Read(const VAddr vaddr) {
|
||||
const u8* page_pointer = impl->current_page_table->pointers[vaddr >> CITRA_PAGE_BITS];
|
||||
T MemorySystem::Read(const std::shared_ptr<PageTable>& page_table, const VAddr vaddr) {
|
||||
const u8* page_pointer = page_table->pointers[vaddr >> CITRA_PAGE_BITS];
|
||||
if (page_pointer) {
|
||||
// NOTE: Avoid adding any extra logic to this fast-path block
|
||||
T value;
|
||||
|
@ -472,7 +472,7 @@ T MemorySystem::Read(const VAddr vaddr) {
|
|||
}
|
||||
}
|
||||
|
||||
PageType type = impl->current_page_table->attributes[vaddr >> CITRA_PAGE_BITS];
|
||||
PageType type = page_table->attributes[vaddr >> CITRA_PAGE_BITS];
|
||||
switch (type) {
|
||||
case PageType::Unmapped:
|
||||
LOG_ERROR(HW_Memory, "unmapped Read{} @ 0x{:08X} at PC 0x{:08X}", sizeof(T) * 8, vaddr,
|
||||
|
@ -496,8 +496,9 @@ T MemorySystem::Read(const VAddr vaddr) {
|
|||
}
|
||||
|
||||
template <typename T>
|
||||
void MemorySystem::Write(const VAddr vaddr, const T data) {
|
||||
u8* page_pointer = impl->current_page_table->pointers[vaddr >> CITRA_PAGE_BITS];
|
||||
void MemorySystem::Write(const std::shared_ptr<PageTable>& page_table, const VAddr vaddr,
|
||||
const T data) {
|
||||
u8* page_pointer = page_table->pointers[vaddr >> CITRA_PAGE_BITS];
|
||||
if (page_pointer) {
|
||||
// NOTE: Avoid adding any extra logic to this fast-path block
|
||||
std::memcpy(&page_pointer[vaddr & CITRA_PAGE_MASK], &data, sizeof(T));
|
||||
|
@ -521,7 +522,7 @@ void MemorySystem::Write(const VAddr vaddr, const T data) {
|
|||
}
|
||||
}
|
||||
|
||||
PageType type = impl->current_page_table->attributes[vaddr >> CITRA_PAGE_BITS];
|
||||
PageType type = page_table->attributes[vaddr >> CITRA_PAGE_BITS];
|
||||
switch (type) {
|
||||
case PageType::Unmapped:
|
||||
LOG_ERROR(HW_Memory, "unmapped Write{} 0x{:08X} @ 0x{:08X} at PC 0x{:08X}",
|
||||
|
@ -773,19 +774,35 @@ void MemorySystem::RasterizerMarkRegionCached(PAddr start, u32 size, bool cached
|
|||
}
|
||||
|
||||
u8 MemorySystem::Read8(const VAddr addr) {
|
||||
return Read<u8>(addr);
|
||||
return Read<u8>(impl->current_page_table, addr);
|
||||
}
|
||||
|
||||
u8 MemorySystem::Read8(const Kernel::Process& process, VAddr addr) {
|
||||
return Read<u8>(process.vm_manager.page_table, addr);
|
||||
}
|
||||
|
||||
u16 MemorySystem::Read16(const VAddr addr) {
|
||||
return Read<u16_le>(addr);
|
||||
return Read<u16_le>(impl->current_page_table, addr);
|
||||
}
|
||||
|
||||
u16 MemorySystem::Read16(const Kernel::Process& process, VAddr addr) {
|
||||
return Read<u16_le>(process.vm_manager.page_table, addr);
|
||||
}
|
||||
|
||||
u32 MemorySystem::Read32(const VAddr addr) {
|
||||
return Read<u32_le>(addr);
|
||||
return Read<u32_le>(impl->current_page_table, addr);
|
||||
}
|
||||
|
||||
u32 MemorySystem::Read32(const Kernel::Process& process, VAddr addr) {
|
||||
return Read<u32_le>(process.vm_manager.page_table, addr);
|
||||
}
|
||||
|
||||
u64 MemorySystem::Read64(const VAddr addr) {
|
||||
return Read<u64_le>(addr);
|
||||
return Read<u64_le>(impl->current_page_table, addr);
|
||||
}
|
||||
|
||||
u64 MemorySystem::Read64(const Kernel::Process& process, VAddr addr) {
|
||||
return Read<u64_le>(process.vm_manager.page_table, addr);
|
||||
}
|
||||
|
||||
void MemorySystem::ReadBlock(const Kernel::Process& process, const VAddr src_addr,
|
||||
|
@ -799,19 +816,35 @@ void MemorySystem::ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size
|
|||
}
|
||||
|
||||
void MemorySystem::Write8(const VAddr addr, const u8 data) {
|
||||
Write<u8>(addr, data);
|
||||
Write<u8>(impl->current_page_table, addr, data);
|
||||
}
|
||||
|
||||
void MemorySystem::Write8(const Kernel::Process& process, const VAddr addr, const u8 data) {
|
||||
Write<u8>(process.vm_manager.page_table, addr, data);
|
||||
}
|
||||
|
||||
void MemorySystem::Write16(const VAddr addr, const u16 data) {
|
||||
Write<u16_le>(addr, data);
|
||||
Write<u16_le>(impl->current_page_table, addr, data);
|
||||
}
|
||||
|
||||
void MemorySystem::Write16(const Kernel::Process& process, const VAddr addr, const u16 data) {
|
||||
Write<u16_le>(process.vm_manager.page_table, addr, data);
|
||||
}
|
||||
|
||||
void MemorySystem::Write32(const VAddr addr, const u32 data) {
|
||||
Write<u32_le>(addr, data);
|
||||
Write<u32_le>(impl->current_page_table, addr, data);
|
||||
}
|
||||
|
||||
void MemorySystem::Write32(const Kernel::Process& process, const VAddr addr, const u32 data) {
|
||||
Write<u32_le>(process.vm_manager.page_table, addr, data);
|
||||
}
|
||||
|
||||
void MemorySystem::Write64(const VAddr addr, const u64 data) {
|
||||
Write<u64_le>(addr, data);
|
||||
Write<u64_le>(impl->current_page_table, addr, data);
|
||||
}
|
||||
|
||||
void MemorySystem::Write64(const Kernel::Process& process, const VAddr addr, const u64 data) {
|
||||
Write<u64_le>(process.vm_manager.page_table, addr, data);
|
||||
}
|
||||
|
||||
bool MemorySystem::WriteExclusive8(const VAddr addr, const u8 data, const u8 expected) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2014 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
|
@ -286,6 +286,17 @@ public:
|
|||
*/
|
||||
u8 Read8(VAddr addr);
|
||||
|
||||
/**
|
||||
* Reads an 8-bit unsigned value from the process' address space
|
||||
* at the given virtual address.
|
||||
*
|
||||
* @param process The process to read from.
|
||||
* @param addr The virtual address to read the 8-bit value from.
|
||||
*
|
||||
* @returns the read 8-bit unsigned value.
|
||||
*/
|
||||
u8 Read8(const Kernel::Process& process, VAddr addr);
|
||||
|
||||
/**
|
||||
* Reads a 16-bit unsigned value from the current process' address space
|
||||
* at the given virtual address.
|
||||
|
@ -296,6 +307,17 @@ public:
|
|||
*/
|
||||
u16 Read16(VAddr addr);
|
||||
|
||||
/**
|
||||
* Reads a 16-bit unsigned value from the process' address space
|
||||
* at the given virtual address.
|
||||
*
|
||||
* @param process The process to read from.
|
||||
* @param addr The virtual address to read the 16-bit value from.
|
||||
*
|
||||
* @returns the read 16-bit unsigned value.
|
||||
*/
|
||||
u16 Read16(const Kernel::Process& process, VAddr addr);
|
||||
|
||||
/**
|
||||
* Reads a 32-bit unsigned value from the current process' address space
|
||||
* at the given virtual address.
|
||||
|
@ -306,6 +328,17 @@ public:
|
|||
*/
|
||||
u32 Read32(VAddr addr);
|
||||
|
||||
/**
|
||||
* Reads a 32-bit unsigned value from the process' address space
|
||||
* at the given virtual address.
|
||||
*
|
||||
* @param process The process to read from.
|
||||
* @param addr The virtual address to read the 32-bit value from.
|
||||
*
|
||||
* @returns the read 32-bit unsigned value.
|
||||
*/
|
||||
u32 Read32(const Kernel::Process& process, VAddr addr);
|
||||
|
||||
/**
|
||||
* Reads a 64-bit unsigned value from the current process' address space
|
||||
* at the given virtual address.
|
||||
|
@ -316,6 +349,17 @@ public:
|
|||
*/
|
||||
u64 Read64(VAddr addr);
|
||||
|
||||
/**
|
||||
* Reads a 64-bit unsigned value from the process' address space
|
||||
* at the given virtual address.
|
||||
*
|
||||
* @param process The process to read from.
|
||||
* @param addr The virtual address to read the 64-bit value from.
|
||||
*
|
||||
* @returns the read 64-bit value.
|
||||
*/
|
||||
u64 Read64(const Kernel::Process& process, VAddr addr);
|
||||
|
||||
/**
|
||||
* Writes an 8-bit unsigned integer to the given virtual address in
|
||||
* the current process' address space.
|
||||
|
@ -327,6 +371,18 @@ public:
|
|||
*/
|
||||
void Write8(VAddr addr, u8 data);
|
||||
|
||||
/**
|
||||
* Writes an 8-bit unsigned integer to the given virtual address in
|
||||
* the process' address space.
|
||||
*
|
||||
* @param process The process to write to.
|
||||
* @param addr The virtual address to write the 8-bit unsigned integer to.
|
||||
* @param data The 8-bit unsigned integer to write to the given virtual address.
|
||||
*
|
||||
* @post The memory at the given virtual address contains the specified data value.
|
||||
*/
|
||||
void Write8(const Kernel::Process& process, VAddr addr, u8 data);
|
||||
|
||||
/**
|
||||
* Writes a 16-bit unsigned integer to the given virtual address in
|
||||
* the current process' address space.
|
||||
|
@ -338,6 +394,18 @@ public:
|
|||
*/
|
||||
void Write16(VAddr addr, u16 data);
|
||||
|
||||
/**
|
||||
* Writes a 16-bit unsigned integer to the given virtual address in
|
||||
* the process' address space.
|
||||
*
|
||||
* @param process The process to write to.
|
||||
* @param addr The virtual address to write the 16-bit unsigned integer to.
|
||||
* @param data The 16-bit unsigned integer to write to the given virtual address.
|
||||
*
|
||||
* @post The memory range [addr, sizeof(data)) contains the given data value.
|
||||
*/
|
||||
void Write16(const Kernel::Process& process, VAddr addr, u16 data);
|
||||
|
||||
/**
|
||||
* Writes a 32-bit unsigned integer to the given virtual address in
|
||||
* the current process' address space.
|
||||
|
@ -349,6 +417,18 @@ public:
|
|||
*/
|
||||
void Write32(VAddr addr, u32 data);
|
||||
|
||||
/**
|
||||
* Writes a 32-bit unsigned integer to the given virtual address in
|
||||
* the process' address space.
|
||||
*
|
||||
* @param process The process to write to.
|
||||
* @param addr The virtual address to write the 32-bit unsigned integer to.
|
||||
* @param data The 32-bit unsigned integer to write to the given virtual address.
|
||||
*
|
||||
* @post The memory range [addr, sizeof(data)) contains the given data value.
|
||||
*/
|
||||
void Write32(const Kernel::Process& process, VAddr addr, u32 data);
|
||||
|
||||
/**
|
||||
* Writes a 64-bit unsigned integer to the given virtual address in
|
||||
* the current process' address space.
|
||||
|
@ -360,6 +440,18 @@ public:
|
|||
*/
|
||||
void Write64(VAddr addr, u64 data);
|
||||
|
||||
/**
|
||||
* Writes a 64-bit unsigned integer to the given virtual address in
|
||||
* the process' address space.
|
||||
*
|
||||
* @param process The process to write to.
|
||||
* @param addr The virtual address to write the 64-bit unsigned integer to.
|
||||
* @param data The 64-bit unsigned integer to write to the given virtual address.
|
||||
*
|
||||
* @post The memory range [addr, sizeof(data)) contains the given data value.
|
||||
*/
|
||||
void Write64(const Kernel::Process& process, VAddr addr, u64 data);
|
||||
|
||||
/**
|
||||
* Writes a {8, 16, 32, 64}-bit unsigned integer to the given virtual address in
|
||||
* the current process' address space if and only if the address contains
|
||||
|
@ -555,10 +647,10 @@ public:
|
|||
|
||||
private:
|
||||
template <typename T>
|
||||
T Read(const VAddr vaddr);
|
||||
T Read(const std::shared_ptr<PageTable>& page_table, const VAddr vaddr);
|
||||
|
||||
template <typename T>
|
||||
void Write(const VAddr vaddr, const T data);
|
||||
void Write(const std::shared_ptr<PageTable>& page_table, const VAddr vaddr, const T data);
|
||||
|
||||
template <typename T>
|
||||
bool WriteExclusive(const VAddr vaddr, const T data, const T expected);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2018 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
|
@ -15,6 +15,8 @@ enum class PacketType : u32 {
|
|||
Undefined = 0,
|
||||
ReadMemory = 1,
|
||||
WriteMemory = 2,
|
||||
ProcessList = 3,
|
||||
SetGetProcess = 4,
|
||||
};
|
||||
|
||||
struct PacketHeader {
|
||||
|
@ -24,11 +26,21 @@ struct PacketHeader {
|
|||
u32 packet_size;
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct ProcessInfo {
|
||||
u32 process_id;
|
||||
u64 title_id;
|
||||
std::array<u8, 8> process_name;
|
||||
};
|
||||
static_assert(sizeof(ProcessInfo) == 0x14, "Incorrect ProcessInfo size");
|
||||
#pragma pack(pop)
|
||||
|
||||
constexpr u32 CURRENT_VERSION = 1;
|
||||
constexpr u32 MIN_PACKET_SIZE = sizeof(PacketHeader);
|
||||
constexpr u32 MAX_PACKET_DATA_SIZE = 32;
|
||||
constexpr u32 MAX_PACKET_DATA_SIZE = 1024;
|
||||
constexpr u32 MAX_PACKET_SIZE = MIN_PACKET_SIZE + MAX_PACKET_DATA_SIZE;
|
||||
constexpr u32 MAX_READ_SIZE = MAX_PACKET_DATA_SIZE;
|
||||
constexpr u32 MAX_PROCESSES_IN_LIST = (MAX_PACKET_DATA_SIZE - sizeof(u32)) / sizeof(ProcessInfo);
|
||||
|
||||
class Packet {
|
||||
public:
|
||||
|
@ -69,9 +81,6 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
void HandleReadMemory(u32 address, u32 data_size);
|
||||
void HandleWriteMemory(u32 address, std::span<const u8> data);
|
||||
|
||||
struct PacketHeader header;
|
||||
std::array<u8, MAX_PACKET_DATA_SIZE> packet_data;
|
||||
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
// Copyright 2019 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/memory.h"
|
||||
#include "core/rpc/packet.h"
|
||||
#include "core/rpc/rpc_server.h"
|
||||
|
@ -22,10 +23,23 @@ void RPCServer::HandleReadMemory(Packet& packet, u32 address, u32 data_size) {
|
|||
if (data_size > MAX_READ_SIZE) {
|
||||
return;
|
||||
}
|
||||
u32 read_size = data_size;
|
||||
|
||||
// Note: Memory read occurs asynchronously from the state of the emulator
|
||||
if (selected_pid == 0xFFFFFFFF) {
|
||||
LOG_ERROR(RPC_Server, "No target process selected, memory access may be invalid.");
|
||||
system.Memory().ReadBlock(address, packet.GetPacketData().data(), data_size);
|
||||
packet.SetPacketDataSize(data_size);
|
||||
} else {
|
||||
auto process = system.Kernel().GetProcessById(selected_pid);
|
||||
if (process) {
|
||||
system.Memory().ReadBlock(*process, address, packet.GetPacketData().data(), data_size);
|
||||
} else {
|
||||
LOG_ERROR(RPC_Server, "Selected process does not exist.");
|
||||
read_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
packet.SetPacketDataSize(read_size);
|
||||
packet.SendReply();
|
||||
}
|
||||
|
||||
|
@ -33,9 +47,21 @@ void RPCServer::HandleWriteMemory(Packet& packet, u32 address, std::span<const u
|
|||
// Only allow writing to certain memory regions
|
||||
if ((address >= Memory::PROCESS_IMAGE_VADDR && address <= Memory::PROCESS_IMAGE_VADDR_END) ||
|
||||
(address >= Memory::HEAP_VADDR && address <= Memory::HEAP_VADDR_END) ||
|
||||
(address >= Memory::LINEAR_HEAP_VADDR && address <= Memory::LINEAR_HEAP_VADDR_END) ||
|
||||
(address >= Memory::N3DS_EXTRA_RAM_VADDR && address <= Memory::N3DS_EXTRA_RAM_VADDR_END)) {
|
||||
// Note: Memory write occurs asynchronously from the state of the emulator
|
||||
if (selected_pid == 0xFFFFFFFF) {
|
||||
LOG_ERROR(RPC_Server, "No target process selected, memory access may be invalid.");
|
||||
system.Memory().WriteBlock(address, data.data(), data.size());
|
||||
} else {
|
||||
auto process = system.Kernel().GetProcessById(selected_pid);
|
||||
if (process) {
|
||||
system.Memory().WriteBlock(*process, address, data.data(), data.size());
|
||||
} else {
|
||||
LOG_ERROR(RPC_Server, "Selected process does not exist.");
|
||||
}
|
||||
}
|
||||
|
||||
// If the memory happens to be executable code, make sure the changes become visible
|
||||
|
||||
// Is current core correct here?
|
||||
|
@ -45,11 +71,57 @@ void RPCServer::HandleWriteMemory(Packet& packet, u32 address, std::span<const u
|
|||
packet.SendReply();
|
||||
}
|
||||
|
||||
void RPCServer::HandleProcessList(Packet& packet, u32 start_index, u32 max_amount) {
|
||||
const auto process_list = system.Kernel().GetProcessList();
|
||||
const u32 start = std::min(start_index, static_cast<u32>(process_list.size()));
|
||||
const u32 end = std::min(start + max_amount, static_cast<u32>(process_list.size()));
|
||||
const u32 count = std::min(end - start, MAX_PROCESSES_IN_LIST);
|
||||
|
||||
u8* out_data = packet.GetPacketData().data();
|
||||
u32 written_bytes = 0;
|
||||
|
||||
memcpy(out_data + written_bytes, &count, sizeof(count));
|
||||
written_bytes += sizeof(count);
|
||||
|
||||
for (u32 i = start; i < start + count; i++) {
|
||||
ProcessInfo info{};
|
||||
info.process_id = process_list[i]->process_id;
|
||||
info.title_id = process_list[i]->codeset->program_id;
|
||||
memcpy(info.process_name.data(), process_list[i]->codeset->name.data(),
|
||||
std::min(process_list[i]->codeset->name.size(), info.process_name.size()));
|
||||
|
||||
memcpy(out_data + written_bytes, &info, sizeof(ProcessInfo));
|
||||
written_bytes += sizeof(ProcessInfo);
|
||||
}
|
||||
|
||||
packet.SetPacketDataSize(written_bytes);
|
||||
packet.SendReply();
|
||||
}
|
||||
|
||||
void RPCServer::HandleSetGetProcess(Packet& packet, u32 operation, u32 process_id) {
|
||||
u8* out_data = packet.GetPacketData().data();
|
||||
u32 written_bytes = 0;
|
||||
|
||||
if (operation == 0) {
|
||||
// Get
|
||||
memcpy(out_data + written_bytes, &selected_pid, sizeof(selected_pid));
|
||||
written_bytes += sizeof(selected_pid);
|
||||
} else {
|
||||
// Set
|
||||
selected_pid = process_id;
|
||||
}
|
||||
|
||||
packet.SetPacketDataSize(written_bytes);
|
||||
packet.SendReply();
|
||||
}
|
||||
|
||||
bool RPCServer::ValidatePacket(const PacketHeader& packet_header) {
|
||||
if (packet_header.version <= CURRENT_VERSION) {
|
||||
switch (packet_header.packet_type) {
|
||||
case PacketType::ReadMemory:
|
||||
case PacketType::WriteMemory:
|
||||
case PacketType::ProcessList:
|
||||
case PacketType::SetGetProcess:
|
||||
if (packet_header.packet_size >= (sizeof(u32) * 2)) {
|
||||
return true;
|
||||
}
|
||||
|
@ -66,26 +138,34 @@ void RPCServer::HandleSingleRequest(std::unique_ptr<Packet> request_packet) {
|
|||
const auto packet_data = request_packet->GetPacketData();
|
||||
|
||||
if (ValidatePacket(request_packet->GetHeader())) {
|
||||
// Currently, all request types use the address/data_size wire format
|
||||
u32 address = 0;
|
||||
u32 data_size = 0;
|
||||
std::memcpy(&address, packet_data.data(), sizeof(address));
|
||||
std::memcpy(&data_size, packet_data.data() + sizeof(address), sizeof(data_size));
|
||||
// Currently, all request types use to arguments
|
||||
u32 arg1 = 0;
|
||||
u32 arg2 = 0;
|
||||
std::memcpy(&arg1, packet_data.data(), sizeof(arg1));
|
||||
std::memcpy(&arg2, packet_data.data() + sizeof(arg1), sizeof(arg2));
|
||||
|
||||
switch (request_packet->GetPacketType()) {
|
||||
case PacketType::ReadMemory:
|
||||
if (data_size > 0 && data_size <= MAX_READ_SIZE) {
|
||||
HandleReadMemory(*request_packet, address, data_size);
|
||||
if (arg2 > 0 && arg2 <= MAX_READ_SIZE) {
|
||||
HandleReadMemory(*request_packet, arg1, arg2);
|
||||
success = true;
|
||||
}
|
||||
break;
|
||||
case PacketType::WriteMemory:
|
||||
if (data_size > 0 && data_size <= MAX_PACKET_DATA_SIZE - (sizeof(u32) * 2)) {
|
||||
const auto data = packet_data.subspan(sizeof(u32) * 2, data_size);
|
||||
HandleWriteMemory(*request_packet, address, data);
|
||||
if (arg2 > 0 && arg2 <= MAX_PACKET_DATA_SIZE - (sizeof(u32) * 2)) {
|
||||
const auto data = packet_data.subspan(sizeof(u32) * 2, arg2);
|
||||
HandleWriteMemory(*request_packet, arg1, data);
|
||||
success = true;
|
||||
}
|
||||
break;
|
||||
case PacketType::ProcessList:
|
||||
HandleProcessList(*request_packet, arg1, arg2);
|
||||
success = true;
|
||||
break;
|
||||
case PacketType::SetGetProcess:
|
||||
HandleSetGetProcess(*request_packet, arg1, arg2);
|
||||
success = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2018 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
|
@ -29,6 +29,8 @@ public:
|
|||
private:
|
||||
void HandleReadMemory(Packet& packet, u32 address, u32 data_size);
|
||||
void HandleWriteMemory(Packet& packet, u32 address, std::span<const u8> data);
|
||||
void HandleProcessList(Packet& packet, u32 start_index, u32 max_amount);
|
||||
void HandleSetGetProcess(Packet& packet, u32 operation, u32 process_id);
|
||||
bool ValidatePacket(const PacketHeader& packet_header);
|
||||
void HandleSingleRequest(std::unique_ptr<Packet> request);
|
||||
void HandleRequestsLoop(std::stop_token stop_token);
|
||||
|
@ -37,6 +39,7 @@ private:
|
|||
Core::System& system;
|
||||
Common::SPSCQueue<std::unique_ptr<Packet>, true> request_queue;
|
||||
std::jthread request_handler_thread;
|
||||
u32 selected_pid = 0xFFFFFFFF;
|
||||
};
|
||||
|
||||
} // namespace Core::RPC
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue