Compare commits
131 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3009d0bd7d | |||
| e228a40db8 | |||
| 3ac522ba41 | |||
| 1415542f73 | |||
| 52895fab67 | |||
| cd07a43724 | |||
| f9e1f559b1 | |||
| cc285b9924 | |||
| be0e6a2bb4 | |||
| 0331b8d799 | |||
| 364c67e49b | |||
| 37aa472269 | |||
| 220674d0d6 | |||
| 185fc03c3c | |||
| ec9f0f064e | |||
| 8decc8d1a5 | |||
| 1f6bbb6257 | |||
| 6b164a80a1 | |||
| f87f8d4610 | |||
| e8c50e709e | |||
| 27cc7b6a73 | |||
| 56c646d82c | |||
| 5c74dd6462 | |||
| 15fdc2cd09 | |||
| d248c1203e | |||
| 29a7a61806 | |||
| 70e632f153 | |||
| 8180b262fc | |||
| 82c3042c0f | |||
| f1521183f8 | |||
| e200161982 | |||
| 22f50c6bc1 | |||
| 52f8f00434 | |||
| d9b1199ffb | |||
| 9a97ef4647 | |||
| 873a9fa7e5 | |||
| ec5f3351b6 | |||
| 86e9e60f07 | |||
| 3c33ba7f18 | |||
| d233de8194 | |||
| add2cfcb96 | |||
| 6f670381cf | |||
| 853e58e593 | |||
| f2743b41b0 | |||
| 35309f27ed | |||
| c78c8190d5 | |||
| 1f37896066 | |||
| 749f76e6fe | |||
| b5e60ae1b0 | |||
| 1f24a4e520 | |||
| 58b52f4884 | |||
| 381f1dd2c9 | |||
| 4498908e72 | |||
| 370e480c8c | |||
| fc84649aab | |||
| 5192c64991 | |||
| a31c195749 | |||
| 3ab82e7582 | |||
| b33f23cc46 | |||
| e06a133717 | |||
| 3c47570563 | |||
| b4c919eba8 | |||
| 96dbb9233a | |||
| fdf81cd112 | |||
| c4df0d8cdc | |||
| ec9689f200 | |||
| 14d28a043d | |||
| 16e5954fcb | |||
| f87f076162 | |||
| 913c2bd2cb | |||
| 7f7df43da2 | |||
| 16784e5bb3 | |||
| adbb9c2b00 | |||
| f7e65eb971 | |||
| 06e2b76c75 | |||
| 5d1a81520c | |||
| a6496deeed | |||
| 9b5e7971dc | |||
| 18e77a54c3 | |||
| 06a21ac229 | |||
| 96d90be59f | |||
| ce46aa4721 | |||
| 20e9501b0d | |||
| 56d9052b73 | |||
| 91d802f68d | |||
| 5f8d6bc504 | |||
| e292b2d991 | |||
| 6c9b9b72da | |||
| f582a4482d | |||
| 1079215871 | |||
| 6978093404 | |||
| 2f45e999d8 | |||
| d44464829b | |||
| efe50d88ec | |||
| 71f62a346d | |||
| c0e45a3c78 | |||
| 70482e6b26 | |||
| ca5e843bf6 | |||
| a74fddc98f | |||
| 57ebcbf2c4 | |||
| c7019db6f4 | |||
| 21f5912ec9 | |||
| 8d46c3cc66 | |||
| 7712e46d64 | |||
| b504df9d02 | |||
| 92b2e92620 | |||
| 2984695265 | |||
| c9260a75f6 | |||
| 1a16d055df | |||
| 83a84f1c2d | |||
| 90a4591563 | |||
| 768fdb269e | |||
| c48b9668f0 | |||
| 027ff7847c | |||
| 4514325b9c | |||
| b5fd9c58cd | |||
| 910a0fa58e | |||
| 1e21f5f872 | |||
| e1201abc1e | |||
| 4390370a19 | |||
| b57d61010f | |||
| 81806603eb | |||
| 6705439cf3 | |||
| 6a1ad03153 | |||
| e35c2fd5d0 | |||
| 29f9a454eb | |||
| 41b65d38fa | |||
| fc9abd3c62 | |||
| c001a2af25 | |||
| 3d24eb54ec | |||
| 21742f0096 |
Vendored
+423
-454
File diff suppressed because it is too large
Load Diff
Vendored
+404
-435
File diff suppressed because it is too large
Load Diff
Vendored
+403
-434
File diff suppressed because it is too large
Load Diff
Vendored
+404
-435
File diff suppressed because it is too large
Load Diff
Vendored
+404
-435
File diff suppressed because it is too large
Load Diff
Vendored
+403
-434
File diff suppressed because it is too large
Load Diff
Vendored
+405
-436
File diff suppressed because it is too large
Load Diff
Vendored
+426
-457
File diff suppressed because it is too large
Load Diff
Vendored
+404
-435
File diff suppressed because it is too large
Load Diff
Vendored
+490
-507
File diff suppressed because it is too large
Load Diff
Vendored
+408
-439
File diff suppressed because it is too large
Load Diff
Vendored
+403
-434
File diff suppressed because it is too large
Load Diff
Vendored
+403
-434
File diff suppressed because it is too large
Load Diff
Vendored
+472
-497
File diff suppressed because it is too large
Load Diff
Vendored
+408
-439
File diff suppressed because it is too large
Load Diff
Vendored
+655
-661
File diff suppressed because it is too large
Load Diff
Vendored
+404
-435
File diff suppressed because it is too large
Load Diff
Vendored
+403
-434
File diff suppressed because it is too large
Load Diff
Vendored
+404
-435
File diff suppressed because it is too large
Load Diff
Vendored
+6202
File diff suppressed because it is too large
Load Diff
Vendored
+405
-436
File diff suppressed because it is too large
Load Diff
Vendored
+404
-435
File diff suppressed because it is too large
Load Diff
Vendored
+406
-437
File diff suppressed because it is too large
Load Diff
Vendored
+1
-1
Submodule externals/dynarmic updated: a8cbfd9af4...0fd32c5fa4
Vendored
+1
-1
@@ -214,6 +214,6 @@ else(WIN32)
|
||||
set(FFmpeg_LDFLAGS "${FFmpeg_LDFLAGS}" PARENT_SCOPE)
|
||||
set(FFmpeg_LIBRARIES "${FFmpeg_LIBRARIES}" PARENT_SCOPE)
|
||||
set(FFmpeg_INCLUDE_DIR "${FFmpeg_INCLUDE_DIR}" PARENT_SCOPE)
|
||||
endif(WIN32)
|
||||
endif()
|
||||
|
||||
unset(FFmpeg_COMPONENTS)
|
||||
|
||||
@@ -15,7 +15,9 @@ constexpr ResultCode ERR_INVALID_PARAMETERS{ErrorModule::Audio, 41};
|
||||
constexpr ResultCode ERR_SPLITTER_SORT_FAILED{ErrorModule::Audio, 43};
|
||||
} // namespace Audren
|
||||
|
||||
constexpr u32_le CURRENT_PROCESS_REVISION = Common::MakeMagic('R', 'E', 'V', '9');
|
||||
constexpr u8 BASE_REVISION = '0';
|
||||
constexpr u32_le CURRENT_PROCESS_REVISION =
|
||||
Common::MakeMagic('R', 'E', 'V', static_cast<u8>(BASE_REVISION + 0xA));
|
||||
constexpr std::size_t MAX_MIX_BUFFERS = 24;
|
||||
constexpr std::size_t MAX_BIQUAD_FILTERS = 2;
|
||||
constexpr std::size_t MAX_CHANNEL_COUNT = 6;
|
||||
|
||||
@@ -57,4 +57,11 @@ requires std::is_integral_v<T>
|
||||
return static_cast<T>(1ULL << ((8U * sizeof(T)) - std::countl_zero(value - 1U)));
|
||||
}
|
||||
|
||||
template <size_t bit_index, typename T>
|
||||
requires std::is_integral_v<T>
|
||||
[[nodiscard]] constexpr bool Bit(const T value) {
|
||||
static_assert(bit_index < BitSize<T>(), "bit_index must be smaller than size of T");
|
||||
return ((value >> bit_index) & T(1)) == T(1);
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -16,6 +16,10 @@ std::u8string BufferToU8String(std::span<const u8> buffer) {
|
||||
return std::u8string{buffer.begin(), std::ranges::find(buffer, u8{0})};
|
||||
}
|
||||
|
||||
std::u8string_view BufferToU8StringView(std::span<const u8> buffer) {
|
||||
return std::u8string_view{reinterpret_cast<const char8_t*>(buffer.data())};
|
||||
}
|
||||
|
||||
std::string ToUTF8String(std::u8string_view u8_string) {
|
||||
return std::string{u8_string.begin(), u8_string.end()};
|
||||
}
|
||||
@@ -24,6 +28,10 @@ std::string BufferToUTF8String(std::span<const u8> buffer) {
|
||||
return std::string{buffer.begin(), std::ranges::find(buffer, u8{0})};
|
||||
}
|
||||
|
||||
std::string_view BufferToUTF8StringView(std::span<const u8> buffer) {
|
||||
return std::string_view{reinterpret_cast<const char*>(buffer.data())};
|
||||
}
|
||||
|
||||
std::string PathToUTF8String(const std::filesystem::path& path) {
|
||||
return ToUTF8String(path.u8string());
|
||||
}
|
||||
|
||||
@@ -37,6 +37,15 @@ concept IsChar = std::same_as<T, char>;
|
||||
*/
|
||||
[[nodiscard]] std::u8string BufferToU8String(std::span<const u8> buffer);
|
||||
|
||||
/**
|
||||
* Same as BufferToU8String, but returns a string view of the buffer.
|
||||
*
|
||||
* @param buffer Buffer of bytes
|
||||
*
|
||||
* @returns UTF-8 encoded std::u8string_view.
|
||||
*/
|
||||
[[nodiscard]] std::u8string_view BufferToU8StringView(std::span<const u8> buffer);
|
||||
|
||||
/**
|
||||
* Converts a std::u8string or std::u8string_view to a UTF-8 encoded std::string.
|
||||
*
|
||||
@@ -57,6 +66,15 @@ concept IsChar = std::same_as<T, char>;
|
||||
*/
|
||||
[[nodiscard]] std::string BufferToUTF8String(std::span<const u8> buffer);
|
||||
|
||||
/**
|
||||
* Same as BufferToUTF8String, but returns a string view of the buffer.
|
||||
*
|
||||
* @param buffer Buffer of bytes
|
||||
*
|
||||
* @returns UTF-8 encoded std::string_view.
|
||||
*/
|
||||
[[nodiscard]] std::string_view BufferToUTF8StringView(std::span<const u8> buffer);
|
||||
|
||||
/**
|
||||
* Converts a filesystem path to a UTF-8 encoded std::string.
|
||||
*
|
||||
|
||||
@@ -327,8 +327,8 @@ private:
|
||||
bool IsNiechePlaceholder(size_t virtual_offset, size_t length) const {
|
||||
const auto it = placeholders.upper_bound({virtual_offset, virtual_offset + length});
|
||||
if (it != placeholders.end() && it->lower() == virtual_offset + length) {
|
||||
const bool is_root = it == placeholders.begin() && virtual_offset == 0;
|
||||
return is_root || std::prev(it)->upper() == virtual_offset;
|
||||
return it == placeholders.begin() ? virtual_offset == 0
|
||||
: std::prev(it)->upper() == virtual_offset;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -218,19 +218,17 @@ private:
|
||||
Impl(const std::filesystem::path& file_backend_filename, const Filter& filter_)
|
||||
: filter{filter_}, file_backend{file_backend_filename} {}
|
||||
|
||||
~Impl() {
|
||||
StopBackendThread();
|
||||
}
|
||||
~Impl() = default;
|
||||
|
||||
void StartBackendThread() {
|
||||
backend_thread = std::thread([this] {
|
||||
backend_thread = std::jthread([this](std::stop_token stop_token) {
|
||||
Common::SetCurrentThreadName("yuzu:Log");
|
||||
Entry entry;
|
||||
const auto write_logs = [this, &entry]() {
|
||||
ForEachBackend([&entry](Backend& backend) { backend.Write(entry); });
|
||||
};
|
||||
while (!stop.stop_requested()) {
|
||||
entry = message_queue.PopWait(stop.get_token());
|
||||
while (!stop_token.stop_requested()) {
|
||||
entry = message_queue.PopWait(stop_token);
|
||||
if (entry.filename != nullptr) {
|
||||
write_logs();
|
||||
}
|
||||
@@ -244,11 +242,6 @@ private:
|
||||
});
|
||||
}
|
||||
|
||||
void StopBackendThread() {
|
||||
stop.request_stop();
|
||||
backend_thread.join();
|
||||
}
|
||||
|
||||
Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr,
|
||||
const char* function, std::string&& message) const {
|
||||
using std::chrono::duration_cast;
|
||||
@@ -283,10 +276,9 @@ private:
|
||||
ColorConsoleBackend color_console_backend{};
|
||||
FileBackend file_backend;
|
||||
|
||||
std::stop_source stop;
|
||||
std::thread backend_thread;
|
||||
MPSCQueue<Entry, true> message_queue{};
|
||||
std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()};
|
||||
std::jthread backend_thread;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
|
||||
@@ -108,6 +108,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
|
||||
SUB(Service, Migration) \
|
||||
SUB(Service, Mii) \
|
||||
SUB(Service, MM) \
|
||||
SUB(Service, MNPP) \
|
||||
SUB(Service, NCM) \
|
||||
SUB(Service, NFC) \
|
||||
SUB(Service, NFP) \
|
||||
|
||||
@@ -76,6 +76,7 @@ enum class Class : u8 {
|
||||
Service_Migration, ///< The migration service
|
||||
Service_Mii, ///< The Mii service
|
||||
Service_MM, ///< The MM (Multimedia) service
|
||||
Service_MNPP, ///< The MNPP service
|
||||
Service_NCM, ///< The NCM service
|
||||
Service_NFC, ///< The NFC (Near-field communication) service
|
||||
Service_NFP, ///< The NFP service
|
||||
|
||||
@@ -10,11 +10,65 @@ PageTable::PageTable() = default;
|
||||
|
||||
PageTable::~PageTable() noexcept = default;
|
||||
|
||||
void PageTable::Resize(size_t address_space_width_in_bits, size_t page_size_in_bits) {
|
||||
const size_t num_page_table_entries{1ULL << (address_space_width_in_bits - page_size_in_bits)};
|
||||
bool PageTable::BeginTraversal(TraversalEntry& out_entry, TraversalContext& out_context,
|
||||
u64 address) const {
|
||||
// Setup invalid defaults.
|
||||
out_entry.phys_addr = 0;
|
||||
out_entry.block_size = page_size;
|
||||
out_context.next_page = 0;
|
||||
|
||||
// Validate that we can read the actual entry.
|
||||
const auto page = address / page_size;
|
||||
if (page >= backing_addr.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate that the entry is mapped.
|
||||
const auto phys_addr = backing_addr[page];
|
||||
if (phys_addr == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Populate the results.
|
||||
out_entry.phys_addr = phys_addr + address;
|
||||
out_context.next_page = page + 1;
|
||||
out_context.next_offset = address + page_size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PageTable::ContinueTraversal(TraversalEntry& out_entry, TraversalContext& context) const {
|
||||
// Setup invalid defaults.
|
||||
out_entry.phys_addr = 0;
|
||||
out_entry.block_size = page_size;
|
||||
|
||||
// Validate that we can read the actual entry.
|
||||
const auto page = context.next_page;
|
||||
if (page >= backing_addr.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate that the entry is mapped.
|
||||
const auto phys_addr = backing_addr[page];
|
||||
if (phys_addr == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Populate the results.
|
||||
out_entry.phys_addr = phys_addr + context.next_offset;
|
||||
context.next_page = page + 1;
|
||||
context.next_offset += page_size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits) {
|
||||
const std::size_t num_page_table_entries{1ULL
|
||||
<< (address_space_width_in_bits - page_size_in_bits)};
|
||||
pointers.resize(num_page_table_entries);
|
||||
backing_addr.resize(num_page_table_entries);
|
||||
current_address_space_width_in_bits = address_space_width_in_bits;
|
||||
page_size = 1ULL << page_size_in_bits;
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
+20
-4
@@ -27,6 +27,16 @@ enum class PageType : u8 {
|
||||
* mimics the way a real CPU page table works.
|
||||
*/
|
||||
struct PageTable {
|
||||
struct TraversalEntry {
|
||||
u64 phys_addr{};
|
||||
std::size_t block_size{};
|
||||
};
|
||||
|
||||
struct TraversalContext {
|
||||
u64 next_page{};
|
||||
u64 next_offset{};
|
||||
};
|
||||
|
||||
/// Number of bits reserved for attribute tagging.
|
||||
/// This can be at most the guaranteed alignment of the pointers in the page table.
|
||||
static constexpr int ATTRIBUTE_BITS = 2;
|
||||
@@ -89,6 +99,10 @@ struct PageTable {
|
||||
PageTable(PageTable&&) noexcept = default;
|
||||
PageTable& operator=(PageTable&&) noexcept = default;
|
||||
|
||||
bool BeginTraversal(TraversalEntry& out_entry, TraversalContext& out_context,
|
||||
u64 address) const;
|
||||
bool ContinueTraversal(TraversalEntry& out_entry, TraversalContext& context) const;
|
||||
|
||||
/**
|
||||
* Resizes the page table to be able to accommodate enough pages within
|
||||
* a given address space.
|
||||
@@ -96,9 +110,9 @@ struct PageTable {
|
||||
* @param address_space_width_in_bits The address size width in bits.
|
||||
* @param page_size_in_bits The page size in bits.
|
||||
*/
|
||||
void Resize(size_t address_space_width_in_bits, size_t page_size_in_bits);
|
||||
void Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits);
|
||||
|
||||
size_t GetAddressSpaceBits() const {
|
||||
std::size_t GetAddressSpaceBits() const {
|
||||
return current_address_space_width_in_bits;
|
||||
}
|
||||
|
||||
@@ -110,9 +124,11 @@ struct PageTable {
|
||||
|
||||
VirtualBuffer<u64> backing_addr;
|
||||
|
||||
size_t current_address_space_width_in_bits;
|
||||
std::size_t current_address_space_width_in_bits{};
|
||||
|
||||
u8* fastmem_arena;
|
||||
u8* fastmem_arena{};
|
||||
|
||||
std::size_t page_size{};
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -167,6 +167,7 @@ void RestoreGlobalState(bool is_powered_on) {
|
||||
|
||||
// Core
|
||||
values.use_multi_core.SetGlobal(true);
|
||||
values.use_extended_memory_layout.SetGlobal(true);
|
||||
|
||||
// CPU
|
||||
values.cpu_accuracy.SetGlobal(true);
|
||||
@@ -175,6 +176,7 @@ void RestoreGlobalState(bool is_powered_on) {
|
||||
values.cpuopt_unsafe_ignore_standard_fpcr.SetGlobal(true);
|
||||
values.cpuopt_unsafe_inaccurate_nan.SetGlobal(true);
|
||||
values.cpuopt_unsafe_fastmem_check.SetGlobal(true);
|
||||
values.cpuopt_unsafe_ignore_global_monitor.SetGlobal(true);
|
||||
|
||||
// Renderer
|
||||
values.renderer_backend.SetGlobal(true);
|
||||
|
||||
@@ -466,6 +466,7 @@ struct Values {
|
||||
|
||||
// Core
|
||||
Setting<bool> use_multi_core{true, "use_multi_core"};
|
||||
Setting<bool> use_extended_memory_layout{false, "use_extended_memory_layout"};
|
||||
|
||||
// Cpu
|
||||
RangedSetting<CPUAccuracy> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto,
|
||||
@@ -483,12 +484,15 @@ struct Values {
|
||||
BasicSetting<bool> cpuopt_misc_ir{true, "cpuopt_misc_ir"};
|
||||
BasicSetting<bool> cpuopt_reduce_misalign_checks{true, "cpuopt_reduce_misalign_checks"};
|
||||
BasicSetting<bool> cpuopt_fastmem{true, "cpuopt_fastmem"};
|
||||
BasicSetting<bool> cpuopt_fastmem_exclusives{true, "cpuopt_fastmem_exclusives"};
|
||||
BasicSetting<bool> cpuopt_recompile_exclusives{true, "cpuopt_recompile_exclusives"};
|
||||
|
||||
Setting<bool> cpuopt_unsafe_unfuse_fma{true, "cpuopt_unsafe_unfuse_fma"};
|
||||
Setting<bool> cpuopt_unsafe_reduce_fp_error{true, "cpuopt_unsafe_reduce_fp_error"};
|
||||
Setting<bool> cpuopt_unsafe_ignore_standard_fpcr{true, "cpuopt_unsafe_ignore_standard_fpcr"};
|
||||
Setting<bool> cpuopt_unsafe_inaccurate_nan{true, "cpuopt_unsafe_inaccurate_nan"};
|
||||
Setting<bool> cpuopt_unsafe_fastmem_check{true, "cpuopt_unsafe_fastmem_check"};
|
||||
Setting<bool> cpuopt_unsafe_ignore_global_monitor{true, "cpuopt_unsafe_ignore_global_monitor"};
|
||||
|
||||
// Renderer
|
||||
RangedSetting<RendererBackend> renderer_backend{
|
||||
|
||||
+44
-16
@@ -55,22 +55,50 @@ void AppendBuildInfo(FieldCollection& fc) {
|
||||
|
||||
void AppendCPUInfo(FieldCollection& fc) {
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
fc.AddField(FieldType::UserSystem, "CPU_Model", Common::GetCPUCaps().cpu_string);
|
||||
fc.AddField(FieldType::UserSystem, "CPU_BrandString", Common::GetCPUCaps().brand_string);
|
||||
fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AES", Common::GetCPUCaps().aes);
|
||||
fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX", Common::GetCPUCaps().avx);
|
||||
fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX2", Common::GetCPUCaps().avx2);
|
||||
fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX512", Common::GetCPUCaps().avx512);
|
||||
fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_BMI1", Common::GetCPUCaps().bmi1);
|
||||
fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_BMI2", Common::GetCPUCaps().bmi2);
|
||||
fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_FMA", Common::GetCPUCaps().fma);
|
||||
fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_FMA4", Common::GetCPUCaps().fma4);
|
||||
fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE", Common::GetCPUCaps().sse);
|
||||
fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE2", Common::GetCPUCaps().sse2);
|
||||
fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE3", Common::GetCPUCaps().sse3);
|
||||
fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSSE3", Common::GetCPUCaps().ssse3);
|
||||
fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE41", Common::GetCPUCaps().sse4_1);
|
||||
fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE42", Common::GetCPUCaps().sse4_2);
|
||||
|
||||
const auto& caps = Common::GetCPUCaps();
|
||||
const auto add_field = [&fc](std::string_view field_name, const auto& field_value) {
|
||||
fc.AddField(FieldType::UserSystem, field_name, field_value);
|
||||
};
|
||||
add_field("CPU_Model", caps.cpu_string);
|
||||
add_field("CPU_BrandString", caps.brand_string);
|
||||
|
||||
add_field("CPU_Extension_x64_SSE", caps.sse);
|
||||
add_field("CPU_Extension_x64_SSE2", caps.sse2);
|
||||
add_field("CPU_Extension_x64_SSE3", caps.sse3);
|
||||
add_field("CPU_Extension_x64_SSSE3", caps.ssse3);
|
||||
add_field("CPU_Extension_x64_SSE41", caps.sse4_1);
|
||||
add_field("CPU_Extension_x64_SSE42", caps.sse4_2);
|
||||
|
||||
add_field("CPU_Extension_x64_AVX", caps.avx);
|
||||
add_field("CPU_Extension_x64_AVX_VNNI", caps.avx_vnni);
|
||||
add_field("CPU_Extension_x64_AVX2", caps.avx2);
|
||||
|
||||
// Skylake-X/SP level AVX512, for compatibility with the previous telemetry field
|
||||
add_field("CPU_Extension_x64_AVX512",
|
||||
caps.avx512f && caps.avx512cd && caps.avx512vl && caps.avx512dq && caps.avx512bw);
|
||||
|
||||
add_field("CPU_Extension_x64_AVX512F", caps.avx512f);
|
||||
add_field("CPU_Extension_x64_AVX512CD", caps.avx512cd);
|
||||
add_field("CPU_Extension_x64_AVX512VL", caps.avx512vl);
|
||||
add_field("CPU_Extension_x64_AVX512DQ", caps.avx512dq);
|
||||
add_field("CPU_Extension_x64_AVX512BW", caps.avx512bw);
|
||||
add_field("CPU_Extension_x64_AVX512BITALG", caps.avx512bitalg);
|
||||
add_field("CPU_Extension_x64_AVX512VBMI", caps.avx512vbmi);
|
||||
|
||||
add_field("CPU_Extension_x64_AES", caps.aes);
|
||||
add_field("CPU_Extension_x64_BMI1", caps.bmi1);
|
||||
add_field("CPU_Extension_x64_BMI2", caps.bmi2);
|
||||
add_field("CPU_Extension_x64_F16C", caps.f16c);
|
||||
add_field("CPU_Extension_x64_FMA", caps.fma);
|
||||
add_field("CPU_Extension_x64_FMA4", caps.fma4);
|
||||
add_field("CPU_Extension_x64_GFNI", caps.gfni);
|
||||
add_field("CPU_Extension_x64_INVARIANT_TSC", caps.invariant_tsc);
|
||||
add_field("CPU_Extension_x64_LZCNT", caps.lzcnt);
|
||||
add_field("CPU_Extension_x64_MOVBE", caps.movbe);
|
||||
add_field("CPU_Extension_x64_PCLMULQDQ", caps.pclmulqdq);
|
||||
add_field("CPU_Extension_x64_POPCNT", caps.popcnt);
|
||||
add_field("CPU_Extension_x64_SHA", caps.sha);
|
||||
#else
|
||||
fc.AddField(FieldType::UserSystem, "CPU_Model", "Other");
|
||||
#endif
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
@@ -55,8 +56,8 @@ class Field : public FieldInterface {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(Field);
|
||||
|
||||
Field(FieldType type_, std::string name_, T value_)
|
||||
: name(std::move(name_)), type(type_), value(std::move(value_)) {}
|
||||
Field(FieldType type_, std::string_view name_, T value_)
|
||||
: name(name_), type(type_), value(std::move(value_)) {}
|
||||
|
||||
~Field() override = default;
|
||||
|
||||
@@ -123,7 +124,7 @@ public:
|
||||
* @param value Value for the field to add.
|
||||
*/
|
||||
template <typename T>
|
||||
void AddField(FieldType type, const char* name, T value) {
|
||||
void AddField(FieldType type, std::string_view name, T value) {
|
||||
return AddField(std::make_unique<Field<T>>(type, name, std::move(value)));
|
||||
}
|
||||
|
||||
@@ -171,6 +172,9 @@ struct VisitorInterface {
|
||||
struct NullVisitor final : public VisitorInterface {
|
||||
YUZU_NON_COPYABLE(NullVisitor);
|
||||
|
||||
NullVisitor() = default;
|
||||
~NullVisitor() override = default;
|
||||
|
||||
void Visit(const Field<bool>& /*field*/) override {}
|
||||
void Visit(const Field<double>& /*field*/) override {}
|
||||
void Visit(const Field<float>& /*field*/) override {}
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
// Copyright 2013 Dolphin Emulator Project / 2015 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
// Copyright 2013 Dolphin Emulator Project / 2015 Citra Emulator Project / 2022 Yuzu Emulator
|
||||
// Project Licensed under GPLv2 or any later version Refer to the license.txt file included.
|
||||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <iterator>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
#include "common/bit_util.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/x64/cpu_detect.h"
|
||||
|
||||
@@ -17,7 +21,7 @@
|
||||
// clang-format on
|
||||
#endif
|
||||
|
||||
static inline void __cpuidex(int info[4], int function_id, int subfunction_id) {
|
||||
static inline void __cpuidex(int info[4], u32 function_id, u32 subfunction_id) {
|
||||
#if defined(__DragonFly__) || defined(__FreeBSD__)
|
||||
// Despite the name, this is just do_cpuid() with ECX as second input.
|
||||
cpuid_count((u_int)function_id, (u_int)subfunction_id, (u_int*)info);
|
||||
@@ -30,7 +34,7 @@ static inline void __cpuidex(int info[4], int function_id, int subfunction_id) {
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __cpuid(int info[4], int function_id) {
|
||||
static inline void __cpuid(int info[4], u32 function_id) {
|
||||
return __cpuidex(info, function_id, 0);
|
||||
}
|
||||
|
||||
@@ -45,6 +49,17 @@ static inline u64 _xgetbv(u32 index) {
|
||||
|
||||
namespace Common {
|
||||
|
||||
CPUCaps::Manufacturer CPUCaps::ParseManufacturer(std::string_view brand_string) {
|
||||
if (brand_string == "GenuineIntel") {
|
||||
return Manufacturer::Intel;
|
||||
} else if (brand_string == "AuthenticAMD") {
|
||||
return Manufacturer::AMD;
|
||||
} else if (brand_string == "HygonGenuine") {
|
||||
return Manufacturer::Hygon;
|
||||
}
|
||||
return Manufacturer::Unknown;
|
||||
}
|
||||
|
||||
// Detects the various CPU features
|
||||
static CPUCaps Detect() {
|
||||
CPUCaps caps = {};
|
||||
@@ -53,75 +68,74 @@ static CPUCaps Detect() {
|
||||
// yuzu at all anyway
|
||||
|
||||
int cpu_id[4];
|
||||
memset(caps.brand_string, 0, sizeof(caps.brand_string));
|
||||
|
||||
// Detect CPU's CPUID capabilities and grab CPU string
|
||||
// Detect CPU's CPUID capabilities and grab manufacturer string
|
||||
__cpuid(cpu_id, 0x00000000);
|
||||
u32 max_std_fn = cpu_id[0]; // EAX
|
||||
const u32 max_std_fn = cpu_id[0]; // EAX
|
||||
|
||||
std::memcpy(&caps.brand_string[0], &cpu_id[1], sizeof(int));
|
||||
std::memcpy(&caps.brand_string[4], &cpu_id[3], sizeof(int));
|
||||
std::memcpy(&caps.brand_string[8], &cpu_id[2], sizeof(int));
|
||||
if (cpu_id[1] == 0x756e6547 && cpu_id[2] == 0x6c65746e && cpu_id[3] == 0x49656e69)
|
||||
caps.manufacturer = Manufacturer::Intel;
|
||||
else if (cpu_id[1] == 0x68747541 && cpu_id[2] == 0x444d4163 && cpu_id[3] == 0x69746e65)
|
||||
caps.manufacturer = Manufacturer::AMD;
|
||||
else if (cpu_id[1] == 0x6f677948 && cpu_id[2] == 0x656e6975 && cpu_id[3] == 0x6e65476e)
|
||||
caps.manufacturer = Manufacturer::Hygon;
|
||||
else
|
||||
caps.manufacturer = Manufacturer::Unknown;
|
||||
std::memset(caps.brand_string, 0, std::size(caps.brand_string));
|
||||
std::memcpy(&caps.brand_string[0], &cpu_id[1], sizeof(u32));
|
||||
std::memcpy(&caps.brand_string[4], &cpu_id[3], sizeof(u32));
|
||||
std::memcpy(&caps.brand_string[8], &cpu_id[2], sizeof(u32));
|
||||
|
||||
caps.manufacturer = CPUCaps::ParseManufacturer(caps.brand_string);
|
||||
|
||||
// Set reasonable default cpu string even if brand string not available
|
||||
std::strncpy(caps.cpu_string, caps.brand_string, std::size(caps.brand_string));
|
||||
|
||||
__cpuid(cpu_id, 0x80000000);
|
||||
|
||||
u32 max_ex_fn = cpu_id[0];
|
||||
|
||||
// Set reasonable default brand string even if brand string not available
|
||||
strcpy(caps.cpu_string, caps.brand_string);
|
||||
const u32 max_ex_fn = cpu_id[0];
|
||||
|
||||
// Detect family and other miscellaneous features
|
||||
if (max_std_fn >= 1) {
|
||||
__cpuid(cpu_id, 0x00000001);
|
||||
if ((cpu_id[3] >> 25) & 1)
|
||||
caps.sse = true;
|
||||
if ((cpu_id[3] >> 26) & 1)
|
||||
caps.sse2 = true;
|
||||
if ((cpu_id[2]) & 1)
|
||||
caps.sse3 = true;
|
||||
if ((cpu_id[2] >> 9) & 1)
|
||||
caps.ssse3 = true;
|
||||
if ((cpu_id[2] >> 19) & 1)
|
||||
caps.sse4_1 = true;
|
||||
if ((cpu_id[2] >> 20) & 1)
|
||||
caps.sse4_2 = true;
|
||||
if ((cpu_id[2] >> 25) & 1)
|
||||
caps.aes = true;
|
||||
caps.sse = Common::Bit<25>(cpu_id[3]);
|
||||
caps.sse2 = Common::Bit<26>(cpu_id[3]);
|
||||
caps.sse3 = Common::Bit<0>(cpu_id[2]);
|
||||
caps.pclmulqdq = Common::Bit<1>(cpu_id[2]);
|
||||
caps.ssse3 = Common::Bit<9>(cpu_id[2]);
|
||||
caps.sse4_1 = Common::Bit<19>(cpu_id[2]);
|
||||
caps.sse4_2 = Common::Bit<20>(cpu_id[2]);
|
||||
caps.movbe = Common::Bit<22>(cpu_id[2]);
|
||||
caps.popcnt = Common::Bit<23>(cpu_id[2]);
|
||||
caps.aes = Common::Bit<25>(cpu_id[2]);
|
||||
caps.f16c = Common::Bit<29>(cpu_id[2]);
|
||||
|
||||
// AVX support requires 3 separate checks:
|
||||
// - Is the AVX bit set in CPUID?
|
||||
// - Is the XSAVE bit set in CPUID?
|
||||
// - XGETBV result has the XCR bit set.
|
||||
if (((cpu_id[2] >> 28) & 1) && ((cpu_id[2] >> 27) & 1)) {
|
||||
if (Common::Bit<28>(cpu_id[2]) && Common::Bit<27>(cpu_id[2])) {
|
||||
if ((_xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) {
|
||||
caps.avx = true;
|
||||
if ((cpu_id[2] >> 12) & 1)
|
||||
if (Common::Bit<12>(cpu_id[2]))
|
||||
caps.fma = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (max_std_fn >= 7) {
|
||||
__cpuidex(cpu_id, 0x00000007, 0x00000000);
|
||||
// Can't enable AVX2 unless the XSAVE/XGETBV checks above passed
|
||||
if ((cpu_id[1] >> 5) & 1)
|
||||
caps.avx2 = caps.avx;
|
||||
if ((cpu_id[1] >> 3) & 1)
|
||||
caps.bmi1 = true;
|
||||
if ((cpu_id[1] >> 8) & 1)
|
||||
caps.bmi2 = true;
|
||||
// Checks for AVX512F, AVX512CD, AVX512VL, AVX512DQ, AVX512BW (Intel Skylake-X/SP)
|
||||
if ((cpu_id[1] >> 16) & 1 && (cpu_id[1] >> 28) & 1 && (cpu_id[1] >> 31) & 1 &&
|
||||
(cpu_id[1] >> 17) & 1 && (cpu_id[1] >> 30) & 1) {
|
||||
caps.avx512 = caps.avx2;
|
||||
// Can't enable AVX{2,512} unless the XSAVE/XGETBV checks above passed
|
||||
if (caps.avx) {
|
||||
caps.avx2 = Common::Bit<5>(cpu_id[1]);
|
||||
caps.avx512f = Common::Bit<16>(cpu_id[1]);
|
||||
caps.avx512dq = Common::Bit<17>(cpu_id[1]);
|
||||
caps.avx512cd = Common::Bit<28>(cpu_id[1]);
|
||||
caps.avx512bw = Common::Bit<30>(cpu_id[1]);
|
||||
caps.avx512vl = Common::Bit<31>(cpu_id[1]);
|
||||
caps.avx512vbmi = Common::Bit<1>(cpu_id[2]);
|
||||
caps.avx512bitalg = Common::Bit<12>(cpu_id[2]);
|
||||
}
|
||||
|
||||
caps.bmi1 = Common::Bit<3>(cpu_id[1]);
|
||||
caps.bmi2 = Common::Bit<8>(cpu_id[1]);
|
||||
caps.sha = Common::Bit<29>(cpu_id[1]);
|
||||
|
||||
caps.gfni = Common::Bit<8>(cpu_id[2]);
|
||||
|
||||
__cpuidex(cpu_id, 0x00000007, 0x00000001);
|
||||
caps.avx_vnni = caps.avx && Common::Bit<4>(cpu_id[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,15 +152,13 @@ static CPUCaps Detect() {
|
||||
if (max_ex_fn >= 0x80000001) {
|
||||
// Check for more features
|
||||
__cpuid(cpu_id, 0x80000001);
|
||||
if ((cpu_id[2] >> 16) & 1)
|
||||
caps.fma4 = true;
|
||||
caps.lzcnt = Common::Bit<5>(cpu_id[2]);
|
||||
caps.fma4 = Common::Bit<16>(cpu_id[2]);
|
||||
}
|
||||
|
||||
if (max_ex_fn >= 0x80000007) {
|
||||
__cpuid(cpu_id, 0x80000007);
|
||||
if (cpu_id[3] & (1 << 8)) {
|
||||
caps.invariant_tsc = true;
|
||||
}
|
||||
caps.invariant_tsc = Common::Bit<8>(cpu_id[3]);
|
||||
}
|
||||
|
||||
if (max_std_fn >= 0x16) {
|
||||
|
||||
+51
-28
@@ -1,42 +1,65 @@
|
||||
// Copyright 2013 Dolphin Emulator Project / 2015 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
// Copyright 2013 Dolphin Emulator Project / 2015 Citra Emulator Project / 2022 Yuzu Emulator
|
||||
// Project Project Licensed under GPLv2 or any later version Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace Common {
|
||||
#include <string_view>
|
||||
#include "common/common_types.h"
|
||||
|
||||
enum class Manufacturer : u32 {
|
||||
Intel = 0,
|
||||
AMD = 1,
|
||||
Hygon = 2,
|
||||
Unknown = 3,
|
||||
};
|
||||
namespace Common {
|
||||
|
||||
/// x86/x64 CPU capabilities that may be detected by this module
|
||||
struct CPUCaps {
|
||||
|
||||
enum class Manufacturer : u8 {
|
||||
Unknown = 0,
|
||||
Intel = 1,
|
||||
AMD = 2,
|
||||
Hygon = 3,
|
||||
};
|
||||
|
||||
static Manufacturer ParseManufacturer(std::string_view brand_string);
|
||||
|
||||
Manufacturer manufacturer;
|
||||
char cpu_string[0x21];
|
||||
char brand_string[0x41];
|
||||
bool sse;
|
||||
bool sse2;
|
||||
bool sse3;
|
||||
bool ssse3;
|
||||
bool sse4_1;
|
||||
bool sse4_2;
|
||||
bool lzcnt;
|
||||
bool avx;
|
||||
bool avx2;
|
||||
bool avx512;
|
||||
bool bmi1;
|
||||
bool bmi2;
|
||||
bool fma;
|
||||
bool fma4;
|
||||
bool aes;
|
||||
bool invariant_tsc;
|
||||
char brand_string[13];
|
||||
|
||||
char cpu_string[48];
|
||||
|
||||
u32 base_frequency;
|
||||
u32 max_frequency;
|
||||
u32 bus_frequency;
|
||||
|
||||
bool sse : 1;
|
||||
bool sse2 : 1;
|
||||
bool sse3 : 1;
|
||||
bool ssse3 : 1;
|
||||
bool sse4_1 : 1;
|
||||
bool sse4_2 : 1;
|
||||
|
||||
bool avx : 1;
|
||||
bool avx_vnni : 1;
|
||||
bool avx2 : 1;
|
||||
bool avx512f : 1;
|
||||
bool avx512dq : 1;
|
||||
bool avx512cd : 1;
|
||||
bool avx512bw : 1;
|
||||
bool avx512vl : 1;
|
||||
bool avx512vbmi : 1;
|
||||
bool avx512bitalg : 1;
|
||||
|
||||
bool aes : 1;
|
||||
bool bmi1 : 1;
|
||||
bool bmi2 : 1;
|
||||
bool f16c : 1;
|
||||
bool fma : 1;
|
||||
bool fma4 : 1;
|
||||
bool gfni : 1;
|
||||
bool invariant_tsc : 1;
|
||||
bool lzcnt : 1;
|
||||
bool movbe : 1;
|
||||
bool pclmulqdq : 1;
|
||||
bool popcnt : 1;
|
||||
bool sha : 1;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -152,6 +152,7 @@ add_library(core STATIC
|
||||
hle/api_version.h
|
||||
hle/ipc.h
|
||||
hle/ipc_helpers.h
|
||||
hle/kernel/board/nintendo/nx/k_memory_layout.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
|
||||
@@ -164,6 +165,7 @@ add_library(core STATIC
|
||||
hle/kernel/hle_ipc.h
|
||||
hle/kernel/init/init_slab_setup.cpp
|
||||
hle/kernel/init/init_slab_setup.h
|
||||
hle/kernel/initial_process.h
|
||||
hle/kernel/k_address_arbiter.cpp
|
||||
hle/kernel/k_address_arbiter.h
|
||||
hle/kernel/k_address_space_info.cpp
|
||||
@@ -467,6 +469,8 @@ add_library(core STATIC
|
||||
hle/service/mii/types.h
|
||||
hle/service/mm/mm_u.cpp
|
||||
hle/service/mm/mm_u.h
|
||||
hle/service/mnpp/mnpp_app.cpp
|
||||
hle/service/mnpp/mnpp_app.h
|
||||
hle/service/ncm/ncm.cpp
|
||||
hle/service/ncm/ncm.h
|
||||
hle/service/nfc/nfc.cpp
|
||||
|
||||
@@ -137,6 +137,8 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
|
||||
config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS;
|
||||
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
|
||||
config.only_detect_misalignment_via_page_table_on_page_boundary = true;
|
||||
config.fastmem_exclusive_access = true;
|
||||
config.recompile_on_exclusive_fastmem_failure = true;
|
||||
|
||||
// Multi-process state
|
||||
config.processor_id = core_index;
|
||||
@@ -146,8 +148,8 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
|
||||
config.wall_clock_cntpct = uses_wall_clock;
|
||||
|
||||
// Code cache size
|
||||
config.code_cache_size = 512_MiB;
|
||||
config.far_code_offset = 400_MiB;
|
||||
config.code_cache_size = 128_MiB;
|
||||
config.far_code_offset = 100_MiB;
|
||||
|
||||
// Safe optimizations
|
||||
if (Settings::values.cpu_debug_mode) {
|
||||
@@ -178,6 +180,12 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
|
||||
if (!Settings::values.cpuopt_fastmem) {
|
||||
config.fastmem_pointer = nullptr;
|
||||
}
|
||||
if (!Settings::values.cpuopt_fastmem_exclusives) {
|
||||
config.fastmem_exclusive_access = false;
|
||||
}
|
||||
if (!Settings::values.cpuopt_recompile_exclusives) {
|
||||
config.recompile_on_exclusive_fastmem_failure = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Unsafe optimizations
|
||||
@@ -195,6 +203,9 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
|
||||
if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
|
||||
}
|
||||
if (Settings::values.cpuopt_unsafe_ignore_global_monitor) {
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
|
||||
}
|
||||
}
|
||||
|
||||
// Curated optimizations
|
||||
@@ -203,6 +214,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
|
||||
}
|
||||
|
||||
return std::make_unique<Dynarmic::A32::Jit>(config);
|
||||
|
||||
@@ -185,6 +185,9 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
|
||||
config.fastmem_pointer = page_table->fastmem_arena;
|
||||
config.fastmem_address_space_bits = address_space_bits;
|
||||
config.silently_mirror_fastmem = false;
|
||||
|
||||
config.fastmem_exclusive_access = true;
|
||||
config.recompile_on_exclusive_fastmem_failure = true;
|
||||
}
|
||||
|
||||
// Multi-process state
|
||||
@@ -205,8 +208,8 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
|
||||
config.wall_clock_cntpct = uses_wall_clock;
|
||||
|
||||
// Code cache size
|
||||
config.code_cache_size = 512_MiB;
|
||||
config.far_code_offset = 400_MiB;
|
||||
config.code_cache_size = 128_MiB;
|
||||
config.far_code_offset = 100_MiB;
|
||||
|
||||
// Safe optimizations
|
||||
if (Settings::values.cpu_debug_mode) {
|
||||
@@ -237,6 +240,12 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
|
||||
if (!Settings::values.cpuopt_fastmem) {
|
||||
config.fastmem_pointer = nullptr;
|
||||
}
|
||||
if (!Settings::values.cpuopt_fastmem_exclusives) {
|
||||
config.fastmem_exclusive_access = false;
|
||||
}
|
||||
if (!Settings::values.cpuopt_recompile_exclusives) {
|
||||
config.recompile_on_exclusive_fastmem_failure = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Unsafe optimizations
|
||||
@@ -254,6 +263,9 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
|
||||
if (Settings::values.cpuopt_unsafe_fastmem_check) {
|
||||
config.fastmem_address_space_bits = 64;
|
||||
}
|
||||
if (Settings::values.cpuopt_unsafe_ignore_global_monitor) {
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
|
||||
}
|
||||
}
|
||||
|
||||
// Curated optimizations
|
||||
@@ -262,6 +274,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
|
||||
config.fastmem_address_space_bits = 64;
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
|
||||
}
|
||||
|
||||
return std::make_shared<Dynarmic::A64::Jit>(config);
|
||||
|
||||
@@ -37,8 +37,8 @@ u128 DynarmicExclusiveMonitor::ExclusiveRead128(std::size_t core_index, VAddr ad
|
||||
});
|
||||
}
|
||||
|
||||
void DynarmicExclusiveMonitor::ClearExclusive() {
|
||||
monitor.Clear();
|
||||
void DynarmicExclusiveMonitor::ClearExclusive(std::size_t core_index) {
|
||||
monitor.ClearProcessor(core_index);
|
||||
}
|
||||
|
||||
bool DynarmicExclusiveMonitor::ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) {
|
||||
|
||||
@@ -29,7 +29,7 @@ public:
|
||||
u32 ExclusiveRead32(std::size_t core_index, VAddr addr) override;
|
||||
u64 ExclusiveRead64(std::size_t core_index, VAddr addr) override;
|
||||
u128 ExclusiveRead128(std::size_t core_index, VAddr addr) override;
|
||||
void ClearExclusive() override;
|
||||
void ClearExclusive(std::size_t core_index) override;
|
||||
|
||||
bool ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) override;
|
||||
bool ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) override;
|
||||
|
||||
@@ -23,7 +23,7 @@ public:
|
||||
virtual u32 ExclusiveRead32(std::size_t core_index, VAddr addr) = 0;
|
||||
virtual u64 ExclusiveRead64(std::size_t core_index, VAddr addr) = 0;
|
||||
virtual u128 ExclusiveRead128(std::size_t core_index, VAddr addr) = 0;
|
||||
virtual void ClearExclusive() = 0;
|
||||
virtual void ClearExclusive(std::size_t core_index) = 0;
|
||||
|
||||
virtual bool ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) = 0;
|
||||
virtual bool ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) = 0;
|
||||
|
||||
+13
-2
@@ -28,7 +28,9 @@
|
||||
#include "core/file_sys/vfs_real.h"
|
||||
#include "core/hardware_interrupt_manager.h"
|
||||
#include "core/hid/hid_core.h"
|
||||
#include "core/hle/kernel/k_memory_manager.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_resource_limit.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/physical_core.h"
|
||||
@@ -252,9 +254,16 @@ struct System::Impl {
|
||||
}
|
||||
|
||||
telemetry_session->AddInitialInfo(*app_loader, fs_controller, *content_provider);
|
||||
|
||||
// Create a resource limit for the process.
|
||||
const auto physical_memory_size =
|
||||
kernel.MemoryManager().GetSize(Kernel::KMemoryManager::Pool::Application);
|
||||
auto* resource_limit = Kernel::CreateResourceLimitForProcess(system, physical_memory_size);
|
||||
|
||||
// Create the process.
|
||||
auto main_process = Kernel::KProcess::Create(system.Kernel());
|
||||
ASSERT(Kernel::KProcess::Initialize(main_process, system, "main",
|
||||
Kernel::KProcess::ProcessType::Userland)
|
||||
Kernel::KProcess::ProcessType::Userland, resource_limit)
|
||||
.IsSuccess());
|
||||
const auto [load_result, load_parameters] = app_loader->Load(*main_process, system);
|
||||
if (load_result != Loader::ResultStatus::Success) {
|
||||
@@ -317,7 +326,9 @@ struct System::Impl {
|
||||
is_powered_on = false;
|
||||
exit_lock = false;
|
||||
|
||||
gpu_core->NotifyShutdown();
|
||||
if (gpu_core != nullptr) {
|
||||
gpu_core->NotifyShutdown();
|
||||
}
|
||||
|
||||
services.reset();
|
||||
service_manager.reset();
|
||||
|
||||
@@ -3,10 +3,13 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/device_memory.h"
|
||||
#include "hle/kernel/board/nintendo/nx/k_system_control.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
DeviceMemory::DeviceMemory() : buffer{DramMemoryMap::Size, 1ULL << 39} {}
|
||||
DeviceMemory::DeviceMemory()
|
||||
: buffer{Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize(),
|
||||
1ULL << 39} {}
|
||||
DeviceMemory::~DeviceMemory() = default;
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -12,12 +12,8 @@ namespace Core {
|
||||
namespace DramMemoryMap {
|
||||
enum : u64 {
|
||||
Base = 0x80000000ULL,
|
||||
Size = 0x100000000ULL,
|
||||
End = Base + Size,
|
||||
KernelReserveBase = Base + 0x60000,
|
||||
SlabHeapBase = KernelReserveBase + 0x85000,
|
||||
SlapHeapSize = 0xa21000,
|
||||
SlabHeapEnd = SlabHeapBase + SlapHeapSize,
|
||||
};
|
||||
}; // namespace DramMemoryMap
|
||||
|
||||
|
||||
@@ -128,15 +128,6 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
||||
if (exefs == nullptr)
|
||||
return exefs;
|
||||
|
||||
if (Settings::values.dump_exefs) {
|
||||
LOG_INFO(Loader, "Dumping ExeFS for title_id={:016X}", title_id);
|
||||
const auto dump_dir = fs_controller.GetModificationDumpRoot(title_id);
|
||||
if (dump_dir != nullptr) {
|
||||
const auto exefs_dir = GetOrCreateDirectoryRelative(dump_dir, "/exefs");
|
||||
VfsRawCopyD(exefs, exefs_dir);
|
||||
}
|
||||
}
|
||||
|
||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
||||
const auto update_disabled =
|
||||
std::find(disabled.cbegin(), disabled.cend(), "Update") != disabled.cend();
|
||||
@@ -179,6 +170,15 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
||||
}
|
||||
}
|
||||
|
||||
if (Settings::values.dump_exefs) {
|
||||
LOG_INFO(Loader, "Dumping ExeFS for title_id={:016X}", title_id);
|
||||
const auto dump_dir = fs_controller.GetModificationDumpRoot(title_id);
|
||||
if (dump_dir != nullptr) {
|
||||
const auto exefs_dir = GetOrCreateDirectoryRelative(dump_dir, "/exefs");
|
||||
VfsRawCopyD(exefs, exefs_dir);
|
||||
}
|
||||
}
|
||||
|
||||
return exefs;
|
||||
}
|
||||
|
||||
|
||||
@@ -42,11 +42,20 @@ public:
|
||||
context.MakeCurrent();
|
||||
}
|
||||
~Scoped() {
|
||||
context.DoneCurrent();
|
||||
if (active) {
|
||||
context.DoneCurrent();
|
||||
}
|
||||
}
|
||||
|
||||
/// In the event that context was destroyed before the Scoped is destroyed, this provides a
|
||||
/// mechanism to prevent calling a destroyed object's method during the deconstructor
|
||||
void Cancel() {
|
||||
active = false;
|
||||
}
|
||||
|
||||
private:
|
||||
GraphicsContext& context;
|
||||
bool active{true};
|
||||
};
|
||||
|
||||
/// Calls MakeCurrent on the context and calls DoneCurrent when the scope for the returned value
|
||||
|
||||
@@ -885,6 +885,12 @@ bool EmulatedController::TestVibration(std::size_t device_index) {
|
||||
return SetVibration(device_index, DEFAULT_VIBRATION_VALUE);
|
||||
}
|
||||
|
||||
bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) {
|
||||
LOG_INFO(Service_HID, "Set polling mode {}", polling_mode);
|
||||
auto& output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
|
||||
return output_device->SetPollingMode(polling_mode) == Common::Input::PollingError::None;
|
||||
}
|
||||
|
||||
void EmulatedController::SetLedPattern() {
|
||||
for (auto& device : output_devices) {
|
||||
if (!device) {
|
||||
|
||||
@@ -299,16 +299,23 @@ public:
|
||||
|
||||
/**
|
||||
* Sends a specific vibration to the output device
|
||||
* @return returns true if vibration had no errors
|
||||
* @return true if vibration had no errors
|
||||
*/
|
||||
bool SetVibration(std::size_t device_index, VibrationValue vibration);
|
||||
|
||||
/**
|
||||
* Sends a small vibration to the output device
|
||||
* @return returns true if SetVibration was successfull
|
||||
* @return true if SetVibration was successfull
|
||||
*/
|
||||
bool TestVibration(std::size_t device_index);
|
||||
|
||||
/**
|
||||
* Sets the desired data to be polled from a controller
|
||||
* @param polling_mode type of input desired buttons, gyro, nfc, ir, etc.
|
||||
* @return true if SetPollingMode was successfull
|
||||
*/
|
||||
bool SetPollingMode(Common::Input::PollingMode polling_mode);
|
||||
|
||||
/// Returns the led pattern corresponding to this emulated controller
|
||||
LedPattern GetLedPattern() const;
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
// Copyright 2022 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 {
|
||||
|
||||
constexpr inline PAddr MainMemoryAddress = 0x80000000;
|
||||
|
||||
} // namespace Kernel
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <random>
|
||||
|
||||
#include "common/literals.h"
|
||||
#include "common/settings.h"
|
||||
|
||||
#include "core/hle/kernel/board/nintendo/nx/k_system_control.h"
|
||||
#include "core/hle/kernel/board/nintendo/nx/secure_monitor.h"
|
||||
@@ -28,33 +29,20 @@ namespace {
|
||||
|
||||
using namespace Common::Literals;
|
||||
|
||||
u32 GetMemoryModeForInit() {
|
||||
return 0x01;
|
||||
}
|
||||
|
||||
u32 GetMemorySizeForInit() {
|
||||
return 0;
|
||||
return Settings::values.use_extended_memory_layout ? Smc::MemorySize_6GB : Smc::MemorySize_4GB;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
return Settings::values.use_extended_memory_layout ? Smc::MemoryArrangement_6GB
|
||||
: Smc::MemoryArrangement_4GB;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
size_t KSystemControl::Init::GetRealMemorySize() {
|
||||
return GetIntendedMemorySize();
|
||||
}
|
||||
|
||||
// Initialization.
|
||||
size_t KSystemControl::Init::GetIntendedMemorySize() {
|
||||
switch (GetMemorySizeForInit()) {
|
||||
@@ -69,7 +57,13 @@ size_t KSystemControl::Init::GetIntendedMemorySize() {
|
||||
}
|
||||
|
||||
PAddr KSystemControl::Init::GetKernelPhysicalBaseAddress(u64 base_address) {
|
||||
return base_address;
|
||||
const size_t real_dram_size = KSystemControl::Init::GetRealMemorySize();
|
||||
const size_t intended_dram_size = KSystemControl::Init::GetIntendedMemorySize();
|
||||
if (intended_dram_size * 2 < real_dram_size) {
|
||||
return base_address;
|
||||
} else {
|
||||
return base_address + ((real_dram_size - intended_dram_size) / 2);
|
||||
}
|
||||
}
|
||||
|
||||
bool KSystemControl::Init::ShouldIncreaseThreadResourceLimit() {
|
||||
|
||||
@@ -13,6 +13,7 @@ public:
|
||||
class Init {
|
||||
public:
|
||||
// Initialization.
|
||||
static std::size_t GetRealMemorySize();
|
||||
static std::size_t GetIntendedMemorySize();
|
||||
static PAddr GetKernelPhysicalBaseAddress(u64 base_address);
|
||||
static bool ShouldIncreaseThreadResourceLimit();
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
// Copyright 2022 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/literals.h"
|
||||
#include "core/hle/kernel/board/nintendo/nx/k_memory_layout.h"
|
||||
#include "core/hle/kernel/board/nintendo/nx/k_system_control.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
using namespace Common::Literals;
|
||||
|
||||
constexpr std::size_t InitialProcessBinarySizeMax = 12_MiB;
|
||||
|
||||
static inline PAddr GetInitialProcessBinaryPhysicalAddress() {
|
||||
return Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetKernelPhysicalBaseAddress(
|
||||
MainMemoryAddress);
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
@@ -49,7 +49,7 @@ bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 valu
|
||||
}
|
||||
} else {
|
||||
// Otherwise, clear our exclusive hold and finish
|
||||
monitor.ClearExclusive();
|
||||
monitor.ClearExclusive(current_core);
|
||||
}
|
||||
|
||||
// We're done.
|
||||
@@ -78,7 +78,7 @@ bool UpdateIfEqual(Core::System& system, s32* out, VAddr address, s32 value, s32
|
||||
}
|
||||
} else {
|
||||
// Otherwise, clear our exclusive hold and finish.
|
||||
monitor.ClearExclusive();
|
||||
monitor.ClearExclusive(current_core);
|
||||
}
|
||||
|
||||
// We're done.
|
||||
|
||||
@@ -14,7 +14,7 @@ KEvent::KEvent(KernelCore& kernel_)
|
||||
|
||||
KEvent::~KEvent() = default;
|
||||
|
||||
void KEvent::Initialize(std::string&& name_) {
|
||||
void KEvent::Initialize(std::string&& name_, KProcess* owner_) {
|
||||
// Increment reference count.
|
||||
// Because reference count is one on creation, this will result
|
||||
// in a reference count of two. Thus, when both readable and
|
||||
@@ -30,10 +30,8 @@ void KEvent::Initialize(std::string&& name_) {
|
||||
writable_event.Initialize(this, name_ + ":Writable");
|
||||
|
||||
// Set our owner process.
|
||||
owner = kernel.CurrentProcess();
|
||||
if (owner) {
|
||||
owner->Open();
|
||||
}
|
||||
owner = owner_;
|
||||
owner->Open();
|
||||
|
||||
// Mark initialized.
|
||||
name = std::move(name_);
|
||||
@@ -47,10 +45,8 @@ void KEvent::Finalize() {
|
||||
void KEvent::PostDestroy(uintptr_t arg) {
|
||||
// Release the event count resource the owner process holds.
|
||||
KProcess* owner = reinterpret_cast<KProcess*>(arg);
|
||||
if (owner) {
|
||||
owner->GetResourceLimit()->Release(LimitableResource::Events, 1);
|
||||
owner->Close();
|
||||
}
|
||||
owner->GetResourceLimit()->Release(LimitableResource::Events, 1);
|
||||
owner->Close();
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -22,7 +22,7 @@ public:
|
||||
explicit KEvent(KernelCore& kernel_);
|
||||
~KEvent() override;
|
||||
|
||||
void Initialize(std::string&& name);
|
||||
void Initialize(std::string&& name, KProcess* owner_);
|
||||
|
||||
void Finalize() override;
|
||||
|
||||
|
||||
@@ -173,6 +173,10 @@ public:
|
||||
return Dereference(FindVirtualLinear(address));
|
||||
}
|
||||
|
||||
const KMemoryRegion& GetPhysicalLinearRegion(PAddr address) const {
|
||||
return Dereference(FindPhysicalLinear(address));
|
||||
}
|
||||
|
||||
const KMemoryRegion* GetPhysicalKernelTraceBufferRegion() const {
|
||||
return GetPhysicalMemoryRegionTree().FindFirstDerived(KMemoryRegionType_KernelTraceBuffer);
|
||||
}
|
||||
|
||||
@@ -10,189 +10,412 @@
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/core.h"
|
||||
#include "core/device_memory.h"
|
||||
#include "core/hle/kernel/initial_process.h"
|
||||
#include "core/hle/kernel/k_memory_manager.h"
|
||||
#include "core/hle/kernel/k_page_linked_list.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/svc_results.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
KMemoryManager::KMemoryManager(Core::System& system_) : system{system_} {}
|
||||
namespace {
|
||||
|
||||
std::size_t KMemoryManager::Impl::Initialize(Pool new_pool, u64 start_address, u64 end_address) {
|
||||
const auto size{end_address - start_address};
|
||||
|
||||
// Calculate metadata sizes
|
||||
const auto ref_count_size{(size / PageSize) * sizeof(u16)};
|
||||
const auto optimize_map_size{(Common::AlignUp((size / PageSize), 64) / 64) * sizeof(u64)};
|
||||
const auto manager_size{Common::AlignUp(optimize_map_size + ref_count_size, PageSize)};
|
||||
const auto page_heap_size{KPageHeap::CalculateManagementOverheadSize(size)};
|
||||
const auto total_metadata_size{manager_size + page_heap_size};
|
||||
ASSERT(manager_size <= total_metadata_size);
|
||||
ASSERT(Common::IsAligned(total_metadata_size, PageSize));
|
||||
|
||||
// Setup region
|
||||
pool = new_pool;
|
||||
|
||||
// Initialize the manager's KPageHeap
|
||||
heap.Initialize(start_address, size, page_heap_size);
|
||||
|
||||
// Free the memory to the heap
|
||||
heap.Free(start_address, size / PageSize);
|
||||
|
||||
// Update the heap's used size
|
||||
heap.UpdateUsedSize();
|
||||
|
||||
return total_metadata_size;
|
||||
constexpr KMemoryManager::Pool GetPoolFromMemoryRegionType(u32 type) {
|
||||
if ((type | KMemoryRegionType_DramApplicationPool) == type) {
|
||||
return KMemoryManager::Pool::Application;
|
||||
} else if ((type | KMemoryRegionType_DramAppletPool) == type) {
|
||||
return KMemoryManager::Pool::Applet;
|
||||
} else if ((type | KMemoryRegionType_DramSystemPool) == type) {
|
||||
return KMemoryManager::Pool::System;
|
||||
} else if ((type | KMemoryRegionType_DramSystemNonSecurePool) == type) {
|
||||
return KMemoryManager::Pool::SystemNonSecure;
|
||||
} else {
|
||||
UNREACHABLE_MSG("InvalidMemoryRegionType for conversion to Pool");
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void KMemoryManager::InitializeManager(Pool pool, u64 start_address, u64 end_address) {
|
||||
ASSERT(pool < Pool::Count);
|
||||
managers[static_cast<std::size_t>(pool)].Initialize(pool, start_address, end_address);
|
||||
} // namespace
|
||||
|
||||
KMemoryManager::KMemoryManager(Core::System& system_)
|
||||
: system{system_}, pool_locks{
|
||||
KLightLock{system_.Kernel()},
|
||||
KLightLock{system_.Kernel()},
|
||||
KLightLock{system_.Kernel()},
|
||||
KLightLock{system_.Kernel()},
|
||||
} {}
|
||||
|
||||
void KMemoryManager::Initialize(VAddr management_region, size_t management_region_size) {
|
||||
|
||||
// Clear the management region to zero.
|
||||
const VAddr management_region_end = management_region + management_region_size;
|
||||
|
||||
// Reset our manager count.
|
||||
num_managers = 0;
|
||||
|
||||
// Traverse the virtual memory layout tree, initializing each manager as appropriate.
|
||||
while (num_managers != MaxManagerCount) {
|
||||
// Locate the region that should initialize the current manager.
|
||||
PAddr region_address = 0;
|
||||
size_t region_size = 0;
|
||||
Pool region_pool = Pool::Count;
|
||||
for (const auto& it : system.Kernel().MemoryLayout().GetPhysicalMemoryRegionTree()) {
|
||||
// We only care about regions that we need to create managers for.
|
||||
if (!it.IsDerivedFrom(KMemoryRegionType_DramUserPool)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// We want to initialize the managers in order.
|
||||
if (it.GetAttributes() != num_managers) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const PAddr cur_start = it.GetAddress();
|
||||
const PAddr cur_end = it.GetEndAddress();
|
||||
|
||||
// Validate the region.
|
||||
ASSERT(cur_end != 0);
|
||||
ASSERT(cur_start != 0);
|
||||
ASSERT(it.GetSize() > 0);
|
||||
|
||||
// Update the region's extents.
|
||||
if (region_address == 0) {
|
||||
region_address = cur_start;
|
||||
region_size = it.GetSize();
|
||||
region_pool = GetPoolFromMemoryRegionType(it.GetType());
|
||||
} else {
|
||||
ASSERT(cur_start == region_address + region_size);
|
||||
|
||||
// Update the size.
|
||||
region_size = cur_end - region_address;
|
||||
ASSERT(GetPoolFromMemoryRegionType(it.GetType()) == region_pool);
|
||||
}
|
||||
}
|
||||
|
||||
// If we didn't find a region, we're done.
|
||||
if (region_size == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Initialize a new manager for the region.
|
||||
Impl* manager = std::addressof(managers[num_managers++]);
|
||||
ASSERT(num_managers <= managers.size());
|
||||
|
||||
const size_t cur_size = manager->Initialize(region_address, region_size, management_region,
|
||||
management_region_end, region_pool);
|
||||
management_region += cur_size;
|
||||
ASSERT(management_region <= management_region_end);
|
||||
|
||||
// Insert the manager into the pool list.
|
||||
const auto region_pool_index = static_cast<u32>(region_pool);
|
||||
if (pool_managers_tail[region_pool_index] == nullptr) {
|
||||
pool_managers_head[region_pool_index] = manager;
|
||||
} else {
|
||||
pool_managers_tail[region_pool_index]->SetNext(manager);
|
||||
manager->SetPrev(pool_managers_tail[region_pool_index]);
|
||||
}
|
||||
pool_managers_tail[region_pool_index] = manager;
|
||||
}
|
||||
|
||||
// Free each region to its corresponding heap.
|
||||
size_t reserved_sizes[MaxManagerCount] = {};
|
||||
const PAddr ini_start = GetInitialProcessBinaryPhysicalAddress();
|
||||
const PAddr ini_end = ini_start + InitialProcessBinarySizeMax;
|
||||
const PAddr ini_last = ini_end - 1;
|
||||
for (const auto& it : system.Kernel().MemoryLayout().GetPhysicalMemoryRegionTree()) {
|
||||
if (it.IsDerivedFrom(KMemoryRegionType_DramUserPool)) {
|
||||
// Get the manager for the region.
|
||||
auto index = it.GetAttributes();
|
||||
auto& manager = managers[index];
|
||||
|
||||
const PAddr cur_start = it.GetAddress();
|
||||
const PAddr cur_last = it.GetLastAddress();
|
||||
const PAddr cur_end = it.GetEndAddress();
|
||||
|
||||
if (cur_start <= ini_start && ini_last <= cur_last) {
|
||||
// Free memory before the ini to the heap.
|
||||
if (cur_start != ini_start) {
|
||||
manager.Free(cur_start, (ini_start - cur_start) / PageSize);
|
||||
}
|
||||
|
||||
// Open/reserve the ini memory.
|
||||
manager.OpenFirst(ini_start, InitialProcessBinarySizeMax / PageSize);
|
||||
reserved_sizes[it.GetAttributes()] += InitialProcessBinarySizeMax;
|
||||
|
||||
// Free memory after the ini to the heap.
|
||||
if (ini_last != cur_last) {
|
||||
ASSERT(cur_end != 0);
|
||||
manager.Free(ini_end, cur_end - ini_end);
|
||||
}
|
||||
} else {
|
||||
// Ensure there's no partial overlap with the ini image.
|
||||
if (cur_start <= ini_last) {
|
||||
ASSERT(cur_last < ini_start);
|
||||
} else {
|
||||
// Otherwise, check the region for general validity.
|
||||
ASSERT(cur_end != 0);
|
||||
}
|
||||
|
||||
// Free the memory to the heap.
|
||||
manager.Free(cur_start, it.GetSize() / PageSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update the used size for all managers.
|
||||
for (size_t i = 0; i < num_managers; ++i) {
|
||||
managers[i].SetInitialUsedHeapSize(reserved_sizes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
VAddr KMemoryManager::AllocateAndOpenContinuous(std::size_t num_pages, std::size_t align_pages,
|
||||
u32 option) {
|
||||
// Early return if we're allocating no pages
|
||||
PAddr KMemoryManager::AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option) {
|
||||
// Early return if we're allocating no pages.
|
||||
if (num_pages == 0) {
|
||||
return {};
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Lock the pool that we're allocating from
|
||||
// Lock the pool that we're allocating from.
|
||||
const auto [pool, dir] = DecodeOption(option);
|
||||
const auto pool_index{static_cast<std::size_t>(pool)};
|
||||
std::lock_guard lock{pool_locks[pool_index]};
|
||||
KScopedLightLock lk(pool_locks[static_cast<std::size_t>(pool)]);
|
||||
|
||||
// Choose a heap based on our page size request
|
||||
const s32 heap_index{KPageHeap::GetAlignedBlockIndex(num_pages, align_pages)};
|
||||
// Choose a heap based on our page size request.
|
||||
const s32 heap_index = KPageHeap::GetAlignedBlockIndex(num_pages, align_pages);
|
||||
|
||||
// Loop, trying to iterate from each block
|
||||
// TODO (bunnei): Support multiple managers
|
||||
Impl& chosen_manager{managers[pool_index]};
|
||||
VAddr allocated_block{chosen_manager.AllocateBlock(heap_index, false)};
|
||||
|
||||
// If we failed to allocate, quit now
|
||||
if (!allocated_block) {
|
||||
return {};
|
||||
// Loop, trying to iterate from each block.
|
||||
Impl* chosen_manager = nullptr;
|
||||
PAddr allocated_block = 0;
|
||||
for (chosen_manager = this->GetFirstManager(pool, dir); chosen_manager != nullptr;
|
||||
chosen_manager = this->GetNextManager(chosen_manager, dir)) {
|
||||
allocated_block = chosen_manager->AllocateBlock(heap_index, true);
|
||||
if (allocated_block != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If we allocated more than we need, free some
|
||||
const auto allocated_pages{KPageHeap::GetBlockNumPages(heap_index)};
|
||||
// If we failed to allocate, quit now.
|
||||
if (allocated_block == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If we allocated more than we need, free some.
|
||||
const size_t allocated_pages = KPageHeap::GetBlockNumPages(heap_index);
|
||||
if (allocated_pages > num_pages) {
|
||||
chosen_manager.Free(allocated_block + num_pages * PageSize, allocated_pages - num_pages);
|
||||
chosen_manager->Free(allocated_block + num_pages * PageSize, allocated_pages - num_pages);
|
||||
}
|
||||
|
||||
// Open the first reference to the pages.
|
||||
chosen_manager->OpenFirst(allocated_block, num_pages);
|
||||
|
||||
return allocated_block;
|
||||
}
|
||||
|
||||
ResultCode KMemoryManager::Allocate(KPageLinkedList& page_list, std::size_t num_pages, Pool pool,
|
||||
Direction dir, u32 heap_fill_value) {
|
||||
ASSERT(page_list.GetNumPages() == 0);
|
||||
ResultCode KMemoryManager::AllocatePageGroupImpl(KPageLinkedList* out, size_t num_pages, Pool pool,
|
||||
Direction dir, bool random) {
|
||||
// Choose a heap based on our page size request.
|
||||
const s32 heap_index = KPageHeap::GetBlockIndex(num_pages);
|
||||
R_UNLESS(0 <= heap_index, ResultOutOfMemory);
|
||||
|
||||
// Early return if we're allocating no pages
|
||||
if (num_pages == 0) {
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
// Lock the pool that we're allocating from
|
||||
const auto pool_index{static_cast<std::size_t>(pool)};
|
||||
std::lock_guard lock{pool_locks[pool_index]};
|
||||
|
||||
// Choose a heap based on our page size request
|
||||
const s32 heap_index{KPageHeap::GetBlockIndex(num_pages)};
|
||||
if (heap_index < 0) {
|
||||
return ResultOutOfMemory;
|
||||
}
|
||||
|
||||
// TODO (bunnei): Support multiple managers
|
||||
Impl& chosen_manager{managers[pool_index]};
|
||||
|
||||
// Ensure that we don't leave anything un-freed
|
||||
auto group_guard = detail::ScopeExit([&] {
|
||||
for (const auto& it : page_list.Nodes()) {
|
||||
const auto min_num_pages{std::min<size_t>(
|
||||
it.GetNumPages(), (chosen_manager.GetEndAddress() - it.GetAddress()) / PageSize)};
|
||||
chosen_manager.Free(it.GetAddress(), min_num_pages);
|
||||
// Ensure that we don't leave anything un-freed.
|
||||
auto group_guard = SCOPE_GUARD({
|
||||
for (const auto& it : out->Nodes()) {
|
||||
auto& manager = this->GetManager(system.Kernel().MemoryLayout(), it.GetAddress());
|
||||
const size_t num_pages_to_free =
|
||||
std::min(it.GetNumPages(), (manager.GetEndAddress() - it.GetAddress()) / PageSize);
|
||||
manager.Free(it.GetAddress(), num_pages_to_free);
|
||||
}
|
||||
});
|
||||
|
||||
// Keep allocating until we've allocated all our pages
|
||||
for (s32 index{heap_index}; index >= 0 && num_pages > 0; index--) {
|
||||
const auto pages_per_alloc{KPageHeap::GetBlockNumPages(index)};
|
||||
|
||||
while (num_pages >= pages_per_alloc) {
|
||||
// Allocate a block
|
||||
VAddr allocated_block{chosen_manager.AllocateBlock(index, false)};
|
||||
if (!allocated_block) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Safely add it to our group
|
||||
{
|
||||
auto block_guard = detail::ScopeExit(
|
||||
[&] { chosen_manager.Free(allocated_block, pages_per_alloc); });
|
||||
|
||||
if (const ResultCode result{page_list.AddBlock(allocated_block, pages_per_alloc)};
|
||||
result.IsError()) {
|
||||
return result;
|
||||
// Keep allocating until we've allocated all our pages.
|
||||
for (s32 index = heap_index; index >= 0 && num_pages > 0; index--) {
|
||||
const size_t pages_per_alloc = KPageHeap::GetBlockNumPages(index);
|
||||
for (Impl* cur_manager = this->GetFirstManager(pool, dir); cur_manager != nullptr;
|
||||
cur_manager = this->GetNextManager(cur_manager, dir)) {
|
||||
while (num_pages >= pages_per_alloc) {
|
||||
// Allocate a block.
|
||||
PAddr allocated_block = cur_manager->AllocateBlock(index, random);
|
||||
if (allocated_block == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
block_guard.Cancel();
|
||||
}
|
||||
// Safely add it to our group.
|
||||
{
|
||||
auto block_guard =
|
||||
SCOPE_GUARD({ cur_manager->Free(allocated_block, pages_per_alloc); });
|
||||
R_TRY(out->AddBlock(allocated_block, pages_per_alloc));
|
||||
block_guard.Cancel();
|
||||
}
|
||||
|
||||
num_pages -= pages_per_alloc;
|
||||
num_pages -= pages_per_alloc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clear allocated memory.
|
||||
for (const auto& it : page_list.Nodes()) {
|
||||
std::memset(system.DeviceMemory().GetPointer(it.GetAddress()), heap_fill_value,
|
||||
it.GetSize());
|
||||
}
|
||||
|
||||
// Only succeed if we allocated as many pages as we wanted
|
||||
if (num_pages) {
|
||||
return ResultOutOfMemory;
|
||||
}
|
||||
// Only succeed if we allocated as many pages as we wanted.
|
||||
R_UNLESS(num_pages == 0, ResultOutOfMemory);
|
||||
|
||||
// We succeeded!
|
||||
group_guard.Cancel();
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KMemoryManager::Free(KPageLinkedList& page_list, std::size_t num_pages, Pool pool,
|
||||
Direction dir, u32 heap_fill_value) {
|
||||
// Early return if we're freeing no pages
|
||||
if (!num_pages) {
|
||||
return ResultSuccess;
|
||||
}
|
||||
ResultCode KMemoryManager::AllocateAndOpen(KPageLinkedList* out, size_t num_pages, u32 option) {
|
||||
ASSERT(out != nullptr);
|
||||
ASSERT(out->GetNumPages() == 0);
|
||||
|
||||
// Lock the pool that we're freeing from
|
||||
const auto pool_index{static_cast<std::size_t>(pool)};
|
||||
std::lock_guard lock{pool_locks[pool_index]};
|
||||
// Early return if we're allocating no pages.
|
||||
R_SUCCEED_IF(num_pages == 0);
|
||||
|
||||
// TODO (bunnei): Support multiple managers
|
||||
Impl& chosen_manager{managers[pool_index]};
|
||||
// Lock the pool that we're allocating from.
|
||||
const auto [pool, dir] = DecodeOption(option);
|
||||
KScopedLightLock lk(pool_locks[static_cast<size_t>(pool)]);
|
||||
|
||||
// Free all of the pages
|
||||
for (const auto& it : page_list.Nodes()) {
|
||||
const auto min_num_pages{std::min<size_t>(
|
||||
it.GetNumPages(), (chosen_manager.GetEndAddress() - it.GetAddress()) / PageSize)};
|
||||
chosen_manager.Free(it.GetAddress(), min_num_pages);
|
||||
// Allocate the page group.
|
||||
R_TRY(this->AllocatePageGroupImpl(out, num_pages, pool, dir, false));
|
||||
|
||||
// Open the first reference to the pages.
|
||||
for (const auto& block : out->Nodes()) {
|
||||
PAddr cur_address = block.GetAddress();
|
||||
size_t remaining_pages = block.GetNumPages();
|
||||
while (remaining_pages > 0) {
|
||||
// Get the manager for the current address.
|
||||
auto& manager = this->GetManager(system.Kernel().MemoryLayout(), cur_address);
|
||||
|
||||
// Process part or all of the block.
|
||||
const size_t cur_pages =
|
||||
std::min(remaining_pages, manager.GetPageOffsetToEnd(cur_address));
|
||||
manager.OpenFirst(cur_address, cur_pages);
|
||||
|
||||
// Advance.
|
||||
cur_address += cur_pages * PageSize;
|
||||
remaining_pages -= cur_pages;
|
||||
}
|
||||
}
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
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 =
|
||||
ResultCode KMemoryManager::AllocateAndOpenForProcess(KPageLinkedList* out, size_t num_pages,
|
||||
u32 option, u64 process_id, u8 fill_pattern) {
|
||||
ASSERT(out != nullptr);
|
||||
ASSERT(out->GetNumPages() == 0);
|
||||
|
||||
// Decode the option.
|
||||
const auto [pool, dir] = DecodeOption(option);
|
||||
|
||||
// Allocate the memory.
|
||||
{
|
||||
// Lock the pool that we're allocating from.
|
||||
KScopedLightLock lk(pool_locks[static_cast<size_t>(pool)]);
|
||||
|
||||
// Allocate the page group.
|
||||
R_TRY(this->AllocatePageGroupImpl(out, num_pages, pool, dir, false));
|
||||
|
||||
// Open the first reference to the pages.
|
||||
for (const auto& block : out->Nodes()) {
|
||||
PAddr cur_address = block.GetAddress();
|
||||
size_t remaining_pages = block.GetNumPages();
|
||||
while (remaining_pages > 0) {
|
||||
// Get the manager for the current address.
|
||||
auto& manager = this->GetManager(system.Kernel().MemoryLayout(), cur_address);
|
||||
|
||||
// Process part or all of the block.
|
||||
const size_t cur_pages =
|
||||
std::min(remaining_pages, manager.GetPageOffsetToEnd(cur_address));
|
||||
manager.OpenFirst(cur_address, cur_pages);
|
||||
|
||||
// Advance.
|
||||
cur_address += cur_pages * PageSize;
|
||||
remaining_pages -= cur_pages;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set all the allocated memory.
|
||||
for (const auto& block : out->Nodes()) {
|
||||
std::memset(system.DeviceMemory().GetPointer(block.GetAddress()), fill_pattern,
|
||||
block.GetSize());
|
||||
}
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void KMemoryManager::Open(PAddr address, size_t num_pages) {
|
||||
// Repeatedly open references until we've done so for all pages.
|
||||
while (num_pages) {
|
||||
auto& manager = this->GetManager(system.Kernel().MemoryLayout(), address);
|
||||
const size_t cur_pages = std::min(num_pages, manager.GetPageOffsetToEnd(address));
|
||||
|
||||
{
|
||||
KScopedLightLock lk(pool_locks[static_cast<size_t>(manager.GetPool())]);
|
||||
manager.Open(address, cur_pages);
|
||||
}
|
||||
|
||||
num_pages -= cur_pages;
|
||||
address += cur_pages * PageSize;
|
||||
}
|
||||
}
|
||||
|
||||
void KMemoryManager::Close(PAddr address, size_t num_pages) {
|
||||
// Repeatedly close references until we've done so for all pages.
|
||||
while (num_pages) {
|
||||
auto& manager = this->GetManager(system.Kernel().MemoryLayout(), address);
|
||||
const size_t cur_pages = std::min(num_pages, manager.GetPageOffsetToEnd(address));
|
||||
|
||||
{
|
||||
KScopedLightLock lk(pool_locks[static_cast<size_t>(manager.GetPool())]);
|
||||
manager.Close(address, cur_pages);
|
||||
}
|
||||
|
||||
num_pages -= cur_pages;
|
||||
address += cur_pages * PageSize;
|
||||
}
|
||||
}
|
||||
|
||||
void KMemoryManager::Close(const KPageLinkedList& pg) {
|
||||
for (const auto& node : pg.Nodes()) {
|
||||
Close(node.GetAddress(), node.GetNumPages());
|
||||
}
|
||||
}
|
||||
void KMemoryManager::Open(const KPageLinkedList& pg) {
|
||||
for (const auto& node : pg.Nodes()) {
|
||||
Open(node.GetAddress(), node.GetNumPages());
|
||||
}
|
||||
}
|
||||
|
||||
size_t KMemoryManager::Impl::Initialize(PAddr address, size_t size, VAddr management,
|
||||
VAddr management_end, Pool p) {
|
||||
// Calculate management sizes.
|
||||
const size_t ref_count_size = (size / PageSize) * sizeof(u16);
|
||||
const size_t optimize_map_size = CalculateOptimizedProcessOverheadSize(size);
|
||||
const size_t manager_size = Common::AlignUp(optimize_map_size + ref_count_size, PageSize);
|
||||
const size_t page_heap_size = KPageHeap::CalculateManagementOverheadSize(size);
|
||||
const size_t total_management_size = manager_size + page_heap_size;
|
||||
ASSERT(manager_size <= total_management_size);
|
||||
ASSERT(management + total_management_size <= management_end);
|
||||
ASSERT(Common::IsAligned(total_management_size, PageSize));
|
||||
|
||||
// Setup region.
|
||||
pool = p;
|
||||
management_region = management;
|
||||
page_reference_counts.resize(
|
||||
Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize() / PageSize);
|
||||
ASSERT(Common::IsAligned(management_region, PageSize));
|
||||
|
||||
// Initialize the manager's KPageHeap.
|
||||
heap.Initialize(address, size, management + manager_size, page_heap_size);
|
||||
|
||||
return total_management_size;
|
||||
}
|
||||
|
||||
size_t KMemoryManager::Impl::CalculateManagementOverheadSize(size_t region_size) {
|
||||
const size_t ref_count_size = (region_size / PageSize) * sizeof(u16);
|
||||
const 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);
|
||||
const size_t manager_meta_size = Common::AlignUp(optimize_map_size + ref_count_size, PageSize);
|
||||
const size_t page_heap_size = KPageHeap::CalculateManagementOverheadSize(region_size);
|
||||
return manager_meta_size + page_heap_size;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,11 +5,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <mutex>
|
||||
#include <tuple>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/k_light_lock.h"
|
||||
#include "core/hle/kernel/k_memory_layout.h"
|
||||
#include "core/hle/kernel/k_page_heap.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
@@ -52,22 +53,33 @@ public:
|
||||
|
||||
explicit KMemoryManager(Core::System& system_);
|
||||
|
||||
constexpr std::size_t GetSize(Pool pool) const {
|
||||
return managers[static_cast<std::size_t>(pool)].GetSize();
|
||||
void Initialize(VAddr management_region, size_t management_region_size);
|
||||
|
||||
constexpr size_t GetSize(Pool pool) const {
|
||||
constexpr Direction GetSizeDirection = Direction::FromFront;
|
||||
size_t total = 0;
|
||||
for (auto* manager = this->GetFirstManager(pool, GetSizeDirection); manager != nullptr;
|
||||
manager = this->GetNextManager(manager, GetSizeDirection)) {
|
||||
total += manager->GetSize();
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
void InitializeManager(Pool pool, u64 start_address, u64 end_address);
|
||||
PAddr AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option);
|
||||
ResultCode AllocateAndOpen(KPageLinkedList* out, size_t num_pages, u32 option);
|
||||
ResultCode AllocateAndOpenForProcess(KPageLinkedList* out, size_t num_pages, u32 option,
|
||||
u64 process_id, u8 fill_pattern);
|
||||
|
||||
VAddr AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option);
|
||||
ResultCode Allocate(KPageLinkedList& page_list, std::size_t num_pages, Pool pool, Direction dir,
|
||||
u32 heap_fill_value = 0);
|
||||
ResultCode Free(KPageLinkedList& page_list, std::size_t num_pages, Pool pool, Direction dir,
|
||||
u32 heap_fill_value = 0);
|
||||
static constexpr size_t MaxManagerCount = 10;
|
||||
|
||||
static constexpr std::size_t MaxManagerCount = 10;
|
||||
void Close(PAddr address, size_t num_pages);
|
||||
void Close(const KPageLinkedList& pg);
|
||||
|
||||
void Open(PAddr address, size_t num_pages);
|
||||
void Open(const KPageLinkedList& pg);
|
||||
|
||||
public:
|
||||
static std::size_t CalculateManagementOverheadSize(std::size_t region_size) {
|
||||
static size_t CalculateManagementOverheadSize(size_t region_size) {
|
||||
return Impl::CalculateManagementOverheadSize(region_size);
|
||||
}
|
||||
|
||||
@@ -100,17 +112,26 @@ private:
|
||||
Impl() = default;
|
||||
~Impl() = default;
|
||||
|
||||
std::size_t Initialize(Pool new_pool, u64 start_address, u64 end_address);
|
||||
size_t Initialize(PAddr address, size_t size, VAddr management, VAddr management_end,
|
||||
Pool p);
|
||||
|
||||
VAddr AllocateBlock(s32 index, bool random) {
|
||||
return heap.AllocateBlock(index, random);
|
||||
}
|
||||
|
||||
void Free(VAddr addr, std::size_t num_pages) {
|
||||
void Free(VAddr addr, size_t num_pages) {
|
||||
heap.Free(addr, num_pages);
|
||||
}
|
||||
|
||||
constexpr std::size_t GetSize() const {
|
||||
void SetInitialUsedHeapSize(size_t reserved_size) {
|
||||
heap.SetInitialUsedSize(reserved_size);
|
||||
}
|
||||
|
||||
constexpr Pool GetPool() const {
|
||||
return pool;
|
||||
}
|
||||
|
||||
constexpr size_t GetSize() const {
|
||||
return heap.GetSize();
|
||||
}
|
||||
|
||||
@@ -122,10 +143,88 @@ private:
|
||||
return heap.GetEndAddress();
|
||||
}
|
||||
|
||||
static std::size_t CalculateManagementOverheadSize(std::size_t region_size);
|
||||
constexpr size_t GetPageOffset(PAddr address) const {
|
||||
return heap.GetPageOffset(address);
|
||||
}
|
||||
|
||||
static constexpr std::size_t CalculateOptimizedProcessOverheadSize(
|
||||
std::size_t region_size) {
|
||||
constexpr size_t GetPageOffsetToEnd(PAddr address) const {
|
||||
return heap.GetPageOffsetToEnd(address);
|
||||
}
|
||||
|
||||
constexpr void SetNext(Impl* n) {
|
||||
next = n;
|
||||
}
|
||||
|
||||
constexpr void SetPrev(Impl* n) {
|
||||
prev = n;
|
||||
}
|
||||
|
||||
constexpr Impl* GetNext() const {
|
||||
return next;
|
||||
}
|
||||
|
||||
constexpr Impl* GetPrev() const {
|
||||
return prev;
|
||||
}
|
||||
|
||||
void OpenFirst(PAddr address, size_t num_pages) {
|
||||
size_t index = this->GetPageOffset(address);
|
||||
const size_t end = index + num_pages;
|
||||
while (index < end) {
|
||||
const RefCount ref_count = (++page_reference_counts[index]);
|
||||
ASSERT(ref_count == 1);
|
||||
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
void Open(PAddr address, size_t num_pages) {
|
||||
size_t index = this->GetPageOffset(address);
|
||||
const size_t end = index + num_pages;
|
||||
while (index < end) {
|
||||
const RefCount ref_count = (++page_reference_counts[index]);
|
||||
ASSERT(ref_count > 1);
|
||||
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
void Close(PAddr address, size_t num_pages) {
|
||||
size_t index = this->GetPageOffset(address);
|
||||
const size_t end = index + num_pages;
|
||||
|
||||
size_t free_start = 0;
|
||||
size_t free_count = 0;
|
||||
while (index < end) {
|
||||
ASSERT(page_reference_counts[index] > 0);
|
||||
const RefCount ref_count = (--page_reference_counts[index]);
|
||||
|
||||
// Keep track of how many zero refcounts we see in a row, to minimize calls to free.
|
||||
if (ref_count == 0) {
|
||||
if (free_count > 0) {
|
||||
free_count++;
|
||||
} else {
|
||||
free_start = index;
|
||||
free_count = 1;
|
||||
}
|
||||
} else {
|
||||
if (free_count > 0) {
|
||||
this->Free(heap.GetAddress() + free_start * PageSize, free_count);
|
||||
free_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
if (free_count > 0) {
|
||||
this->Free(heap.GetAddress() + free_start * PageSize, free_count);
|
||||
}
|
||||
}
|
||||
|
||||
static size_t CalculateManagementOverheadSize(size_t region_size);
|
||||
|
||||
static constexpr size_t CalculateOptimizedProcessOverheadSize(size_t region_size) {
|
||||
return (Common::AlignUp((region_size / PageSize), Common::BitSize<u64>()) /
|
||||
Common::BitSize<u64>()) *
|
||||
sizeof(u64);
|
||||
@@ -135,13 +234,45 @@ private:
|
||||
using RefCount = u16;
|
||||
|
||||
KPageHeap heap;
|
||||
std::vector<RefCount> page_reference_counts;
|
||||
VAddr management_region{};
|
||||
Pool pool{};
|
||||
Impl* next{};
|
||||
Impl* prev{};
|
||||
};
|
||||
|
||||
private:
|
||||
Impl& GetManager(const KMemoryLayout& memory_layout, PAddr address) {
|
||||
return managers[memory_layout.GetPhysicalLinearRegion(address).GetAttributes()];
|
||||
}
|
||||
|
||||
const Impl& GetManager(const KMemoryLayout& memory_layout, PAddr address) const {
|
||||
return managers[memory_layout.GetPhysicalLinearRegion(address).GetAttributes()];
|
||||
}
|
||||
|
||||
constexpr Impl* GetFirstManager(Pool pool, Direction dir) const {
|
||||
return dir == Direction::FromBack ? pool_managers_tail[static_cast<size_t>(pool)]
|
||||
: pool_managers_head[static_cast<size_t>(pool)];
|
||||
}
|
||||
|
||||
constexpr Impl* GetNextManager(Impl* cur, Direction dir) const {
|
||||
if (dir == Direction::FromBack) {
|
||||
return cur->GetPrev();
|
||||
} else {
|
||||
return cur->GetNext();
|
||||
}
|
||||
}
|
||||
|
||||
ResultCode AllocatePageGroupImpl(KPageLinkedList* out, size_t num_pages, Pool pool,
|
||||
Direction dir, bool random);
|
||||
|
||||
private:
|
||||
Core::System& system;
|
||||
std::array<std::mutex, static_cast<std::size_t>(Pool::Count)> pool_locks;
|
||||
std::array<KLightLock, static_cast<size_t>(Pool::Count)> pool_locks;
|
||||
std::array<Impl*, MaxManagerCount> pool_managers_head{};
|
||||
std::array<Impl*, MaxManagerCount> pool_managers_tail{};
|
||||
std::array<Impl, MaxManagerCount> managers;
|
||||
size_t num_managers{};
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -14,7 +14,8 @@
|
||||
namespace Kernel {
|
||||
|
||||
enum KMemoryRegionType : u32 {
|
||||
KMemoryRegionAttr_CarveoutProtected = 0x04000000,
|
||||
KMemoryRegionAttr_CarveoutProtected = 0x02000000,
|
||||
KMemoryRegionAttr_Uncached = 0x04000000,
|
||||
KMemoryRegionAttr_DidKernelMap = 0x08000000,
|
||||
KMemoryRegionAttr_ShouldKernelMap = 0x10000000,
|
||||
KMemoryRegionAttr_UserReadOnly = 0x20000000,
|
||||
@@ -239,6 +240,11 @@ static_assert(KMemoryRegionType_VirtualDramHeapBase.GetValue() == 0x1A);
|
||||
static_assert(KMemoryRegionType_VirtualDramKernelPtHeap.GetValue() == 0x2A);
|
||||
static_assert(KMemoryRegionType_VirtualDramKernelTraceBuffer.GetValue() == 0x4A);
|
||||
|
||||
// UNUSED: .DeriveSparse(2, 2, 0);
|
||||
constexpr auto KMemoryRegionType_VirtualDramUnknownDebug =
|
||||
KMemoryRegionType_Dram.DeriveSparse(2, 2, 1);
|
||||
static_assert(KMemoryRegionType_VirtualDramUnknownDebug.GetValue() == (0x52));
|
||||
|
||||
constexpr auto KMemoryRegionType_VirtualDramKernelInitPt =
|
||||
KMemoryRegionType_VirtualDramHeapBase.Derive(3, 0);
|
||||
constexpr auto KMemoryRegionType_VirtualDramPoolManagement =
|
||||
@@ -330,6 +336,8 @@ constexpr KMemoryRegionType GetTypeForVirtualLinearMapping(u32 type_id) {
|
||||
return KMemoryRegionType_VirtualDramKernelTraceBuffer;
|
||||
} else if (KMemoryRegionType_DramKernelPtHeap.IsAncestorOf(type_id)) {
|
||||
return KMemoryRegionType_VirtualDramKernelPtHeap;
|
||||
} else if ((type_id | KMemoryRegionAttr_ShouldKernelMap) == type_id) {
|
||||
return KMemoryRegionType_VirtualDramUnknownDebug;
|
||||
} else {
|
||||
return KMemoryRegionType_Dram;
|
||||
}
|
||||
|
||||
@@ -7,35 +7,51 @@
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
void KPageHeap::Initialize(VAddr address, std::size_t size, std::size_t metadata_size) {
|
||||
// Check our assumptions
|
||||
ASSERT(Common::IsAligned((address), PageSize));
|
||||
void KPageHeap::Initialize(PAddr address, size_t size, VAddr management_address,
|
||||
size_t management_size, const size_t* block_shifts,
|
||||
size_t num_block_shifts) {
|
||||
// Check our assumptions.
|
||||
ASSERT(Common::IsAligned(address, PageSize));
|
||||
ASSERT(Common::IsAligned(size, PageSize));
|
||||
ASSERT(0 < num_block_shifts && num_block_shifts <= NumMemoryBlockPageShifts);
|
||||
const VAddr management_end = management_address + management_size;
|
||||
|
||||
// Set our members
|
||||
heap_address = address;
|
||||
heap_size = size;
|
||||
// Set our members.
|
||||
m_heap_address = address;
|
||||
m_heap_size = size;
|
||||
m_num_blocks = num_block_shifts;
|
||||
|
||||
// Setup bitmaps
|
||||
metadata.resize(metadata_size / sizeof(u64));
|
||||
u64* cur_bitmap_storage{metadata.data()};
|
||||
for (std::size_t i = 0; i < MemoryBlockPageShifts.size(); i++) {
|
||||
const std::size_t cur_block_shift{MemoryBlockPageShifts[i]};
|
||||
const std::size_t next_block_shift{
|
||||
(i != MemoryBlockPageShifts.size() - 1) ? MemoryBlockPageShifts[i + 1] : 0};
|
||||
cur_bitmap_storage = blocks[i].Initialize(heap_address, heap_size, cur_block_shift,
|
||||
next_block_shift, cur_bitmap_storage);
|
||||
// Setup bitmaps.
|
||||
m_management_data.resize(management_size / sizeof(u64));
|
||||
u64* cur_bitmap_storage{m_management_data.data()};
|
||||
for (size_t i = 0; i < num_block_shifts; i++) {
|
||||
const size_t cur_block_shift = block_shifts[i];
|
||||
const size_t next_block_shift = (i != num_block_shifts - 1) ? block_shifts[i + 1] : 0;
|
||||
cur_bitmap_storage = m_blocks[i].Initialize(m_heap_address, m_heap_size, cur_block_shift,
|
||||
next_block_shift, cur_bitmap_storage);
|
||||
}
|
||||
|
||||
// Ensure we didn't overextend our bounds.
|
||||
ASSERT(VAddr(cur_bitmap_storage) <= management_end);
|
||||
}
|
||||
|
||||
VAddr KPageHeap::AllocateBlock(s32 index, bool random) {
|
||||
const std::size_t needed_size{blocks[index].GetSize()};
|
||||
size_t KPageHeap::GetNumFreePages() const {
|
||||
size_t num_free = 0;
|
||||
|
||||
for (s32 i{index}; i < static_cast<s32>(MemoryBlockPageShifts.size()); i++) {
|
||||
if (const VAddr addr{blocks[i].PopBlock(random)}; addr) {
|
||||
if (const std::size_t allocated_size{blocks[i].GetSize()};
|
||||
allocated_size > needed_size) {
|
||||
Free(addr + needed_size, (allocated_size - needed_size) / PageSize);
|
||||
for (size_t i = 0; i < m_num_blocks; i++) {
|
||||
num_free += m_blocks[i].GetNumFreePages();
|
||||
}
|
||||
|
||||
return num_free;
|
||||
}
|
||||
|
||||
PAddr KPageHeap::AllocateBlock(s32 index, bool random) {
|
||||
const size_t needed_size = m_blocks[index].GetSize();
|
||||
|
||||
for (s32 i = index; i < static_cast<s32>(m_num_blocks); i++) {
|
||||
if (const PAddr addr = m_blocks[i].PopBlock(random); addr != 0) {
|
||||
if (const size_t allocated_size = m_blocks[i].GetSize(); allocated_size > needed_size) {
|
||||
this->Free(addr + needed_size, (allocated_size - needed_size) / PageSize);
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
@@ -44,34 +60,34 @@ VAddr KPageHeap::AllocateBlock(s32 index, bool random) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void KPageHeap::FreeBlock(VAddr block, s32 index) {
|
||||
void KPageHeap::FreeBlock(PAddr block, s32 index) {
|
||||
do {
|
||||
block = blocks[index++].PushBlock(block);
|
||||
block = m_blocks[index++].PushBlock(block);
|
||||
} while (block != 0);
|
||||
}
|
||||
|
||||
void KPageHeap::Free(VAddr addr, std::size_t num_pages) {
|
||||
// Freeing no pages is a no-op
|
||||
void KPageHeap::Free(PAddr addr, size_t num_pages) {
|
||||
// Freeing no pages is a no-op.
|
||||
if (num_pages == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the largest block size that we can free, and free as many as possible
|
||||
s32 big_index{static_cast<s32>(MemoryBlockPageShifts.size()) - 1};
|
||||
const VAddr start{addr};
|
||||
const VAddr end{(num_pages * PageSize) + addr};
|
||||
VAddr before_start{start};
|
||||
VAddr before_end{start};
|
||||
VAddr after_start{end};
|
||||
VAddr after_end{end};
|
||||
// Find the largest block size that we can free, and free as many as possible.
|
||||
s32 big_index = static_cast<s32>(m_num_blocks) - 1;
|
||||
const PAddr start = addr;
|
||||
const PAddr end = addr + num_pages * PageSize;
|
||||
PAddr before_start = start;
|
||||
PAddr before_end = start;
|
||||
PAddr after_start = end;
|
||||
PAddr after_end = end;
|
||||
while (big_index >= 0) {
|
||||
const std::size_t block_size{blocks[big_index].GetSize()};
|
||||
const VAddr big_start{Common::AlignUp((start), block_size)};
|
||||
const VAddr big_end{Common::AlignDown((end), block_size)};
|
||||
const size_t block_size = m_blocks[big_index].GetSize();
|
||||
const PAddr big_start = Common::AlignUp(start, block_size);
|
||||
const PAddr big_end = Common::AlignDown(end, block_size);
|
||||
if (big_start < big_end) {
|
||||
// Free as many big blocks as we can
|
||||
for (auto block{big_start}; block < big_end; block += block_size) {
|
||||
FreeBlock(block, big_index);
|
||||
// Free as many big blocks as we can.
|
||||
for (auto block = big_start; block < big_end; block += block_size) {
|
||||
this->FreeBlock(block, big_index);
|
||||
}
|
||||
before_end = big_start;
|
||||
after_start = big_end;
|
||||
@@ -81,31 +97,31 @@ void KPageHeap::Free(VAddr addr, std::size_t num_pages) {
|
||||
}
|
||||
ASSERT(big_index >= 0);
|
||||
|
||||
// Free space before the big blocks
|
||||
for (s32 i{big_index - 1}; i >= 0; i--) {
|
||||
const std::size_t block_size{blocks[i].GetSize()};
|
||||
// Free space before the big blocks.
|
||||
for (s32 i = big_index - 1; i >= 0; i--) {
|
||||
const size_t block_size = m_blocks[i].GetSize();
|
||||
while (before_start + block_size <= before_end) {
|
||||
before_end -= block_size;
|
||||
FreeBlock(before_end, i);
|
||||
this->FreeBlock(before_end, i);
|
||||
}
|
||||
}
|
||||
|
||||
// Free space after the big blocks
|
||||
for (s32 i{big_index - 1}; i >= 0; i--) {
|
||||
const std::size_t block_size{blocks[i].GetSize()};
|
||||
// Free space after the big blocks.
|
||||
for (s32 i = big_index - 1; i >= 0; i--) {
|
||||
const size_t block_size = m_blocks[i].GetSize();
|
||||
while (after_start + block_size <= after_end) {
|
||||
FreeBlock(after_start, i);
|
||||
this->FreeBlock(after_start, i);
|
||||
after_start += block_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t KPageHeap::CalculateManagementOverheadSize(std::size_t region_size) {
|
||||
std::size_t overhead_size = 0;
|
||||
for (std::size_t i = 0; i < MemoryBlockPageShifts.size(); i++) {
|
||||
const std::size_t cur_block_shift{MemoryBlockPageShifts[i]};
|
||||
const std::size_t next_block_shift{
|
||||
(i != MemoryBlockPageShifts.size() - 1) ? MemoryBlockPageShifts[i + 1] : 0};
|
||||
size_t KPageHeap::CalculateManagementOverheadSize(size_t region_size, const size_t* block_shifts,
|
||||
size_t num_block_shifts) {
|
||||
size_t overhead_size = 0;
|
||||
for (size_t i = 0; i < num_block_shifts; i++) {
|
||||
const size_t cur_block_shift = block_shifts[i];
|
||||
const size_t next_block_shift = (i != num_block_shifts - 1) ? block_shifts[i + 1] : 0;
|
||||
overhead_size += KPageHeap::Block::CalculateManagementOverheadSize(
|
||||
region_size, cur_block_shift, next_block_shift);
|
||||
}
|
||||
|
||||
@@ -23,54 +23,73 @@ public:
|
||||
KPageHeap() = default;
|
||||
~KPageHeap() = default;
|
||||
|
||||
constexpr VAddr GetAddress() const {
|
||||
return heap_address;
|
||||
constexpr PAddr GetAddress() const {
|
||||
return m_heap_address;
|
||||
}
|
||||
constexpr std::size_t GetSize() const {
|
||||
return heap_size;
|
||||
constexpr size_t GetSize() const {
|
||||
return m_heap_size;
|
||||
}
|
||||
constexpr VAddr GetEndAddress() const {
|
||||
return GetAddress() + GetSize();
|
||||
constexpr PAddr GetEndAddress() const {
|
||||
return this->GetAddress() + this->GetSize();
|
||||
}
|
||||
constexpr std::size_t GetPageOffset(VAddr block) const {
|
||||
return (block - GetAddress()) / PageSize;
|
||||
constexpr size_t GetPageOffset(PAddr block) const {
|
||||
return (block - this->GetAddress()) / PageSize;
|
||||
}
|
||||
constexpr size_t GetPageOffsetToEnd(PAddr block) const {
|
||||
return (this->GetEndAddress() - block) / PageSize;
|
||||
}
|
||||
|
||||
void Initialize(VAddr heap_address, std::size_t heap_size, std::size_t metadata_size);
|
||||
VAddr AllocateBlock(s32 index, bool random);
|
||||
void Free(VAddr addr, std::size_t num_pages);
|
||||
|
||||
void UpdateUsedSize() {
|
||||
used_size = heap_size - (GetNumFreePages() * PageSize);
|
||||
void Initialize(PAddr heap_address, size_t heap_size, VAddr management_address,
|
||||
size_t management_size) {
|
||||
return this->Initialize(heap_address, heap_size, management_address, management_size,
|
||||
MemoryBlockPageShifts.data(), NumMemoryBlockPageShifts);
|
||||
}
|
||||
|
||||
static std::size_t CalculateManagementOverheadSize(std::size_t region_size);
|
||||
size_t GetFreeSize() const {
|
||||
return this->GetNumFreePages() * PageSize;
|
||||
}
|
||||
|
||||
static constexpr s32 GetAlignedBlockIndex(std::size_t num_pages, std::size_t align_pages) {
|
||||
const auto target_pages{std::max(num_pages, align_pages)};
|
||||
for (std::size_t i = 0; i < NumMemoryBlockPageShifts; i++) {
|
||||
if (target_pages <=
|
||||
(static_cast<std::size_t>(1) << MemoryBlockPageShifts[i]) / PageSize) {
|
||||
void SetInitialUsedSize(size_t reserved_size) {
|
||||
// Check that the reserved size is valid.
|
||||
const size_t free_size = this->GetNumFreePages() * PageSize;
|
||||
ASSERT(m_heap_size >= free_size + reserved_size);
|
||||
|
||||
// Set the initial used size.
|
||||
m_initial_used_size = m_heap_size - free_size - reserved_size;
|
||||
}
|
||||
|
||||
PAddr AllocateBlock(s32 index, bool random);
|
||||
void Free(PAddr addr, size_t num_pages);
|
||||
|
||||
static size_t CalculateManagementOverheadSize(size_t region_size) {
|
||||
return CalculateManagementOverheadSize(region_size, MemoryBlockPageShifts.data(),
|
||||
NumMemoryBlockPageShifts);
|
||||
}
|
||||
|
||||
static constexpr s32 GetAlignedBlockIndex(size_t num_pages, size_t align_pages) {
|
||||
const size_t target_pages = std::max(num_pages, align_pages);
|
||||
for (size_t i = 0; i < NumMemoryBlockPageShifts; i++) {
|
||||
if (target_pages <= (size_t(1) << MemoryBlockPageShifts[i]) / PageSize) {
|
||||
return static_cast<s32>(i);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static constexpr s32 GetBlockIndex(std::size_t num_pages) {
|
||||
for (s32 i{static_cast<s32>(NumMemoryBlockPageShifts) - 1}; i >= 0; i--) {
|
||||
if (num_pages >= (static_cast<std::size_t>(1) << MemoryBlockPageShifts[i]) / PageSize) {
|
||||
static constexpr s32 GetBlockIndex(size_t num_pages) {
|
||||
for (s32 i = static_cast<s32>(NumMemoryBlockPageShifts) - 1; i >= 0; i--) {
|
||||
if (num_pages >= (size_t(1) << MemoryBlockPageShifts[i]) / PageSize) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static constexpr std::size_t GetBlockSize(std::size_t index) {
|
||||
return static_cast<std::size_t>(1) << MemoryBlockPageShifts[index];
|
||||
static constexpr size_t GetBlockSize(size_t index) {
|
||||
return size_t(1) << MemoryBlockPageShifts[index];
|
||||
}
|
||||
|
||||
static constexpr std::size_t GetBlockNumPages(std::size_t index) {
|
||||
static constexpr size_t GetBlockNumPages(size_t index) {
|
||||
return GetBlockSize(index) / PageSize;
|
||||
}
|
||||
|
||||
@@ -83,114 +102,116 @@ private:
|
||||
Block() = default;
|
||||
~Block() = default;
|
||||
|
||||
constexpr std::size_t GetShift() const {
|
||||
return block_shift;
|
||||
constexpr size_t GetShift() const {
|
||||
return m_block_shift;
|
||||
}
|
||||
constexpr std::size_t GetNextShift() const {
|
||||
return next_block_shift;
|
||||
constexpr size_t GetNextShift() const {
|
||||
return m_next_block_shift;
|
||||
}
|
||||
constexpr std::size_t GetSize() const {
|
||||
return static_cast<std::size_t>(1) << GetShift();
|
||||
constexpr size_t GetSize() const {
|
||||
return u64(1) << this->GetShift();
|
||||
}
|
||||
constexpr std::size_t GetNumPages() const {
|
||||
return GetSize() / PageSize;
|
||||
constexpr size_t GetNumPages() const {
|
||||
return this->GetSize() / PageSize;
|
||||
}
|
||||
constexpr std::size_t GetNumFreeBlocks() const {
|
||||
return bitmap.GetNumBits();
|
||||
constexpr size_t GetNumFreeBlocks() const {
|
||||
return m_bitmap.GetNumBits();
|
||||
}
|
||||
constexpr std::size_t GetNumFreePages() const {
|
||||
return GetNumFreeBlocks() * GetNumPages();
|
||||
constexpr size_t GetNumFreePages() const {
|
||||
return this->GetNumFreeBlocks() * this->GetNumPages();
|
||||
}
|
||||
|
||||
u64* Initialize(VAddr addr, std::size_t size, std::size_t bs, std::size_t nbs,
|
||||
u64* bit_storage) {
|
||||
// Set shifts
|
||||
block_shift = bs;
|
||||
next_block_shift = nbs;
|
||||
u64* Initialize(PAddr addr, size_t size, size_t bs, size_t nbs, u64* bit_storage) {
|
||||
// Set shifts.
|
||||
m_block_shift = bs;
|
||||
m_next_block_shift = nbs;
|
||||
|
||||
// Align up the address
|
||||
VAddr end{addr + size};
|
||||
const auto align{(next_block_shift != 0) ? (1ULL << next_block_shift)
|
||||
: (1ULL << block_shift)};
|
||||
addr = Common::AlignDown((addr), align);
|
||||
end = Common::AlignUp((end), align);
|
||||
// Align up the address.
|
||||
PAddr end = addr + size;
|
||||
const size_t align = (m_next_block_shift != 0) ? (u64(1) << m_next_block_shift)
|
||||
: (u64(1) << m_block_shift);
|
||||
addr = Common::AlignDown(addr, align);
|
||||
end = Common::AlignUp(end, align);
|
||||
|
||||
heap_address = addr;
|
||||
end_offset = (end - addr) / (1ULL << block_shift);
|
||||
return bitmap.Initialize(bit_storage, end_offset);
|
||||
m_heap_address = addr;
|
||||
m_end_offset = (end - addr) / (u64(1) << m_block_shift);
|
||||
return m_bitmap.Initialize(bit_storage, m_end_offset);
|
||||
}
|
||||
|
||||
VAddr PushBlock(VAddr address) {
|
||||
// Set the bit for the free block
|
||||
std::size_t offset{(address - heap_address) >> GetShift()};
|
||||
bitmap.SetBit(offset);
|
||||
PAddr PushBlock(PAddr address) {
|
||||
// Set the bit for the free block.
|
||||
size_t offset = (address - m_heap_address) >> this->GetShift();
|
||||
m_bitmap.SetBit(offset);
|
||||
|
||||
// If we have a next shift, try to clear the blocks below and return the address
|
||||
if (GetNextShift()) {
|
||||
const auto diff{1ULL << (GetNextShift() - GetShift())};
|
||||
// If we have a next shift, try to clear the blocks below this one and return the new
|
||||
// address.
|
||||
if (this->GetNextShift()) {
|
||||
const size_t diff = u64(1) << (this->GetNextShift() - this->GetShift());
|
||||
offset = Common::AlignDown(offset, diff);
|
||||
if (bitmap.ClearRange(offset, diff)) {
|
||||
return heap_address + (offset << GetShift());
|
||||
if (m_bitmap.ClearRange(offset, diff)) {
|
||||
return m_heap_address + (offset << this->GetShift());
|
||||
}
|
||||
}
|
||||
|
||||
// We couldn't coalesce, or we're already as big as possible
|
||||
return 0;
|
||||
// We couldn't coalesce, or we're already as big as possible.
|
||||
return {};
|
||||
}
|
||||
|
||||
VAddr PopBlock(bool random) {
|
||||
// Find a free block
|
||||
const s64 soffset{bitmap.FindFreeBlock(random)};
|
||||
PAddr PopBlock(bool random) {
|
||||
// Find a free block.
|
||||
s64 soffset = m_bitmap.FindFreeBlock(random);
|
||||
if (soffset < 0) {
|
||||
return 0;
|
||||
return {};
|
||||
}
|
||||
const auto offset{static_cast<std::size_t>(soffset)};
|
||||
const size_t offset = static_cast<size_t>(soffset);
|
||||
|
||||
// Update our tracking and return it
|
||||
bitmap.ClearBit(offset);
|
||||
return heap_address + (offset << GetShift());
|
||||
// Update our tracking and return it.
|
||||
m_bitmap.ClearBit(offset);
|
||||
return m_heap_address + (offset << this->GetShift());
|
||||
}
|
||||
|
||||
static constexpr std::size_t CalculateManagementOverheadSize(std::size_t region_size,
|
||||
std::size_t cur_block_shift,
|
||||
std::size_t next_block_shift) {
|
||||
const auto cur_block_size{(1ULL << cur_block_shift)};
|
||||
const auto next_block_size{(1ULL << next_block_shift)};
|
||||
const auto align{(next_block_shift != 0) ? next_block_size : cur_block_size};
|
||||
public:
|
||||
static constexpr size_t CalculateManagementOverheadSize(size_t region_size,
|
||||
size_t cur_block_shift,
|
||||
size_t next_block_shift) {
|
||||
const size_t cur_block_size = (u64(1) << cur_block_shift);
|
||||
const size_t next_block_size = (u64(1) << next_block_shift);
|
||||
const size_t align = (next_block_shift != 0) ? next_block_size : cur_block_size;
|
||||
return KPageBitmap::CalculateManagementOverheadSize(
|
||||
(align * 2 + Common::AlignUp(region_size, align)) / cur_block_size);
|
||||
}
|
||||
|
||||
private:
|
||||
KPageBitmap bitmap;
|
||||
VAddr heap_address{};
|
||||
uintptr_t end_offset{};
|
||||
std::size_t block_shift{};
|
||||
std::size_t next_block_shift{};
|
||||
KPageBitmap m_bitmap;
|
||||
PAddr m_heap_address{};
|
||||
uintptr_t m_end_offset{};
|
||||
size_t m_block_shift{};
|
||||
size_t m_next_block_shift{};
|
||||
};
|
||||
|
||||
constexpr std::size_t GetNumFreePages() const {
|
||||
std::size_t num_free{};
|
||||
private:
|
||||
void Initialize(PAddr heap_address, size_t heap_size, VAddr management_address,
|
||||
size_t management_size, const size_t* block_shifts, size_t num_block_shifts);
|
||||
size_t GetNumFreePages() const;
|
||||
|
||||
for (const auto& block : blocks) {
|
||||
num_free += block.GetNumFreePages();
|
||||
}
|
||||
void FreeBlock(PAddr block, s32 index);
|
||||
|
||||
return num_free;
|
||||
}
|
||||
|
||||
void FreeBlock(VAddr block, s32 index);
|
||||
|
||||
static constexpr std::size_t NumMemoryBlockPageShifts{7};
|
||||
static constexpr std::array<std::size_t, NumMemoryBlockPageShifts> MemoryBlockPageShifts{
|
||||
static constexpr size_t NumMemoryBlockPageShifts{7};
|
||||
static constexpr std::array<size_t, NumMemoryBlockPageShifts> MemoryBlockPageShifts{
|
||||
0xC, 0x10, 0x15, 0x16, 0x19, 0x1D, 0x1E,
|
||||
};
|
||||
|
||||
VAddr heap_address{};
|
||||
std::size_t heap_size{};
|
||||
std::size_t used_size{};
|
||||
std::array<Block, NumMemoryBlockPageShifts> blocks{};
|
||||
std::vector<u64> metadata;
|
||||
private:
|
||||
static size_t CalculateManagementOverheadSize(size_t region_size, const size_t* block_shifts,
|
||||
size_t num_block_shifts);
|
||||
|
||||
private:
|
||||
PAddr m_heap_address{};
|
||||
size_t m_heap_size{};
|
||||
size_t m_initial_used_size{};
|
||||
size_t m_num_blocks{};
|
||||
std::array<Block, NumMemoryBlockPageShifts> m_blocks{};
|
||||
std::vector<u64> m_management_data;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -41,24 +41,6 @@ constexpr std::size_t GetAddressSpaceWidthFromType(FileSys::ProgramAddressSpaceT
|
||||
}
|
||||
}
|
||||
|
||||
constexpr u64 GetAddressInRange(const KMemoryInfo& info, VAddr addr) {
|
||||
if (info.GetAddress() < addr) {
|
||||
return addr;
|
||||
}
|
||||
return info.GetAddress();
|
||||
}
|
||||
|
||||
constexpr std::size_t GetSizeInRange(const KMemoryInfo& info, VAddr start, VAddr end) {
|
||||
std::size_t size{info.GetSize()};
|
||||
if (info.GetAddress() < start) {
|
||||
size -= start - info.GetAddress();
|
||||
}
|
||||
if (info.GetEndAddress() > end) {
|
||||
size -= info.GetEndAddress() - end;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
KPageTable::KPageTable(Core::System& system_)
|
||||
@@ -291,83 +273,153 @@ ResultCode KPageTable::MapProcessCode(VAddr addr, std::size_t num_pages, KMemory
|
||||
R_TRY(this->CheckMemoryState(addr, size, KMemoryState::All, KMemoryState::Free,
|
||||
KMemoryPermission::None, KMemoryPermission::None,
|
||||
KMemoryAttribute::None, KMemoryAttribute::None));
|
||||
KPageLinkedList pg;
|
||||
R_TRY(system.Kernel().MemoryManager().AllocateAndOpen(
|
||||
&pg, num_pages,
|
||||
KMemoryManager::EncodeOption(KMemoryManager::Pool::Application, allocation_option)));
|
||||
|
||||
KPageLinkedList page_linked_list;
|
||||
R_TRY(system.Kernel().MemoryManager().Allocate(page_linked_list, num_pages, memory_pool,
|
||||
allocation_option));
|
||||
R_TRY(Operate(addr, num_pages, page_linked_list, OperationType::MapGroup));
|
||||
R_TRY(Operate(addr, num_pages, pg, OperationType::MapGroup));
|
||||
|
||||
block_manager->Update(addr, num_pages, state, perm);
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KPageTable::MapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
|
||||
ResultCode KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size) {
|
||||
// Validate the mapping request.
|
||||
R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode),
|
||||
ResultInvalidMemoryRegion);
|
||||
|
||||
// Lock the table.
|
||||
KScopedLightLock lk(general_lock);
|
||||
|
||||
const std::size_t num_pages{size / PageSize};
|
||||
// Verify that the source memory is normal heap.
|
||||
KMemoryState src_state{};
|
||||
KMemoryPermission src_perm{};
|
||||
std::size_t num_src_allocator_blocks{};
|
||||
R_TRY(this->CheckMemoryState(&src_state, &src_perm, nullptr, &num_src_allocator_blocks,
|
||||
src_address, size, KMemoryState::All, KMemoryState::Normal,
|
||||
KMemoryPermission::All, KMemoryPermission::UserReadWrite,
|
||||
KMemoryAttribute::All, KMemoryAttribute::None));
|
||||
|
||||
KMemoryState state{};
|
||||
KMemoryPermission perm{};
|
||||
CASCADE_CODE(CheckMemoryState(&state, &perm, nullptr, nullptr, src_addr, size,
|
||||
KMemoryState::All, KMemoryState::Normal, KMemoryPermission::All,
|
||||
KMemoryPermission::UserReadWrite, KMemoryAttribute::Mask,
|
||||
KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped));
|
||||
|
||||
if (IsRegionMapped(dst_addr, size)) {
|
||||
return ResultInvalidCurrentMemory;
|
||||
}
|
||||
|
||||
KPageLinkedList page_linked_list;
|
||||
AddRegionToPages(src_addr, num_pages, page_linked_list);
|
||||
// Verify that the destination memory is unmapped.
|
||||
std::size_t num_dst_allocator_blocks{};
|
||||
R_TRY(this->CheckMemoryState(&num_dst_allocator_blocks, dst_address, size, KMemoryState::All,
|
||||
KMemoryState::Free, KMemoryPermission::None,
|
||||
KMemoryPermission::None, KMemoryAttribute::None,
|
||||
KMemoryAttribute::None));
|
||||
|
||||
// Map the code memory.
|
||||
{
|
||||
auto block_guard = detail::ScopeExit(
|
||||
[&] { Operate(src_addr, num_pages, perm, OperationType::ChangePermissions); });
|
||||
// Determine the number of pages being operated on.
|
||||
const std::size_t num_pages = size / PageSize;
|
||||
|
||||
CASCADE_CODE(Operate(src_addr, num_pages, KMemoryPermission::None,
|
||||
OperationType::ChangePermissions));
|
||||
CASCADE_CODE(MapPages(dst_addr, page_linked_list, KMemoryPermission::None));
|
||||
// Create page groups for the memory being mapped.
|
||||
KPageLinkedList pg;
|
||||
AddRegionToPages(src_address, num_pages, pg);
|
||||
|
||||
block_guard.Cancel();
|
||||
// Reprotect the source as kernel-read/not mapped.
|
||||
const auto new_perm = static_cast<KMemoryPermission>(KMemoryPermission::KernelRead |
|
||||
KMemoryPermission::NotMapped);
|
||||
R_TRY(Operate(src_address, num_pages, new_perm, OperationType::ChangePermissions));
|
||||
|
||||
// Ensure that we unprotect the source pages on failure.
|
||||
auto unprot_guard = SCOPE_GUARD({
|
||||
ASSERT(this->Operate(src_address, num_pages, src_perm, OperationType::ChangePermissions)
|
||||
.IsSuccess());
|
||||
});
|
||||
|
||||
// Map the alias pages.
|
||||
R_TRY(MapPages(dst_address, pg, new_perm));
|
||||
|
||||
// We successfully mapped the alias pages, so we don't need to unprotect the src pages on
|
||||
// failure.
|
||||
unprot_guard.Cancel();
|
||||
|
||||
// Apply the memory block updates.
|
||||
block_manager->Update(src_address, num_pages, src_state, new_perm,
|
||||
KMemoryAttribute::Locked);
|
||||
block_manager->Update(dst_address, num_pages, KMemoryState::AliasCode, new_perm,
|
||||
KMemoryAttribute::None);
|
||||
}
|
||||
|
||||
block_manager->Update(src_addr, num_pages, state, KMemoryPermission::None,
|
||||
KMemoryAttribute::Locked);
|
||||
block_manager->Update(dst_addr, num_pages, KMemoryState::AliasCode);
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KPageTable::UnmapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
|
||||
ResultCode KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size) {
|
||||
// Validate the mapping request.
|
||||
R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode),
|
||||
ResultInvalidMemoryRegion);
|
||||
|
||||
// Lock the table.
|
||||
KScopedLightLock lk(general_lock);
|
||||
|
||||
if (!size) {
|
||||
return ResultSuccess;
|
||||
// Verify that the source memory is locked normal heap.
|
||||
std::size_t num_src_allocator_blocks{};
|
||||
R_TRY(this->CheckMemoryState(std::addressof(num_src_allocator_blocks), src_address, size,
|
||||
KMemoryState::All, KMemoryState::Normal, KMemoryPermission::None,
|
||||
KMemoryPermission::None, KMemoryAttribute::All,
|
||||
KMemoryAttribute::Locked));
|
||||
|
||||
// Verify that the destination memory is aliasable code.
|
||||
std::size_t num_dst_allocator_blocks{};
|
||||
R_TRY(this->CheckMemoryStateContiguous(
|
||||
std::addressof(num_dst_allocator_blocks), dst_address, size, KMemoryState::FlagCanCodeAlias,
|
||||
KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None,
|
||||
KMemoryAttribute::All, KMemoryAttribute::None));
|
||||
|
||||
// Determine whether any pages being unmapped are code.
|
||||
bool any_code_pages = false;
|
||||
{
|
||||
KMemoryBlockManager::const_iterator it = block_manager->FindIterator(dst_address);
|
||||
while (true) {
|
||||
// Get the memory info.
|
||||
const KMemoryInfo info = it->GetMemoryInfo();
|
||||
|
||||
// Check if the memory has code flag.
|
||||
if ((info.GetState() & KMemoryState::FlagCode) != KMemoryState::None) {
|
||||
any_code_pages = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if we're done.
|
||||
if (dst_address + size - 1 <= info.GetLastAddress()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Advance.
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
const std::size_t num_pages{size / PageSize};
|
||||
// Ensure that we maintain the instruction cache.
|
||||
bool reprotected_pages = false;
|
||||
SCOPE_EXIT({
|
||||
if (reprotected_pages && any_code_pages) {
|
||||
system.InvalidateCpuInstructionCacheRange(dst_address, size);
|
||||
}
|
||||
});
|
||||
|
||||
CASCADE_CODE(CheckMemoryState(nullptr, nullptr, nullptr, nullptr, src_addr, size,
|
||||
KMemoryState::All, KMemoryState::Normal, KMemoryPermission::None,
|
||||
KMemoryPermission::None, KMemoryAttribute::Mask,
|
||||
KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped));
|
||||
// Unmap.
|
||||
{
|
||||
// Determine the number of pages being operated on.
|
||||
const std::size_t num_pages = size / PageSize;
|
||||
|
||||
KMemoryState state{};
|
||||
CASCADE_CODE(CheckMemoryState(
|
||||
&state, nullptr, nullptr, nullptr, dst_addr, PageSize, KMemoryState::FlagCanCodeAlias,
|
||||
KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None,
|
||||
KMemoryAttribute::Mask, KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped));
|
||||
CASCADE_CODE(CheckMemoryState(dst_addr, size, KMemoryState::All, state, KMemoryPermission::None,
|
||||
KMemoryPermission::None, KMemoryAttribute::Mask,
|
||||
KMemoryAttribute::None));
|
||||
CASCADE_CODE(Operate(dst_addr, num_pages, KMemoryPermission::None, OperationType::Unmap));
|
||||
// Unmap the aliased copy of the pages.
|
||||
R_TRY(Operate(dst_address, num_pages, KMemoryPermission::None, OperationType::Unmap));
|
||||
|
||||
block_manager->Update(dst_addr, num_pages, KMemoryState::Free);
|
||||
block_manager->Update(src_addr, num_pages, KMemoryState::Normal,
|
||||
KMemoryPermission::UserReadWrite);
|
||||
// Try to set the permissions for the source pages back to what they should be.
|
||||
R_TRY(Operate(src_address, num_pages, KMemoryPermission::UserReadWrite,
|
||||
OperationType::ChangePermissions));
|
||||
|
||||
system.InvalidateCpuInstructionCacheRange(dst_addr, size);
|
||||
// Apply the memory block updates.
|
||||
block_manager->Update(dst_address, num_pages, KMemoryState::None);
|
||||
block_manager->Update(src_address, num_pages, KMemoryState::Normal,
|
||||
KMemoryPermission::UserReadWrite);
|
||||
|
||||
// Note that we reprotected pages.
|
||||
reprotected_pages = true;
|
||||
}
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
@@ -400,148 +452,476 @@ ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size,
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KPageTable::MapPhysicalMemory(VAddr addr, std::size_t size) {
|
||||
ResultCode KPageTable::MapPhysicalMemory(VAddr address, std::size_t size) {
|
||||
// Lock the physical memory lock.
|
||||
KScopedLightLock map_phys_mem_lk(map_physical_memory_lock);
|
||||
|
||||
// Lock the table.
|
||||
KScopedLightLock lk(general_lock);
|
||||
// Calculate the last address for convenience.
|
||||
const VAddr last_address = address + size - 1;
|
||||
|
||||
std::size_t mapped_size{};
|
||||
const VAddr end_addr{addr + size};
|
||||
// Define iteration variables.
|
||||
VAddr cur_address;
|
||||
std::size_t mapped_size;
|
||||
|
||||
block_manager->IterateForRange(addr, end_addr, [&](const KMemoryInfo& info) {
|
||||
if (info.state != KMemoryState::Free) {
|
||||
mapped_size += GetSizeInRange(info, addr, end_addr);
|
||||
}
|
||||
});
|
||||
// The entire mapping process can be retried.
|
||||
while (true) {
|
||||
// Check if the memory is already mapped.
|
||||
{
|
||||
// Lock the table.
|
||||
KScopedLightLock lk(general_lock);
|
||||
|
||||
if (mapped_size == size) {
|
||||
return ResultSuccess;
|
||||
}
|
||||
// Iterate over the memory.
|
||||
cur_address = address;
|
||||
mapped_size = 0;
|
||||
|
||||
const std::size_t remaining_size{size - mapped_size};
|
||||
const std::size_t remaining_pages{remaining_size / PageSize};
|
||||
auto it = block_manager->FindIterator(cur_address);
|
||||
while (true) {
|
||||
// Check that the iterator is valid.
|
||||
ASSERT(it != block_manager->end());
|
||||
|
||||
// Reserve the memory from the process resource limit.
|
||||
KScopedResourceReservation memory_reservation(
|
||||
system.Kernel().CurrentProcess()->GetResourceLimit(), LimitableResource::PhysicalMemory,
|
||||
remaining_size);
|
||||
if (!memory_reservation.Succeeded()) {
|
||||
LOG_ERROR(Kernel, "Could not reserve remaining {:X} bytes", remaining_size);
|
||||
return ResultLimitReached;
|
||||
}
|
||||
// Get the memory info.
|
||||
const KMemoryInfo info = it->GetMemoryInfo();
|
||||
|
||||
KPageLinkedList page_linked_list;
|
||||
// Check if we're done.
|
||||
if (last_address <= info.GetLastAddress()) {
|
||||
if (info.GetState() != KMemoryState::Free) {
|
||||
mapped_size += (last_address + 1 - cur_address);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
CASCADE_CODE(system.Kernel().MemoryManager().Allocate(page_linked_list, remaining_pages,
|
||||
memory_pool, allocation_option));
|
||||
// Track the memory if it's mapped.
|
||||
if (info.GetState() != KMemoryState::Free) {
|
||||
mapped_size += VAddr(info.GetEndAddress()) - cur_address;
|
||||
}
|
||||
|
||||
// We succeeded, so commit the memory reservation.
|
||||
memory_reservation.Commit();
|
||||
|
||||
// Map the memory.
|
||||
auto node{page_linked_list.Nodes().begin()};
|
||||
PAddr map_addr{node->GetAddress()};
|
||||
std::size_t src_num_pages{node->GetNumPages()};
|
||||
block_manager->IterateForRange(addr, end_addr, [&](const KMemoryInfo& info) {
|
||||
if (info.state != KMemoryState::Free) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::size_t dst_num_pages{GetSizeInRange(info, addr, end_addr) / PageSize};
|
||||
VAddr dst_addr{GetAddressInRange(info, addr)};
|
||||
|
||||
while (dst_num_pages) {
|
||||
if (!src_num_pages) {
|
||||
node = std::next(node);
|
||||
map_addr = node->GetAddress();
|
||||
src_num_pages = node->GetNumPages();
|
||||
// Advance.
|
||||
cur_address = info.GetEndAddress();
|
||||
++it;
|
||||
}
|
||||
|
||||
const std::size_t num_pages{std::min(src_num_pages, dst_num_pages)};
|
||||
Operate(dst_addr, num_pages, KMemoryPermission::UserReadWrite, OperationType::Map,
|
||||
map_addr);
|
||||
|
||||
dst_addr += num_pages * PageSize;
|
||||
map_addr += num_pages * PageSize;
|
||||
src_num_pages -= num_pages;
|
||||
dst_num_pages -= num_pages;
|
||||
// If the size mapped is the size requested, we've nothing to do.
|
||||
R_SUCCEED_IF(size == mapped_size);
|
||||
}
|
||||
});
|
||||
|
||||
mapped_physical_memory_size += remaining_size;
|
||||
// Allocate and map the memory.
|
||||
{
|
||||
// Reserve the memory from the process resource limit.
|
||||
KScopedResourceReservation memory_reservation(
|
||||
system.Kernel().CurrentProcess()->GetResourceLimit(),
|
||||
LimitableResource::PhysicalMemory, size - mapped_size);
|
||||
R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
|
||||
|
||||
const std::size_t num_pages{size / PageSize};
|
||||
block_manager->Update(addr, num_pages, KMemoryState::Free, KMemoryPermission::None,
|
||||
KMemoryAttribute::None, KMemoryState::Normal,
|
||||
KMemoryPermission::UserReadWrite, KMemoryAttribute::None);
|
||||
// Allocate pages for the new memory.
|
||||
KPageLinkedList pg;
|
||||
R_TRY(system.Kernel().MemoryManager().AllocateAndOpenForProcess(
|
||||
&pg, (size - mapped_size) / PageSize,
|
||||
KMemoryManager::EncodeOption(memory_pool, allocation_option), 0, 0));
|
||||
|
||||
return ResultSuccess;
|
||||
// Map the memory.
|
||||
{
|
||||
// Lock the table.
|
||||
KScopedLightLock lk(general_lock);
|
||||
|
||||
size_t num_allocator_blocks = 0;
|
||||
|
||||
// Verify that nobody has mapped memory since we first checked.
|
||||
{
|
||||
// Iterate over the memory.
|
||||
size_t checked_mapped_size = 0;
|
||||
cur_address = address;
|
||||
|
||||
auto it = block_manager->FindIterator(cur_address);
|
||||
while (true) {
|
||||
// Check that the iterator is valid.
|
||||
ASSERT(it != block_manager->end());
|
||||
|
||||
// Get the memory info.
|
||||
const KMemoryInfo info = it->GetMemoryInfo();
|
||||
|
||||
const bool is_free = info.GetState() == KMemoryState::Free;
|
||||
if (is_free) {
|
||||
if (info.GetAddress() < address) {
|
||||
++num_allocator_blocks;
|
||||
}
|
||||
if (last_address < info.GetLastAddress()) {
|
||||
++num_allocator_blocks;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we're done.
|
||||
if (last_address <= info.GetLastAddress()) {
|
||||
if (!is_free) {
|
||||
checked_mapped_size += (last_address + 1 - cur_address);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Track the memory if it's mapped.
|
||||
if (!is_free) {
|
||||
checked_mapped_size += VAddr(info.GetEndAddress()) - cur_address;
|
||||
}
|
||||
|
||||
// Advance.
|
||||
cur_address = info.GetEndAddress();
|
||||
++it;
|
||||
}
|
||||
|
||||
// If the size now isn't what it was before, somebody mapped or unmapped
|
||||
// concurrently. If this happened, retry.
|
||||
if (mapped_size != checked_mapped_size) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Reset the current tracking address, and make sure we clean up on failure.
|
||||
cur_address = address;
|
||||
auto unmap_guard = detail::ScopeExit([&] {
|
||||
if (cur_address > address) {
|
||||
const VAddr last_unmap_address = cur_address - 1;
|
||||
|
||||
// Iterate, unmapping the pages.
|
||||
cur_address = address;
|
||||
|
||||
auto it = block_manager->FindIterator(cur_address);
|
||||
while (true) {
|
||||
// Check that the iterator is valid.
|
||||
ASSERT(it != block_manager->end());
|
||||
|
||||
// Get the memory info.
|
||||
const KMemoryInfo info = it->GetMemoryInfo();
|
||||
|
||||
// If the memory state is free, we mapped it and need to unmap it.
|
||||
if (info.GetState() == KMemoryState::Free) {
|
||||
// Determine the range to unmap.
|
||||
const size_t cur_pages =
|
||||
std::min(VAddr(info.GetEndAddress()) - cur_address,
|
||||
last_unmap_address + 1 - cur_address) /
|
||||
PageSize;
|
||||
|
||||
// Unmap.
|
||||
ASSERT(Operate(cur_address, cur_pages, KMemoryPermission::None,
|
||||
OperationType::Unmap)
|
||||
.IsSuccess());
|
||||
}
|
||||
|
||||
// Check if we're done.
|
||||
if (last_unmap_address <= info.GetLastAddress()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Advance.
|
||||
cur_address = info.GetEndAddress();
|
||||
++it;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Iterate over the memory.
|
||||
auto pg_it = pg.Nodes().begin();
|
||||
PAddr pg_phys_addr = pg_it->GetAddress();
|
||||
size_t pg_pages = pg_it->GetNumPages();
|
||||
|
||||
auto it = block_manager->FindIterator(cur_address);
|
||||
while (true) {
|
||||
// Check that the iterator is valid.
|
||||
ASSERT(it != block_manager->end());
|
||||
|
||||
// Get the memory info.
|
||||
const KMemoryInfo info = it->GetMemoryInfo();
|
||||
|
||||
// If it's unmapped, we need to map it.
|
||||
if (info.GetState() == KMemoryState::Free) {
|
||||
// Determine the range to map.
|
||||
size_t map_pages = std::min(VAddr(info.GetEndAddress()) - cur_address,
|
||||
last_address + 1 - cur_address) /
|
||||
PageSize;
|
||||
|
||||
// While we have pages to map, map them.
|
||||
while (map_pages > 0) {
|
||||
// Check if we're at the end of the physical block.
|
||||
if (pg_pages == 0) {
|
||||
// Ensure there are more pages to map.
|
||||
ASSERT(pg_it != pg.Nodes().end());
|
||||
|
||||
// Advance our physical block.
|
||||
++pg_it;
|
||||
pg_phys_addr = pg_it->GetAddress();
|
||||
pg_pages = pg_it->GetNumPages();
|
||||
}
|
||||
|
||||
// Map whatever we can.
|
||||
const size_t cur_pages = std::min(pg_pages, map_pages);
|
||||
R_TRY(Operate(cur_address, cur_pages, KMemoryPermission::UserReadWrite,
|
||||
OperationType::Map, pg_phys_addr));
|
||||
|
||||
// Advance.
|
||||
cur_address += cur_pages * PageSize;
|
||||
map_pages -= cur_pages;
|
||||
|
||||
pg_phys_addr += cur_pages * PageSize;
|
||||
pg_pages -= cur_pages;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we're done.
|
||||
if (last_address <= info.GetLastAddress()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Advance.
|
||||
cur_address = info.GetEndAddress();
|
||||
++it;
|
||||
}
|
||||
|
||||
// We succeeded, so commit the memory reservation.
|
||||
memory_reservation.Commit();
|
||||
|
||||
// Increase our tracked mapped size.
|
||||
mapped_physical_memory_size += (size - mapped_size);
|
||||
|
||||
// Update the relevant memory blocks.
|
||||
block_manager->Update(address, size / PageSize, KMemoryState::Free,
|
||||
KMemoryPermission::None, KMemoryAttribute::None,
|
||||
KMemoryState::Normal, KMemoryPermission::UserReadWrite,
|
||||
KMemoryAttribute::None);
|
||||
|
||||
// Cancel our guard.
|
||||
unmap_guard.Cancel();
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ResultCode KPageTable::UnmapPhysicalMemory(VAddr addr, std::size_t size) {
|
||||
ResultCode KPageTable::UnmapPhysicalMemory(VAddr address, std::size_t size) {
|
||||
// Lock the physical memory lock.
|
||||
KScopedLightLock map_phys_mem_lk(map_physical_memory_lock);
|
||||
|
||||
// Lock the table.
|
||||
KScopedLightLock lk(general_lock);
|
||||
|
||||
const VAddr end_addr{addr + size};
|
||||
ResultCode result{ResultSuccess};
|
||||
std::size_t mapped_size{};
|
||||
// Calculate the last address for convenience.
|
||||
const VAddr last_address = address + size - 1;
|
||||
|
||||
// Verify that the region can be unmapped
|
||||
block_manager->IterateForRange(addr, end_addr, [&](const KMemoryInfo& info) {
|
||||
if (info.state == KMemoryState::Normal) {
|
||||
if (info.attribute != KMemoryAttribute::None) {
|
||||
result = ResultInvalidCurrentMemory;
|
||||
return;
|
||||
// Define iteration variables.
|
||||
VAddr cur_address = 0;
|
||||
std::size_t mapped_size = 0;
|
||||
std::size_t num_allocator_blocks = 0;
|
||||
|
||||
// Check if the memory is mapped.
|
||||
{
|
||||
// Iterate over the memory.
|
||||
cur_address = address;
|
||||
mapped_size = 0;
|
||||
|
||||
auto it = block_manager->FindIterator(cur_address);
|
||||
while (true) {
|
||||
// Check that the iterator is valid.
|
||||
ASSERT(it != block_manager->end());
|
||||
|
||||
// Get the memory info.
|
||||
const KMemoryInfo info = it->GetMemoryInfo();
|
||||
|
||||
// Verify the memory's state.
|
||||
const bool is_normal = info.GetState() == KMemoryState::Normal &&
|
||||
info.GetAttribute() == KMemoryAttribute::None;
|
||||
const bool is_free = info.GetState() == KMemoryState::Free;
|
||||
R_UNLESS(is_normal || is_free, ResultInvalidCurrentMemory);
|
||||
|
||||
if (is_normal) {
|
||||
R_UNLESS(info.GetAttribute() == KMemoryAttribute::None, ResultInvalidCurrentMemory);
|
||||
|
||||
if (info.GetAddress() < address) {
|
||||
++num_allocator_blocks;
|
||||
}
|
||||
if (last_address < info.GetLastAddress()) {
|
||||
++num_allocator_blocks;
|
||||
}
|
||||
}
|
||||
mapped_size += GetSizeInRange(info, addr, end_addr);
|
||||
} else if (info.state != KMemoryState::Free) {
|
||||
result = ResultInvalidCurrentMemory;
|
||||
|
||||
// Check if we're done.
|
||||
if (last_address <= info.GetLastAddress()) {
|
||||
if (is_normal) {
|
||||
mapped_size += (last_address + 1 - cur_address);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Track the memory if it's mapped.
|
||||
if (is_normal) {
|
||||
mapped_size += VAddr(info.GetEndAddress()) - cur_address;
|
||||
}
|
||||
|
||||
// Advance.
|
||||
cur_address = info.GetEndAddress();
|
||||
++it;
|
||||
}
|
||||
});
|
||||
|
||||
if (result.IsError()) {
|
||||
return result;
|
||||
// If there's nothing mapped, we've nothing to do.
|
||||
R_SUCCEED_IF(mapped_size == 0);
|
||||
}
|
||||
|
||||
if (!mapped_size) {
|
||||
return ResultSuccess;
|
||||
// Make a page group for the unmap region.
|
||||
KPageLinkedList pg;
|
||||
{
|
||||
auto& impl = this->PageTableImpl();
|
||||
|
||||
// Begin traversal.
|
||||
Common::PageTable::TraversalContext context;
|
||||
Common::PageTable::TraversalEntry cur_entry = {.phys_addr = 0, .block_size = 0};
|
||||
bool cur_valid = false;
|
||||
Common::PageTable::TraversalEntry next_entry;
|
||||
bool next_valid = false;
|
||||
size_t tot_size = 0;
|
||||
|
||||
cur_address = address;
|
||||
next_valid = impl.BeginTraversal(next_entry, context, cur_address);
|
||||
next_entry.block_size =
|
||||
(next_entry.block_size - (next_entry.phys_addr & (next_entry.block_size - 1)));
|
||||
|
||||
// Iterate, building the group.
|
||||
while (true) {
|
||||
if ((!next_valid && !cur_valid) ||
|
||||
(next_valid && cur_valid &&
|
||||
next_entry.phys_addr == cur_entry.phys_addr + cur_entry.block_size)) {
|
||||
cur_entry.block_size += next_entry.block_size;
|
||||
} else {
|
||||
if (cur_valid) {
|
||||
// ASSERT(IsHeapPhysicalAddress(cur_entry.phys_addr));
|
||||
R_TRY(pg.AddBlock(cur_entry.phys_addr, cur_entry.block_size / PageSize));
|
||||
}
|
||||
|
||||
// Update tracking variables.
|
||||
tot_size += cur_entry.block_size;
|
||||
cur_entry = next_entry;
|
||||
cur_valid = next_valid;
|
||||
}
|
||||
|
||||
if (cur_entry.block_size + tot_size >= size) {
|
||||
break;
|
||||
}
|
||||
|
||||
next_valid = impl.ContinueTraversal(next_entry, context);
|
||||
}
|
||||
|
||||
// Add the last block.
|
||||
if (cur_valid) {
|
||||
// ASSERT(IsHeapPhysicalAddress(cur_entry.phys_addr));
|
||||
R_TRY(pg.AddBlock(cur_entry.phys_addr, (size - tot_size) / PageSize));
|
||||
}
|
||||
}
|
||||
ASSERT(pg.GetNumPages() == mapped_size / PageSize);
|
||||
|
||||
// Unmap each region within the range
|
||||
KPageLinkedList page_linked_list;
|
||||
block_manager->IterateForRange(addr, end_addr, [&](const KMemoryInfo& info) {
|
||||
if (info.state == KMemoryState::Normal) {
|
||||
const std::size_t block_size{GetSizeInRange(info, addr, end_addr)};
|
||||
const std::size_t block_num_pages{block_size / PageSize};
|
||||
const VAddr block_addr{GetAddressInRange(info, addr)};
|
||||
// Reset the current tracking address, and make sure we clean up on failure.
|
||||
cur_address = address;
|
||||
auto remap_guard = detail::ScopeExit([&] {
|
||||
if (cur_address > address) {
|
||||
const VAddr last_map_address = cur_address - 1;
|
||||
cur_address = address;
|
||||
|
||||
AddRegionToPages(block_addr, block_size / PageSize, page_linked_list);
|
||||
// Iterate over the memory we unmapped.
|
||||
auto it = block_manager->FindIterator(cur_address);
|
||||
auto pg_it = pg.Nodes().begin();
|
||||
PAddr pg_phys_addr = pg_it->GetAddress();
|
||||
size_t pg_pages = pg_it->GetNumPages();
|
||||
|
||||
if (result = Operate(block_addr, block_num_pages, KMemoryPermission::None,
|
||||
OperationType::Unmap);
|
||||
result.IsError()) {
|
||||
return;
|
||||
while (true) {
|
||||
// Get the memory info for the pages we unmapped, convert to property.
|
||||
const KMemoryInfo info = it->GetMemoryInfo();
|
||||
|
||||
// If the memory is normal, we unmapped it and need to re-map it.
|
||||
if (info.GetState() == KMemoryState::Normal) {
|
||||
// Determine the range to map.
|
||||
size_t map_pages = std::min(VAddr(info.GetEndAddress()) - cur_address,
|
||||
last_map_address + 1 - cur_address) /
|
||||
PageSize;
|
||||
|
||||
// While we have pages to map, map them.
|
||||
while (map_pages > 0) {
|
||||
// Check if we're at the end of the physical block.
|
||||
if (pg_pages == 0) {
|
||||
// Ensure there are more pages to map.
|
||||
ASSERT(pg_it != pg.Nodes().end());
|
||||
|
||||
// Advance our physical block.
|
||||
++pg_it;
|
||||
pg_phys_addr = pg_it->GetAddress();
|
||||
pg_pages = pg_it->GetNumPages();
|
||||
}
|
||||
|
||||
// Map whatever we can.
|
||||
const size_t cur_pages = std::min(pg_pages, map_pages);
|
||||
ASSERT(this->Operate(cur_address, cur_pages, info.GetPermission(),
|
||||
OperationType::Map, pg_phys_addr) == ResultSuccess);
|
||||
|
||||
// Advance.
|
||||
cur_address += cur_pages * PageSize;
|
||||
map_pages -= cur_pages;
|
||||
|
||||
pg_phys_addr += cur_pages * PageSize;
|
||||
pg_pages -= cur_pages;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we're done.
|
||||
if (last_map_address <= info.GetLastAddress()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Advance.
|
||||
++it;
|
||||
}
|
||||
}
|
||||
});
|
||||
if (result.IsError()) {
|
||||
return result;
|
||||
|
||||
// Iterate over the memory, unmapping as we go.
|
||||
auto it = block_manager->FindIterator(cur_address);
|
||||
while (true) {
|
||||
// Check that the iterator is valid.
|
||||
ASSERT(it != block_manager->end());
|
||||
|
||||
// Get the memory info.
|
||||
const KMemoryInfo info = it->GetMemoryInfo();
|
||||
|
||||
// If the memory state is normal, we need to unmap it.
|
||||
if (info.GetState() == KMemoryState::Normal) {
|
||||
// Determine the range to unmap.
|
||||
const size_t cur_pages = std::min(VAddr(info.GetEndAddress()) - cur_address,
|
||||
last_address + 1 - cur_address) /
|
||||
PageSize;
|
||||
|
||||
// Unmap.
|
||||
R_TRY(Operate(cur_address, cur_pages, KMemoryPermission::None, OperationType::Unmap));
|
||||
}
|
||||
|
||||
// Check if we're done.
|
||||
if (last_address <= info.GetLastAddress()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Advance.
|
||||
cur_address = info.GetEndAddress();
|
||||
++it;
|
||||
}
|
||||
|
||||
const std::size_t num_pages{size / PageSize};
|
||||
system.Kernel().MemoryManager().Free(page_linked_list, num_pages, memory_pool,
|
||||
allocation_option);
|
||||
|
||||
block_manager->Update(addr, num_pages, KMemoryState::Free);
|
||||
|
||||
// Release the memory resource.
|
||||
mapped_physical_memory_size -= mapped_size;
|
||||
auto process{system.Kernel().CurrentProcess()};
|
||||
process->GetResourceLimit()->Release(LimitableResource::PhysicalMemory, mapped_size);
|
||||
mapped_physical_memory_size -= mapped_size;
|
||||
|
||||
// Update memory blocks.
|
||||
block_manager->Update(address, size / PageSize, KMemoryState::Free, KMemoryPermission::None,
|
||||
KMemoryAttribute::None);
|
||||
|
||||
// TODO(bunnei): This is a workaround until the next set of changes, where we add reference
|
||||
// counting for mapped pages. Until then, we must manually close the reference to the page
|
||||
// group.
|
||||
system.Kernel().MemoryManager().Close(pg);
|
||||
|
||||
// We succeeded.
|
||||
remap_guard.Cancel();
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
@@ -681,9 +1061,8 @@ ResultCode KPageTable::UnmapPages(VAddr addr, const KPageLinkedList& page_linked
|
||||
VAddr cur_addr{addr};
|
||||
|
||||
for (const auto& node : page_linked_list.Nodes()) {
|
||||
const std::size_t num_pages{(addr - cur_addr) / PageSize};
|
||||
if (const auto result{
|
||||
Operate(addr, num_pages, KMemoryPermission::None, OperationType::Unmap)};
|
||||
if (const auto result{Operate(cur_addr, node.GetNumPages(), KMemoryPermission::None,
|
||||
OperationType::Unmap)};
|
||||
result.IsError()) {
|
||||
return result;
|
||||
}
|
||||
@@ -966,9 +1345,16 @@ ResultCode KPageTable::SetHeapSize(VAddr* out, std::size_t size) {
|
||||
R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
|
||||
|
||||
// Allocate pages for the heap extension.
|
||||
KPageLinkedList page_linked_list;
|
||||
R_TRY(system.Kernel().MemoryManager().Allocate(page_linked_list, allocation_size / PageSize,
|
||||
memory_pool, allocation_option));
|
||||
KPageLinkedList pg;
|
||||
R_TRY(system.Kernel().MemoryManager().AllocateAndOpen(
|
||||
&pg, allocation_size / PageSize,
|
||||
KMemoryManager::EncodeOption(memory_pool, allocation_option)));
|
||||
|
||||
// Clear all the newly allocated pages.
|
||||
for (const auto& it : pg.Nodes()) {
|
||||
std::memset(system.DeviceMemory().GetPointer(it.GetAddress()), heap_fill_value,
|
||||
it.GetSize());
|
||||
}
|
||||
|
||||
// Map the pages.
|
||||
{
|
||||
@@ -987,7 +1373,7 @@ ResultCode KPageTable::SetHeapSize(VAddr* out, std::size_t size) {
|
||||
|
||||
// Map the pages.
|
||||
const auto num_pages = allocation_size / PageSize;
|
||||
R_TRY(Operate(current_heap_end, num_pages, page_linked_list, OperationType::MapGroup));
|
||||
R_TRY(Operate(current_heap_end, num_pages, pg, OperationType::MapGroup));
|
||||
|
||||
// Clear all the newly allocated pages.
|
||||
for (std::size_t cur_page = 0; cur_page < num_pages; ++cur_page) {
|
||||
@@ -1035,8 +1421,9 @@ ResultVal<VAddr> KPageTable::AllocateAndMapMemory(std::size_t needed_num_pages,
|
||||
R_TRY(Operate(addr, needed_num_pages, perm, OperationType::Map, map_addr));
|
||||
} else {
|
||||
KPageLinkedList page_group;
|
||||
R_TRY(system.Kernel().MemoryManager().Allocate(page_group, needed_num_pages, memory_pool,
|
||||
allocation_option));
|
||||
R_TRY(system.Kernel().MemoryManager().AllocateAndOpenForProcess(
|
||||
&page_group, needed_num_pages,
|
||||
KMemoryManager::EncodeOption(memory_pool, allocation_option), 0, 0));
|
||||
R_TRY(Operate(addr, needed_num_pages, page_group, OperationType::MapGroup));
|
||||
}
|
||||
|
||||
@@ -1243,7 +1630,7 @@ ResultCode KPageTable::Operate(VAddr addr, std::size_t num_pages, KMemoryPermiss
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
constexpr VAddr KPageTable::GetRegionAddress(KMemoryState state) const {
|
||||
VAddr KPageTable::GetRegionAddress(KMemoryState state) const {
|
||||
switch (state) {
|
||||
case KMemoryState::Free:
|
||||
case KMemoryState::Kernel:
|
||||
@@ -1279,7 +1666,7 @@ constexpr VAddr KPageTable::GetRegionAddress(KMemoryState state) const {
|
||||
}
|
||||
}
|
||||
|
||||
constexpr std::size_t KPageTable::GetRegionSize(KMemoryState state) const {
|
||||
std::size_t KPageTable::GetRegionSize(KMemoryState state) const {
|
||||
switch (state) {
|
||||
case KMemoryState::Free:
|
||||
case KMemoryState::Kernel:
|
||||
|
||||
@@ -36,8 +36,8 @@ public:
|
||||
KMemoryManager::Pool pool);
|
||||
ResultCode MapProcessCode(VAddr addr, std::size_t pages_count, KMemoryState state,
|
||||
KMemoryPermission perm);
|
||||
ResultCode MapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
|
||||
ResultCode UnmapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
|
||||
ResultCode MapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size);
|
||||
ResultCode UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size);
|
||||
ResultCode UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table,
|
||||
VAddr src_addr);
|
||||
ResultCode MapPhysicalMemory(VAddr addr, std::size_t size);
|
||||
@@ -102,8 +102,8 @@ private:
|
||||
OperationType operation);
|
||||
ResultCode Operate(VAddr addr, std::size_t num_pages, KMemoryPermission perm,
|
||||
OperationType operation, PAddr map_addr = 0);
|
||||
constexpr VAddr GetRegionAddress(KMemoryState state) const;
|
||||
constexpr std::size_t GetRegionSize(KMemoryState state) const;
|
||||
VAddr GetRegionAddress(KMemoryState state) const;
|
||||
std::size_t GetRegionSize(KMemoryState state) const;
|
||||
|
||||
ResultCode CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr,
|
||||
std::size_t size, KMemoryState state_mask,
|
||||
@@ -253,9 +253,10 @@ public:
|
||||
constexpr bool IsInsideASLRRegion(VAddr address, std::size_t size) const {
|
||||
return !IsOutsideASLRRegion(address, size);
|
||||
}
|
||||
|
||||
PAddr GetPhysicalAddr(VAddr addr) {
|
||||
ASSERT(IsLockedByCurrentThread());
|
||||
constexpr std::size_t GetNumGuardPages() const {
|
||||
return IsKernel() ? 1 : 4;
|
||||
}
|
||||
PAddr GetPhysicalAddr(VAddr addr) const {
|
||||
const auto backing_addr = page_table_impl.backing_addr[addr >> PageBits];
|
||||
ASSERT(backing_addr);
|
||||
return backing_addr + addr;
|
||||
@@ -276,10 +277,6 @@ private:
|
||||
return is_aslr_enabled;
|
||||
}
|
||||
|
||||
constexpr std::size_t GetNumGuardPages() const {
|
||||
return IsKernel() ? 1 : 4;
|
||||
}
|
||||
|
||||
constexpr bool ContainsPages(VAddr addr, std::size_t num_pages) const {
|
||||
return (address_space_start <= addr) &&
|
||||
(num_pages <= (address_space_end - address_space_start) / PageSize) &&
|
||||
@@ -311,6 +308,8 @@ private:
|
||||
bool is_kernel{};
|
||||
bool is_aslr_enabled{};
|
||||
|
||||
u32 heap_fill_value{};
|
||||
|
||||
KMemoryManager::Pool memory_pool{KMemoryManager::Pool::Application};
|
||||
KMemoryManager::Direction allocation_option{KMemoryManager::Direction::FromFront};
|
||||
|
||||
|
||||
@@ -123,12 +123,11 @@ private:
|
||||
};
|
||||
|
||||
ResultCode KProcess::Initialize(KProcess* process, Core::System& system, std::string process_name,
|
||||
ProcessType type) {
|
||||
ProcessType type, KResourceLimit* res_limit) {
|
||||
auto& kernel = system.Kernel();
|
||||
|
||||
process->name = std::move(process_name);
|
||||
|
||||
process->resource_limit = kernel.GetSystemResourceLimit();
|
||||
process->resource_limit = res_limit;
|
||||
process->status = ProcessStatus::Created;
|
||||
process->program_id = 0;
|
||||
process->process_id = type == ProcessType::KernelInternal ? kernel.CreateNewKernelProcessID()
|
||||
@@ -143,9 +142,6 @@ ResultCode KProcess::Initialize(KProcess* process, Core::System& system, std::st
|
||||
|
||||
kernel.AppendNewProcess(process);
|
||||
|
||||
// Open a reference to the resource limit.
|
||||
process->resource_limit->Open();
|
||||
|
||||
// Clear remaining fields.
|
||||
process->num_running_threads = 0;
|
||||
process->is_signaled = false;
|
||||
@@ -153,6 +149,9 @@ ResultCode KProcess::Initialize(KProcess* process, Core::System& system, std::st
|
||||
process->is_suspended = false;
|
||||
process->schedule_count = 0;
|
||||
|
||||
// Open a reference to the resource limit.
|
||||
process->resource_limit->Open();
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ public:
|
||||
static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4;
|
||||
|
||||
static ResultCode Initialize(KProcess* process, Core::System& system, std::string process_name,
|
||||
ProcessType type);
|
||||
ProcessType type, KResourceLimit* res_limit);
|
||||
|
||||
/// Gets a reference to the process' page table.
|
||||
KPageTable& PageTable() {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/kernel/k_resource_limit.h"
|
||||
#include "core/hle/kernel/svc_results.h"
|
||||
@@ -151,4 +152,22 @@ void KResourceLimit::Release(LimitableResource which, s64 value, s64 hint) {
|
||||
}
|
||||
}
|
||||
|
||||
KResourceLimit* CreateResourceLimitForProcess(Core::System& system, s64 physical_memory_size) {
|
||||
auto* resource_limit = KResourceLimit::Create(system.Kernel());
|
||||
resource_limit->Initialize(&system.CoreTiming());
|
||||
|
||||
// Initialize default resource limit values.
|
||||
// TODO(bunnei): These values are the system defaults, the limits for service processes are
|
||||
// lower. These should use the correct limit values.
|
||||
|
||||
ASSERT(resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, physical_memory_size)
|
||||
.IsSuccess());
|
||||
ASSERT(resource_limit->SetLimitValue(LimitableResource::Threads, 800).IsSuccess());
|
||||
ASSERT(resource_limit->SetLimitValue(LimitableResource::Events, 900).IsSuccess());
|
||||
ASSERT(resource_limit->SetLimitValue(LimitableResource::TransferMemory, 200).IsSuccess());
|
||||
ASSERT(resource_limit->SetLimitValue(LimitableResource::Sessions, 1133).IsSuccess());
|
||||
|
||||
return resource_limit;
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -67,4 +67,7 @@ private:
|
||||
KLightConditionVariable cond_var;
|
||||
const Core::Timing::CoreTiming* core_timing{};
|
||||
};
|
||||
|
||||
KResourceLimit* CreateResourceLimitForProcess(Core::System& system, s64 physical_memory_size);
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -70,13 +70,12 @@ struct KernelCore::Impl {
|
||||
|
||||
// Derive the initial memory layout from the emulated board
|
||||
Init::InitializeSlabResourceCounts(kernel);
|
||||
KMemoryLayout memory_layout;
|
||||
DeriveInitialMemoryLayout(memory_layout);
|
||||
Init::InitializeSlabHeaps(system, memory_layout);
|
||||
DeriveInitialMemoryLayout();
|
||||
Init::InitializeSlabHeaps(system, *memory_layout);
|
||||
|
||||
// Initialize kernel memory and resources.
|
||||
InitializeSystemResourceLimit(kernel, system.CoreTiming(), memory_layout);
|
||||
InitializeMemoryLayout(memory_layout);
|
||||
InitializeSystemResourceLimit(kernel, system.CoreTiming());
|
||||
InitializeMemoryLayout();
|
||||
InitializePageSlab();
|
||||
InitializeSchedulers();
|
||||
InitializeSuspendThreads();
|
||||
@@ -219,12 +218,11 @@ struct KernelCore::Impl {
|
||||
|
||||
// Creates the default system resource limit
|
||||
void InitializeSystemResourceLimit(KernelCore& kernel,
|
||||
const Core::Timing::CoreTiming& core_timing,
|
||||
const KMemoryLayout& memory_layout) {
|
||||
const Core::Timing::CoreTiming& core_timing) {
|
||||
system_resource_limit = KResourceLimit::Create(system.Kernel());
|
||||
system_resource_limit->Initialize(&core_timing);
|
||||
|
||||
const auto [total_size, kernel_size] = memory_layout.GetTotalAndKernelMemorySizes();
|
||||
const auto [total_size, kernel_size] = memory_layout->GetTotalAndKernelMemorySizes();
|
||||
|
||||
// If setting the default system values fails, then something seriously wrong has occurred.
|
||||
ASSERT(system_resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, total_size)
|
||||
@@ -240,13 +238,6 @@ struct KernelCore::Impl {
|
||||
constexpr u64 secure_applet_memory_size{4_MiB};
|
||||
ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemory,
|
||||
secure_applet_memory_size));
|
||||
|
||||
// This memory seems to be reserved on hardware, but is not reserved/used by yuzu.
|
||||
// Likely Horizon OS reserved memory
|
||||
// TODO(ameerj): Derive the memory rather than hardcode it.
|
||||
constexpr u64 unknown_reserved_memory{0x2f896000};
|
||||
ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemory,
|
||||
unknown_reserved_memory));
|
||||
}
|
||||
|
||||
void InitializePreemption(KernelCore& kernel) {
|
||||
@@ -360,16 +351,18 @@ struct KernelCore::Impl {
|
||||
return schedulers[thread_id]->GetCurrentThread();
|
||||
}
|
||||
|
||||
void DeriveInitialMemoryLayout(KMemoryLayout& memory_layout) {
|
||||
void DeriveInitialMemoryLayout() {
|
||||
memory_layout = std::make_unique<KMemoryLayout>();
|
||||
|
||||
// Insert the root region for the virtual memory tree, from which all other regions will
|
||||
// derive.
|
||||
memory_layout.GetVirtualMemoryRegionTree().InsertDirectly(
|
||||
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(
|
||||
memory_layout->GetPhysicalMemoryRegionTree().InsertDirectly(
|
||||
KernelPhysicalAddressSpaceBase,
|
||||
KernelPhysicalAddressSpaceBase + KernelPhysicalAddressSpaceSize - 1);
|
||||
|
||||
@@ -386,7 +379,7 @@ struct KernelCore::Impl {
|
||||
if (!(kernel_region_start + KernelRegionSize - 1 <= KernelVirtualAddressSpaceLast)) {
|
||||
kernel_region_size = KernelVirtualAddressSpaceEnd - kernel_region_start;
|
||||
}
|
||||
ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert(
|
||||
ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert(
|
||||
kernel_region_start, kernel_region_size, KMemoryRegionType_Kernel));
|
||||
|
||||
// Setup the code region.
|
||||
@@ -395,11 +388,11 @@ struct KernelCore::Impl {
|
||||
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(
|
||||
ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert(
|
||||
code_region_start, code_region_size, KMemoryRegionType_KernelCode));
|
||||
|
||||
// Setup board-specific device physical regions.
|
||||
Init::SetupDevicePhysicalMemoryRegions(memory_layout);
|
||||
Init::SetupDevicePhysicalMemoryRegions(*memory_layout);
|
||||
|
||||
// Determine the amount of space needed for the misc region.
|
||||
size_t misc_region_needed_size;
|
||||
@@ -408,7 +401,7 @@ struct KernelCore::Impl {
|
||||
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()) {
|
||||
for (const auto& region : memory_layout->GetPhysicalMemoryRegionTree()) {
|
||||
if (region.HasTypeAttribute(KMemoryRegionAttr_ShouldKernelMap)) {
|
||||
// Check that the region is valid.
|
||||
ASSERT(region.GetEndAddress() != 0);
|
||||
@@ -433,22 +426,22 @@ struct KernelCore::Impl {
|
||||
|
||||
// Setup the misc region.
|
||||
const VAddr misc_region_start =
|
||||
memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegion(
|
||||
memory_layout->GetVirtualMemoryRegionTree().GetRandomAlignedRegion(
|
||||
misc_region_size, MiscRegionAlign, KMemoryRegionType_Kernel);
|
||||
ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert(
|
||||
ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert(
|
||||
misc_region_start, misc_region_size, KMemoryRegionType_KernelMisc));
|
||||
|
||||
// Setup the stack region.
|
||||
constexpr size_t StackRegionSize = 14_MiB;
|
||||
constexpr size_t StackRegionAlign = KernelAslrAlignment;
|
||||
const VAddr stack_region_start =
|
||||
memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegion(
|
||||
memory_layout->GetVirtualMemoryRegionTree().GetRandomAlignedRegion(
|
||||
StackRegionSize, StackRegionAlign, KMemoryRegionType_Kernel);
|
||||
ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert(
|
||||
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();
|
||||
const size_t resource_region_size = memory_layout->GetResourceRegionSizeForInit();
|
||||
|
||||
// Determine the size of the slab region.
|
||||
const size_t slab_region_size =
|
||||
@@ -465,23 +458,23 @@ struct KernelCore::Impl {
|
||||
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(
|
||||
memory_layout->GetVirtualMemoryRegionTree().GetRandomAlignedRegion(
|
||||
slab_region_needed_size, SlabRegionAlign, KMemoryRegionType_Kernel) +
|
||||
(code_end_phys_addr % SlabRegionAlign);
|
||||
ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert(
|
||||
ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert(
|
||||
slab_region_start, slab_region_size, KMemoryRegionType_KernelSlab));
|
||||
|
||||
// Setup the temp region.
|
||||
constexpr size_t TempRegionSize = 128_MiB;
|
||||
constexpr size_t TempRegionAlign = KernelAslrAlignment;
|
||||
const VAddr temp_region_start =
|
||||
memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegion(
|
||||
memory_layout->GetVirtualMemoryRegionTree().GetRandomAlignedRegion(
|
||||
TempRegionSize, TempRegionAlign, KMemoryRegionType_Kernel);
|
||||
ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert(temp_region_start, TempRegionSize,
|
||||
KMemoryRegionType_KernelTemp));
|
||||
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()) {
|
||||
for (auto& region : memory_layout->GetPhysicalMemoryRegionTree()) {
|
||||
// We only care about kernel regions.
|
||||
if (!region.IsDerivedFrom(KMemoryRegionType_Kernel)) {
|
||||
continue;
|
||||
@@ -508,21 +501,21 @@ struct KernelCore::Impl {
|
||||
const size_t map_size =
|
||||
Common::AlignUp(region.GetEndAddress(), PageSize) - map_phys_addr;
|
||||
const VAddr map_virt_addr =
|
||||
memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegionWithGuard(
|
||||
memory_layout->GetVirtualMemoryRegionTree().GetRandomAlignedRegionWithGuard(
|
||||
map_size, PageSize, KMemoryRegionType_KernelMisc, PageSize);
|
||||
ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert(
|
||||
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);
|
||||
Init::SetupDramPhysicalMemoryRegions(*memory_layout);
|
||||
|
||||
// Insert a physical region for the kernel code region.
|
||||
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
|
||||
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(
|
||||
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.
|
||||
@@ -531,12 +524,12 @@ struct KernelCore::Impl {
|
||||
ASSERT(page_table_heap_size / 4_MiB > 2);
|
||||
|
||||
// Insert a physical region for the kernel page table heap region
|
||||
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
|
||||
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()) {
|
||||
for (auto& region : memory_layout->GetPhysicalMemoryRegionTree()) {
|
||||
if (region.GetType() == KMemoryRegionType_Dram) {
|
||||
// Check that the region is valid.
|
||||
ASSERT(region.GetEndAddress() != 0);
|
||||
@@ -548,7 +541,7 @@ struct KernelCore::Impl {
|
||||
|
||||
// Get the linear region extents.
|
||||
const auto linear_extents =
|
||||
memory_layout.GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
|
||||
memory_layout->GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
|
||||
KMemoryRegionAttr_LinearMapped);
|
||||
ASSERT(linear_extents.GetEndAddress() != 0);
|
||||
|
||||
@@ -560,7 +553,7 @@ struct KernelCore::Impl {
|
||||
Common::AlignUp(linear_extents.GetEndAddress(), LinearRegionAlign) -
|
||||
aligned_linear_phys_start;
|
||||
const VAddr linear_region_start =
|
||||
memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegionWithGuard(
|
||||
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;
|
||||
@@ -569,7 +562,7 @@ struct KernelCore::Impl {
|
||||
{
|
||||
PAddr cur_phys_addr = 0;
|
||||
u64 cur_size = 0;
|
||||
for (auto& region : memory_layout.GetPhysicalMemoryRegionTree()) {
|
||||
for (auto& region : memory_layout->GetPhysicalMemoryRegionTree()) {
|
||||
if (!region.HasTypeAttribute(KMemoryRegionAttr_LinearMapped)) {
|
||||
continue;
|
||||
}
|
||||
@@ -588,55 +581,49 @@ struct KernelCore::Impl {
|
||||
|
||||
const VAddr region_virt_addr =
|
||||
region.GetAddress() + linear_region_phys_to_virt_diff;
|
||||
ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert(
|
||||
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);
|
||||
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(
|
||||
ASSERT(memory_layout->GetPhysicalMemoryRegionTree().Insert(
|
||||
resource_end_phys_addr, KernelPageTableHeapSize, KMemoryRegionType_DramKernelInitPt));
|
||||
ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert(
|
||||
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()) {
|
||||
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);
|
||||
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);
|
||||
memory_layout->InitializeLinearMemoryRegionTrees(aligned_linear_phys_start,
|
||||
linear_region_start);
|
||||
}
|
||||
|
||||
void InitializeMemoryLayout(const KMemoryLayout& memory_layout) {
|
||||
const auto system_pool = memory_layout.GetKernelSystemPoolRegionPhysicalExtents();
|
||||
const auto applet_pool = memory_layout.GetKernelAppletPoolRegionPhysicalExtents();
|
||||
const auto application_pool = memory_layout.GetKernelApplicationPoolRegionPhysicalExtents();
|
||||
void InitializeMemoryLayout() {
|
||||
const auto system_pool = memory_layout->GetKernelSystemPoolRegionPhysicalExtents();
|
||||
|
||||
// Initialize memory managers
|
||||
// Initialize the memory manager.
|
||||
memory_manager = std::make_unique<KMemoryManager>(system);
|
||||
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());
|
||||
const auto& management_region = memory_layout->GetPoolManagementRegion();
|
||||
ASSERT(management_region.GetEndAddress() != 0);
|
||||
memory_manager->Initialize(management_region.GetAddress(), management_region.GetSize());
|
||||
|
||||
// Setup memory regions for emulated processes
|
||||
// TODO(bunnei): These should not be hardcoded regions initialized within the kernel
|
||||
@@ -777,6 +764,9 @@ struct KernelCore::Impl {
|
||||
Kernel::KSharedMemory* irs_shared_mem{};
|
||||
Kernel::KSharedMemory* time_shared_mem{};
|
||||
|
||||
// Memory layout
|
||||
std::unique_ptr<KMemoryLayout> memory_layout;
|
||||
|
||||
// Threads used for services
|
||||
std::unordered_set<std::shared_ptr<Kernel::ServiceThread>> service_threads;
|
||||
Common::ThreadWorker service_threads_manager;
|
||||
@@ -1142,6 +1132,10 @@ const KWorkerTaskManager& KernelCore::WorkerTaskManager() const {
|
||||
return impl->worker_task_manager;
|
||||
}
|
||||
|
||||
const KMemoryLayout& KernelCore::MemoryLayout() const {
|
||||
return *impl->memory_layout;
|
||||
}
|
||||
|
||||
bool KernelCore::IsPhantomModeForSingleCore() const {
|
||||
return impl->IsPhantomModeForSingleCore();
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ class KClientSession;
|
||||
class KEvent;
|
||||
class KHandleTable;
|
||||
class KLinkedListNode;
|
||||
class KMemoryLayout;
|
||||
class KMemoryManager;
|
||||
class KPort;
|
||||
class KProcess;
|
||||
@@ -350,6 +351,9 @@ public:
|
||||
/// Gets the current worker task manager, used for dispatching KThread/KProcess tasks.
|
||||
const KWorkerTaskManager& WorkerTaskManager() const;
|
||||
|
||||
/// Gets the memory layout.
|
||||
const KMemoryLayout& MemoryLayout() const;
|
||||
|
||||
private:
|
||||
friend class KProcess;
|
||||
friend class KThread;
|
||||
|
||||
@@ -2332,7 +2332,7 @@ static ResultCode CreateEvent(Core::System& system, Handle* out_write, Handle* o
|
||||
R_UNLESS(event != nullptr, ResultOutOfResource);
|
||||
|
||||
// Initialize the event.
|
||||
event->Initialize("CreateEvent");
|
||||
event->Initialize("CreateEvent", kernel.CurrentProcess());
|
||||
|
||||
// Commit the thread reservation.
|
||||
event_reservation.Commit();
|
||||
|
||||
@@ -618,7 +618,7 @@ void AppletMessageQueue::PushMessage(AppletMessage msg) {
|
||||
AppletMessageQueue::AppletMessage AppletMessageQueue::PopMessage() {
|
||||
if (messages.empty()) {
|
||||
on_new_message->GetWritableEvent().Clear();
|
||||
return AppletMessage::NoMessage;
|
||||
return AppletMessage::None;
|
||||
}
|
||||
auto msg = messages.front();
|
||||
messages.pop();
|
||||
@@ -633,7 +633,7 @@ std::size_t AppletMessageQueue::GetMessageCount() const {
|
||||
}
|
||||
|
||||
void AppletMessageQueue::RequestExit() {
|
||||
PushMessage(AppletMessage::ExitRequested);
|
||||
PushMessage(AppletMessage::Exit);
|
||||
}
|
||||
|
||||
void AppletMessageQueue::FocusStateChanged() {
|
||||
@@ -732,7 +732,7 @@ void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) {
|
||||
const auto message = msg_queue->PopMessage();
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
|
||||
if (message == AppletMessageQueue::AppletMessage::NoMessage) {
|
||||
if (message == AppletMessageQueue::AppletMessage::None) {
|
||||
LOG_ERROR(Service_AM, "Message queue is empty");
|
||||
rb.Push(ERR_NO_MESSAGES);
|
||||
rb.PushEnum<AppletMessageQueue::AppletMessage>(message);
|
||||
|
||||
@@ -22,6 +22,7 @@ class NVFlinger;
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
// This is nn::settings::Language
|
||||
enum SystemLanguage {
|
||||
Japanese = 0,
|
||||
English = 1, // en-US
|
||||
@@ -41,16 +42,44 @@ enum SystemLanguage {
|
||||
// 4.0.0+
|
||||
SimplifiedChinese = 15,
|
||||
TraditionalChinese = 16,
|
||||
// 10.1.0+
|
||||
BrazilianPortuguese = 17,
|
||||
};
|
||||
|
||||
class AppletMessageQueue {
|
||||
public:
|
||||
// This is nn::am::AppletMessage
|
||||
enum class AppletMessage : u32 {
|
||||
NoMessage = 0,
|
||||
ExitRequested = 4,
|
||||
None = 0,
|
||||
ChangeIntoForeground = 1,
|
||||
ChangeIntoBackground = 2,
|
||||
Exit = 4,
|
||||
ApplicationExited = 6,
|
||||
FocusStateChanged = 15,
|
||||
Resume = 16,
|
||||
DetectShortPressingHomeButton = 20,
|
||||
DetectLongPressingHomeButton = 21,
|
||||
DetectShortPressingPowerButton = 22,
|
||||
DetectMiddlePressingPowerButton = 23,
|
||||
DetectLongPressingPowerButton = 24,
|
||||
RequestToPrepareSleep = 25,
|
||||
FinishedSleepSequence = 26,
|
||||
SleepRequiredByHighTemperature = 27,
|
||||
SleepRequiredByLowBattery = 28,
|
||||
AutoPowerDown = 29,
|
||||
OperationModeChanged = 30,
|
||||
PerformanceModeChanged = 31,
|
||||
DetectReceivingCecSystemStandby = 32,
|
||||
SdCardRemoved = 33,
|
||||
LaunchApplicationRequested = 50,
|
||||
RequestToDisplay = 51,
|
||||
ShowApplicationLogo = 55,
|
||||
HideApplicationLogo = 56,
|
||||
ForceHideApplicationLogo = 57,
|
||||
FloatingApplicationDetected = 60,
|
||||
DetectShortPressingCaptureButton = 90,
|
||||
AlbumScreenShotTaken = 92,
|
||||
AlbumRecordingSaved = 93,
|
||||
};
|
||||
|
||||
explicit AppletMessageQueue(Core::System& system);
|
||||
@@ -179,11 +208,14 @@ public:
|
||||
~ICommonStateGetter() override;
|
||||
|
||||
private:
|
||||
// This is nn::oe::FocusState
|
||||
enum class FocusState : u8 {
|
||||
InFocus = 1,
|
||||
NotInFocus = 2,
|
||||
Background = 3,
|
||||
};
|
||||
|
||||
// This is nn::oe::OperationMode
|
||||
enum class OperationMode : u8 {
|
||||
Handheld = 0,
|
||||
Docked = 1,
|
||||
|
||||
@@ -17,8 +17,8 @@ constexpr auto DEFAULT_PERFORMANCE_CONFIGURATION = PerformanceConfiguration::Con
|
||||
|
||||
Controller::Controller(Core::Timing::CoreTiming& core_timing_)
|
||||
: core_timing{core_timing_}, configs{
|
||||
{PerformanceMode::Handheld, DEFAULT_PERFORMANCE_CONFIGURATION},
|
||||
{PerformanceMode::Docked, DEFAULT_PERFORMANCE_CONFIGURATION},
|
||||
{PerformanceMode::Normal, DEFAULT_PERFORMANCE_CONFIGURATION},
|
||||
{PerformanceMode::Boost, DEFAULT_PERFORMANCE_CONFIGURATION},
|
||||
} {}
|
||||
|
||||
Controller::~Controller() = default;
|
||||
@@ -63,13 +63,13 @@ void Controller::SetFromCpuBoostMode(CpuBoostMode mode) {
|
||||
PerformanceConfiguration::Config15,
|
||||
}};
|
||||
|
||||
SetPerformanceConfiguration(PerformanceMode::Docked,
|
||||
SetPerformanceConfiguration(PerformanceMode::Boost,
|
||||
BOOST_MODE_TO_CONFIG_MAP.at(static_cast<u32>(mode)));
|
||||
}
|
||||
|
||||
PerformanceMode Controller::GetCurrentPerformanceMode() const {
|
||||
return Settings::values.use_docked_mode.GetValue() ? PerformanceMode::Docked
|
||||
: PerformanceMode::Handheld;
|
||||
return Settings::values.use_docked_mode.GetValue() ? PerformanceMode::Boost
|
||||
: PerformanceMode::Normal;
|
||||
}
|
||||
|
||||
PerformanceConfiguration Controller::GetCurrentPerformanceConfiguration(PerformanceMode mode) {
|
||||
|
||||
@@ -32,15 +32,18 @@ enum class PerformanceConfiguration : u32 {
|
||||
Config16 = 0x9222000C,
|
||||
};
|
||||
|
||||
// This is nn::oe::CpuBoostMode
|
||||
enum class CpuBoostMode : u32 {
|
||||
Disabled = 0,
|
||||
Full = 1, // CPU + GPU -> Config 13, 14, 15, or 16
|
||||
Partial = 2, // GPU Only -> Config 15 or 16
|
||||
Normal = 0, // Boost mode disabled
|
||||
FastLoad = 1, // CPU + GPU -> Config 13, 14, 15, or 16
|
||||
Partial = 2, // GPU Only -> Config 15 or 16
|
||||
};
|
||||
|
||||
enum class PerformanceMode : u8 {
|
||||
Handheld = 0,
|
||||
Docked = 1,
|
||||
// This is nn::oe::PerformanceMode
|
||||
enum class PerformanceMode : s32 {
|
||||
Invalid = -1,
|
||||
Normal = 0,
|
||||
Boost = 1,
|
||||
};
|
||||
|
||||
// Class to manage the state and change of the emulated system performance.
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/kernel/k_memory_manager.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_readable_event.h"
|
||||
#include "core/hle/kernel/k_resource_limit.h"
|
||||
@@ -15,10 +17,21 @@ namespace Service::KernelHelpers {
|
||||
|
||||
ServiceContext::ServiceContext(Core::System& system_, std::string name_)
|
||||
: kernel(system_.Kernel()) {
|
||||
|
||||
// Create a resource limit for the process.
|
||||
const auto physical_memory_size =
|
||||
kernel.MemoryManager().GetSize(Kernel::KMemoryManager::Pool::System);
|
||||
auto* resource_limit = Kernel::CreateResourceLimitForProcess(system_, physical_memory_size);
|
||||
|
||||
// Create the process.
|
||||
process = Kernel::KProcess::Create(kernel);
|
||||
ASSERT(Kernel::KProcess::Initialize(process, system_, std::move(name_),
|
||||
Kernel::KProcess::ProcessType::Userland)
|
||||
Kernel::KProcess::ProcessType::KernelInternal,
|
||||
resource_limit)
|
||||
.IsSuccess());
|
||||
|
||||
// Close reference to our resource limit, as the process opens one.
|
||||
resource_limit->Close();
|
||||
}
|
||||
|
||||
ServiceContext::~ServiceContext() {
|
||||
@@ -43,7 +56,7 @@ Kernel::KEvent* ServiceContext::CreateEvent(std::string&& name) {
|
||||
}
|
||||
|
||||
// Initialize the event.
|
||||
event->Initialize(std::move(name));
|
||||
event->Initialize(std::move(name), process);
|
||||
|
||||
// Commit the thread reservation.
|
||||
event_reservation.Commit();
|
||||
|
||||
@@ -288,7 +288,7 @@ public:
|
||||
}
|
||||
|
||||
bool ValidateRegionForMap(Kernel::KPageTable& page_table, VAddr start, std::size_t size) const {
|
||||
constexpr std::size_t padding_size{4 * Kernel::PageSize};
|
||||
const std::size_t padding_size{page_table.GetNumGuardPages() * Kernel::PageSize};
|
||||
const auto start_info{page_table.QueryInfo(start - 1)};
|
||||
|
||||
if (start_info.state != Kernel::KMemoryState::Free) {
|
||||
@@ -308,31 +308,69 @@ public:
|
||||
return (start + size + padding_size) <= (end_info.GetAddress() + end_info.GetSize());
|
||||
}
|
||||
|
||||
VAddr GetRandomMapRegion(const Kernel::KPageTable& page_table, std::size_t size) const {
|
||||
VAddr addr{};
|
||||
const std::size_t end_pages{(page_table.GetAliasCodeRegionSize() - size) >>
|
||||
Kernel::PageBits};
|
||||
do {
|
||||
addr = page_table.GetAliasCodeRegionStart() +
|
||||
(Kernel::KSystemControl::GenerateRandomRange(0, end_pages) << Kernel::PageBits);
|
||||
} while (!page_table.IsInsideAddressSpace(addr, size) ||
|
||||
page_table.IsInsideHeapRegion(addr, size) ||
|
||||
page_table.IsInsideAliasRegion(addr, size));
|
||||
return addr;
|
||||
ResultCode GetAvailableMapRegion(Kernel::KPageTable& page_table, u64 size, VAddr& out_addr) {
|
||||
size = Common::AlignUp(size, Kernel::PageSize);
|
||||
size += page_table.GetNumGuardPages() * Kernel::PageSize * 4;
|
||||
|
||||
const auto is_region_available = [&](VAddr addr) {
|
||||
const auto end_addr = addr + size;
|
||||
while (addr < end_addr) {
|
||||
if (system.Memory().IsValidVirtualAddress(addr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!page_table.IsInsideAddressSpace(out_addr, size)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (page_table.IsInsideHeapRegion(out_addr, size)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (page_table.IsInsideAliasRegion(out_addr, size)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
addr += Kernel::PageSize;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
bool succeeded = false;
|
||||
const auto map_region_end =
|
||||
page_table.GetAliasCodeRegionStart() + page_table.GetAliasCodeRegionSize();
|
||||
while (current_map_addr < map_region_end) {
|
||||
if (is_region_available(current_map_addr)) {
|
||||
succeeded = true;
|
||||
break;
|
||||
}
|
||||
current_map_addr += 0x100000;
|
||||
}
|
||||
|
||||
if (!succeeded) {
|
||||
UNREACHABLE_MSG("Out of address space!");
|
||||
return Kernel::ResultOutOfMemory;
|
||||
}
|
||||
|
||||
out_addr = current_map_addr;
|
||||
current_map_addr += size;
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultVal<VAddr> MapProcessCodeMemory(Kernel::KProcess* process, VAddr baseAddress,
|
||||
u64 size) const {
|
||||
for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) {
|
||||
auto& page_table{process->PageTable()};
|
||||
const VAddr addr{GetRandomMapRegion(page_table, size)};
|
||||
const ResultCode result{page_table.MapCodeMemory(addr, baseAddress, size)};
|
||||
ResultVal<VAddr> MapProcessCodeMemory(Kernel::KProcess* process, VAddr base_addr, u64 size) {
|
||||
auto& page_table{process->PageTable()};
|
||||
VAddr addr{};
|
||||
|
||||
for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) {
|
||||
R_TRY(GetAvailableMapRegion(page_table, size, addr));
|
||||
|
||||
const ResultCode result{page_table.MapCodeMemory(addr, base_addr, size)};
|
||||
if (result == Kernel::ResultInvalidCurrentMemory) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CASCADE_CODE(result);
|
||||
R_TRY(result);
|
||||
|
||||
if (ValidateRegionForMap(page_table, addr, size)) {
|
||||
return addr;
|
||||
@@ -343,7 +381,7 @@ public:
|
||||
}
|
||||
|
||||
ResultVal<VAddr> MapNro(Kernel::KProcess* process, VAddr nro_addr, std::size_t nro_size,
|
||||
VAddr bss_addr, std::size_t bss_size, std::size_t size) const {
|
||||
VAddr bss_addr, std::size_t bss_size, std::size_t size) {
|
||||
for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) {
|
||||
auto& page_table{process->PageTable()};
|
||||
VAddr addr{};
|
||||
@@ -597,6 +635,7 @@ public:
|
||||
LOG_WARNING(Service_LDR, "(STUBBED) called");
|
||||
|
||||
initialized = true;
|
||||
current_map_addr = system.CurrentProcess()->PageTable().GetAliasCodeRegionStart();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
@@ -607,6 +646,7 @@ private:
|
||||
|
||||
std::map<VAddr, NROInfo> nro;
|
||||
std::map<VAddr, std::vector<SHA256Hash>> nrr;
|
||||
VAddr current_map_addr{};
|
||||
|
||||
bool IsValidNROHash(const SHA256Hash& hash) const {
|
||||
return std::any_of(nrr.begin(), nrr.end(), [&hash](const auto& p) {
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
// Copyright 2022 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/service/mnpp/mnpp_app.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
|
||||
namespace Service::MNPP {
|
||||
|
||||
class MNPP_APP final : public ServiceFramework<MNPP_APP> {
|
||||
public:
|
||||
explicit MNPP_APP(Core::System& system_) : ServiceFramework{system_, "mnpp:app"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &MNPP_APP::Unknown0, "unknown0"},
|
||||
{1, &MNPP_APP::Unknown1, "unknown1"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
void Unknown0(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_MNPP, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void Unknown1(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_MNPP, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
};
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
|
||||
std::make_shared<MNPP_APP>(system)->InstallAsService(service_manager);
|
||||
}
|
||||
|
||||
} // namespace Service::MNPP
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright 2022 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::SM {
|
||||
class ServiceManager;
|
||||
}
|
||||
|
||||
namespace Service::MNPP {
|
||||
|
||||
/// Registers all MNPP services with the specified service manager.
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
|
||||
|
||||
} // namespace Service::MNPP
|
||||
+764
-314
File diff suppressed because it is too large
Load Diff
+228
-14
@@ -7,15 +7,132 @@
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/mii/mii_manager.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Kernel {
|
||||
class KEvent;
|
||||
}
|
||||
class KReadableEvent;
|
||||
} // namespace Kernel
|
||||
|
||||
namespace Core::HID {
|
||||
enum class NpadIdType : u32;
|
||||
} // namespace Core::HID
|
||||
|
||||
namespace Service::NFP {
|
||||
|
||||
enum class ServiceType : u32 {
|
||||
User,
|
||||
Debug,
|
||||
System,
|
||||
};
|
||||
|
||||
enum class State : u32 {
|
||||
NonInitialized,
|
||||
Initialized,
|
||||
};
|
||||
|
||||
enum class DeviceState : u32 {
|
||||
Initialized,
|
||||
SearchingForTag,
|
||||
TagFound,
|
||||
TagRemoved,
|
||||
TagMounted,
|
||||
Unaviable,
|
||||
Finalized,
|
||||
};
|
||||
|
||||
enum class ModelType : u32 {
|
||||
Amiibo,
|
||||
};
|
||||
|
||||
enum class MountTarget : u32 {
|
||||
Rom,
|
||||
Ram,
|
||||
All,
|
||||
};
|
||||
|
||||
enum class AmiiboType : u8 {
|
||||
Figure,
|
||||
Card,
|
||||
Yarn,
|
||||
};
|
||||
|
||||
enum class AmiiboSeries : u8 {
|
||||
SuperSmashBros,
|
||||
SuperMario,
|
||||
ChibiRobo,
|
||||
YoshiWoollyWorld,
|
||||
Splatoon,
|
||||
AnimalCrossing,
|
||||
EightBitMario,
|
||||
Skylanders,
|
||||
Unknown8,
|
||||
TheLegendOfZelda,
|
||||
ShovelKnight,
|
||||
Unknown11,
|
||||
Kiby,
|
||||
Pokemon,
|
||||
MarioSportsSuperstars,
|
||||
MonsterHunter,
|
||||
BoxBoy,
|
||||
Pikmin,
|
||||
FireEmblem,
|
||||
Metroid,
|
||||
Others,
|
||||
MegaMan,
|
||||
Diablo
|
||||
};
|
||||
|
||||
using TagUuid = std::array<u8, 10>;
|
||||
|
||||
struct TagInfo {
|
||||
TagUuid uuid;
|
||||
u8 uuid_length;
|
||||
INSERT_PADDING_BYTES(0x15);
|
||||
s32 protocol;
|
||||
u32 tag_type;
|
||||
INSERT_PADDING_BYTES(0x30);
|
||||
};
|
||||
static_assert(sizeof(TagInfo) == 0x58, "TagInfo is an invalid size");
|
||||
|
||||
struct CommonInfo {
|
||||
u16 last_write_year;
|
||||
u8 last_write_month;
|
||||
u8 last_write_day;
|
||||
u16 write_counter;
|
||||
u16 version;
|
||||
u32 application_area_size;
|
||||
INSERT_PADDING_BYTES(0x34);
|
||||
};
|
||||
static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size");
|
||||
|
||||
struct ModelInfo {
|
||||
u16 character_id;
|
||||
u8 character_variant;
|
||||
AmiiboType amiibo_type;
|
||||
u16 model_number;
|
||||
AmiiboSeries series;
|
||||
u8 fixed; // Must be 02
|
||||
INSERT_PADDING_BYTES(0x4); // Unknown
|
||||
INSERT_PADDING_BYTES(0x20); // Probably a SHA256-(HMAC?) hash
|
||||
INSERT_PADDING_BYTES(0x14); // SHA256-HMAC
|
||||
};
|
||||
static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size");
|
||||
|
||||
struct RegisterInfo {
|
||||
Service::Mii::MiiInfo mii_char_info;
|
||||
u16 first_write_year;
|
||||
u8 first_write_month;
|
||||
u8 first_write_day;
|
||||
std::array<u8, 11> amiibo_name;
|
||||
u8 unknown;
|
||||
INSERT_PADDING_BYTES(0x98);
|
||||
};
|
||||
static_assert(sizeof(RegisterInfo) == 0x100, "RegisterInfo is an invalid size");
|
||||
|
||||
class Module final {
|
||||
public:
|
||||
class Interface : public ServiceFramework<Interface> {
|
||||
@@ -24,34 +141,131 @@ public:
|
||||
const char* name);
|
||||
~Interface() override;
|
||||
|
||||
struct ModelInfo {
|
||||
std::array<u8, 0x8> amiibo_identification_block;
|
||||
INSERT_PADDING_BYTES(0x38);
|
||||
struct EncryptedAmiiboFile {
|
||||
u16 crypto_init; // Must be A5 XX
|
||||
u16 write_count; // Number of times the amiibo has been written?
|
||||
INSERT_PADDING_BYTES(0x20); // System crypts
|
||||
INSERT_PADDING_BYTES(0x20); // SHA256-(HMAC?) hash
|
||||
ModelInfo model_info; // This struct is bigger than documentation
|
||||
INSERT_PADDING_BYTES(0xC); // SHA256-HMAC
|
||||
INSERT_PADDING_BYTES(0x114); // section 1 encrypted buffer
|
||||
INSERT_PADDING_BYTES(0x54); // section 2 encrypted buffer
|
||||
};
|
||||
static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size");
|
||||
static_assert(sizeof(EncryptedAmiiboFile) == 0x1F8, "AmiiboFile is an invalid size");
|
||||
|
||||
struct AmiiboFile {
|
||||
std::array<u8, 10> uuid;
|
||||
INSERT_PADDING_BYTES(0x4a);
|
||||
ModelInfo model_info;
|
||||
struct NTAG215Password {
|
||||
u32 PWD; // Password to allow write access
|
||||
u16 PACK; // Password acknowledge reply
|
||||
u16 RFUI; // Reserved for future use
|
||||
};
|
||||
static_assert(sizeof(AmiiboFile) == 0x94, "AmiiboFile is an invalid size");
|
||||
static_assert(sizeof(NTAG215Password) == 0x8, "NTAG215Password is an invalid size");
|
||||
|
||||
struct NTAG215File {
|
||||
TagUuid uuid; // Unique serial number
|
||||
u16 lock_bytes; // Set defined pages as read only
|
||||
u32 compability_container; // Defines available memory
|
||||
EncryptedAmiiboFile user_memory; // Writable data
|
||||
u32 dynamic_lock; // Dynamic lock
|
||||
u32 CFG0; // Defines memory protected by password
|
||||
u32 CFG1; // Defines number of verification attempts
|
||||
NTAG215Password password; // Password data
|
||||
};
|
||||
static_assert(sizeof(NTAG215File) == 0x21C, "NTAG215File is an invalid size");
|
||||
|
||||
void CreateUserInterface(Kernel::HLERequestContext& ctx);
|
||||
bool LoadAmiibo(const std::vector<u8>& buffer);
|
||||
Kernel::KReadableEvent& GetNFCEvent();
|
||||
const AmiiboFile& GetAmiiboBuffer() const;
|
||||
void CloseAmiibo();
|
||||
|
||||
void Initialize();
|
||||
void Finalize();
|
||||
|
||||
ResultCode StartDetection(s32 protocol_);
|
||||
ResultCode StopDetection();
|
||||
ResultCode Mount();
|
||||
ResultCode Unmount();
|
||||
|
||||
ResultCode GetTagInfo(TagInfo& tag_info) const;
|
||||
ResultCode GetCommonInfo(CommonInfo& common_info) const;
|
||||
ResultCode GetModelInfo(ModelInfo& model_info) const;
|
||||
ResultCode GetRegisterInfo(RegisterInfo& register_info) const;
|
||||
|
||||
ResultCode OpenApplicationArea(u32 access_id);
|
||||
ResultCode GetApplicationArea(std::vector<u8>& data) const;
|
||||
ResultCode SetApplicationArea(const std::vector<u8>& data);
|
||||
ResultCode CreateApplicationArea(u32 access_id, const std::vector<u8>& data);
|
||||
|
||||
u64 GetHandle() const;
|
||||
DeviceState GetCurrentState() const;
|
||||
Core::HID::NpadIdType GetNpadId() const;
|
||||
|
||||
Kernel::KReadableEvent& GetActivateEvent() const;
|
||||
Kernel::KReadableEvent& GetDeactivateEvent() const;
|
||||
|
||||
protected:
|
||||
std::shared_ptr<Module> module;
|
||||
|
||||
private:
|
||||
/// Validates that the amiibo file is not corrupted
|
||||
bool IsAmiiboValid() const;
|
||||
|
||||
bool AmiiboApplicationDataExist(u32 access_id) const;
|
||||
std::vector<u8> LoadAmiiboApplicationData(u32 access_id) const;
|
||||
void SaveAmiiboApplicationData(u32 access_id, const std::vector<u8>& data) const;
|
||||
|
||||
/// return password needed to allow write access to protected memory
|
||||
u32 GetTagPassword(const TagUuid& uuid) const;
|
||||
|
||||
const Core::HID::NpadIdType npad_id;
|
||||
|
||||
DeviceState device_state{DeviceState::Unaviable};
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
Kernel::KEvent* nfc_tag_load;
|
||||
AmiiboFile amiibo{};
|
||||
Kernel::KEvent* activate_event;
|
||||
Kernel::KEvent* deactivate_event;
|
||||
NTAG215File tag_data{};
|
||||
s32 protocol;
|
||||
bool is_application_area_initialized{};
|
||||
u32 application_area_id;
|
||||
std::vector<u8> application_area_data;
|
||||
};
|
||||
};
|
||||
|
||||
class IUser final : public ServiceFramework<IUser> {
|
||||
public:
|
||||
explicit IUser(Module::Interface& nfp_interface_, Core::System& system_);
|
||||
|
||||
private:
|
||||
void Initialize(Kernel::HLERequestContext& ctx);
|
||||
void Finalize(Kernel::HLERequestContext& ctx);
|
||||
void ListDevices(Kernel::HLERequestContext& ctx);
|
||||
void StartDetection(Kernel::HLERequestContext& ctx);
|
||||
void StopDetection(Kernel::HLERequestContext& ctx);
|
||||
void Mount(Kernel::HLERequestContext& ctx);
|
||||
void Unmount(Kernel::HLERequestContext& ctx);
|
||||
void OpenApplicationArea(Kernel::HLERequestContext& ctx);
|
||||
void GetApplicationArea(Kernel::HLERequestContext& ctx);
|
||||
void SetApplicationArea(Kernel::HLERequestContext& ctx);
|
||||
void CreateApplicationArea(Kernel::HLERequestContext& ctx);
|
||||
void GetTagInfo(Kernel::HLERequestContext& ctx);
|
||||
void GetRegisterInfo(Kernel::HLERequestContext& ctx);
|
||||
void GetCommonInfo(Kernel::HLERequestContext& ctx);
|
||||
void GetModelInfo(Kernel::HLERequestContext& ctx);
|
||||
void AttachActivateEvent(Kernel::HLERequestContext& ctx);
|
||||
void AttachDeactivateEvent(Kernel::HLERequestContext& ctx);
|
||||
void GetState(Kernel::HLERequestContext& ctx);
|
||||
void GetDeviceState(Kernel::HLERequestContext& ctx);
|
||||
void GetNpadId(Kernel::HLERequestContext& ctx);
|
||||
void GetApplicationAreaSize(Kernel::HLERequestContext& ctx);
|
||||
void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx);
|
||||
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
|
||||
// TODO(german77): We should have a vector of interfaces
|
||||
Module::Interface& nfp_interface;
|
||||
|
||||
State state{State::NonInitialized};
|
||||
Kernel::KEvent* availability_change_event;
|
||||
};
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
|
||||
|
||||
} // namespace Service::NFP
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include "core/hle/service/mig/mig.h"
|
||||
#include "core/hle/service/mii/mii.h"
|
||||
#include "core/hle/service/mm/mm_u.h"
|
||||
#include "core/hle/service/mnpp/mnpp_app.h"
|
||||
#include "core/hle/service/ncm/ncm.h"
|
||||
#include "core/hle/service/nfc/nfc.h"
|
||||
#include "core/hle/service/nfp/nfp.h"
|
||||
@@ -265,6 +266,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
|
||||
Migration::InstallInterfaces(*sm, system);
|
||||
Mii::InstallInterfaces(*sm, system);
|
||||
MM::InstallInterfaces(*sm, system);
|
||||
MNPP::InstallInterfaces(*sm, system);
|
||||
NCM::InstallInterfaces(*sm, system);
|
||||
NFC::InstallInterfaces(*sm, system);
|
||||
NFP::InstallInterfaces(*sm, system);
|
||||
|
||||
+1
-2
@@ -39,8 +39,7 @@ struct Memory::Impl {
|
||||
void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target) {
|
||||
ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
|
||||
ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
|
||||
ASSERT_MSG(target >= DramMemoryMap::Base && target < DramMemoryMap::End,
|
||||
"Out of bounds target: {:016X}", target);
|
||||
ASSERT_MSG(target >= DramMemoryMap::Base, "Out of bounds target: {:016X}", target);
|
||||
MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, Common::PageType::Memory);
|
||||
|
||||
if (Settings::IsFastmemEnabled()) {
|
||||
|
||||
@@ -175,22 +175,23 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
BatteryLevel GetBatteryLevel() {
|
||||
Common::Input::BatteryLevel GetBatteryLevel() {
|
||||
const auto level = SDL_JoystickCurrentPowerLevel(sdl_joystick.get());
|
||||
switch (level) {
|
||||
case SDL_JOYSTICK_POWER_EMPTY:
|
||||
return BatteryLevel::Empty;
|
||||
return Common::Input::BatteryLevel::Empty;
|
||||
case SDL_JOYSTICK_POWER_LOW:
|
||||
return BatteryLevel::Low;
|
||||
return Common::Input::BatteryLevel::Low;
|
||||
case SDL_JOYSTICK_POWER_MEDIUM:
|
||||
return BatteryLevel::Medium;
|
||||
return Common::Input::BatteryLevel::Medium;
|
||||
case SDL_JOYSTICK_POWER_FULL:
|
||||
case SDL_JOYSTICK_POWER_MAX:
|
||||
return BatteryLevel::Full;
|
||||
case SDL_JOYSTICK_POWER_UNKNOWN:
|
||||
return Common::Input::BatteryLevel::Full;
|
||||
case SDL_JOYSTICK_POWER_WIRED:
|
||||
return Common::Input::BatteryLevel::Charging;
|
||||
case SDL_JOYSTICK_POWER_UNKNOWN:
|
||||
default:
|
||||
return BatteryLevel::Charging;
|
||||
return Common::Input::BatteryLevel::None;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -351,6 +352,8 @@ void SDLDriver::HandleGameControllerEvent(const SDL_Event& event) {
|
||||
if (const auto joystick = GetSDLJoystickBySDLID(event.jbutton.which)) {
|
||||
const PadIdentifier identifier = joystick->GetPadIdentifier();
|
||||
SetButton(identifier, event.jbutton.button, true);
|
||||
// Battery doesn't trigger an event so just update every button press
|
||||
SetBattery(identifier, joystick->GetBatteryLevel());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -192,22 +192,22 @@ std::size_t UDPClient::GetClientNumber(std::string_view host, u16 port) const {
|
||||
return MAX_UDP_CLIENTS;
|
||||
}
|
||||
|
||||
BatteryLevel UDPClient::GetBatteryLevel(Response::Battery battery) const {
|
||||
Common::Input::BatteryLevel UDPClient::GetBatteryLevel(Response::Battery battery) const {
|
||||
switch (battery) {
|
||||
case Response::Battery::Dying:
|
||||
return BatteryLevel::Empty;
|
||||
return Common::Input::BatteryLevel::Empty;
|
||||
case Response::Battery::Low:
|
||||
return BatteryLevel::Critical;
|
||||
return Common::Input::BatteryLevel::Critical;
|
||||
case Response::Battery::Medium:
|
||||
return BatteryLevel::Low;
|
||||
return Common::Input::BatteryLevel::Low;
|
||||
case Response::Battery::High:
|
||||
return BatteryLevel::Medium;
|
||||
return Common::Input::BatteryLevel::Medium;
|
||||
case Response::Battery::Full:
|
||||
case Response::Battery::Charged:
|
||||
return BatteryLevel::Full;
|
||||
return Common::Input::BatteryLevel::Full;
|
||||
case Response::Battery::Charging:
|
||||
default:
|
||||
return BatteryLevel::Charging;
|
||||
return Common::Input::BatteryLevel::Charging;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -141,7 +141,7 @@ private:
|
||||
std::size_t GetClientNumber(std::string_view host, u16 port) const;
|
||||
|
||||
// Translates UDP battery level to input engine battery level
|
||||
BatteryLevel GetBatteryLevel(Response::Battery battery) const;
|
||||
Common::Input::BatteryLevel GetBatteryLevel(Response::Battery battery) const;
|
||||
|
||||
void OnVersion(Response::Version);
|
||||
void OnPortInfo(Response::PortInfo);
|
||||
|
||||
@@ -70,7 +70,7 @@ void InputEngine::SetAxis(const PadIdentifier& identifier, int axis, f32 value)
|
||||
TriggerOnAxisChange(identifier, axis, value);
|
||||
}
|
||||
|
||||
void InputEngine::SetBattery(const PadIdentifier& identifier, BatteryLevel value) {
|
||||
void InputEngine::SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value) {
|
||||
{
|
||||
std::lock_guard lock{mutex};
|
||||
ControllerData& controller = controller_list.at(identifier);
|
||||
@@ -143,13 +143,13 @@ f32 InputEngine::GetAxis(const PadIdentifier& identifier, int axis) const {
|
||||
return axis_iter->second;
|
||||
}
|
||||
|
||||
BatteryLevel InputEngine::GetBattery(const PadIdentifier& identifier) const {
|
||||
Common::Input::BatteryLevel InputEngine::GetBattery(const PadIdentifier& identifier) const {
|
||||
std::lock_guard lock{mutex};
|
||||
const auto controller_iter = controller_list.find(identifier);
|
||||
if (controller_iter == controller_list.cend()) {
|
||||
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
|
||||
identifier.pad, identifier.port);
|
||||
return BatteryLevel::Charging;
|
||||
return Common::Input::BatteryLevel::Charging;
|
||||
}
|
||||
const ControllerData& controller = controller_iter->second;
|
||||
return controller.battery;
|
||||
@@ -270,7 +270,7 @@ void InputEngine::TriggerOnAxisChange(const PadIdentifier& identifier, int axis,
|
||||
}
|
||||
|
||||
void InputEngine::TriggerOnBatteryChange(const PadIdentifier& identifier,
|
||||
[[maybe_unused]] BatteryLevel value) {
|
||||
[[maybe_unused]] Common::Input::BatteryLevel value) {
|
||||
std::lock_guard lock{mutex_callback};
|
||||
for (const auto& poller_pair : callback_list) {
|
||||
const InputIdentifier& poller = poller_pair.second;
|
||||
|
||||
@@ -34,16 +34,6 @@ struct BasicMotion {
|
||||
u64 delta_timestamp{};
|
||||
};
|
||||
|
||||
// Stages of a battery charge
|
||||
enum class BatteryLevel {
|
||||
Empty,
|
||||
Critical,
|
||||
Low,
|
||||
Medium,
|
||||
Full,
|
||||
Charging,
|
||||
};
|
||||
|
||||
// Types of input that are stored in the engine
|
||||
enum class EngineInputType {
|
||||
None,
|
||||
@@ -178,7 +168,7 @@ public:
|
||||
bool GetButton(const PadIdentifier& identifier, int button) const;
|
||||
bool GetHatButton(const PadIdentifier& identifier, int button, u8 direction) const;
|
||||
f32 GetAxis(const PadIdentifier& identifier, int axis) const;
|
||||
BatteryLevel GetBattery(const PadIdentifier& identifier) const;
|
||||
Common::Input::BatteryLevel GetBattery(const PadIdentifier& identifier) const;
|
||||
BasicMotion GetMotion(const PadIdentifier& identifier, int motion) const;
|
||||
|
||||
int SetCallback(InputIdentifier input_identifier);
|
||||
@@ -189,7 +179,7 @@ protected:
|
||||
void SetButton(const PadIdentifier& identifier, int button, bool value);
|
||||
void SetHatButton(const PadIdentifier& identifier, int button, u8 value);
|
||||
void SetAxis(const PadIdentifier& identifier, int axis, f32 value);
|
||||
void SetBattery(const PadIdentifier& identifier, BatteryLevel value);
|
||||
void SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value);
|
||||
void SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value);
|
||||
|
||||
virtual std::string GetHatButtonName([[maybe_unused]] u8 direction_value) const {
|
||||
@@ -202,13 +192,13 @@ private:
|
||||
std::unordered_map<int, u8> hat_buttons;
|
||||
std::unordered_map<int, float> axes;
|
||||
std::unordered_map<int, BasicMotion> motions;
|
||||
BatteryLevel battery{};
|
||||
Common::Input::BatteryLevel battery{};
|
||||
};
|
||||
|
||||
void TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value);
|
||||
void TriggerOnHatButtonChange(const PadIdentifier& identifier, int button, u8 value);
|
||||
void TriggerOnAxisChange(const PadIdentifier& identifier, int axis, f32 value);
|
||||
void TriggerOnBatteryChange(const PadIdentifier& identifier, BatteryLevel value);
|
||||
void TriggerOnBatteryChange(const PadIdentifier& identifier, Common::Input::BatteryLevel value);
|
||||
void TriggerOnMotionChange(const PadIdentifier& identifier, int motion,
|
||||
const BasicMotion& value);
|
||||
|
||||
|
||||
@@ -470,7 +470,7 @@ public:
|
||||
}
|
||||
|
||||
Common::Input::BatteryStatus GetStatus() const {
|
||||
return static_cast<Common::Input::BatteryLevel>(input_engine->GetBattery(identifier));
|
||||
return input_engine->GetBattery(identifier);
|
||||
}
|
||||
|
||||
void ForceUpdate() override {
|
||||
|
||||
@@ -22,7 +22,7 @@ constexpr u32 NUM_TEXTURE_AND_IMAGE_SCALING_WORDS =
|
||||
struct RescalingLayout {
|
||||
alignas(16) std::array<u32, NUM_TEXTURE_SCALING_WORDS> rescaling_textures;
|
||||
alignas(16) std::array<u32, NUM_IMAGE_SCALING_WORDS> rescaling_images;
|
||||
alignas(16) u32 down_factor;
|
||||
u32 down_factor;
|
||||
};
|
||||
constexpr u32 RESCALING_LAYOUT_WORDS_OFFSET = offsetof(RescalingLayout, rescaling_textures);
|
||||
constexpr u32 RESCALING_LAYOUT_DOWN_FACTOR_OFFSET = offsetof(RescalingLayout, down_factor);
|
||||
|
||||
@@ -123,34 +123,36 @@ std::optional<OutAttr> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) {
|
||||
}
|
||||
|
||||
Id GetCbuf(EmitContext& ctx, Id result_type, Id UniformDefinitions::*member_ptr, u32 element_size,
|
||||
const IR::Value& binding, const IR::Value& offset) {
|
||||
const IR::Value& binding, const IR::Value& offset, const Id indirect_func) {
|
||||
Id buffer_offset;
|
||||
const Id uniform_type{ctx.uniform_types.*member_ptr};
|
||||
if (offset.IsImmediate()) {
|
||||
// Hardware been proved to read the aligned offset (e.g. LDC.U32 at 6 will read offset 4)
|
||||
const Id imm_offset{ctx.Const(offset.U32() / element_size)};
|
||||
buffer_offset = imm_offset;
|
||||
} else if (element_size > 1) {
|
||||
const u32 log2_element_size{static_cast<u32>(std::countr_zero(element_size))};
|
||||
const Id shift{ctx.Const(log2_element_size)};
|
||||
buffer_offset = ctx.OpShiftRightArithmetic(ctx.U32[1], ctx.Def(offset), shift);
|
||||
} else {
|
||||
buffer_offset = ctx.Def(offset);
|
||||
}
|
||||
if (!binding.IsImmediate()) {
|
||||
throw NotImplementedException("Constant buffer indexing");
|
||||
return ctx.OpFunctionCall(result_type, indirect_func, ctx.Def(binding), buffer_offset);
|
||||
}
|
||||
const Id cbuf{ctx.cbufs[binding.U32()].*member_ptr};
|
||||
const Id uniform_type{ctx.uniform_types.*member_ptr};
|
||||
if (!offset.IsImmediate()) {
|
||||
Id index{ctx.Def(offset)};
|
||||
if (element_size > 1) {
|
||||
const u32 log2_element_size{static_cast<u32>(std::countr_zero(element_size))};
|
||||
const Id shift{ctx.Const(log2_element_size)};
|
||||
index = ctx.OpShiftRightArithmetic(ctx.U32[1], ctx.Def(offset), shift);
|
||||
}
|
||||
const Id access_chain{ctx.OpAccessChain(uniform_type, cbuf, ctx.u32_zero_value, index)};
|
||||
return ctx.OpLoad(result_type, access_chain);
|
||||
}
|
||||
// Hardware been proved to read the aligned offset (e.g. LDC.U32 at 6 will read offset 4)
|
||||
const Id imm_offset{ctx.Const(offset.U32() / element_size)};
|
||||
const Id access_chain{ctx.OpAccessChain(uniform_type, cbuf, ctx.u32_zero_value, imm_offset)};
|
||||
const Id access_chain{ctx.OpAccessChain(uniform_type, cbuf, ctx.u32_zero_value, buffer_offset)};
|
||||
return ctx.OpLoad(result_type, access_chain);
|
||||
}
|
||||
|
||||
Id GetCbufU32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
|
||||
return GetCbuf(ctx, ctx.U32[1], &UniformDefinitions::U32, sizeof(u32), binding, offset);
|
||||
return GetCbuf(ctx, ctx.U32[1], &UniformDefinitions::U32, sizeof(u32), binding, offset,
|
||||
ctx.load_const_func_u32);
|
||||
}
|
||||
|
||||
Id GetCbufU32x4(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
|
||||
return GetCbuf(ctx, ctx.U32[4], &UniformDefinitions::U32x4, sizeof(u32[4]), binding, offset);
|
||||
return GetCbuf(ctx, ctx.U32[4], &UniformDefinitions::U32x4, sizeof(u32[4]), binding, offset,
|
||||
ctx.load_const_func_u32x4);
|
||||
}
|
||||
|
||||
Id GetCbufElement(EmitContext& ctx, Id vector, const IR::Value& offset, u32 index_offset) {
|
||||
@@ -201,7 +203,8 @@ void EmitGetIndirectBranchVariable(EmitContext&) {
|
||||
|
||||
Id EmitGetCbufU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
|
||||
if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int8) {
|
||||
const Id load{GetCbuf(ctx, ctx.U8, &UniformDefinitions::U8, sizeof(u8), binding, offset)};
|
||||
const Id load{GetCbuf(ctx, ctx.U8, &UniformDefinitions::U8, sizeof(u8), binding, offset,
|
||||
ctx.load_const_func_u8)};
|
||||
return ctx.OpUConvert(ctx.U32[1], load);
|
||||
}
|
||||
Id element{};
|
||||
@@ -217,7 +220,8 @@ Id EmitGetCbufU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& of
|
||||
|
||||
Id EmitGetCbufS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
|
||||
if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int8) {
|
||||
const Id load{GetCbuf(ctx, ctx.S8, &UniformDefinitions::S8, sizeof(s8), binding, offset)};
|
||||
const Id load{GetCbuf(ctx, ctx.S8, &UniformDefinitions::S8, sizeof(s8), binding, offset,
|
||||
ctx.load_const_func_u8)};
|
||||
return ctx.OpSConvert(ctx.U32[1], load);
|
||||
}
|
||||
Id element{};
|
||||
@@ -233,8 +237,8 @@ Id EmitGetCbufS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& of
|
||||
|
||||
Id EmitGetCbufU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
|
||||
if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int16) {
|
||||
const Id load{
|
||||
GetCbuf(ctx, ctx.U16, &UniformDefinitions::U16, sizeof(u16), binding, offset)};
|
||||
const Id load{GetCbuf(ctx, ctx.U16, &UniformDefinitions::U16, sizeof(u16), binding, offset,
|
||||
ctx.load_const_func_u16)};
|
||||
return ctx.OpUConvert(ctx.U32[1], load);
|
||||
}
|
||||
Id element{};
|
||||
@@ -250,8 +254,8 @@ Id EmitGetCbufU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& o
|
||||
|
||||
Id EmitGetCbufS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
|
||||
if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int16) {
|
||||
const Id load{
|
||||
GetCbuf(ctx, ctx.S16, &UniformDefinitions::S16, sizeof(s16), binding, offset)};
|
||||
const Id load{GetCbuf(ctx, ctx.S16, &UniformDefinitions::S16, sizeof(s16), binding, offset,
|
||||
ctx.load_const_func_u16)};
|
||||
return ctx.OpSConvert(ctx.U32[1], load);
|
||||
}
|
||||
Id element{};
|
||||
@@ -276,7 +280,8 @@ Id EmitGetCbufU32(EmitContext& ctx, const IR::Value& binding, const IR::Value& o
|
||||
|
||||
Id EmitGetCbufF32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
|
||||
if (ctx.profile.support_descriptor_aliasing) {
|
||||
return GetCbuf(ctx, ctx.F32[1], &UniformDefinitions::F32, sizeof(f32), binding, offset);
|
||||
return GetCbuf(ctx, ctx.F32[1], &UniformDefinitions::F32, sizeof(f32), binding, offset,
|
||||
ctx.load_const_func_f32);
|
||||
} else {
|
||||
const Id vector{GetCbufU32x4(ctx, binding, offset)};
|
||||
return ctx.OpBitcast(ctx.F32[1], GetCbufElement(ctx, vector, offset, 0u));
|
||||
@@ -285,8 +290,8 @@ Id EmitGetCbufF32(EmitContext& ctx, const IR::Value& binding, const IR::Value& o
|
||||
|
||||
Id EmitGetCbufU32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
|
||||
if (ctx.profile.support_descriptor_aliasing) {
|
||||
return GetCbuf(ctx, ctx.U32[2], &UniformDefinitions::U32x2, sizeof(u32[2]), binding,
|
||||
offset);
|
||||
return GetCbuf(ctx, ctx.U32[2], &UniformDefinitions::U32x2, sizeof(u32[2]), binding, offset,
|
||||
ctx.load_const_func_u32x2);
|
||||
} else {
|
||||
const Id vector{GetCbufU32x4(ctx, binding, offset)};
|
||||
return ctx.OpCompositeConstruct(ctx.U32[2], GetCbufElement(ctx, vector, offset, 0u),
|
||||
|
||||
@@ -464,6 +464,7 @@ EmitContext::EmitContext(const Profile& profile_, const RuntimeInfo& runtime_inf
|
||||
DefineSharedMemory(program);
|
||||
DefineSharedMemoryFunctions(program);
|
||||
DefineConstantBuffers(program.info, uniform_binding);
|
||||
DefineConstantBufferIndirectFunctions(program.info);
|
||||
DefineStorageBuffers(program.info, storage_binding);
|
||||
DefineTextureBuffers(program.info, texture_binding);
|
||||
DefineImageBuffers(program.info, image_binding);
|
||||
@@ -993,7 +994,7 @@ void EmitContext::DefineConstantBuffers(const Info& info, u32& binding) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
IR::Type types{info.used_constant_buffer_types};
|
||||
IR::Type types{info.used_constant_buffer_types | info.used_indirect_cbuf_types};
|
||||
if (True(types & IR::Type::U8)) {
|
||||
if (profile.support_int8) {
|
||||
DefineConstBuffers(*this, info, &UniformDefinitions::U8, binding, U8, 'u', sizeof(u8));
|
||||
@@ -1027,6 +1028,62 @@ void EmitContext::DefineConstantBuffers(const Info& info, u32& binding) {
|
||||
binding += static_cast<u32>(info.constant_buffer_descriptors.size());
|
||||
}
|
||||
|
||||
void EmitContext::DefineConstantBufferIndirectFunctions(const Info& info) {
|
||||
if (!info.uses_cbuf_indirect) {
|
||||
return;
|
||||
}
|
||||
const auto make_accessor{[&](Id buffer_type, Id UniformDefinitions::*member_ptr) {
|
||||
const Id func_type{TypeFunction(buffer_type, U32[1], U32[1])};
|
||||
const Id func{OpFunction(buffer_type, spv::FunctionControlMask::MaskNone, func_type)};
|
||||
const Id binding{OpFunctionParameter(U32[1])};
|
||||
const Id offset{OpFunctionParameter(U32[1])};
|
||||
|
||||
AddLabel();
|
||||
|
||||
const Id merge_label{OpLabel()};
|
||||
const Id uniform_type{uniform_types.*member_ptr};
|
||||
|
||||
std::array<Id, Info::MAX_CBUFS> buf_labels;
|
||||
std::array<Sirit::Literal, Info::MAX_CBUFS> buf_literals;
|
||||
for (u32 i = 0; i < Info::MAX_CBUFS; i++) {
|
||||
buf_labels[i] = OpLabel();
|
||||
buf_literals[i] = Sirit::Literal{i};
|
||||
}
|
||||
OpSelectionMerge(merge_label, spv::SelectionControlMask::MaskNone);
|
||||
OpSwitch(binding, buf_labels[0], buf_literals, buf_labels);
|
||||
for (u32 i = 0; i < Info::MAX_CBUFS; i++) {
|
||||
AddLabel(buf_labels[i]);
|
||||
const Id cbuf{cbufs[i].*member_ptr};
|
||||
const Id access_chain{OpAccessChain(uniform_type, cbuf, u32_zero_value, offset)};
|
||||
const Id result{OpLoad(buffer_type, access_chain)};
|
||||
OpReturnValue(result);
|
||||
}
|
||||
AddLabel(merge_label);
|
||||
OpUnreachable();
|
||||
OpFunctionEnd();
|
||||
return func;
|
||||
}};
|
||||
IR::Type types{info.used_indirect_cbuf_types};
|
||||
if (True(types & IR::Type::U8)) {
|
||||
load_const_func_u8 = make_accessor(U8, &UniformDefinitions::U8);
|
||||
}
|
||||
if (True(types & IR::Type::U16)) {
|
||||
load_const_func_u16 = make_accessor(U16, &UniformDefinitions::U16);
|
||||
}
|
||||
if (True(types & IR::Type::F32)) {
|
||||
load_const_func_f32 = make_accessor(F32[1], &UniformDefinitions::F32);
|
||||
}
|
||||
if (True(types & IR::Type::U32)) {
|
||||
load_const_func_u32 = make_accessor(U32[1], &UniformDefinitions::U32);
|
||||
}
|
||||
if (True(types & IR::Type::U32x2)) {
|
||||
load_const_func_u32x2 = make_accessor(U32[2], &UniformDefinitions::U32x2);
|
||||
}
|
||||
if (True(types & IR::Type::U32x4)) {
|
||||
load_const_func_u32x4 = make_accessor(U32[4], &UniformDefinitions::U32x4);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitContext::DefineStorageBuffers(const Info& info, u32& binding) {
|
||||
if (info.storage_buffers_descriptors.empty()) {
|
||||
return;
|
||||
|
||||
@@ -294,6 +294,13 @@ public:
|
||||
|
||||
std::vector<Id> interfaces;
|
||||
|
||||
Id load_const_func_u8{};
|
||||
Id load_const_func_u16{};
|
||||
Id load_const_func_u32{};
|
||||
Id load_const_func_f32{};
|
||||
Id load_const_func_u32x2{};
|
||||
Id load_const_func_u32x4{};
|
||||
|
||||
private:
|
||||
void DefineCommonTypes(const Info& info);
|
||||
void DefineCommonConstants();
|
||||
@@ -302,6 +309,7 @@ private:
|
||||
void DefineSharedMemory(const IR::Program& program);
|
||||
void DefineSharedMemoryFunctions(const IR::Program& program);
|
||||
void DefineConstantBuffers(const Info& info, u32& binding);
|
||||
void DefineConstantBufferIndirectFunctions(const Info& info);
|
||||
void DefineStorageBuffers(const Info& info, u32& binding);
|
||||
void DefineTextureBuffers(const Info& info, u32& binding);
|
||||
void DefineImageBuffers(const Info& info, u32& binding);
|
||||
|
||||
@@ -11,10 +11,20 @@ namespace Shader::Maxwell {
|
||||
using namespace LDC;
|
||||
namespace {
|
||||
std::pair<IR::U32, IR::U32> Slot(IR::IREmitter& ir, Mode mode, const IR::U32& imm_index,
|
||||
const IR::U32& reg, const IR::U32& imm) {
|
||||
const IR::U32& reg, const IR::U32& imm_offset) {
|
||||
switch (mode) {
|
||||
case Mode::Default:
|
||||
return {imm_index, ir.IAdd(reg, imm)};
|
||||
return {imm_index, ir.IAdd(reg, imm_offset)};
|
||||
case Mode::IS: {
|
||||
// Segmented addressing mode
|
||||
// Ra+imm_offset points into a flat mapping of const buffer
|
||||
// address space
|
||||
const IR::U32 address{ir.IAdd(reg, imm_offset)};
|
||||
const IR::U32 index{ir.BitFieldExtract(address, ir.Imm32(16), ir.Imm32(16))};
|
||||
const IR::U32 offset{ir.BitFieldExtract(address, ir.Imm32(0), ir.Imm32(16))};
|
||||
|
||||
return {ir.IAdd(index, imm_index), offset};
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
+528
-52
@@ -13,59 +13,535 @@ namespace {
|
||||
// Emulate GPU's LOP3.LUT (three-input logic op with 8-bit truth table)
|
||||
IR::U32 ApplyLUT(IR::IREmitter& ir, const IR::U32& a, const IR::U32& b, const IR::U32& c,
|
||||
u64 ttbl) {
|
||||
IR::U32 r{ir.Imm32(0)};
|
||||
const IR::U32 not_a{ir.BitwiseNot(a)};
|
||||
const IR::U32 not_b{ir.BitwiseNot(b)};
|
||||
const IR::U32 not_c{ir.BitwiseNot(c)};
|
||||
if (ttbl & 0x01) {
|
||||
// r |= ~a & ~b & ~c;
|
||||
const auto lhs{ir.BitwiseAnd(not_a, not_b)};
|
||||
const auto rhs{ir.BitwiseAnd(lhs, not_c)};
|
||||
r = ir.BitwiseOr(r, rhs);
|
||||
switch (ttbl) {
|
||||
// generated code, do not edit manually
|
||||
case 0:
|
||||
return ir.Imm32(0);
|
||||
case 1:
|
||||
return ir.BitwiseNot(ir.BitwiseOr(a, ir.BitwiseOr(b, c)));
|
||||
case 2:
|
||||
return ir.BitwiseAnd(c, ir.BitwiseNot(ir.BitwiseOr(a, b)));
|
||||
case 3:
|
||||
return ir.BitwiseNot(ir.BitwiseOr(a, b));
|
||||
case 4:
|
||||
return ir.BitwiseAnd(b, ir.BitwiseNot(ir.BitwiseOr(a, c)));
|
||||
case 5:
|
||||
return ir.BitwiseNot(ir.BitwiseOr(a, c));
|
||||
case 6:
|
||||
return ir.BitwiseAnd(ir.BitwiseNot(a), ir.BitwiseXor(b, c));
|
||||
case 7:
|
||||
return ir.BitwiseNot(ir.BitwiseOr(a, ir.BitwiseAnd(b, c)));
|
||||
case 8:
|
||||
return ir.BitwiseAnd(ir.BitwiseAnd(b, c), ir.BitwiseNot(a));
|
||||
case 9:
|
||||
return ir.BitwiseNot(ir.BitwiseOr(a, ir.BitwiseXor(b, c)));
|
||||
case 10:
|
||||
return ir.BitwiseAnd(c, ir.BitwiseNot(a));
|
||||
case 11:
|
||||
return ir.BitwiseAnd(ir.BitwiseNot(a), ir.BitwiseOr(c, ir.BitwiseNot(b)));
|
||||
case 12:
|
||||
return ir.BitwiseAnd(b, ir.BitwiseNot(a));
|
||||
case 13:
|
||||
return ir.BitwiseAnd(ir.BitwiseNot(a), ir.BitwiseOr(b, ir.BitwiseNot(c)));
|
||||
case 14:
|
||||
return ir.BitwiseAnd(ir.BitwiseNot(a), ir.BitwiseOr(b, c));
|
||||
case 15:
|
||||
return ir.BitwiseNot(a);
|
||||
case 16:
|
||||
return ir.BitwiseAnd(a, ir.BitwiseNot(ir.BitwiseOr(b, c)));
|
||||
case 17:
|
||||
return ir.BitwiseNot(ir.BitwiseOr(b, c));
|
||||
case 18:
|
||||
return ir.BitwiseAnd(ir.BitwiseNot(b), ir.BitwiseXor(a, c));
|
||||
case 19:
|
||||
return ir.BitwiseNot(ir.BitwiseOr(b, ir.BitwiseAnd(a, c)));
|
||||
case 20:
|
||||
return ir.BitwiseAnd(ir.BitwiseNot(c), ir.BitwiseXor(a, b));
|
||||
case 21:
|
||||
return ir.BitwiseNot(ir.BitwiseOr(c, ir.BitwiseAnd(a, b)));
|
||||
case 22:
|
||||
return ir.BitwiseXor(ir.BitwiseOr(a, b), ir.BitwiseOr(c, ir.BitwiseAnd(a, b)));
|
||||
case 23:
|
||||
return ir.BitwiseXor(ir.BitwiseAnd(ir.BitwiseXor(a, b), ir.BitwiseXor(a, c)),
|
||||
ir.BitwiseNot(a));
|
||||
case 24:
|
||||
return ir.BitwiseAnd(ir.BitwiseXor(a, b), ir.BitwiseXor(a, c));
|
||||
case 25:
|
||||
return ir.BitwiseNot(ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseXor(b, c)));
|
||||
case 26:
|
||||
return ir.BitwiseAnd(ir.BitwiseOr(c, ir.BitwiseNot(b)), ir.BitwiseXor(a, c));
|
||||
case 27:
|
||||
return ir.BitwiseXor(ir.BitwiseOr(a, ir.BitwiseNot(c)), ir.BitwiseOr(b, c));
|
||||
case 28:
|
||||
return ir.BitwiseAnd(ir.BitwiseOr(b, ir.BitwiseNot(c)), ir.BitwiseXor(a, b));
|
||||
case 29:
|
||||
return ir.BitwiseXor(ir.BitwiseOr(a, ir.BitwiseNot(b)), ir.BitwiseOr(b, c));
|
||||
case 30:
|
||||
return ir.BitwiseXor(a, ir.BitwiseOr(b, c));
|
||||
case 31:
|
||||
return ir.BitwiseNot(ir.BitwiseAnd(a, ir.BitwiseOr(b, c)));
|
||||
case 32:
|
||||
return ir.BitwiseAnd(ir.BitwiseAnd(a, c), ir.BitwiseNot(b));
|
||||
case 33:
|
||||
return ir.BitwiseNot(ir.BitwiseOr(b, ir.BitwiseXor(a, c)));
|
||||
case 34:
|
||||
return ir.BitwiseAnd(c, ir.BitwiseNot(b));
|
||||
case 35:
|
||||
return ir.BitwiseAnd(ir.BitwiseNot(b), ir.BitwiseOr(c, ir.BitwiseNot(a)));
|
||||
case 36:
|
||||
return ir.BitwiseAnd(ir.BitwiseXor(a, b), ir.BitwiseXor(b, c));
|
||||
case 37:
|
||||
return ir.BitwiseNot(ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseXor(a, c)));
|
||||
case 38:
|
||||
return ir.BitwiseAnd(ir.BitwiseOr(c, ir.BitwiseNot(a)), ir.BitwiseXor(b, c));
|
||||
case 39:
|
||||
return ir.BitwiseXor(ir.BitwiseOr(a, c), ir.BitwiseOr(b, ir.BitwiseNot(c)));
|
||||
case 40:
|
||||
return ir.BitwiseAnd(c, ir.BitwiseXor(a, b));
|
||||
case 41:
|
||||
return ir.BitwiseXor(ir.BitwiseOr(a, b),
|
||||
ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseNot(c)));
|
||||
case 42:
|
||||
return ir.BitwiseAnd(c, ir.BitwiseNot(ir.BitwiseAnd(a, b)));
|
||||
case 43:
|
||||
return ir.BitwiseXor(ir.BitwiseOr(a, ir.BitwiseNot(c)),
|
||||
ir.BitwiseOr(b, ir.BitwiseXor(a, c)));
|
||||
case 44:
|
||||
return ir.BitwiseAnd(ir.BitwiseOr(b, c), ir.BitwiseXor(a, b));
|
||||
case 45:
|
||||
return ir.BitwiseXor(a, ir.BitwiseOr(b, ir.BitwiseNot(c)));
|
||||
case 46:
|
||||
return ir.BitwiseXor(ir.BitwiseAnd(a, b), ir.BitwiseOr(b, c));
|
||||
case 47:
|
||||
return ir.BitwiseOr(ir.BitwiseAnd(c, ir.BitwiseNot(b)), ir.BitwiseNot(a));
|
||||
case 48:
|
||||
return ir.BitwiseAnd(a, ir.BitwiseNot(b));
|
||||
case 49:
|
||||
return ir.BitwiseAnd(ir.BitwiseNot(b), ir.BitwiseOr(a, ir.BitwiseNot(c)));
|
||||
case 50:
|
||||
return ir.BitwiseAnd(ir.BitwiseNot(b), ir.BitwiseOr(a, c));
|
||||
case 51:
|
||||
return ir.BitwiseNot(b);
|
||||
case 52:
|
||||
return ir.BitwiseAnd(ir.BitwiseOr(a, ir.BitwiseNot(c)), ir.BitwiseXor(a, b));
|
||||
case 53:
|
||||
return ir.BitwiseXor(ir.BitwiseOr(a, c), ir.BitwiseOr(b, ir.BitwiseNot(a)));
|
||||
case 54:
|
||||
return ir.BitwiseXor(b, ir.BitwiseOr(a, c));
|
||||
case 55:
|
||||
return ir.BitwiseNot(ir.BitwiseAnd(b, ir.BitwiseOr(a, c)));
|
||||
case 56:
|
||||
return ir.BitwiseAnd(ir.BitwiseOr(a, c), ir.BitwiseXor(a, b));
|
||||
case 57:
|
||||
return ir.BitwiseXor(b, ir.BitwiseOr(a, ir.BitwiseNot(c)));
|
||||
case 58:
|
||||
return ir.BitwiseXor(ir.BitwiseAnd(a, b), ir.BitwiseOr(a, c));
|
||||
case 59:
|
||||
return ir.BitwiseOr(ir.BitwiseAnd(c, ir.BitwiseNot(a)), ir.BitwiseNot(b));
|
||||
case 60:
|
||||
return ir.BitwiseXor(a, b);
|
||||
case 61:
|
||||
return ir.BitwiseOr(ir.BitwiseNot(ir.BitwiseOr(a, c)), ir.BitwiseXor(a, b));
|
||||
case 62:
|
||||
return ir.BitwiseOr(ir.BitwiseAnd(c, ir.BitwiseNot(a)), ir.BitwiseXor(a, b));
|
||||
case 63:
|
||||
return ir.BitwiseNot(ir.BitwiseAnd(a, b));
|
||||
case 64:
|
||||
return ir.BitwiseAnd(ir.BitwiseAnd(a, b), ir.BitwiseNot(c));
|
||||
case 65:
|
||||
return ir.BitwiseNot(ir.BitwiseOr(c, ir.BitwiseXor(a, b)));
|
||||
case 66:
|
||||
return ir.BitwiseAnd(ir.BitwiseXor(a, c), ir.BitwiseXor(b, c));
|
||||
case 67:
|
||||
return ir.BitwiseNot(ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseXor(a, b)));
|
||||
case 68:
|
||||
return ir.BitwiseAnd(b, ir.BitwiseNot(c));
|
||||
case 69:
|
||||
return ir.BitwiseAnd(ir.BitwiseNot(c), ir.BitwiseOr(b, ir.BitwiseNot(a)));
|
||||
case 70:
|
||||
return ir.BitwiseAnd(ir.BitwiseOr(b, ir.BitwiseNot(a)), ir.BitwiseXor(b, c));
|
||||
case 71:
|
||||
return ir.BitwiseXor(ir.BitwiseOr(a, b), ir.BitwiseOr(c, ir.BitwiseNot(b)));
|
||||
case 72:
|
||||
return ir.BitwiseAnd(b, ir.BitwiseXor(a, c));
|
||||
case 73:
|
||||
return ir.BitwiseXor(ir.BitwiseOr(a, c),
|
||||
ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseNot(b)));
|
||||
case 74:
|
||||
return ir.BitwiseAnd(ir.BitwiseOr(b, c), ir.BitwiseXor(a, c));
|
||||
case 75:
|
||||
return ir.BitwiseXor(a, ir.BitwiseOr(c, ir.BitwiseNot(b)));
|
||||
case 76:
|
||||
return ir.BitwiseAnd(b, ir.BitwiseNot(ir.BitwiseAnd(a, c)));
|
||||
case 77:
|
||||
return ir.BitwiseXor(ir.BitwiseOr(a, ir.BitwiseNot(b)),
|
||||
ir.BitwiseOr(c, ir.BitwiseXor(a, b)));
|
||||
case 78:
|
||||
return ir.BitwiseXor(ir.BitwiseAnd(a, c), ir.BitwiseOr(b, c));
|
||||
case 79:
|
||||
return ir.BitwiseOr(ir.BitwiseAnd(b, ir.BitwiseNot(c)), ir.BitwiseNot(a));
|
||||
case 80:
|
||||
return ir.BitwiseAnd(a, ir.BitwiseNot(c));
|
||||
case 81:
|
||||
return ir.BitwiseAnd(ir.BitwiseNot(c), ir.BitwiseOr(a, ir.BitwiseNot(b)));
|
||||
case 82:
|
||||
return ir.BitwiseAnd(ir.BitwiseOr(a, ir.BitwiseNot(b)), ir.BitwiseXor(a, c));
|
||||
case 83:
|
||||
return ir.BitwiseXor(ir.BitwiseOr(a, b), ir.BitwiseOr(c, ir.BitwiseNot(a)));
|
||||
case 84:
|
||||
return ir.BitwiseAnd(ir.BitwiseNot(c), ir.BitwiseOr(a, b));
|
||||
case 85:
|
||||
return ir.BitwiseNot(c);
|
||||
case 86:
|
||||
return ir.BitwiseXor(c, ir.BitwiseOr(a, b));
|
||||
case 87:
|
||||
return ir.BitwiseNot(ir.BitwiseAnd(c, ir.BitwiseOr(a, b)));
|
||||
case 88:
|
||||
return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseXor(a, c));
|
||||
case 89:
|
||||
return ir.BitwiseXor(c, ir.BitwiseOr(a, ir.BitwiseNot(b)));
|
||||
case 90:
|
||||
return ir.BitwiseXor(a, c);
|
||||
case 91:
|
||||
return ir.BitwiseOr(ir.BitwiseNot(ir.BitwiseOr(a, b)), ir.BitwiseXor(a, c));
|
||||
case 92:
|
||||
return ir.BitwiseXor(ir.BitwiseAnd(a, c), ir.BitwiseOr(a, b));
|
||||
case 93:
|
||||
return ir.BitwiseOr(ir.BitwiseAnd(b, ir.BitwiseNot(a)), ir.BitwiseNot(c));
|
||||
case 94:
|
||||
return ir.BitwiseOr(ir.BitwiseAnd(b, ir.BitwiseNot(a)), ir.BitwiseXor(a, c));
|
||||
case 95:
|
||||
return ir.BitwiseNot(ir.BitwiseAnd(a, c));
|
||||
case 96:
|
||||
return ir.BitwiseAnd(a, ir.BitwiseXor(b, c));
|
||||
case 97:
|
||||
return ir.BitwiseXor(ir.BitwiseOr(b, c),
|
||||
ir.BitwiseOr(ir.BitwiseAnd(b, c), ir.BitwiseNot(a)));
|
||||
case 98:
|
||||
return ir.BitwiseAnd(ir.BitwiseOr(a, c), ir.BitwiseXor(b, c));
|
||||
case 99:
|
||||
return ir.BitwiseXor(b, ir.BitwiseOr(c, ir.BitwiseNot(a)));
|
||||
case 100:
|
||||
return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseXor(b, c));
|
||||
case 101:
|
||||
return ir.BitwiseXor(c, ir.BitwiseOr(b, ir.BitwiseNot(a)));
|
||||
case 102:
|
||||
return ir.BitwiseXor(b, c);
|
||||
case 103:
|
||||
return ir.BitwiseOr(ir.BitwiseNot(ir.BitwiseOr(a, b)), ir.BitwiseXor(b, c));
|
||||
case 104:
|
||||
return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseXor(c, ir.BitwiseAnd(a, b)));
|
||||
case 105:
|
||||
return ir.BitwiseXor(ir.BitwiseNot(a), ir.BitwiseXor(b, c));
|
||||
case 106:
|
||||
return ir.BitwiseXor(c, ir.BitwiseAnd(a, b));
|
||||
case 107:
|
||||
return ir.BitwiseXor(ir.BitwiseAnd(c, ir.BitwiseOr(a, b)),
|
||||
ir.BitwiseXor(a, ir.BitwiseNot(b)));
|
||||
case 108:
|
||||
return ir.BitwiseXor(b, ir.BitwiseAnd(a, c));
|
||||
case 109:
|
||||
return ir.BitwiseXor(ir.BitwiseAnd(b, ir.BitwiseOr(a, c)),
|
||||
ir.BitwiseXor(a, ir.BitwiseNot(c)));
|
||||
case 110:
|
||||
return ir.BitwiseOr(ir.BitwiseAnd(b, ir.BitwiseNot(a)), ir.BitwiseXor(b, c));
|
||||
case 111:
|
||||
return ir.BitwiseOr(ir.BitwiseNot(a), ir.BitwiseXor(b, c));
|
||||
case 112:
|
||||
return ir.BitwiseAnd(a, ir.BitwiseNot(ir.BitwiseAnd(b, c)));
|
||||
case 113:
|
||||
return ir.BitwiseXor(ir.BitwiseOr(b, ir.BitwiseNot(a)),
|
||||
ir.BitwiseOr(c, ir.BitwiseXor(a, b)));
|
||||
case 114:
|
||||
return ir.BitwiseXor(ir.BitwiseAnd(b, c), ir.BitwiseOr(a, c));
|
||||
case 115:
|
||||
return ir.BitwiseOr(ir.BitwiseAnd(a, ir.BitwiseNot(c)), ir.BitwiseNot(b));
|
||||
case 116:
|
||||
return ir.BitwiseXor(ir.BitwiseAnd(b, c), ir.BitwiseOr(a, b));
|
||||
case 117:
|
||||
return ir.BitwiseOr(ir.BitwiseAnd(a, ir.BitwiseNot(b)), ir.BitwiseNot(c));
|
||||
case 118:
|
||||
return ir.BitwiseOr(ir.BitwiseAnd(a, ir.BitwiseNot(b)), ir.BitwiseXor(b, c));
|
||||
case 119:
|
||||
return ir.BitwiseNot(ir.BitwiseAnd(b, c));
|
||||
case 120:
|
||||
return ir.BitwiseXor(a, ir.BitwiseAnd(b, c));
|
||||
case 121:
|
||||
return ir.BitwiseXor(ir.BitwiseAnd(a, ir.BitwiseOr(b, c)),
|
||||
ir.BitwiseXor(b, ir.BitwiseNot(c)));
|
||||
case 122:
|
||||
return ir.BitwiseOr(ir.BitwiseAnd(a, ir.BitwiseNot(b)), ir.BitwiseXor(a, c));
|
||||
case 123:
|
||||
return ir.BitwiseOr(ir.BitwiseNot(b), ir.BitwiseXor(a, c));
|
||||
case 124:
|
||||
return ir.BitwiseOr(ir.BitwiseAnd(a, ir.BitwiseNot(c)), ir.BitwiseXor(a, b));
|
||||
case 125:
|
||||
return ir.BitwiseOr(ir.BitwiseNot(c), ir.BitwiseXor(a, b));
|
||||
case 126:
|
||||
return ir.BitwiseOr(ir.BitwiseXor(a, b), ir.BitwiseXor(a, c));
|
||||
case 127:
|
||||
return ir.BitwiseNot(ir.BitwiseAnd(a, ir.BitwiseAnd(b, c)));
|
||||
case 128:
|
||||
return ir.BitwiseAnd(a, ir.BitwiseAnd(b, c));
|
||||
case 129:
|
||||
return ir.BitwiseNot(ir.BitwiseOr(ir.BitwiseXor(a, b), ir.BitwiseXor(a, c)));
|
||||
case 130:
|
||||
return ir.BitwiseAnd(c, ir.BitwiseXor(a, ir.BitwiseNot(b)));
|
||||
case 131:
|
||||
return ir.BitwiseAnd(ir.BitwiseOr(c, ir.BitwiseNot(a)), ir.BitwiseXor(a, ir.BitwiseNot(b)));
|
||||
case 132:
|
||||
return ir.BitwiseAnd(b, ir.BitwiseXor(a, ir.BitwiseNot(c)));
|
||||
case 133:
|
||||
return ir.BitwiseAnd(ir.BitwiseOr(b, ir.BitwiseNot(a)), ir.BitwiseXor(a, ir.BitwiseNot(c)));
|
||||
case 134:
|
||||
return ir.BitwiseAnd(ir.BitwiseOr(b, c), ir.BitwiseXor(a, ir.BitwiseXor(b, c)));
|
||||
case 135:
|
||||
return ir.BitwiseXor(ir.BitwiseAnd(b, c), ir.BitwiseNot(a));
|
||||
case 136:
|
||||
return ir.BitwiseAnd(b, c);
|
||||
case 137:
|
||||
return ir.BitwiseAnd(ir.BitwiseOr(b, ir.BitwiseNot(a)), ir.BitwiseXor(b, ir.BitwiseNot(c)));
|
||||
case 138:
|
||||
return ir.BitwiseAnd(c, ir.BitwiseOr(b, ir.BitwiseNot(a)));
|
||||
case 139:
|
||||
return ir.BitwiseOr(ir.BitwiseAnd(b, c), ir.BitwiseNot(ir.BitwiseOr(a, b)));
|
||||
case 140:
|
||||
return ir.BitwiseAnd(b, ir.BitwiseOr(c, ir.BitwiseNot(a)));
|
||||
case 141:
|
||||
return ir.BitwiseOr(ir.BitwiseAnd(b, c), ir.BitwiseNot(ir.BitwiseOr(a, c)));
|
||||
case 142:
|
||||
return ir.BitwiseXor(a, ir.BitwiseOr(ir.BitwiseXor(a, b), ir.BitwiseXor(a, c)));
|
||||
case 143:
|
||||
return ir.BitwiseOr(ir.BitwiseAnd(b, c), ir.BitwiseNot(a));
|
||||
case 144:
|
||||
return ir.BitwiseAnd(a, ir.BitwiseXor(b, ir.BitwiseNot(c)));
|
||||
case 145:
|
||||
return ir.BitwiseAnd(ir.BitwiseOr(a, ir.BitwiseNot(b)), ir.BitwiseXor(b, ir.BitwiseNot(c)));
|
||||
case 146:
|
||||
return ir.BitwiseAnd(ir.BitwiseOr(a, c), ir.BitwiseXor(a, ir.BitwiseXor(b, c)));
|
||||
case 147:
|
||||
return ir.BitwiseXor(ir.BitwiseAnd(a, c), ir.BitwiseNot(b));
|
||||
case 148:
|
||||
return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseXor(a, ir.BitwiseXor(b, c)));
|
||||
case 149:
|
||||
return ir.BitwiseXor(ir.BitwiseAnd(a, b), ir.BitwiseNot(c));
|
||||
case 150:
|
||||
return ir.BitwiseXor(a, ir.BitwiseXor(b, c));
|
||||
case 151:
|
||||
return ir.BitwiseOr(ir.BitwiseNot(ir.BitwiseOr(a, b)),
|
||||
ir.BitwiseXor(a, ir.BitwiseXor(b, c)));
|
||||
case 152:
|
||||
return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseXor(b, ir.BitwiseNot(c)));
|
||||
case 153:
|
||||
return ir.BitwiseXor(b, ir.BitwiseNot(c));
|
||||
case 154:
|
||||
return ir.BitwiseXor(c, ir.BitwiseAnd(a, ir.BitwiseNot(b)));
|
||||
case 155:
|
||||
return ir.BitwiseNot(ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseXor(b, c)));
|
||||
case 156:
|
||||
return ir.BitwiseXor(b, ir.BitwiseAnd(a, ir.BitwiseNot(c)));
|
||||
case 157:
|
||||
return ir.BitwiseNot(ir.BitwiseAnd(ir.BitwiseOr(a, c), ir.BitwiseXor(b, c)));
|
||||
case 158:
|
||||
return ir.BitwiseOr(ir.BitwiseAnd(b, c), ir.BitwiseXor(a, ir.BitwiseOr(b, c)));
|
||||
case 159:
|
||||
return ir.BitwiseNot(ir.BitwiseAnd(a, ir.BitwiseXor(b, c)));
|
||||
case 160:
|
||||
return ir.BitwiseAnd(a, c);
|
||||
case 161:
|
||||
return ir.BitwiseAnd(ir.BitwiseOr(a, ir.BitwiseNot(b)), ir.BitwiseXor(a, ir.BitwiseNot(c)));
|
||||
case 162:
|
||||
return ir.BitwiseAnd(c, ir.BitwiseOr(a, ir.BitwiseNot(b)));
|
||||
case 163:
|
||||
return ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseNot(ir.BitwiseOr(a, b)));
|
||||
case 164:
|
||||
return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseXor(a, ir.BitwiseNot(c)));
|
||||
case 165:
|
||||
return ir.BitwiseXor(a, ir.BitwiseNot(c));
|
||||
case 166:
|
||||
return ir.BitwiseXor(c, ir.BitwiseAnd(b, ir.BitwiseNot(a)));
|
||||
case 167:
|
||||
return ir.BitwiseNot(ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseXor(a, c)));
|
||||
case 168:
|
||||
return ir.BitwiseAnd(c, ir.BitwiseOr(a, b));
|
||||
case 169:
|
||||
return ir.BitwiseXor(ir.BitwiseNot(c), ir.BitwiseOr(a, b));
|
||||
case 170:
|
||||
return c;
|
||||
case 171:
|
||||
return ir.BitwiseOr(c, ir.BitwiseNot(ir.BitwiseOr(a, b)));
|
||||
case 172:
|
||||
return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseOr(c, ir.BitwiseNot(a)));
|
||||
case 173:
|
||||
return ir.BitwiseOr(ir.BitwiseAnd(b, c), ir.BitwiseXor(a, ir.BitwiseNot(c)));
|
||||
case 174:
|
||||
return ir.BitwiseOr(c, ir.BitwiseAnd(b, ir.BitwiseNot(a)));
|
||||
case 175:
|
||||
return ir.BitwiseOr(c, ir.BitwiseNot(a));
|
||||
case 176:
|
||||
return ir.BitwiseAnd(a, ir.BitwiseOr(c, ir.BitwiseNot(b)));
|
||||
case 177:
|
||||
return ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseNot(ir.BitwiseOr(b, c)));
|
||||
case 178:
|
||||
return ir.BitwiseXor(b, ir.BitwiseOr(ir.BitwiseXor(a, b), ir.BitwiseXor(a, c)));
|
||||
case 179:
|
||||
return ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseNot(b));
|
||||
case 180:
|
||||
return ir.BitwiseXor(a, ir.BitwiseAnd(b, ir.BitwiseNot(c)));
|
||||
case 181:
|
||||
return ir.BitwiseNot(ir.BitwiseAnd(ir.BitwiseOr(b, c), ir.BitwiseXor(a, c)));
|
||||
case 182:
|
||||
return ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseXor(b, ir.BitwiseOr(a, c)));
|
||||
case 183:
|
||||
return ir.BitwiseNot(ir.BitwiseAnd(b, ir.BitwiseXor(a, c)));
|
||||
case 184:
|
||||
return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseOr(c, ir.BitwiseNot(b)));
|
||||
case 185:
|
||||
return ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseXor(b, ir.BitwiseNot(c)));
|
||||
case 186:
|
||||
return ir.BitwiseOr(c, ir.BitwiseAnd(a, ir.BitwiseNot(b)));
|
||||
case 187:
|
||||
return ir.BitwiseOr(c, ir.BitwiseNot(b));
|
||||
case 188:
|
||||
return ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseXor(a, b));
|
||||
case 189:
|
||||
return ir.BitwiseOr(ir.BitwiseXor(a, b), ir.BitwiseXor(a, ir.BitwiseNot(c)));
|
||||
case 190:
|
||||
return ir.BitwiseOr(c, ir.BitwiseXor(a, b));
|
||||
case 191:
|
||||
return ir.BitwiseOr(c, ir.BitwiseNot(ir.BitwiseAnd(a, b)));
|
||||
case 192:
|
||||
return ir.BitwiseAnd(a, b);
|
||||
case 193:
|
||||
return ir.BitwiseAnd(ir.BitwiseOr(a, ir.BitwiseNot(c)), ir.BitwiseXor(a, ir.BitwiseNot(b)));
|
||||
case 194:
|
||||
return ir.BitwiseAnd(ir.BitwiseOr(a, c), ir.BitwiseXor(a, ir.BitwiseNot(b)));
|
||||
case 195:
|
||||
return ir.BitwiseXor(a, ir.BitwiseNot(b));
|
||||
case 196:
|
||||
return ir.BitwiseAnd(b, ir.BitwiseOr(a, ir.BitwiseNot(c)));
|
||||
case 197:
|
||||
return ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseNot(ir.BitwiseOr(a, c)));
|
||||
case 198:
|
||||
return ir.BitwiseXor(b, ir.BitwiseAnd(c, ir.BitwiseNot(a)));
|
||||
case 199:
|
||||
return ir.BitwiseNot(ir.BitwiseAnd(ir.BitwiseOr(a, c), ir.BitwiseXor(a, b)));
|
||||
case 200:
|
||||
return ir.BitwiseAnd(b, ir.BitwiseOr(a, c));
|
||||
case 201:
|
||||
return ir.BitwiseXor(ir.BitwiseNot(b), ir.BitwiseOr(a, c));
|
||||
case 202:
|
||||
return ir.BitwiseAnd(ir.BitwiseOr(a, c), ir.BitwiseOr(b, ir.BitwiseNot(a)));
|
||||
case 203:
|
||||
return ir.BitwiseOr(ir.BitwiseAnd(b, c), ir.BitwiseXor(a, ir.BitwiseNot(b)));
|
||||
case 204:
|
||||
return b;
|
||||
case 205:
|
||||
return ir.BitwiseOr(b, ir.BitwiseNot(ir.BitwiseOr(a, c)));
|
||||
case 206:
|
||||
return ir.BitwiseOr(b, ir.BitwiseAnd(c, ir.BitwiseNot(a)));
|
||||
case 207:
|
||||
return ir.BitwiseOr(b, ir.BitwiseNot(a));
|
||||
case 208:
|
||||
return ir.BitwiseAnd(a, ir.BitwiseOr(b, ir.BitwiseNot(c)));
|
||||
case 209:
|
||||
return ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseNot(ir.BitwiseOr(b, c)));
|
||||
case 210:
|
||||
return ir.BitwiseXor(a, ir.BitwiseAnd(c, ir.BitwiseNot(b)));
|
||||
case 211:
|
||||
return ir.BitwiseNot(ir.BitwiseAnd(ir.BitwiseOr(b, c), ir.BitwiseXor(a, b)));
|
||||
case 212:
|
||||
return ir.BitwiseXor(c, ir.BitwiseOr(ir.BitwiseXor(a, b), ir.BitwiseXor(a, c)));
|
||||
case 213:
|
||||
return ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseNot(c));
|
||||
case 214:
|
||||
return ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseXor(c, ir.BitwiseOr(a, b)));
|
||||
case 215:
|
||||
return ir.BitwiseNot(ir.BitwiseAnd(c, ir.BitwiseXor(a, b)));
|
||||
case 216:
|
||||
return ir.BitwiseAnd(ir.BitwiseOr(a, c), ir.BitwiseOr(b, ir.BitwiseNot(c)));
|
||||
case 217:
|
||||
return ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseXor(b, ir.BitwiseNot(c)));
|
||||
case 218:
|
||||
return ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseXor(a, c));
|
||||
case 219:
|
||||
return ir.BitwiseOr(ir.BitwiseXor(a, c), ir.BitwiseXor(a, ir.BitwiseNot(b)));
|
||||
case 220:
|
||||
return ir.BitwiseOr(b, ir.BitwiseAnd(a, ir.BitwiseNot(c)));
|
||||
case 221:
|
||||
return ir.BitwiseOr(b, ir.BitwiseNot(c));
|
||||
case 222:
|
||||
return ir.BitwiseOr(b, ir.BitwiseXor(a, c));
|
||||
case 223:
|
||||
return ir.BitwiseOr(b, ir.BitwiseNot(ir.BitwiseAnd(a, c)));
|
||||
case 224:
|
||||
return ir.BitwiseAnd(a, ir.BitwiseOr(b, c));
|
||||
case 225:
|
||||
return ir.BitwiseXor(ir.BitwiseNot(a), ir.BitwiseOr(b, c));
|
||||
case 226:
|
||||
return ir.BitwiseAnd(ir.BitwiseOr(a, ir.BitwiseNot(b)), ir.BitwiseOr(b, c));
|
||||
case 227:
|
||||
return ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseXor(a, ir.BitwiseNot(b)));
|
||||
case 228:
|
||||
return ir.BitwiseAnd(ir.BitwiseOr(a, ir.BitwiseNot(c)), ir.BitwiseOr(b, c));
|
||||
case 229:
|
||||
return ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseXor(a, ir.BitwiseNot(c)));
|
||||
case 230:
|
||||
return ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseXor(b, c));
|
||||
case 231:
|
||||
return ir.BitwiseOr(ir.BitwiseXor(a, ir.BitwiseNot(b)), ir.BitwiseXor(b, c));
|
||||
case 232:
|
||||
return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseOr(c, ir.BitwiseAnd(a, b)));
|
||||
case 233:
|
||||
return ir.BitwiseOr(ir.BitwiseAnd(a, b),
|
||||
ir.BitwiseXor(ir.BitwiseNot(c), ir.BitwiseOr(a, b)));
|
||||
case 234:
|
||||
return ir.BitwiseOr(c, ir.BitwiseAnd(a, b));
|
||||
case 235:
|
||||
return ir.BitwiseOr(c, ir.BitwiseXor(a, ir.BitwiseNot(b)));
|
||||
case 236:
|
||||
return ir.BitwiseOr(b, ir.BitwiseAnd(a, c));
|
||||
case 237:
|
||||
return ir.BitwiseOr(b, ir.BitwiseXor(a, ir.BitwiseNot(c)));
|
||||
case 238:
|
||||
return ir.BitwiseOr(b, c);
|
||||
case 239:
|
||||
return ir.BitwiseOr(ir.BitwiseNot(a), ir.BitwiseOr(b, c));
|
||||
case 240:
|
||||
return a;
|
||||
case 241:
|
||||
return ir.BitwiseOr(a, ir.BitwiseNot(ir.BitwiseOr(b, c)));
|
||||
case 242:
|
||||
return ir.BitwiseOr(a, ir.BitwiseAnd(c, ir.BitwiseNot(b)));
|
||||
case 243:
|
||||
return ir.BitwiseOr(a, ir.BitwiseNot(b));
|
||||
case 244:
|
||||
return ir.BitwiseOr(a, ir.BitwiseAnd(b, ir.BitwiseNot(c)));
|
||||
case 245:
|
||||
return ir.BitwiseOr(a, ir.BitwiseNot(c));
|
||||
case 246:
|
||||
return ir.BitwiseOr(a, ir.BitwiseXor(b, c));
|
||||
case 247:
|
||||
return ir.BitwiseOr(a, ir.BitwiseNot(ir.BitwiseAnd(b, c)));
|
||||
case 248:
|
||||
return ir.BitwiseOr(a, ir.BitwiseAnd(b, c));
|
||||
case 249:
|
||||
return ir.BitwiseOr(a, ir.BitwiseXor(b, ir.BitwiseNot(c)));
|
||||
case 250:
|
||||
return ir.BitwiseOr(a, c);
|
||||
case 251:
|
||||
return ir.BitwiseOr(ir.BitwiseNot(b), ir.BitwiseOr(a, c));
|
||||
case 252:
|
||||
return ir.BitwiseOr(a, b);
|
||||
case 253:
|
||||
return ir.BitwiseOr(ir.BitwiseNot(c), ir.BitwiseOr(a, b));
|
||||
case 254:
|
||||
return ir.BitwiseOr(a, ir.BitwiseOr(b, c));
|
||||
case 255:
|
||||
return ir.Imm32(0xFFFFFFFF);
|
||||
// end of generated code
|
||||
}
|
||||
if (ttbl & 0x02) {
|
||||
// r |= ~a & ~b & c;
|
||||
const auto lhs{ir.BitwiseAnd(not_a, not_b)};
|
||||
const auto rhs{ir.BitwiseAnd(lhs, c)};
|
||||
r = ir.BitwiseOr(r, rhs);
|
||||
}
|
||||
if (ttbl & 0x04) {
|
||||
// r |= ~a & b & ~c;
|
||||
const auto lhs{ir.BitwiseAnd(not_a, b)};
|
||||
const auto rhs{ir.BitwiseAnd(lhs, not_c)};
|
||||
r = ir.BitwiseOr(r, rhs);
|
||||
}
|
||||
if (ttbl & 0x08) {
|
||||
// r |= ~a & b & c;
|
||||
const auto lhs{ir.BitwiseAnd(not_a, b)};
|
||||
const auto rhs{ir.BitwiseAnd(lhs, c)};
|
||||
r = ir.BitwiseOr(r, rhs);
|
||||
}
|
||||
if (ttbl & 0x10) {
|
||||
// r |= a & ~b & ~c;
|
||||
const auto lhs{ir.BitwiseAnd(a, not_b)};
|
||||
const auto rhs{ir.BitwiseAnd(lhs, not_c)};
|
||||
r = ir.BitwiseOr(r, rhs);
|
||||
}
|
||||
if (ttbl & 0x20) {
|
||||
// r |= a & ~b & c;
|
||||
const auto lhs{ir.BitwiseAnd(a, not_b)};
|
||||
const auto rhs{ir.BitwiseAnd(lhs, c)};
|
||||
r = ir.BitwiseOr(r, rhs);
|
||||
}
|
||||
if (ttbl & 0x40) {
|
||||
// r |= a & b & ~c;
|
||||
const auto lhs{ir.BitwiseAnd(a, b)};
|
||||
const auto rhs{ir.BitwiseAnd(lhs, not_c)};
|
||||
r = ir.BitwiseOr(r, rhs);
|
||||
}
|
||||
if (ttbl & 0x80) {
|
||||
// r |= a & b & c;
|
||||
const auto lhs{ir.BitwiseAnd(a, b)};
|
||||
const auto rhs{ir.BitwiseAnd(lhs, c)};
|
||||
r = ir.BitwiseOr(r, rhs);
|
||||
}
|
||||
return r;
|
||||
throw NotImplementedException("LOP3 with out of range ttbl");
|
||||
}
|
||||
|
||||
IR::U32 LOP3(TranslatorVisitor& v, u64 insn, const IR::U32& op_b, const IR::U32& op_c, u64 lut) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user