2026-04-28 01:15:21 +02:00
|
|
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
2026-01-13 00:27:31 +01:00
|
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
|
|
2022-04-23 04:59:50 -04:00
|
|
|
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
|
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2019-03-02 15:20:28 -05:00
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
2020-12-29 21:16:57 -03:00
|
|
|
#include <atomic>
|
2020-11-17 19:58:41 -05:00
|
|
|
|
2019-03-02 15:20:28 -05:00
|
|
|
#include "common/common_types.h"
|
2026-09-09 21:35:49 +02:00
|
|
|
#include "common/sparse_large_vector.h"
|
2023-10-22 21:16:38 -04:00
|
|
|
#include "common/typed_address.h"
|
2019-03-02 15:20:28 -05:00
|
|
|
|
|
|
|
|
namespace Common {
|
|
|
|
|
|
|
|
|
|
enum class PageType : u8 {
|
|
|
|
|
/// Page is unmapped and should cause an access error.
|
2026-09-09 21:35:49 +02:00
|
|
|
Unmapped = 0b00,
|
2019-03-02 15:20:28 -05:00
|
|
|
/// Page is mapped to regular memory. This is the only type you can get pointers to.
|
2026-09-09 21:35:49 +02:00
|
|
|
Memory = 0b01,
|
2022-06-06 12:56:01 -04:00
|
|
|
/// Page is mapped to regular memory, but inaccessible from CPU fastmem and must use
|
|
|
|
|
/// the callbacks.
|
2026-09-09 21:35:49 +02:00
|
|
|
DebugMemory = 0b10,
|
2019-03-02 15:20:28 -05:00
|
|
|
/// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
|
|
|
|
|
/// invalidation
|
2026-09-09 21:35:49 +02:00
|
|
|
RasterizerCachedMemory = 0b11,
|
2019-03-02 15:20:28 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* A (reasonably) fast way of allowing switchable and remappable process address spaces. It loosely
|
|
|
|
|
* mimics the way a real CPU page table works.
|
|
|
|
|
*/
|
|
|
|
|
struct PageTable {
|
2022-02-18 23:42:27 -08:00
|
|
|
struct TraversalEntry {
|
|
|
|
|
u64 phys_addr{};
|
|
|
|
|
std::size_t block_size{};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct TraversalContext {
|
|
|
|
|
u64 next_page{};
|
|
|
|
|
u64 next_offset{};
|
|
|
|
|
};
|
|
|
|
|
|
2026-09-09 21:35:49 +02:00
|
|
|
/// Masks out bits reserved for attribute tagging.
|
|
|
|
|
static constexpr u64 ATTRIBUTE_MASK = ((1ULL << 44) - 1) << 12;
|
|
|
|
|
|
|
|
|
|
/// Specifies sign bit for page table entries.
|
|
|
|
|
static constexpr u64 SIGN_BIT = 45 + 12; // 44 bits of data + page offset
|
2020-12-29 21:16:57 -03:00
|
|
|
|
|
|
|
|
/**
|
2026-09-09 21:35:49 +02:00
|
|
|
* Atomic tuple of host pointer, page type, and block id.
|
|
|
|
|
* This uses the lower bits of a given pointer to store the attributes.
|
2020-12-29 21:16:57 -03:00
|
|
|
* Writing and reading the pointer attribute pair is guaranteed to be atomic for the same method
|
|
|
|
|
* call. In other words, they are guaranteed to be synchronized at all times.
|
|
|
|
|
*/
|
2026-09-09 21:35:49 +02:00
|
|
|
class PageEntryData {
|
2020-12-29 21:16:57 -03:00
|
|
|
public:
|
2026-09-09 21:35:49 +02:00
|
|
|
struct Data {
|
|
|
|
|
Data(bool marked_, PageType type_, u16 block_, u64 page_)
|
|
|
|
|
: marked(static_cast<u64>(marked_) & 0b1)
|
|
|
|
|
, type(static_cast<u64>(type_) & ((1ULL << 2) - 1))
|
|
|
|
|
, block(static_cast<u64>(block_) & ((1ULL << 9) - 1))
|
|
|
|
|
, page((page_ >> 12) & ((1ULL << 45) - 1))
|
|
|
|
|
, block2((static_cast<u64>(block_) >> 9) & ((1ULL << 7) - 1)) {}
|
|
|
|
|
u64 marked : 1;
|
|
|
|
|
u64 type : 2;
|
|
|
|
|
u64 block : 9;
|
|
|
|
|
u64 page : 45; // 44 bits of actual data (64 - page offset (12) - reserved (8)) + a sign bit
|
|
|
|
|
u64 block2 : 7;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] Data Raw() const noexcept {
|
|
|
|
|
return std::bit_cast<Data>(data_raw.load(std::memory_order_relaxed));
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-29 21:16:57 -03:00
|
|
|
/// Returns the page pointer
|
2026-09-09 21:35:49 +02:00
|
|
|
[[nodiscard]] uintptr_t Pointer(bool ignored_marked = false) const noexcept {
|
|
|
|
|
return ExtractPointer(std::bit_cast<Data>(data_raw.load(std::memory_order_relaxed)), ignored_marked);
|
2020-12-29 21:16:57 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Returns the page type attribute
|
|
|
|
|
[[nodiscard]] PageType Type() const noexcept {
|
2026-09-09 21:35:49 +02:00
|
|
|
return static_cast<PageType>(std::bit_cast<Data>(data_raw.load(std::memory_order_relaxed)).type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Returns the block identifier.
|
|
|
|
|
[[nodiscard]] u16 Block() const noexcept {
|
|
|
|
|
return ExtractBlock(std::bit_cast<Data>(data_raw.load(std::memory_order_relaxed)));
|
2020-12-29 21:16:57 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Returns the page pointer and attribute pair, extracted from the same atomic read
|
2026-09-09 21:35:49 +02:00
|
|
|
[[nodiscard]] std::tuple<uintptr_t, PageType, u16> PointerTypeBlock(bool ignore_marked = false) const noexcept {
|
|
|
|
|
const auto non_atomic_raw = std::bit_cast<Data>(data_raw.load(std::memory_order_relaxed));
|
|
|
|
|
return {ExtractPointer(non_atomic_raw, ignore_marked), static_cast<PageType>(non_atomic_raw.type), ExtractBlock(non_atomic_raw)};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Write page info atomically
|
|
|
|
|
constexpr void Store(bool marked, PageType type, u16 block, uintptr_t pointer) noexcept {
|
|
|
|
|
data_raw.store(std::bit_cast<u64>(Data{marked, type, block, pointer}));
|
2020-12-29 21:16:57 -03:00
|
|
|
}
|
|
|
|
|
|
2026-09-09 21:35:49 +02:00
|
|
|
constexpr void MarkRasterizerCached() noexcept {
|
|
|
|
|
data_raw.fetch_or(0b111);
|
2020-12-29 21:16:57 -03:00
|
|
|
}
|
|
|
|
|
|
2026-09-09 21:35:49 +02:00
|
|
|
constexpr void MarkDebug(u64 ptr, u16 block) noexcept {
|
|
|
|
|
Store(true, PageType::DebugMemory, block, ptr);
|
2020-12-29 21:16:57 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Unpack a pointer from a page info raw representation
|
2026-09-09 21:35:49 +02:00
|
|
|
[[nodiscard]] static uintptr_t ExtractPointer(Data raw, bool ignore_marked = false) noexcept {
|
|
|
|
|
return raw.marked && !ignore_marked ? 0
|
|
|
|
|
// shift raw.page's fake sign bit to the actual sign bit, then sign extend
|
|
|
|
|
: ((s64)(raw.page << (64 - 44))) >> (64 - 44 - 12);
|
2020-12-29 21:16:57 -03:00
|
|
|
}
|
|
|
|
|
|
2026-09-09 21:35:49 +02:00
|
|
|
[[nodiscard]] static u16 ExtractBlock(Data raw) noexcept {
|
|
|
|
|
return static_cast<u16>(raw.block | (raw.block2 << 9));
|
2020-12-29 21:16:57 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
2026-09-09 21:35:49 +02:00
|
|
|
std::atomic<u64> data_raw;
|
|
|
|
|
static_assert(sizeof(Data) == sizeof(std::atomic<u64>));
|
2020-12-29 21:16:57 -03:00
|
|
|
};
|
|
|
|
|
|
2020-04-08 22:49:51 -04:00
|
|
|
PageTable();
|
2020-11-17 19:58:41 -05:00
|
|
|
~PageTable() noexcept;
|
|
|
|
|
|
|
|
|
|
PageTable(const PageTable&) = delete;
|
|
|
|
|
PageTable& operator=(const PageTable&) = delete;
|
2026-09-09 21:35:49 +02:00
|
|
|
PageTable(PageTable&&) noexcept = delete;
|
|
|
|
|
PageTable& operator=(PageTable&&) noexcept = delete;
|
2022-02-18 23:42:27 -08:00
|
|
|
|
2019-03-02 15:20:28 -05:00
|
|
|
/**
|
2021-01-02 09:00:05 -05:00
|
|
|
* Resizes the page table to be able to accommodate enough pages within
|
2019-03-02 15:20:28 -05:00
|
|
|
* a given address space.
|
|
|
|
|
*
|
|
|
|
|
* @param address_space_width_in_bits The address size width in bits.
|
2020-11-17 19:45:17 -05:00
|
|
|
* @param page_size_in_bits The page size in bits.
|
2019-03-02 15:20:28 -05:00
|
|
|
*/
|
2022-02-18 23:42:27 -08:00
|
|
|
void Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits);
|
2019-03-02 15:20:28 -05:00
|
|
|
|
2022-02-18 23:42:27 -08:00
|
|
|
std::size_t GetAddressSpaceBits() const {
|
2021-05-29 09:24:09 +02:00
|
|
|
return current_address_space_width_in_bits;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-13 00:27:31 +01:00
|
|
|
/// Vector of memory pointers backing each page. An entry can only be non-null if the
|
|
|
|
|
/// corresponding attribute element is of type `Memory`.
|
2026-09-09 21:35:49 +02:00
|
|
|
SparseLargeVector<PageEntryData> entries;
|
|
|
|
|
static_assert(sizeof(PageEntryData) == 8);
|
2021-05-29 09:24:09 +02:00
|
|
|
|
2022-02-18 23:42:27 -08:00
|
|
|
u8* fastmem_arena{};
|
2026-04-28 01:15:21 +02:00
|
|
|
std::size_t current_address_space_width_in_bits{};
|
2026-09-09 21:35:49 +02:00
|
|
|
std::size_t current_page_bits{};
|
2020-03-13 16:33:47 -04:00
|
|
|
};
|
|
|
|
|
|
2019-03-02 15:20:28 -05:00
|
|
|
} // namespace Common
|