File size: 5,555 Bytes
3708354 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 | // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/memory.h"
#include "core/tools/freezer.h"
namespace Tools {
namespace {
constexpr auto memory_freezer_ns = std::chrono::nanoseconds{1000000000 / 60};
u64 MemoryReadWidth(Core::Memory::Memory& memory, u32 width, VAddr addr) {
switch (width) {
case 1:
return memory.Read8(addr);
case 2:
return memory.Read16(addr);
case 4:
return memory.Read32(addr);
case 8:
return memory.Read64(addr);
default:
UNREACHABLE();
}
}
void MemoryWriteWidth(Core::Memory::Memory& memory, u32 width, VAddr addr, u64 value) {
switch (width) {
case 1:
memory.Write8(addr, static_cast<u8>(value));
break;
case 2:
memory.Write16(addr, static_cast<u16>(value));
break;
case 4:
memory.Write32(addr, static_cast<u32>(value));
break;
case 8:
memory.Write64(addr, value);
break;
default:
UNREACHABLE();
}
}
} // Anonymous namespace
Freezer::Freezer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& memory_)
: core_timing{core_timing_}, memory{memory_} {
event = Core::Timing::CreateEvent("MemoryFreezer::FrameCallback",
[this](s64 time, std::chrono::nanoseconds ns_late)
-> std::optional<std::chrono::nanoseconds> {
FrameCallback(ns_late);
return std::nullopt;
});
core_timing.ScheduleEvent(memory_freezer_ns, event);
}
Freezer::~Freezer() {
core_timing.UnscheduleEvent(event);
}
void Freezer::SetActive(bool is_active) {
if (!active.exchange(is_active)) {
FillEntryReads();
core_timing.ScheduleEvent(memory_freezer_ns, event);
LOG_DEBUG(Common_Memory, "Memory freezer activated!");
} else {
LOG_DEBUG(Common_Memory, "Memory freezer deactivated!");
}
}
bool Freezer::IsActive() const {
return active.load(std::memory_order_relaxed);
}
void Freezer::Clear() {
std::scoped_lock lock{entries_mutex};
LOG_DEBUG(Common_Memory, "Clearing all frozen memory values.");
entries.clear();
}
u64 Freezer::Freeze(VAddr address, u32 width) {
std::scoped_lock lock{entries_mutex};
const auto current_value = MemoryReadWidth(memory, width, address);
entries.push_back({address, width, current_value});
LOG_DEBUG(Common_Memory,
"Freezing memory for address={:016X}, width={:02X}, current_value={:016X}", address,
width, current_value);
return current_value;
}
void Freezer::Unfreeze(VAddr address) {
std::scoped_lock lock{entries_mutex};
LOG_DEBUG(Common_Memory, "Unfreezing memory for address={:016X}", address);
std::erase_if(entries, [address](const Entry& entry) { return entry.address == address; });
}
bool Freezer::IsFrozen(VAddr address) const {
std::scoped_lock lock{entries_mutex};
return FindEntry(address) != entries.cend();
}
void Freezer::SetFrozenValue(VAddr address, u64 value) {
std::scoped_lock lock{entries_mutex};
const auto iter = FindEntry(address);
if (iter == entries.cend()) {
LOG_ERROR(Common_Memory,
"Tried to set freeze value for address={:016X} that is not frozen!", address);
return;
}
LOG_DEBUG(Common_Memory,
"Manually overridden freeze value for address={:016X}, width={:02X} to value={:016X}",
iter->address, iter->width, value);
iter->value = value;
}
std::optional<Freezer::Entry> Freezer::GetEntry(VAddr address) const {
std::scoped_lock lock{entries_mutex};
const auto iter = FindEntry(address);
if (iter == entries.cend()) {
return std::nullopt;
}
return *iter;
}
std::vector<Freezer::Entry> Freezer::GetEntries() const {
std::scoped_lock lock{entries_mutex};
return entries;
}
Freezer::Entries::iterator Freezer::FindEntry(VAddr address) {
return std::find_if(entries.begin(), entries.end(),
[address](const Entry& entry) { return entry.address == address; });
}
Freezer::Entries::const_iterator Freezer::FindEntry(VAddr address) const {
return std::find_if(entries.begin(), entries.end(),
[address](const Entry& entry) { return entry.address == address; });
}
void Freezer::FrameCallback(std::chrono::nanoseconds ns_late) {
if (!IsActive()) {
LOG_DEBUG(Common_Memory, "Memory freezer has been deactivated, ending callback events.");
return;
}
std::scoped_lock lock{entries_mutex};
for (const auto& entry : entries) {
LOG_DEBUG(Common_Memory,
"Enforcing memory freeze at address={:016X}, value={:016X}, width={:02X}",
entry.address, entry.value, entry.width);
MemoryWriteWidth(memory, entry.width, entry.address, entry.value);
}
core_timing.ScheduleEvent(memory_freezer_ns - ns_late, event);
}
void Freezer::FillEntryReads() {
std::scoped_lock lock{entries_mutex};
LOG_DEBUG(Common_Memory, "Updating memory freeze entries to current values.");
for (auto& entry : entries) {
entry.value = MemoryReadWidth(memory, entry.width, entry.address);
}
}
} // namespace Tools
|