|
|
@@ -5,6 +5,10 @@
|
|
|
#include <algorithm>
|
|
|
#include <cstring>
|
|
|
|
|
|
+#define BOOST_HANA_CONFIG_ENABLE_STRING_UDL
|
|
|
+#include <boost/hana/string.hpp>
|
|
|
+#undef BOOST_HANA_CONFIG_ENABLE_STRING_UDL
|
|
|
+
|
|
|
#include "common/assert.h"
|
|
|
#include "common/atomic_ops.h"
|
|
|
#include "common/common_types.h"
|
|
|
@@ -19,6 +23,8 @@
|
|
|
#include "core/memory.h"
|
|
|
#include "video_core/gpu.h"
|
|
|
|
|
|
+using namespace boost::hana::literals;
|
|
|
+
|
|
|
namespace Core::Memory {
|
|
|
|
|
|
// Implementation class used to keep the specifics of the memory subsystem hidden
|
|
|
@@ -68,18 +74,6 @@ struct Memory::Impl {
|
|
|
return system.DeviceMemory().GetPointer(paddr) + vaddr;
|
|
|
}
|
|
|
|
|
|
- [[nodiscard]] u8* GetPointer(const VAddr vaddr) const {
|
|
|
- const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw();
|
|
|
- if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {
|
|
|
- return pointer + vaddr;
|
|
|
- }
|
|
|
- const auto type = Common::PageTable::PageInfo::ExtractType(raw_pointer);
|
|
|
- if (type == Common::PageType::RasterizerCachedMemory) {
|
|
|
- return GetPointerFromRasterizerCachedMemory(vaddr);
|
|
|
- }
|
|
|
- return nullptr;
|
|
|
- }
|
|
|
-
|
|
|
u8 Read8(const VAddr addr) {
|
|
|
return Read<u8>(addr);
|
|
|
}
|
|
|
@@ -453,51 +447,106 @@ struct Memory::Impl {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Reads a particular data type out of memory at the given virtual address.
|
|
|
+ * Returns a message like "Unmapped NameBits @ 0x{:016X}Suffix".
|
|
|
*
|
|
|
- * @param vaddr The virtual address to read the data type from.
|
|
|
- *
|
|
|
- * @tparam T The data type to read out of memory. This type *must* be
|
|
|
- * trivially copyable, otherwise the behavior of this function
|
|
|
- * is undefined.
|
|
|
- *
|
|
|
- * @returns The instance of T read from the specified virtual address.
|
|
|
+ * @tparam NAME The caller name like "Read"_s or "Write"_s.
|
|
|
+ * @tparam BYTES The number of bits written. 0 is for read and sizeof(T) is for write.
|
|
|
+ * @tparam SUFFIX A suffix. ""_s is for read and " = 0x{:016X}" is for write.
|
|
|
*/
|
|
|
- template <typename T>
|
|
|
- T Read(VAddr vaddr) {
|
|
|
+ template <boost::hana::string NAME, int BYTES, boost::hana::string SUFFIX>
|
|
|
+ static consteval const char* GetPointerImplError() {
|
|
|
+ constexpr auto unmapped_fmt = ([]() {
|
|
|
+ constexpr auto prefix = "Unmapped "_s + NAME;
|
|
|
+ constexpr auto suffix = " @ 0x{:016X}"_s + SUFFIX;
|
|
|
+ const char* result = nullptr;
|
|
|
+ switch (BYTES * 8) {
|
|
|
+ case 0:
|
|
|
+ result = (prefix + suffix).c_str();
|
|
|
+ break;
|
|
|
+#define BITS_CASE(x) \
|
|
|
+ case x: \
|
|
|
+ result = (prefix + BOOST_HANA_STRING(#x) + suffix).c_str(); \
|
|
|
+ break;
|
|
|
+ BITS_CASE(8)
|
|
|
+ BITS_CASE(16)
|
|
|
+ BITS_CASE(32)
|
|
|
+ BITS_CASE(64)
|
|
|
+ BITS_CASE(128)
|
|
|
+#undef BITS_CASE
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ })();
|
|
|
+ static_assert(unmapped_fmt);
|
|
|
+ return unmapped_fmt;
|
|
|
+ }
|
|
|
+
|
|
|
+ [[nodiscard]] u8* GetPointerImpl(VAddr vaddr, auto on_unmapped, auto on_rasterizer) const {
|
|
|
// AARCH64 masks the upper 16 bit of all memory accesses
|
|
|
vaddr &= 0xffffffffffffLL;
|
|
|
|
|
|
if (vaddr >= 1uLL << current_page_table->GetAddressSpaceBits()) {
|
|
|
- LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, vaddr);
|
|
|
- return 0;
|
|
|
+ on_unmapped();
|
|
|
+ return nullptr;
|
|
|
}
|
|
|
|
|
|
// Avoid adding any extra logic to this fast-path block
|
|
|
const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw();
|
|
|
- if (const u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {
|
|
|
- T value;
|
|
|
- std::memcpy(&value, &pointer[vaddr], sizeof(T));
|
|
|
- return value;
|
|
|
+ if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {
|
|
|
+ return &pointer[vaddr];
|
|
|
}
|
|
|
switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) {
|
|
|
case Common::PageType::Unmapped:
|
|
|
- LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, vaddr);
|
|
|
- return 0;
|
|
|
+ on_unmapped();
|
|
|
+ return nullptr;
|
|
|
case Common::PageType::Memory:
|
|
|
- ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);
|
|
|
- break;
|
|
|
+ ASSERT_MSG(false, "Mapped memory page without a pointer @ 0x{:016X}", vaddr);
|
|
|
+ return nullptr;
|
|
|
case Common::PageType::RasterizerCachedMemory: {
|
|
|
- const u8* const host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)};
|
|
|
- system.GPU().FlushRegion(vaddr, sizeof(T));
|
|
|
- T value;
|
|
|
- std::memcpy(&value, host_ptr, sizeof(T));
|
|
|
- return value;
|
|
|
+ u8* const host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)};
|
|
|
+ on_rasterizer();
|
|
|
+ return host_ptr;
|
|
|
}
|
|
|
default:
|
|
|
UNREACHABLE();
|
|
|
}
|
|
|
- return {};
|
|
|
+ return nullptr;
|
|
|
+ }
|
|
|
+
|
|
|
+ [[nodiscard]] u8* GetPointer(const VAddr vaddr) const {
|
|
|
+ return GetPointerImpl(
|
|
|
+ vaddr,
|
|
|
+ [vaddr]() {
|
|
|
+ LOG_ERROR(HW_Memory, GetPointerImplError<"GetPointer"_s, 0, ""_s>(), vaddr);
|
|
|
+ },
|
|
|
+ []() {});
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Reads a particular data type out of memory at the given virtual address.
|
|
|
+ *
|
|
|
+ * @param vaddr The virtual address to read the data type from.
|
|
|
+ *
|
|
|
+ * @tparam T The data type to read out of memory. This type *must* be
|
|
|
+ * trivially copyable, otherwise the behavior of this function
|
|
|
+ * is undefined.
|
|
|
+ *
|
|
|
+ * @returns The instance of T read from the specified virtual address.
|
|
|
+ */
|
|
|
+ template <typename T>
|
|
|
+ T Read(VAddr vaddr) {
|
|
|
+ T result = 0;
|
|
|
+ const u8* const ptr = GetPointerImpl(
|
|
|
+ vaddr,
|
|
|
+ [vaddr]() {
|
|
|
+ LOG_ERROR(HW_Memory, GetPointerImplError<"Read"_s, sizeof(T), ""_s>(), vaddr);
|
|
|
+ },
|
|
|
+ [&system = system, vaddr]() { system.GPU().FlushRegion(vaddr, sizeof(T)); });
|
|
|
+ if (ptr) {
|
|
|
+ std::memcpy(&result, ptr, sizeof(T));
|
|
|
+ }
|
|
|
+ return result;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -511,110 +560,47 @@ struct Memory::Impl {
|
|
|
*/
|
|
|
template <typename T>
|
|
|
void Write(VAddr vaddr, const T data) {
|
|
|
- // AARCH64 masks the upper 16 bit of all memory accesses
|
|
|
- vaddr &= 0xffffffffffffLL;
|
|
|
-
|
|
|
- if (vaddr >= 1uLL << current_page_table->GetAddressSpaceBits()) {
|
|
|
- LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8,
|
|
|
- static_cast<u32>(data), vaddr);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // Avoid adding any extra logic to this fast-path block
|
|
|
- const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw();
|
|
|
- if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {
|
|
|
- std::memcpy(&pointer[vaddr], &data, sizeof(T));
|
|
|
- return;
|
|
|
- }
|
|
|
- switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) {
|
|
|
- case Common::PageType::Unmapped:
|
|
|
- LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8,
|
|
|
- static_cast<u32>(data), vaddr);
|
|
|
- return;
|
|
|
- case Common::PageType::Memory:
|
|
|
- ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);
|
|
|
- break;
|
|
|
- case Common::PageType::RasterizerCachedMemory: {
|
|
|
- u8* const host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)};
|
|
|
- system.GPU().InvalidateRegion(vaddr, sizeof(T));
|
|
|
- std::memcpy(host_ptr, &data, sizeof(T));
|
|
|
- break;
|
|
|
- }
|
|
|
- default:
|
|
|
- UNREACHABLE();
|
|
|
+ u8* const ptr = GetPointerImpl(
|
|
|
+ vaddr,
|
|
|
+ [vaddr, data]() {
|
|
|
+ LOG_ERROR(HW_Memory, GetPointerImplError<"Write"_s, sizeof(T), " = 0x{:016X}"_s>(),
|
|
|
+ vaddr, static_cast<u64>(data));
|
|
|
+ },
|
|
|
+ [&system = system, vaddr]() { system.GPU().InvalidateRegion(vaddr, sizeof(T)); });
|
|
|
+ if (ptr) {
|
|
|
+ std::memcpy(ptr, &data, sizeof(T));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
template <typename T>
|
|
|
bool WriteExclusive(VAddr vaddr, const T data, const T expected) {
|
|
|
- // AARCH64 masks the upper 16 bit of all memory accesses
|
|
|
- vaddr &= 0xffffffffffffLL;
|
|
|
-
|
|
|
- if (vaddr >= 1uLL << current_page_table->GetAddressSpaceBits()) {
|
|
|
- LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8,
|
|
|
- static_cast<u32>(data), vaddr);
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw();
|
|
|
- if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {
|
|
|
- // NOTE: Avoid adding any extra logic to this fast-path block
|
|
|
- const auto volatile_pointer = reinterpret_cast<volatile T*>(&pointer[vaddr]);
|
|
|
+ u8* const ptr = GetPointerImpl(
|
|
|
+ vaddr,
|
|
|
+ [vaddr, data]() {
|
|
|
+ LOG_ERROR(HW_Memory, GetPointerImplError<"Write"_s, sizeof(T), " = 0x{:016X}"_s>(),
|
|
|
+ vaddr, static_cast<u64>(data));
|
|
|
+ },
|
|
|
+ [&system = system, vaddr]() { system.GPU().InvalidateRegion(vaddr, sizeof(T)); });
|
|
|
+ if (ptr) {
|
|
|
+ const auto volatile_pointer = reinterpret_cast<volatile T*>(ptr);
|
|
|
return Common::AtomicCompareAndSwap(volatile_pointer, data, expected);
|
|
|
}
|
|
|
- switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) {
|
|
|
- case Common::PageType::Unmapped:
|
|
|
- LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8,
|
|
|
- static_cast<u32>(data), vaddr);
|
|
|
- return true;
|
|
|
- case Common::PageType::Memory:
|
|
|
- ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);
|
|
|
- break;
|
|
|
- case Common::PageType::RasterizerCachedMemory: {
|
|
|
- u8* host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)};
|
|
|
- system.GPU().InvalidateRegion(vaddr, sizeof(T));
|
|
|
- auto* pointer = reinterpret_cast<volatile T*>(&host_ptr);
|
|
|
- return Common::AtomicCompareAndSwap(pointer, data, expected);
|
|
|
- }
|
|
|
- default:
|
|
|
- UNREACHABLE();
|
|
|
- }
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
bool WriteExclusive128(VAddr vaddr, const u128 data, const u128 expected) {
|
|
|
- // AARCH64 masks the upper 16 bit of all memory accesses
|
|
|
- vaddr &= 0xffffffffffffLL;
|
|
|
-
|
|
|
- if (vaddr >= 1uLL << current_page_table->GetAddressSpaceBits()) {
|
|
|
- LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8,
|
|
|
- static_cast<u32>(data[0]), vaddr);
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw();
|
|
|
- if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {
|
|
|
- // NOTE: Avoid adding any extra logic to this fast-path block
|
|
|
- const auto volatile_pointer = reinterpret_cast<volatile u64*>(&pointer[vaddr]);
|
|
|
+ u8* const ptr = GetPointerImpl(
|
|
|
+ vaddr,
|
|
|
+ [vaddr, data]() {
|
|
|
+ LOG_ERROR(HW_Memory,
|
|
|
+ GetPointerImplError<"Write"_s, sizeof(u128), " = 0x{:016X}{:016X}"_s>(),
|
|
|
+ vaddr, static_cast<u64>(data[1]), static_cast<u64>(data[0]));
|
|
|
+ },
|
|
|
+ [&system = system, vaddr]() { system.GPU().InvalidateRegion(vaddr, sizeof(u128)); });
|
|
|
+ if (ptr) {
|
|
|
+ const auto volatile_pointer = reinterpret_cast<volatile u64*>(ptr);
|
|
|
return Common::AtomicCompareAndSwap(volatile_pointer, data, expected);
|
|
|
}
|
|
|
- switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) {
|
|
|
- case Common::PageType::Unmapped:
|
|
|
- LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}{:016X}", sizeof(data) * 8,
|
|
|
- static_cast<u64>(data[1]), static_cast<u64>(data[0]), vaddr);
|
|
|
- return true;
|
|
|
- case Common::PageType::Memory:
|
|
|
- ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);
|
|
|
- break;
|
|
|
- case Common::PageType::RasterizerCachedMemory: {
|
|
|
- u8* host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)};
|
|
|
- system.GPU().InvalidateRegion(vaddr, sizeof(u128));
|
|
|
- auto* pointer = reinterpret_cast<volatile u64*>(&host_ptr);
|
|
|
- return Common::AtomicCompareAndSwap(pointer, data, expected);
|
|
|
- }
|
|
|
- default:
|
|
|
- UNREACHABLE();
|
|
|
- }
|
|
|
return true;
|
|
|
}
|
|
|
|