Browse Source
Merge pull request #5953 from bunnei/memory-refactor-1
Merge pull request #5953 from bunnei/memory-refactor-1
Kernel Rework: Memory updates and refactoring (Part 1)remotes/1778439668340191200/nce_cpp
committed by
GitHub
56 changed files with 1690 additions and 1212 deletions
-
1src/common/CMakeLists.txt
-
5src/common/alignment.h
-
250src/common/tiny_mt.h
-
41src/core/CMakeLists.txt
-
60src/core/hle/kernel/k_address_space_info.cpp
-
15src/core/hle/kernel/k_address_space_info.h
-
149src/core/hle/kernel/k_memory_block.h
-
60src/core/hle/kernel/k_memory_block_manager.cpp
-
33src/core/hle/kernel/k_memory_block_manager.h
-
34src/core/hle/kernel/k_memory_layout.h
-
39src/core/hle/kernel/k_memory_manager.cpp
-
49src/core/hle/kernel/k_memory_manager.h
-
279src/core/hle/kernel/k_page_bitmap.h
-
23src/core/hle/kernel/k_page_heap.cpp
-
193src/core/hle/kernel/k_page_heap.h
-
14src/core/hle/kernel/k_page_linked_list.h
-
592src/core/hle/kernel/k_page_table.cpp
-
98src/core/hle/kernel/k_page_table.h
-
29src/core/hle/kernel/k_shared_memory.cpp
-
24src/core/hle/kernel/k_shared_memory.h
-
21src/core/hle/kernel/k_slab_heap.h
-
54src/core/hle/kernel/k_spin_lock.cpp
-
33src/core/hle/kernel/k_spin_lock.h
-
18src/core/hle/kernel/k_system_control.cpp
-
19src/core/hle/kernel/k_system_control.h
-
8src/core/hle/kernel/k_thread.cpp
-
88src/core/hle/kernel/kernel.cpp
-
44src/core/hle/kernel/kernel.h
-
370src/core/hle/kernel/memory/page_heap.h
-
13src/core/hle/kernel/memory/system_control.h
-
4src/core/hle/kernel/memory_types.h
-
58src/core/hle/kernel/process.cpp
-
11src/core/hle/kernel/process.h
-
14src/core/hle/kernel/process_capability.cpp
-
16src/core/hle/kernel/process_capability.h
-
51src/core/hle/kernel/svc.cpp
-
4src/core/hle/kernel/transfer_memory.cpp
-
6src/core/hle/kernel/transfer_memory.h
-
2src/core/hle/service/hid/hid.cpp
-
4src/core/hle/service/hid/hid.h
-
2src/core/hle/service/hid/irs.cpp
-
4src/core/hle/service/hid/irs.h
-
30src/core/hle/service/ldr/ldr.cpp
-
4src/core/hle/service/ns/pl_u.cpp
-
2src/core/hle/service/time/time_sharedmemory.cpp
-
6src/core/hle/service/time/time_sharedmemory.h
-
2src/core/loader/deconstructed_rom_directory.cpp
-
2src/core/loader/elf.cpp
-
2src/core/loader/kip.cpp
-
2src/core/loader/nro.cpp
-
2src/core/loader/nso.cpp
-
2src/core/memory.cpp
-
10src/core/memory.h
-
2src/core/memory/cheat_engine.cpp
-
2src/core/reporter.cpp
-
2src/video_core/memory_manager.cpp
@ -0,0 +1,250 @@ |
|||||
|
// Copyright 2021 yuzu Emulator Project |
||||
|
// Licensed under GPLv2 or any later version |
||||
|
// Refer to the license.txt file included. |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include <array> |
||||
|
|
||||
|
#include "common/alignment.h" |
||||
|
#include "common/common_types.h" |
||||
|
|
||||
|
namespace Common { |
||||
|
|
||||
|
// Implementation of TinyMT (mersenne twister RNG). |
||||
|
// Like Nintendo, we will use the sample parameters. |
||||
|
class TinyMT { |
||||
|
public: |
||||
|
static constexpr std::size_t NumStateWords = 4; |
||||
|
|
||||
|
struct State { |
||||
|
std::array<u32, NumStateWords> data{}; |
||||
|
}; |
||||
|
|
||||
|
private: |
||||
|
static constexpr u32 ParamMat1 = 0x8F7011EE; |
||||
|
static constexpr u32 ParamMat2 = 0xFC78FF1F; |
||||
|
static constexpr u32 ParamTmat = 0x3793FDFF; |
||||
|
|
||||
|
static constexpr u32 ParamMult = 0x6C078965; |
||||
|
static constexpr u32 ParamPlus = 0x0019660D; |
||||
|
static constexpr u32 ParamXor = 0x5D588B65; |
||||
|
|
||||
|
static constexpr u32 TopBitmask = 0x7FFFFFFF; |
||||
|
|
||||
|
static constexpr int MinimumInitIterations = 8; |
||||
|
static constexpr int NumDiscardedInitOutputs = 8; |
||||
|
|
||||
|
static constexpr u32 XorByShifted27(u32 value) { |
||||
|
return value ^ (value >> 27); |
||||
|
} |
||||
|
|
||||
|
static constexpr u32 XorByShifted30(u32 value) { |
||||
|
return value ^ (value >> 30); |
||||
|
} |
||||
|
|
||||
|
private: |
||||
|
State state{}; |
||||
|
|
||||
|
private: |
||||
|
// Internal API. |
||||
|
void FinalizeInitialization() { |
||||
|
const u32 state0 = this->state.data[0] & TopBitmask; |
||||
|
const u32 state1 = this->state.data[1]; |
||||
|
const u32 state2 = this->state.data[2]; |
||||
|
const u32 state3 = this->state.data[3]; |
||||
|
|
||||
|
if (state0 == 0 && state1 == 0 && state2 == 0 && state3 == 0) { |
||||
|
this->state.data[0] = 'T'; |
||||
|
this->state.data[1] = 'I'; |
||||
|
this->state.data[2] = 'N'; |
||||
|
this->state.data[3] = 'Y'; |
||||
|
} |
||||
|
|
||||
|
for (int i = 0; i < NumDiscardedInitOutputs; i++) { |
||||
|
this->GenerateRandomU32(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
u32 GenerateRandomU24() { |
||||
|
return (this->GenerateRandomU32() >> 8); |
||||
|
} |
||||
|
|
||||
|
static void GenerateInitialValuePlus(TinyMT::State* state, int index, u32 value) { |
||||
|
u32& state0 = state->data[(index + 0) % NumStateWords]; |
||||
|
u32& state1 = state->data[(index + 1) % NumStateWords]; |
||||
|
u32& state2 = state->data[(index + 2) % NumStateWords]; |
||||
|
u32& state3 = state->data[(index + 3) % NumStateWords]; |
||||
|
|
||||
|
const u32 x = XorByShifted27(state0 ^ state1 ^ state3) * ParamPlus; |
||||
|
const u32 y = x + index + value; |
||||
|
|
||||
|
state0 = y; |
||||
|
state1 += x; |
||||
|
state2 += y; |
||||
|
} |
||||
|
|
||||
|
static void GenerateInitialValueXor(TinyMT::State* state, int index) { |
||||
|
u32& state0 = state->data[(index + 0) % NumStateWords]; |
||||
|
u32& state1 = state->data[(index + 1) % NumStateWords]; |
||||
|
u32& state2 = state->data[(index + 2) % NumStateWords]; |
||||
|
u32& state3 = state->data[(index + 3) % NumStateWords]; |
||||
|
|
||||
|
const u32 x = XorByShifted27(state0 + state1 + state3) * ParamXor; |
||||
|
const u32 y = x - index; |
||||
|
|
||||
|
state0 = y; |
||||
|
state1 ^= x; |
||||
|
state2 ^= y; |
||||
|
} |
||||
|
|
||||
|
public: |
||||
|
constexpr TinyMT() = default; |
||||
|
|
||||
|
// Public API. |
||||
|
|
||||
|
// Initialization. |
||||
|
void Initialize(u32 seed) { |
||||
|
this->state.data[0] = seed; |
||||
|
this->state.data[1] = ParamMat1; |
||||
|
this->state.data[2] = ParamMat2; |
||||
|
this->state.data[3] = ParamTmat; |
||||
|
|
||||
|
for (int i = 1; i < MinimumInitIterations; i++) { |
||||
|
const u32 mixed = XorByShifted30(this->state.data[(i - 1) % NumStateWords]); |
||||
|
this->state.data[i % NumStateWords] ^= mixed * ParamMult + i; |
||||
|
} |
||||
|
|
||||
|
this->FinalizeInitialization(); |
||||
|
} |
||||
|
|
||||
|
void Initialize(const u32* seed, int seed_count) { |
||||
|
this->state.data[0] = 0; |
||||
|
this->state.data[1] = ParamMat1; |
||||
|
this->state.data[2] = ParamMat2; |
||||
|
this->state.data[3] = ParamTmat; |
||||
|
|
||||
|
{ |
||||
|
const int num_init_iterations = std::max(seed_count + 1, MinimumInitIterations) - 1; |
||||
|
|
||||
|
GenerateInitialValuePlus(&this->state, 0, seed_count); |
||||
|
|
||||
|
for (int i = 0; i < num_init_iterations; i++) { |
||||
|
GenerateInitialValuePlus(&this->state, (i + 1) % NumStateWords, |
||||
|
(i < seed_count) ? seed[i] : 0); |
||||
|
} |
||||
|
|
||||
|
for (int i = 0; i < static_cast<int>(NumStateWords); i++) { |
||||
|
GenerateInitialValueXor(&this->state, |
||||
|
(i + 1 + num_init_iterations) % NumStateWords); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
this->FinalizeInitialization(); |
||||
|
} |
||||
|
|
||||
|
// State management. |
||||
|
void GetState(TinyMT::State& out) const { |
||||
|
out.data = this->state.data; |
||||
|
} |
||||
|
|
||||
|
void SetState(const TinyMT::State& state_) { |
||||
|
this->state.data = state_.data; |
||||
|
} |
||||
|
|
||||
|
// Random generation. |
||||
|
void GenerateRandomBytes(void* dst, std::size_t size) { |
||||
|
const uintptr_t start = reinterpret_cast<uintptr_t>(dst); |
||||
|
const uintptr_t end = start + size; |
||||
|
const uintptr_t aligned_start = Common::AlignUp(start, 4); |
||||
|
const uintptr_t aligned_end = Common::AlignDown(end, 4); |
||||
|
|
||||
|
// Make sure we're aligned. |
||||
|
if (start < aligned_start) { |
||||
|
const u32 rnd = this->GenerateRandomU32(); |
||||
|
std::memcpy(dst, &rnd, aligned_start - start); |
||||
|
} |
||||
|
|
||||
|
// Write as many aligned u32s as we can. |
||||
|
{ |
||||
|
u32* cur_dst = reinterpret_cast<u32*>(aligned_start); |
||||
|
u32* const end_dst = reinterpret_cast<u32*>(aligned_end); |
||||
|
|
||||
|
while (cur_dst < end_dst) { |
||||
|
*(cur_dst++) = this->GenerateRandomU32(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Handle any leftover unaligned data. |
||||
|
if (aligned_end < end) { |
||||
|
const u32 rnd = this->GenerateRandomU32(); |
||||
|
std::memcpy(reinterpret_cast<void*>(aligned_end), &rnd, end - aligned_end); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
u32 GenerateRandomU32() { |
||||
|
// Advance state. |
||||
|
const u32 x0 = |
||||
|
(this->state.data[0] & TopBitmask) ^ this->state.data[1] ^ this->state.data[2]; |
||||
|
const u32 y0 = this->state.data[3]; |
||||
|
const u32 x1 = x0 ^ (x0 << 1); |
||||
|
const u32 y1 = y0 ^ (y0 >> 1) ^ x1; |
||||
|
|
||||
|
const u32 state0 = this->state.data[1]; |
||||
|
u32 state1 = this->state.data[2]; |
||||
|
u32 state2 = x1 ^ (y1 << 10); |
||||
|
const u32 state3 = y1; |
||||
|
|
||||
|
if ((y1 & 1) != 0) { |
||||
|
state1 ^= ParamMat1; |
||||
|
state2 ^= ParamMat2; |
||||
|
} |
||||
|
|
||||
|
this->state.data[0] = state0; |
||||
|
this->state.data[1] = state1; |
||||
|
this->state.data[2] = state2; |
||||
|
this->state.data[3] = state3; |
||||
|
|
||||
|
// Temper. |
||||
|
const u32 t1 = state0 + (state2 >> 8); |
||||
|
u32 t0 = state3 ^ t1; |
||||
|
|
||||
|
if ((t1 & 1) != 0) { |
||||
|
t0 ^= ParamTmat; |
||||
|
} |
||||
|
|
||||
|
return t0; |
||||
|
} |
||||
|
|
||||
|
u64 GenerateRandomU64() { |
||||
|
const u32 lo = this->GenerateRandomU32(); |
||||
|
const u32 hi = this->GenerateRandomU32(); |
||||
|
return (u64{hi} << 32) | u64{lo}; |
||||
|
} |
||||
|
|
||||
|
float GenerateRandomF32() { |
||||
|
// Floats have 24 bits of mantissa. |
||||
|
constexpr u32 MantissaBits = 24; |
||||
|
return static_cast<float>(GenerateRandomU24()) * (1.0f / (1U << MantissaBits)); |
||||
|
} |
||||
|
|
||||
|
double GenerateRandomF64() { |
||||
|
// Doubles have 53 bits of mantissa. |
||||
|
// The smart way to generate 53 bits of random would be to use 32 bits |
||||
|
// from the first rnd32() call, and then 21 from the second. |
||||
|
// Nintendo does not. They use (32 - 5) = 27 bits from the first rnd32() |
||||
|
// call, and (32 - 6) bits from the second. We'll do what they do, but |
||||
|
// There's not a clear reason why. |
||||
|
constexpr u32 MantissaBits = 53; |
||||
|
constexpr u32 Shift1st = (64 - MantissaBits) / 2; |
||||
|
constexpr u32 Shift2nd = (64 - MantissaBits) - Shift1st; |
||||
|
|
||||
|
const u32 first = (this->GenerateRandomU32() >> Shift1st); |
||||
|
const u32 second = (this->GenerateRandomU32() >> Shift2nd); |
||||
|
|
||||
|
return (1.0 * first * (u64{1} << (32 - Shift2nd)) + second) * |
||||
|
(1.0 / (u64{1} << MantissaBits)); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
} // namespace Common |
||||
@ -0,0 +1,279 @@ |
|||||
|
// Copyright 2021 yuzu Emulator Project |
||||
|
// Licensed under GPLv2 or any later version |
||||
|
// Refer to the license.txt file included. |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include <array> |
||||
|
#include <bit> |
||||
|
|
||||
|
#include "common/alignment.h" |
||||
|
#include "common/assert.h" |
||||
|
#include "common/bit_util.h" |
||||
|
#include "common/common_types.h" |
||||
|
#include "common/tiny_mt.h" |
||||
|
#include "core/hle/kernel/k_system_control.h" |
||||
|
|
||||
|
namespace Kernel { |
||||
|
|
||||
|
class KPageBitmap { |
||||
|
private: |
||||
|
class RandomBitGenerator { |
||||
|
private: |
||||
|
Common::TinyMT rng{}; |
||||
|
u32 entropy{}; |
||||
|
u32 bits_available{}; |
||||
|
|
||||
|
private: |
||||
|
void RefreshEntropy() { |
||||
|
entropy = rng.GenerateRandomU32(); |
||||
|
bits_available = static_cast<u32>(Common::BitSize<decltype(entropy)>()); |
||||
|
} |
||||
|
|
||||
|
bool GenerateRandomBit() { |
||||
|
if (bits_available == 0) { |
||||
|
this->RefreshEntropy(); |
||||
|
} |
||||
|
|
||||
|
const bool rnd_bit = (entropy & 1) != 0; |
||||
|
entropy >>= 1; |
||||
|
--bits_available; |
||||
|
return rnd_bit; |
||||
|
} |
||||
|
|
||||
|
public: |
||||
|
RandomBitGenerator() { |
||||
|
rng.Initialize(static_cast<u32>(KSystemControl::GenerateRandomU64())); |
||||
|
} |
||||
|
|
||||
|
std::size_t SelectRandomBit(u64 bitmap) { |
||||
|
u64 selected = 0; |
||||
|
|
||||
|
u64 cur_num_bits = Common::BitSize<decltype(bitmap)>() / 2; |
||||
|
u64 cur_mask = (1ULL << cur_num_bits) - 1; |
||||
|
|
||||
|
while (cur_num_bits) { |
||||
|
const u64 low = (bitmap >> 0) & cur_mask; |
||||
|
const u64 high = (bitmap >> cur_num_bits) & cur_mask; |
||||
|
|
||||
|
bool choose_low; |
||||
|
if (high == 0) { |
||||
|
// If only low val is set, choose low. |
||||
|
choose_low = true; |
||||
|
} else if (low == 0) { |
||||
|
// If only high val is set, choose high. |
||||
|
choose_low = false; |
||||
|
} else { |
||||
|
// If both are set, choose random. |
||||
|
choose_low = this->GenerateRandomBit(); |
||||
|
} |
||||
|
|
||||
|
// If we chose low, proceed with low. |
||||
|
if (choose_low) { |
||||
|
bitmap = low; |
||||
|
selected += 0; |
||||
|
} else { |
||||
|
bitmap = high; |
||||
|
selected += cur_num_bits; |
||||
|
} |
||||
|
|
||||
|
// Proceed. |
||||
|
cur_num_bits /= 2; |
||||
|
cur_mask >>= cur_num_bits; |
||||
|
} |
||||
|
|
||||
|
return selected; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
public: |
||||
|
static constexpr std::size_t MaxDepth = 4; |
||||
|
|
||||
|
private: |
||||
|
std::array<u64*, MaxDepth> bit_storages{}; |
||||
|
RandomBitGenerator rng{}; |
||||
|
std::size_t num_bits{}; |
||||
|
std::size_t used_depths{}; |
||||
|
|
||||
|
public: |
||||
|
KPageBitmap() = default; |
||||
|
|
||||
|
constexpr std::size_t GetNumBits() const { |
||||
|
return num_bits; |
||||
|
} |
||||
|
constexpr s32 GetHighestDepthIndex() const { |
||||
|
return static_cast<s32>(used_depths) - 1; |
||||
|
} |
||||
|
|
||||
|
u64* Initialize(u64* storage, std::size_t size) { |
||||
|
// Initially, everything is un-set. |
||||
|
num_bits = 0; |
||||
|
|
||||
|
// Calculate the needed bitmap depth. |
||||
|
used_depths = static_cast<std::size_t>(GetRequiredDepth(size)); |
||||
|
ASSERT(used_depths <= MaxDepth); |
||||
|
|
||||
|
// Set the bitmap pointers. |
||||
|
for (s32 depth = this->GetHighestDepthIndex(); depth >= 0; depth--) { |
||||
|
bit_storages[depth] = storage; |
||||
|
size = Common::AlignUp(size, Common::BitSize<u64>()) / Common::BitSize<u64>(); |
||||
|
storage += size; |
||||
|
} |
||||
|
|
||||
|
return storage; |
||||
|
} |
||||
|
|
||||
|
s64 FindFreeBlock(bool random) { |
||||
|
uintptr_t offset = 0; |
||||
|
s32 depth = 0; |
||||
|
|
||||
|
if (random) { |
||||
|
do { |
||||
|
const u64 v = bit_storages[depth][offset]; |
||||
|
if (v == 0) { |
||||
|
// If depth is bigger than zero, then a previous level indicated a block was |
||||
|
// free. |
||||
|
ASSERT(depth == 0); |
||||
|
return -1; |
||||
|
} |
||||
|
offset = offset * Common::BitSize<u64>() + rng.SelectRandomBit(v); |
||||
|
++depth; |
||||
|
} while (depth < static_cast<s32>(used_depths)); |
||||
|
} else { |
||||
|
do { |
||||
|
const u64 v = bit_storages[depth][offset]; |
||||
|
if (v == 0) { |
||||
|
// If depth is bigger than zero, then a previous level indicated a block was |
||||
|
// free. |
||||
|
ASSERT(depth == 0); |
||||
|
return -1; |
||||
|
} |
||||
|
offset = offset * Common::BitSize<u64>() + std::countr_zero(v); |
||||
|
++depth; |
||||
|
} while (depth < static_cast<s32>(used_depths)); |
||||
|
} |
||||
|
|
||||
|
return static_cast<s64>(offset); |
||||
|
} |
||||
|
|
||||
|
void SetBit(std::size_t offset) { |
||||
|
this->SetBit(this->GetHighestDepthIndex(), offset); |
||||
|
num_bits++; |
||||
|
} |
||||
|
|
||||
|
void ClearBit(std::size_t offset) { |
||||
|
this->ClearBit(this->GetHighestDepthIndex(), offset); |
||||
|
num_bits--; |
||||
|
} |
||||
|
|
||||
|
bool ClearRange(std::size_t offset, std::size_t count) { |
||||
|
s32 depth = this->GetHighestDepthIndex(); |
||||
|
u64* bits = bit_storages[depth]; |
||||
|
std::size_t bit_ind = offset / Common::BitSize<u64>(); |
||||
|
if (count < Common::BitSize<u64>()) { |
||||
|
const std::size_t shift = offset % Common::BitSize<u64>(); |
||||
|
ASSERT(shift + count <= Common::BitSize<u64>()); |
||||
|
// Check that all the bits are set. |
||||
|
const u64 mask = ((u64(1) << count) - 1) << shift; |
||||
|
u64 v = bits[bit_ind]; |
||||
|
if ((v & mask) != mask) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// Clear the bits. |
||||
|
v &= ~mask; |
||||
|
bits[bit_ind] = v; |
||||
|
if (v == 0) { |
||||
|
this->ClearBit(depth - 1, bit_ind); |
||||
|
} |
||||
|
} else { |
||||
|
ASSERT(offset % Common::BitSize<u64>() == 0); |
||||
|
ASSERT(count % Common::BitSize<u64>() == 0); |
||||
|
// Check that all the bits are set. |
||||
|
std::size_t remaining = count; |
||||
|
std::size_t i = 0; |
||||
|
do { |
||||
|
if (bits[bit_ind + i++] != ~u64(0)) { |
||||
|
return false; |
||||
|
} |
||||
|
remaining -= Common::BitSize<u64>(); |
||||
|
} while (remaining > 0); |
||||
|
|
||||
|
// Clear the bits. |
||||
|
remaining = count; |
||||
|
i = 0; |
||||
|
do { |
||||
|
bits[bit_ind + i] = 0; |
||||
|
this->ClearBit(depth - 1, bit_ind + i); |
||||
|
i++; |
||||
|
remaining -= Common::BitSize<u64>(); |
||||
|
} while (remaining > 0); |
||||
|
} |
||||
|
|
||||
|
num_bits -= count; |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
private: |
||||
|
void SetBit(s32 depth, std::size_t offset) { |
||||
|
while (depth >= 0) { |
||||
|
std::size_t ind = offset / Common::BitSize<u64>(); |
||||
|
std::size_t which = offset % Common::BitSize<u64>(); |
||||
|
const u64 mask = u64(1) << which; |
||||
|
|
||||
|
u64* bit = std::addressof(bit_storages[depth][ind]); |
||||
|
u64 v = *bit; |
||||
|
ASSERT((v & mask) == 0); |
||||
|
*bit = v | mask; |
||||
|
if (v) { |
||||
|
break; |
||||
|
} |
||||
|
offset = ind; |
||||
|
depth--; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void ClearBit(s32 depth, std::size_t offset) { |
||||
|
while (depth >= 0) { |
||||
|
std::size_t ind = offset / Common::BitSize<u64>(); |
||||
|
std::size_t which = offset % Common::BitSize<u64>(); |
||||
|
const u64 mask = u64(1) << which; |
||||
|
|
||||
|
u64* bit = std::addressof(bit_storages[depth][ind]); |
||||
|
u64 v = *bit; |
||||
|
ASSERT((v & mask) != 0); |
||||
|
v &= ~mask; |
||||
|
*bit = v; |
||||
|
if (v) { |
||||
|
break; |
||||
|
} |
||||
|
offset = ind; |
||||
|
depth--; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private: |
||||
|
static constexpr s32 GetRequiredDepth(std::size_t region_size) { |
||||
|
s32 depth = 0; |
||||
|
while (true) { |
||||
|
region_size /= Common::BitSize<u64>(); |
||||
|
depth++; |
||||
|
if (region_size == 0) { |
||||
|
return depth; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public: |
||||
|
static constexpr std::size_t CalculateManagementOverheadSize(std::size_t region_size) { |
||||
|
std::size_t overhead_bits = 0; |
||||
|
for (s32 depth = GetRequiredDepth(region_size) - 1; depth >= 0; depth--) { |
||||
|
region_size = |
||||
|
Common::AlignUp(region_size, Common::BitSize<u64>()) / Common::BitSize<u64>(); |
||||
|
overhead_bits += region_size; |
||||
|
} |
||||
|
return overhead_bits * sizeof(u64); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
} // namespace Kernel |
||||
@ -0,0 +1,193 @@ |
|||||
|
// Copyright 2020 yuzu Emulator Project |
||||
|
// Licensed under GPLv2 or any later version |
||||
|
// Refer to the license.txt file included. |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include <array> |
||||
|
#include <bit> |
||||
|
#include <vector> |
||||
|
|
||||
|
#include "common/alignment.h" |
||||
|
#include "common/assert.h" |
||||
|
#include "common/common_funcs.h" |
||||
|
#include "common/common_types.h" |
||||
|
#include "core/hle/kernel/k_page_bitmap.h" |
||||
|
#include "core/hle/kernel/memory_types.h" |
||||
|
|
||||
|
namespace Kernel { |
||||
|
|
||||
|
class KPageHeap final : NonCopyable { |
||||
|
public: |
||||
|
static constexpr s32 GetAlignedBlockIndex(std::size_t num_pages, std::size_t align_pages) { |
||||
|
const auto target_pages{std::max(num_pages, align_pages)}; |
||||
|
for (std::size_t i = 0; i < NumMemoryBlockPageShifts; i++) { |
||||
|
if (target_pages <= |
||||
|
(static_cast<std::size_t>(1) << MemoryBlockPageShifts[i]) / PageSize) { |
||||
|
return static_cast<s32>(i); |
||||
|
} |
||||
|
} |
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
static constexpr s32 GetBlockIndex(std::size_t num_pages) { |
||||
|
for (s32 i{static_cast<s32>(NumMemoryBlockPageShifts) - 1}; i >= 0; i--) { |
||||
|
if (num_pages >= (static_cast<std::size_t>(1) << MemoryBlockPageShifts[i]) / PageSize) { |
||||
|
return i; |
||||
|
} |
||||
|
} |
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
static constexpr std::size_t GetBlockSize(std::size_t index) { |
||||
|
return static_cast<std::size_t>(1) << MemoryBlockPageShifts[index]; |
||||
|
} |
||||
|
|
||||
|
static constexpr std::size_t GetBlockNumPages(std::size_t index) { |
||||
|
return GetBlockSize(index) / PageSize; |
||||
|
} |
||||
|
|
||||
|
private: |
||||
|
static constexpr std::size_t NumMemoryBlockPageShifts{7}; |
||||
|
static constexpr std::array<std::size_t, NumMemoryBlockPageShifts> MemoryBlockPageShifts{ |
||||
|
0xC, 0x10, 0x15, 0x16, 0x19, 0x1D, 0x1E, |
||||
|
}; |
||||
|
|
||||
|
class Block final : NonCopyable { |
||||
|
private: |
||||
|
KPageBitmap bitmap; |
||||
|
VAddr heap_address{}; |
||||
|
uintptr_t end_offset{}; |
||||
|
std::size_t block_shift{}; |
||||
|
std::size_t next_block_shift{}; |
||||
|
|
||||
|
public: |
||||
|
Block() = default; |
||||
|
|
||||
|
constexpr std::size_t GetShift() const { |
||||
|
return block_shift; |
||||
|
} |
||||
|
constexpr std::size_t GetNextShift() const { |
||||
|
return next_block_shift; |
||||
|
} |
||||
|
constexpr std::size_t GetSize() const { |
||||
|
return static_cast<std::size_t>(1) << GetShift(); |
||||
|
} |
||||
|
constexpr std::size_t GetNumPages() const { |
||||
|
return GetSize() / PageSize; |
||||
|
} |
||||
|
constexpr std::size_t GetNumFreeBlocks() const { |
||||
|
return bitmap.GetNumBits(); |
||||
|
} |
||||
|
constexpr std::size_t GetNumFreePages() const { |
||||
|
return GetNumFreeBlocks() * GetNumPages(); |
||||
|
} |
||||
|
|
||||
|
u64* Initialize(VAddr addr, std::size_t size, std::size_t bs, std::size_t nbs, |
||||
|
u64* bit_storage) { |
||||
|
// Set shifts |
||||
|
block_shift = bs; |
||||
|
next_block_shift = nbs; |
||||
|
|
||||
|
// Align up the address |
||||
|
VAddr end{addr + size}; |
||||
|
const auto align{(next_block_shift != 0) ? (1ULL << next_block_shift) |
||||
|
: (1ULL << block_shift)}; |
||||
|
addr = Common::AlignDown((addr), align); |
||||
|
end = Common::AlignUp((end), align); |
||||
|
|
||||
|
heap_address = addr; |
||||
|
end_offset = (end - addr) / (1ULL << block_shift); |
||||
|
return bitmap.Initialize(bit_storage, end_offset); |
||||
|
} |
||||
|
|
||||
|
VAddr PushBlock(VAddr address) { |
||||
|
// Set the bit for the free block |
||||
|
std::size_t offset{(address - heap_address) >> GetShift()}; |
||||
|
bitmap.SetBit(offset); |
||||
|
|
||||
|
// If we have a next shift, try to clear the blocks below and return the address |
||||
|
if (GetNextShift()) { |
||||
|
const auto diff{1ULL << (GetNextShift() - GetShift())}; |
||||
|
offset = Common::AlignDown(offset, diff); |
||||
|
if (bitmap.ClearRange(offset, diff)) { |
||||
|
return heap_address + (offset << GetShift()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// We couldn't coalesce, or we're already as big as possible |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
VAddr PopBlock(bool random) { |
||||
|
// Find a free block |
||||
|
const s64 soffset{bitmap.FindFreeBlock(random)}; |
||||
|
if (soffset < 0) { |
||||
|
return 0; |
||||
|
} |
||||
|
const auto offset{static_cast<std::size_t>(soffset)}; |
||||
|
|
||||
|
// Update our tracking and return it |
||||
|
bitmap.ClearBit(offset); |
||||
|
return heap_address + (offset << GetShift()); |
||||
|
} |
||||
|
|
||||
|
public: |
||||
|
static constexpr std::size_t CalculateManagementOverheadSize(std::size_t region_size, |
||||
|
std::size_t cur_block_shift, |
||||
|
std::size_t next_block_shift) { |
||||
|
const auto cur_block_size{(1ULL << cur_block_shift)}; |
||||
|
const auto next_block_size{(1ULL << next_block_shift)}; |
||||
|
const auto align{(next_block_shift != 0) ? next_block_size : cur_block_size}; |
||||
|
return KPageBitmap::CalculateManagementOverheadSize( |
||||
|
(align * 2 + Common::AlignUp(region_size, align)) / cur_block_size); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
public: |
||||
|
KPageHeap() = default; |
||||
|
|
||||
|
constexpr VAddr GetAddress() const { |
||||
|
return heap_address; |
||||
|
} |
||||
|
constexpr std::size_t GetSize() const { |
||||
|
return heap_size; |
||||
|
} |
||||
|
constexpr VAddr GetEndAddress() const { |
||||
|
return GetAddress() + GetSize(); |
||||
|
} |
||||
|
constexpr std::size_t GetPageOffset(VAddr block) const { |
||||
|
return (block - GetAddress()) / PageSize; |
||||
|
} |
||||
|
|
||||
|
void Initialize(VAddr heap_address, std::size_t heap_size, std::size_t metadata_size); |
||||
|
VAddr AllocateBlock(s32 index, bool random); |
||||
|
void Free(VAddr addr, std::size_t num_pages); |
||||
|
|
||||
|
void UpdateUsedSize() { |
||||
|
used_size = heap_size - (GetNumFreePages() * PageSize); |
||||
|
} |
||||
|
|
||||
|
static std::size_t CalculateManagementOverheadSize(std::size_t region_size); |
||||
|
|
||||
|
private: |
||||
|
constexpr std::size_t GetNumFreePages() const { |
||||
|
std::size_t num_free{}; |
||||
|
|
||||
|
for (const auto& block : blocks) { |
||||
|
num_free += block.GetNumFreePages(); |
||||
|
} |
||||
|
|
||||
|
return num_free; |
||||
|
} |
||||
|
|
||||
|
void FreeBlock(VAddr block, s32 index); |
||||
|
|
||||
|
VAddr heap_address{}; |
||||
|
std::size_t heap_size{}; |
||||
|
std::size_t used_size{}; |
||||
|
std::array<Block, NumMemoryBlockPageShifts> blocks{}; |
||||
|
std::vector<u64> metadata; |
||||
|
}; |
||||
|
|
||||
|
} // namespace Kernel |
||||
592
src/core/hle/kernel/k_page_table.cpp
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,54 @@ |
|||||
|
// Copyright 2021 yuzu Emulator Project
|
||||
|
// Licensed under GPLv2 or any later version
|
||||
|
// Refer to the license.txt file included.
|
||||
|
|
||||
|
#include "core/hle/kernel/k_spin_lock.h"
|
||||
|
|
||||
|
#if _MSC_VER
|
||||
|
#include <intrin.h>
|
||||
|
#if _M_AMD64
|
||||
|
#define __x86_64__ 1
|
||||
|
#endif
|
||||
|
#if _M_ARM64
|
||||
|
#define __aarch64__ 1
|
||||
|
#endif
|
||||
|
#else
|
||||
|
#if __x86_64__
|
||||
|
#include <xmmintrin.h>
|
||||
|
#endif
|
||||
|
#endif
|
||||
|
|
||||
|
namespace { |
||||
|
|
||||
|
void ThreadPause() { |
||||
|
#if __x86_64__
|
||||
|
_mm_pause(); |
||||
|
#elif __aarch64__ && _MSC_VER
|
||||
|
__yield(); |
||||
|
#elif __aarch64__
|
||||
|
asm("yield"); |
||||
|
#endif
|
||||
|
} |
||||
|
|
||||
|
} // namespace
|
||||
|
|
||||
|
namespace Kernel { |
||||
|
|
||||
|
void KSpinLock::Lock() { |
||||
|
while (lck.test_and_set(std::memory_order_acquire)) { |
||||
|
ThreadPause(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void KSpinLock::Unlock() { |
||||
|
lck.clear(std::memory_order_release); |
||||
|
} |
||||
|
|
||||
|
bool KSpinLock::TryLock() { |
||||
|
if (lck.test_and_set(std::memory_order_acquire)) { |
||||
|
return false; |
||||
|
} |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
} // namespace Kernel
|
||||
@ -0,0 +1,33 @@ |
|||||
|
// Copyright 2021 yuzu Emulator Project |
||||
|
// Licensed under GPLv2 or any later version |
||||
|
// Refer to the license.txt file included. |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include <atomic> |
||||
|
|
||||
|
#include "core/hle/kernel/k_scoped_lock.h" |
||||
|
|
||||
|
namespace Kernel { |
||||
|
|
||||
|
class KSpinLock { |
||||
|
public: |
||||
|
KSpinLock() = default; |
||||
|
|
||||
|
KSpinLock(const KSpinLock&) = delete; |
||||
|
KSpinLock& operator=(const KSpinLock&) = delete; |
||||
|
|
||||
|
KSpinLock(KSpinLock&&) = delete; |
||||
|
KSpinLock& operator=(KSpinLock&&) = delete; |
||||
|
|
||||
|
void Lock(); |
||||
|
void Unlock(); |
||||
|
[[nodiscard]] bool TryLock(); |
||||
|
|
||||
|
private: |
||||
|
std::atomic_flag lck = ATOMIC_FLAG_INIT; |
||||
|
}; |
||||
|
|
||||
|
using KScopedSpinLock = KScopedLock<KSpinLock>; |
||||
|
|
||||
|
} // namespace Kernel |
||||
@ -0,0 +1,19 @@ |
|||||
|
// Copyright 2021 yuzu Emulator Project |
||||
|
// Licensed under GPLv2 or any later version |
||||
|
// Refer to the license.txt file included. |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include "common/common_types.h" |
||||
|
|
||||
|
namespace Kernel { |
||||
|
|
||||
|
class KSystemControl { |
||||
|
public: |
||||
|
KSystemControl() = default; |
||||
|
|
||||
|
static u64 GenerateRandomRange(u64 min, u64 max); |
||||
|
static u64 GenerateRandomU64(); |
||||
|
}; |
||||
|
|
||||
|
} // namespace Kernel |
||||
@ -1,370 +0,0 @@ |
|||||
// Copyright 2020 yuzu Emulator Project |
|
||||
// Licensed under GPLv2 or any later version |
|
||||
// Refer to the license.txt file included. |
|
||||
|
|
||||
// This file references various implementation details from Atmosphere, an open-source firmware for |
|
||||
// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX. |
|
||||
|
|
||||
#pragma once |
|
||||
|
|
||||
#include <array> |
|
||||
#include <bit> |
|
||||
#include <vector> |
|
||||
|
|
||||
#include "common/alignment.h" |
|
||||
#include "common/assert.h" |
|
||||
#include "common/common_funcs.h" |
|
||||
#include "common/common_types.h" |
|
||||
#include "core/hle/kernel/memory/memory_types.h" |
|
||||
|
|
||||
namespace Kernel::Memory { |
|
||||
|
|
||||
class PageHeap final : NonCopyable { |
|
||||
public: |
|
||||
static constexpr s32 GetAlignedBlockIndex(std::size_t num_pages, std::size_t align_pages) { |
|
||||
const auto target_pages{std::max(num_pages, align_pages)}; |
|
||||
for (std::size_t i = 0; i < NumMemoryBlockPageShifts; i++) { |
|
||||
if (target_pages <= |
|
||||
(static_cast<std::size_t>(1) << MemoryBlockPageShifts[i]) / PageSize) { |
|
||||
return static_cast<s32>(i); |
|
||||
} |
|
||||
} |
|
||||
return -1; |
|
||||
} |
|
||||
|
|
||||
static constexpr s32 GetBlockIndex(std::size_t num_pages) { |
|
||||
for (s32 i{static_cast<s32>(NumMemoryBlockPageShifts) - 1}; i >= 0; i--) { |
|
||||
if (num_pages >= (static_cast<std::size_t>(1) << MemoryBlockPageShifts[i]) / PageSize) { |
|
||||
return i; |
|
||||
} |
|
||||
} |
|
||||
return -1; |
|
||||
} |
|
||||
|
|
||||
static constexpr std::size_t GetBlockSize(std::size_t index) { |
|
||||
return static_cast<std::size_t>(1) << MemoryBlockPageShifts[index]; |
|
||||
} |
|
||||
|
|
||||
static constexpr std::size_t GetBlockNumPages(std::size_t index) { |
|
||||
return GetBlockSize(index) / PageSize; |
|
||||
} |
|
||||
|
|
||||
private: |
|
||||
static constexpr std::size_t NumMemoryBlockPageShifts{7}; |
|
||||
static constexpr std::array<std::size_t, NumMemoryBlockPageShifts> MemoryBlockPageShifts{ |
|
||||
0xC, 0x10, 0x15, 0x16, 0x19, 0x1D, 0x1E, |
|
||||
}; |
|
||||
|
|
||||
class Block final : NonCopyable { |
|
||||
private: |
|
||||
class Bitmap final : NonCopyable { |
|
||||
public: |
|
||||
static constexpr std::size_t MaxDepth{4}; |
|
||||
|
|
||||
private: |
|
||||
std::array<u64*, MaxDepth> bit_storages{}; |
|
||||
std::size_t num_bits{}; |
|
||||
std::size_t used_depths{}; |
|
||||
|
|
||||
public: |
|
||||
constexpr Bitmap() = default; |
|
||||
|
|
||||
constexpr std::size_t GetNumBits() const { |
|
||||
return num_bits; |
|
||||
} |
|
||||
constexpr s32 GetHighestDepthIndex() const { |
|
||||
return static_cast<s32>(used_depths) - 1; |
|
||||
} |
|
||||
|
|
||||
constexpr u64* Initialize(u64* storage, std::size_t size) { |
|
||||
//* Initially, everything is un-set |
|
||||
num_bits = 0; |
|
||||
|
|
||||
// Calculate the needed bitmap depth |
|
||||
used_depths = static_cast<std::size_t>(GetRequiredDepth(size)); |
|
||||
ASSERT(used_depths <= MaxDepth); |
|
||||
|
|
||||
// Set the bitmap pointers |
|
||||
for (s32 depth{GetHighestDepthIndex()}; depth >= 0; depth--) { |
|
||||
bit_storages[depth] = storage; |
|
||||
size = Common::AlignUp(size, 64) / 64; |
|
||||
storage += size; |
|
||||
} |
|
||||
|
|
||||
return storage; |
|
||||
} |
|
||||
|
|
||||
s64 FindFreeBlock() const { |
|
||||
uintptr_t offset{}; |
|
||||
s32 depth{}; |
|
||||
|
|
||||
do { |
|
||||
const u64 v{bit_storages[depth][offset]}; |
|
||||
if (v == 0) { |
|
||||
// Non-zero depth indicates that a previous level had a free block |
|
||||
ASSERT(depth == 0); |
|
||||
return -1; |
|
||||
} |
|
||||
offset = offset * 64 + static_cast<u32>(std::countr_zero(v)); |
|
||||
++depth; |
|
||||
} while (depth < static_cast<s32>(used_depths)); |
|
||||
|
|
||||
return static_cast<s64>(offset); |
|
||||
} |
|
||||
|
|
||||
constexpr void SetBit(std::size_t offset) { |
|
||||
SetBit(GetHighestDepthIndex(), offset); |
|
||||
num_bits++; |
|
||||
} |
|
||||
|
|
||||
constexpr void ClearBit(std::size_t offset) { |
|
||||
ClearBit(GetHighestDepthIndex(), offset); |
|
||||
num_bits--; |
|
||||
} |
|
||||
|
|
||||
constexpr bool ClearRange(std::size_t offset, std::size_t count) { |
|
||||
const s32 depth{GetHighestDepthIndex()}; |
|
||||
const auto bit_ind{offset / 64}; |
|
||||
u64* bits{bit_storages[depth]}; |
|
||||
if (count < 64) { |
|
||||
const auto shift{offset % 64}; |
|
||||
ASSERT(shift + count <= 64); |
|
||||
// Check that all the bits are set |
|
||||
const u64 mask{((1ULL << count) - 1) << shift}; |
|
||||
u64 v{bits[bit_ind]}; |
|
||||
if ((v & mask) != mask) { |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
// Clear the bits |
|
||||
v &= ~mask; |
|
||||
bits[bit_ind] = v; |
|
||||
if (v == 0) { |
|
||||
ClearBit(depth - 1, bit_ind); |
|
||||
} |
|
||||
} else { |
|
||||
ASSERT(offset % 64 == 0); |
|
||||
ASSERT(count % 64 == 0); |
|
||||
// Check that all the bits are set |
|
||||
std::size_t remaining{count}; |
|
||||
std::size_t i = 0; |
|
||||
do { |
|
||||
if (bits[bit_ind + i++] != ~u64(0)) { |
|
||||
return false; |
|
||||
} |
|
||||
remaining -= 64; |
|
||||
} while (remaining > 0); |
|
||||
|
|
||||
// Clear the bits |
|
||||
remaining = count; |
|
||||
i = 0; |
|
||||
do { |
|
||||
bits[bit_ind + i] = 0; |
|
||||
ClearBit(depth - 1, bit_ind + i); |
|
||||
i++; |
|
||||
remaining -= 64; |
|
||||
} while (remaining > 0); |
|
||||
} |
|
||||
|
|
||||
num_bits -= count; |
|
||||
return true; |
|
||||
} |
|
||||
|
|
||||
private: |
|
||||
constexpr void SetBit(s32 depth, std::size_t offset) { |
|
||||
while (depth >= 0) { |
|
||||
const auto ind{offset / 64}; |
|
||||
const auto which{offset % 64}; |
|
||||
const u64 mask{1ULL << which}; |
|
||||
|
|
||||
u64* bit{std::addressof(bit_storages[depth][ind])}; |
|
||||
const u64 v{*bit}; |
|
||||
ASSERT((v & mask) == 0); |
|
||||
*bit = v | mask; |
|
||||
if (v) { |
|
||||
break; |
|
||||
} |
|
||||
offset = ind; |
|
||||
depth--; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
constexpr void ClearBit(s32 depth, std::size_t offset) { |
|
||||
while (depth >= 0) { |
|
||||
const auto ind{offset / 64}; |
|
||||
const auto which{offset % 64}; |
|
||||
const u64 mask{1ULL << which}; |
|
||||
|
|
||||
u64* bit{std::addressof(bit_storages[depth][ind])}; |
|
||||
u64 v{*bit}; |
|
||||
ASSERT((v & mask) != 0); |
|
||||
v &= ~mask; |
|
||||
*bit = v; |
|
||||
if (v) { |
|
||||
break; |
|
||||
} |
|
||||
offset = ind; |
|
||||
depth--; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private: |
|
||||
static constexpr s32 GetRequiredDepth(std::size_t region_size) { |
|
||||
s32 depth = 0; |
|
||||
while (true) { |
|
||||
region_size /= 64; |
|
||||
depth++; |
|
||||
if (region_size == 0) { |
|
||||
return depth; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public: |
|
||||
static constexpr std::size_t CalculateMetadataOverheadSize(std::size_t region_size) { |
|
||||
std::size_t overhead_bits = 0; |
|
||||
for (s32 depth{GetRequiredDepth(region_size) - 1}; depth >= 0; depth--) { |
|
||||
region_size = Common::AlignUp(region_size, 64) / 64; |
|
||||
overhead_bits += region_size; |
|
||||
} |
|
||||
return overhead_bits * sizeof(u64); |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
private: |
|
||||
Bitmap bitmap; |
|
||||
VAddr heap_address{}; |
|
||||
uintptr_t end_offset{}; |
|
||||
std::size_t block_shift{}; |
|
||||
std::size_t next_block_shift{}; |
|
||||
|
|
||||
public: |
|
||||
constexpr Block() = default; |
|
||||
|
|
||||
constexpr std::size_t GetShift() const { |
|
||||
return block_shift; |
|
||||
} |
|
||||
constexpr std::size_t GetNextShift() const { |
|
||||
return next_block_shift; |
|
||||
} |
|
||||
constexpr std::size_t GetSize() const { |
|
||||
return static_cast<std::size_t>(1) << GetShift(); |
|
||||
} |
|
||||
constexpr std::size_t GetNumPages() const { |
|
||||
return GetSize() / PageSize; |
|
||||
} |
|
||||
constexpr std::size_t GetNumFreeBlocks() const { |
|
||||
return bitmap.GetNumBits(); |
|
||||
} |
|
||||
constexpr std::size_t GetNumFreePages() const { |
|
||||
return GetNumFreeBlocks() * GetNumPages(); |
|
||||
} |
|
||||
|
|
||||
constexpr u64* Initialize(VAddr addr, std::size_t size, std::size_t bs, std::size_t nbs, |
|
||||
u64* bit_storage) { |
|
||||
// Set shifts |
|
||||
block_shift = bs; |
|
||||
next_block_shift = nbs; |
|
||||
|
|
||||
// Align up the address |
|
||||
VAddr end{addr + size}; |
|
||||
const auto align{(next_block_shift != 0) ? (1ULL << next_block_shift) |
|
||||
: (1ULL << block_shift)}; |
|
||||
addr = Common::AlignDown((addr), align); |
|
||||
end = Common::AlignUp((end), align); |
|
||||
|
|
||||
heap_address = addr; |
|
||||
end_offset = (end - addr) / (1ULL << block_shift); |
|
||||
return bitmap.Initialize(bit_storage, end_offset); |
|
||||
} |
|
||||
|
|
||||
constexpr VAddr PushBlock(VAddr address) { |
|
||||
// Set the bit for the free block |
|
||||
std::size_t offset{(address - heap_address) >> GetShift()}; |
|
||||
bitmap.SetBit(offset); |
|
||||
|
|
||||
// If we have a next shift, try to clear the blocks below and return the address |
|
||||
if (GetNextShift()) { |
|
||||
const auto diff{1ULL << (GetNextShift() - GetShift())}; |
|
||||
offset = Common::AlignDown(offset, diff); |
|
||||
if (bitmap.ClearRange(offset, diff)) { |
|
||||
return heap_address + (offset << GetShift()); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// We couldn't coalesce, or we're already as big as possible |
|
||||
return 0; |
|
||||
} |
|
||||
|
|
||||
VAddr PopBlock() { |
|
||||
// Find a free block |
|
||||
const s64 soffset{bitmap.FindFreeBlock()}; |
|
||||
if (soffset < 0) { |
|
||||
return 0; |
|
||||
} |
|
||||
const auto offset{static_cast<std::size_t>(soffset)}; |
|
||||
|
|
||||
// Update our tracking and return it |
|
||||
bitmap.ClearBit(offset); |
|
||||
return heap_address + (offset << GetShift()); |
|
||||
} |
|
||||
|
|
||||
public: |
|
||||
static constexpr std::size_t CalculateMetadataOverheadSize(std::size_t region_size, |
|
||||
std::size_t cur_block_shift, |
|
||||
std::size_t next_block_shift) { |
|
||||
const auto cur_block_size{(1ULL << cur_block_shift)}; |
|
||||
const auto next_block_size{(1ULL << next_block_shift)}; |
|
||||
const auto align{(next_block_shift != 0) ? next_block_size : cur_block_size}; |
|
||||
return Bitmap::CalculateMetadataOverheadSize( |
|
||||
(align * 2 + Common::AlignUp(region_size, align)) / cur_block_size); |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
public: |
|
||||
PageHeap() = default; |
|
||||
|
|
||||
constexpr VAddr GetAddress() const { |
|
||||
return heap_address; |
|
||||
} |
|
||||
constexpr std::size_t GetSize() const { |
|
||||
return heap_size; |
|
||||
} |
|
||||
constexpr VAddr GetEndAddress() const { |
|
||||
return GetAddress() + GetSize(); |
|
||||
} |
|
||||
constexpr std::size_t GetPageOffset(VAddr block) const { |
|
||||
return (block - GetAddress()) / PageSize; |
|
||||
} |
|
||||
|
|
||||
void Initialize(VAddr heap_address, std::size_t heap_size, std::size_t metadata_size); |
|
||||
VAddr AllocateBlock(s32 index); |
|
||||
void Free(VAddr addr, std::size_t num_pages); |
|
||||
|
|
||||
void UpdateUsedSize() { |
|
||||
used_size = heap_size - (GetNumFreePages() * PageSize); |
|
||||
} |
|
||||
|
|
||||
static std::size_t CalculateMetadataOverheadSize(std::size_t region_size); |
|
||||
|
|
||||
private: |
|
||||
constexpr std::size_t GetNumFreePages() const { |
|
||||
std::size_t num_free{}; |
|
||||
|
|
||||
for (const auto& block : blocks) { |
|
||||
num_free += block.GetNumFreePages(); |
|
||||
} |
|
||||
|
|
||||
return num_free; |
|
||||
} |
|
||||
|
|
||||
void FreeBlock(VAddr block, s32 index); |
|
||||
|
|
||||
VAddr heap_address{}; |
|
||||
std::size_t heap_size{}; |
|
||||
std::size_t used_size{}; |
|
||||
std::array<Block, NumMemoryBlockPageShifts> blocks{}; |
|
||||
std::vector<u64> metadata; |
|
||||
}; |
|
||||
|
|
||||
} // namespace Kernel::Memory |
|
||||
@ -1,13 +0,0 @@ |
|||||
// Copyright 2020 yuzu Emulator Project |
|
||||
// Licensed under GPLv2 or any later version |
|
||||
// Refer to the license.txt file included. |
|
||||
|
|
||||
#pragma once |
|
||||
|
|
||||
#include "common/common_types.h" |
|
||||
|
|
||||
namespace Kernel::Memory::SystemControl { |
|
||||
|
|
||||
u64 GenerateRandomRange(u64 min, u64 max); |
|
||||
|
|
||||
} // namespace Kernel::Memory::SystemControl |
|
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue