mirror of
https://github.com/yuzu-emu/yuzu-android.git
synced 2024-12-26 05:05:40 +00:00
Merge pull request #6099 from bunnei/derive-mem
Kernel Rework: Derive memory regions from board layout.
This commit is contained in:
commit
b04877dd95
|
@ -110,6 +110,7 @@ add_library(common STATIC
|
||||||
cityhash.h
|
cityhash.h
|
||||||
common_funcs.h
|
common_funcs.h
|
||||||
common_paths.h
|
common_paths.h
|
||||||
|
common_sizes.h
|
||||||
common_types.h
|
common_types.h
|
||||||
concepts.h
|
concepts.h
|
||||||
div_ceil.h
|
div_ceil.h
|
||||||
|
|
43
src/common/common_sizes.h
Normal file
43
src/common/common_sizes.h
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
// Copyright 2021 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
|
|
||||||
|
namespace Common {
|
||||||
|
|
||||||
|
enum : u64 {
|
||||||
|
Size_1_KB = 0x400ULL,
|
||||||
|
Size_64_KB = 64ULL * Size_1_KB,
|
||||||
|
Size_128_KB = 128ULL * Size_1_KB,
|
||||||
|
Size_1_MB = 0x100000ULL,
|
||||||
|
Size_2_MB = 2ULL * Size_1_MB,
|
||||||
|
Size_4_MB = 4ULL * Size_1_MB,
|
||||||
|
Size_5_MB = 5ULL * Size_1_MB,
|
||||||
|
Size_14_MB = 14ULL * Size_1_MB,
|
||||||
|
Size_32_MB = 32ULL * Size_1_MB,
|
||||||
|
Size_33_MB = 33ULL * Size_1_MB,
|
||||||
|
Size_128_MB = 128ULL * Size_1_MB,
|
||||||
|
Size_448_MB = 448ULL * Size_1_MB,
|
||||||
|
Size_507_MB = 507ULL * Size_1_MB,
|
||||||
|
Size_562_MB = 562ULL * Size_1_MB,
|
||||||
|
Size_1554_MB = 1554ULL * Size_1_MB,
|
||||||
|
Size_2048_MB = 2048ULL * Size_1_MB,
|
||||||
|
Size_2193_MB = 2193ULL * Size_1_MB,
|
||||||
|
Size_3285_MB = 3285ULL * Size_1_MB,
|
||||||
|
Size_4916_MB = 4916ULL * Size_1_MB,
|
||||||
|
Size_1_GB = 0x40000000ULL,
|
||||||
|
Size_2_GB = 2ULL * Size_1_GB,
|
||||||
|
Size_4_GB = 4ULL * Size_1_GB,
|
||||||
|
Size_6_GB = 6ULL * Size_1_GB,
|
||||||
|
Size_8_GB = 8ULL * Size_1_GB,
|
||||||
|
Size_64_GB = 64ULL * Size_1_GB,
|
||||||
|
Size_512_GB = 512ULL * Size_1_GB,
|
||||||
|
Size_Invalid = std::numeric_limits<u64>::max(),
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Common
|
|
@ -141,6 +141,9 @@ add_library(core STATIC
|
||||||
hardware_interrupt_manager.h
|
hardware_interrupt_manager.h
|
||||||
hle/ipc.h
|
hle/ipc.h
|
||||||
hle/ipc_helpers.h
|
hle/ipc_helpers.h
|
||||||
|
hle/kernel/board/nintendo/nx/k_system_control.cpp
|
||||||
|
hle/kernel/board/nintendo/nx/k_system_control.h
|
||||||
|
hle/kernel/board/nintendo/nx/secure_monitor.h
|
||||||
hle/kernel/client_port.cpp
|
hle/kernel/client_port.cpp
|
||||||
hle/kernel/client_port.h
|
hle/kernel/client_port.h
|
||||||
hle/kernel/client_session.cpp
|
hle/kernel/client_session.cpp
|
||||||
|
@ -169,9 +172,13 @@ add_library(core STATIC
|
||||||
hle/kernel/k_memory_block.h
|
hle/kernel/k_memory_block.h
|
||||||
hle/kernel/k_memory_block_manager.cpp
|
hle/kernel/k_memory_block_manager.cpp
|
||||||
hle/kernel/k_memory_block_manager.h
|
hle/kernel/k_memory_block_manager.h
|
||||||
|
hle/kernel/k_memory_layout.cpp
|
||||||
|
hle/kernel/k_memory_layout.board.nintendo_nx.cpp
|
||||||
hle/kernel/k_memory_layout.h
|
hle/kernel/k_memory_layout.h
|
||||||
hle/kernel/k_memory_manager.cpp
|
hle/kernel/k_memory_manager.cpp
|
||||||
hle/kernel/k_memory_manager.h
|
hle/kernel/k_memory_manager.h
|
||||||
|
hle/kernel/k_memory_region.h
|
||||||
|
hle/kernel/k_memory_region_type.h
|
||||||
hle/kernel/k_page_bitmap.h
|
hle/kernel/k_page_bitmap.h
|
||||||
hle/kernel/k_page_heap.cpp
|
hle/kernel/k_page_heap.cpp
|
||||||
hle/kernel/k_page_heap.h
|
hle/kernel/k_page_heap.h
|
||||||
|
@ -196,11 +203,11 @@ add_library(core STATIC
|
||||||
hle/kernel/k_spin_lock.h
|
hle/kernel/k_spin_lock.h
|
||||||
hle/kernel/k_synchronization_object.cpp
|
hle/kernel/k_synchronization_object.cpp
|
||||||
hle/kernel/k_synchronization_object.h
|
hle/kernel/k_synchronization_object.h
|
||||||
hle/kernel/k_system_control.cpp
|
|
||||||
hle/kernel/k_system_control.h
|
hle/kernel/k_system_control.h
|
||||||
hle/kernel/k_thread.cpp
|
hle/kernel/k_thread.cpp
|
||||||
hle/kernel/k_thread.h
|
hle/kernel/k_thread.h
|
||||||
hle/kernel/k_thread_queue.h
|
hle/kernel/k_thread_queue.h
|
||||||
|
hle/kernel/k_trace.h
|
||||||
hle/kernel/k_writable_event.cpp
|
hle/kernel/k_writable_event.cpp
|
||||||
hle/kernel/k_writable_event.h
|
hle/kernel/k_writable_event.h
|
||||||
hle/kernel/kernel.cpp
|
hle/kernel/kernel.cpp
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
// Copyright 2021 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
// All architectures must define NumArchitectureDeviceRegions.
|
||||||
|
constexpr inline const auto NumArchitectureDeviceRegions = 3;
|
||||||
|
|
||||||
|
constexpr inline const auto KMemoryRegionType_Uart =
|
||||||
|
KMemoryRegionType_ArchDeviceBase.DeriveSparse(0, NumArchitectureDeviceRegions, 0);
|
||||||
|
constexpr inline const auto KMemoryRegionType_InterruptCpuInterface =
|
||||||
|
KMemoryRegionType_ArchDeviceBase.DeriveSparse(0, NumArchitectureDeviceRegions, 1)
|
||||||
|
.SetAttribute(KMemoryRegionAttr_NoUserMap);
|
||||||
|
constexpr inline const auto KMemoryRegionType_InterruptDistributor =
|
||||||
|
KMemoryRegionType_ArchDeviceBase.DeriveSparse(0, NumArchitectureDeviceRegions, 2)
|
||||||
|
.SetAttribute(KMemoryRegionAttr_NoUserMap);
|
||||||
|
static_assert(KMemoryRegionType_Uart.GetValue() == (0x1D));
|
||||||
|
static_assert(KMemoryRegionType_InterruptCpuInterface.GetValue() ==
|
||||||
|
(0x2D | KMemoryRegionAttr_NoUserMap));
|
||||||
|
static_assert(KMemoryRegionType_InterruptDistributor.GetValue() ==
|
||||||
|
(0x4D | KMemoryRegionAttr_NoUserMap));
|
|
@ -0,0 +1,52 @@
|
||||||
|
// Copyright 2021 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
// All architectures must define NumBoardDeviceRegions.
|
||||||
|
constexpr inline const auto NumBoardDeviceRegions = 6;
|
||||||
|
// UNUSED: .Derive(NumBoardDeviceRegions, 0);
|
||||||
|
constexpr inline const auto KMemoryRegionType_MemoryController =
|
||||||
|
KMemoryRegionType_BoardDeviceBase.Derive(NumBoardDeviceRegions, 1)
|
||||||
|
.SetAttribute(KMemoryRegionAttr_NoUserMap);
|
||||||
|
constexpr inline const auto KMemoryRegionType_MemoryController1 =
|
||||||
|
KMemoryRegionType_BoardDeviceBase.Derive(NumBoardDeviceRegions, 2)
|
||||||
|
.SetAttribute(KMemoryRegionAttr_NoUserMap);
|
||||||
|
constexpr inline const auto KMemoryRegionType_MemoryController0 =
|
||||||
|
KMemoryRegionType_BoardDeviceBase.Derive(NumBoardDeviceRegions, 3)
|
||||||
|
.SetAttribute(KMemoryRegionAttr_NoUserMap);
|
||||||
|
constexpr inline const auto KMemoryRegionType_PowerManagementController =
|
||||||
|
KMemoryRegionType_BoardDeviceBase.Derive(NumBoardDeviceRegions, 4).DeriveTransition();
|
||||||
|
constexpr inline const auto KMemoryRegionType_LegacyLpsDevices =
|
||||||
|
KMemoryRegionType_BoardDeviceBase.Derive(NumBoardDeviceRegions, 5);
|
||||||
|
static_assert(KMemoryRegionType_MemoryController.GetValue() ==
|
||||||
|
(0x55 | KMemoryRegionAttr_NoUserMap));
|
||||||
|
static_assert(KMemoryRegionType_MemoryController1.GetValue() ==
|
||||||
|
(0x65 | KMemoryRegionAttr_NoUserMap));
|
||||||
|
static_assert(KMemoryRegionType_MemoryController0.GetValue() ==
|
||||||
|
(0x95 | KMemoryRegionAttr_NoUserMap));
|
||||||
|
static_assert(KMemoryRegionType_PowerManagementController.GetValue() == (0x1A5));
|
||||||
|
|
||||||
|
static_assert(KMemoryRegionType_LegacyLpsDevices.GetValue() == 0xC5);
|
||||||
|
|
||||||
|
constexpr inline const auto NumLegacyLpsDevices = 7;
|
||||||
|
constexpr inline const auto KMemoryRegionType_LegacyLpsExceptionVectors =
|
||||||
|
KMemoryRegionType_LegacyLpsDevices.Derive(NumLegacyLpsDevices, 0);
|
||||||
|
constexpr inline const auto KMemoryRegionType_LegacyLpsIram =
|
||||||
|
KMemoryRegionType_LegacyLpsDevices.Derive(NumLegacyLpsDevices, 1);
|
||||||
|
constexpr inline const auto KMemoryRegionType_LegacyLpsFlowController =
|
||||||
|
KMemoryRegionType_LegacyLpsDevices.Derive(NumLegacyLpsDevices, 2);
|
||||||
|
constexpr inline const auto KMemoryRegionType_LegacyLpsPrimaryICtlr =
|
||||||
|
KMemoryRegionType_LegacyLpsDevices.Derive(NumLegacyLpsDevices, 3);
|
||||||
|
constexpr inline const auto KMemoryRegionType_LegacyLpsSemaphore =
|
||||||
|
KMemoryRegionType_LegacyLpsDevices.Derive(NumLegacyLpsDevices, 4);
|
||||||
|
constexpr inline const auto KMemoryRegionType_LegacyLpsAtomics =
|
||||||
|
KMemoryRegionType_LegacyLpsDevices.Derive(NumLegacyLpsDevices, 5);
|
||||||
|
constexpr inline const auto KMemoryRegionType_LegacyLpsClkRst =
|
||||||
|
KMemoryRegionType_LegacyLpsDevices.Derive(NumLegacyLpsDevices, 6);
|
||||||
|
static_assert(KMemoryRegionType_LegacyLpsExceptionVectors.GetValue() == 0x3C5);
|
||||||
|
static_assert(KMemoryRegionType_LegacyLpsIram.GetValue() == 0x5C5);
|
||||||
|
static_assert(KMemoryRegionType_LegacyLpsFlowController.GetValue() == 0x6C5);
|
||||||
|
static_assert(KMemoryRegionType_LegacyLpsPrimaryICtlr.GetValue() == 0x9C5);
|
||||||
|
static_assert(KMemoryRegionType_LegacyLpsSemaphore.GetValue() == 0xAC5);
|
||||||
|
static_assert(KMemoryRegionType_LegacyLpsAtomics.GetValue() == 0xCC5);
|
||||||
|
static_assert(KMemoryRegionType_LegacyLpsClkRst.GetValue() == 0x11C5);
|
164
src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp
Normal file
164
src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
// Copyright 2021 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <random>
|
||||||
|
|
||||||
|
#include "common/common_sizes.h"
|
||||||
|
#include "core/hle/kernel/board/nintendo/nx/k_system_control.h"
|
||||||
|
#include "core/hle/kernel/board/nintendo/nx/secure_monitor.h"
|
||||||
|
#include "core/hle/kernel/k_trace.h"
|
||||||
|
|
||||||
|
namespace Kernel::Board::Nintendo::Nx {
|
||||||
|
|
||||||
|
namespace impl {
|
||||||
|
|
||||||
|
constexpr const std::size_t RequiredNonSecureSystemMemorySizeVi = 0x2238 * 4 * 1024;
|
||||||
|
constexpr const std::size_t RequiredNonSecureSystemMemorySizeNvservices = 0x710 * 4 * 1024;
|
||||||
|
constexpr const std::size_t RequiredNonSecureSystemMemorySizeMisc = 0x80 * 4 * 1024;
|
||||||
|
|
||||||
|
} // namespace impl
|
||||||
|
|
||||||
|
constexpr const std::size_t RequiredNonSecureSystemMemorySize =
|
||||||
|
impl::RequiredNonSecureSystemMemorySizeVi + impl::RequiredNonSecureSystemMemorySizeNvservices +
|
||||||
|
impl::RequiredNonSecureSystemMemorySizeMisc;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
u32 GetMemoryModeForInit() {
|
||||||
|
return 0x01;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 GetMemorySizeForInit() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Smc::MemoryArrangement GetMemoryArrangeForInit() {
|
||||||
|
switch (GetMemoryModeForInit() & 0x3F) {
|
||||||
|
case 0x01:
|
||||||
|
default:
|
||||||
|
return Smc::MemoryArrangement_4GB;
|
||||||
|
case 0x02:
|
||||||
|
return Smc::MemoryArrangement_4GBForAppletDev;
|
||||||
|
case 0x03:
|
||||||
|
return Smc::MemoryArrangement_4GBForSystemDev;
|
||||||
|
case 0x11:
|
||||||
|
return Smc::MemoryArrangement_6GB;
|
||||||
|
case 0x12:
|
||||||
|
return Smc::MemoryArrangement_6GBForAppletDev;
|
||||||
|
case 0x21:
|
||||||
|
return Smc::MemoryArrangement_8GB;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
// Initialization.
|
||||||
|
size_t KSystemControl::Init::GetIntendedMemorySize() {
|
||||||
|
switch (GetMemorySizeForInit()) {
|
||||||
|
case Smc::MemorySize_4GB:
|
||||||
|
default: // All invalid modes should go to 4GB.
|
||||||
|
return Common::Size_4_GB;
|
||||||
|
case Smc::MemorySize_6GB:
|
||||||
|
return Common::Size_6_GB;
|
||||||
|
case Smc::MemorySize_8GB:
|
||||||
|
return Common::Size_8_GB;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PAddr KSystemControl::Init::GetKernelPhysicalBaseAddress(u64 base_address) {
|
||||||
|
return base_address;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KSystemControl::Init::ShouldIncreaseThreadResourceLimit() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t KSystemControl::Init::GetApplicationPoolSize() {
|
||||||
|
// Get the base pool size.
|
||||||
|
const size_t base_pool_size = []() -> size_t {
|
||||||
|
switch (GetMemoryArrangeForInit()) {
|
||||||
|
case Smc::MemoryArrangement_4GB:
|
||||||
|
default:
|
||||||
|
return Common::Size_3285_MB;
|
||||||
|
case Smc::MemoryArrangement_4GBForAppletDev:
|
||||||
|
return Common::Size_2048_MB;
|
||||||
|
case Smc::MemoryArrangement_4GBForSystemDev:
|
||||||
|
return Common::Size_3285_MB;
|
||||||
|
case Smc::MemoryArrangement_6GB:
|
||||||
|
return Common::Size_4916_MB;
|
||||||
|
case Smc::MemoryArrangement_6GBForAppletDev:
|
||||||
|
return Common::Size_3285_MB;
|
||||||
|
case Smc::MemoryArrangement_8GB:
|
||||||
|
return Common::Size_4916_MB;
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
|
||||||
|
// Return (possibly) adjusted size.
|
||||||
|
return base_pool_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t KSystemControl::Init::GetAppletPoolSize() {
|
||||||
|
// Get the base pool size.
|
||||||
|
const size_t base_pool_size = []() -> size_t {
|
||||||
|
switch (GetMemoryArrangeForInit()) {
|
||||||
|
case Smc::MemoryArrangement_4GB:
|
||||||
|
default:
|
||||||
|
return Common::Size_507_MB;
|
||||||
|
case Smc::MemoryArrangement_4GBForAppletDev:
|
||||||
|
return Common::Size_1554_MB;
|
||||||
|
case Smc::MemoryArrangement_4GBForSystemDev:
|
||||||
|
return Common::Size_448_MB;
|
||||||
|
case Smc::MemoryArrangement_6GB:
|
||||||
|
return Common::Size_562_MB;
|
||||||
|
case Smc::MemoryArrangement_6GBForAppletDev:
|
||||||
|
return Common::Size_2193_MB;
|
||||||
|
case Smc::MemoryArrangement_8GB:
|
||||||
|
return Common::Size_2193_MB;
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
|
||||||
|
// Return (possibly) adjusted size.
|
||||||
|
constexpr size_t ExtraSystemMemoryForAtmosphere = Common::Size_33_MB;
|
||||||
|
return base_pool_size - ExtraSystemMemoryForAtmosphere - KTraceBufferSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t KSystemControl::Init::GetMinimumNonSecureSystemPoolSize() {
|
||||||
|
// Verify that our minimum is at least as large as Nintendo's.
|
||||||
|
constexpr size_t MinimumSize = RequiredNonSecureSystemMemorySize;
|
||||||
|
static_assert(MinimumSize >= 0x29C8000);
|
||||||
|
|
||||||
|
return MinimumSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
template <typename F>
|
||||||
|
u64 GenerateUniformRange(u64 min, u64 max, F f) {
|
||||||
|
// Handle the case where the difference is too large to represent.
|
||||||
|
if (max == std::numeric_limits<u64>::max() && min == std::numeric_limits<u64>::min()) {
|
||||||
|
return f();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate until we get a value in range.
|
||||||
|
const u64 range_size = ((max + 1) - min);
|
||||||
|
const u64 effective_max = (std::numeric_limits<u64>::max() / range_size) * range_size;
|
||||||
|
while (true) {
|
||||||
|
if (const u64 rnd = f(); rnd < effective_max) {
|
||||||
|
return min + (rnd % range_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // Anonymous namespace
|
||||||
|
|
||||||
|
u64 KSystemControl::GenerateRandomU64() {
|
||||||
|
static std::random_device device;
|
||||||
|
static std::mt19937 gen(device());
|
||||||
|
static std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
|
||||||
|
return distribution(gen);
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 KSystemControl::GenerateRandomRange(u64 min, u64 max) {
|
||||||
|
return GenerateUniformRange(min, max, GenerateRandomU64);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Kernel::Board::Nintendo::Nx
|
28
src/core/hle/kernel/board/nintendo/nx/k_system_control.h
Normal file
28
src/core/hle/kernel/board/nintendo/nx/k_system_control.h
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
// 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::Board::Nintendo::Nx {
|
||||||
|
|
||||||
|
class KSystemControl {
|
||||||
|
public:
|
||||||
|
class Init {
|
||||||
|
public:
|
||||||
|
// Initialization.
|
||||||
|
static std::size_t GetIntendedMemorySize();
|
||||||
|
static PAddr GetKernelPhysicalBaseAddress(u64 base_address);
|
||||||
|
static bool ShouldIncreaseThreadResourceLimit();
|
||||||
|
static std::size_t GetApplicationPoolSize();
|
||||||
|
static std::size_t GetAppletPoolSize();
|
||||||
|
static std::size_t GetMinimumNonSecureSystemPoolSize();
|
||||||
|
};
|
||||||
|
|
||||||
|
static u64 GenerateRandomRange(u64 min, u64 max);
|
||||||
|
static u64 GenerateRandomU64();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Kernel::Board::Nintendo::Nx
|
26
src/core/hle/kernel/board/nintendo/nx/secure_monitor.h
Normal file
26
src/core/hle/kernel/board/nintendo/nx/secure_monitor.h
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
// 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::Board::Nintendo::Nx::Smc {
|
||||||
|
|
||||||
|
enum MemorySize {
|
||||||
|
MemorySize_4GB = 0,
|
||||||
|
MemorySize_6GB = 1,
|
||||||
|
MemorySize_8GB = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum MemoryArrangement {
|
||||||
|
MemoryArrangement_4GB = 0,
|
||||||
|
MemoryArrangement_4GBForAppletDev = 1,
|
||||||
|
MemoryArrangement_4GBForSystemDev = 2,
|
||||||
|
MemoryArrangement_6GB = 3,
|
||||||
|
MemoryArrangement_6GBForAppletDev = 4,
|
||||||
|
MemoryArrangement_8GB = 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Kernel::Board::Nintendo::Nx::Smc
|
|
@ -5,45 +5,34 @@
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
|
#include "common/common_sizes.h"
|
||||||
#include "core/hle/kernel/k_address_space_info.h"
|
#include "core/hle/kernel/k_address_space_info.h"
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
enum : u64 {
|
|
||||||
Size_1_MB = 0x100000,
|
|
||||||
Size_2_MB = 2 * Size_1_MB,
|
|
||||||
Size_128_MB = 128 * Size_1_MB,
|
|
||||||
Size_1_GB = 0x40000000,
|
|
||||||
Size_2_GB = 2 * Size_1_GB,
|
|
||||||
Size_4_GB = 4 * Size_1_GB,
|
|
||||||
Size_6_GB = 6 * Size_1_GB,
|
|
||||||
Size_64_GB = 64 * Size_1_GB,
|
|
||||||
Size_512_GB = 512 * Size_1_GB,
|
|
||||||
Invalid = std::numeric_limits<u64>::max(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
constexpr std::array<KAddressSpaceInfo, 13> AddressSpaceInfos{{
|
constexpr std::array<KAddressSpaceInfo, 13> AddressSpaceInfos{{
|
||||||
{ .bit_width = 32, .address = Size_2_MB , .size = Size_1_GB - Size_2_MB , .type = KAddressSpaceInfo::Type::MapSmall, },
|
{ .bit_width = 32, .address = Common::Size_2_MB , .size = Common::Size_1_GB - Common::Size_2_MB , .type = KAddressSpaceInfo::Type::MapSmall, },
|
||||||
{ .bit_width = 32, .address = Size_1_GB , .size = Size_4_GB - Size_1_GB , .type = KAddressSpaceInfo::Type::MapLarge, },
|
{ .bit_width = 32, .address = Common::Size_1_GB , .size = Common::Size_4_GB - Common::Size_1_GB , .type = KAddressSpaceInfo::Type::MapLarge, },
|
||||||
{ .bit_width = 32, .address = Invalid , .size = Size_1_GB , .type = KAddressSpaceInfo::Type::Heap, },
|
{ .bit_width = 32, .address = Common::Size_Invalid, .size = Common::Size_1_GB , .type = KAddressSpaceInfo::Type::Alias, },
|
||||||
{ .bit_width = 32, .address = Invalid , .size = Size_1_GB , .type = KAddressSpaceInfo::Type::Alias, },
|
{ .bit_width = 32, .address = Common::Size_Invalid, .size = Common::Size_1_GB , .type = KAddressSpaceInfo::Type::Heap, },
|
||||||
{ .bit_width = 36, .address = Size_128_MB, .size = Size_2_GB - Size_128_MB, .type = KAddressSpaceInfo::Type::MapSmall, },
|
{ .bit_width = 36, .address = Common::Size_128_MB , .size = Common::Size_2_GB - Common::Size_128_MB, .type = KAddressSpaceInfo::Type::MapSmall, },
|
||||||
{ .bit_width = 36, .address = Size_2_GB , .size = Size_64_GB - Size_2_GB , .type = KAddressSpaceInfo::Type::MapLarge, },
|
{ .bit_width = 36, .address = Common::Size_2_GB , .size = Common::Size_64_GB - Common::Size_2_GB , .type = KAddressSpaceInfo::Type::MapLarge, },
|
||||||
{ .bit_width = 36, .address = Invalid , .size = Size_6_GB , .type = KAddressSpaceInfo::Type::Heap, },
|
{ .bit_width = 36, .address = Common::Size_Invalid, .size = Common::Size_6_GB , .type = KAddressSpaceInfo::Type::Heap, },
|
||||||
{ .bit_width = 36, .address = Invalid , .size = Size_6_GB , .type = KAddressSpaceInfo::Type::Alias, },
|
{ .bit_width = 36, .address = Common::Size_Invalid, .size = Common::Size_6_GB , .type = KAddressSpaceInfo::Type::Alias, },
|
||||||
{ .bit_width = 39, .address = Size_128_MB, .size = Size_512_GB - Size_128_MB, .type = KAddressSpaceInfo::Type::Map39Bit, },
|
{ .bit_width = 39, .address = Common::Size_128_MB , .size = Common::Size_512_GB - Common::Size_128_MB, .type = KAddressSpaceInfo::Type::Map39Bit, },
|
||||||
{ .bit_width = 39, .address = Invalid , .size = Size_64_GB , .type = KAddressSpaceInfo::Type::MapSmall },
|
{ .bit_width = 39, .address = Common::Size_Invalid, .size = Common::Size_64_GB , .type = KAddressSpaceInfo::Type::MapSmall },
|
||||||
{ .bit_width = 39, .address = Invalid , .size = Size_6_GB , .type = KAddressSpaceInfo::Type::Heap, },
|
{ .bit_width = 39, .address = Common::Size_Invalid, .size = Common::Size_6_GB , .type = KAddressSpaceInfo::Type::Heap, },
|
||||||
{ .bit_width = 39, .address = Invalid , .size = Size_64_GB , .type = KAddressSpaceInfo::Type::Alias, },
|
{ .bit_width = 39, .address = Common::Size_Invalid, .size = Common::Size_64_GB , .type = KAddressSpaceInfo::Type::Alias, },
|
||||||
{ .bit_width = 39, .address = Invalid , .size = Size_2_GB , .type = KAddressSpaceInfo::Type::Stack, },
|
{ .bit_width = 39, .address = Common::Size_Invalid, .size = Common::Size_2_GB , .type = KAddressSpaceInfo::Type::Stack, },
|
||||||
}};
|
}};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
constexpr bool IsAllowedIndexForAddress(std::size_t index) {
|
constexpr bool IsAllowedIndexForAddress(std::size_t index) {
|
||||||
return index < AddressSpaceInfos.size() && AddressSpaceInfos[index].address != Invalid;
|
return index < AddressSpaceInfos.size() &&
|
||||||
|
AddressSpaceInfos[index].address != Common::Size_Invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
using IndexArray =
|
using IndexArray =
|
||||||
|
|
199
src/core/hle/kernel/k_memory_layout.board.nintendo_nx.cpp
Normal file
199
src/core/hle/kernel/k_memory_layout.board.nintendo_nx.cpp
Normal file
|
@ -0,0 +1,199 @@
|
||||||
|
// Copyright 2021 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include "common/alignment.h"
|
||||||
|
#include "core/hle/kernel/k_memory_layout.h"
|
||||||
|
#include "core/hle/kernel/k_memory_manager.h"
|
||||||
|
#include "core/hle/kernel/k_system_control.h"
|
||||||
|
#include "core/hle/kernel/k_trace.h"
|
||||||
|
|
||||||
|
namespace Kernel {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr size_t CarveoutAlignment = 0x20000;
|
||||||
|
constexpr size_t CarveoutSizeMax = (512ULL * 1024 * 1024) - CarveoutAlignment;
|
||||||
|
|
||||||
|
bool SetupPowerManagementControllerMemoryRegion(KMemoryLayout& memory_layout) {
|
||||||
|
// Above firmware 2.0.0, the PMC is not mappable.
|
||||||
|
return memory_layout.GetPhysicalMemoryRegionTree().Insert(
|
||||||
|
0x7000E000, 0x400, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap) &&
|
||||||
|
memory_layout.GetPhysicalMemoryRegionTree().Insert(
|
||||||
|
0x7000E400, 0xC00,
|
||||||
|
KMemoryRegionType_PowerManagementController | KMemoryRegionAttr_NoUserMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InsertPoolPartitionRegionIntoBothTrees(KMemoryLayout& memory_layout, size_t start, size_t size,
|
||||||
|
KMemoryRegionType phys_type,
|
||||||
|
KMemoryRegionType virt_type, u32& cur_attr) {
|
||||||
|
const u32 attr = cur_attr++;
|
||||||
|
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(start, size,
|
||||||
|
static_cast<u32>(phys_type), attr));
|
||||||
|
const KMemoryRegion* phys = memory_layout.GetPhysicalMemoryRegionTree().FindByTypeAndAttribute(
|
||||||
|
static_cast<u32>(phys_type), attr);
|
||||||
|
ASSERT(phys != nullptr);
|
||||||
|
ASSERT(phys->GetEndAddress() != 0);
|
||||||
|
ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert(phys->GetPairAddress(), size,
|
||||||
|
static_cast<u32>(virt_type), attr));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace Init {
|
||||||
|
|
||||||
|
void SetupDevicePhysicalMemoryRegions(KMemoryLayout& memory_layout) {
|
||||||
|
ASSERT(SetupPowerManagementControllerMemoryRegion(memory_layout));
|
||||||
|
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
|
||||||
|
0x70019000, 0x1000, KMemoryRegionType_MemoryController | KMemoryRegionAttr_NoUserMap));
|
||||||
|
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
|
||||||
|
0x7001C000, 0x1000, KMemoryRegionType_MemoryController0 | KMemoryRegionAttr_NoUserMap));
|
||||||
|
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
|
||||||
|
0x7001D000, 0x1000, KMemoryRegionType_MemoryController1 | KMemoryRegionAttr_NoUserMap));
|
||||||
|
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
|
||||||
|
0x50040000, 0x1000, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap));
|
||||||
|
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
|
||||||
|
0x50041000, 0x1000,
|
||||||
|
KMemoryRegionType_InterruptDistributor | KMemoryRegionAttr_ShouldKernelMap));
|
||||||
|
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
|
||||||
|
0x50042000, 0x1000,
|
||||||
|
KMemoryRegionType_InterruptCpuInterface | KMemoryRegionAttr_ShouldKernelMap));
|
||||||
|
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
|
||||||
|
0x50043000, 0x1D000, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap));
|
||||||
|
|
||||||
|
// Map IRAM unconditionally, to support debug-logging-to-iram build config.
|
||||||
|
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
|
||||||
|
0x40000000, 0x40000, KMemoryRegionType_LegacyLpsIram | KMemoryRegionAttr_ShouldKernelMap));
|
||||||
|
|
||||||
|
// Above firmware 2.0.0, prevent mapping the bpmp exception vectors or the ipatch region.
|
||||||
|
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
|
||||||
|
0x6000F000, 0x1000, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap));
|
||||||
|
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
|
||||||
|
0x6001DC00, 0x400, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetupDramPhysicalMemoryRegions(KMemoryLayout& memory_layout) {
|
||||||
|
const size_t intended_memory_size = KSystemControl::Init::GetIntendedMemorySize();
|
||||||
|
const PAddr physical_memory_base_address =
|
||||||
|
KSystemControl::Init::GetKernelPhysicalBaseAddress(DramPhysicalAddress);
|
||||||
|
|
||||||
|
// Insert blocks into the tree.
|
||||||
|
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
|
||||||
|
physical_memory_base_address, intended_memory_size, KMemoryRegionType_Dram));
|
||||||
|
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
|
||||||
|
physical_memory_base_address, ReservedEarlyDramSize, KMemoryRegionType_DramReservedEarly));
|
||||||
|
|
||||||
|
// Insert the KTrace block at the end of Dram, if KTrace is enabled.
|
||||||
|
static_assert(!IsKTraceEnabled || KTraceBufferSize > 0);
|
||||||
|
if constexpr (IsKTraceEnabled) {
|
||||||
|
const PAddr ktrace_buffer_phys_addr =
|
||||||
|
physical_memory_base_address + intended_memory_size - KTraceBufferSize;
|
||||||
|
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
|
||||||
|
ktrace_buffer_phys_addr, KTraceBufferSize, KMemoryRegionType_KernelTraceBuffer));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetupPoolPartitionMemoryRegions(KMemoryLayout& memory_layout) {
|
||||||
|
// Start by identifying the extents of the DRAM memory region.
|
||||||
|
const auto dram_extents = memory_layout.GetMainMemoryPhysicalExtents();
|
||||||
|
ASSERT(dram_extents.GetEndAddress() != 0);
|
||||||
|
|
||||||
|
// Determine the end of the pool region.
|
||||||
|
const u64 pool_end = dram_extents.GetEndAddress() - KTraceBufferSize;
|
||||||
|
|
||||||
|
// Find the start of the kernel DRAM region.
|
||||||
|
const KMemoryRegion* kernel_dram_region =
|
||||||
|
memory_layout.GetPhysicalMemoryRegionTree().FindFirstDerived(
|
||||||
|
KMemoryRegionType_DramKernelBase);
|
||||||
|
ASSERT(kernel_dram_region != nullptr);
|
||||||
|
|
||||||
|
const u64 kernel_dram_start = kernel_dram_region->GetAddress();
|
||||||
|
ASSERT(Common::IsAligned(kernel_dram_start, CarveoutAlignment));
|
||||||
|
|
||||||
|
// Find the start of the pool partitions region.
|
||||||
|
const KMemoryRegion* pool_partitions_region =
|
||||||
|
memory_layout.GetPhysicalMemoryRegionTree().FindByTypeAndAttribute(
|
||||||
|
KMemoryRegionType_DramPoolPartition, 0);
|
||||||
|
ASSERT(pool_partitions_region != nullptr);
|
||||||
|
const u64 pool_partitions_start = pool_partitions_region->GetAddress();
|
||||||
|
|
||||||
|
// Setup the pool partition layouts.
|
||||||
|
// On 5.0.0+, setup modern 4-pool-partition layout.
|
||||||
|
|
||||||
|
// Get Application and Applet pool sizes.
|
||||||
|
const size_t application_pool_size = KSystemControl::Init::GetApplicationPoolSize();
|
||||||
|
const size_t applet_pool_size = KSystemControl::Init::GetAppletPoolSize();
|
||||||
|
const size_t unsafe_system_pool_min_size =
|
||||||
|
KSystemControl::Init::GetMinimumNonSecureSystemPoolSize();
|
||||||
|
|
||||||
|
// Decide on starting addresses for our pools.
|
||||||
|
const u64 application_pool_start = pool_end - application_pool_size;
|
||||||
|
const u64 applet_pool_start = application_pool_start - applet_pool_size;
|
||||||
|
const u64 unsafe_system_pool_start = std::min(
|
||||||
|
kernel_dram_start + CarveoutSizeMax,
|
||||||
|
Common::AlignDown(applet_pool_start - unsafe_system_pool_min_size, CarveoutAlignment));
|
||||||
|
const size_t unsafe_system_pool_size = applet_pool_start - unsafe_system_pool_start;
|
||||||
|
|
||||||
|
// We want to arrange application pool depending on where the middle of dram is.
|
||||||
|
const u64 dram_midpoint = (dram_extents.GetAddress() + dram_extents.GetEndAddress()) / 2;
|
||||||
|
u32 cur_pool_attr = 0;
|
||||||
|
size_t total_overhead_size = 0;
|
||||||
|
if (dram_extents.GetEndAddress() <= dram_midpoint || dram_midpoint <= application_pool_start) {
|
||||||
|
InsertPoolPartitionRegionIntoBothTrees(
|
||||||
|
memory_layout, application_pool_start, application_pool_size,
|
||||||
|
KMemoryRegionType_DramApplicationPool, KMemoryRegionType_VirtualDramApplicationPool,
|
||||||
|
cur_pool_attr);
|
||||||
|
total_overhead_size +=
|
||||||
|
KMemoryManager::CalculateManagementOverheadSize(application_pool_size);
|
||||||
|
} else {
|
||||||
|
const size_t first_application_pool_size = dram_midpoint - application_pool_start;
|
||||||
|
const size_t second_application_pool_size =
|
||||||
|
application_pool_start + application_pool_size - dram_midpoint;
|
||||||
|
InsertPoolPartitionRegionIntoBothTrees(
|
||||||
|
memory_layout, application_pool_start, first_application_pool_size,
|
||||||
|
KMemoryRegionType_DramApplicationPool, KMemoryRegionType_VirtualDramApplicationPool,
|
||||||
|
cur_pool_attr);
|
||||||
|
InsertPoolPartitionRegionIntoBothTrees(
|
||||||
|
memory_layout, dram_midpoint, second_application_pool_size,
|
||||||
|
KMemoryRegionType_DramApplicationPool, KMemoryRegionType_VirtualDramApplicationPool,
|
||||||
|
cur_pool_attr);
|
||||||
|
total_overhead_size +=
|
||||||
|
KMemoryManager::CalculateManagementOverheadSize(first_application_pool_size);
|
||||||
|
total_overhead_size +=
|
||||||
|
KMemoryManager::CalculateManagementOverheadSize(second_application_pool_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert the applet pool.
|
||||||
|
InsertPoolPartitionRegionIntoBothTrees(memory_layout, applet_pool_start, applet_pool_size,
|
||||||
|
KMemoryRegionType_DramAppletPool,
|
||||||
|
KMemoryRegionType_VirtualDramAppletPool, cur_pool_attr);
|
||||||
|
total_overhead_size += KMemoryManager::CalculateManagementOverheadSize(applet_pool_size);
|
||||||
|
|
||||||
|
// Insert the nonsecure system pool.
|
||||||
|
InsertPoolPartitionRegionIntoBothTrees(
|
||||||
|
memory_layout, unsafe_system_pool_start, unsafe_system_pool_size,
|
||||||
|
KMemoryRegionType_DramSystemNonSecurePool, KMemoryRegionType_VirtualDramSystemNonSecurePool,
|
||||||
|
cur_pool_attr);
|
||||||
|
total_overhead_size += KMemoryManager::CalculateManagementOverheadSize(unsafe_system_pool_size);
|
||||||
|
|
||||||
|
// Insert the pool management region.
|
||||||
|
total_overhead_size += KMemoryManager::CalculateManagementOverheadSize(
|
||||||
|
(unsafe_system_pool_start - pool_partitions_start) - total_overhead_size);
|
||||||
|
const u64 pool_management_start = unsafe_system_pool_start - total_overhead_size;
|
||||||
|
const size_t pool_management_size = total_overhead_size;
|
||||||
|
u32 pool_management_attr = 0;
|
||||||
|
InsertPoolPartitionRegionIntoBothTrees(
|
||||||
|
memory_layout, pool_management_start, pool_management_size,
|
||||||
|
KMemoryRegionType_DramPoolManagement, KMemoryRegionType_VirtualDramPoolManagement,
|
||||||
|
pool_management_attr);
|
||||||
|
|
||||||
|
// Insert the system pool.
|
||||||
|
const u64 system_pool_size = pool_management_start - pool_partitions_start;
|
||||||
|
InsertPoolPartitionRegionIntoBothTrees(memory_layout, pool_partitions_start, system_pool_size,
|
||||||
|
KMemoryRegionType_DramSystemPool,
|
||||||
|
KMemoryRegionType_VirtualDramSystemPool, cur_pool_attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Init
|
||||||
|
|
||||||
|
} // namespace Kernel
|
166
src/core/hle/kernel/k_memory_layout.cpp
Normal file
166
src/core/hle/kernel/k_memory_layout.cpp
Normal file
|
@ -0,0 +1,166 @@
|
||||||
|
// Copyright 2021 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
#include "common/alignment.h"
|
||||||
|
#include "core/hle/kernel/k_memory_layout.h"
|
||||||
|
#include "core/hle/kernel/k_system_control.h"
|
||||||
|
|
||||||
|
namespace Kernel {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
KMemoryRegion* AllocateRegion(KMemoryRegionAllocator& memory_region_allocator, Args&&... args) {
|
||||||
|
return memory_region_allocator.Allocate(std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
KMemoryRegionTree::KMemoryRegionTree(KMemoryRegionAllocator& memory_region_allocator_)
|
||||||
|
: memory_region_allocator{memory_region_allocator_} {}
|
||||||
|
|
||||||
|
void KMemoryRegionTree::InsertDirectly(u64 address, u64 last_address, u32 attr, u32 type_id) {
|
||||||
|
this->insert(*AllocateRegion(memory_region_allocator, address, last_address, attr, type_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KMemoryRegionTree::Insert(u64 address, size_t size, u32 type_id, u32 new_attr, u32 old_attr) {
|
||||||
|
// Locate the memory region that contains the address.
|
||||||
|
KMemoryRegion* found = this->FindModifiable(address);
|
||||||
|
|
||||||
|
// We require that the old attr is correct.
|
||||||
|
if (found->GetAttributes() != old_attr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We further require that the region can be split from the old region.
|
||||||
|
const u64 inserted_region_end = address + size;
|
||||||
|
const u64 inserted_region_last = inserted_region_end - 1;
|
||||||
|
if (found->GetLastAddress() < inserted_region_last) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Further, we require that the type id is a valid transformation.
|
||||||
|
if (!found->CanDerive(type_id)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cache information from the region before we remove it.
|
||||||
|
const u64 old_address = found->GetAddress();
|
||||||
|
const u64 old_last = found->GetLastAddress();
|
||||||
|
const u64 old_pair = found->GetPairAddress();
|
||||||
|
const u32 old_type = found->GetType();
|
||||||
|
|
||||||
|
// Erase the existing region from the tree.
|
||||||
|
this->erase(this->iterator_to(*found));
|
||||||
|
|
||||||
|
// Insert the new region into the tree.
|
||||||
|
if (old_address == address) {
|
||||||
|
// Reuse the old object for the new region, if we can.
|
||||||
|
found->Reset(address, inserted_region_last, old_pair, new_attr, type_id);
|
||||||
|
this->insert(*found);
|
||||||
|
} else {
|
||||||
|
// If we can't re-use, adjust the old region.
|
||||||
|
found->Reset(old_address, address - 1, old_pair, old_attr, old_type);
|
||||||
|
this->insert(*found);
|
||||||
|
|
||||||
|
// Insert a new region for the split.
|
||||||
|
const u64 new_pair = (old_pair != std::numeric_limits<u64>::max())
|
||||||
|
? old_pair + (address - old_address)
|
||||||
|
: old_pair;
|
||||||
|
this->insert(*AllocateRegion(memory_region_allocator, address, inserted_region_last,
|
||||||
|
new_pair, new_attr, type_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we need to insert a region after the region, do so.
|
||||||
|
if (old_last != inserted_region_last) {
|
||||||
|
const u64 after_pair = (old_pair != std::numeric_limits<u64>::max())
|
||||||
|
? old_pair + (inserted_region_end - old_address)
|
||||||
|
: old_pair;
|
||||||
|
this->insert(*AllocateRegion(memory_region_allocator, inserted_region_end, old_last,
|
||||||
|
after_pair, old_attr, old_type));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
VAddr KMemoryRegionTree::GetRandomAlignedRegion(size_t size, size_t alignment, u32 type_id) {
|
||||||
|
// We want to find the total extents of the type id.
|
||||||
|
const auto extents = this->GetDerivedRegionExtents(static_cast<KMemoryRegionType>(type_id));
|
||||||
|
|
||||||
|
// Ensure that our alignment is correct.
|
||||||
|
ASSERT(Common::IsAligned(extents.GetAddress(), alignment));
|
||||||
|
|
||||||
|
const u64 first_address = extents.GetAddress();
|
||||||
|
const u64 last_address = extents.GetLastAddress();
|
||||||
|
|
||||||
|
const u64 first_index = first_address / alignment;
|
||||||
|
const u64 last_index = last_address / alignment;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const u64 candidate =
|
||||||
|
KSystemControl::GenerateRandomRange(first_index, last_index) * alignment;
|
||||||
|
|
||||||
|
// Ensure that the candidate doesn't overflow with the size.
|
||||||
|
if (!(candidate < candidate + size)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const u64 candidate_last = candidate + size - 1;
|
||||||
|
|
||||||
|
// Ensure that the candidate fits within the region.
|
||||||
|
if (candidate_last > last_address) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Locate the candidate region, and ensure it fits and has the correct type id.
|
||||||
|
if (const auto& candidate_region = *this->Find(candidate);
|
||||||
|
!(candidate_last <= candidate_region.GetLastAddress() &&
|
||||||
|
candidate_region.GetType() == type_id)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
KMemoryLayout::KMemoryLayout()
|
||||||
|
: virtual_tree{memory_region_allocator}, physical_tree{memory_region_allocator},
|
||||||
|
virtual_linear_tree{memory_region_allocator}, physical_linear_tree{memory_region_allocator} {}
|
||||||
|
|
||||||
|
void KMemoryLayout::InitializeLinearMemoryRegionTrees(PAddr aligned_linear_phys_start,
|
||||||
|
VAddr linear_virtual_start) {
|
||||||
|
// Set static differences.
|
||||||
|
linear_phys_to_virt_diff = linear_virtual_start - aligned_linear_phys_start;
|
||||||
|
linear_virt_to_phys_diff = aligned_linear_phys_start - linear_virtual_start;
|
||||||
|
|
||||||
|
// Initialize linear trees.
|
||||||
|
for (auto& region : GetPhysicalMemoryRegionTree()) {
|
||||||
|
if (region.HasTypeAttribute(KMemoryRegionAttr_LinearMapped)) {
|
||||||
|
GetPhysicalLinearMemoryRegionTree().InsertDirectly(
|
||||||
|
region.GetAddress(), region.GetLastAddress(), region.GetAttributes(),
|
||||||
|
region.GetType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& region : GetVirtualMemoryRegionTree()) {
|
||||||
|
if (region.IsDerivedFrom(KMemoryRegionType_Dram)) {
|
||||||
|
GetVirtualLinearMemoryRegionTree().InsertDirectly(
|
||||||
|
region.GetAddress(), region.GetLastAddress(), region.GetAttributes(),
|
||||||
|
region.GetType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t KMemoryLayout::GetResourceRegionSizeForInit() {
|
||||||
|
// Calculate resource region size based on whether we allow extra threads.
|
||||||
|
const bool use_extra_resources = KSystemControl::Init::ShouldIncreaseThreadResourceLimit();
|
||||||
|
size_t resource_region_size =
|
||||||
|
KernelResourceSize + (use_extra_resources ? KernelSlabHeapAdditionalSize : 0);
|
||||||
|
|
||||||
|
return resource_region_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Kernel
|
|
@ -1,23 +1,69 @@
|
||||||
// Copyright 2020 yuzu Emulator Project
|
// Copyright 2021 yuzu Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "common/alignment.h"
|
||||||
|
#include "common/common_sizes.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/device_memory.h"
|
#include "core/device_memory.h"
|
||||||
|
#include "core/hle/kernel/k_memory_region.h"
|
||||||
|
#include "core/hle/kernel/k_memory_region_type.h"
|
||||||
|
#include "core/hle/kernel/memory_types.h"
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
constexpr std::size_t KernelAslrAlignment = 2 * 1024 * 1024;
|
constexpr std::size_t L1BlockSize = Common::Size_1_GB;
|
||||||
|
constexpr std::size_t L2BlockSize = Common::Size_2_MB;
|
||||||
|
|
||||||
|
constexpr std::size_t GetMaximumOverheadSize(std::size_t size) {
|
||||||
|
return (Common::DivideUp(size, L1BlockSize) + Common::DivideUp(size, L2BlockSize)) * PageSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr std::size_t MainMemorySize = Common::Size_4_GB;
|
||||||
|
constexpr std::size_t MainMemorySizeMax = Common::Size_8_GB;
|
||||||
|
|
||||||
|
constexpr std::size_t ReservedEarlyDramSize = 0x60000;
|
||||||
|
constexpr std::size_t DramPhysicalAddress = 0x80000000;
|
||||||
|
|
||||||
|
constexpr std::size_t KernelAslrAlignment = Common::Size_2_MB;
|
||||||
constexpr std::size_t KernelVirtualAddressSpaceWidth = 1ULL << 39;
|
constexpr std::size_t KernelVirtualAddressSpaceWidth = 1ULL << 39;
|
||||||
constexpr std::size_t KernelPhysicalAddressSpaceWidth = 1ULL << 48;
|
constexpr std::size_t KernelPhysicalAddressSpaceWidth = 1ULL << 48;
|
||||||
|
|
||||||
constexpr std::size_t KernelVirtualAddressSpaceBase = 0ULL - KernelVirtualAddressSpaceWidth;
|
constexpr std::size_t KernelVirtualAddressSpaceBase = 0ULL - KernelVirtualAddressSpaceWidth;
|
||||||
constexpr std::size_t KernelVirtualAddressSpaceEnd =
|
constexpr std::size_t KernelVirtualAddressSpaceEnd =
|
||||||
KernelVirtualAddressSpaceBase + (KernelVirtualAddressSpaceWidth - KernelAslrAlignment);
|
KernelVirtualAddressSpaceBase + (KernelVirtualAddressSpaceWidth - KernelAslrAlignment);
|
||||||
constexpr std::size_t KernelVirtualAddressSpaceLast = KernelVirtualAddressSpaceEnd - 1;
|
constexpr std::size_t KernelVirtualAddressSpaceLast = KernelVirtualAddressSpaceEnd - 1ULL;
|
||||||
constexpr std::size_t KernelVirtualAddressSpaceSize =
|
constexpr std::size_t KernelVirtualAddressSpaceSize =
|
||||||
KernelVirtualAddressSpaceEnd - KernelVirtualAddressSpaceBase;
|
KernelVirtualAddressSpaceEnd - KernelVirtualAddressSpaceBase;
|
||||||
|
constexpr std::size_t KernelVirtualAddressCodeBase = KernelVirtualAddressSpaceBase;
|
||||||
|
constexpr std::size_t KernelVirtualAddressCodeSize = 0x62000;
|
||||||
|
constexpr std::size_t KernelVirtualAddressCodeEnd =
|
||||||
|
KernelVirtualAddressCodeBase + KernelVirtualAddressCodeSize;
|
||||||
|
|
||||||
|
constexpr std::size_t KernelPhysicalAddressSpaceBase = 0ULL;
|
||||||
|
constexpr std::size_t KernelPhysicalAddressSpaceEnd =
|
||||||
|
KernelPhysicalAddressSpaceBase + KernelPhysicalAddressSpaceWidth;
|
||||||
|
constexpr std::size_t KernelPhysicalAddressSpaceLast = KernelPhysicalAddressSpaceEnd - 1ULL;
|
||||||
|
constexpr std::size_t KernelPhysicalAddressSpaceSize =
|
||||||
|
KernelPhysicalAddressSpaceEnd - KernelPhysicalAddressSpaceBase;
|
||||||
|
constexpr std::size_t KernelPhysicalAddressCodeBase = DramPhysicalAddress + ReservedEarlyDramSize;
|
||||||
|
|
||||||
|
constexpr std::size_t KernelPageTableHeapSize = GetMaximumOverheadSize(MainMemorySizeMax);
|
||||||
|
constexpr std::size_t KernelInitialPageHeapSize = Common::Size_128_KB;
|
||||||
|
|
||||||
|
constexpr std::size_t KernelSlabHeapDataSize = Common::Size_5_MB;
|
||||||
|
constexpr std::size_t KernelSlabHeapGapsSize = Common::Size_2_MB - Common::Size_64_KB;
|
||||||
|
constexpr std::size_t KernelSlabHeapSize = KernelSlabHeapDataSize + KernelSlabHeapGapsSize;
|
||||||
|
|
||||||
|
// NOTE: This is calculated from KThread slab counts, assuming KThread size <= 0x860.
|
||||||
|
constexpr std::size_t KernelSlabHeapAdditionalSize = 0x68000ULL;
|
||||||
|
|
||||||
|
constexpr std::size_t KernelResourceSize =
|
||||||
|
KernelPageTableHeapSize + KernelInitialPageHeapSize + KernelSlabHeapSize;
|
||||||
|
|
||||||
constexpr bool IsKernelAddressKey(VAddr key) {
|
constexpr bool IsKernelAddressKey(VAddr key) {
|
||||||
return KernelVirtualAddressSpaceBase <= key && key <= KernelVirtualAddressSpaceLast;
|
return KernelVirtualAddressSpaceBase <= key && key <= KernelVirtualAddressSpaceLast;
|
||||||
|
@ -27,64 +73,327 @@ constexpr bool IsKernelAddress(VAddr address) {
|
||||||
return KernelVirtualAddressSpaceBase <= address && address < KernelVirtualAddressSpaceEnd;
|
return KernelVirtualAddressSpaceBase <= address && address < KernelVirtualAddressSpaceEnd;
|
||||||
}
|
}
|
||||||
|
|
||||||
class KMemoryRegion final {
|
|
||||||
friend class KMemoryLayout;
|
|
||||||
|
|
||||||
public:
|
|
||||||
constexpr PAddr StartAddress() const {
|
|
||||||
return start_address;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr PAddr EndAddress() const {
|
|
||||||
return end_address;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
constexpr KMemoryRegion() = default;
|
|
||||||
constexpr KMemoryRegion(PAddr start_address, PAddr end_address)
|
|
||||||
: start_address{start_address}, end_address{end_address} {}
|
|
||||||
|
|
||||||
const PAddr start_address{};
|
|
||||||
const PAddr end_address{};
|
|
||||||
};
|
|
||||||
|
|
||||||
class KMemoryLayout final {
|
class KMemoryLayout final {
|
||||||
public:
|
public:
|
||||||
constexpr const KMemoryRegion& Application() const {
|
KMemoryLayout();
|
||||||
return application;
|
|
||||||
|
KMemoryRegionTree& GetVirtualMemoryRegionTree() {
|
||||||
|
return virtual_tree;
|
||||||
|
}
|
||||||
|
const KMemoryRegionTree& GetVirtualMemoryRegionTree() const {
|
||||||
|
return virtual_tree;
|
||||||
|
}
|
||||||
|
KMemoryRegionTree& GetPhysicalMemoryRegionTree() {
|
||||||
|
return physical_tree;
|
||||||
|
}
|
||||||
|
const KMemoryRegionTree& GetPhysicalMemoryRegionTree() const {
|
||||||
|
return physical_tree;
|
||||||
|
}
|
||||||
|
KMemoryRegionTree& GetVirtualLinearMemoryRegionTree() {
|
||||||
|
return virtual_linear_tree;
|
||||||
|
}
|
||||||
|
const KMemoryRegionTree& GetVirtualLinearMemoryRegionTree() const {
|
||||||
|
return virtual_linear_tree;
|
||||||
|
}
|
||||||
|
KMemoryRegionTree& GetPhysicalLinearMemoryRegionTree() {
|
||||||
|
return physical_linear_tree;
|
||||||
|
}
|
||||||
|
const KMemoryRegionTree& GetPhysicalLinearMemoryRegionTree() const {
|
||||||
|
return physical_linear_tree;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr const KMemoryRegion& Applet() const {
|
VAddr GetLinearVirtualAddress(PAddr address) const {
|
||||||
return applet;
|
return address + linear_phys_to_virt_diff;
|
||||||
|
}
|
||||||
|
PAddr GetLinearPhysicalAddress(VAddr address) const {
|
||||||
|
return address + linear_virt_to_phys_diff;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr const KMemoryRegion& System() const {
|
const KMemoryRegion* FindVirtual(VAddr address) const {
|
||||||
return system;
|
return Find(address, GetVirtualMemoryRegionTree());
|
||||||
|
}
|
||||||
|
const KMemoryRegion* FindPhysical(PAddr address) const {
|
||||||
|
return Find(address, GetPhysicalMemoryRegionTree());
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr KMemoryLayout GetDefaultLayout() {
|
const KMemoryRegion* FindVirtualLinear(VAddr address) const {
|
||||||
constexpr std::size_t application_size{0xcd500000};
|
return Find(address, GetVirtualLinearMemoryRegionTree());
|
||||||
constexpr std::size_t applet_size{0x1fb00000};
|
}
|
||||||
constexpr PAddr application_start_address{Core::DramMemoryMap::End - application_size};
|
const KMemoryRegion* FindPhysicalLinear(PAddr address) const {
|
||||||
constexpr PAddr application_end_address{Core::DramMemoryMap::End};
|
return Find(address, GetPhysicalLinearMemoryRegionTree());
|
||||||
constexpr PAddr applet_start_address{application_start_address - applet_size};
|
}
|
||||||
constexpr PAddr applet_end_address{applet_start_address + applet_size};
|
|
||||||
constexpr PAddr system_start_address{Core::DramMemoryMap::SlabHeapEnd};
|
VAddr GetMainStackTopAddress(s32 core_id) const {
|
||||||
constexpr PAddr system_end_address{applet_start_address};
|
return GetStackTopAddress(core_id, KMemoryRegionType_KernelMiscMainStack);
|
||||||
return {application_start_address, application_end_address, applet_start_address,
|
}
|
||||||
applet_end_address, system_start_address, system_end_address};
|
VAddr GetIdleStackTopAddress(s32 core_id) const {
|
||||||
|
return GetStackTopAddress(core_id, KMemoryRegionType_KernelMiscIdleStack);
|
||||||
|
}
|
||||||
|
VAddr GetExceptionStackTopAddress(s32 core_id) const {
|
||||||
|
return GetStackTopAddress(core_id, KMemoryRegionType_KernelMiscExceptionStack);
|
||||||
|
}
|
||||||
|
|
||||||
|
VAddr GetSlabRegionAddress() const {
|
||||||
|
return Dereference(GetVirtualMemoryRegionTree().FindByType(KMemoryRegionType_KernelSlab))
|
||||||
|
.GetAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
const KMemoryRegion& GetDeviceRegion(KMemoryRegionType type) const {
|
||||||
|
return Dereference(GetPhysicalMemoryRegionTree().FindFirstDerived(type));
|
||||||
|
}
|
||||||
|
PAddr GetDevicePhysicalAddress(KMemoryRegionType type) const {
|
||||||
|
return GetDeviceRegion(type).GetAddress();
|
||||||
|
}
|
||||||
|
VAddr GetDeviceVirtualAddress(KMemoryRegionType type) const {
|
||||||
|
return GetDeviceRegion(type).GetPairAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
const KMemoryRegion& GetPoolManagementRegion() const {
|
||||||
|
return Dereference(
|
||||||
|
GetVirtualMemoryRegionTree().FindByType(KMemoryRegionType_VirtualDramPoolManagement));
|
||||||
|
}
|
||||||
|
const KMemoryRegion& GetPageTableHeapRegion() const {
|
||||||
|
return Dereference(
|
||||||
|
GetVirtualMemoryRegionTree().FindByType(KMemoryRegionType_VirtualDramKernelPtHeap));
|
||||||
|
}
|
||||||
|
const KMemoryRegion& GetKernelStackRegion() const {
|
||||||
|
return Dereference(GetVirtualMemoryRegionTree().FindByType(KMemoryRegionType_KernelStack));
|
||||||
|
}
|
||||||
|
const KMemoryRegion& GetTempRegion() const {
|
||||||
|
return Dereference(GetVirtualMemoryRegionTree().FindByType(KMemoryRegionType_KernelTemp));
|
||||||
|
}
|
||||||
|
|
||||||
|
const KMemoryRegion& GetKernelTraceBufferRegion() const {
|
||||||
|
return Dereference(GetVirtualLinearMemoryRegionTree().FindByType(
|
||||||
|
KMemoryRegionType_VirtualDramKernelTraceBuffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
const KMemoryRegion& GetVirtualLinearRegion(VAddr address) const {
|
||||||
|
return Dereference(FindVirtualLinear(address));
|
||||||
|
}
|
||||||
|
|
||||||
|
const KMemoryRegion* GetPhysicalKernelTraceBufferRegion() const {
|
||||||
|
return GetPhysicalMemoryRegionTree().FindFirstDerived(KMemoryRegionType_KernelTraceBuffer);
|
||||||
|
}
|
||||||
|
const KMemoryRegion* GetPhysicalOnMemoryBootImageRegion() const {
|
||||||
|
return GetPhysicalMemoryRegionTree().FindFirstDerived(KMemoryRegionType_OnMemoryBootImage);
|
||||||
|
}
|
||||||
|
const KMemoryRegion* GetPhysicalDTBRegion() const {
|
||||||
|
return GetPhysicalMemoryRegionTree().FindFirstDerived(KMemoryRegionType_DTB);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsHeapPhysicalAddress(const KMemoryRegion*& region, PAddr address) const {
|
||||||
|
return IsTypedAddress(region, address, GetPhysicalLinearMemoryRegionTree(),
|
||||||
|
KMemoryRegionType_DramUserPool);
|
||||||
|
}
|
||||||
|
bool IsHeapVirtualAddress(const KMemoryRegion*& region, VAddr address) const {
|
||||||
|
return IsTypedAddress(region, address, GetVirtualLinearMemoryRegionTree(),
|
||||||
|
KMemoryRegionType_VirtualDramUserPool);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsHeapPhysicalAddress(const KMemoryRegion*& region, PAddr address, size_t size) const {
|
||||||
|
return IsTypedAddress(region, address, size, GetPhysicalLinearMemoryRegionTree(),
|
||||||
|
KMemoryRegionType_DramUserPool);
|
||||||
|
}
|
||||||
|
bool IsHeapVirtualAddress(const KMemoryRegion*& region, VAddr address, size_t size) const {
|
||||||
|
return IsTypedAddress(region, address, size, GetVirtualLinearMemoryRegionTree(),
|
||||||
|
KMemoryRegionType_VirtualDramUserPool);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsLinearMappedPhysicalAddress(const KMemoryRegion*& region, PAddr address) const {
|
||||||
|
return IsTypedAddress(region, address, GetPhysicalLinearMemoryRegionTree(),
|
||||||
|
static_cast<KMemoryRegionType>(KMemoryRegionAttr_LinearMapped));
|
||||||
|
}
|
||||||
|
bool IsLinearMappedPhysicalAddress(const KMemoryRegion*& region, PAddr address,
|
||||||
|
size_t size) const {
|
||||||
|
return IsTypedAddress(region, address, size, GetPhysicalLinearMemoryRegionTree(),
|
||||||
|
static_cast<KMemoryRegionType>(KMemoryRegionAttr_LinearMapped));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<size_t, size_t> GetTotalAndKernelMemorySizes() const {
|
||||||
|
size_t total_size = 0, kernel_size = 0;
|
||||||
|
for (const auto& region : GetPhysicalMemoryRegionTree()) {
|
||||||
|
if (region.IsDerivedFrom(KMemoryRegionType_Dram)) {
|
||||||
|
total_size += region.GetSize();
|
||||||
|
if (!region.IsDerivedFrom(KMemoryRegionType_DramUserPool)) {
|
||||||
|
kernel_size += region.GetSize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return std::make_pair(total_size, kernel_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitializeLinearMemoryRegionTrees(PAddr aligned_linear_phys_start,
|
||||||
|
VAddr linear_virtual_start);
|
||||||
|
static size_t GetResourceRegionSizeForInit();
|
||||||
|
|
||||||
|
auto GetKernelRegionExtents() const {
|
||||||
|
return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_Kernel);
|
||||||
|
}
|
||||||
|
auto GetKernelCodeRegionExtents() const {
|
||||||
|
return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_KernelCode);
|
||||||
|
}
|
||||||
|
auto GetKernelStackRegionExtents() const {
|
||||||
|
return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_KernelStack);
|
||||||
|
}
|
||||||
|
auto GetKernelMiscRegionExtents() const {
|
||||||
|
return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_KernelMisc);
|
||||||
|
}
|
||||||
|
auto GetKernelSlabRegionExtents() const {
|
||||||
|
return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_KernelSlab);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto GetLinearRegionPhysicalExtents() const {
|
||||||
|
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
|
||||||
|
KMemoryRegionAttr_LinearMapped);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto GetLinearRegionVirtualExtents() const {
|
||||||
|
const auto physical = GetLinearRegionPhysicalExtents();
|
||||||
|
return KMemoryRegion(GetLinearVirtualAddress(physical.GetAddress()),
|
||||||
|
GetLinearVirtualAddress(physical.GetLastAddress()), 0,
|
||||||
|
KMemoryRegionType_None);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto GetMainMemoryPhysicalExtents() const {
|
||||||
|
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_Dram);
|
||||||
|
}
|
||||||
|
auto GetCarveoutRegionExtents() const {
|
||||||
|
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
|
||||||
|
KMemoryRegionAttr_CarveoutProtected);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto GetKernelRegionPhysicalExtents() const {
|
||||||
|
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
|
||||||
|
KMemoryRegionType_DramKernelBase);
|
||||||
|
}
|
||||||
|
auto GetKernelCodeRegionPhysicalExtents() const {
|
||||||
|
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
|
||||||
|
KMemoryRegionType_DramKernelCode);
|
||||||
|
}
|
||||||
|
auto GetKernelSlabRegionPhysicalExtents() const {
|
||||||
|
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
|
||||||
|
KMemoryRegionType_DramKernelSlab);
|
||||||
|
}
|
||||||
|
auto GetKernelPageTableHeapRegionPhysicalExtents() const {
|
||||||
|
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
|
||||||
|
KMemoryRegionType_DramKernelPtHeap);
|
||||||
|
}
|
||||||
|
auto GetKernelInitPageTableRegionPhysicalExtents() const {
|
||||||
|
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
|
||||||
|
KMemoryRegionType_DramKernelInitPt);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto GetKernelPoolManagementRegionPhysicalExtents() const {
|
||||||
|
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
|
||||||
|
KMemoryRegionType_DramPoolManagement);
|
||||||
|
}
|
||||||
|
auto GetKernelPoolPartitionRegionPhysicalExtents() const {
|
||||||
|
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
|
||||||
|
KMemoryRegionType_DramPoolPartition);
|
||||||
|
}
|
||||||
|
auto GetKernelSystemPoolRegionPhysicalExtents() const {
|
||||||
|
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
|
||||||
|
KMemoryRegionType_DramSystemPool);
|
||||||
|
}
|
||||||
|
auto GetKernelSystemNonSecurePoolRegionPhysicalExtents() const {
|
||||||
|
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
|
||||||
|
KMemoryRegionType_DramSystemNonSecurePool);
|
||||||
|
}
|
||||||
|
auto GetKernelAppletPoolRegionPhysicalExtents() const {
|
||||||
|
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
|
||||||
|
KMemoryRegionType_DramAppletPool);
|
||||||
|
}
|
||||||
|
auto GetKernelApplicationPoolRegionPhysicalExtents() const {
|
||||||
|
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
|
||||||
|
KMemoryRegionType_DramApplicationPool);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto GetKernelTraceBufferRegionPhysicalExtents() const {
|
||||||
|
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
|
||||||
|
KMemoryRegionType_KernelTraceBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
constexpr KMemoryLayout(PAddr application_start_address, std::size_t application_size,
|
template <typename AddressType>
|
||||||
PAddr applet_start_address, std::size_t applet_size,
|
static bool IsTypedAddress(const KMemoryRegion*& region, AddressType address,
|
||||||
PAddr system_start_address, std::size_t system_size)
|
const KMemoryRegionTree& tree, KMemoryRegionType type) {
|
||||||
: application{application_start_address, application_size},
|
// Check if the cached region already contains the address.
|
||||||
applet{applet_start_address, applet_size}, system{system_start_address, system_size} {}
|
if (region != nullptr && region->Contains(address)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
const KMemoryRegion application;
|
// Find the containing region, and update the cache.
|
||||||
const KMemoryRegion applet;
|
if (const KMemoryRegion* found = tree.Find(address);
|
||||||
const KMemoryRegion system;
|
found != nullptr && found->IsDerivedFrom(type)) {
|
||||||
|
region = found;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename AddressType>
|
||||||
|
static bool IsTypedAddress(const KMemoryRegion*& region, AddressType address, size_t size,
|
||||||
|
const KMemoryRegionTree& tree, KMemoryRegionType type) {
|
||||||
|
// Get the end of the checked region.
|
||||||
|
const u64 last_address = address + size - 1;
|
||||||
|
|
||||||
|
// Walk the tree to verify the region is correct.
|
||||||
|
const KMemoryRegion* cur =
|
||||||
|
(region != nullptr && region->Contains(address)) ? region : tree.Find(address);
|
||||||
|
while (cur != nullptr && cur->IsDerivedFrom(type)) {
|
||||||
|
if (last_address <= cur->GetLastAddress()) {
|
||||||
|
region = cur;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
cur = cur->GetNext();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename AddressType>
|
||||||
|
static const KMemoryRegion* Find(AddressType address, const KMemoryRegionTree& tree) {
|
||||||
|
return tree.Find(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
static KMemoryRegion& Dereference(KMemoryRegion* region) {
|
||||||
|
ASSERT(region != nullptr);
|
||||||
|
return *region;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const KMemoryRegion& Dereference(const KMemoryRegion* region) {
|
||||||
|
ASSERT(region != nullptr);
|
||||||
|
return *region;
|
||||||
|
}
|
||||||
|
|
||||||
|
VAddr GetStackTopAddress(s32 core_id, KMemoryRegionType type) const {
|
||||||
|
const auto& region = Dereference(
|
||||||
|
GetVirtualMemoryRegionTree().FindByTypeAndAttribute(type, static_cast<u32>(core_id)));
|
||||||
|
ASSERT(region.GetEndAddress() != 0);
|
||||||
|
return region.GetEndAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
u64 linear_phys_to_virt_diff{};
|
||||||
|
u64 linear_virt_to_phys_diff{};
|
||||||
|
KMemoryRegionAllocator memory_region_allocator;
|
||||||
|
KMemoryRegionTree virtual_tree;
|
||||||
|
KMemoryRegionTree physical_tree;
|
||||||
|
KMemoryRegionTree virtual_linear_tree;
|
||||||
|
KMemoryRegionTree physical_linear_tree;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
namespace Init {
|
||||||
|
|
||||||
|
// These should be generic, regardless of board.
|
||||||
|
void SetupPoolPartitionMemoryRegions(KMemoryLayout& memory_layout);
|
||||||
|
|
||||||
|
// These may be implemented in a board-specific manner.
|
||||||
|
void SetupDevicePhysicalMemoryRegions(KMemoryLayout& memory_layout);
|
||||||
|
void SetupDramPhysicalMemoryRegions(KMemoryLayout& memory_layout);
|
||||||
|
|
||||||
|
} // namespace Init
|
||||||
|
|
||||||
} // namespace Kernel
|
} // namespace Kernel
|
||||||
|
|
|
@ -173,4 +173,16 @@ ResultCode KMemoryManager::Free(KPageLinkedList& page_list, std::size_t num_page
|
||||||
return RESULT_SUCCESS;
|
return RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::size_t KMemoryManager::Impl::CalculateManagementOverheadSize(std::size_t region_size) {
|
||||||
|
const std::size_t ref_count_size = (region_size / PageSize) * sizeof(u16);
|
||||||
|
const std::size_t optimize_map_size =
|
||||||
|
(Common::AlignUp((region_size / PageSize), Common::BitSize<u64>()) /
|
||||||
|
Common::BitSize<u64>()) *
|
||||||
|
sizeof(u64);
|
||||||
|
const std::size_t manager_meta_size =
|
||||||
|
Common::AlignUp(optimize_map_size + ref_count_size, PageSize);
|
||||||
|
const std::size_t page_heap_size = KPageHeap::CalculateManagementOverheadSize(region_size);
|
||||||
|
return manager_meta_size + page_heap_size;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Kernel
|
} // namespace Kernel
|
||||||
|
|
|
@ -29,6 +29,10 @@ public:
|
||||||
|
|
||||||
Shift = 4,
|
Shift = 4,
|
||||||
Mask = (0xF << Shift),
|
Mask = (0xF << Shift),
|
||||||
|
|
||||||
|
// Aliases.
|
||||||
|
Unsafe = Application,
|
||||||
|
Secure = System,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class Direction : u32 {
|
enum class Direction : u32 {
|
||||||
|
@ -56,6 +60,10 @@ public:
|
||||||
static constexpr std::size_t MaxManagerCount = 10;
|
static constexpr std::size_t MaxManagerCount = 10;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
static std::size_t CalculateManagementOverheadSize(std::size_t region_size) {
|
||||||
|
return Impl::CalculateManagementOverheadSize(region_size);
|
||||||
|
}
|
||||||
|
|
||||||
static constexpr u32 EncodeOption(Pool pool, Direction dir) {
|
static constexpr u32 EncodeOption(Pool pool, Direction dir) {
|
||||||
return (static_cast<u32>(pool) << static_cast<u32>(Pool::Shift)) |
|
return (static_cast<u32>(pool) << static_cast<u32>(Pool::Shift)) |
|
||||||
(static_cast<u32>(dir) << static_cast<u32>(Direction::Shift));
|
(static_cast<u32>(dir) << static_cast<u32>(Direction::Shift));
|
||||||
|
@ -85,6 +93,16 @@ private:
|
||||||
KPageHeap heap;
|
KPageHeap heap;
|
||||||
Pool pool{};
|
Pool pool{};
|
||||||
|
|
||||||
|
public:
|
||||||
|
static std::size_t CalculateManagementOverheadSize(std::size_t region_size);
|
||||||
|
|
||||||
|
static constexpr std::size_t CalculateOptimizedProcessOverheadSize(
|
||||||
|
std::size_t region_size) {
|
||||||
|
return (Common::AlignUp((region_size / PageSize), Common::BitSize<u64>()) /
|
||||||
|
Common::BitSize<u64>()) *
|
||||||
|
sizeof(u64);
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Impl() = default;
|
Impl() = default;
|
||||||
|
|
||||||
|
|
350
src/core/hle/kernel/k_memory_region.h
Normal file
350
src/core/hle/kernel/k_memory_region.h
Normal file
|
@ -0,0 +1,350 @@
|
||||||
|
// Copyright 2021 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "common/intrusive_red_black_tree.h"
|
||||||
|
#include "core/hle/kernel/k_memory_region_type.h"
|
||||||
|
|
||||||
|
namespace Kernel {
|
||||||
|
|
||||||
|
class KMemoryRegionAllocator;
|
||||||
|
|
||||||
|
class KMemoryRegion final : public Common::IntrusiveRedBlackTreeBaseNode<KMemoryRegion>,
|
||||||
|
NonCopyable {
|
||||||
|
friend class KMemoryRegionTree;
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr KMemoryRegion() = default;
|
||||||
|
constexpr KMemoryRegion(u64 address_, u64 last_address_)
|
||||||
|
: address{address_}, last_address{last_address_} {}
|
||||||
|
constexpr KMemoryRegion(u64 address_, u64 last_address_, u64 pair_address_, u32 attributes_,
|
||||||
|
u32 type_id_)
|
||||||
|
: address(address_), last_address(last_address_), pair_address(pair_address_),
|
||||||
|
attributes(attributes_), type_id(type_id_) {}
|
||||||
|
constexpr KMemoryRegion(u64 address_, u64 last_address_, u32 attributes_, u32 type_id_)
|
||||||
|
: KMemoryRegion(address_, last_address_, std::numeric_limits<u64>::max(), attributes_,
|
||||||
|
type_id_) {}
|
||||||
|
|
||||||
|
static constexpr int Compare(const KMemoryRegion& lhs, const KMemoryRegion& rhs) {
|
||||||
|
if (lhs.GetAddress() < rhs.GetAddress()) {
|
||||||
|
return -1;
|
||||||
|
} else if (lhs.GetAddress() <= rhs.GetLastAddress()) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
constexpr void Reset(u64 a, u64 la, u64 p, u32 r, u32 t) {
|
||||||
|
address = a;
|
||||||
|
pair_address = p;
|
||||||
|
last_address = la;
|
||||||
|
attributes = r;
|
||||||
|
type_id = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr u64 GetAddress() const {
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr u64 GetPairAddress() const {
|
||||||
|
return pair_address;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr u64 GetLastAddress() const {
|
||||||
|
return last_address;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr u64 GetEndAddress() const {
|
||||||
|
return this->GetLastAddress() + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr size_t GetSize() const {
|
||||||
|
return this->GetEndAddress() - this->GetAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr u32 GetAttributes() const {
|
||||||
|
return attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr u32 GetType() const {
|
||||||
|
return type_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void SetType(u32 type) {
|
||||||
|
ASSERT(this->CanDerive(type));
|
||||||
|
type_id = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool Contains(u64 address) const {
|
||||||
|
ASSERT(this->GetEndAddress() != 0);
|
||||||
|
return this->GetAddress() <= address && address <= this->GetLastAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool IsDerivedFrom(u32 type) const {
|
||||||
|
return (this->GetType() | type) == this->GetType();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool HasTypeAttribute(u32 attr) const {
|
||||||
|
return (this->GetType() | attr) == this->GetType();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool CanDerive(u32 type) const {
|
||||||
|
return (this->GetType() | type) == type;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void SetPairAddress(u64 a) {
|
||||||
|
pair_address = a;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void SetTypeAttribute(u32 attr) {
|
||||||
|
type_id |= attr;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
u64 address{};
|
||||||
|
u64 last_address{};
|
||||||
|
u64 pair_address{};
|
||||||
|
u32 attributes{};
|
||||||
|
u32 type_id{};
|
||||||
|
};
|
||||||
|
|
||||||
|
class KMemoryRegionTree final : NonCopyable {
|
||||||
|
public:
|
||||||
|
struct DerivedRegionExtents {
|
||||||
|
const KMemoryRegion* first_region{};
|
||||||
|
const KMemoryRegion* last_region{};
|
||||||
|
|
||||||
|
constexpr DerivedRegionExtents() = default;
|
||||||
|
|
||||||
|
constexpr u64 GetAddress() const {
|
||||||
|
return this->first_region->GetAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr u64 GetLastAddress() const {
|
||||||
|
return this->last_region->GetLastAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr u64 GetEndAddress() const {
|
||||||
|
return this->GetLastAddress() + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr size_t GetSize() const {
|
||||||
|
return this->GetEndAddress() - this->GetAddress();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
using TreeType =
|
||||||
|
Common::IntrusiveRedBlackTreeBaseTraits<KMemoryRegion>::TreeType<KMemoryRegion>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using value_type = TreeType::value_type;
|
||||||
|
using size_type = TreeType::size_type;
|
||||||
|
using difference_type = TreeType::difference_type;
|
||||||
|
using pointer = TreeType::pointer;
|
||||||
|
using const_pointer = TreeType::const_pointer;
|
||||||
|
using reference = TreeType::reference;
|
||||||
|
using const_reference = TreeType::const_reference;
|
||||||
|
using iterator = TreeType::iterator;
|
||||||
|
using const_iterator = TreeType::const_iterator;
|
||||||
|
|
||||||
|
private:
|
||||||
|
TreeType m_tree{};
|
||||||
|
KMemoryRegionAllocator& memory_region_allocator;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit KMemoryRegionTree(KMemoryRegionAllocator& memory_region_allocator_);
|
||||||
|
|
||||||
|
public:
|
||||||
|
KMemoryRegion* FindModifiable(u64 address) {
|
||||||
|
if (auto it = this->find(KMemoryRegion(address, address, 0, 0)); it != this->end()) {
|
||||||
|
return std::addressof(*it);
|
||||||
|
} else {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const KMemoryRegion* Find(u64 address) const {
|
||||||
|
if (auto it = this->find(KMemoryRegion(address, address, 0, 0)); it != this->cend()) {
|
||||||
|
return std::addressof(*it);
|
||||||
|
} else {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const KMemoryRegion* FindByType(KMemoryRegionType type_id) const {
|
||||||
|
for (auto it = this->cbegin(); it != this->cend(); ++it) {
|
||||||
|
if (it->GetType() == static_cast<u32>(type_id)) {
|
||||||
|
return std::addressof(*it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const KMemoryRegion* FindByTypeAndAttribute(u32 type_id, u32 attr) const {
|
||||||
|
for (auto it = this->cbegin(); it != this->cend(); ++it) {
|
||||||
|
if (it->GetType() == type_id && it->GetAttributes() == attr) {
|
||||||
|
return std::addressof(*it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const KMemoryRegion* FindFirstDerived(KMemoryRegionType type_id) const {
|
||||||
|
for (auto it = this->cbegin(); it != this->cend(); it++) {
|
||||||
|
if (it->IsDerivedFrom(type_id)) {
|
||||||
|
return std::addressof(*it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const KMemoryRegion* FindLastDerived(KMemoryRegionType type_id) const {
|
||||||
|
const KMemoryRegion* region = nullptr;
|
||||||
|
for (auto it = this->begin(); it != this->end(); it++) {
|
||||||
|
if (it->IsDerivedFrom(type_id)) {
|
||||||
|
region = std::addressof(*it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return region;
|
||||||
|
}
|
||||||
|
|
||||||
|
DerivedRegionExtents GetDerivedRegionExtents(KMemoryRegionType type_id) const {
|
||||||
|
DerivedRegionExtents extents;
|
||||||
|
|
||||||
|
ASSERT(extents.first_region == nullptr);
|
||||||
|
ASSERT(extents.last_region == nullptr);
|
||||||
|
|
||||||
|
for (auto it = this->cbegin(); it != this->cend(); it++) {
|
||||||
|
if (it->IsDerivedFrom(type_id)) {
|
||||||
|
if (extents.first_region == nullptr) {
|
||||||
|
extents.first_region = std::addressof(*it);
|
||||||
|
}
|
||||||
|
extents.last_region = std::addressof(*it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT(extents.first_region != nullptr);
|
||||||
|
ASSERT(extents.last_region != nullptr);
|
||||||
|
|
||||||
|
return extents;
|
||||||
|
}
|
||||||
|
|
||||||
|
DerivedRegionExtents GetDerivedRegionExtents(u32 type_id) const {
|
||||||
|
return GetDerivedRegionExtents(static_cast<KMemoryRegionType>(type_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
void InsertDirectly(u64 address, u64 last_address, u32 attr = 0, u32 type_id = 0);
|
||||||
|
bool Insert(u64 address, size_t size, u32 type_id, u32 new_attr = 0, u32 old_attr = 0);
|
||||||
|
|
||||||
|
VAddr GetRandomAlignedRegion(size_t size, size_t alignment, u32 type_id);
|
||||||
|
|
||||||
|
VAddr GetRandomAlignedRegionWithGuard(size_t size, size_t alignment, u32 type_id,
|
||||||
|
size_t guard_size) {
|
||||||
|
return this->GetRandomAlignedRegion(size + 2 * guard_size, alignment, type_id) + guard_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Iterator accessors.
|
||||||
|
iterator begin() {
|
||||||
|
return m_tree.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator begin() const {
|
||||||
|
return m_tree.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator end() {
|
||||||
|
return m_tree.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator end() const {
|
||||||
|
return m_tree.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator cbegin() const {
|
||||||
|
return this->begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator cend() const {
|
||||||
|
return this->end();
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator iterator_to(reference ref) {
|
||||||
|
return m_tree.iterator_to(ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator iterator_to(const_reference ref) const {
|
||||||
|
return m_tree.iterator_to(ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Content management.
|
||||||
|
bool empty() const {
|
||||||
|
return m_tree.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
reference back() {
|
||||||
|
return m_tree.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
const_reference back() const {
|
||||||
|
return m_tree.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
reference front() {
|
||||||
|
return m_tree.front();
|
||||||
|
}
|
||||||
|
|
||||||
|
const_reference front() const {
|
||||||
|
return m_tree.front();
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator insert(reference ref) {
|
||||||
|
return m_tree.insert(ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator erase(iterator it) {
|
||||||
|
return m_tree.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator find(const_reference ref) const {
|
||||||
|
return m_tree.find(ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator nfind(const_reference ref) const {
|
||||||
|
return m_tree.nfind(ref);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class KMemoryRegionAllocator final : NonCopyable {
|
||||||
|
public:
|
||||||
|
static constexpr size_t MaxMemoryRegions = 200;
|
||||||
|
|
||||||
|
constexpr KMemoryRegionAllocator() = default;
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
KMemoryRegion* Allocate(Args&&... args) {
|
||||||
|
// Ensure we stay within the bounds of our heap.
|
||||||
|
ASSERT(this->num_regions < MaxMemoryRegions);
|
||||||
|
|
||||||
|
// Create the new region.
|
||||||
|
KMemoryRegion* region = std::addressof(this->region_heap[this->num_regions++]);
|
||||||
|
new (region) KMemoryRegion(std::forward<Args>(args)...);
|
||||||
|
|
||||||
|
return region;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::array<KMemoryRegion, MaxMemoryRegions> region_heap{};
|
||||||
|
size_t num_regions{};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Kernel
|
338
src/core/hle/kernel/k_memory_region_type.h
Normal file
338
src/core/hle/kernel/k_memory_region_type.h
Normal file
|
@ -0,0 +1,338 @@
|
||||||
|
// Copyright 2021 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/bit_util.h"
|
||||||
|
#include "common/common_funcs.h"
|
||||||
|
#include "common/common_types.h"
|
||||||
|
|
||||||
|
#define ARCH_ARM64
|
||||||
|
#define BOARD_NINTENDO_NX
|
||||||
|
|
||||||
|
namespace Kernel {
|
||||||
|
|
||||||
|
enum KMemoryRegionType : u32 {
|
||||||
|
KMemoryRegionAttr_CarveoutProtected = 0x04000000,
|
||||||
|
KMemoryRegionAttr_DidKernelMap = 0x08000000,
|
||||||
|
KMemoryRegionAttr_ShouldKernelMap = 0x10000000,
|
||||||
|
KMemoryRegionAttr_UserReadOnly = 0x20000000,
|
||||||
|
KMemoryRegionAttr_NoUserMap = 0x40000000,
|
||||||
|
KMemoryRegionAttr_LinearMapped = 0x80000000,
|
||||||
|
};
|
||||||
|
DECLARE_ENUM_FLAG_OPERATORS(KMemoryRegionType);
|
||||||
|
|
||||||
|
namespace impl {
|
||||||
|
|
||||||
|
constexpr size_t BitsForDeriveSparse(size_t n) {
|
||||||
|
return n + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr size_t BitsForDeriveDense(size_t n) {
|
||||||
|
size_t low = 0, high = 1;
|
||||||
|
for (size_t i = 0; i < n - 1; ++i) {
|
||||||
|
if ((++low) == high) {
|
||||||
|
++high;
|
||||||
|
low = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return high + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
class KMemoryRegionTypeValue {
|
||||||
|
public:
|
||||||
|
using ValueType = std::underlying_type_t<KMemoryRegionType>;
|
||||||
|
|
||||||
|
constexpr KMemoryRegionTypeValue() = default;
|
||||||
|
|
||||||
|
constexpr operator KMemoryRegionType() const {
|
||||||
|
return static_cast<KMemoryRegionType>(m_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr ValueType GetValue() const {
|
||||||
|
return m_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const KMemoryRegionTypeValue& Finalize() {
|
||||||
|
m_finalized = true;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const KMemoryRegionTypeValue& SetSparseOnly() {
|
||||||
|
m_sparse_only = true;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const KMemoryRegionTypeValue& SetDenseOnly() {
|
||||||
|
m_dense_only = true;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr KMemoryRegionTypeValue& SetAttribute(u32 attr) {
|
||||||
|
m_value |= attr;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr KMemoryRegionTypeValue DeriveInitial(
|
||||||
|
size_t i, size_t next = Common::BitSize<ValueType>()) const {
|
||||||
|
KMemoryRegionTypeValue new_type = *this;
|
||||||
|
new_type.m_value = (ValueType{1} << i);
|
||||||
|
new_type.m_next_bit = next;
|
||||||
|
return new_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr KMemoryRegionTypeValue DeriveAttribute(u32 attr) const {
|
||||||
|
KMemoryRegionTypeValue new_type = *this;
|
||||||
|
new_type.m_value |= attr;
|
||||||
|
return new_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr KMemoryRegionTypeValue DeriveTransition(size_t ofs = 0, size_t adv = 1) const {
|
||||||
|
KMemoryRegionTypeValue new_type = *this;
|
||||||
|
new_type.m_value |= (ValueType{1} << (m_next_bit + ofs));
|
||||||
|
new_type.m_next_bit += adv;
|
||||||
|
return new_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr KMemoryRegionTypeValue DeriveSparse(size_t ofs, size_t n, size_t i) const {
|
||||||
|
KMemoryRegionTypeValue new_type = *this;
|
||||||
|
new_type.m_value |= (ValueType{1} << (m_next_bit + ofs));
|
||||||
|
new_type.m_value |= (ValueType{1} << (m_next_bit + ofs + 1 + i));
|
||||||
|
new_type.m_next_bit += ofs + n + 1;
|
||||||
|
return new_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr KMemoryRegionTypeValue Derive(size_t n, size_t i) const {
|
||||||
|
size_t low = 0, high = 1;
|
||||||
|
for (size_t j = 0; j < i; ++j) {
|
||||||
|
if ((++low) == high) {
|
||||||
|
++high;
|
||||||
|
low = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
KMemoryRegionTypeValue new_type = *this;
|
||||||
|
new_type.m_value |= (ValueType{1} << (m_next_bit + low));
|
||||||
|
new_type.m_value |= (ValueType{1} << (m_next_bit + high));
|
||||||
|
new_type.m_next_bit += BitsForDeriveDense(n);
|
||||||
|
return new_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr KMemoryRegionTypeValue Advance(size_t n) const {
|
||||||
|
KMemoryRegionTypeValue new_type = *this;
|
||||||
|
new_type.m_next_bit += n;
|
||||||
|
return new_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool IsAncestorOf(ValueType v) const {
|
||||||
|
return (m_value | v) == v;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
constexpr KMemoryRegionTypeValue(ValueType v) : m_value(v) {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ValueType m_value{};
|
||||||
|
size_t m_next_bit{};
|
||||||
|
bool m_finalized{};
|
||||||
|
bool m_sparse_only{};
|
||||||
|
bool m_dense_only{};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace impl
|
||||||
|
|
||||||
|
constexpr auto KMemoryRegionType_None = impl::KMemoryRegionTypeValue();
|
||||||
|
constexpr auto KMemoryRegionType_Kernel = KMemoryRegionType_None.DeriveInitial(0, 2);
|
||||||
|
constexpr auto KMemoryRegionType_Dram = KMemoryRegionType_None.DeriveInitial(1, 2);
|
||||||
|
static_assert(KMemoryRegionType_Kernel.GetValue() == 0x1);
|
||||||
|
static_assert(KMemoryRegionType_Dram.GetValue() == 0x2);
|
||||||
|
|
||||||
|
constexpr auto KMemoryRegionType_DramKernelBase =
|
||||||
|
KMemoryRegionType_Dram.DeriveSparse(0, 3, 0)
|
||||||
|
.SetAttribute(KMemoryRegionAttr_NoUserMap)
|
||||||
|
.SetAttribute(KMemoryRegionAttr_CarveoutProtected);
|
||||||
|
constexpr auto KMemoryRegionType_DramReservedBase = KMemoryRegionType_Dram.DeriveSparse(0, 3, 1);
|
||||||
|
constexpr auto KMemoryRegionType_DramHeapBase =
|
||||||
|
KMemoryRegionType_Dram.DeriveSparse(0, 3, 2).SetAttribute(KMemoryRegionAttr_LinearMapped);
|
||||||
|
static_assert(KMemoryRegionType_DramKernelBase.GetValue() ==
|
||||||
|
(0xE | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap));
|
||||||
|
static_assert(KMemoryRegionType_DramReservedBase.GetValue() == (0x16));
|
||||||
|
static_assert(KMemoryRegionType_DramHeapBase.GetValue() == (0x26 | KMemoryRegionAttr_LinearMapped));
|
||||||
|
|
||||||
|
constexpr auto KMemoryRegionType_DramKernelCode =
|
||||||
|
KMemoryRegionType_DramKernelBase.DeriveSparse(0, 4, 0);
|
||||||
|
constexpr auto KMemoryRegionType_DramKernelSlab =
|
||||||
|
KMemoryRegionType_DramKernelBase.DeriveSparse(0, 4, 1);
|
||||||
|
constexpr auto KMemoryRegionType_DramKernelPtHeap =
|
||||||
|
KMemoryRegionType_DramKernelBase.DeriveSparse(0, 4, 2).SetAttribute(
|
||||||
|
KMemoryRegionAttr_LinearMapped);
|
||||||
|
constexpr auto KMemoryRegionType_DramKernelInitPt =
|
||||||
|
KMemoryRegionType_DramKernelBase.DeriveSparse(0, 4, 3).SetAttribute(
|
||||||
|
KMemoryRegionAttr_LinearMapped);
|
||||||
|
static_assert(KMemoryRegionType_DramKernelCode.GetValue() ==
|
||||||
|
(0xCE | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap));
|
||||||
|
static_assert(KMemoryRegionType_DramKernelSlab.GetValue() ==
|
||||||
|
(0x14E | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap));
|
||||||
|
static_assert(KMemoryRegionType_DramKernelPtHeap.GetValue() ==
|
||||||
|
(0x24E | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap |
|
||||||
|
KMemoryRegionAttr_LinearMapped));
|
||||||
|
static_assert(KMemoryRegionType_DramKernelInitPt.GetValue() ==
|
||||||
|
(0x44E | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap |
|
||||||
|
KMemoryRegionAttr_LinearMapped));
|
||||||
|
|
||||||
|
constexpr auto KMemoryRegionType_DramReservedEarly =
|
||||||
|
KMemoryRegionType_DramReservedBase.DeriveAttribute(KMemoryRegionAttr_NoUserMap);
|
||||||
|
static_assert(KMemoryRegionType_DramReservedEarly.GetValue() ==
|
||||||
|
(0x16 | KMemoryRegionAttr_NoUserMap));
|
||||||
|
|
||||||
|
constexpr auto KMemoryRegionType_KernelTraceBuffer =
|
||||||
|
KMemoryRegionType_DramReservedBase.DeriveSparse(0, 3, 0)
|
||||||
|
.SetAttribute(KMemoryRegionAttr_LinearMapped)
|
||||||
|
.SetAttribute(KMemoryRegionAttr_UserReadOnly);
|
||||||
|
constexpr auto KMemoryRegionType_OnMemoryBootImage =
|
||||||
|
KMemoryRegionType_DramReservedBase.DeriveSparse(0, 3, 1);
|
||||||
|
constexpr auto KMemoryRegionType_DTB = KMemoryRegionType_DramReservedBase.DeriveSparse(0, 3, 2);
|
||||||
|
static_assert(KMemoryRegionType_KernelTraceBuffer.GetValue() ==
|
||||||
|
(0xD6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_UserReadOnly));
|
||||||
|
static_assert(KMemoryRegionType_OnMemoryBootImage.GetValue() == 0x156);
|
||||||
|
static_assert(KMemoryRegionType_DTB.GetValue() == 0x256);
|
||||||
|
|
||||||
|
constexpr auto KMemoryRegionType_DramPoolPartition =
|
||||||
|
KMemoryRegionType_DramHeapBase.DeriveAttribute(KMemoryRegionAttr_NoUserMap);
|
||||||
|
static_assert(KMemoryRegionType_DramPoolPartition.GetValue() ==
|
||||||
|
(0x26 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
|
||||||
|
|
||||||
|
constexpr auto KMemoryRegionType_DramPoolManagement =
|
||||||
|
KMemoryRegionType_DramPoolPartition.DeriveTransition(0, 2).DeriveTransition().SetAttribute(
|
||||||
|
KMemoryRegionAttr_CarveoutProtected);
|
||||||
|
constexpr auto KMemoryRegionType_DramUserPool =
|
||||||
|
KMemoryRegionType_DramPoolPartition.DeriveTransition(1, 2).DeriveTransition();
|
||||||
|
static_assert(KMemoryRegionType_DramPoolManagement.GetValue() ==
|
||||||
|
(0x166 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap |
|
||||||
|
KMemoryRegionAttr_CarveoutProtected));
|
||||||
|
static_assert(KMemoryRegionType_DramUserPool.GetValue() ==
|
||||||
|
(0x1A6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
|
||||||
|
|
||||||
|
constexpr auto KMemoryRegionType_DramApplicationPool = KMemoryRegionType_DramUserPool.Derive(4, 0);
|
||||||
|
constexpr auto KMemoryRegionType_DramAppletPool = KMemoryRegionType_DramUserPool.Derive(4, 1);
|
||||||
|
constexpr auto KMemoryRegionType_DramSystemNonSecurePool =
|
||||||
|
KMemoryRegionType_DramUserPool.Derive(4, 2);
|
||||||
|
constexpr auto KMemoryRegionType_DramSystemPool =
|
||||||
|
KMemoryRegionType_DramUserPool.Derive(4, 3).SetAttribute(KMemoryRegionAttr_CarveoutProtected);
|
||||||
|
static_assert(KMemoryRegionType_DramApplicationPool.GetValue() ==
|
||||||
|
(0x7A6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
|
||||||
|
static_assert(KMemoryRegionType_DramAppletPool.GetValue() ==
|
||||||
|
(0xBA6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
|
||||||
|
static_assert(KMemoryRegionType_DramSystemNonSecurePool.GetValue() ==
|
||||||
|
(0xDA6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
|
||||||
|
static_assert(KMemoryRegionType_DramSystemPool.GetValue() ==
|
||||||
|
(0x13A6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap |
|
||||||
|
KMemoryRegionAttr_CarveoutProtected));
|
||||||
|
|
||||||
|
constexpr auto KMemoryRegionType_VirtualDramHeapBase = KMemoryRegionType_Dram.DeriveSparse(1, 3, 0);
|
||||||
|
constexpr auto KMemoryRegionType_VirtualDramKernelPtHeap =
|
||||||
|
KMemoryRegionType_Dram.DeriveSparse(1, 3, 1);
|
||||||
|
constexpr auto KMemoryRegionType_VirtualDramKernelTraceBuffer =
|
||||||
|
KMemoryRegionType_Dram.DeriveSparse(1, 3, 2);
|
||||||
|
static_assert(KMemoryRegionType_VirtualDramHeapBase.GetValue() == 0x1A);
|
||||||
|
static_assert(KMemoryRegionType_VirtualDramKernelPtHeap.GetValue() == 0x2A);
|
||||||
|
static_assert(KMemoryRegionType_VirtualDramKernelTraceBuffer.GetValue() == 0x4A);
|
||||||
|
|
||||||
|
constexpr auto KMemoryRegionType_VirtualDramKernelInitPt =
|
||||||
|
KMemoryRegionType_VirtualDramHeapBase.Derive(3, 0);
|
||||||
|
constexpr auto KMemoryRegionType_VirtualDramPoolManagement =
|
||||||
|
KMemoryRegionType_VirtualDramHeapBase.Derive(3, 1);
|
||||||
|
constexpr auto KMemoryRegionType_VirtualDramUserPool =
|
||||||
|
KMemoryRegionType_VirtualDramHeapBase.Derive(3, 2);
|
||||||
|
static_assert(KMemoryRegionType_VirtualDramKernelInitPt.GetValue() == 0x19A);
|
||||||
|
static_assert(KMemoryRegionType_VirtualDramPoolManagement.GetValue() == 0x29A);
|
||||||
|
static_assert(KMemoryRegionType_VirtualDramUserPool.GetValue() == 0x31A);
|
||||||
|
|
||||||
|
// NOTE: For unknown reason, the pools are derived out-of-order here. It's worth eventually trying
|
||||||
|
// to understand why Nintendo made this choice.
|
||||||
|
// UNUSED: .Derive(6, 0);
|
||||||
|
// UNUSED: .Derive(6, 1);
|
||||||
|
constexpr auto KMemoryRegionType_VirtualDramAppletPool =
|
||||||
|
KMemoryRegionType_VirtualDramUserPool.Derive(6, 2);
|
||||||
|
constexpr auto KMemoryRegionType_VirtualDramApplicationPool =
|
||||||
|
KMemoryRegionType_VirtualDramUserPool.Derive(6, 3);
|
||||||
|
constexpr auto KMemoryRegionType_VirtualDramSystemNonSecurePool =
|
||||||
|
KMemoryRegionType_VirtualDramUserPool.Derive(6, 4);
|
||||||
|
constexpr auto KMemoryRegionType_VirtualDramSystemPool =
|
||||||
|
KMemoryRegionType_VirtualDramUserPool.Derive(6, 5);
|
||||||
|
static_assert(KMemoryRegionType_VirtualDramAppletPool.GetValue() == 0x1B1A);
|
||||||
|
static_assert(KMemoryRegionType_VirtualDramApplicationPool.GetValue() == 0x271A);
|
||||||
|
static_assert(KMemoryRegionType_VirtualDramSystemNonSecurePool.GetValue() == 0x2B1A);
|
||||||
|
static_assert(KMemoryRegionType_VirtualDramSystemPool.GetValue() == 0x331A);
|
||||||
|
|
||||||
|
constexpr auto KMemoryRegionType_ArchDeviceBase =
|
||||||
|
KMemoryRegionType_Kernel.DeriveTransition(0, 1).SetSparseOnly();
|
||||||
|
constexpr auto KMemoryRegionType_BoardDeviceBase =
|
||||||
|
KMemoryRegionType_Kernel.DeriveTransition(0, 2).SetDenseOnly();
|
||||||
|
static_assert(KMemoryRegionType_ArchDeviceBase.GetValue() == 0x5);
|
||||||
|
static_assert(KMemoryRegionType_BoardDeviceBase.GetValue() == 0x5);
|
||||||
|
|
||||||
|
#if defined(ARCH_ARM64)
|
||||||
|
#include "core/hle/kernel/arch/arm64/k_memory_region_device_types.inc"
|
||||||
|
#elif defined(ARCH_ARM)
|
||||||
|
#error "Unimplemented"
|
||||||
|
#else
|
||||||
|
// Default to no architecture devices.
|
||||||
|
constexpr auto NumArchitectureDeviceRegions = 0;
|
||||||
|
#endif
|
||||||
|
static_assert(NumArchitectureDeviceRegions >= 0);
|
||||||
|
|
||||||
|
#if defined(BOARD_NINTENDO_NX)
|
||||||
|
#include "core/hle/kernel/board/nintendo/nx/k_memory_region_device_types.inc"
|
||||||
|
#else
|
||||||
|
// Default to no board devices.
|
||||||
|
constexpr auto NumBoardDeviceRegions = 0;
|
||||||
|
#endif
|
||||||
|
static_assert(NumBoardDeviceRegions >= 0);
|
||||||
|
|
||||||
|
constexpr auto KMemoryRegionType_KernelCode = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 0);
|
||||||
|
constexpr auto KMemoryRegionType_KernelStack = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 1);
|
||||||
|
constexpr auto KMemoryRegionType_KernelMisc = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 2);
|
||||||
|
constexpr auto KMemoryRegionType_KernelSlab = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 3);
|
||||||
|
static_assert(KMemoryRegionType_KernelCode.GetValue() == 0x19);
|
||||||
|
static_assert(KMemoryRegionType_KernelStack.GetValue() == 0x29);
|
||||||
|
static_assert(KMemoryRegionType_KernelMisc.GetValue() == 0x49);
|
||||||
|
static_assert(KMemoryRegionType_KernelSlab.GetValue() == 0x89);
|
||||||
|
|
||||||
|
constexpr auto KMemoryRegionType_KernelMiscDerivedBase =
|
||||||
|
KMemoryRegionType_KernelMisc.DeriveTransition();
|
||||||
|
static_assert(KMemoryRegionType_KernelMiscDerivedBase.GetValue() == 0x149);
|
||||||
|
|
||||||
|
// UNUSED: .Derive(7, 0);
|
||||||
|
constexpr auto KMemoryRegionType_KernelMiscMainStack =
|
||||||
|
KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 1);
|
||||||
|
constexpr auto KMemoryRegionType_KernelMiscMappedDevice =
|
||||||
|
KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 2);
|
||||||
|
constexpr auto KMemoryRegionType_KernelMiscExceptionStack =
|
||||||
|
KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 3);
|
||||||
|
constexpr auto KMemoryRegionType_KernelMiscUnknownDebug =
|
||||||
|
KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 4);
|
||||||
|
// UNUSED: .Derive(7, 5);
|
||||||
|
constexpr auto KMemoryRegionType_KernelMiscIdleStack =
|
||||||
|
KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 6);
|
||||||
|
static_assert(KMemoryRegionType_KernelMiscMainStack.GetValue() == 0xB49);
|
||||||
|
static_assert(KMemoryRegionType_KernelMiscMappedDevice.GetValue() == 0xD49);
|
||||||
|
static_assert(KMemoryRegionType_KernelMiscExceptionStack.GetValue() == 0x1349);
|
||||||
|
static_assert(KMemoryRegionType_KernelMiscUnknownDebug.GetValue() == 0x1549);
|
||||||
|
static_assert(KMemoryRegionType_KernelMiscIdleStack.GetValue() == 0x2349);
|
||||||
|
|
||||||
|
constexpr auto KMemoryRegionType_KernelTemp = KMemoryRegionType_Kernel.Advance(2).Derive(2, 0);
|
||||||
|
static_assert(KMemoryRegionType_KernelTemp.GetValue() == 0x31);
|
||||||
|
|
||||||
|
constexpr KMemoryRegionType GetTypeForVirtualLinearMapping(u32 type_id) {
|
||||||
|
if (KMemoryRegionType_KernelTraceBuffer.IsAncestorOf(type_id)) {
|
||||||
|
return KMemoryRegionType_VirtualDramKernelTraceBuffer;
|
||||||
|
} else if (KMemoryRegionType_DramKernelPtHeap.IsAncestorOf(type_id)) {
|
||||||
|
return KMemoryRegionType_VirtualDramKernelPtHeap;
|
||||||
|
} else {
|
||||||
|
return KMemoryRegionType_Dram;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Kernel
|
|
@ -62,7 +62,7 @@ void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedul
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 KScheduler::UpdateHighestPriorityThread(KThread* highest_thread) {
|
u64 KScheduler::UpdateHighestPriorityThread(KThread* highest_thread) {
|
||||||
std::scoped_lock lock{guard};
|
KScopedSpinLock lk{guard};
|
||||||
if (KThread* prev_highest_thread = state.highest_priority_thread;
|
if (KThread* prev_highest_thread = state.highest_priority_thread;
|
||||||
prev_highest_thread != highest_thread) {
|
prev_highest_thread != highest_thread) {
|
||||||
if (prev_highest_thread != nullptr) {
|
if (prev_highest_thread != nullptr) {
|
||||||
|
@ -637,11 +637,11 @@ void KScheduler::RescheduleCurrentCore() {
|
||||||
if (phys_core.IsInterrupted()) {
|
if (phys_core.IsInterrupted()) {
|
||||||
phys_core.ClearInterrupt();
|
phys_core.ClearInterrupt();
|
||||||
}
|
}
|
||||||
guard.lock();
|
guard.Lock();
|
||||||
if (state.needs_scheduling.load()) {
|
if (state.needs_scheduling.load()) {
|
||||||
Schedule();
|
Schedule();
|
||||||
} else {
|
} else {
|
||||||
guard.unlock();
|
guard.Unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -669,7 +669,7 @@ void KScheduler::Unload(KThread* thread) {
|
||||||
} else {
|
} else {
|
||||||
prev_thread = nullptr;
|
prev_thread = nullptr;
|
||||||
}
|
}
|
||||||
thread->context_guard.unlock();
|
thread->context_guard.Unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -713,7 +713,7 @@ void KScheduler::ScheduleImpl() {
|
||||||
|
|
||||||
// If we're not actually switching thread, there's nothing to do.
|
// If we're not actually switching thread, there's nothing to do.
|
||||||
if (next_thread == current_thread.load()) {
|
if (next_thread == current_thread.load()) {
|
||||||
guard.unlock();
|
guard.Unlock();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -732,7 +732,7 @@ void KScheduler::ScheduleImpl() {
|
||||||
} else {
|
} else {
|
||||||
old_context = &idle_thread->GetHostContext();
|
old_context = &idle_thread->GetHostContext();
|
||||||
}
|
}
|
||||||
guard.unlock();
|
guard.Unlock();
|
||||||
|
|
||||||
Common::Fiber::YieldTo(*old_context, *switch_fiber);
|
Common::Fiber::YieldTo(*old_context, *switch_fiber);
|
||||||
/// When a thread wakes up, the scheduler may have changed to other in another core.
|
/// When a thread wakes up, the scheduler may have changed to other in another core.
|
||||||
|
@ -748,24 +748,24 @@ void KScheduler::OnSwitch(void* this_scheduler) {
|
||||||
void KScheduler::SwitchToCurrent() {
|
void KScheduler::SwitchToCurrent() {
|
||||||
while (true) {
|
while (true) {
|
||||||
{
|
{
|
||||||
std::scoped_lock lock{guard};
|
KScopedSpinLock lk{guard};
|
||||||
current_thread.store(state.highest_priority_thread);
|
current_thread.store(state.highest_priority_thread);
|
||||||
state.needs_scheduling.store(false);
|
state.needs_scheduling.store(false);
|
||||||
}
|
}
|
||||||
const auto is_switch_pending = [this] {
|
const auto is_switch_pending = [this] {
|
||||||
std::scoped_lock lock{guard};
|
KScopedSpinLock lk{guard};
|
||||||
return state.needs_scheduling.load();
|
return state.needs_scheduling.load();
|
||||||
};
|
};
|
||||||
do {
|
do {
|
||||||
auto next_thread = current_thread.load();
|
auto next_thread = current_thread.load();
|
||||||
if (next_thread != nullptr) {
|
if (next_thread != nullptr) {
|
||||||
next_thread->context_guard.lock();
|
next_thread->context_guard.Lock();
|
||||||
if (next_thread->GetRawState() != ThreadState::Runnable) {
|
if (next_thread->GetRawState() != ThreadState::Runnable) {
|
||||||
next_thread->context_guard.unlock();
|
next_thread->context_guard.Unlock();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (next_thread->GetActiveCore() != core_id) {
|
if (next_thread->GetActiveCore() != core_id) {
|
||||||
next_thread->context_guard.unlock();
|
next_thread->context_guard.Unlock();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,19 +2,16 @@
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// 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
|
#pragma once
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/spin_lock.h"
|
|
||||||
#include "core/hle/kernel/global_scheduler_context.h"
|
#include "core/hle/kernel/global_scheduler_context.h"
|
||||||
#include "core/hle/kernel/k_priority_queue.h"
|
#include "core/hle/kernel/k_priority_queue.h"
|
||||||
#include "core/hle/kernel/k_scheduler_lock.h"
|
#include "core/hle/kernel/k_scheduler_lock.h"
|
||||||
#include "core/hle/kernel/k_scoped_lock.h"
|
#include "core/hle/kernel/k_scoped_lock.h"
|
||||||
|
#include "core/hle/kernel/k_spin_lock.h"
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
class Fiber;
|
class Fiber;
|
||||||
|
@ -195,7 +192,7 @@ private:
|
||||||
u64 last_context_switch_time{};
|
u64 last_context_switch_time{};
|
||||||
const s32 core_id;
|
const s32 core_id;
|
||||||
|
|
||||||
Common::SpinLock guard{};
|
KSpinLock guard{};
|
||||||
};
|
};
|
||||||
|
|
||||||
class [[nodiscard]] KScopedSchedulerLock : KScopedLock<GlobalSchedulerContext::LockType> {
|
class [[nodiscard]] KScopedSchedulerLock : KScopedLock<GlobalSchedulerContext::LockType> {
|
||||||
|
|
|
@ -2,14 +2,11 @@
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// 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
|
#pragma once
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/spin_lock.h"
|
|
||||||
#include "core/hardware_properties.h"
|
#include "core/hardware_properties.h"
|
||||||
|
#include "core/hle/kernel/k_spin_lock.h"
|
||||||
#include "core/hle/kernel/k_thread.h"
|
#include "core/hle/kernel/k_thread.h"
|
||||||
#include "core/hle/kernel/kernel.h"
|
#include "core/hle/kernel/kernel.h"
|
||||||
|
|
||||||
|
@ -34,7 +31,7 @@ public:
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, we want to disable scheduling and acquire the spinlock.
|
// Otherwise, we want to disable scheduling and acquire the spinlock.
|
||||||
SchedulerType::DisableScheduling(kernel);
|
SchedulerType::DisableScheduling(kernel);
|
||||||
spin_lock.lock();
|
spin_lock.Lock();
|
||||||
|
|
||||||
// For debug, ensure that our state is valid.
|
// For debug, ensure that our state is valid.
|
||||||
ASSERT(lock_count == 0);
|
ASSERT(lock_count == 0);
|
||||||
|
@ -58,7 +55,7 @@ public:
|
||||||
|
|
||||||
// Note that we no longer hold the lock, and unlock the spinlock.
|
// Note that we no longer hold the lock, and unlock the spinlock.
|
||||||
owner_thread = nullptr;
|
owner_thread = nullptr;
|
||||||
spin_lock.unlock();
|
spin_lock.Unlock();
|
||||||
|
|
||||||
// Enable scheduling, and perform a rescheduling operation.
|
// Enable scheduling, and perform a rescheduling operation.
|
||||||
SchedulerType::EnableScheduling(kernel, cores_needing_scheduling);
|
SchedulerType::EnableScheduling(kernel, cores_needing_scheduling);
|
||||||
|
@ -67,7 +64,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
KernelCore& kernel;
|
KernelCore& kernel;
|
||||||
Common::SpinLock spin_lock{};
|
KAlignedSpinLock spin_lock{};
|
||||||
s32 lock_count{};
|
s32 lock_count{};
|
||||||
KThread* owner_thread{};
|
KThread* owner_thread{};
|
||||||
};
|
};
|
||||||
|
|
|
@ -28,6 +28,12 @@ private:
|
||||||
std::atomic_flag lck = ATOMIC_FLAG_INIT;
|
std::atomic_flag lck = ATOMIC_FLAG_INIT;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO(bunnei): Alias for now, in case we want to implement these accurately in the future.
|
||||||
|
using KAlignedSpinLock = KSpinLock;
|
||||||
|
using KNotAlignedSpinLock = KSpinLock;
|
||||||
|
|
||||||
using KScopedSpinLock = KScopedLock<KSpinLock>;
|
using KScopedSpinLock = KScopedLock<KSpinLock>;
|
||||||
|
using KScopedAlignedSpinLock = KScopedLock<KAlignedSpinLock>;
|
||||||
|
using KScopedNotAlignedSpinLock = KScopedLock<KNotAlignedSpinLock>;
|
||||||
|
|
||||||
} // namespace Kernel
|
} // namespace Kernel
|
||||||
|
|
|
@ -1,42 +0,0 @@
|
||||||
// Copyright 2021 yuzu Emulator Project
|
|
||||||
// Licensed under GPLv2 or any later version
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#include <random>
|
|
||||||
|
|
||||||
#include "core/hle/kernel/k_system_control.h"
|
|
||||||
|
|
||||||
namespace Kernel {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
template <typename F>
|
|
||||||
u64 GenerateUniformRange(u64 min, u64 max, F f) {
|
|
||||||
// Handle the case where the difference is too large to represent.
|
|
||||||
if (max == std::numeric_limits<u64>::max() && min == std::numeric_limits<u64>::min()) {
|
|
||||||
return f();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iterate until we get a value in range.
|
|
||||||
const u64 range_size = ((max + 1) - min);
|
|
||||||
const u64 effective_max = (std::numeric_limits<u64>::max() / range_size) * range_size;
|
|
||||||
while (true) {
|
|
||||||
if (const u64 rnd = f(); rnd < effective_max) {
|
|
||||||
return min + (rnd % range_size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // Anonymous namespace
|
|
||||||
|
|
||||||
u64 KSystemControl::GenerateRandomU64() {
|
|
||||||
static std::random_device device;
|
|
||||||
static std::mt19937 gen(device());
|
|
||||||
static std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
|
|
||||||
return distribution(gen);
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 KSystemControl::GenerateRandomRange(u64 min, u64 max) {
|
|
||||||
return GenerateUniformRange(min, max, GenerateRandomU64);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Kernel
|
|
|
@ -6,14 +6,18 @@
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
|
||||||
|
#define BOARD_NINTENDO_NX
|
||||||
|
|
||||||
|
#ifdef BOARD_NINTENDO_NX
|
||||||
|
|
||||||
|
#include "core/hle/kernel/board/nintendo/nx/k_system_control.h"
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
class KSystemControl {
|
using Kernel::Board::Nintendo::Nx::KSystemControl;
|
||||||
public:
|
|
||||||
KSystemControl() = default;
|
|
||||||
|
|
||||||
static u64 GenerateRandomRange(u64 min, u64 max);
|
|
||||||
static u64 GenerateRandomU64();
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Kernel
|
} // namespace Kernel
|
||||||
|
|
||||||
|
#else
|
||||||
|
#error "Unknown board for KSystemControl"
|
||||||
|
#endif
|
||||||
|
|
|
@ -14,10 +14,10 @@
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/intrusive_red_black_tree.h"
|
#include "common/intrusive_red_black_tree.h"
|
||||||
#include "common/spin_lock.h"
|
|
||||||
#include "core/arm/arm_interface.h"
|
#include "core/arm/arm_interface.h"
|
||||||
#include "core/hle/kernel/k_affinity_mask.h"
|
#include "core/hle/kernel/k_affinity_mask.h"
|
||||||
#include "core/hle/kernel/k_light_lock.h"
|
#include "core/hle/kernel/k_light_lock.h"
|
||||||
|
#include "core/hle/kernel/k_spin_lock.h"
|
||||||
#include "core/hle/kernel/k_synchronization_object.h"
|
#include "core/hle/kernel/k_synchronization_object.h"
|
||||||
#include "core/hle/kernel/object.h"
|
#include "core/hle/kernel/object.h"
|
||||||
#include "core/hle/kernel/svc_common.h"
|
#include "core/hle/kernel/svc_common.h"
|
||||||
|
@ -732,7 +732,7 @@ private:
|
||||||
s8 priority_inheritance_count{};
|
s8 priority_inheritance_count{};
|
||||||
bool resource_limit_release_hint{};
|
bool resource_limit_release_hint{};
|
||||||
StackParameters stack_parameters{};
|
StackParameters stack_parameters{};
|
||||||
Common::SpinLock context_guard{};
|
KSpinLock context_guard{};
|
||||||
|
|
||||||
// For emulation
|
// For emulation
|
||||||
std::shared_ptr<Common::Fiber> host_context{};
|
std::shared_ptr<Common::Fiber> host_context{};
|
||||||
|
|
12
src/core/hle/kernel/k_trace.h
Normal file
12
src/core/hle/kernel/k_trace.h
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
// Copyright 2021 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Kernel {
|
||||||
|
|
||||||
|
constexpr bool IsKTraceEnabled = false;
|
||||||
|
constexpr std::size_t KTraceBufferSize = IsKTraceEnabled ? 16 * 1024 * 1024 : 0;
|
||||||
|
|
||||||
|
} // namespace Kernel
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2014 Citra Emulator Project
|
// Copyright 2021 yuzu Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
|
#include "common/common_sizes.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/microprofile.h"
|
#include "common/microprofile.h"
|
||||||
#include "common/thread.h"
|
#include "common/thread.h"
|
||||||
|
@ -268,45 +269,314 @@ struct KernelCore::Impl {
|
||||||
return schedulers[thread_id]->GetCurrentThread();
|
return schedulers[thread_id]->GetCurrentThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DeriveInitialMemoryLayout(KMemoryLayout& memory_layout) {
|
||||||
|
// Insert the root region for the virtual memory tree, from which all other regions will
|
||||||
|
// derive.
|
||||||
|
memory_layout.GetVirtualMemoryRegionTree().InsertDirectly(
|
||||||
|
KernelVirtualAddressSpaceBase,
|
||||||
|
KernelVirtualAddressSpaceBase + KernelVirtualAddressSpaceSize - 1);
|
||||||
|
|
||||||
|
// Insert the root region for the physical memory tree, from which all other regions will
|
||||||
|
// derive.
|
||||||
|
memory_layout.GetPhysicalMemoryRegionTree().InsertDirectly(
|
||||||
|
KernelPhysicalAddressSpaceBase,
|
||||||
|
KernelPhysicalAddressSpaceBase + KernelPhysicalAddressSpaceSize - 1);
|
||||||
|
|
||||||
|
// Save start and end for ease of use.
|
||||||
|
const VAddr code_start_virt_addr = KernelVirtualAddressCodeBase;
|
||||||
|
const VAddr code_end_virt_addr = KernelVirtualAddressCodeEnd;
|
||||||
|
|
||||||
|
// Setup the containing kernel region.
|
||||||
|
constexpr size_t KernelRegionSize = Common::Size_1_GB;
|
||||||
|
constexpr size_t KernelRegionAlign = Common::Size_1_GB;
|
||||||
|
constexpr VAddr kernel_region_start =
|
||||||
|
Common::AlignDown(code_start_virt_addr, KernelRegionAlign);
|
||||||
|
size_t kernel_region_size = KernelRegionSize;
|
||||||
|
if (!(kernel_region_start + KernelRegionSize - 1 <= KernelVirtualAddressSpaceLast)) {
|
||||||
|
kernel_region_size = KernelVirtualAddressSpaceEnd - kernel_region_start;
|
||||||
|
}
|
||||||
|
ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert(
|
||||||
|
kernel_region_start, kernel_region_size, KMemoryRegionType_Kernel));
|
||||||
|
|
||||||
|
// Setup the code region.
|
||||||
|
constexpr size_t CodeRegionAlign = PageSize;
|
||||||
|
constexpr VAddr code_region_start =
|
||||||
|
Common::AlignDown(code_start_virt_addr, CodeRegionAlign);
|
||||||
|
constexpr VAddr code_region_end = Common::AlignUp(code_end_virt_addr, CodeRegionAlign);
|
||||||
|
constexpr size_t code_region_size = code_region_end - code_region_start;
|
||||||
|
ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert(
|
||||||
|
code_region_start, code_region_size, KMemoryRegionType_KernelCode));
|
||||||
|
|
||||||
|
// Setup board-specific device physical regions.
|
||||||
|
Init::SetupDevicePhysicalMemoryRegions(memory_layout);
|
||||||
|
|
||||||
|
// Determine the amount of space needed for the misc region.
|
||||||
|
size_t misc_region_needed_size;
|
||||||
|
{
|
||||||
|
// Each core has a one page stack for all three stack types (Main, Idle, Exception).
|
||||||
|
misc_region_needed_size = Core::Hardware::NUM_CPU_CORES * (3 * (PageSize + PageSize));
|
||||||
|
|
||||||
|
// Account for each auto-map device.
|
||||||
|
for (const auto& region : memory_layout.GetPhysicalMemoryRegionTree()) {
|
||||||
|
if (region.HasTypeAttribute(KMemoryRegionAttr_ShouldKernelMap)) {
|
||||||
|
// Check that the region is valid.
|
||||||
|
ASSERT(region.GetEndAddress() != 0);
|
||||||
|
|
||||||
|
// Account for the region.
|
||||||
|
misc_region_needed_size +=
|
||||||
|
PageSize + (Common::AlignUp(region.GetLastAddress(), PageSize) -
|
||||||
|
Common::AlignDown(region.GetAddress(), PageSize));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multiply the needed size by three, to account for the need for guard space.
|
||||||
|
misc_region_needed_size *= 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decide on the actual size for the misc region.
|
||||||
|
constexpr size_t MiscRegionAlign = KernelAslrAlignment;
|
||||||
|
constexpr size_t MiscRegionMinimumSize = Common::Size_32_MB;
|
||||||
|
const size_t misc_region_size = Common::AlignUp(
|
||||||
|
std::max(misc_region_needed_size, MiscRegionMinimumSize), MiscRegionAlign);
|
||||||
|
ASSERT(misc_region_size > 0);
|
||||||
|
|
||||||
|
// Setup the misc region.
|
||||||
|
const VAddr misc_region_start =
|
||||||
|
memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegion(
|
||||||
|
misc_region_size, MiscRegionAlign, KMemoryRegionType_Kernel);
|
||||||
|
ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert(
|
||||||
|
misc_region_start, misc_region_size, KMemoryRegionType_KernelMisc));
|
||||||
|
|
||||||
|
// Setup the stack region.
|
||||||
|
constexpr size_t StackRegionSize = Common::Size_14_MB;
|
||||||
|
constexpr size_t StackRegionAlign = KernelAslrAlignment;
|
||||||
|
const VAddr stack_region_start =
|
||||||
|
memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegion(
|
||||||
|
StackRegionSize, StackRegionAlign, KMemoryRegionType_Kernel);
|
||||||
|
ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert(
|
||||||
|
stack_region_start, StackRegionSize, KMemoryRegionType_KernelStack));
|
||||||
|
|
||||||
|
// Determine the size of the resource region.
|
||||||
|
const size_t resource_region_size = memory_layout.GetResourceRegionSizeForInit();
|
||||||
|
|
||||||
|
// Determine the size of the slab region.
|
||||||
|
const size_t slab_region_size = Common::AlignUp(KernelSlabHeapSize, PageSize);
|
||||||
|
ASSERT(slab_region_size <= resource_region_size);
|
||||||
|
|
||||||
|
// Setup the slab region.
|
||||||
|
const PAddr code_start_phys_addr = KernelPhysicalAddressCodeBase;
|
||||||
|
const PAddr code_end_phys_addr = code_start_phys_addr + code_region_size;
|
||||||
|
const PAddr slab_start_phys_addr = code_end_phys_addr;
|
||||||
|
const PAddr slab_end_phys_addr = slab_start_phys_addr + slab_region_size;
|
||||||
|
constexpr size_t SlabRegionAlign = KernelAslrAlignment;
|
||||||
|
const size_t slab_region_needed_size =
|
||||||
|
Common::AlignUp(code_end_phys_addr + slab_region_size, SlabRegionAlign) -
|
||||||
|
Common::AlignDown(code_end_phys_addr, SlabRegionAlign);
|
||||||
|
const VAddr slab_region_start =
|
||||||
|
memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegion(
|
||||||
|
slab_region_needed_size, SlabRegionAlign, KMemoryRegionType_Kernel) +
|
||||||
|
(code_end_phys_addr % SlabRegionAlign);
|
||||||
|
ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert(
|
||||||
|
slab_region_start, slab_region_size, KMemoryRegionType_KernelSlab));
|
||||||
|
|
||||||
|
// Setup the temp region.
|
||||||
|
constexpr size_t TempRegionSize = Common::Size_128_MB;
|
||||||
|
constexpr size_t TempRegionAlign = KernelAslrAlignment;
|
||||||
|
const VAddr temp_region_start =
|
||||||
|
memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegion(
|
||||||
|
TempRegionSize, TempRegionAlign, KMemoryRegionType_Kernel);
|
||||||
|
ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert(temp_region_start, TempRegionSize,
|
||||||
|
KMemoryRegionType_KernelTemp));
|
||||||
|
|
||||||
|
// Automatically map in devices that have auto-map attributes.
|
||||||
|
for (auto& region : memory_layout.GetPhysicalMemoryRegionTree()) {
|
||||||
|
// We only care about kernel regions.
|
||||||
|
if (!region.IsDerivedFrom(KMemoryRegionType_Kernel)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether we should map the region.
|
||||||
|
if (!region.HasTypeAttribute(KMemoryRegionAttr_ShouldKernelMap)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this region has already been mapped, no need to consider it.
|
||||||
|
if (region.HasTypeAttribute(KMemoryRegionAttr_DidKernelMap)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the region is valid.
|
||||||
|
ASSERT(region.GetEndAddress() != 0);
|
||||||
|
|
||||||
|
// Set the attribute to note we've mapped this region.
|
||||||
|
region.SetTypeAttribute(KMemoryRegionAttr_DidKernelMap);
|
||||||
|
|
||||||
|
// Create a virtual pair region and insert it into the tree.
|
||||||
|
const PAddr map_phys_addr = Common::AlignDown(region.GetAddress(), PageSize);
|
||||||
|
const size_t map_size =
|
||||||
|
Common::AlignUp(region.GetEndAddress(), PageSize) - map_phys_addr;
|
||||||
|
const VAddr map_virt_addr =
|
||||||
|
memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegionWithGuard(
|
||||||
|
map_size, PageSize, KMemoryRegionType_KernelMisc, PageSize);
|
||||||
|
ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert(
|
||||||
|
map_virt_addr, map_size, KMemoryRegionType_KernelMiscMappedDevice));
|
||||||
|
region.SetPairAddress(map_virt_addr + region.GetAddress() - map_phys_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
Init::SetupDramPhysicalMemoryRegions(memory_layout);
|
||||||
|
|
||||||
|
// Insert a physical region for the kernel code region.
|
||||||
|
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
|
||||||
|
code_start_phys_addr, code_region_size, KMemoryRegionType_DramKernelCode));
|
||||||
|
|
||||||
|
// Insert a physical region for the kernel slab region.
|
||||||
|
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
|
||||||
|
slab_start_phys_addr, slab_region_size, KMemoryRegionType_DramKernelSlab));
|
||||||
|
|
||||||
|
// Determine size available for kernel page table heaps, requiring > 8 MB.
|
||||||
|
const PAddr resource_end_phys_addr = slab_start_phys_addr + resource_region_size;
|
||||||
|
const size_t page_table_heap_size = resource_end_phys_addr - slab_end_phys_addr;
|
||||||
|
ASSERT(page_table_heap_size / Common::Size_4_MB > 2);
|
||||||
|
|
||||||
|
// Insert a physical region for the kernel page table heap region
|
||||||
|
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
|
||||||
|
slab_end_phys_addr, page_table_heap_size, KMemoryRegionType_DramKernelPtHeap));
|
||||||
|
|
||||||
|
// All DRAM regions that we haven't tagged by this point will be mapped under the linear
|
||||||
|
// mapping. Tag them.
|
||||||
|
for (auto& region : memory_layout.GetPhysicalMemoryRegionTree()) {
|
||||||
|
if (region.GetType() == KMemoryRegionType_Dram) {
|
||||||
|
// Check that the region is valid.
|
||||||
|
ASSERT(region.GetEndAddress() != 0);
|
||||||
|
|
||||||
|
// Set the linear map attribute.
|
||||||
|
region.SetTypeAttribute(KMemoryRegionAttr_LinearMapped);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the linear region extents.
|
||||||
|
const auto linear_extents =
|
||||||
|
memory_layout.GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
|
||||||
|
KMemoryRegionAttr_LinearMapped);
|
||||||
|
ASSERT(linear_extents.GetEndAddress() != 0);
|
||||||
|
|
||||||
|
// Setup the linear mapping region.
|
||||||
|
constexpr size_t LinearRegionAlign = Common::Size_1_GB;
|
||||||
|
const PAddr aligned_linear_phys_start =
|
||||||
|
Common::AlignDown(linear_extents.GetAddress(), LinearRegionAlign);
|
||||||
|
const size_t linear_region_size =
|
||||||
|
Common::AlignUp(linear_extents.GetEndAddress(), LinearRegionAlign) -
|
||||||
|
aligned_linear_phys_start;
|
||||||
|
const VAddr linear_region_start =
|
||||||
|
memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegionWithGuard(
|
||||||
|
linear_region_size, LinearRegionAlign, KMemoryRegionType_None, LinearRegionAlign);
|
||||||
|
|
||||||
|
const u64 linear_region_phys_to_virt_diff = linear_region_start - aligned_linear_phys_start;
|
||||||
|
|
||||||
|
// Map and create regions for all the linearly-mapped data.
|
||||||
|
{
|
||||||
|
PAddr cur_phys_addr = 0;
|
||||||
|
u64 cur_size = 0;
|
||||||
|
for (auto& region : memory_layout.GetPhysicalMemoryRegionTree()) {
|
||||||
|
if (!region.HasTypeAttribute(KMemoryRegionAttr_LinearMapped)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT(region.GetEndAddress() != 0);
|
||||||
|
|
||||||
|
if (cur_size == 0) {
|
||||||
|
cur_phys_addr = region.GetAddress();
|
||||||
|
cur_size = region.GetSize();
|
||||||
|
} else if (cur_phys_addr + cur_size == region.GetAddress()) {
|
||||||
|
cur_size += region.GetSize();
|
||||||
|
} else {
|
||||||
|
cur_phys_addr = region.GetAddress();
|
||||||
|
cur_size = region.GetSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
const VAddr region_virt_addr =
|
||||||
|
region.GetAddress() + linear_region_phys_to_virt_diff;
|
||||||
|
ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert(
|
||||||
|
region_virt_addr, region.GetSize(),
|
||||||
|
GetTypeForVirtualLinearMapping(region.GetType())));
|
||||||
|
region.SetPairAddress(region_virt_addr);
|
||||||
|
|
||||||
|
KMemoryRegion* virt_region =
|
||||||
|
memory_layout.GetVirtualMemoryRegionTree().FindModifiable(region_virt_addr);
|
||||||
|
ASSERT(virt_region != nullptr);
|
||||||
|
virt_region->SetPairAddress(region.GetAddress());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert regions for the initial page table region.
|
||||||
|
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
|
||||||
|
resource_end_phys_addr, KernelPageTableHeapSize, KMemoryRegionType_DramKernelInitPt));
|
||||||
|
ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert(
|
||||||
|
resource_end_phys_addr + linear_region_phys_to_virt_diff, KernelPageTableHeapSize,
|
||||||
|
KMemoryRegionType_VirtualDramKernelInitPt));
|
||||||
|
|
||||||
|
// All linear-mapped DRAM regions that we haven't tagged by this point will be allocated to
|
||||||
|
// some pool partition. Tag them.
|
||||||
|
for (auto& region : memory_layout.GetPhysicalMemoryRegionTree()) {
|
||||||
|
if (region.GetType() == (KMemoryRegionType_Dram | KMemoryRegionAttr_LinearMapped)) {
|
||||||
|
region.SetType(KMemoryRegionType_DramPoolPartition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup all other memory regions needed to arrange the pool partitions.
|
||||||
|
Init::SetupPoolPartitionMemoryRegions(memory_layout);
|
||||||
|
|
||||||
|
// Cache all linear regions in their own trees for faster access, later.
|
||||||
|
memory_layout.InitializeLinearMemoryRegionTrees(aligned_linear_phys_start,
|
||||||
|
linear_region_start);
|
||||||
|
}
|
||||||
|
|
||||||
void InitializeMemoryLayout() {
|
void InitializeMemoryLayout() {
|
||||||
// Initialize memory layout
|
// Derive the initial memory layout from the emulated board
|
||||||
constexpr KMemoryLayout layout{KMemoryLayout::GetDefaultLayout()};
|
KMemoryLayout memory_layout;
|
||||||
|
DeriveInitialMemoryLayout(memory_layout);
|
||||||
|
|
||||||
|
const auto system_pool = memory_layout.GetKernelSystemPoolRegionPhysicalExtents();
|
||||||
|
const auto applet_pool = memory_layout.GetKernelAppletPoolRegionPhysicalExtents();
|
||||||
|
const auto application_pool = memory_layout.GetKernelApplicationPoolRegionPhysicalExtents();
|
||||||
|
|
||||||
|
// Initialize memory managers
|
||||||
|
memory_manager = std::make_unique<KMemoryManager>();
|
||||||
|
memory_manager->InitializeManager(KMemoryManager::Pool::Application,
|
||||||
|
application_pool.GetAddress(),
|
||||||
|
application_pool.GetEndAddress());
|
||||||
|
memory_manager->InitializeManager(KMemoryManager::Pool::Applet, applet_pool.GetAddress(),
|
||||||
|
applet_pool.GetEndAddress());
|
||||||
|
memory_manager->InitializeManager(KMemoryManager::Pool::System, system_pool.GetAddress(),
|
||||||
|
system_pool.GetEndAddress());
|
||||||
|
|
||||||
|
// Setup memory regions for emulated processes
|
||||||
|
// TODO(bunnei): These should not be hardcoded regions initialized within the kernel
|
||||||
constexpr std::size_t hid_size{0x40000};
|
constexpr std::size_t hid_size{0x40000};
|
||||||
constexpr std::size_t font_size{0x1100000};
|
constexpr std::size_t font_size{0x1100000};
|
||||||
constexpr std::size_t irs_size{0x8000};
|
constexpr std::size_t irs_size{0x8000};
|
||||||
constexpr std::size_t time_size{0x1000};
|
constexpr std::size_t time_size{0x1000};
|
||||||
constexpr PAddr hid_addr{layout.System().StartAddress()};
|
|
||||||
constexpr PAddr font_pa{layout.System().StartAddress() + hid_size};
|
|
||||||
constexpr PAddr irs_addr{layout.System().StartAddress() + hid_size + font_size};
|
|
||||||
constexpr PAddr time_addr{layout.System().StartAddress() + hid_size + font_size + irs_size};
|
|
||||||
|
|
||||||
// Initialize memory manager
|
const PAddr hid_phys_addr{system_pool.GetAddress()};
|
||||||
memory_manager = std::make_unique<KMemoryManager>();
|
const PAddr font_phys_addr{system_pool.GetAddress() + hid_size};
|
||||||
memory_manager->InitializeManager(KMemoryManager::Pool::Application,
|
const PAddr irs_phys_addr{system_pool.GetAddress() + hid_size + font_size};
|
||||||
layout.Application().StartAddress(),
|
const PAddr time_phys_addr{system_pool.GetAddress() + hid_size + font_size + irs_size};
|
||||||
layout.Application().EndAddress());
|
|
||||||
memory_manager->InitializeManager(KMemoryManager::Pool::Applet,
|
|
||||||
layout.Applet().StartAddress(),
|
|
||||||
layout.Applet().EndAddress());
|
|
||||||
memory_manager->InitializeManager(KMemoryManager::Pool::System,
|
|
||||||
layout.System().StartAddress(),
|
|
||||||
layout.System().EndAddress());
|
|
||||||
|
|
||||||
hid_shared_mem = Kernel::KSharedMemory::Create(
|
hid_shared_mem = Kernel::KSharedMemory::Create(
|
||||||
system.Kernel(), system.DeviceMemory(), nullptr, {hid_addr, hid_size / PageSize},
|
system.Kernel(), system.DeviceMemory(), nullptr, {hid_phys_addr, hid_size / PageSize},
|
||||||
KMemoryPermission::None, KMemoryPermission::Read, hid_addr, hid_size,
|
KMemoryPermission::None, KMemoryPermission::Read, hid_phys_addr, hid_size,
|
||||||
"HID:SharedMemory");
|
"HID:SharedMemory");
|
||||||
font_shared_mem = Kernel::KSharedMemory::Create(
|
font_shared_mem = Kernel::KSharedMemory::Create(
|
||||||
system.Kernel(), system.DeviceMemory(), nullptr, {font_pa, font_size / PageSize},
|
system.Kernel(), system.DeviceMemory(), nullptr, {font_phys_addr, font_size / PageSize},
|
||||||
KMemoryPermission::None, KMemoryPermission::Read, font_pa, font_size,
|
KMemoryPermission::None, KMemoryPermission::Read, font_phys_addr, font_size,
|
||||||
"Font:SharedMemory");
|
"Font:SharedMemory");
|
||||||
irs_shared_mem = Kernel::KSharedMemory::Create(
|
irs_shared_mem = Kernel::KSharedMemory::Create(
|
||||||
system.Kernel(), system.DeviceMemory(), nullptr, {irs_addr, irs_size / PageSize},
|
system.Kernel(), system.DeviceMemory(), nullptr, {irs_phys_addr, irs_size / PageSize},
|
||||||
KMemoryPermission::None, KMemoryPermission::Read, irs_addr, irs_size,
|
KMemoryPermission::None, KMemoryPermission::Read, irs_phys_addr, irs_size,
|
||||||
"IRS:SharedMemory");
|
"IRS:SharedMemory");
|
||||||
time_shared_mem = Kernel::KSharedMemory::Create(
|
time_shared_mem = Kernel::KSharedMemory::Create(
|
||||||
system.Kernel(), system.DeviceMemory(), nullptr, {time_addr, time_size / PageSize},
|
system.Kernel(), system.DeviceMemory(), nullptr, {time_phys_addr, time_size / PageSize},
|
||||||
KMemoryPermission::None, KMemoryPermission::Read, time_addr, time_size,
|
KMemoryPermission::None, KMemoryPermission::Read, time_phys_addr, time_size,
|
||||||
"Time:SharedMemory");
|
"Time:SharedMemory");
|
||||||
|
|
||||||
// Allocate slab heaps
|
// Allocate slab heaps
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2014 Citra Emulator Project / PPSSPP Project
|
// Copyright 2021 yuzu Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue