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)pull/15/merge
committed by
GitHub
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
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