@@ -8,20 +8,22 @@

HLE::SystemVABI::VAList::~VAList() = default;

u32 HLE::SystemVABI::VAList::GetGPR(u32 gpr) const
u32 HLE::SystemVABI::VAList::GetGPR(const Core::CPUThreadGuard&, u32 gpr) const
{
return m_system.GetPPCState().gpr[gpr];
}

double HLE::SystemVABI::VAList::GetFPR(u32 fpr) const
double HLE::SystemVABI::VAList::GetFPR(const Core::CPUThreadGuard&, u32 fpr) const
{
return m_system.GetPPCState().ps[fpr].PS0AsDouble();
}

HLE::SystemVABI::VAListStruct::VAListStruct(Core::System& system, u32 address)
: VAList(system, 0), m_va_list{PowerPC::HostRead_U8(address), PowerPC::HostRead_U8(address + 1),
PowerPC::HostRead_U32(address + 4),
PowerPC::HostRead_U32(address + 8)},
HLE::SystemVABI::VAListStruct::VAListStruct(Core::System& system, const Core::CPUThreadGuard& guard,
u32 address)
: VAList(system, 0), m_va_list{PowerPC::HostRead_U8(guard, address),
PowerPC::HostRead_U8(guard, address + 1),
PowerPC::HostRead_U32(guard, address + 4),
PowerPC::HostRead_U32(guard, address + 8)},
m_address(address), m_has_fpr_area(system.GetPPCState().cr.GetBit(6) == 1)
{
m_stack = m_va_list.overflow_arg_area;
@@ -39,24 +41,24 @@ u32 HLE::SystemVABI::VAListStruct::GetFPRArea() const
return GetGPRArea() + 4 * 8;
}

u32 HLE::SystemVABI::VAListStruct::GetGPR(u32 gpr) const
u32 HLE::SystemVABI::VAListStruct::GetGPR(const Core::CPUThreadGuard& guard, u32 gpr) const
{
if (gpr < 3 || gpr > 10)
{
ERROR_LOG_FMT(OSHLE, "VAListStruct at {:08x} doesn't have GPR{}!", m_address, gpr);
return 0;
}
const u32 gpr_address = Common::AlignUp(GetGPRArea() + 4 * (gpr - 3), 4);
return PowerPC::HostRead_U32(gpr_address);
return PowerPC::HostRead_U32(guard, gpr_address);
}

double HLE::SystemVABI::VAListStruct::GetFPR(u32 fpr) const
double HLE::SystemVABI::VAListStruct::GetFPR(const Core::CPUThreadGuard& guard, u32 fpr) const
{
if (!m_has_fpr_area || fpr < 1 || fpr > 8)
{
ERROR_LOG_FMT(OSHLE, "VAListStruct at {:08x} doesn't have FPR{}!", m_address, fpr);
return 0.0;
}
const u32 fpr_address = Common::AlignUp(GetFPRArea() + 8 * (fpr - 1), 8);
return PowerPC::HostRead_F64(fpr_address);
return PowerPC::HostRead_F64(guard, fpr_address);
}
@@ -13,8 +13,9 @@

namespace Core
{
class CPUThreadGuard;
class System;
}
} // namespace Core

namespace HLE::SystemVABI
{
@@ -47,35 +48,35 @@ class VAList

// 0 - arg_ARGPOINTER
template <typename T, typename std::enable_if_t<IS_ARG_POINTER<T>>* = nullptr>
T GetArg()
T GetArg(const Core::CPUThreadGuard& guard)
{
T obj;
u32 addr = GetArg<u32>();
u32 addr = GetArg<u32>(guard);

for (size_t i = 0; i < sizeof(T); i += 1, addr += 1)
{
reinterpret_cast<u8*>(&obj)[i] = PowerPC::HostRead_U8(addr);
reinterpret_cast<u8*>(&obj)[i] = PowerPC::HostRead_U8(guard, addr);
}

return obj;
}

// 1 - arg_WORD
template <typename T, typename std::enable_if_t<IS_WORD<T>>* = nullptr>
T GetArg()
T GetArg(const Core::CPUThreadGuard& guard)
{
static_assert(!std::is_pointer<T>(), "VAList doesn't support pointers");
u64 value;

if (m_gpr <= m_gpr_max)
{
value = GetGPR(m_gpr);
value = GetGPR(guard, m_gpr);
m_gpr += 1;
}
else
{
m_stack = Common::AlignUp(m_stack, 4);
value = PowerPC::HostRead_U32(m_stack);
value = PowerPC::HostRead_U32(guard, m_stack);
m_stack += 4;
}

@@ -84,21 +85,21 @@ class VAList

// 2 - arg_DOUBLEWORD
template <typename T, typename std::enable_if_t<IS_DOUBLE_WORD<T>>* = nullptr>
T GetArg()
T GetArg(const Core::CPUThreadGuard& guard)
{
u64 value;

if (m_gpr % 2 == 0)
m_gpr += 1;
if (m_gpr < m_gpr_max)
{
value = static_cast<u64>(GetGPR(m_gpr)) << 32 | GetGPR(m_gpr + 1);
value = static_cast<u64>(GetGPR(guard, m_gpr)) << 32 | GetGPR(guard, m_gpr + 1);
m_gpr += 2;
}
else
{
m_stack = Common::AlignUp(m_stack, 8);
value = PowerPC::HostRead_U64(m_stack);
value = PowerPC::HostRead_U64(guard, m_stack);
m_stack += 8;
}

@@ -107,19 +108,19 @@ class VAList

// 3 - arg_ARGREAL
template <typename T, typename std::enable_if_t<IS_ARG_REAL<T>>* = nullptr>
T GetArg()
T GetArg(const Core::CPUThreadGuard& guard)
{
double value;

if (m_fpr <= m_fpr_max)
{
value = GetFPR(m_fpr);
value = GetFPR(guard, m_fpr);
m_fpr += 1;
}
else
{
m_stack = Common::AlignUp(m_stack, 8);
value = PowerPC::HostRead_F64(m_stack);
value = PowerPC::HostRead_F64(guard, m_stack);
m_stack += 8;
}

@@ -128,9 +129,9 @@ class VAList

// Helper
template <typename T>
T GetArgT()
T GetArgT(const Core::CPUThreadGuard& guard)
{
return static_cast<T>(GetArg<T>());
return static_cast<T>(GetArg<T>(guard));
}

protected:
@@ -142,8 +143,8 @@ class VAList
u32 m_stack;

private:
virtual u32 GetGPR(u32 gpr) const;
virtual double GetFPR(u32 fpr) const;
virtual u32 GetGPR(const Core::CPUThreadGuard& guard, u32 gpr) const;
virtual double GetFPR(const Core::CPUThreadGuard& guard, u32 fpr) const;
};

// See System V ABI (SVR4) for more details
@@ -155,7 +156,7 @@ class VAList
class VAListStruct : public VAList
{
public:
explicit VAListStruct(Core::System& system, u32 address);
explicit VAListStruct(Core::System& system, const Core::CPUThreadGuard& guard, u32 address);
~VAListStruct() = default;

private:
@@ -173,8 +174,8 @@ class VAListStruct : public VAList
u32 GetGPRArea() const;
u32 GetFPRArea() const;

u32 GetGPR(u32 gpr) const override;
double GetFPR(u32 fpr) const override;
u32 GetGPR(const Core::CPUThreadGuard& guard, u32 gpr) const override;
double GetFPR(const Core::CPUThreadGuard& guard, u32 fpr) const override;
};

} // namespace HLE::SystemVABI
@@ -14,48 +14,48 @@

namespace AddressSpace
{
u16 Accessors::ReadU16(u32 address) const
u16 Accessors::ReadU16(const Core::CPUThreadGuard& guard, u32 address) const
{
u32 result = ReadU8(address);
result = result << 8 | ReadU8(address + 1);
u32 result = ReadU8(guard, address);
result = result << 8 | ReadU8(guard, address + 1);
return result;
}

void Accessors::WriteU16(u32 address, u16 value)
void Accessors::WriteU16(const Core::CPUThreadGuard& guard, u32 address, u16 value)
{
WriteU8(address, value & 0xff);
WriteU8(address + 1, (value >> 8) & 0xff);
WriteU8(guard, address, value & 0xff);
WriteU8(guard, address + 1, (value >> 8) & 0xff);
}

u32 Accessors::ReadU32(u32 address) const
u32 Accessors::ReadU32(const Core::CPUThreadGuard& guard, u32 address) const
{
u32 result = ReadU16(address);
result = result << 16 | ReadU16(address + 2);
u32 result = ReadU16(guard, address);
result = result << 16 | ReadU16(guard, address + 2);
return result;
}

void Accessors::WriteU32(u32 address, u32 value)
void Accessors::WriteU32(const Core::CPUThreadGuard& guard, u32 address, u32 value)
{
WriteU16(address, value & 0xffff);
WriteU16(address + 2, (value >> 16) & 0xffff);
WriteU16(guard, address, value & 0xffff);
WriteU16(guard, address + 2, (value >> 16) & 0xffff);
}

u64 Accessors::ReadU64(u32 address) const
u64 Accessors::ReadU64(const Core::CPUThreadGuard& guard, u32 address) const
{
u64 result = ReadU32(address);
result = result << 32 | ReadU32(address + 4);
u64 result = ReadU32(guard, address);
result = result << 32 | ReadU32(guard, address + 4);
return result;
}

void Accessors::WriteU64(u32 address, u64 value)
void Accessors::WriteU64(const Core::CPUThreadGuard& guard, u32 address, u64 value)
{
WriteU32(address, value & 0xffffffff);
WriteU32(address + 4, (value >> 32) & 0xffffffff);
WriteU32(guard, address, value & 0xffffffff);
WriteU32(guard, address + 4, (value >> 32) & 0xffffffff);
}

float Accessors::ReadF32(u32 address) const
float Accessors::ReadF32(const Core::CPUThreadGuard& guard, u32 address) const
{
return Common::BitCast<float>(ReadU32(address));
return Common::BitCast<float>(ReadU32(guard, address));
}

Accessors::iterator Accessors::begin() const
@@ -68,8 +68,9 @@ Accessors::iterator Accessors::end() const
return nullptr;
}

std::optional<u32> Accessors::Search(u32 haystack_start, const u8* needle_start,
std::size_t needle_size, bool forwards) const
std::optional<u32> Accessors::Search(const Core::CPUThreadGuard& guard, u32 haystack_start,
const u8* needle_start, std::size_t needle_size,
bool forwards) const
{
return std::nullopt;
}
@@ -80,18 +81,49 @@ Accessors::~Accessors()

struct EffectiveAddressSpaceAccessors : Accessors
{
bool IsValidAddress(u32 address) const override { return PowerPC::HostIsRAMAddress(address); }
u8 ReadU8(u32 address) const override { return PowerPC::HostRead_U8(address); }
void WriteU8(u32 address, u8 value) override { PowerPC::HostWrite_U8(value, address); }
u16 ReadU16(u32 address) const override { return PowerPC::HostRead_U16(address); }
void WriteU16(u32 address, u16 value) override { PowerPC::HostWrite_U16(value, address); }
u32 ReadU32(u32 address) const override { return PowerPC::HostRead_U32(address); }
void WriteU32(u32 address, u32 value) override { PowerPC::HostWrite_U32(value, address); }
u64 ReadU64(u32 address) const override { return PowerPC::HostRead_U64(address); }
void WriteU64(u32 address, u64 value) override { PowerPC::HostWrite_U64(value, address); }
float ReadF32(u32 address) const override { return PowerPC::HostRead_F32(address); };

bool Matches(u32 haystack_start, const u8* needle_start, std::size_t needle_size) const
bool IsValidAddress(const Core::CPUThreadGuard& guard, u32 address) const override
{
return PowerPC::HostIsRAMAddress(guard, address);
}
u8 ReadU8(const Core::CPUThreadGuard& guard, u32 address) const override
{
return PowerPC::HostRead_U8(guard, address);
}
void WriteU8(const Core::CPUThreadGuard& guard, u32 address, u8 value) override
{
PowerPC::HostWrite_U8(guard, value, address);
}
u16 ReadU16(const Core::CPUThreadGuard& guard, u32 address) const override
{
return PowerPC::HostRead_U16(guard, address);
}
void WriteU16(const Core::CPUThreadGuard& guard, u32 address, u16 value) override
{
PowerPC::HostWrite_U16(guard, value, address);
}
u32 ReadU32(const Core::CPUThreadGuard& guard, u32 address) const override
{
return PowerPC::HostRead_U32(guard, address);
}
void WriteU32(const Core::CPUThreadGuard& guard, u32 address, u32 value) override
{
PowerPC::HostWrite_U32(guard, value, address);
}
u64 ReadU64(const Core::CPUThreadGuard& guard, u32 address) const override
{
return PowerPC::HostRead_U64(guard, address);
}
void WriteU64(const Core::CPUThreadGuard& guard, u32 address, u64 value) override
{
PowerPC::HostWrite_U64(guard, value, address);
}
float ReadF32(const Core::CPUThreadGuard& guard, u32 address) const override
{
return PowerPC::HostRead_F32(guard, address);
};

bool Matches(const Core::CPUThreadGuard& guard, u32 haystack_start, const u8* needle_start,
std::size_t needle_size) const
{
auto& system = Core::System::GetInstance();
auto& memory = system.GetMemory();
@@ -100,7 +132,7 @@ struct EffectiveAddressSpaceAccessors : Accessors
u32 offset = haystack_start & 0x0000fff;
do
{
if (!PowerPC::HostIsRAMAddress(page_base))
if (!PowerPC::HostIsRAMAddress(guard, page_base))
{
return false;
}
@@ -137,7 +169,8 @@ struct EffectiveAddressSpaceAccessors : Accessors
return (needle_size == 0);
}

std::optional<u32> Search(u32 haystack_start, const u8* needle_start, std::size_t needle_size,
std::optional<u32> Search(const Core::CPUThreadGuard& guard, u32 haystack_start,
const u8* needle_start, std::size_t needle_size,
bool forward) const override
{
u32 haystack_address = haystack_start;
@@ -150,11 +183,11 @@ struct EffectiveAddressSpaceAccessors : Accessors
const u32 haystack_offset_change = forward ? 1 : -1;
do
{
if (PowerPC::HostIsRAMAddress(haystack_address))
if (PowerPC::HostIsRAMAddress(guard, haystack_address))
{
do
{
if (Matches(haystack_address, needle_start, needle_size))
if (Matches(guard, haystack_address, needle_start, needle_size))
{
return std::optional<u32>(haystack_address);
}
@@ -173,17 +206,17 @@ struct EffectiveAddressSpaceAccessors : Accessors
struct AuxiliaryAddressSpaceAccessors : Accessors
{
static constexpr u32 aram_base_address = 0;
bool IsValidAddress(u32 address) const override
bool IsValidAddress(const Core::CPUThreadGuard& guard, u32 address) const override
{
return !SConfig::GetInstance().bWii && (address - aram_base_address) < GetSize();
}
u8 ReadU8(u32 address) const override
u8 ReadU8(const Core::CPUThreadGuard& guard, u32 address) const override
{
const u8* base = DSP::GetARAMPtr();
return base[address];
}

void WriteU8(u32 address, u8 value) override
void WriteU8(const Core::CPUThreadGuard& guard, u32 address, u8 value) override
{
u8* base = DSP::GetARAMPtr();
base[address] = value;
@@ -193,10 +226,11 @@ struct AuxiliaryAddressSpaceAccessors : Accessors

iterator end() const override { return DSP::GetARAMPtr() + GetSize(); }

std::optional<u32> Search(u32 haystack_offset, const u8* needle_start, std::size_t needle_size,
std::optional<u32> Search(const Core::CPUThreadGuard& guard, u32 haystack_offset,
const u8* needle_start, std::size_t needle_size,
bool forward) const override
{
if (!IsValidAddress(haystack_offset))
if (!IsValidAddress(guard, haystack_offset))
{
return std::nullopt;
}
@@ -243,42 +277,44 @@ struct CompositeAddressSpaceAccessors : Accessors
{
}

bool IsValidAddress(u32 address) const override
bool IsValidAddress(const Core::CPUThreadGuard& guard, u32 address) const override
{
return FindAppropriateAccessor(address) != m_accessor_mappings.end();
return FindAppropriateAccessor(guard, address) != m_accessor_mappings.end();
}

u8 ReadU8(u32 address) const override
u8 ReadU8(const Core::CPUThreadGuard& guard, u32 address) const override
{
auto mapping = FindAppropriateAccessor(address);
auto mapping = FindAppropriateAccessor(guard, address);
if (mapping == m_accessor_mappings.end())
{
return 0;
}
return mapping->accessors->ReadU8(address - mapping->base);
return mapping->accessors->ReadU8(guard, address - mapping->base);
}

void WriteU8(u32 address, u8 value) override
void WriteU8(const Core::CPUThreadGuard& guard, u32 address, u8 value) override
{
auto mapping = FindAppropriateAccessor(address);
auto mapping = FindAppropriateAccessor(guard, address);
if (mapping == m_accessor_mappings.end())
{
return;
}
return mapping->accessors->WriteU8(address - mapping->base, value);
return mapping->accessors->WriteU8(guard, address - mapping->base, value);
}

std::optional<u32> Search(u32 haystack_offset, const u8* needle_start, std::size_t needle_size,
std::optional<u32> Search(const Core::CPUThreadGuard& guard, u32 haystack_offset,
const u8* needle_start, std::size_t needle_size,
bool forward) const override
{
for (const AccessorMapping& mapping : m_accessor_mappings)
{
u32 mapping_offset = haystack_offset - mapping.base;
if (!mapping.accessors->IsValidAddress(mapping_offset))
if (!mapping.accessors->IsValidAddress(guard, mapping_offset))
{
continue;
}
auto result = mapping.accessors->Search(mapping_offset, needle_start, needle_size, forward);
auto result =
mapping.accessors->Search(guard, mapping_offset, needle_start, needle_size, forward);
if (result.has_value())
{
return std::optional<u32>(*result + mapping.base);
@@ -289,18 +325,20 @@ struct CompositeAddressSpaceAccessors : Accessors

private:
std::vector<AccessorMapping> m_accessor_mappings;
std::vector<AccessorMapping>::iterator FindAppropriateAccessor(u32 address)
std::vector<AccessorMapping>::iterator FindAppropriateAccessor(const Core::CPUThreadGuard& guard,
u32 address)
{
return std::find_if(m_accessor_mappings.begin(), m_accessor_mappings.end(),
[address](const AccessorMapping& a) {
return a.accessors->IsValidAddress(address - a.base);
[&guard, address](const AccessorMapping& a) {
return a.accessors->IsValidAddress(guard, address - a.base);
});
}
std::vector<AccessorMapping>::const_iterator FindAppropriateAccessor(u32 address) const
std::vector<AccessorMapping>::const_iterator
FindAppropriateAccessor(const Core::CPUThreadGuard& guard, u32 address) const
{
return std::find_if(m_accessor_mappings.begin(), m_accessor_mappings.end(),
[address](const AccessorMapping& a) {
return a.accessors->IsValidAddress(address - a.base);
[&guard, address](const AccessorMapping& a) {
return a.accessors->IsValidAddress(guard, address - a.base);
});
}
};
@@ -310,13 +348,19 @@ struct SmallBlockAccessors : Accessors
SmallBlockAccessors() = default;
SmallBlockAccessors(u8** alloc_base_, u32 size_) : alloc_base{alloc_base_}, size{size_} {}

bool IsValidAddress(u32 address) const override
bool IsValidAddress(const Core::CPUThreadGuard& guard, u32 address) const override
{
return (*alloc_base != nullptr) && (address < size);
}
u8 ReadU8(u32 address) const override { return (*alloc_base)[address]; }
u8 ReadU8(const Core::CPUThreadGuard& guard, u32 address) const override
{
return (*alloc_base)[address];
}

void WriteU8(u32 address, u8 value) override { (*alloc_base)[address] = value; }
void WriteU8(const Core::CPUThreadGuard& guard, u32 address, u8 value) override
{
(*alloc_base)[address] = value;
}

iterator begin() const override { return *alloc_base; }

@@ -325,11 +369,12 @@ struct SmallBlockAccessors : Accessors
return (*alloc_base == nullptr) ? nullptr : (*alloc_base + size);
}

std::optional<u32> Search(u32 haystack_offset, const u8* needle_start, std::size_t needle_size,
std::optional<u32> Search(const Core::CPUThreadGuard& guard, u32 haystack_offset,
const u8* needle_start, std::size_t needle_size,
bool forward) const override
{
if (!IsValidAddress(haystack_offset) ||
!IsValidAddress(haystack_offset + static_cast<u32>(needle_size) - 1))
if (!IsValidAddress(guard, haystack_offset) ||
!IsValidAddress(guard, haystack_offset + static_cast<u32>(needle_size) - 1))
{
return std::nullopt;
}
@@ -364,9 +409,12 @@ struct SmallBlockAccessors : Accessors

struct NullAccessors : Accessors
{
bool IsValidAddress(u32 address) const override { return false; }
u8 ReadU8(u32 address) const override { return 0; }
void WriteU8(u32 address, u8 value) override {}
bool IsValidAddress(const Core::CPUThreadGuard& guard, u32 address) const override
{
return false;
}
u8 ReadU8(const Core::CPUThreadGuard& guard, u32 address) const override { return 0; }
void WriteU8(const Core::CPUThreadGuard& guard, u32 address, u8 value) override {}
};

static EffectiveAddressSpaceAccessors s_effective_address_space_accessors;
@@ -7,6 +7,11 @@

#include "Common/CommonTypes.h"

namespace Core
{
class CPUThreadGuard;
}

namespace AddressSpace
{
enum class Type
@@ -23,24 +28,25 @@ struct Accessors
{
using iterator = const u8*;

virtual bool IsValidAddress(u32 address) const = 0;
virtual u8 ReadU8(u32 address) const = 0;
virtual void WriteU8(u32 address, u8 value) = 0;
virtual bool IsValidAddress(const Core::CPUThreadGuard& guard, u32 address) const = 0;
virtual u8 ReadU8(const Core::CPUThreadGuard& guard, u32 address) const = 0;
virtual void WriteU8(const Core::CPUThreadGuard& guard, u32 address, u8 value) = 0;

// overrideable naive implementations of below are defined
virtual u16 ReadU16(u32 address) const;
virtual void WriteU16(u32 address, u16 value);
virtual u32 ReadU32(u32 address) const;
virtual void WriteU32(u32 address, u32 value);
virtual u64 ReadU64(u32 address) const;
virtual void WriteU64(u32 address, u64 value);
virtual float ReadF32(u32 address) const;
virtual u16 ReadU16(const Core::CPUThreadGuard& guard, u32 address) const;
virtual void WriteU16(const Core::CPUThreadGuard& guard, u32 address, u16 value);
virtual u32 ReadU32(const Core::CPUThreadGuard& guard, u32 address) const;
virtual void WriteU32(const Core::CPUThreadGuard& guard, u32 address, u32 value);
virtual u64 ReadU64(const Core::CPUThreadGuard& guard, u32 address) const;
virtual void WriteU64(const Core::CPUThreadGuard& guard, u32 address, u64 value);
virtual float ReadF32(const Core::CPUThreadGuard& guard, u32 address) const;

virtual iterator begin() const;
virtual iterator end() const;

virtual std::optional<u32> Search(u32 haystack_offset, const u8* needle_start,
std::size_t needle_size, bool forward) const;
virtual std::optional<u32> Search(const Core::CPUThreadGuard& guard, u32 haystack_offset,
const u8* needle_start, std::size_t needle_size,
bool forward) const;
virtual ~Accessors();
};

@@ -17,6 +17,7 @@
#include "Common/CommonTypes.h"
#include "Common/Logging/Log.h"
#include "Common/Timer.h"

#include "Core/Boot/AncastTypes.h"
#include "Core/Boot/DolReader.h"
#include "Core/Boot/ElfReader.h"
@@ -912,7 +913,10 @@ static void FinishPPCBootstrap(Core::System& system, u64 userdata, s64 cycles_la
else
ReleasePPC();

SConfig::OnNewTitleLoad();
ASSERT(Core::IsCPUThread());
Core::CPUThreadGuard guard;
SConfig::OnNewTitleLoad(guard);

INFO_LOG_FMT(IOS, "Bootstrapping done.");
}

@@ -6,11 +6,13 @@
#include <cstring>
#include <utility>

#include "Common/Assert.h"
#include "Common/CommonTypes.h"
#include "Common/FileUtil.h"
#include "Common/Logging/Log.h"
#include "Common/MsgHandler.h"
#include "Common/Swap.h"

#include "Core/Config/MainSettings.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h"
@@ -57,6 +59,10 @@ bool Load()
{
auto& system = Core::System::GetInstance();
auto& memory = system.GetMemory();

ASSERT(Core::IsCPUThread());
Core::CPUThreadGuard guard;

memory.Write_U32(0x00000000, ADDRESS_INIT_SEMAPHORE);
memory.Write_U32(0x09142001, 0x3180);

@@ -69,7 +75,7 @@ bool Load()
g_symbolDB.Clear();
Host_NotifyMapLoaded();
}
if (g_symbolDB.LoadMap(File::GetUserPath(D_MAPS_IDX) + "mios-ipl.map"))
if (g_symbolDB.LoadMap(guard, File::GetUserPath(D_MAPS_IDX) + "mios-ipl.map"))
{
::HLE::Clear();
::HLE::PatchFunctions(system);
@@ -93,7 +99,7 @@ bool Load()
NOTICE_LOG_FMT(IOS, "IPL ready.");
SConfig::GetInstance().m_is_mios = true;
DVDInterface::UpdateRunningGameMetadata();
SConfig::OnNewTitleLoad();
SConfig::OnNewTitleLoad(guard);
return true;
}
} // namespace IOS::HLE::MIOS
@@ -67,19 +67,19 @@ bool MemoryWatcher::OpenSocket(const std::string& path)
return m_fd >= 0;
}

u32 MemoryWatcher::ChasePointer(const std::string& line)
u32 MemoryWatcher::ChasePointer(const Core::CPUThreadGuard& guard, const std::string& line)
{
u32 value = 0;
for (u32 offset : m_addresses[line])
{
value = PowerPC::HostRead_U32(value + offset);
if (!PowerPC::HostIsRAMAddress(value))
value = PowerPC::HostRead_U32(guard, value + offset);
if (!PowerPC::HostIsRAMAddress(guard, value))
break;
}
return value;
}

std::string MemoryWatcher::ComposeMessages()
std::string MemoryWatcher::ComposeMessages(const Core::CPUThreadGuard& guard)
{
std::ostringstream message_stream;
message_stream << std::hex;
@@ -89,7 +89,7 @@ std::string MemoryWatcher::ComposeMessages()
std::string address = entry.first;
u32& current_value = entry.second;

u32 new_value = ChasePointer(address);
u32 new_value = ChasePointer(guard, address);
if (new_value != current_value)
{
// Update the value
@@ -101,12 +101,12 @@ std::string MemoryWatcher::ComposeMessages()
return message_stream.str();
}

void MemoryWatcher::Step()
void MemoryWatcher::Step(const Core::CPUThreadGuard& guard)
{
if (!m_running)
return;

std::string message = ComposeMessages();
std::string message = ComposeMessages(guard);
sendto(m_fd, message.c_str(), message.size() + 1, 0, reinterpret_cast<sockaddr*>(&m_addr),
sizeof(m_addr));
}
@@ -11,6 +11,11 @@
#include <sys/un.h>
#include <vector>

namespace Core
{
class CPUThreadGuard;
}

// MemoryWatcher reads a file containing in-game memory addresses and outputs
// changes to those memory addresses to a unix domain socket as the game runs.
//
@@ -24,15 +29,15 @@ class MemoryWatcher final
public:
MemoryWatcher();
~MemoryWatcher();
void Step();
void Step(const Core::CPUThreadGuard& guard);

private:
bool LoadAddresses(const std::string& path);
bool OpenSocket(const std::string& path);

void ParseLine(const std::string& line);
u32 ChasePointer(const std::string& line);
std::string ComposeMessages();
u32 ChasePointer(const Core::CPUThreadGuard& guard, const std::string& line);
std::string ComposeMessages(const Core::CPUThreadGuard& guard);

bool m_running = false;

@@ -28,6 +28,7 @@
#include "Core/CheatCodes.h"
#include "Core/Config/SessionSettings.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h"
#include "Core/Debugger/PPCDebugInterface.h"
#include "Core/GeckoCode.h"
#include "Core/GeckoCodeConfig.h"
@@ -230,7 +231,7 @@ void LoadPatches()
LoadSpeedhacks("Speedhacks", merged);
}

static void ApplyPatches(const std::vector<Patch>& patches)
static void ApplyPatches(const Core::CPUThreadGuard& guard, const std::vector<Patch>& patches)
{
for (const Patch& patch : patches)
{
@@ -244,16 +245,17 @@ static void ApplyPatches(const std::vector<Patch>& patches)
switch (entry.type)
{
case PatchType::Patch8Bit:
if (!entry.conditional || PowerPC::HostRead_U8(addr) == static_cast<u8>(comparand))
PowerPC::HostWrite_U8(static_cast<u8>(value), addr);
if (!entry.conditional || PowerPC::HostRead_U8(guard, addr) == static_cast<u8>(comparand))
PowerPC::HostWrite_U8(guard, static_cast<u8>(value), addr);
break;
case PatchType::Patch16Bit:
if (!entry.conditional || PowerPC::HostRead_U16(addr) == static_cast<u16>(comparand))
PowerPC::HostWrite_U16(static_cast<u16>(value), addr);
if (!entry.conditional ||
PowerPC::HostRead_U16(guard, addr) == static_cast<u16>(comparand))
PowerPC::HostWrite_U16(guard, static_cast<u16>(value), addr);
break;
case PatchType::Patch32Bit:
if (!entry.conditional || PowerPC::HostRead_U32(addr) == comparand)
PowerPC::HostWrite_U32(value, addr);
if (!entry.conditional || PowerPC::HostRead_U32(guard, addr) == comparand)
PowerPC::HostWrite_U32(guard, value, addr);
break;
default:
// unknown patchtype
@@ -264,19 +266,20 @@ static void ApplyPatches(const std::vector<Patch>& patches)
}
}

static void ApplyMemoryPatches(std::span<const std::size_t> memory_patch_indices)
static void ApplyMemoryPatches(const Core::CPUThreadGuard& guard,
std::span<const std::size_t> memory_patch_indices)
{
std::lock_guard lock(s_on_frame_memory_mutex);
for (std::size_t index : memory_patch_indices)
{
PowerPC::debug_interface.ApplyExistingPatch(index);
PowerPC::debug_interface.ApplyExistingPatch(guard, index);
}
}

// Requires MSR.DR, MSR.IR
// There's no perfect way to do this, it's just a heuristic.
// We require at least 2 stack frames, if the stack is shallower than that then it won't work.
static bool IsStackSane()
static bool IsStackValid(const Core::CPUThreadGuard& guard)
{
auto& system = Core::System::GetInstance();
auto& ppc_state = system.GetPPCState();
@@ -285,19 +288,19 @@ static bool IsStackSane()

// Check the stack pointer
u32 SP = ppc_state.gpr[1];
if (!PowerPC::HostIsRAMAddress(SP))
if (!PowerPC::HostIsRAMAddress(guard, SP))
return false;

// Read the frame pointer from the stack (find 2nd frame from top), assert that it makes sense
u32 next_SP = PowerPC::HostRead_U32(SP);
if (next_SP <= SP || !PowerPC::HostIsRAMAddress(next_SP) ||
!PowerPC::HostIsRAMAddress(next_SP + 4))
u32 next_SP = PowerPC::HostRead_U32(guard, SP);
if (next_SP <= SP || !PowerPC::HostIsRAMAddress(guard, next_SP) ||
!PowerPC::HostIsRAMAddress(guard, next_SP + 4))
return false;

// Check the link register makes sense (that it points to a valid IBAT address)
const u32 address = PowerPC::HostRead_U32(next_SP + 4);
return PowerPC::HostIsInstructionRAMAddress(address) &&
0 != PowerPC::HostRead_Instruction(address);
const u32 address = PowerPC::HostRead_U32(guard, next_SP + 4);
return PowerPC::HostIsInstructionRAMAddress(guard, address) &&
0 != PowerPC::HostRead_Instruction(guard, address);
}

void AddMemoryPatch(std::size_t index)
@@ -318,11 +321,14 @@ bool ApplyFramePatches()
auto& system = Core::System::GetInstance();
auto& ppc_state = system.GetPPCState();

ASSERT(Core::IsCPUThread());
Core::CPUThreadGuard guard;

// Because we're using the VI Interrupt to time this instead of patching the game with a
// callback hook we can end up catching the game in an exception vector.
// We deal with this by returning false so that SystemTimers will reschedule us in a few cycles
// where we can try again after the CPU hopefully returns back to the normal instruction flow.
if (!ppc_state.msr.DR || !ppc_state.msr.IR || !IsStackSane())
if (!ppc_state.msr.DR || !ppc_state.msr.IR || !IsStackValid(guard))
{
DEBUG_LOG_FMT(ACTIONREPLAY,
"Need to retry later. CPU configuration is currently incorrect. PC = {:#010x}, "
@@ -331,12 +337,12 @@ bool ApplyFramePatches()
return false;
}

ApplyPatches(s_on_frame);
ApplyMemoryPatches(s_on_frame_memory);
ApplyPatches(guard, s_on_frame);
ApplyMemoryPatches(guard, s_on_frame_memory);

// Run the Gecko code handler
Gecko::RunCodeHandler();
ActionReplay::RunAllActive();
Gecko::RunCodeHandler(guard);
ActionReplay::RunAllActive(guard);

return true;
}
@@ -23,76 +23,78 @@
#include "Core/System.h"

template <typename T>
static T HostRead(u32 address);
static T HostRead(const Core::CPUThreadGuard& guard, u32 address);

template <typename T>
static void HostWrite(T var, u32 address);
static void HostWrite(const Core::CPUThreadGuard& guard, T var, u32 address);

template <>
u8 HostRead(u32 address)
u8 HostRead(const Core::CPUThreadGuard& guard, u32 address)
{
return PowerPC::HostRead_U8(address);
return PowerPC::HostRead_U8(guard, address);
}

template <>
u16 HostRead(u32 address)
u16 HostRead(const Core::CPUThreadGuard& guard, u32 address)
{
return PowerPC::HostRead_U16(address);
return PowerPC::HostRead_U16(guard, address);
}

template <>
u32 HostRead(u32 address)
u32 HostRead(const Core::CPUThreadGuard& guard, u32 address)
{
return PowerPC::HostRead_U32(address);
return PowerPC::HostRead_U32(guard, address);
}

template <>
u64 HostRead(u32 address)
u64 HostRead(const Core::CPUThreadGuard& guard, u32 address)
{
return PowerPC::HostRead_U64(address);
return PowerPC::HostRead_U64(guard, address);
}

template <>
void HostWrite(u8 var, u32 address)
void HostWrite(const Core::CPUThreadGuard& guard, u8 var, u32 address)
{
PowerPC::HostWrite_U8(var, address);
PowerPC::HostWrite_U8(guard, var, address);
}

template <>
void HostWrite(u16 var, u32 address)
void HostWrite(const Core::CPUThreadGuard& guard, u16 var, u32 address)
{
PowerPC::HostWrite_U16(var, address);
PowerPC::HostWrite_U16(guard, var, address);
}

template <>
void HostWrite(u32 var, u32 address)
void HostWrite(const Core::CPUThreadGuard& guard, u32 var, u32 address)
{
PowerPC::HostWrite_U32(var, address);
PowerPC::HostWrite_U32(guard, var, address);
}

template <>
void HostWrite(u64 var, u32 address)
void HostWrite(const Core::CPUThreadGuard& guard, u64 var, u32 address)
{
PowerPC::HostWrite_U64(var, address);
PowerPC::HostWrite_U64(guard, var, address);
}

template <typename T, typename U = T>
static double HostReadFunc(expr_func* f, vec_expr_t* args, void* c)
{
if (vec_len(args) != 1)
return 0;
const auto* guard = reinterpret_cast<const Core::CPUThreadGuard*>(c);
const u32 address = static_cast<u32>(expr_eval(&vec_nth(args, 0)));
return Common::BitCast<T>(HostRead<U>(address));
return Common::BitCast<T>(HostRead<U>(*guard, address));
}

template <typename T, typename U = T>
static double HostWriteFunc(expr_func* f, vec_expr_t* args, void* c)
{
if (vec_len(args) != 2)
return 0;
const auto* guard = reinterpret_cast<const Core::CPUThreadGuard*>(c);
const T var = static_cast<T>(expr_eval(&vec_nth(args, 0)));
const u32 address = static_cast<u32>(expr_eval(&vec_nth(args, 1)));
HostWrite<U>(Common::BitCast<U>(var), address);
HostWrite<U>(*guard, Common::BitCast<U>(var), address);
return var;
}

@@ -110,7 +112,8 @@ static double CallstackFunc(expr_func* f, vec_expr_t* args, void* c)
return 0;

std::vector<Dolphin_Debugger::CallstackEntry> stack;
bool success = Dolphin_Debugger::GetCallstack(Core::System::GetInstance(), stack);
const auto* guard = reinterpret_cast<const Core::CPUThreadGuard*>(c);
bool success = Dolphin_Debugger::GetCallstack(Core::System::GetInstance(), *guard, stack);
if (!success)
return 0;

@@ -225,7 +228,13 @@ double Expression::Evaluate() const
{
SynchronizeBindings(SynchronizeDirection::From);

double result = expr_eval(m_expr.get());
double result;
{
Core::CPUThreadGuard guard;
m_expr->param.func.context = &guard;
result = expr_eval(m_expr.get());
m_expr->param.func.context = nullptr;
}

SynchronizeBindings(SynchronizeDirection::To);

@@ -12,6 +12,11 @@
struct expr;
struct expr_var_list;

namespace Core
{
class CPUThreadGuard;
}

struct ExprDeleter
{
void operator()(expr* expression) const;
@@ -25,6 +25,7 @@ typedef SSIZE_T ssize_t;
#include <unistd.h>
#endif

#include "Common/Assert.h"
#include "Common/Event.h"
#include "Common/Logging/Log.h"
#include "Common/SocketContext.h"
@@ -799,7 +800,7 @@ static void WriteRegister()
SendReply("OK");
}

static void ReadMemory()
static void ReadMemory(const Core::CPUThreadGuard& guard)
{
static u8 reply[GDB_BFR_MAX - 4];
u32 addr, len;
@@ -819,7 +820,7 @@ static void ReadMemory()
if (len * 2 > sizeof reply)
SendReply("E01");

if (!PowerPC::HostIsRAMAddress(addr))
if (!PowerPC::HostIsRAMAddress(guard, addr))
return SendReply("E00");

auto& system = Core::System::GetInstance();
@@ -830,7 +831,7 @@ static void ReadMemory()
SendReply((char*)reply);
}

static void WriteMemory()
static void WriteMemory(const Core::CPUThreadGuard& guard)
{
u32 addr, len;
u32 i;
@@ -846,7 +847,7 @@ static void WriteMemory()
len = (len << 4) | Hex2char(s_cmd_bfr[i++]);
INFO_LOG_FMT(GDB_STUB, "gdb: write memory: {:08x} bytes to {:08x}", len, addr);

if (!PowerPC::HostIsRAMAddress(addr))
if (!PowerPC::HostIsRAMAddress(guard, addr))
return SendReply("E00");

auto& system = Core::System::GetInstance();
@@ -990,11 +991,19 @@ void ProcessCommands(bool loop_until_continue)
WriteRegister();
break;
case 'm':
ReadMemory();
{
ASSERT(Core::IsCPUThread());
Core::CPUThreadGuard guard;

ReadMemory(guard);
break;
}
case 'M':
{
WriteMemory();
ASSERT(Core::IsCPUThread());
Core::CPUThreadGuard guard;

WriteMemory(guard);
auto& system = Core::System::GetInstance();
auto& ppc_state = system.GetPPCState();
ppc_state.iCache.Reset();
@@ -14,6 +14,7 @@
#include "Common/Logging/Log.h"
#include "Common/StringUtil.h"
#include "Core/Config/MainSettings.h"
#include "Core/Core.h"
#include "Core/CoreTiming.h"
#include "Core/Debugger/Debugger_SymbolMap.h"
#include "Core/HLE/HLE.h"
@@ -339,11 +340,14 @@ void Interpreter::Run()

void Interpreter::unknown_instruction(UGeckoInstruction inst)
{
const u32 opcode = PowerPC::HostRead_U32(last_pc);
ASSERT(Core::IsCPUThread());
Core::CPUThreadGuard guard;

const u32 opcode = PowerPC::HostRead_U32(guard, last_pc);
const std::string disasm = Common::GekkoDisassembler::Disassemble(opcode, last_pc);
NOTICE_LOG_FMT(POWERPC, "Last PC = {:08x} : {}", last_pc, disasm);
Dolphin_Debugger::PrintCallstack(Core::System::GetInstance(), Common::Log::LogType::POWERPC,
Common::Log::LogLevel::LNOTICE);
Dolphin_Debugger::PrintCallstack(Core::System::GetInstance(), guard,
Common::Log::LogType::POWERPC, Common::Log::LogLevel::LNOTICE);
NOTICE_LOG_FMT(
POWERPC,
"\nIntCPU: Unknown instruction {:08x} at PC = {:08x} last_PC = {:08x} LR = {:08x}\n",
@@ -6,6 +6,7 @@
#include "Common/Assert.h"
#include "Common/CommonTypes.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h"
#include "Core/HLE/HLE.h"
#include "Core/PowerPC/Interpreter/ExceptionUtils.h"
#include "Core/PowerPC/PowerPC.h"
@@ -95,7 +96,11 @@ void Interpreter::bclrx(UGeckoInstruction inst)
void Interpreter::HLEFunction(UGeckoInstruction inst)
{
m_end_block = true;
HLE::Execute(PowerPC::ppcState.pc, inst.hex);

ASSERT(Core::IsCPUThread());
Core::CPUThreadGuard guard;

HLE::Execute(guard, PowerPC::ppcState.pc, inst.hex);
}

void Interpreter::rfi(UGeckoInstruction inst)
@@ -16,6 +16,7 @@

#include <fmt/format.h>

#include "Common/Assert.h"
#include "Common/ChunkFile.h"
#include "Common/CommonTypes.h"
#include "Common/IOFile.h"
@@ -271,8 +272,12 @@ void CompileExceptionCheck(ExceptionType type)
{
if (type == ExceptionType::FIFOWrite)
{
ASSERT(Core::IsCPUThread());
Core::CPUThreadGuard guard;

// Check in case the code has been replaced since: do we need to do this?
const OpType optype = PPCTables::GetOpInfo(PowerPC::HostRead_U32(PowerPC::ppcState.pc))->type;
const OpType optype =
PPCTables::GetOpInfo(PowerPC::HostRead_U32(guard, PowerPC::ppcState.pc))->type;
if (optype != OpType::Store && optype != OpType::StoreFP && optype != OpType::StorePS)
return;
}
@@ -519,17 +519,18 @@ TryReadInstResult TryReadInstruction(u32 address)
return TryReadInstResult{true, from_bat, hex, address};
}

u32 HostRead_Instruction(const u32 address)
u32 HostRead_Instruction(const Core::CPUThreadGuard& guard, const u32 address)
{
auto& system = Core::System::GetInstance();
auto& memory = system.GetMemory();
return ReadFromHardware<XCheckTLBFlag::OpcodeNoException, u32>(memory, address);
}

std::optional<ReadResult<u32>> HostTryReadInstruction(const u32 address,
std::optional<ReadResult<u32>> HostTryReadInstruction(const Core::CPUThreadGuard& guard,
const u32 address,
RequestedAddressSpace space)
{
if (!HostIsInstructionRAMAddress(address, space))
if (!HostIsInstructionRAMAddress(guard, address, space))
return std::nullopt;

auto& system = Core::System::GetInstance();
@@ -648,9 +649,10 @@ float Read_F32(const u32 address)
}

template <typename T>
static std::optional<ReadResult<T>> HostTryReadUX(const u32 address, RequestedAddressSpace space)
static std::optional<ReadResult<T>> HostTryReadUX(const Core::CPUThreadGuard& guard,
const u32 address, RequestedAddressSpace space)
{
if (!HostIsRAMAddress(address, space))
if (!HostIsRAMAddress(guard, address, space))
return std::nullopt;

auto& system = Core::System::GetInstance();
@@ -681,37 +683,43 @@ static std::optional<ReadResult<T>> HostTryReadUX(const u32 address, RequestedAd
return std::nullopt;
}

std::optional<ReadResult<u8>> HostTryReadU8(u32 address, RequestedAddressSpace space)
std::optional<ReadResult<u8>> HostTryReadU8(const Core::CPUThreadGuard& guard, u32 address,
RequestedAddressSpace space)
{
return HostTryReadUX<u8>(address, space);
return HostTryReadUX<u8>(guard, address, space);
}

std::optional<ReadResult<u16>> HostTryReadU16(u32 address, RequestedAddressSpace space)
std::optional<ReadResult<u16>> HostTryReadU16(const Core::CPUThreadGuard& guard, u32 address,
RequestedAddressSpace space)
{
return HostTryReadUX<u16>(address, space);
return HostTryReadUX<u16>(guard, address, space);
}

std::optional<ReadResult<u32>> HostTryReadU32(u32 address, RequestedAddressSpace space)
std::optional<ReadResult<u32>> HostTryReadU32(const Core::CPUThreadGuard& guard, u32 address,
RequestedAddressSpace space)
{
return HostTryReadUX<u32>(address, space);
return HostTryReadUX<u32>(guard, address, space);
}

std::optional<ReadResult<u64>> HostTryReadU64(u32 address, RequestedAddressSpace space)
std::optional<ReadResult<u64>> HostTryReadU64(const Core::CPUThreadGuard& guard, u32 address,
RequestedAddressSpace space)
{
return HostTryReadUX<u64>(address, space);
return HostTryReadUX<u64>(guard, address, space);
}

std::optional<ReadResult<float>> HostTryReadF32(u32 address, RequestedAddressSpace space)
std::optional<ReadResult<float>> HostTryReadF32(const Core::CPUThreadGuard& guard, u32 address,
RequestedAddressSpace space)
{
const auto result = HostTryReadUX<u32>(address, space);
const auto result = HostTryReadUX<u32>(guard, address, space);
if (!result)
return std::nullopt;
return ReadResult<float>(result->translated, Common::BitCast<float>(result->value));
}

std::optional<ReadResult<double>> HostTryReadF64(u32 address, RequestedAddressSpace space)
std::optional<ReadResult<double>> HostTryReadF64(const Core::CPUThreadGuard& guard, u32 address,
RequestedAddressSpace space)
{
const auto result = HostTryReadUX<u64>(address, space);
const auto result = HostTryReadUX<u64>(guard, address, space);
if (!result)
return std::nullopt;
return ReadResult<double>(result->translated, Common::BitCast<double>(result->value));
@@ -780,70 +788,70 @@ void Write_F64(const double var, const u32 address)
Write_U64(integral, address);
}

u8 HostRead_U8(const u32 address)
u8 HostRead_U8(const Core::CPUThreadGuard& guard, const u32 address)
{
auto& system = Core::System::GetInstance();
auto& memory = system.GetMemory();
return ReadFromHardware<XCheckTLBFlag::NoException, u8>(memory, address);
}

u16 HostRead_U16(const u32 address)
u16 HostRead_U16(const Core::CPUThreadGuard& guard, const u32 address)
{
auto& system = Core::System::GetInstance();
auto& memory = system.GetMemory();
return ReadFromHardware<XCheckTLBFlag::NoException, u16>(memory, address);
}

u32 HostRead_U32(const u32 address)
u32 HostRead_U32(const Core::CPUThreadGuard& guard, const u32 address)
{
auto& system = Core::System::GetInstance();
auto& memory = system.GetMemory();
return ReadFromHardware<XCheckTLBFlag::NoException, u32>(memory, address);
}

u64 HostRead_U64(const u32 address)
u64 HostRead_U64(const Core::CPUThreadGuard& guard, const u32 address)
{
auto& system = Core::System::GetInstance();
auto& memory = system.GetMemory();
return ReadFromHardware<XCheckTLBFlag::NoException, u64>(memory, address);
}

float HostRead_F32(const u32 address)
float HostRead_F32(const Core::CPUThreadGuard& guard, const u32 address)
{
const u32 integral = HostRead_U32(address);
const u32 integral = HostRead_U32(guard, address);

return Common::BitCast<float>(integral);
}

double HostRead_F64(const u32 address)
double HostRead_F64(const Core::CPUThreadGuard& guard, const u32 address)
{
const u64 integral = HostRead_U64(address);
const u64 integral = HostRead_U64(guard, address);

return Common::BitCast<double>(integral);
}

void HostWrite_U8(const u32 var, const u32 address)
void HostWrite_U8(const Core::CPUThreadGuard& guard, const u32 var, const u32 address)
{
auto& system = Core::System::GetInstance();
auto& memory = system.GetMemory();
WriteToHardware<XCheckTLBFlag::NoException>(system, memory, address, var, 1);
}

void HostWrite_U16(const u32 var, const u32 address)
void HostWrite_U16(const Core::CPUThreadGuard& guard, const u32 var, const u32 address)
{
auto& system = Core::System::GetInstance();
auto& memory = system.GetMemory();
WriteToHardware<XCheckTLBFlag::NoException>(system, memory, address, var, 2);
}

void HostWrite_U32(const u32 var, const u32 address)
void HostWrite_U32(const Core::CPUThreadGuard& guard, const u32 var, const u32 address)
{
auto& system = Core::System::GetInstance();
auto& memory = system.GetMemory();
WriteToHardware<XCheckTLBFlag::NoException>(system, memory, address, var, 4);
}

void HostWrite_U64(const u64 var, const u32 address)
void HostWrite_U64(const Core::CPUThreadGuard& guard, const u64 var, const u32 address)
{
auto& system = Core::System::GetInstance();
auto& memory = system.GetMemory();
@@ -853,24 +861,25 @@ void HostWrite_U64(const u64 var, const u32 address)
static_cast<u32>(var), 4);
}

void HostWrite_F32(const float var, const u32 address)
void HostWrite_F32(const Core::CPUThreadGuard& guard, const float var, const u32 address)
{
const u32 integral = Common::BitCast<u32>(var);

HostWrite_U32(integral, address);
HostWrite_U32(guard, integral, address);
}

void HostWrite_F64(const double var, const u32 address)
void HostWrite_F64(const Core::CPUThreadGuard& guard, const double var, const u32 address)
{
const u64 integral = Common::BitCast<u64>(var);

HostWrite_U64(integral, address);
HostWrite_U64(guard, integral, address);
}

static std::optional<WriteResult> HostTryWriteUX(const u32 var, const u32 address, const u32 size,
static std::optional<WriteResult> HostTryWriteUX(const Core::CPUThreadGuard& guard, const u32 var,
const u32 address, const u32 size,
RequestedAddressSpace space)
{
if (!HostIsRAMAddress(address, space))
if (!HostIsRAMAddress(guard, address, space))
return std::nullopt;

auto& system = Core::System::GetInstance();
@@ -895,56 +904,56 @@ static std::optional<WriteResult> HostTryWriteUX(const u32 var, const u32 addres
return std::nullopt;
}

std::optional<WriteResult> HostTryWriteU8(const u32 var, const u32 address,
RequestedAddressSpace space)
std::optional<WriteResult> HostTryWriteU8(const Core::CPUThreadGuard& guard, const u32 var,
const u32 address, RequestedAddressSpace space)
{
return HostTryWriteUX(var, address, 1, space);
return HostTryWriteUX(guard, var, address, 1, space);
}

std::optional<WriteResult> HostTryWriteU16(const u32 var, const u32 address,
RequestedAddressSpace space)
std::optional<WriteResult> HostTryWriteU16(const Core::CPUThreadGuard& guard, const u32 var,
const u32 address, RequestedAddressSpace space)
{
return HostTryWriteUX(var, address, 2, space);
return HostTryWriteUX(guard, var, address, 2, space);
}

std::optional<WriteResult> HostTryWriteU32(const u32 var, const u32 address,
RequestedAddressSpace space)
std::optional<WriteResult> HostTryWriteU32(const Core::CPUThreadGuard& guard, const u32 var,
const u32 address, RequestedAddressSpace space)
{
return HostTryWriteUX(var, address, 4, space);
return HostTryWriteUX(guard, var, address, 4, space);
}

std::optional<WriteResult> HostTryWriteU64(const u64 var, const u32 address,
RequestedAddressSpace space)
std::optional<WriteResult> HostTryWriteU64(const Core::CPUThreadGuard& guard, const u64 var,
const u32 address, RequestedAddressSpace space)
{
const auto result = HostTryWriteUX(static_cast<u32>(var >> 32), address, 4, space);
const auto result = HostTryWriteUX(guard, static_cast<u32>(var >> 32), address, 4, space);
if (!result)
return result;

return HostTryWriteUX(static_cast<u32>(var), address + 4, 4, space);
return HostTryWriteUX(guard, static_cast<u32>(var), address + 4, 4, space);
}

std::optional<WriteResult> HostTryWriteF32(const float var, const u32 address,
RequestedAddressSpace space)
std::optional<WriteResult> HostTryWriteF32(const Core::CPUThreadGuard& guard, const float var,
const u32 address, RequestedAddressSpace space)
{
const u32 integral = Common::BitCast<u32>(var);
return HostTryWriteU32(integral, address, space);
return HostTryWriteU32(guard, integral, address, space);
}

std::optional<WriteResult> HostTryWriteF64(const double var, const u32 address,
RequestedAddressSpace space)
std::optional<WriteResult> HostTryWriteF64(const Core::CPUThreadGuard& guard, const double var,
const u32 address, RequestedAddressSpace space)
{
const u64 integral = Common::BitCast<u64>(var);
return HostTryWriteU64(integral, address, space);
return HostTryWriteU64(guard, integral, address, space);
}

std::string HostGetString(u32 address, size_t size)
std::string HostGetString(const Core::CPUThreadGuard& guard, u32 address, size_t size)
{
std::string s;
do
{
if (!HostIsRAMAddress(address))
if (!HostIsRAMAddress(guard, address))
break;
u8 res = HostRead_U8(address);
u8 res = HostRead_U8(guard, address);
if (!res)
break;
s += static_cast<char>(res);
@@ -953,10 +962,11 @@ std::string HostGetString(u32 address, size_t size)
return s;
}

std::optional<ReadResult<std::string>> HostTryReadString(u32 address, size_t size,
std::optional<ReadResult<std::string>> HostTryReadString(const Core::CPUThreadGuard& guard,
u32 address, size_t size,
RequestedAddressSpace space)
{
auto c = HostTryReadU8(address, space);
auto c = HostTryReadU8(guard, address, space);
if (!c)
return std::nullopt;
if (c->value == 0)
@@ -967,7 +977,7 @@ std::optional<ReadResult<std::string>> HostTryReadString(u32 address, size_t siz
while (size == 0 || s.length() < size)
{
++address;
const auto res = HostTryReadU8(address, space);
const auto res = HostTryReadU8(guard, address, space);
if (!res || res->value == 0)
break;
s += static_cast<char>(res->value);
@@ -1024,7 +1034,7 @@ static bool IsRAMAddress(Memory::MemoryManager& memory, u32 address, bool transl
return false;
}

bool HostIsRAMAddress(u32 address, RequestedAddressSpace space)
bool HostIsRAMAddress(const Core::CPUThreadGuard& guard, u32 address, RequestedAddressSpace space)
{
auto& system = Core::System::GetInstance();
auto& memory = system.GetMemory();
@@ -1045,7 +1055,8 @@ bool HostIsRAMAddress(u32 address, RequestedAddressSpace space)
return false;
}

bool HostIsInstructionRAMAddress(u32 address, RequestedAddressSpace space)
bool HostIsInstructionRAMAddress(const Core::CPUThreadGuard& guard, u32 address,
RequestedAddressSpace space)
{
// Instructions are always 32bit aligned.
if (address & 3)
@@ -10,6 +10,11 @@

#include "Common/CommonTypes.h"

namespace Core
{
class CPUThreadGuard;
};

namespace PowerPC
{
// Routines for debugger UI, cheats, etc. to access emulated memory from the
@@ -27,14 +32,14 @@ enum class RequestedAddressSpace
// If the read fails (eg. address does not correspond to a mapped address in the current address
// space), a PanicAlert will be shown to the user and zero (or an empty string for the string case)
// will be returned.
u8 HostRead_U8(u32 address);
u16 HostRead_U16(u32 address);
u32 HostRead_U32(u32 address);
u64 HostRead_U64(u32 address);
float HostRead_F32(u32 address);
double HostRead_F64(u32 address);
u32 HostRead_Instruction(u32 address);
std::string HostGetString(u32 address, size_t size = 0);
u8 HostRead_U8(const Core::CPUThreadGuard& guard, u32 address);
u16 HostRead_U16(const Core::CPUThreadGuard& guard, u32 address);
u32 HostRead_U32(const Core::CPUThreadGuard& guard, u32 address);
u64 HostRead_U64(const Core::CPUThreadGuard& guard, u32 address);
float HostRead_F32(const Core::CPUThreadGuard& guard, u32 address);
double HostRead_F64(const Core::CPUThreadGuard& guard, u32 address);
u32 HostRead_Instruction(const Core::CPUThreadGuard& guard, u32 address);
std::string HostGetString(const Core::CPUThreadGuard& guard, u32 address, size_t size = 0);

template <typename T>
struct ReadResult
@@ -57,32 +62,39 @@ struct ReadResult
// value and information on whether the given address had to be translated or not. Unlike the
// HostRead functions, this does not raise a user-visible alert on failure.
std::optional<ReadResult<u8>>
HostTryReadU8(u32 address, RequestedAddressSpace space = RequestedAddressSpace::Effective);
HostTryReadU8(const Core::CPUThreadGuard& guard, u32 address,
RequestedAddressSpace space = RequestedAddressSpace::Effective);
std::optional<ReadResult<u16>>
HostTryReadU16(u32 address, RequestedAddressSpace space = RequestedAddressSpace::Effective);
HostTryReadU16(const Core::CPUThreadGuard& guard, u32 address,
RequestedAddressSpace space = RequestedAddressSpace::Effective);
std::optional<ReadResult<u32>>
HostTryReadU32(u32 address, RequestedAddressSpace space = RequestedAddressSpace::Effective);
HostTryReadU32(const Core::CPUThreadGuard& guard, u32 address,
RequestedAddressSpace space = RequestedAddressSpace::Effective);
std::optional<ReadResult<u64>>
HostTryReadU64(u32 address, RequestedAddressSpace space = RequestedAddressSpace::Effective);
HostTryReadU64(const Core::CPUThreadGuard& guard, u32 address,
RequestedAddressSpace space = RequestedAddressSpace::Effective);
std::optional<ReadResult<float>>
HostTryReadF32(u32 address, RequestedAddressSpace space = RequestedAddressSpace::Effective);
HostTryReadF32(const Core::CPUThreadGuard& guard, u32 address,
RequestedAddressSpace space = RequestedAddressSpace::Effective);
std::optional<ReadResult<double>>
HostTryReadF64(u32 address, RequestedAddressSpace space = RequestedAddressSpace::Effective);
HostTryReadF64(const Core::CPUThreadGuard& guard, u32 address,
RequestedAddressSpace space = RequestedAddressSpace::Effective);
std::optional<ReadResult<u32>>
HostTryReadInstruction(u32 address, RequestedAddressSpace space = RequestedAddressSpace::Effective);
HostTryReadInstruction(const Core::CPUThreadGuard& guard, u32 address,
RequestedAddressSpace space = RequestedAddressSpace::Effective);
std::optional<ReadResult<std::string>>
HostTryReadString(u32 address, size_t size = 0,
HostTryReadString(const Core::CPUThreadGuard& guard, u32 address, size_t size = 0,
RequestedAddressSpace space = RequestedAddressSpace::Effective);

// Writes a value to emulated memory using the currently active MMU settings.
// If the write fails (eg. address does not correspond to a mapped address in the current address
// space), a PanicAlert will be shown to the user.
void HostWrite_U8(u32 var, u32 address);
void HostWrite_U16(u32 var, u32 address);
void HostWrite_U32(u32 var, u32 address);
void HostWrite_U64(u64 var, u32 address);
void HostWrite_F32(float var, u32 address);
void HostWrite_F64(double var, u32 address);
void HostWrite_U8(const Core::CPUThreadGuard& guard, u32 var, u32 address);
void HostWrite_U16(const Core::CPUThreadGuard& guard, u32 var, u32 address);
void HostWrite_U32(const Core::CPUThreadGuard& guard, u32 var, u32 address);
void HostWrite_U64(const Core::CPUThreadGuard& guard, u64 var, u32 address);
void HostWrite_F32(const Core::CPUThreadGuard& guard, float var, u32 address);
void HostWrite_F64(const Core::CPUThreadGuard& guard, double var, u32 address);

struct WriteResult
{
@@ -98,30 +110,31 @@ struct WriteResult
// address had to be translated or not. Unlike the HostWrite functions, this does not raise a
// user-visible alert on failure.
std::optional<WriteResult>
HostTryWriteU8(u32 var, const u32 address,
HostTryWriteU8(const Core::CPUThreadGuard& guard, u32 var, const u32 address,
RequestedAddressSpace space = RequestedAddressSpace::Effective);
std::optional<WriteResult>
HostTryWriteU16(u32 var, const u32 address,
HostTryWriteU16(const Core::CPUThreadGuard& guard, u32 var, const u32 address,
RequestedAddressSpace space = RequestedAddressSpace::Effective);
std::optional<WriteResult>
HostTryWriteU32(u32 var, const u32 address,
HostTryWriteU32(const Core::CPUThreadGuard& guard, u32 var, const u32 address,
RequestedAddressSpace space = RequestedAddressSpace::Effective);
std::optional<WriteResult>
HostTryWriteU64(u64 var, const u32 address,
HostTryWriteU64(const Core::CPUThreadGuard& guard, u64 var, const u32 address,
RequestedAddressSpace space = RequestedAddressSpace::Effective);
std::optional<WriteResult>
HostTryWriteF32(float var, const u32 address,
HostTryWriteF32(const Core::CPUThreadGuard& guard, float var, const u32 address,
RequestedAddressSpace space = RequestedAddressSpace::Effective);
std::optional<WriteResult>
HostTryWriteF64(double var, const u32 address,
HostTryWriteF64(const Core::CPUThreadGuard& guard, double var, const u32 address,
RequestedAddressSpace space = RequestedAddressSpace::Effective);

// Returns whether a read or write to the given address will resolve to a RAM access in the given
// address space.
bool HostIsRAMAddress(u32 address, RequestedAddressSpace space = RequestedAddressSpace::Effective);
bool HostIsRAMAddress(const Core::CPUThreadGuard& guard, u32 address,
RequestedAddressSpace space = RequestedAddressSpace::Effective);

// Same as HostIsRAMAddress, but uses IBAT instead of DBAT.
bool HostIsInstructionRAMAddress(u32 address,
bool HostIsInstructionRAMAddress(const Core::CPUThreadGuard& guard, u32 address,
RequestedAddressSpace space = RequestedAddressSpace::Effective);

// Routines for the CPU core to access memory.
@@ -74,7 +74,8 @@ static u32 EvaluateBranchTarget(UGeckoInstruction instr, u32 pc)
// Also collect which internal branch goes the farthest.
// If any one goes farther than the blr or rfi, assume that there is more than
// one blr or rfi, and keep scanning.
bool AnalyzeFunction(u32 startAddr, Common::Symbol& func, u32 max_size)
bool AnalyzeFunction(const Core::CPUThreadGuard& guard, u32 startAddr, Common::Symbol& func,
u32 max_size)
{
if (func.name.empty())
func.Rename(fmt::format("zz_{:08x}_", startAddr));
@@ -91,15 +92,18 @@ bool AnalyzeFunction(u32 startAddr, Common::Symbol& func, u32 max_size)
for (u32 addr = startAddr; true; addr += 4)
{
func.size += 4;
if (func.size >= JitBase::code_buffer_size * 4 || !PowerPC::HostIsInstructionRAMAddress(addr))
if (func.size >= JitBase::code_buffer_size * 4 ||
!PowerPC::HostIsInstructionRAMAddress(guard, addr))
{
return false;
}

if (max_size && func.size > max_size)
{
func.address = startAddr;
func.analyzed = true;
func.size -= 4;
func.hash = HashSignatureDB::ComputeCodeChecksum(startAddr, addr - 4);
func.hash = HashSignatureDB::ComputeCodeChecksum(guard, startAddr, addr - 4);
if (numInternalBranches == 0)
func.flags |= Common::FFLAG_STRAIGHT;
return true;
@@ -121,7 +125,7 @@ bool AnalyzeFunction(u32 startAddr, Common::Symbol& func, u32 max_size)
// Let's calc the checksum and get outta here
func.address = startAddr;
func.analyzed = true;
func.hash = HashSignatureDB::ComputeCodeChecksum(startAddr, addr);
func.hash = HashSignatureDB::ComputeCodeChecksum(guard, startAddr, addr);
if (numInternalBranches == 0)
func.flags |= Common::FFLAG_STRAIGHT;
return true;
@@ -167,12 +171,13 @@ bool AnalyzeFunction(u32 startAddr, Common::Symbol& func, u32 max_size)
}
}

bool ReanalyzeFunction(u32 start_addr, Common::Symbol& func, u32 max_size)
bool ReanalyzeFunction(const Core::CPUThreadGuard& guard, u32 start_addr, Common::Symbol& func,
u32 max_size)
{
ASSERT_MSG(SYMBOLS, func.analyzed, "The function wasn't previously analyzed!");

func.analyzed = false;
return AnalyzeFunction(start_addr, func, max_size);
return AnalyzeFunction(guard, start_addr, func, max_size);
}

// Second pass analysis, done after the first pass is done for all functions
@@ -256,7 +261,8 @@ bool PPCAnalyzer::CanSwapAdjacentOps(const CodeOp& a, const CodeOp& b) const
// called by another function. Therefore, let's scan the
// entire space for bl operations and find what functions
// get called.
static void FindFunctionsFromBranches(u32 startAddr, u32 endAddr, Common::SymbolDB* func_db)
static void FindFunctionsFromBranches(const Core::CPUThreadGuard& guard, u32 startAddr, u32 endAddr,
Common::SymbolDB* func_db)
{
for (u32 addr = startAddr; addr < endAddr; addr += 4)
{
@@ -274,9 +280,9 @@ static void FindFunctionsFromBranches(u32 startAddr, u32 endAddr, Common::Symbol
u32 target = SignExt26(instr.LI << 2);
if (!instr.AA)
target += addr;
if (PowerPC::HostIsRAMAddress(target))
if (PowerPC::HostIsRAMAddress(guard, target))
{
func_db->AddFunction(target);
func_db->AddFunction(guard, target);
}
}
}
@@ -288,7 +294,7 @@ static void FindFunctionsFromBranches(u32 startAddr, u32 endAddr, Common::Symbol
}
}

static void FindFunctionsFromHandlers(PPCSymbolDB* func_db)
static void FindFunctionsFromHandlers(const Core::CPUThreadGuard& guard, PPCSymbolDB* func_db)
{
static const std::map<u32, const char* const> handlers = {
{0x80000100, "system_reset_exception_handler"},
@@ -314,15 +320,16 @@ static void FindFunctionsFromHandlers(PPCSymbolDB* func_db)
if (read_result.valid && PPCTables::IsValidInstruction(read_result.hex))
{
// Check if this function is already mapped
Common::Symbol* f = func_db->AddFunction(entry.first);
Common::Symbol* f = func_db->AddFunction(guard, entry.first);
if (!f)
continue;
f->Rename(entry.second);
}
}
}

static void FindFunctionsAfterReturnInstruction(PPCSymbolDB* func_db)
static void FindFunctionsAfterReturnInstruction(const Core::CPUThreadGuard& guard,
PPCSymbolDB* func_db)
{
std::vector<u32> funcAddrs;

@@ -346,7 +353,7 @@ static void FindFunctionsAfterReturnInstruction(PPCSymbolDB* func_db)
if (read_result.valid && PPCTables::IsValidInstruction(read_result.hex))
{
// check if this function is already mapped
Common::Symbol* f = func_db->AddFunction(location);
Common::Symbol* f = func_db->AddFunction(guard, location);
if (!f)
break;
else
@@ -358,12 +365,13 @@ static void FindFunctionsAfterReturnInstruction(PPCSymbolDB* func_db)
}
}

void FindFunctions(u32 startAddr, u32 endAddr, PPCSymbolDB* func_db)
void FindFunctions(const Core::CPUThreadGuard& guard, u32 startAddr, u32 endAddr,
PPCSymbolDB* func_db)
{
// Step 1: Find all functions
FindFunctionsFromBranches(startAddr, endAddr, func_db);
FindFunctionsFromHandlers(func_db);
FindFunctionsAfterReturnInstruction(func_db);
FindFunctionsFromBranches(guard, startAddr, endAddr, func_db);
FindFunctionsFromHandlers(guard, func_db);
FindFunctionsAfterReturnInstruction(guard, func_db);

// Step 2:
func_db->FillInCallers();
@@ -19,6 +19,11 @@ namespace Common
struct Symbol;
}

namespace Core
{
class CPUThreadGuard;
}

namespace PPCAnalyst
{
struct CodeOp // 16B
@@ -201,8 +206,11 @@ class PPCAnalyzer
bool m_enable_div_by_zero_exceptions = false;
};

void FindFunctions(u32 startAddr, u32 endAddr, PPCSymbolDB* func_db);
bool AnalyzeFunction(u32 startAddr, Common::Symbol& func, u32 max_size = 0);
bool ReanalyzeFunction(u32 start_addr, Common::Symbol& func, u32 max_size = 0);
void FindFunctions(const Core::CPUThreadGuard& guard, u32 startAddr, u32 endAddr,
PPCSymbolDB* func_db);
bool AnalyzeFunction(const Core::CPUThreadGuard& guard, u32 startAddr, Common::Symbol& func,
u32 max_size = 0);
bool ReanalyzeFunction(const Core::CPUThreadGuard& guard, u32 start_addr, Common::Symbol& func,
u32 max_size = 0);

} // namespace PPCAnalyst
@@ -32,14 +32,14 @@ PPCSymbolDB::PPCSymbolDB() : debugger{&PowerPC::debug_interface}
PPCSymbolDB::~PPCSymbolDB() = default;

// Adds the function to the list, unless it's already there
Common::Symbol* PPCSymbolDB::AddFunction(u32 start_addr)
Common::Symbol* PPCSymbolDB::AddFunction(const Core::CPUThreadGuard& guard, u32 start_addr)
{
// It's already in the list
if (m_functions.find(start_addr) != m_functions.end())
return nullptr;

Common::Symbol symbol;
if (!PPCAnalyst::AnalyzeFunction(start_addr, symbol))
if (!PPCAnalyst::AnalyzeFunction(guard, start_addr, symbol))
return nullptr;

m_functions[start_addr] = std::move(symbol);
@@ -49,16 +49,16 @@ Common::Symbol* PPCSymbolDB::AddFunction(u32 start_addr)
return ptr;
}

void PPCSymbolDB::AddKnownSymbol(u32 startAddr, u32 size, const std::string& name,
Common::Symbol::Type type)
void PPCSymbolDB::AddKnownSymbol(const Core::CPUThreadGuard& guard, u32 startAddr, u32 size,
const std::string& name, Common::Symbol::Type type)
{
auto iter = m_functions.find(startAddr);
if (iter != m_functions.end())
{
// already got it, let's just update name, checksum & size to be sure.
Common::Symbol* tempfunc = &iter->second;
tempfunc->Rename(name);
tempfunc->hash = HashSignatureDB::ComputeCodeChecksum(startAddr, startAddr + size - 4);
tempfunc->hash = HashSignatureDB::ComputeCodeChecksum(guard, startAddr, startAddr + size - 4);
tempfunc->type = type;
tempfunc->size = size;
}
@@ -71,7 +71,7 @@ void PPCSymbolDB::AddKnownSymbol(u32 startAddr, u32 size, const std::string& nam
tf.address = startAddr;
if (tf.type == Common::Symbol::Type::Function)
{
PPCAnalyst::AnalyzeFunction(startAddr, tf, size);
PPCAnalyst::AnalyzeFunction(guard, startAddr, tf, size);
// Do not truncate symbol when a size is expected
if (size != 0 && tf.size != size)
{
@@ -224,7 +224,7 @@ void PPCSymbolDB::LogFunctionCall(u32 addr)
// This one can load both leftover map files on game discs (like Zelda), and mapfiles
// produced by SaveSymbolMap below.
// bad=true means carefully load map files that might not be from exactly the right version
bool PPCSymbolDB::LoadMap(const std::string& filename, bool bad)
bool PPCSymbolDB::LoadMap(const Core::CPUThreadGuard& guard, const std::string& filename, bool bad)
{
File::IOFile f(filename, "r");
if (!f)
@@ -407,8 +407,8 @@ bool PPCSymbolDB::LoadMap(const std::string& filename, bool bad)
if (strlen(name) > 0)
{
// Can't compute the checksum if not in RAM
bool good = !bad && PowerPC::HostIsInstructionRAMAddress(vaddress) &&
PowerPC::HostIsInstructionRAMAddress(vaddress + size - 4);
bool good = !bad && PowerPC::HostIsInstructionRAMAddress(guard, vaddress) &&
PowerPC::HostIsInstructionRAMAddress(guard, vaddress + size - 4);
if (!good)
{
// check for BLR before function
@@ -423,10 +423,10 @@ bool PPCSymbolDB::LoadMap(const std::string& filename, bool bad)
if (good)
{
++good_count;
if (section_name == ".text" || section_name == ".init")
AddKnownSymbol(vaddress, size, name, Common::Symbol::Type::Function);
else
AddKnownSymbol(vaddress, size, name, Common::Symbol::Type::Data);
const Common::Symbol::Type type = section_name == ".text" || section_name == ".init" ?
Common::Symbol::Type::Function :
Common::Symbol::Type::Data;
AddKnownSymbol(guard, vaddress, size, name, type);
}
else
{
@@ -485,7 +485,7 @@ bool PPCSymbolDB::SaveSymbolMap(const std::string& filename) const
// Notes:
// - Dolphin doesn't load back code maps
// - It's a custom code map format
bool PPCSymbolDB::SaveCodeMap(const std::string& filename) const
bool PPCSymbolDB::SaveCodeMap(const Core::CPUThreadGuard& guard, const std::string& filename) const
{
constexpr int SYMBOL_NAME_LIMIT = 30;
File::IOFile f(filename, "w");
@@ -515,7 +515,7 @@ bool PPCSymbolDB::SaveCodeMap(const std::string& filename) const
// Write the code
for (u32 address = symbol.address; address < next_address; address += 4)
{
const std::string disasm = debugger->Disassemble(address);
const std::string disasm = debugger->Disassemble(&guard, address);
f.WriteString(fmt::format("{0:08x} {1:<{2}.{3}} {4}\n", address, symbol.name,
SYMBOL_NAME_LIMIT, SYMBOL_NAME_LIMIT, disasm));
}
@@ -12,15 +12,21 @@

#include "Core/Debugger/PPCDebugInterface.h"

namespace Core
{
class CPUThreadGuard;
}

// This has functionality overlapping Debugger_Symbolmap. Should merge that stuff in here later.
class PPCSymbolDB : public Common::SymbolDB
{
public:
PPCSymbolDB();
~PPCSymbolDB() override;

Common::Symbol* AddFunction(u32 start_addr) override;
void AddKnownSymbol(u32 startAddr, u32 size, const std::string& name,
Common::Symbol* AddFunction(const Core::CPUThreadGuard& guard, u32 start_addr) override;
void AddKnownSymbol(const Core::CPUThreadGuard& guard, u32 startAddr, u32 size,
const std::string& name,
Common::Symbol::Type type = Common::Symbol::Type::Function);

Common::Symbol* GetSymbolFromAddr(u32 addr) override;
@@ -29,9 +35,9 @@ class PPCSymbolDB : public Common::SymbolDB

void FillInCallers();

bool LoadMap(const std::string& filename, bool bad = false);
bool LoadMap(const Core::CPUThreadGuard& guard, const std::string& filename, bool bad = false);
bool SaveSymbolMap(const std::string& filename) const;
bool SaveCodeMap(const std::string& filename) const;
bool SaveCodeMap(const Core::CPUThreadGuard& guard, const std::string& filename) const;

void PrintCalls(u32 funcAddr) const;
void PrintCallers(u32 funcAddr) const;
@@ -101,16 +101,18 @@ bool GetRefs(MEGASignature* sig, std::istringstream* iss)
return true;
}

bool Compare(u32 address, u32 size, const MEGASignature& sig)
bool Compare(const Core::CPUThreadGuard& guard, u32 address, u32 size, const MEGASignature& sig)
{
if (size != sig.code.size() * sizeof(u32))
return false;

for (size_t i = 0; i < sig.code.size(); ++i)
{
if (sig.code[i] != 0 &&
PowerPC::HostRead_U32(static_cast<u32>(address + i * sizeof(u32))) != sig.code[i])
PowerPC::HostRead_U32(guard, static_cast<u32>(address + i * sizeof(u32))) != sig.code[i])
{
return false;
}
}
return true;
}
@@ -156,14 +158,14 @@ bool MEGASignatureDB::Save(const std::string& file_path) const
return false;
}

void MEGASignatureDB::Apply(PPCSymbolDB* symbol_db) const
void MEGASignatureDB::Apply(const Core::CPUThreadGuard& guard, PPCSymbolDB* symbol_db) const
{
for (auto& it : symbol_db->AccessSymbols())
{
auto& symbol = it.second;
for (const auto& sig : m_signatures)
{
if (Compare(symbol.address, symbol.size, sig))
if (Compare(guard, symbol.address, symbol.size, sig))
{
symbol.name = sig.name;
INFO_LOG_FMT(SYMBOLS, "Found {} at {:08x} (size: {:08x})!", sig.name, symbol.address,
@@ -180,7 +182,8 @@ void MEGASignatureDB::Populate(const PPCSymbolDB* func_db, const std::string& fi
ERROR_LOG_FMT(SYMBOLS, "MEGA database can't be populated yet.");
}

bool MEGASignatureDB::Add(u32 startAddr, u32 size, const std::string& name)
bool MEGASignatureDB::Add(const Core::CPUThreadGuard& guard, u32 startAddr, u32 size,
const std::string& name)
{
ERROR_LOG_FMT(SYMBOLS, "Can't add symbol to MEGA database yet.");
return false;
@@ -9,6 +9,11 @@
#include "Common/CommonTypes.h"
#include "Core/PowerPC/SignatureDB/SignatureDB.h"

namespace Core
{
class CPUThreadGuard;
}

class PPCSymbolDB;

struct MEGASignatureReference
@@ -46,10 +51,11 @@ class MEGASignatureDB : public SignatureDBFormatHandler
bool Save(const std::string& file_path) const override;
void List() const override;

void Apply(PPCSymbolDB* symbol_db) const override;
void Apply(const Core::CPUThreadGuard& guard, PPCSymbolDB* symbol_db) const override;
void Populate(const PPCSymbolDB* func_db, const std::string& filter = "") override;

bool Add(u32 startAddr, u32 size, const std::string& name) override;
bool Add(const Core::CPUThreadGuard& guard, u32 startAddr, u32 size,
const std::string& name) override;

private:
std::vector<MEGASignature> m_signatures;
@@ -76,20 +76,22 @@ void SignatureDB::Populate(const PPCSymbolDB* func_db, const std::string& filter
m_handler->Populate(func_db, filter);
}

void SignatureDB::Apply(PPCSymbolDB* func_db) const
void SignatureDB::Apply(const Core::CPUThreadGuard& guard, PPCSymbolDB* func_db) const
{
m_handler->Apply(func_db);
m_handler->Apply(guard, func_db);
}

bool SignatureDB::Add(u32 start_addr, u32 size, const std::string& name)
bool SignatureDB::Add(const Core::CPUThreadGuard& guard, u32 start_addr, u32 size,
const std::string& name)
{
return m_handler->Add(start_addr, size, name);
return m_handler->Add(guard, start_addr, size, name);
}

// Adds a known function to the hash database
bool HashSignatureDB::Add(u32 startAddr, u32 size, const std::string& name)
bool HashSignatureDB::Add(const Core::CPUThreadGuard& guard, u32 startAddr, u32 size,
const std::string& name)
{
u32 hash = ComputeCodeChecksum(startAddr, startAddr + size - 4);
u32 hash = ComputeCodeChecksum(guard, startAddr, startAddr + size - 4);

DBFunc temp_dbfunc;
temp_dbfunc.size = size;
@@ -119,7 +121,7 @@ void HashSignatureDB::Clear()
m_database.clear();
}

void HashSignatureDB::Apply(PPCSymbolDB* symbol_db) const
void HashSignatureDB::Apply(const Core::CPUThreadGuard& guard, PPCSymbolDB* symbol_db) const
{
for (const auto& entry : m_database)
{
@@ -158,12 +160,13 @@ void HashSignatureDB::Populate(const PPCSymbolDB* symbol_db, const std::string&
}
}

u32 HashSignatureDB::ComputeCodeChecksum(u32 offsetStart, u32 offsetEnd)
u32 HashSignatureDB::ComputeCodeChecksum(const Core::CPUThreadGuard& guard, u32 offsetStart,
u32 offsetEnd)
{
u32 sum = 0;
for (u32 offset = offsetStart; offset <= offsetEnd; offset += 4)
{
u32 opcode = PowerPC::HostRead_Instruction(offset);
u32 opcode = PowerPC::HostRead_Instruction(guard, offset);
u32 op = opcode & 0xFC000000;
u32 op2 = 0;
u32 op3 = 0;
@@ -11,6 +11,11 @@

// You're not meant to keep around SignatureDB objects persistently. Use 'em, throw them away.

namespace Core
{
class CPUThreadGuard;
}

class PPCSymbolDB;
class SignatureDBFormatHandler;

@@ -33,9 +38,9 @@ class SignatureDB
void List() const;

void Populate(const PPCSymbolDB* func_db, const std::string& filter = "");
void Apply(PPCSymbolDB* func_db) const;
void Apply(const Core::CPUThreadGuard& guard, PPCSymbolDB* func_db) const;

bool Add(u32 start_addr, u32 size, const std::string& name);
bool Add(const Core::CPUThreadGuard& guard, u32 start_addr, u32 size, const std::string& name);

private:
std::unique_ptr<SignatureDBFormatHandler> m_handler;
@@ -52,9 +57,10 @@ class SignatureDBFormatHandler
virtual void List() const = 0;

virtual void Populate(const PPCSymbolDB* func_db, const std::string& filter = "") = 0;
virtual void Apply(PPCSymbolDB* func_db) const = 0;
virtual void Apply(const Core::CPUThreadGuard& guard, PPCSymbolDB* func_db) const = 0;

virtual bool Add(u32 startAddr, u32 size, const std::string& name) = 0;
virtual bool Add(const Core::CPUThreadGuard& guard, u32 startAddr, u32 size,
const std::string& name) = 0;
};

class HashSignatureDB : public SignatureDBFormatHandler
@@ -69,15 +75,16 @@ class HashSignatureDB : public SignatureDBFormatHandler
};
using FuncDB = std::map<u32, DBFunc>;

static u32 ComputeCodeChecksum(u32 offsetStart, u32 offsetEnd);
static u32 ComputeCodeChecksum(const Core::CPUThreadGuard& guard, u32 offsetStart, u32 offsetEnd);

void Clear() override;
void List() const override;

void Populate(const PPCSymbolDB* func_db, const std::string& filter = "") override;
void Apply(PPCSymbolDB* func_db) const override;
void Apply(const Core::CPUThreadGuard& guard, PPCSymbolDB* func_db) const override;

bool Add(u32 startAddr, u32 size, const std::string& name) override;
bool Add(const Core::CPUThreadGuard& guard, u32 startAddr, u32 size,
const std::string& name) override;

protected:
// Map from signature to function. We store the DB in this map because it optimizes the
@@ -477,30 +477,31 @@ void ApplyPatchesToFiles(const std::vector<Patch>& patches, PatchIndex index,
}
}

static bool MemoryMatchesAt(u32 offset, const std::vector<u8>& value)
static bool MemoryMatchesAt(const Core::CPUThreadGuard& guard, u32 offset,
const std::vector<u8>& value)
{
for (u32 i = 0; i < value.size(); ++i)
{
auto result = PowerPC::HostTryReadU8(offset + i);
auto result = PowerPC::HostTryReadU8(guard, offset + i);
if (!result || result->value != value[i])
return false;
}
return true;
}

static void ApplyMemoryPatch(u32 offset, const std::vector<u8>& value,
const std::vector<u8>& original)
static void ApplyMemoryPatch(const Core::CPUThreadGuard& guard, u32 offset,
const std::vector<u8>& value, const std::vector<u8>& original)
{
if (value.empty())
return;

if (!original.empty() && !MemoryMatchesAt(offset, original))
if (!original.empty() && !MemoryMatchesAt(guard, offset, original))
return;

auto& system = Core::System::GetInstance();
const u32 size = static_cast<u32>(value.size());
for (u32 i = 0; i < size; ++i)
PowerPC::HostTryWriteU8(value[i], offset + i);
PowerPC::HostTryWriteU8(guard, value[i], offset + i);
const u32 overlapping_hook_count = HLE::UnpatchRange(system, offset, offset + size);
if (overlapping_hook_count != 0)
{
@@ -516,17 +517,18 @@ static std::vector<u8> GetMemoryPatchValue(const Patch& patch, const Memory& mem
return memory_patch.m_value;
}

static void ApplyMemoryPatch(const Patch& patch, const Memory& memory_patch)
static void ApplyMemoryPatch(const Core::CPUThreadGuard& guard, const Patch& patch,
const Memory& memory_patch)
{
if (memory_patch.m_offset == 0)
return;

ApplyMemoryPatch(memory_patch.m_offset | 0x80000000, GetMemoryPatchValue(patch, memory_patch),
memory_patch.m_original);
ApplyMemoryPatch(guard, memory_patch.m_offset | 0x80000000,
GetMemoryPatchValue(patch, memory_patch), memory_patch.m_original);
}

static void ApplySearchMemoryPatch(const Patch& patch, const Memory& memory_patch, u32 ram_start,
u32 length)
static void ApplySearchMemoryPatch(const Core::CPUThreadGuard& guard, const Patch& patch,
const Memory& memory_patch, u32 ram_start, u32 length)
{
if (memory_patch.m_original.empty() || memory_patch.m_align == 0)
return;
@@ -535,16 +537,16 @@ static void ApplySearchMemoryPatch(const Patch& patch, const Memory& memory_patc
for (u32 i = 0; i < length - (stride - 1); i += stride)
{
const u32 address = ram_start + i;
if (MemoryMatchesAt(address, memory_patch.m_original))
if (MemoryMatchesAt(guard, address, memory_patch.m_original))
{
ApplyMemoryPatch(address, GetMemoryPatchValue(patch, memory_patch), {});
ApplyMemoryPatch(guard, address, GetMemoryPatchValue(patch, memory_patch), {});
break;
}
}
}

static void ApplyOcarinaMemoryPatch(const Patch& patch, const Memory& memory_patch, u32 ram_start,
u32 length)
static void ApplyOcarinaMemoryPatch(const Core::CPUThreadGuard& guard, const Patch& patch,
const Memory& memory_patch, u32 ram_start, u32 length)
{
if (memory_patch.m_offset == 0)
return;
@@ -557,19 +559,19 @@ static void ApplyOcarinaMemoryPatch(const Patch& patch, const Memory& memory_pat
{
// first find the pattern
const u32 address = ram_start + i;
if (MemoryMatchesAt(address, value))
if (MemoryMatchesAt(guard, address, value))
{
for (; i < length; i += 4)
{
// from the pattern find the next blr instruction
const u32 blr_address = ram_start + i;
auto blr = PowerPC::HostTryReadU32(blr_address);
auto blr = PowerPC::HostTryReadU32(guard, blr_address);
if (blr && blr->value == 0x4e800020)
{
// and replace it with a jump to the given offset
const u32 target = memory_patch.m_offset | 0x80000000;
const u32 jmp = ((target - blr_address) & 0x03fffffc) | 0x48000000;
PowerPC::HostTryWriteU32(jmp, blr_address);
PowerPC::HostTryWriteU32(guard, jmp, blr_address);
const u32 overlapping_hook_count =
HLE::UnpatchRange(system, blr_address, blr_address + 4);
if (overlapping_hook_count != 0)
@@ -584,7 +586,7 @@ static void ApplyOcarinaMemoryPatch(const Patch& patch, const Memory& memory_pat
}
}

void ApplyGeneralMemoryPatches(const std::vector<Patch>& patches)
void ApplyGeneralMemoryPatches(const Core::CPUThreadGuard& guard, const std::vector<Patch>& patches)
{
auto& system = Core::System::GetInstance();
auto& system_memory = system.GetMemory();
@@ -597,14 +599,15 @@ void ApplyGeneralMemoryPatches(const std::vector<Patch>& patches)
continue;

if (memory.m_search)
ApplySearchMemoryPatch(patch, memory, 0x80000000, system_memory.GetRamSize());
ApplySearchMemoryPatch(guard, patch, memory, 0x80000000, system_memory.GetRamSize());
else
ApplyMemoryPatch(patch, memory);
ApplyMemoryPatch(guard, patch, memory);
}
}
}

void ApplyApploaderMemoryPatches(const std::vector<Patch>& patches, u32 ram_address, u32 ram_length)
void ApplyApploaderMemoryPatches(const Core::CPUThreadGuard& guard,
const std::vector<Patch>& patches, u32 ram_address, u32 ram_length)
{
for (const auto& patch : patches)
{
@@ -614,9 +617,9 @@ void ApplyApploaderMemoryPatches(const std::vector<Patch>& patches, u32 ram_addr
continue;

if (memory.m_ocarina)
ApplyOcarinaMemoryPatch(patch, memory, ram_address, ram_length);
ApplyOcarinaMemoryPatch(guard, patch, memory, ram_address, ram_length);
else
ApplySearchMemoryPatch(patch, memory, ram_address, ram_length);
ApplySearchMemoryPatch(guard, patch, memory, ram_address, ram_length);
}
}
}
@@ -11,6 +11,11 @@
#include "DiscIO/DirectoryBlob.h"
#include "DiscIO/RiivolutionParser.h"

namespace Core
{
class CPUThreadGuard;
}

namespace DiscIO::Riivolution
{
struct SavegameRedirect
@@ -74,8 +79,10 @@ enum class PatchIndex
void ApplyPatchesToFiles(const std::vector<Patch>& patches, PatchIndex index,
std::vector<DiscIO::FSTBuilderNode>* fst,
DiscIO::FSTBuilderNode* dol_node);
void ApplyGeneralMemoryPatches(const std::vector<Patch>& patches);
void ApplyApploaderMemoryPatches(const std::vector<Patch>& patches, u32 ram_address,
void ApplyGeneralMemoryPatches(const Core::CPUThreadGuard& guard,
const std::vector<Patch>& patches);
void ApplyApploaderMemoryPatches(const Core::CPUThreadGuard& guard,
const std::vector<Patch>& patches, u32 ram_address,
u32 ram_length);
std::optional<SavegameRedirect>
ExtractSavegameRedirect(const std::vector<Patch>& riivolution_patches);
@@ -36,6 +36,7 @@
#include "Core/CheatGeneration.h"
#include "Core/CheatSearch.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h"
#include "Core/PowerPC/PowerPC.h"

#include "DolphinQt/Config/CheatCodeEditor.h"
@@ -286,7 +287,11 @@ void CheatSearchWidget::OnNextScanClicked()
return;
}
}
Cheats::SearchErrorCode error_code = m_session->RunSearch();

const Cheats::SearchErrorCode error_code = [this] {
Core::CPUThreadGuard guard;
return m_session->RunSearch(guard);
}();

if (error_code == Cheats::SearchErrorCode::Success)
{
@@ -391,7 +396,13 @@ bool CheatSearchWidget::RefreshValues()
}

tmp->SetFilterType(Cheats::FilterType::DoNotFilter);
if (tmp->RunSearch() != Cheats::SearchErrorCode::Success)

const Cheats::SearchErrorCode error_code = [&tmp] {
Core::CPUThreadGuard guard;
return tmp->RunSearch(guard);
}();

if (error_code != Cheats::SearchErrorCode::Success)
{
m_info_label_1->setText(tr("Refresh failed. Please run the game for a bit and try again."));
return false;
@@ -483,7 +483,11 @@ void CodeDiffDialog::OnSetBLR()
Common::Symbol* symbol = g_symbolDB.GetSymbolFromAddr(item->data(Qt::UserRole).toUInt());
if (!symbol)
return;
PowerPC::debug_interface.SetPatch(symbol->address, 0x4E800020);

{
Core::CPUThreadGuard guard;
PowerPC::debug_interface.SetPatch(guard, symbol->address, 0x4E800020);
}

int row = item->row();
m_matching_results_table->item(row, 0)->setForeground(QBrush(Qt::red));
@@ -175,14 +175,15 @@ CodeViewWidget::CodeViewWidget()
Update();
});

connect(&Settings::Instance(), &Settings::ThemeChanged, this, &CodeViewWidget::Update);
connect(&Settings::Instance(), &Settings::ThemeChanged, this,
qOverload<>(&CodeViewWidget::Update));
}

CodeViewWidget::~CodeViewWidget() = default;

static u32 GetBranchFromAddress(u32 addr)
static u32 GetBranchFromAddress(const Core::CPUThreadGuard& guard, u32 addr)
{
std::string disasm = PowerPC::debug_interface.Disassemble(addr);
std::string disasm = PowerPC::debug_interface.Disassemble(&guard, addr);
size_t pos = disasm.find("->0x");

if (pos == std::string::npos)
@@ -248,6 +249,26 @@ static bool IsInstructionLoadStore(std::string_view ins)
}

void CodeViewWidget::Update()
{
if (!isVisible())
return;

if (m_updating)
return;

if (Core::GetState() == Core::State::Paused)
{
Core::CPUThreadGuard guard;
Update(&guard);
}
else
{
// If the core is running, blank out the view of memory instead of reading anything.
Update(nullptr);
}
}

void CodeViewWidget::Update(const Core::CPUThreadGuard* guard)
{
if (!isVisible())
return;
@@ -284,11 +305,11 @@ void CodeViewWidget::Update()
for (int i = 0; i < rowCount(); i++)
{
const u32 addr = AddressForRow(i);
const u32 color = PowerPC::debug_interface.GetColor(addr);
const u32 color = PowerPC::debug_interface.GetColor(guard, addr);
auto* bp_item = new QTableWidgetItem;
auto* addr_item = new QTableWidgetItem(QStringLiteral("%1").arg(addr, 8, 16, QLatin1Char('0')));

std::string disas = PowerPC::debug_interface.Disassemble(addr);
std::string disas = PowerPC::debug_interface.Disassemble(guard, addr);
auto split = disas.find('\t');

std::string ins = (split == std::string::npos ? disas : disas.substr(0, split));
@@ -332,9 +353,9 @@ void CodeViewWidget::Update()
hex_str = param.substr(pos);
}

if (hex_str.length() == VALID_BRANCH_LENGTH && desc != "---")
if (guard && hex_str.length() == VALID_BRANCH_LENGTH && desc != "---")
{
u32 branch_addr = GetBranchFromAddress(addr);
u32 branch_addr = GetBranchFromAddress(*guard, addr);
CodeViewBranch& branch = m_branches.emplace_back();
branch.src_addr = addr;
branch.dst_addr = branch_addr;
@@ -514,15 +535,20 @@ void CodeViewWidget::SetAddress(u32 address, SetAddressUpdate update)

void CodeViewWidget::ReplaceAddress(u32 address, ReplaceWith replace)
{
PowerPC::debug_interface.SetPatch(address, replace == ReplaceWith::BLR ? 0x4e800020 : 0x60000000);
Update();
Core::CPUThreadGuard guard;

PowerPC::debug_interface.SetPatch(guard, address,
replace == ReplaceWith::BLR ? 0x4e800020 : 0x60000000);

Update(&guard);
}

void CodeViewWidget::OnContextMenu()
{
QMenu* menu = new QMenu(this);

bool running = Core::GetState() != Core::State::Uninitialized;
const bool running = Core::GetState() != Core::State::Uninitialized;
const bool paused = Core::GetState() == Core::State::Paused;

const u32 addr = GetContextAddress();

@@ -567,14 +593,25 @@ void CodeViewWidget::OnContextMenu()
menu->addAction(tr("Restore instruction"), this, &CodeViewWidget::OnRestoreInstruction);

QString target;
if (addr == PowerPC::ppcState.pc && running && Core::GetState() == Core::State::Paused)
bool valid_load_store = false;
bool follow_branch_enabled = false;
if (paused)
{
const std::string line = PowerPC::debug_interface.Disassemble(PowerPC::ppcState.pc);
const auto target_it = std::find(line.begin(), line.end(), '\t');
const auto target_end = std::find(target_it, line.end(), ',');
Core::CPUThreadGuard guard;
const std::string disasm = PowerPC::debug_interface.Disassemble(&guard, PowerPC::ppcState.pc);

if (addr == PowerPC::ppcState.pc)
{
const auto target_it = std::find(disasm.begin(), disasm.end(), '\t');
const auto target_end = std::find(target_it, disasm.end(), ',');

if (target_it != line.end() && target_end != line.end())
target = QString::fromStdString(std::string{target_it + 1, target_end});
if (target_it != disasm.end() && target_end != disasm.end())
target = QString::fromStdString(std::string{target_it + 1, target_end});
}

valid_load_store = IsInstructionLoadStore(disasm);

follow_branch_enabled = GetBranchFromAddress(guard, addr);
}

auto* run_until_menu = menu->addMenu(tr("Run until (ignoring breakpoints)"));
@@ -589,18 +626,17 @@ void CodeViewWidget::OnContextMenu()
[this] { AutoStep(CodeTrace::AutoStop::Changed); });

run_until_menu->setEnabled(!target.isEmpty());
follow_branch_action->setEnabled(running && GetBranchFromAddress(addr));
follow_branch_action->setEnabled(follow_branch_enabled);

for (auto* action : {copy_address_action, copy_line_action, copy_hex_action, function_action,
ppc_action, insert_blr_action, insert_nop_action, replace_action})
{
action->setEnabled(running);
}

for (auto* action : {symbol_rename_action, symbol_size_action, symbol_end_action})
action->setEnabled(has_symbol);

const bool valid_load_store = Core::GetState() == Core::State::Paused &&
IsInstructionLoadStore(PowerPC::debug_interface.Disassemble(addr));

for (auto* action : {copy_target_memory, show_target_memory})
{
action->setEnabled(valid_load_store);
@@ -617,6 +653,8 @@ void CodeViewWidget::AutoStep(CodeTrace::AutoStop option)
// Autosteps and follows value in the target (left-most) register. The Used and Changed options
// silently follows target through reshuffles in memory and registers and stops on use or update.

Core::CPUThreadGuard guard;

CodeTrace code_trace;
bool repeat = false;

@@ -628,7 +666,7 @@ void CodeViewWidget::AutoStep(CodeTrace::AutoStop option)
do
{
// Run autostep then update codeview
const AutoStepResults results = code_trace.AutoStepping(repeat, option);
const AutoStepResults results = code_trace.AutoStepping(guard, repeat, option);
emit Host::GetInstance()->UpdateDisasmDialog();
repeat = true;

@@ -703,16 +741,24 @@ void CodeViewWidget::OnCopyTargetAddress()
if (Core::GetState() != Core::State::Paused)
return;

const std::string code_line = PowerPC::debug_interface.Disassemble(GetContextAddress());
const u32 addr = GetContextAddress();

const std::string code_line = [addr] {
Core::CPUThreadGuard guard;
return PowerPC::debug_interface.Disassemble(&guard, addr);
}();

if (!IsInstructionLoadStore(code_line))
return;

const std::optional<u32> addr =
const std::optional<u32> target_addr =
PowerPC::debug_interface.GetMemoryAddressFromInstruction(code_line);

if (addr)
QApplication::clipboard()->setText(QStringLiteral("%1").arg(*addr, 8, 16, QLatin1Char('0')));
{
QApplication::clipboard()->setText(
QStringLiteral("%1").arg(*target_addr, 8, 16, QLatin1Char('0')));
}
}

void CodeViewWidget::OnShowInMemory()
@@ -725,24 +771,33 @@ void CodeViewWidget::OnShowTargetInMemory()
if (Core::GetState() != Core::State::Paused)
return;

const std::string code_line = PowerPC::debug_interface.Disassemble(GetContextAddress());
const u32 addr = GetContextAddress();

const std::string code_line = [addr] {
Core::CPUThreadGuard guard;
return PowerPC::debug_interface.Disassemble(&guard, addr);
}();

if (!IsInstructionLoadStore(code_line))
return;

const std::optional<u32> addr =
const std::optional<u32> target_addr =
PowerPC::debug_interface.GetMemoryAddressFromInstruction(code_line);

if (addr)
emit ShowMemory(*addr);
emit ShowMemory(*target_addr);
}

void CodeViewWidget::OnCopyCode()
{
const u32 addr = GetContextAddress();

QApplication::clipboard()->setText(
QString::fromStdString(PowerPC::debug_interface.Disassemble(addr)));
const std::string text = [addr] {
Core::CPUThreadGuard guard;
return PowerPC::debug_interface.Disassemble(&guard, addr);
}();

QApplication::clipboard()->setText(QString::fromStdString(text));
}

void CodeViewWidget::OnCopyFunction()
@@ -754,13 +809,18 @@ void CodeViewWidget::OnCopyFunction()
return;

std::string text = symbol->name + "\r\n";
// we got a function
const u32 start = symbol->address;
const u32 end = start + symbol->size;
for (u32 addr = start; addr != end; addr += 4)

{
const std::string disasm = PowerPC::debug_interface.Disassemble(addr);
fmt::format_to(std::back_inserter(text), "{:08x}: {}\r\n", addr, disasm);
Core::CPUThreadGuard guard;

// we got a function
const u32 start = symbol->address;
const u32 end = start + symbol->size;
for (u32 addr = start; addr != end; addr += 4)
{
const std::string disasm = PowerPC::debug_interface.Disassemble(&guard, addr);
fmt::format_to(std::back_inserter(text), "{:08x}: {}\r\n", addr, disasm);
}
}

QApplication::clipboard()->setText(QString::fromStdString(text));
@@ -769,7 +829,11 @@ void CodeViewWidget::OnCopyFunction()
void CodeViewWidget::OnCopyHex()
{
const u32 addr = GetContextAddress();
const u32 instruction = PowerPC::debug_interface.ReadInstruction(addr);

const u32 instruction = [addr] {
Core::CPUThreadGuard guard;
return PowerPC::debug_interface.ReadInstruction(guard, addr);
}();

QApplication::clipboard()->setText(
QStringLiteral("%1").arg(instruction, 8, 16, QLatin1Char('0')));
@@ -795,9 +859,11 @@ void CodeViewWidget::OnAddFunction()
{
const u32 addr = GetContextAddress();

g_symbolDB.AddFunction(addr);
Core::CPUThreadGuard guard;

g_symbolDB.AddFunction(guard, addr);
emit SymbolsChanged();
Update();
Update(&guard);
}

void CodeViewWidget::OnInsertBLR()
@@ -818,7 +884,10 @@ void CodeViewWidget::OnFollowBranch()
{
const u32 addr = GetContextAddress();

u32 branch_addr = GetBranchFromAddress(addr);
const u32 branch_addr = [addr] {
Core::CPUThreadGuard guard;
return GetBranchFromAddress(guard, addr);
}();

if (!branch_addr)
return;
@@ -879,9 +948,11 @@ void CodeViewWidget::OnSetSymbolSize()
if (!good)
return;

PPCAnalyst::ReanalyzeFunction(symbol->address, *symbol, size);
Core::CPUThreadGuard guard;

PPCAnalyst::ReanalyzeFunction(guard, symbol->address, *symbol, size);
emit SymbolsChanged();
Update();
Update(&guard);
}

void CodeViewWidget::OnSetSymbolEndAddress()
@@ -905,37 +976,43 @@ void CodeViewWidget::OnSetSymbolEndAddress()
if (!good)
return;

PPCAnalyst::ReanalyzeFunction(symbol->address, *symbol, address - symbol->address);
Core::CPUThreadGuard guard;

PPCAnalyst::ReanalyzeFunction(guard, symbol->address, *symbol, address - symbol->address);
emit SymbolsChanged();
Update();
Update(&guard);
}

void CodeViewWidget::OnReplaceInstruction()
{
Core::CPUThreadGuard guard;

const u32 addr = GetContextAddress();

if (!PowerPC::HostIsInstructionRAMAddress(addr))
if (!PowerPC::HostIsInstructionRAMAddress(guard, addr))
return;

const PowerPC::TryReadInstResult read_result = PowerPC::TryReadInstruction(addr);
if (!read_result.valid)
return;

PatchInstructionDialog dialog(this, addr, PowerPC::debug_interface.ReadInstruction(addr));
PatchInstructionDialog dialog(this, addr, PowerPC::debug_interface.ReadInstruction(guard, addr));

if (dialog.exec() == QDialog::Accepted)
{
PowerPC::debug_interface.SetPatch(addr, dialog.GetCode());
Update();
PowerPC::debug_interface.SetPatch(guard, addr, dialog.GetCode());
Update(&guard);
}
}

void CodeViewWidget::OnRestoreInstruction()
{
Core::CPUThreadGuard guard;

const u32 addr = GetContextAddress();

PowerPC::debug_interface.UnsetPatch(addr);
Update();
PowerPC::debug_interface.UnsetPatch(guard, addr);
Update(&guard);
}

void CodeViewWidget::resizeEvent(QResizeEvent*)
@@ -15,6 +15,11 @@ class QMouseEvent;
class QResizeEvent;
class QShowEvent;

namespace Core
{
class CPUThreadGuard;
};

struct CodeViewBranch;
class BranchDisplayDelegate;

@@ -39,6 +44,7 @@ class CodeViewWidget : public QTableWidget
// Set tighter row height. Set BP column sizing. This needs to run when font type changes.
void FontBasedSizing();
void Update();
void Update(const Core::CPUThreadGuard* guard);

void ToggleBreakpoint();
void AddBreakpoint();