summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/CMakeLists.txt4
-rw-r--r--src/common/bit_field.h12
-rw-r--r--src/common/common_types.h7
-rw-r--r--src/common/memory_hook.cpp (renamed from src/core/memory_hook.cpp)6
-rw-r--r--src/common/memory_hook.h (renamed from src/core/memory_hook.h)4
-rw-r--r--src/common/page_table.cpp31
-rw-r--r--src/common/page_table.h84
-rw-r--r--src/common/swap.h174
-rw-r--r--src/common/thread_queue_list.h6
-rw-r--r--src/common/uint128.cpp4
-rw-r--r--src/common/uint128.h5
-rw-r--r--src/core/CMakeLists.txt8
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.h4
-rw-r--r--src/core/core.cpp11
-rw-r--r--src/core/core.h4
-rw-r--r--src/core/file_sys/cheat_engine.cpp490
-rw-r--r--src/core/file_sys/cheat_engine.h234
-rw-r--r--src/core/file_sys/content_archive.h15
-rw-r--r--src/core/file_sys/patch_manager.cpp79
-rw-r--r--src/core/file_sys/patch_manager.h9
-rw-r--r--src/core/file_sys/registered_cache.cpp2
-rw-r--r--src/core/hle/ipc.h44
-rw-r--r--src/core/hle/kernel/code_set.cpp12
-rw-r--r--src/core/hle/kernel/code_set.h90
-rw-r--r--src/core/hle/kernel/mutex.cpp35
-rw-r--r--src/core/hle/kernel/mutex.h20
-rw-r--r--src/core/hle/kernel/process.cpp17
-rw-r--r--src/core/hle/kernel/process.h59
-rw-r--r--src/core/hle/kernel/scheduler.cpp2
-rw-r--r--src/core/hle/kernel/svc.cpp17
-rw-r--r--src/core/hle/kernel/thread.cpp53
-rw-r--r--src/core/hle/kernel/thread.h10
-rw-r--r--src/core/hle/kernel/vm_manager.cpp26
-rw-r--r--src/core/hle/kernel/vm_manager.h20
-rw-r--r--src/core/hle/service/am/am.cpp86
-rw-r--r--src/core/hle/service/am/am.h16
-rw-r--r--src/core/hle/service/audio/hwopus.cpp82
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp5
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.h1
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.h30
-rw-r--r--src/core/hle/service/hid/controllers/npad.h102
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.h4
-rw-r--r--src/core/hle/service/hid/hid.h3
-rw-r--r--src/core/hle/service/ldr/ldr.cpp8
-rw-r--r--src/core/hle/service/lm/lm.cpp2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdevice.h10
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp16
-rw-r--r--src/core/loader/elf.cpp1
-rw-r--r--src/core/loader/linker.cpp147
-rw-r--r--src/core/loader/linker.h36
-rw-r--r--src/core/loader/nro.cpp1
-rw-r--r--src/core/loader/nro.h4
-rw-r--r--src/core/loader/nso.cpp114
-rw-r--r--src/core/loader/nso.h43
-rw-r--r--src/core/memory.cpp136
-rw-r--r--src/core/memory.h77
-rw-r--r--src/core/memory_setup.h19
-rw-r--r--src/input_common/sdl/sdl.h12
-rw-r--r--src/input_common/sdl/sdl_impl.cpp14
-rw-r--r--src/input_common/sdl/sdl_impl.h5
-rw-r--r--src/tests/CMakeLists.txt1
-rw-r--r--src/tests/common/bit_field.cpp90
-rw-r--r--src/tests/core/arm/arm_test_common.cpp3
-rw-r--r--src/tests/core/arm/arm_test_common.h8
-rw-r--r--src/video_core/dma_pusher.h1
-rw-r--r--src/video_core/engines/kepler_memory.cpp2
-rw-r--r--src/video_core/engines/maxwell_3d.cpp8
-rw-r--r--src/video_core/engines/maxwell_dma.cpp10
-rw-r--r--src/video_core/gpu.cpp7
-rw-r--r--src/video_core/gpu.h6
-rw-r--r--src/video_core/memory_manager.cpp482
-rw-r--r--src/video_core/memory_manager.h170
-rw-r--r--src/video_core/rasterizer_cache.h4
-rw-r--r--src/video_core/rasterizer_interface.h1
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.cpp4
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.h2
-rw-r--r--src/video_core/renderer_opengl/gl_global_cache.cpp6
-rw-r--r--src/video_core/renderer_opengl/gl_global_cache.h2
-rw-r--r--src/video_core/renderer_opengl/gl_primitive_assembler.cpp3
-rw-r--r--src/video_core/renderer_opengl/gl_primitive_assembler.h2
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp8
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp43
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h19
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp4
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.cpp3
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.h3
-rw-r--r--src/yuzu/bootmanager.cpp4
-rw-r--r--src/yuzu/debugger/graphics/graphics_surface.cpp2
-rw-r--r--src/yuzu/debugger/graphics/graphics_surface.h2
-rw-r--r--src/yuzu_cmd/yuzu.cpp4
90 files changed, 2451 insertions, 1025 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index c538c6415..43ae8a9e7 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -92,10 +92,14 @@ add_library(common STATIC
92 logging/text_formatter.cpp 92 logging/text_formatter.cpp
93 logging/text_formatter.h 93 logging/text_formatter.h
94 math_util.h 94 math_util.h
95 memory_hook.cpp
96 memory_hook.h
95 microprofile.cpp 97 microprofile.cpp
96 microprofile.h 98 microprofile.h
97 microprofileui.h 99 microprofileui.h
98 misc.cpp 100 misc.cpp
101 page_table.cpp
102 page_table.h
99 param_package.cpp 103 param_package.cpp
100 param_package.h 104 param_package.h
101 quaternion.h 105 quaternion.h
diff --git a/src/common/bit_field.h b/src/common/bit_field.h
index 7433c39ba..902e668e3 100644
--- a/src/common/bit_field.h
+++ b/src/common/bit_field.h
@@ -34,6 +34,7 @@
34#include <limits> 34#include <limits>
35#include <type_traits> 35#include <type_traits>
36#include "common/common_funcs.h" 36#include "common/common_funcs.h"
37#include "common/swap.h"
37 38
38/* 39/*
39 * Abstract bitfield class 40 * Abstract bitfield class
@@ -108,7 +109,7 @@
108 * symptoms. 109 * symptoms.
109 */ 110 */
110#pragma pack(1) 111#pragma pack(1)
111template <std::size_t Position, std::size_t Bits, typename T> 112template <std::size_t Position, std::size_t Bits, typename T, typename EndianTag = LETag>
112struct BitField { 113struct BitField {
113private: 114private:
114 // UnderlyingType is T for non-enum types and the underlying type of T if 115 // UnderlyingType is T for non-enum types and the underlying type of T if
@@ -121,6 +122,8 @@ private:
121 // We store the value as the unsigned type to avoid undefined behaviour on value shifting 122 // We store the value as the unsigned type to avoid undefined behaviour on value shifting
122 using StorageType = std::make_unsigned_t<UnderlyingType>; 123 using StorageType = std::make_unsigned_t<UnderlyingType>;
123 124
125 using StorageTypeWithEndian = typename AddEndian<StorageType, EndianTag>::type;
126
124public: 127public:
125 /// Constants to allow limited introspection of fields if needed 128 /// Constants to allow limited introspection of fields if needed
126 static constexpr std::size_t position = Position; 129 static constexpr std::size_t position = Position;
@@ -170,7 +173,7 @@ public:
170 } 173 }
171 174
172 constexpr FORCE_INLINE void Assign(const T& value) { 175 constexpr FORCE_INLINE void Assign(const T& value) {
173 storage = (storage & ~mask) | FormatValue(value); 176 storage = (static_cast<StorageType>(storage) & ~mask) | FormatValue(value);
174 } 177 }
175 178
176 constexpr T Value() const { 179 constexpr T Value() const {
@@ -182,7 +185,7 @@ public:
182 } 185 }
183 186
184private: 187private:
185 StorageType storage; 188 StorageTypeWithEndian storage;
186 189
187 static_assert(bits + position <= 8 * sizeof(T), "Bitfield out of range"); 190 static_assert(bits + position <= 8 * sizeof(T), "Bitfield out of range");
188 191
@@ -193,3 +196,6 @@ private:
193 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable in a BitField"); 196 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable in a BitField");
194}; 197};
195#pragma pack() 198#pragma pack()
199
200template <std::size_t Position, std::size_t Bits, typename T>
201using BitFieldBE = BitField<Position, Bits, T, BETag>;
diff --git a/src/common/common_types.h b/src/common/common_types.h
index 6b1766dca..4cec89fbd 100644
--- a/src/common/common_types.h
+++ b/src/common/common_types.h
@@ -40,10 +40,9 @@ using s64 = std::int64_t; ///< 64-bit signed int
40using f32 = float; ///< 32-bit floating point 40using f32 = float; ///< 32-bit floating point
41using f64 = double; ///< 64-bit floating point 41using f64 = double; ///< 64-bit floating point
42 42
43// TODO: It would be nice to eventually replace these with strong types that prevent accidental 43using VAddr = u64; ///< Represents a pointer in the userspace virtual address space.
44// conversion between each other. 44using PAddr = u64; ///< Represents a pointer in the ARM11 physical address space.
45using VAddr = u64; ///< Represents a pointer in the userspace virtual address space. 45using GPUVAddr = u64; ///< Represents a pointer in the GPU virtual address space.
46using PAddr = u64; ///< Represents a pointer in the ARM11 physical address space.
47 46
48using u128 = std::array<std::uint64_t, 2>; 47using u128 = std::array<std::uint64_t, 2>;
49static_assert(sizeof(u128) == 16, "u128 must be 128 bits wide"); 48static_assert(sizeof(u128) == 16, "u128 must be 128 bits wide");
diff --git a/src/core/memory_hook.cpp b/src/common/memory_hook.cpp
index c61c6c1fb..3986986d6 100644
--- a/src/core/memory_hook.cpp
+++ b/src/common/memory_hook.cpp
@@ -2,10 +2,10 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "core/memory_hook.h" 5#include "common/memory_hook.h"
6 6
7namespace Memory { 7namespace Common {
8 8
9MemoryHook::~MemoryHook() = default; 9MemoryHook::~MemoryHook() = default;
10 10
11} // namespace Memory 11} // namespace Common
diff --git a/src/core/memory_hook.h b/src/common/memory_hook.h
index 940777107..adaa4c2c5 100644
--- a/src/core/memory_hook.h
+++ b/src/common/memory_hook.h
@@ -9,7 +9,7 @@
9 9
10#include "common/common_types.h" 10#include "common/common_types.h"
11 11
12namespace Memory { 12namespace Common {
13 13
14/** 14/**
15 * Memory hooks have two purposes: 15 * Memory hooks have two purposes:
@@ -44,4 +44,4 @@ public:
44}; 44};
45 45
46using MemoryHookPointer = std::shared_ptr<MemoryHook>; 46using MemoryHookPointer = std::shared_ptr<MemoryHook>;
47} // namespace Memory 47} // namespace Common
diff --git a/src/common/page_table.cpp b/src/common/page_table.cpp
new file mode 100644
index 000000000..69b7abc54
--- /dev/null
+++ b/src/common/page_table.cpp
@@ -0,0 +1,31 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/page_table.h"
6
7namespace Common {
8
9PageTable::PageTable(std::size_t page_size_in_bits) : page_size_in_bits{page_size_in_bits} {}
10
11PageTable::~PageTable() = default;
12
13void PageTable::Resize(std::size_t address_space_width_in_bits) {
14 const std::size_t num_page_table_entries = 1ULL
15 << (address_space_width_in_bits - page_size_in_bits);
16
17 pointers.resize(num_page_table_entries);
18 attributes.resize(num_page_table_entries);
19 backing_addr.resize(num_page_table_entries);
20
21 // The default is a 39-bit address space, which causes an initial 1GB allocation size. If the
22 // vector size is subsequently decreased (via resize), the vector might not automatically
23 // actually reallocate/resize its underlying allocation, which wastes up to ~800 MB for
24 // 36-bit titles. Call shrink_to_fit to reduce capacity to what's actually in use.
25
26 pointers.shrink_to_fit();
27 attributes.shrink_to_fit();
28 backing_addr.shrink_to_fit();
29}
30
31} // namespace Common
diff --git a/src/common/page_table.h b/src/common/page_table.h
new file mode 100644
index 000000000..8b8ff0bb8
--- /dev/null
+++ b/src/common/page_table.h
@@ -0,0 +1,84 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <vector>
8#include <boost/icl/interval_map.hpp>
9#include "common/common_types.h"
10#include "common/memory_hook.h"
11
12namespace Common {
13
14enum class PageType : u8 {
15 /// Page is unmapped and should cause an access error.
16 Unmapped,
17 /// Page is mapped to regular memory. This is the only type you can get pointers to.
18 Memory,
19 /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
20 /// invalidation
21 RasterizerCachedMemory,
22 /// Page is mapped to a I/O region. Writing and reading to this page is handled by functions.
23 Special,
24 /// Page is allocated for use.
25 Allocated,
26};
27
28struct SpecialRegion {
29 enum class Type {
30 DebugHook,
31 IODevice,
32 } type;
33
34 MemoryHookPointer handler;
35
36 bool operator<(const SpecialRegion& other) const {
37 return std::tie(type, handler) < std::tie(other.type, other.handler);
38 }
39
40 bool operator==(const SpecialRegion& other) const {
41 return std::tie(type, handler) == std::tie(other.type, other.handler);
42 }
43};
44
45/**
46 * A (reasonably) fast way of allowing switchable and remappable process address spaces. It loosely
47 * mimics the way a real CPU page table works.
48 */
49struct PageTable {
50 explicit PageTable(std::size_t page_size_in_bits);
51 ~PageTable();
52
53 /**
54 * Resizes the page table to be able to accomodate enough pages within
55 * a given address space.
56 *
57 * @param address_space_width_in_bits The address size width in bits.
58 */
59 void Resize(std::size_t address_space_width_in_bits);
60
61 /**
62 * Vector of memory pointers backing each page. An entry can only be non-null if the
63 * corresponding entry in the `attributes` vector is of type `Memory`.
64 */
65 std::vector<u8*> pointers;
66
67 /**
68 * Contains MMIO handlers that back memory regions whose entries in the `attribute` vector is
69 * of type `Special`.
70 */
71 boost::icl::interval_map<u64, std::set<SpecialRegion>> special_regions;
72
73 /**
74 * Vector of fine grained page attributes. If it is set to any value other than `Memory`, then
75 * the corresponding entry in `pointers` MUST be set to null.
76 */
77 std::vector<PageType> attributes;
78
79 std::vector<u64> backing_addr;
80
81 const std::size_t page_size_in_bits{};
82};
83
84} // namespace Common
diff --git a/src/common/swap.h b/src/common/swap.h
index 0e219747f..b3eab1324 100644
--- a/src/common/swap.h
+++ b/src/common/swap.h
@@ -17,6 +17,8 @@
17 17
18#pragma once 18#pragma once
19 19
20#include <type_traits>
21
20#if defined(_MSC_VER) 22#if defined(_MSC_VER)
21#include <cstdlib> 23#include <cstdlib>
22#elif defined(__linux__) 24#elif defined(__linux__)
@@ -170,7 +172,7 @@ struct swap_struct_t {
170 using swapped_t = swap_struct_t; 172 using swapped_t = swap_struct_t;
171 173
172protected: 174protected:
173 T value = T(); 175 T value;
174 176
175 static T swap(T v) { 177 static T swap(T v) {
176 return F::swap(v); 178 return F::swap(v);
@@ -605,52 +607,154 @@ struct swap_double_t {
605 } 607 }
606}; 608};
607 609
608#if COMMON_LITTLE_ENDIAN 610template <typename T>
609using u16_le = u16; 611struct swap_enum_t {
610using u32_le = u32; 612 static_assert(std::is_enum_v<T>);
611using u64_le = u64; 613 using base = std::underlying_type_t<T>;
614
615public:
616 swap_enum_t() = default;
617 swap_enum_t(const T& v) : value(swap(v)) {}
618
619 swap_enum_t& operator=(const T& v) {
620 value = swap(v);
621 return *this;
622 }
623
624 operator T() const {
625 return swap(value);
626 }
627
628 explicit operator base() const {
629 return static_cast<base>(swap(value));
630 }
612 631
613using s16_le = s16; 632protected:
614using s32_le = s32; 633 T value{};
615using s64_le = s64; 634 // clang-format off
635 using swap_t = std::conditional_t<
636 std::is_same_v<base, u16>, swap_16_t<u16>, std::conditional_t<
637 std::is_same_v<base, s16>, swap_16_t<s16>, std::conditional_t<
638 std::is_same_v<base, u32>, swap_32_t<u32>, std::conditional_t<
639 std::is_same_v<base, s32>, swap_32_t<s32>, std::conditional_t<
640 std::is_same_v<base, u64>, swap_64_t<u64>, std::conditional_t<
641 std::is_same_v<base, s64>, swap_64_t<s64>, void>>>>>>;
642 // clang-format on
643 static T swap(T x) {
644 return static_cast<T>(swap_t::swap(static_cast<base>(x)));
645 }
646};
616 647
617using float_le = float; 648struct SwapTag {}; // Use the different endianness from the system
618using double_le = double; 649struct KeepTag {}; // Use the same endianness as the system
619 650
620using u64_be = swap_struct_t<u64, swap_64_t<u64>>; 651template <typename T, typename Tag>
621using s64_be = swap_struct_t<s64, swap_64_t<s64>>; 652struct AddEndian;
622 653
623using u32_be = swap_struct_t<u32, swap_32_t<u32>>; 654// KeepTag specializations
624using s32_be = swap_struct_t<s32, swap_32_t<s32>>;
625 655
626using u16_be = swap_struct_t<u16, swap_16_t<u16>>; 656template <typename T>
627using s16_be = swap_struct_t<s16, swap_16_t<s16>>; 657struct AddEndian<T, KeepTag> {
658 using type = T;
659};
628 660
629using float_be = swap_struct_t<float, swap_float_t<float>>; 661// SwapTag specializations
630using double_be = swap_struct_t<double, swap_double_t<double>>; 662
631#else 663template <>
664struct AddEndian<u8, SwapTag> {
665 using type = u8;
666};
667
668template <>
669struct AddEndian<u16, SwapTag> {
670 using type = swap_struct_t<u16, swap_16_t<u16>>;
671};
672
673template <>
674struct AddEndian<u32, SwapTag> {
675 using type = swap_struct_t<u32, swap_32_t<u32>>;
676};
632 677
633using u64_le = swap_struct_t<u64, swap_64_t<u64>>; 678template <>
634using s64_le = swap_struct_t<s64, swap_64_t<s64>>; 679struct AddEndian<u64, SwapTag> {
680 using type = swap_struct_t<u64, swap_64_t<u64>>;
681};
682
683template <>
684struct AddEndian<s8, SwapTag> {
685 using type = s8;
686};
635 687
636using u32_le = swap_struct_t<u32, swap_32_t<u32>>; 688template <>
637using s32_le = swap_struct_t<s32, swap_32_t<s32>>; 689struct AddEndian<s16, SwapTag> {
690 using type = swap_struct_t<s16, swap_16_t<s16>>;
691};
638 692
639using u16_le = swap_struct_t<u16, swap_16_t<u16>>; 693template <>
640using s16_le = swap_struct_t<s16, swap_16_t<s16>>; 694struct AddEndian<s32, SwapTag> {
695 using type = swap_struct_t<s32, swap_32_t<s32>>;
696};
697
698template <>
699struct AddEndian<s64, SwapTag> {
700 using type = swap_struct_t<s64, swap_64_t<s64>>;
701};
702
703template <>
704struct AddEndian<float, SwapTag> {
705 using type = swap_struct_t<float, swap_float_t<float>>;
706};
707
708template <>
709struct AddEndian<double, SwapTag> {
710 using type = swap_struct_t<double, swap_double_t<double>>;
711};
712
713template <typename T>
714struct AddEndian<T, SwapTag> {
715 static_assert(std::is_enum_v<T>);
716 using type = swap_enum_t<T>;
717};
641 718
642using float_le = swap_struct_t<float, swap_float_t<float>>; 719// Alias LETag/BETag as KeepTag/SwapTag depending on the system
643using double_le = swap_struct_t<double, swap_double_t<double>>; 720#if COMMON_LITTLE_ENDIAN
644 721
645using u16_be = u16; 722using LETag = KeepTag;
646using u32_be = u32; 723using BETag = SwapTag;
647using u64_be = u64;
648 724
649using s16_be = s16; 725#else
650using s32_be = s32;
651using s64_be = s64;
652 726
653using float_be = float; 727using BETag = KeepTag;
654using double_be = double; 728using LETag = SwapTag;
655 729
656#endif 730#endif
731
732// Aliases for LE types
733using u16_le = AddEndian<u16, LETag>::type;
734using u32_le = AddEndian<u32, LETag>::type;
735using u64_le = AddEndian<u64, LETag>::type;
736
737using s16_le = AddEndian<s16, LETag>::type;
738using s32_le = AddEndian<s32, LETag>::type;
739using s64_le = AddEndian<s64, LETag>::type;
740
741template <typename T>
742using enum_le = std::enable_if_t<std::is_enum_v<T>, typename AddEndian<T, LETag>::type>;
743
744using float_le = AddEndian<float, LETag>::type;
745using double_le = AddEndian<double, LETag>::type;
746
747// Aliases for BE types
748using u16_be = AddEndian<u16, BETag>::type;
749using u32_be = AddEndian<u32, BETag>::type;
750using u64_be = AddEndian<u64, BETag>::type;
751
752using s16_be = AddEndian<s16, BETag>::type;
753using s32_be = AddEndian<s32, BETag>::type;
754using s64_be = AddEndian<s64, BETag>::type;
755
756template <typename T>
757using enum_be = std::enable_if_t<std::is_enum_v<T>, typename AddEndian<T, BETag>::type>;
758
759using float_be = AddEndian<float, BETag>::type;
760using double_be = AddEndian<double, BETag>::type;
diff --git a/src/common/thread_queue_list.h b/src/common/thread_queue_list.h
index e7594db68..791f99a8c 100644
--- a/src/common/thread_queue_list.h
+++ b/src/common/thread_queue_list.h
@@ -6,7 +6,6 @@
6 6
7#include <array> 7#include <array>
8#include <deque> 8#include <deque>
9#include <boost/range/algorithm_ext/erase.hpp>
10 9
11namespace Common { 10namespace Common {
12 11
@@ -111,8 +110,9 @@ struct ThreadQueueList {
111 } 110 }
112 111
113 void remove(Priority priority, const T& thread_id) { 112 void remove(Priority priority, const T& thread_id) {
114 Queue* cur = &queues[priority]; 113 Queue* const cur = &queues[priority];
115 boost::remove_erase(cur->data, thread_id); 114 const auto iter = std::remove(cur->data.begin(), cur->data.end(), thread_id);
115 cur->data.erase(iter, cur->data.end());
116 } 116 }
117 117
118 void rotate(Priority priority) { 118 void rotate(Priority priority) {
diff --git a/src/common/uint128.cpp b/src/common/uint128.cpp
index 2238a52c5..32bf56730 100644
--- a/src/common/uint128.cpp
+++ b/src/common/uint128.cpp
@@ -1,3 +1,7 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
1#ifdef _MSC_VER 5#ifdef _MSC_VER
2#include <intrin.h> 6#include <intrin.h>
3 7
diff --git a/src/common/uint128.h b/src/common/uint128.h
index 52e6b46eb..a3be2a2cb 100644
--- a/src/common/uint128.h
+++ b/src/common/uint128.h
@@ -1,3 +1,8 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
1 6
2#include <utility> 7#include <utility>
3#include "common/common_types.h" 8#include "common/common_types.h"
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 8ccb2d5f0..bbbe60896 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -31,6 +31,8 @@ add_library(core STATIC
31 file_sys/bis_factory.h 31 file_sys/bis_factory.h
32 file_sys/card_image.cpp 32 file_sys/card_image.cpp
33 file_sys/card_image.h 33 file_sys/card_image.h
34 file_sys/cheat_engine.cpp
35 file_sys/cheat_engine.h
34 file_sys/content_archive.cpp 36 file_sys/content_archive.cpp
35 file_sys/content_archive.h 37 file_sys/content_archive.h
36 file_sys/control_metadata.cpp 38 file_sys/control_metadata.cpp
@@ -107,6 +109,8 @@ add_library(core STATIC
107 hle/kernel/client_port.h 109 hle/kernel/client_port.h
108 hle/kernel/client_session.cpp 110 hle/kernel/client_session.cpp
109 hle/kernel/client_session.h 111 hle/kernel/client_session.h
112 hle/kernel/code_set.cpp
113 hle/kernel/code_set.h
110 hle/kernel/errors.h 114 hle/kernel/errors.h
111 hle/kernel/handle_table.cpp 115 hle/kernel/handle_table.cpp
112 hle/kernel/handle_table.h 116 hle/kernel/handle_table.h
@@ -419,8 +423,6 @@ add_library(core STATIC
419 loader/deconstructed_rom_directory.h 423 loader/deconstructed_rom_directory.h
420 loader/elf.cpp 424 loader/elf.cpp
421 loader/elf.h 425 loader/elf.h
422 loader/linker.cpp
423 loader/linker.h
424 loader/loader.cpp 426 loader/loader.cpp
425 loader/loader.h 427 loader/loader.h
426 loader/nax.cpp 428 loader/nax.cpp
@@ -437,8 +439,6 @@ add_library(core STATIC
437 loader/xci.h 439 loader/xci.h
438 memory.cpp 440 memory.cpp
439 memory.h 441 memory.h
440 memory_hook.cpp
441 memory_hook.h
442 memory_setup.h 442 memory_setup.h
443 perf_stats.cpp 443 perf_stats.cpp
444 perf_stats.h 444 perf_stats.h
diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h
index 6cc458296..aada1e862 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.h
+++ b/src/core/arm/dynarmic/arm_dynarmic.h
@@ -12,7 +12,7 @@
12#include "core/arm/exclusive_monitor.h" 12#include "core/arm/exclusive_monitor.h"
13#include "core/arm/unicorn/arm_unicorn.h" 13#include "core/arm/unicorn/arm_unicorn.h"
14 14
15namespace Memory { 15namespace Common {
16struct PageTable; 16struct PageTable;
17} 17}
18 18
@@ -70,7 +70,7 @@ private:
70 Timing::CoreTiming& core_timing; 70 Timing::CoreTiming& core_timing;
71 DynarmicExclusiveMonitor& exclusive_monitor; 71 DynarmicExclusiveMonitor& exclusive_monitor;
72 72
73 Memory::PageTable* current_page_table = nullptr; 73 Common::PageTable* current_page_table = nullptr;
74}; 74};
75 75
76class DynarmicExclusiveMonitor final : public ExclusiveMonitor { 76class DynarmicExclusiveMonitor final : public ExclusiveMonitor {
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 89b3fb418..4fe77c25b 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -32,6 +32,7 @@
32#include "core/perf_stats.h" 32#include "core/perf_stats.h"
33#include "core/settings.h" 33#include "core/settings.h"
34#include "core/telemetry_session.h" 34#include "core/telemetry_session.h"
35#include "file_sys/cheat_engine.h"
35#include "frontend/applets/profile_select.h" 36#include "frontend/applets/profile_select.h"
36#include "frontend/applets/software_keyboard.h" 37#include "frontend/applets/software_keyboard.h"
37#include "frontend/applets/web_browser.h" 38#include "frontend/applets/web_browser.h"
@@ -205,6 +206,7 @@ struct System::Impl {
205 GDBStub::Shutdown(); 206 GDBStub::Shutdown();
206 Service::Shutdown(); 207 Service::Shutdown();
207 service_manager.reset(); 208 service_manager.reset();
209 cheat_engine.reset();
208 telemetry_session.reset(); 210 telemetry_session.reset();
209 gpu_core.reset(); 211 gpu_core.reset();
210 212
@@ -255,6 +257,8 @@ struct System::Impl {
255 CpuCoreManager cpu_core_manager; 257 CpuCoreManager cpu_core_manager;
256 bool is_powered_on = false; 258 bool is_powered_on = false;
257 259
260 std::unique_ptr<FileSys::CheatEngine> cheat_engine;
261
258 /// Frontend applets 262 /// Frontend applets
259 std::unique_ptr<Core::Frontend::ProfileSelectApplet> profile_selector; 263 std::unique_ptr<Core::Frontend::ProfileSelectApplet> profile_selector;
260 std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard; 264 std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard;
@@ -453,6 +457,13 @@ Tegra::DebugContext* System::GetGPUDebugContext() const {
453 return impl->debug_context.get(); 457 return impl->debug_context.get();
454} 458}
455 459
460void System::RegisterCheatList(const std::vector<FileSys::CheatList>& list,
461 const std::string& build_id, VAddr code_region_start,
462 VAddr code_region_end) {
463 impl->cheat_engine = std::make_unique<FileSys::CheatEngine>(*this, list, build_id,
464 code_region_start, code_region_end);
465}
466
456void System::SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs) { 467void System::SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs) {
457 impl->virtual_filesystem = std::move(vfs); 468 impl->virtual_filesystem = std::move(vfs);
458} 469}
diff --git a/src/core/core.h b/src/core/core.h
index ba76a41d8..4d83b93cc 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -20,6 +20,7 @@ class WebBrowserApplet;
20} // namespace Core::Frontend 20} // namespace Core::Frontend
21 21
22namespace FileSys { 22namespace FileSys {
23class CheatList;
23class VfsFilesystem; 24class VfsFilesystem;
24} // namespace FileSys 25} // namespace FileSys
25 26
@@ -253,6 +254,9 @@ public:
253 254
254 std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const; 255 std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const;
255 256
257 void RegisterCheatList(const std::vector<FileSys::CheatList>& list, const std::string& build_id,
258 VAddr code_region_start, VAddr code_region_end);
259
256 void SetProfileSelector(std::unique_ptr<Frontend::ProfileSelectApplet> applet); 260 void SetProfileSelector(std::unique_ptr<Frontend::ProfileSelectApplet> applet);
257 261
258 const Frontend::ProfileSelectApplet& GetProfileSelector() const; 262 const Frontend::ProfileSelectApplet& GetProfileSelector() const;
diff --git a/src/core/file_sys/cheat_engine.cpp b/src/core/file_sys/cheat_engine.cpp
new file mode 100644
index 000000000..247fbc864
--- /dev/null
+++ b/src/core/file_sys/cheat_engine.cpp
@@ -0,0 +1,490 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <locale>
6#include "common/hex_util.h"
7#include "common/microprofile.h"
8#include "common/swap.h"
9#include "core/core.h"
10#include "core/core_timing.h"
11#include "core/core_timing_util.h"
12#include "core/file_sys/cheat_engine.h"
13#include "core/hle/kernel/process.h"
14#include "core/hle/service/hid/controllers/npad.h"
15#include "core/hle/service/hid/hid.h"
16#include "core/hle/service/sm/sm.h"
17
18namespace FileSys {
19
20constexpr s64 CHEAT_ENGINE_TICKS = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 60);
21constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF;
22
23u64 Cheat::Address() const {
24 u64 out;
25 std::memcpy(&out, raw.data(), sizeof(u64));
26 return Common::swap64(out) & 0xFFFFFFFFFF;
27}
28
29u64 Cheat::ValueWidth(u64 offset) const {
30 return Value(offset, width);
31}
32
33u64 Cheat::Value(u64 offset, u64 width) const {
34 u64 out;
35 std::memcpy(&out, raw.data() + offset, sizeof(u64));
36 out = Common::swap64(out);
37 if (width == 8)
38 return out;
39 return out & ((1ull << (width * CHAR_BIT)) - 1);
40}
41
42u32 Cheat::KeypadValue() const {
43 u32 out;
44 std::memcpy(&out, raw.data(), sizeof(u32));
45 return Common::swap32(out) & 0x0FFFFFFF;
46}
47
48void CheatList::SetMemoryParameters(VAddr main_begin, VAddr heap_begin, VAddr main_end,
49 VAddr heap_end, MemoryWriter writer, MemoryReader reader) {
50 this->main_region_begin = main_begin;
51 this->main_region_end = main_end;
52 this->heap_region_begin = heap_begin;
53 this->heap_region_end = heap_end;
54 this->writer = writer;
55 this->reader = reader;
56}
57
58MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70));
59
60void CheatList::Execute() {
61 MICROPROFILE_SCOPE(Cheat_Engine);
62
63 std::fill(scratch.begin(), scratch.end(), 0);
64 in_standard = false;
65 for (std::size_t i = 0; i < master_list.size(); ++i) {
66 LOG_DEBUG(Common_Filesystem, "Executing block #{:08X} ({})", i, master_list[i].first);
67 current_block = i;
68 ExecuteBlock(master_list[i].second);
69 }
70
71 in_standard = true;
72 for (std::size_t i = 0; i < standard_list.size(); ++i) {
73 LOG_DEBUG(Common_Filesystem, "Executing block #{:08X} ({})", i, standard_list[i].first);
74 current_block = i;
75 ExecuteBlock(standard_list[i].second);
76 }
77}
78
79CheatList::CheatList(const Core::System& system_, ProgramSegment master, ProgramSegment standard)
80 : master_list{std::move(master)}, standard_list{std::move(standard)}, system{&system_} {}
81
82bool CheatList::EvaluateConditional(const Cheat& cheat) const {
83 using ComparisonFunction = bool (*)(u64, u64);
84 constexpr std::array<ComparisonFunction, 6> comparison_functions{
85 [](u64 a, u64 b) { return a > b; }, [](u64 a, u64 b) { return a >= b; },
86 [](u64 a, u64 b) { return a < b; }, [](u64 a, u64 b) { return a <= b; },
87 [](u64 a, u64 b) { return a == b; }, [](u64 a, u64 b) { return a != b; },
88 };
89
90 if (cheat.type == CodeType::ConditionalInput) {
91 const auto applet_resource =
92 system->ServiceManager().GetService<Service::HID::Hid>("hid")->GetAppletResource();
93 if (applet_resource == nullptr) {
94 LOG_WARNING(
95 Common_Filesystem,
96 "Attempted to evaluate input conditional, but applet resource is not initialized!");
97 return false;
98 }
99
100 const auto press_state =
101 applet_resource
102 ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad)
103 .GetAndResetPressState();
104 return ((press_state & cheat.KeypadValue()) & KEYPAD_BITMASK) != 0;
105 }
106
107 ASSERT(cheat.type == CodeType::Conditional);
108
109 const auto offset =
110 cheat.memory_type == MemoryType::MainNSO ? main_region_begin : heap_region_begin;
111 ASSERT(static_cast<u8>(cheat.comparison_op.Value()) < 6);
112 auto* function = comparison_functions[static_cast<u8>(cheat.comparison_op.Value())];
113 const auto addr = cheat.Address() + offset;
114
115 return function(reader(cheat.width, SanitizeAddress(addr)), cheat.ValueWidth(8));
116}
117
118void CheatList::ProcessBlockPairs(const Block& block) {
119 block_pairs.clear();
120
121 u64 scope = 0;
122 std::map<u64, u64> pairs;
123
124 for (std::size_t i = 0; i < block.size(); ++i) {
125 const auto& cheat = block[i];
126
127 switch (cheat.type) {
128 case CodeType::Conditional:
129 case CodeType::ConditionalInput:
130 pairs.insert_or_assign(scope, i);
131 ++scope;
132 break;
133 case CodeType::EndConditional: {
134 --scope;
135 const auto idx = pairs.at(scope);
136 block_pairs.insert_or_assign(idx, i);
137 break;
138 }
139 case CodeType::Loop: {
140 if (cheat.end_of_loop) {
141 --scope;
142 const auto idx = pairs.at(scope);
143 block_pairs.insert_or_assign(idx, i);
144 } else {
145 pairs.insert_or_assign(scope, i);
146 ++scope;
147 }
148 break;
149 }
150 }
151 }
152}
153
154void CheatList::WriteImmediate(const Cheat& cheat) {
155 const auto offset =
156 cheat.memory_type == MemoryType::MainNSO ? main_region_begin : heap_region_begin;
157 const auto& register_3 = scratch.at(cheat.register_3);
158
159 const auto addr = cheat.Address() + offset + register_3;
160 LOG_DEBUG(Common_Filesystem, "writing value={:016X} to addr={:016X}", addr,
161 cheat.Value(8, cheat.width));
162 writer(cheat.width, SanitizeAddress(addr), cheat.ValueWidth(8));
163}
164
165void CheatList::BeginConditional(const Cheat& cheat) {
166 if (EvaluateConditional(cheat)) {
167 return;
168 }
169
170 const auto iter = block_pairs.find(current_index);
171 ASSERT(iter != block_pairs.end());
172 current_index = iter->second - 1;
173}
174
175void CheatList::EndConditional(const Cheat& cheat) {
176 LOG_DEBUG(Common_Filesystem, "Ending conditional block.");
177}
178
179void CheatList::Loop(const Cheat& cheat) {
180 if (cheat.end_of_loop.Value())
181 ASSERT(!cheat.end_of_loop.Value());
182
183 auto& register_3 = scratch.at(cheat.register_3);
184 const auto iter = block_pairs.find(current_index);
185 ASSERT(iter != block_pairs.end());
186 ASSERT(iter->first < iter->second);
187
188 const s32 initial_value = static_cast<s32>(cheat.Value(4, sizeof(s32)));
189 for (s32 i = initial_value; i >= 0; --i) {
190 register_3 = static_cast<u64>(i);
191 for (std::size_t c = iter->first + 1; c < iter->second; ++c) {
192 current_index = c;
193 ExecuteSingleCheat(
194 (in_standard ? standard_list : master_list)[current_block].second[c]);
195 }
196 }
197
198 current_index = iter->second;
199}
200
201void CheatList::LoadImmediate(const Cheat& cheat) {
202 auto& register_3 = scratch.at(cheat.register_3);
203
204 LOG_DEBUG(Common_Filesystem, "setting register={:01X} equal to value={:016X}", cheat.register_3,
205 cheat.Value(4, 8));
206 register_3 = cheat.Value(4, 8);
207}
208
209void CheatList::LoadIndexed(const Cheat& cheat) {
210 const auto offset =
211 cheat.memory_type == MemoryType::MainNSO ? main_region_begin : heap_region_begin;
212 auto& register_3 = scratch.at(cheat.register_3);
213
214 const auto addr = (cheat.load_from_register.Value() ? register_3 : offset) + cheat.Address();
215 LOG_DEBUG(Common_Filesystem, "writing indexed value to register={:01X}, addr={:016X}",
216 cheat.register_3, addr);
217 register_3 = reader(cheat.width, SanitizeAddress(addr));
218}
219
220void CheatList::StoreIndexed(const Cheat& cheat) {
221 const auto& register_3 = scratch.at(cheat.register_3);
222
223 const auto addr =
224 register_3 + (cheat.add_additional_register.Value() ? scratch.at(cheat.register_6) : 0);
225 LOG_DEBUG(Common_Filesystem, "writing value={:016X} to addr={:016X}",
226 cheat.Value(4, cheat.width), addr);
227 writer(cheat.width, SanitizeAddress(addr), cheat.ValueWidth(4));
228}
229
230void CheatList::RegisterArithmetic(const Cheat& cheat) {
231 using ArithmeticFunction = u64 (*)(u64, u64);
232 constexpr std::array<ArithmeticFunction, 5> arithmetic_functions{
233 [](u64 a, u64 b) { return a + b; }, [](u64 a, u64 b) { return a - b; },
234 [](u64 a, u64 b) { return a * b; }, [](u64 a, u64 b) { return a << b; },
235 [](u64 a, u64 b) { return a >> b; },
236 };
237
238 using ArithmeticOverflowCheck = bool (*)(u64, u64);
239 constexpr std::array<ArithmeticOverflowCheck, 5> arithmetic_overflow_checks{
240 [](u64 a, u64 b) { return a > (std::numeric_limits<u64>::max() - b); }, // a + b
241 [](u64 a, u64 b) { return a > (std::numeric_limits<u64>::max() + b); }, // a - b
242 [](u64 a, u64 b) { return a > (std::numeric_limits<u64>::max() / b); }, // a * b
243 [](u64 a, u64 b) { return b >= 64 || (a & ~((1ull << (64 - b)) - 1)) != 0; }, // a << b
244 [](u64 a, u64 b) { return b >= 64 || (a & ((1ull << b) - 1)) != 0; }, // a >> b
245 };
246
247 static_assert(sizeof(arithmetic_functions) == sizeof(arithmetic_overflow_checks),
248 "Missing or have extra arithmetic overflow checks compared to functions!");
249
250 auto& register_3 = scratch.at(cheat.register_3);
251
252 ASSERT(static_cast<u8>(cheat.arithmetic_op.Value()) < 5);
253 auto* function = arithmetic_functions[static_cast<u8>(cheat.arithmetic_op.Value())];
254 auto* overflow_function =
255 arithmetic_overflow_checks[static_cast<u8>(cheat.arithmetic_op.Value())];
256 LOG_DEBUG(Common_Filesystem, "performing arithmetic with register={:01X}, value={:016X}",
257 cheat.register_3, cheat.ValueWidth(4));
258
259 if (overflow_function(register_3, cheat.ValueWidth(4))) {
260 LOG_WARNING(Common_Filesystem,
261 "overflow will occur when performing arithmetic operation={:02X} with operands "
262 "a={:016X}, b={:016X}!",
263 static_cast<u8>(cheat.arithmetic_op.Value()), register_3, cheat.ValueWidth(4));
264 }
265
266 register_3 = function(register_3, cheat.ValueWidth(4));
267}
268
269void CheatList::BeginConditionalInput(const Cheat& cheat) {
270 if (EvaluateConditional(cheat))
271 return;
272
273 const auto iter = block_pairs.find(current_index);
274 ASSERT(iter != block_pairs.end());
275 current_index = iter->second - 1;
276}
277
278VAddr CheatList::SanitizeAddress(VAddr in) const {
279 if ((in < main_region_begin || in >= main_region_end) &&
280 (in < heap_region_begin || in >= heap_region_end)) {
281 LOG_ERROR(Common_Filesystem,
282 "Cheat attempting to access memory at invalid address={:016X}, if this persists, "
283 "the cheat may be incorrect. However, this may be normal early in execution if "
284 "the game has not properly set up yet.",
285 in);
286 return 0; ///< Invalid addresses will hard crash
287 }
288
289 return in;
290}
291
292void CheatList::ExecuteSingleCheat(const Cheat& cheat) {
293 using CheatOperationFunction = void (CheatList::*)(const Cheat&);
294 constexpr std::array<CheatOperationFunction, 9> cheat_operation_functions{
295 &CheatList::WriteImmediate, &CheatList::BeginConditional,
296 &CheatList::EndConditional, &CheatList::Loop,
297 &CheatList::LoadImmediate, &CheatList::LoadIndexed,
298 &CheatList::StoreIndexed, &CheatList::RegisterArithmetic,
299 &CheatList::BeginConditionalInput,
300 };
301
302 const auto index = static_cast<u8>(cheat.type.Value());
303 ASSERT(index < sizeof(cheat_operation_functions));
304 const auto op = cheat_operation_functions[index];
305 (this->*op)(cheat);
306}
307
308void CheatList::ExecuteBlock(const Block& block) {
309 encountered_loops.clear();
310
311 ProcessBlockPairs(block);
312 for (std::size_t i = 0; i < block.size(); ++i) {
313 current_index = i;
314 ExecuteSingleCheat(block[i]);
315 i = current_index;
316 }
317}
318
319CheatParser::~CheatParser() = default;
320
321CheatList CheatParser::MakeCheatList(const Core::System& system, CheatList::ProgramSegment master,
322 CheatList::ProgramSegment standard) const {
323 return {system, std::move(master), std::move(standard)};
324}
325
326TextCheatParser::~TextCheatParser() = default;
327
328CheatList TextCheatParser::Parse(const Core::System& system, const std::vector<u8>& data) const {
329 std::stringstream ss;
330 ss.write(reinterpret_cast<const char*>(data.data()), data.size());
331
332 std::vector<std::string> lines;
333 std::string stream_line;
334 while (std::getline(ss, stream_line)) {
335 // Remove a trailing \r
336 if (!stream_line.empty() && stream_line.back() == '\r')
337 stream_line.pop_back();
338 lines.push_back(std::move(stream_line));
339 }
340
341 CheatList::ProgramSegment master_list;
342 CheatList::ProgramSegment standard_list;
343
344 for (std::size_t i = 0; i < lines.size(); ++i) {
345 auto line = lines[i];
346
347 if (!line.empty() && (line[0] == '[' || line[0] == '{')) {
348 const auto master = line[0] == '{';
349 const auto begin = master ? line.find('{') : line.find('[');
350 const auto end = master ? line.rfind('}') : line.rfind(']');
351
352 ASSERT(begin != std::string::npos && end != std::string::npos);
353
354 const std::string patch_name{line.begin() + begin + 1, line.begin() + end};
355 CheatList::Block block{};
356
357 while (i < lines.size() - 1) {
358 line = lines[++i];
359 if (!line.empty() && (line[0] == '[' || line[0] == '{')) {
360 --i;
361 break;
362 }
363
364 if (line.size() < 8)
365 continue;
366
367 Cheat out{};
368 out.raw = ParseSingleLineCheat(line);
369 block.push_back(out);
370 }
371
372 (master ? master_list : standard_list).emplace_back(patch_name, block);
373 }
374 }
375
376 return MakeCheatList(system, master_list, standard_list);
377}
378
379std::array<u8, 16> TextCheatParser::ParseSingleLineCheat(const std::string& line) const {
380 std::array<u8, 16> out{};
381
382 if (line.size() < 8)
383 return out;
384
385 const auto word1 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data(), 8});
386 std::memcpy(out.data(), word1.data(), sizeof(u32));
387
388 if (line.size() < 17 || line[8] != ' ')
389 return out;
390
391 const auto word2 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data() + 9, 8});
392 std::memcpy(out.data() + sizeof(u32), word2.data(), sizeof(u32));
393
394 if (line.size() < 26 || line[17] != ' ') {
395 // Perform shifting in case value is truncated early.
396 const auto type = static_cast<CodeType>((out[0] & 0xF0) >> 4);
397 if (type == CodeType::Loop || type == CodeType::LoadImmediate ||
398 type == CodeType::StoreIndexed || type == CodeType::RegisterArithmetic) {
399 std::memcpy(out.data() + 8, out.data() + 4, sizeof(u32));
400 std::memset(out.data() + 4, 0, sizeof(u32));
401 }
402
403 return out;
404 }
405
406 const auto word3 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data() + 18, 8});
407 std::memcpy(out.data() + 2 * sizeof(u32), word3.data(), sizeof(u32));
408
409 if (line.size() < 35 || line[26] != ' ') {
410 // Perform shifting in case value is truncated early.
411 const auto type = static_cast<CodeType>((out[0] & 0xF0) >> 4);
412 if (type == CodeType::WriteImmediate || type == CodeType::Conditional) {
413 std::memcpy(out.data() + 12, out.data() + 8, sizeof(u32));
414 std::memset(out.data() + 8, 0, sizeof(u32));
415 }
416
417 return out;
418 }
419
420 const auto word4 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data() + 27, 8});
421 std::memcpy(out.data() + 3 * sizeof(u32), word4.data(), sizeof(u32));
422
423 return out;
424}
425
426u64 MemoryReadImpl(u32 width, VAddr addr) {
427 switch (width) {
428 case 1:
429 return Memory::Read8(addr);
430 case 2:
431 return Memory::Read16(addr);
432 case 4:
433 return Memory::Read32(addr);
434 case 8:
435 return Memory::Read64(addr);
436 default:
437 UNREACHABLE();
438 return 0;
439 }
440}
441
442void MemoryWriteImpl(u32 width, VAddr addr, u64 value) {
443 switch (width) {
444 case 1:
445 Memory::Write8(addr, static_cast<u8>(value));
446 break;
447 case 2:
448 Memory::Write16(addr, static_cast<u16>(value));
449 break;
450 case 4:
451 Memory::Write32(addr, static_cast<u32>(value));
452 break;
453 case 8:
454 Memory::Write64(addr, value);
455 break;
456 default:
457 UNREACHABLE();
458 }
459}
460
461CheatEngine::CheatEngine(Core::System& system, std::vector<CheatList> cheats_,
462 const std::string& build_id, VAddr code_region_start,
463 VAddr code_region_end)
464 : cheats{std::move(cheats_)}, core_timing{system.CoreTiming()} {
465 event = core_timing.RegisterEvent(
466 "CheatEngine::FrameCallback::" + build_id,
467 [this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); });
468 core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS, event);
469
470 const auto& vm_manager = system.CurrentProcess()->VMManager();
471 for (auto& list : this->cheats) {
472 list.SetMemoryParameters(code_region_start, vm_manager.GetHeapRegionBaseAddress(),
473 code_region_end, vm_manager.GetHeapRegionEndAddress(),
474 &MemoryWriteImpl, &MemoryReadImpl);
475 }
476}
477
478CheatEngine::~CheatEngine() {
479 core_timing.UnscheduleEvent(event, 0);
480}
481
482void CheatEngine::FrameCallback(u64 userdata, s64 cycles_late) {
483 for (auto& list : cheats) {
484 list.Execute();
485 }
486
487 core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS - cycles_late, event);
488}
489
490} // namespace FileSys
diff --git a/src/core/file_sys/cheat_engine.h b/src/core/file_sys/cheat_engine.h
new file mode 100644
index 000000000..ac22a82cb
--- /dev/null
+++ b/src/core/file_sys/cheat_engine.h
@@ -0,0 +1,234 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <map>
8#include <set>
9#include <vector>
10#include "common/bit_field.h"
11#include "common/common_types.h"
12
13namespace Core {
14class System;
15}
16
17namespace Core::Timing {
18class CoreTiming;
19struct EventType;
20} // namespace Core::Timing
21
22namespace FileSys {
23
24enum class CodeType : u32 {
25 // 0TMR00AA AAAAAAAA YYYYYYYY YYYYYYYY
26 // Writes a T sized value Y to the address A added to the value of register R in memory domain M
27 WriteImmediate = 0,
28
29 // 1TMC00AA AAAAAAAA YYYYYYYY YYYYYYYY
30 // Compares the T sized value Y to the value at address A in memory domain M using the
31 // conditional function C. If success, continues execution. If failure, jumps to the matching
32 // EndConditional statement.
33 Conditional = 1,
34
35 // 20000000
36 // Terminates a Conditional or ConditionalInput block.
37 EndConditional = 2,
38
39 // 300R0000 VVVVVVVV
40 // Starts looping V times, storing the current count in register R.
41 // Loop block is terminated with a matching 310R0000.
42 Loop = 3,
43
44 // 400R0000 VVVVVVVV VVVVVVVV
45 // Sets the value of register R to the value V.
46 LoadImmediate = 4,
47
48 // 5TMRI0AA AAAAAAAA
49 // Sets the value of register R to the value of width T at address A in memory domain M, with
50 // the current value of R added to the address if I == 1.
51 LoadIndexed = 5,
52
53 // 6T0RIFG0 VVVVVVVV VVVVVVVV
54 // Writes the value V of width T to the memory address stored in register R. Adds the value of
55 // register G to the final calculation if F is nonzero. Increments the value of register R by T
56 // after operation if I is nonzero.
57 StoreIndexed = 6,
58
59 // 7T0RA000 VVVVVVVV
60 // Performs the arithmetic operation A on the value in register R and the value V of width T,
61 // storing the result in register R.
62 RegisterArithmetic = 7,
63
64 // 8KKKKKKK
65 // Checks to see if any of the buttons defined by the bitmask K are pressed. If any are,
66 // execution continues. If none are, execution skips to the next EndConditional command.
67 ConditionalInput = 8,
68};
69
70enum class MemoryType : u32 {
71 // Addressed relative to start of main NSO
72 MainNSO = 0,
73
74 // Addressed relative to start of heap
75 Heap = 1,
76};
77
78enum class ArithmeticOp : u32 {
79 Add = 0,
80 Sub = 1,
81 Mult = 2,
82 LShift = 3,
83 RShift = 4,
84};
85
86enum class ComparisonOp : u32 {
87 GreaterThan = 1,
88 GreaterThanEqual = 2,
89 LessThan = 3,
90 LessThanEqual = 4,
91 Equal = 5,
92 Inequal = 6,
93};
94
95union Cheat {
96 std::array<u8, 16> raw;
97
98 BitField<4, 4, CodeType> type;
99 BitField<0, 4, u32> width; // Can be 1, 2, 4, or 8. Measured in bytes.
100 BitField<0, 4, u32> end_of_loop;
101 BitField<12, 4, MemoryType> memory_type;
102 BitField<8, 4, u32> register_3;
103 BitField<8, 4, ComparisonOp> comparison_op;
104 BitField<20, 4, u32> load_from_register;
105 BitField<20, 4, u32> increment_register;
106 BitField<20, 4, ArithmeticOp> arithmetic_op;
107 BitField<16, 4, u32> add_additional_register;
108 BitField<28, 4, u32> register_6;
109
110 u64 Address() const;
111 u64 ValueWidth(u64 offset) const;
112 u64 Value(u64 offset, u64 width) const;
113 u32 KeypadValue() const;
114};
115
116class CheatParser;
117
118// Represents a full collection of cheats for a game. The Execute function should be called every
119// interval that all cheats should be executed. Clients should not directly instantiate this class
120// (hence private constructor), they should instead receive an instance from CheatParser, which
121// guarantees the list is always in an acceptable state.
122class CheatList {
123public:
124 friend class CheatParser;
125
126 using Block = std::vector<Cheat>;
127 using ProgramSegment = std::vector<std::pair<std::string, Block>>;
128
129 // (width in bytes, address, value)
130 using MemoryWriter = void (*)(u32, VAddr, u64);
131 // (width in bytes, address) -> value
132 using MemoryReader = u64 (*)(u32, VAddr);
133
134 void SetMemoryParameters(VAddr main_begin, VAddr heap_begin, VAddr main_end, VAddr heap_end,
135 MemoryWriter writer, MemoryReader reader);
136
137 void Execute();
138
139private:
140 CheatList(const Core::System& system_, ProgramSegment master, ProgramSegment standard);
141
142 void ProcessBlockPairs(const Block& block);
143 void ExecuteSingleCheat(const Cheat& cheat);
144
145 void ExecuteBlock(const Block& block);
146
147 bool EvaluateConditional(const Cheat& cheat) const;
148
149 // Individual cheat operations
150 void WriteImmediate(const Cheat& cheat);
151 void BeginConditional(const Cheat& cheat);
152 void EndConditional(const Cheat& cheat);
153 void Loop(const Cheat& cheat);
154 void LoadImmediate(const Cheat& cheat);
155 void LoadIndexed(const Cheat& cheat);
156 void StoreIndexed(const Cheat& cheat);
157 void RegisterArithmetic(const Cheat& cheat);
158 void BeginConditionalInput(const Cheat& cheat);
159
160 VAddr SanitizeAddress(VAddr in) const;
161
162 // Master Codes are defined as codes that cannot be disabled and are run prior to all
163 // others.
164 ProgramSegment master_list;
165 // All other codes
166 ProgramSegment standard_list;
167
168 bool in_standard = false;
169
170 // 16 (0x0-0xF) scratch registers that can be used by cheats
171 std::array<u64, 16> scratch{};
172
173 MemoryWriter writer = nullptr;
174 MemoryReader reader = nullptr;
175
176 u64 main_region_begin{};
177 u64 heap_region_begin{};
178 u64 main_region_end{};
179 u64 heap_region_end{};
180
181 u64 current_block{};
182 // The current index of the cheat within the current Block
183 u64 current_index{};
184
185 // The 'stack' of the program. When a conditional or loop statement is encountered, its index is
186 // pushed onto this queue. When a end block is encountered, the condition is checked.
187 std::map<u64, u64> block_pairs;
188
189 std::set<u64> encountered_loops;
190
191 const Core::System* system;
192};
193
194// Intermediary class that parses a text file or other disk format for storing cheats into a
195// CheatList object, that can be used for execution.
196class CheatParser {
197public:
198 virtual ~CheatParser();
199
200 virtual CheatList Parse(const Core::System& system, const std::vector<u8>& data) const = 0;
201
202protected:
203 CheatList MakeCheatList(const Core::System& system_, CheatList::ProgramSegment master,
204 CheatList::ProgramSegment standard) const;
205};
206
207// CheatParser implementation that parses text files
208class TextCheatParser final : public CheatParser {
209public:
210 ~TextCheatParser() override;
211
212 CheatList Parse(const Core::System& system, const std::vector<u8>& data) const override;
213
214private:
215 std::array<u8, 16> ParseSingleLineCheat(const std::string& line) const;
216};
217
218// Class that encapsulates a CheatList and manages its interaction with memory and CoreTiming
219class CheatEngine final {
220public:
221 CheatEngine(Core::System& system_, std::vector<CheatList> cheats_, const std::string& build_id,
222 VAddr code_region_start, VAddr code_region_end);
223 ~CheatEngine();
224
225private:
226 void FrameCallback(u64 userdata, s64 cycles_late);
227
228 std::vector<CheatList> cheats;
229
230 Core::Timing::EventType* event;
231 Core::Timing::CoreTiming& core_timing;
232};
233
234} // namespace FileSys
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h
index 5d4d05c82..15b9e6624 100644
--- a/src/core/file_sys/content_archive.h
+++ b/src/core/file_sys/content_archive.h
@@ -24,13 +24,26 @@ namespace FileSys {
24 24
25union NCASectionHeader; 25union NCASectionHeader;
26 26
27/// Describes the type of content within an NCA archive.
27enum class NCAContentType : u8 { 28enum class NCAContentType : u8 {
29 /// Executable-related data
28 Program = 0, 30 Program = 0,
31
32 /// Metadata.
29 Meta = 1, 33 Meta = 1,
34
35 /// Access control data.
30 Control = 2, 36 Control = 2,
37
38 /// Information related to the game manual
39 /// e.g. Legal information, etc.
31 Manual = 3, 40 Manual = 3,
41
42 /// System data.
32 Data = 4, 43 Data = 4,
33 Data_Unknown5 = 5, ///< Seems to be used on some system archives 44
45 /// Data that can be accessed by applications.
46 PublicData = 5,
34}; 47};
35 48
36enum class NCASectionCryptoType : u8 { 49enum class NCASectionCryptoType : u8 {
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index 61706966e..e11217708 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -7,6 +7,7 @@
7#include <cstddef> 7#include <cstddef>
8#include <cstring> 8#include <cstring>
9 9
10#include "common/file_util.h"
10#include "common/hex_util.h" 11#include "common/hex_util.h"
11#include "common/logging/log.h" 12#include "common/logging/log.h"
12#include "core/file_sys/content_archive.h" 13#include "core/file_sys/content_archive.h"
@@ -19,6 +20,7 @@
19#include "core/file_sys/vfs_vector.h" 20#include "core/file_sys/vfs_vector.h"
20#include "core/hle/service/filesystem/filesystem.h" 21#include "core/hle/service/filesystem/filesystem.h"
21#include "core/loader/loader.h" 22#include "core/loader/loader.h"
23#include "core/loader/nso.h"
22#include "core/settings.h" 24#include "core/settings.h"
23 25
24namespace FileSys { 26namespace FileSys {
@@ -31,14 +33,6 @@ constexpr std::array<const char*, 14> EXEFS_FILE_NAMES{
31 "subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7", "subsdk8", "subsdk9", 33 "subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7", "subsdk8", "subsdk9",
32}; 34};
33 35
34struct NSOBuildHeader {
35 u32_le magic;
36 INSERT_PADDING_BYTES(0x3C);
37 std::array<u8, 0x20> build_id;
38 INSERT_PADDING_BYTES(0xA0);
39};
40static_assert(sizeof(NSOBuildHeader) == 0x100, "NSOBuildHeader has incorrect size.");
41
42std::string FormatTitleVersion(u32 version, TitleVersionFormat format) { 36std::string FormatTitleVersion(u32 version, TitleVersionFormat format) {
43 std::array<u8, sizeof(u32)> bytes{}; 37 std::array<u8, sizeof(u32)> bytes{};
44 bytes[0] = version % SINGLE_BYTE_MODULUS; 38 bytes[0] = version % SINGLE_BYTE_MODULUS;
@@ -162,14 +156,16 @@ std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualD
162} 156}
163 157
164std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const { 158std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const {
165 if (nso.size() < 0x100) 159 if (nso.size() < sizeof(Loader::NSOHeader)) {
166 return nso; 160 return nso;
161 }
167 162
168 NSOBuildHeader header; 163 Loader::NSOHeader header;
169 std::memcpy(&header, nso.data(), sizeof(NSOBuildHeader)); 164 std::memcpy(&header, nso.data(), sizeof(header));
170 165
171 if (header.magic != Common::MakeMagic('N', 'S', 'O', '0')) 166 if (header.magic != Common::MakeMagic('N', 'S', 'O', '0')) {
172 return nso; 167 return nso;
168 }
173 169
174 const auto build_id_raw = Common::HexArrayToString(header.build_id); 170 const auto build_id_raw = Common::HexArrayToString(header.build_id);
175 const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1); 171 const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1);
@@ -212,9 +208,11 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const {
212 } 208 }
213 } 209 }
214 210
215 if (out.size() < 0x100) 211 if (out.size() < sizeof(Loader::NSOHeader)) {
216 return nso; 212 return nso;
217 std::memcpy(out.data(), &header, sizeof(NSOBuildHeader)); 213 }
214
215 std::memcpy(out.data(), &header, sizeof(header));
218 return out; 216 return out;
219} 217}
220 218
@@ -232,6 +230,57 @@ bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const {
232 return !CollectPatches(patch_dirs, build_id).empty(); 230 return !CollectPatches(patch_dirs, build_id).empty();
233} 231}
234 232
233static std::optional<CheatList> ReadCheatFileFromFolder(const Core::System& system, u64 title_id,
234 const std::array<u8, 0x20>& build_id_,
235 const VirtualDir& base_path, bool upper) {
236 const auto build_id_raw = Common::HexArrayToString(build_id_, upper);
237 const auto build_id = build_id_raw.substr(0, sizeof(u64) * 2);
238 const auto file = base_path->GetFile(fmt::format("{}.txt", build_id));
239
240 if (file == nullptr) {
241 LOG_INFO(Common_Filesystem, "No cheats file found for title_id={:016X}, build_id={}",
242 title_id, build_id);
243 return std::nullopt;
244 }
245
246 std::vector<u8> data(file->GetSize());
247 if (file->Read(data.data(), data.size()) != data.size()) {
248 LOG_INFO(Common_Filesystem, "Failed to read cheats file for title_id={:016X}, build_id={}",
249 title_id, build_id);
250 return std::nullopt;
251 }
252
253 TextCheatParser parser;
254 return parser.Parse(system, data);
255}
256
257std::vector<CheatList> PatchManager::CreateCheatList(const Core::System& system,
258 const std::array<u8, 32>& build_id_) const {
259 const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
260 auto patch_dirs = load_dir->GetSubdirectories();
261 std::sort(patch_dirs.begin(), patch_dirs.end(),
262 [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
263
264 std::vector<CheatList> out;
265 out.reserve(patch_dirs.size());
266 for (const auto& subdir : patch_dirs) {
267 auto cheats_dir = subdir->GetSubdirectory("cheats");
268 if (cheats_dir != nullptr) {
269 auto res = ReadCheatFileFromFolder(system, title_id, build_id_, cheats_dir, true);
270 if (res.has_value()) {
271 out.push_back(std::move(*res));
272 continue;
273 }
274
275 res = ReadCheatFileFromFolder(system, title_id, build_id_, cheats_dir, false);
276 if (res.has_value())
277 out.push_back(std::move(*res));
278 }
279 }
280
281 return out;
282}
283
235static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) { 284static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) {
236 const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); 285 const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
237 if ((type != ContentRecordType::Program && type != ContentRecordType::Data) || 286 if ((type != ContentRecordType::Program && type != ContentRecordType::Data) ||
@@ -403,6 +452,8 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
403 } 452 }
404 if (IsDirValidAndNonEmpty(mod->GetSubdirectory("romfs"))) 453 if (IsDirValidAndNonEmpty(mod->GetSubdirectory("romfs")))
405 AppendCommaIfNotEmpty(types, "LayeredFS"); 454 AppendCommaIfNotEmpty(types, "LayeredFS");
455 if (IsDirValidAndNonEmpty(mod->GetSubdirectory("cheats")))
456 AppendCommaIfNotEmpty(types, "Cheats");
406 457
407 if (types.empty()) 458 if (types.empty())
408 continue; 459 continue;
diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h
index b8a1652fd..de2672c76 100644
--- a/src/core/file_sys/patch_manager.h
+++ b/src/core/file_sys/patch_manager.h
@@ -8,9 +8,14 @@
8#include <memory> 8#include <memory>
9#include <string> 9#include <string>
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "core/file_sys/cheat_engine.h"
11#include "core/file_sys/nca_metadata.h" 12#include "core/file_sys/nca_metadata.h"
12#include "core/file_sys/vfs.h" 13#include "core/file_sys/vfs.h"
13 14
15namespace Core {
16class System;
17}
18
14namespace FileSys { 19namespace FileSys {
15 20
16class NCA; 21class NCA;
@@ -45,6 +50,10 @@ public:
45 // Used to prevent expensive copies in NSO loader. 50 // Used to prevent expensive copies in NSO loader.
46 bool HasNSOPatch(const std::array<u8, 0x20>& build_id) const; 51 bool HasNSOPatch(const std::array<u8, 0x20>& build_id) const;
47 52
53 // Creates a CheatList object with all
54 std::vector<CheatList> CreateCheatList(const Core::System& system,
55 const std::array<u8, 0x20>& build_id) const;
56
48 // Currently tracked RomFS patches: 57 // Currently tracked RomFS patches:
49 // - Game Updates 58 // - Game Updates
50 // - LayeredFS 59 // - LayeredFS
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index 128199063..1c6bacace 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -94,7 +94,7 @@ static ContentRecordType GetCRTypeFromNCAType(NCAContentType type) {
94 case NCAContentType::Control: 94 case NCAContentType::Control:
95 return ContentRecordType::Control; 95 return ContentRecordType::Control;
96 case NCAContentType::Data: 96 case NCAContentType::Data:
97 case NCAContentType::Data_Unknown5: 97 case NCAContentType::PublicData:
98 return ContentRecordType::Data; 98 return ContentRecordType::Data;
99 case NCAContentType::Manual: 99 case NCAContentType::Manual:
100 // TODO(DarkLordZach): Peek at NCA contents to differentiate Manual and Legal. 100 // TODO(DarkLordZach): Peek at NCA contents to differentiate Manual and Legal.
diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h
index 455d1f346..fae54bcc7 100644
--- a/src/core/hle/ipc.h
+++ b/src/core/hle/ipc.h
@@ -39,10 +39,10 @@ struct CommandHeader {
39 union { 39 union {
40 u32_le raw_low; 40 u32_le raw_low;
41 BitField<0, 16, CommandType> type; 41 BitField<0, 16, CommandType> type;
42 BitField<16, 4, u32_le> num_buf_x_descriptors; 42 BitField<16, 4, u32> num_buf_x_descriptors;
43 BitField<20, 4, u32_le> num_buf_a_descriptors; 43 BitField<20, 4, u32> num_buf_a_descriptors;
44 BitField<24, 4, u32_le> num_buf_b_descriptors; 44 BitField<24, 4, u32> num_buf_b_descriptors;
45 BitField<28, 4, u32_le> num_buf_w_descriptors; 45 BitField<28, 4, u32> num_buf_w_descriptors;
46 }; 46 };
47 47
48 enum class BufferDescriptorCFlag : u32 { 48 enum class BufferDescriptorCFlag : u32 {
@@ -53,28 +53,28 @@ struct CommandHeader {
53 53
54 union { 54 union {
55 u32_le raw_high; 55 u32_le raw_high;
56 BitField<0, 10, u32_le> data_size; 56 BitField<0, 10, u32> data_size;
57 BitField<10, 4, BufferDescriptorCFlag> buf_c_descriptor_flags; 57 BitField<10, 4, BufferDescriptorCFlag> buf_c_descriptor_flags;
58 BitField<31, 1, u32_le> enable_handle_descriptor; 58 BitField<31, 1, u32> enable_handle_descriptor;
59 }; 59 };
60}; 60};
61static_assert(sizeof(CommandHeader) == 8, "CommandHeader size is incorrect"); 61static_assert(sizeof(CommandHeader) == 8, "CommandHeader size is incorrect");
62 62
63union HandleDescriptorHeader { 63union HandleDescriptorHeader {
64 u32_le raw_high; 64 u32_le raw_high;
65 BitField<0, 1, u32_le> send_current_pid; 65 BitField<0, 1, u32> send_current_pid;
66 BitField<1, 4, u32_le> num_handles_to_copy; 66 BitField<1, 4, u32> num_handles_to_copy;
67 BitField<5, 4, u32_le> num_handles_to_move; 67 BitField<5, 4, u32> num_handles_to_move;
68}; 68};
69static_assert(sizeof(HandleDescriptorHeader) == 4, "HandleDescriptorHeader size is incorrect"); 69static_assert(sizeof(HandleDescriptorHeader) == 4, "HandleDescriptorHeader size is incorrect");
70 70
71struct BufferDescriptorX { 71struct BufferDescriptorX {
72 union { 72 union {
73 BitField<0, 6, u32_le> counter_bits_0_5; 73 BitField<0, 6, u32> counter_bits_0_5;
74 BitField<6, 3, u32_le> address_bits_36_38; 74 BitField<6, 3, u32> address_bits_36_38;
75 BitField<9, 3, u32_le> counter_bits_9_11; 75 BitField<9, 3, u32> counter_bits_9_11;
76 BitField<12, 4, u32_le> address_bits_32_35; 76 BitField<12, 4, u32> address_bits_32_35;
77 BitField<16, 16, u32_le> size; 77 BitField<16, 16, u32> size;
78 }; 78 };
79 79
80 u32_le address_bits_0_31; 80 u32_le address_bits_0_31;
@@ -103,10 +103,10 @@ struct BufferDescriptorABW {
103 u32_le address_bits_0_31; 103 u32_le address_bits_0_31;
104 104
105 union { 105 union {
106 BitField<0, 2, u32_le> flags; 106 BitField<0, 2, u32> flags;
107 BitField<2, 3, u32_le> address_bits_36_38; 107 BitField<2, 3, u32> address_bits_36_38;
108 BitField<24, 4, u32_le> size_bits_32_35; 108 BitField<24, 4, u32> size_bits_32_35;
109 BitField<28, 4, u32_le> address_bits_32_35; 109 BitField<28, 4, u32> address_bits_32_35;
110 }; 110 };
111 111
112 VAddr Address() const { 112 VAddr Address() const {
@@ -128,8 +128,8 @@ struct BufferDescriptorC {
128 u32_le address_bits_0_31; 128 u32_le address_bits_0_31;
129 129
130 union { 130 union {
131 BitField<0, 16, u32_le> address_bits_32_47; 131 BitField<0, 16, u32> address_bits_32_47;
132 BitField<16, 16, u32_le> size; 132 BitField<16, 16, u32> size;
133 }; 133 };
134 134
135 VAddr Address() const { 135 VAddr Address() const {
@@ -167,8 +167,8 @@ struct DomainMessageHeader {
167 struct { 167 struct {
168 union { 168 union {
169 BitField<0, 8, CommandType> command; 169 BitField<0, 8, CommandType> command;
170 BitField<8, 8, u32_le> input_object_count; 170 BitField<8, 8, u32> input_object_count;
171 BitField<16, 16, u32_le> size; 171 BitField<16, 16, u32> size;
172 }; 172 };
173 u32_le object_id; 173 u32_le object_id;
174 INSERT_PADDING_WORDS(2); 174 INSERT_PADDING_WORDS(2);
diff --git a/src/core/hle/kernel/code_set.cpp b/src/core/hle/kernel/code_set.cpp
new file mode 100644
index 000000000..1f434e9af
--- /dev/null
+++ b/src/core/hle/kernel/code_set.cpp
@@ -0,0 +1,12 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/kernel/code_set.h"
6
7namespace Kernel {
8
9CodeSet::CodeSet() = default;
10CodeSet::~CodeSet() = default;
11
12} // namespace Kernel
diff --git a/src/core/hle/kernel/code_set.h b/src/core/hle/kernel/code_set.h
new file mode 100644
index 000000000..834fd23d2
--- /dev/null
+++ b/src/core/hle/kernel/code_set.h
@@ -0,0 +1,90 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <cstddef>
8#include <memory>
9#include <vector>
10
11#include "common/common_types.h"
12
13namespace Kernel {
14
15/**
16 * Represents executable data that may be loaded into a kernel process.
17 *
18 * A code set consists of three basic segments:
19 * - A code (AKA text) segment,
20 * - A read-only data segment (rodata)
21 * - A data segment
22 *
23 * The code segment is the portion of the object file that contains
24 * executable instructions.
25 *
26 * The read-only data segment in the portion of the object file that
27 * contains (as one would expect) read-only data, such as fixed constant
28 * values and data structures.
29 *
30 * The data segment is similar to the read-only data segment -- it contains
31 * variables and data structures that have predefined values, however,
32 * entities within this segment can be modified.
33 */
34struct CodeSet final {
35 /// A single segment within a code set.
36 struct Segment final {
37 /// The byte offset that this segment is located at.
38 std::size_t offset = 0;
39
40 /// The address to map this segment to.
41 VAddr addr = 0;
42
43 /// The size of this segment in bytes.
44 u32 size = 0;
45 };
46
47 explicit CodeSet();
48 ~CodeSet();
49
50 CodeSet(const CodeSet&) = delete;
51 CodeSet& operator=(const CodeSet&) = delete;
52
53 CodeSet(CodeSet&&) = default;
54 CodeSet& operator=(CodeSet&&) = default;
55
56 Segment& CodeSegment() {
57 return segments[0];
58 }
59
60 const Segment& CodeSegment() const {
61 return segments[0];
62 }
63
64 Segment& RODataSegment() {
65 return segments[1];
66 }
67
68 const Segment& RODataSegment() const {
69 return segments[1];
70 }
71
72 Segment& DataSegment() {
73 return segments[2];
74 }
75
76 const Segment& DataSegment() const {
77 return segments[2];
78 }
79
80 /// The overall data that backs this code set.
81 std::shared_ptr<std::vector<u8>> memory;
82
83 /// The segments that comprise this code set.
84 std::array<Segment, 3> segments;
85
86 /// The entry point address for this code set.
87 VAddr entrypoint = 0;
88};
89
90} // namespace Kernel
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp
index 0743670ad..98e87313b 100644
--- a/src/core/hle/kernel/mutex.cpp
+++ b/src/core/hle/kernel/mutex.cpp
@@ -2,7 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <map>
6#include <utility> 5#include <utility>
7#include <vector> 6#include <vector>
8 7
@@ -10,8 +9,11 @@
10#include "core/core.h" 9#include "core/core.h"
11#include "core/hle/kernel/errors.h" 10#include "core/hle/kernel/errors.h"
12#include "core/hle/kernel/handle_table.h" 11#include "core/hle/kernel/handle_table.h"
12#include "core/hle/kernel/kernel.h"
13#include "core/hle/kernel/mutex.h" 13#include "core/hle/kernel/mutex.h"
14#include "core/hle/kernel/object.h" 14#include "core/hle/kernel/object.h"
15#include "core/hle/kernel/process.h"
16#include "core/hle/kernel/scheduler.h"
15#include "core/hle/kernel/thread.h" 17#include "core/hle/kernel/thread.h"
16#include "core/hle/result.h" 18#include "core/hle/result.h"
17#include "core/memory.h" 19#include "core/memory.h"
@@ -57,41 +59,47 @@ static void TransferMutexOwnership(VAddr mutex_addr, SharedPtr<Thread> current_t
57 } 59 }
58} 60}
59 61
60ResultCode Mutex::TryAcquire(HandleTable& handle_table, VAddr address, Handle holding_thread_handle, 62Mutex::Mutex(Core::System& system) : system{system} {}
63Mutex::~Mutex() = default;
64
65ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle,
61 Handle requesting_thread_handle) { 66 Handle requesting_thread_handle) {
62 // The mutex address must be 4-byte aligned 67 // The mutex address must be 4-byte aligned
63 if ((address % sizeof(u32)) != 0) { 68 if ((address % sizeof(u32)) != 0) {
64 return ERR_INVALID_ADDRESS; 69 return ERR_INVALID_ADDRESS;
65 } 70 }
66 71
72 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
73 Thread* const current_thread = system.CurrentScheduler().GetCurrentThread();
67 SharedPtr<Thread> holding_thread = handle_table.Get<Thread>(holding_thread_handle); 74 SharedPtr<Thread> holding_thread = handle_table.Get<Thread>(holding_thread_handle);
68 SharedPtr<Thread> requesting_thread = handle_table.Get<Thread>(requesting_thread_handle); 75 SharedPtr<Thread> requesting_thread = handle_table.Get<Thread>(requesting_thread_handle);
69 76
70 // TODO(Subv): It is currently unknown if it is possible to lock a mutex in behalf of another 77 // TODO(Subv): It is currently unknown if it is possible to lock a mutex in behalf of another
71 // thread. 78 // thread.
72 ASSERT(requesting_thread == GetCurrentThread()); 79 ASSERT(requesting_thread == current_thread);
73 80
74 u32 addr_value = Memory::Read32(address); 81 const u32 addr_value = Memory::Read32(address);
75 82
76 // If the mutex isn't being held, just return success. 83 // If the mutex isn't being held, just return success.
77 if (addr_value != (holding_thread_handle | Mutex::MutexHasWaitersFlag)) { 84 if (addr_value != (holding_thread_handle | Mutex::MutexHasWaitersFlag)) {
78 return RESULT_SUCCESS; 85 return RESULT_SUCCESS;
79 } 86 }
80 87
81 if (holding_thread == nullptr) 88 if (holding_thread == nullptr) {
82 return ERR_INVALID_HANDLE; 89 return ERR_INVALID_HANDLE;
90 }
83 91
84 // Wait until the mutex is released 92 // Wait until the mutex is released
85 GetCurrentThread()->SetMutexWaitAddress(address); 93 current_thread->SetMutexWaitAddress(address);
86 GetCurrentThread()->SetWaitHandle(requesting_thread_handle); 94 current_thread->SetWaitHandle(requesting_thread_handle);
87 95
88 GetCurrentThread()->SetStatus(ThreadStatus::WaitMutex); 96 current_thread->SetStatus(ThreadStatus::WaitMutex);
89 GetCurrentThread()->InvalidateWakeupCallback(); 97 current_thread->InvalidateWakeupCallback();
90 98
91 // Update the lock holder thread's priority to prevent priority inversion. 99 // Update the lock holder thread's priority to prevent priority inversion.
92 holding_thread->AddMutexWaiter(GetCurrentThread()); 100 holding_thread->AddMutexWaiter(current_thread);
93 101
94 Core::System::GetInstance().PrepareReschedule(); 102 system.PrepareReschedule();
95 103
96 return RESULT_SUCCESS; 104 return RESULT_SUCCESS;
97} 105}
@@ -102,7 +110,8 @@ ResultCode Mutex::Release(VAddr address) {
102 return ERR_INVALID_ADDRESS; 110 return ERR_INVALID_ADDRESS;
103 } 111 }
104 112
105 auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(GetCurrentThread(), address); 113 auto* const current_thread = system.CurrentScheduler().GetCurrentThread();
114 auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(current_thread, address);
106 115
107 // There are no more threads waiting for the mutex, release it completely. 116 // There are no more threads waiting for the mutex, release it completely.
108 if (thread == nullptr) { 117 if (thread == nullptr) {
@@ -111,7 +120,7 @@ ResultCode Mutex::Release(VAddr address) {
111 } 120 }
112 121
113 // Transfer the ownership of the mutex from the previous owner to the new one. 122 // Transfer the ownership of the mutex from the previous owner to the new one.
114 TransferMutexOwnership(address, GetCurrentThread(), thread); 123 TransferMutexOwnership(address, current_thread, thread);
115 124
116 u32 mutex_value = thread->GetWaitHandle(); 125 u32 mutex_value = thread->GetWaitHandle();
117 126
diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h
index 81e62d497..b904de2e8 100644
--- a/src/core/hle/kernel/mutex.h
+++ b/src/core/hle/kernel/mutex.h
@@ -5,32 +5,34 @@
5#pragma once 5#pragma once
6 6
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "core/hle/kernel/object.h"
9 8
10union ResultCode; 9union ResultCode;
11 10
12namespace Kernel { 11namespace Core {
12class System;
13}
13 14
14class HandleTable; 15namespace Kernel {
15class Thread;
16 16
17class Mutex final { 17class Mutex final {
18public: 18public:
19 explicit Mutex(Core::System& system);
20 ~Mutex();
21
19 /// Flag that indicates that a mutex still has threads waiting for it. 22 /// Flag that indicates that a mutex still has threads waiting for it.
20 static constexpr u32 MutexHasWaitersFlag = 0x40000000; 23 static constexpr u32 MutexHasWaitersFlag = 0x40000000;
21 /// Mask of the bits in a mutex address value that contain the mutex owner. 24 /// Mask of the bits in a mutex address value that contain the mutex owner.
22 static constexpr u32 MutexOwnerMask = 0xBFFFFFFF; 25 static constexpr u32 MutexOwnerMask = 0xBFFFFFFF;
23 26
24 /// Attempts to acquire a mutex at the specified address. 27 /// Attempts to acquire a mutex at the specified address.
25 static ResultCode TryAcquire(HandleTable& handle_table, VAddr address, 28 ResultCode TryAcquire(VAddr address, Handle holding_thread_handle,
26 Handle holding_thread_handle, Handle requesting_thread_handle); 29 Handle requesting_thread_handle);
27 30
28 /// Releases the mutex at the specified address. 31 /// Releases the mutex at the specified address.
29 static ResultCode Release(VAddr address); 32 ResultCode Release(VAddr address);
30 33
31private: 34private:
32 Mutex() = default; 35 Core::System& system;
33 ~Mutex() = default;
34}; 36};
35 37
36} // namespace Kernel 38} // namespace Kernel
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index 49fced7b1..0d782e4ba 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -9,6 +9,7 @@
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "core/core.h" 10#include "core/core.h"
11#include "core/file_sys/program_metadata.h" 11#include "core/file_sys/program_metadata.h"
12#include "core/hle/kernel/code_set.h"
12#include "core/hle/kernel/errors.h" 13#include "core/hle/kernel/errors.h"
13#include "core/hle/kernel/kernel.h" 14#include "core/hle/kernel/kernel.h"
14#include "core/hle/kernel/process.h" 15#include "core/hle/kernel/process.h"
@@ -31,7 +32,7 @@ namespace {
31 */ 32 */
32void SetupMainThread(Process& owner_process, KernelCore& kernel, VAddr entry_point, u32 priority) { 33void SetupMainThread(Process& owner_process, KernelCore& kernel, VAddr entry_point, u32 priority) {
33 // Setup page table so we can write to memory 34 // Setup page table so we can write to memory
34 SetCurrentPageTable(&owner_process.VMManager().page_table); 35 Memory::SetCurrentPageTable(&owner_process.VMManager().page_table);
35 36
36 // Initialize new "main" thread 37 // Initialize new "main" thread
37 const VAddr stack_top = owner_process.VMManager().GetTLSIORegionEndAddress(); 38 const VAddr stack_top = owner_process.VMManager().GetTLSIORegionEndAddress();
@@ -50,9 +51,6 @@ void SetupMainThread(Process& owner_process, KernelCore& kernel, VAddr entry_poi
50} 51}
51} // Anonymous namespace 52} // Anonymous namespace
52 53
53CodeSet::CodeSet() = default;
54CodeSet::~CodeSet() = default;
55
56SharedPtr<Process> Process::Create(Core::System& system, std::string&& name) { 54SharedPtr<Process> Process::Create(Core::System& system, std::string&& name) {
57 auto& kernel = system.Kernel(); 55 auto& kernel = system.Kernel();
58 56
@@ -212,7 +210,7 @@ void Process::FreeTLSSlot(VAddr tls_address) {
212} 210}
213 211
214void Process::LoadModule(CodeSet module_, VAddr base_addr) { 212void Process::LoadModule(CodeSet module_, VAddr base_addr) {
215 const auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions, 213 const auto MapSegment = [&](const CodeSet::Segment& segment, VMAPermission permissions,
216 MemoryState memory_state) { 214 MemoryState memory_state) {
217 const auto vma = vm_manager 215 const auto vma = vm_manager
218 .MapMemoryBlock(segment.addr + base_addr, module_.memory, 216 .MapMemoryBlock(segment.addr + base_addr, module_.memory,
@@ -222,16 +220,17 @@ void Process::LoadModule(CodeSet module_, VAddr base_addr) {
222 }; 220 };
223 221
224 // Map CodeSet segments 222 // Map CodeSet segments
225 MapSegment(module_.CodeSegment(), VMAPermission::ReadExecute, MemoryState::CodeStatic); 223 MapSegment(module_.CodeSegment(), VMAPermission::ReadExecute, MemoryState::Code);
226 MapSegment(module_.RODataSegment(), VMAPermission::Read, MemoryState::CodeMutable); 224 MapSegment(module_.RODataSegment(), VMAPermission::Read, MemoryState::CodeData);
227 MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable); 225 MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeData);
228 226
229 // Clear instruction cache in CPU JIT 227 // Clear instruction cache in CPU JIT
230 system.InvalidateCpuInstructionCaches(); 228 system.InvalidateCpuInstructionCaches();
231} 229}
232 230
233Process::Process(Core::System& system) 231Process::Process(Core::System& system)
234 : WaitObject{system.Kernel()}, address_arbiter{system}, system{system} {} 232 : WaitObject{system.Kernel()}, address_arbiter{system}, mutex{system}, system{system} {}
233
235Process::~Process() = default; 234Process::~Process() = default;
236 235
237void Process::Acquire(Thread* thread) { 236void Process::Acquire(Thread* thread) {
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index 47ffd4ad3..1bd7bf5c1 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -7,13 +7,13 @@
7#include <array> 7#include <array>
8#include <bitset> 8#include <bitset>
9#include <cstddef> 9#include <cstddef>
10#include <memory>
11#include <string> 10#include <string>
12#include <vector> 11#include <vector>
13#include <boost/container/static_vector.hpp> 12#include <boost/container/static_vector.hpp>
14#include "common/common_types.h" 13#include "common/common_types.h"
15#include "core/hle/kernel/address_arbiter.h" 14#include "core/hle/kernel/address_arbiter.h"
16#include "core/hle/kernel/handle_table.h" 15#include "core/hle/kernel/handle_table.h"
16#include "core/hle/kernel/mutex.h"
17#include "core/hle/kernel/process_capability.h" 17#include "core/hle/kernel/process_capability.h"
18#include "core/hle/kernel/vm_manager.h" 18#include "core/hle/kernel/vm_manager.h"
19#include "core/hle/kernel/wait_object.h" 19#include "core/hle/kernel/wait_object.h"
@@ -33,6 +33,8 @@ class KernelCore;
33class ResourceLimit; 33class ResourceLimit;
34class Thread; 34class Thread;
35 35
36struct CodeSet;
37
36struct AddressMapping { 38struct AddressMapping {
37 // Address and size must be page-aligned 39 // Address and size must be page-aligned
38 VAddr address; 40 VAddr address;
@@ -65,46 +67,6 @@ enum class ProcessStatus {
65 DebugBreak, 67 DebugBreak,
66}; 68};
67 69
68struct CodeSet final {
69 struct Segment {
70 std::size_t offset = 0;
71 VAddr addr = 0;
72 u32 size = 0;
73 };
74
75 explicit CodeSet();
76 ~CodeSet();
77
78 Segment& CodeSegment() {
79 return segments[0];
80 }
81
82 const Segment& CodeSegment() const {
83 return segments[0];
84 }
85
86 Segment& RODataSegment() {
87 return segments[1];
88 }
89
90 const Segment& RODataSegment() const {
91 return segments[1];
92 }
93
94 Segment& DataSegment() {
95 return segments[2];
96 }
97
98 const Segment& DataSegment() const {
99 return segments[2];
100 }
101
102 std::shared_ptr<std::vector<u8>> memory;
103
104 std::array<Segment, 3> segments;
105 VAddr entrypoint = 0;
106};
107
108class Process final : public WaitObject { 70class Process final : public WaitObject {
109public: 71public:
110 enum : u64 { 72 enum : u64 {
@@ -165,6 +127,16 @@ public:
165 return address_arbiter; 127 return address_arbiter;
166 } 128 }
167 129
130 /// Gets a reference to the process' mutex lock.
131 Mutex& GetMutex() {
132 return mutex;
133 }
134
135 /// Gets a const reference to the process' mutex lock
136 const Mutex& GetMutex() const {
137 return mutex;
138 }
139
168 /// Gets the current status of the process 140 /// Gets the current status of the process
169 ProcessStatus GetStatus() const { 141 ProcessStatus GetStatus() const {
170 return status; 142 return status;
@@ -327,6 +299,11 @@ private:
327 /// Per-process address arbiter. 299 /// Per-process address arbiter.
328 AddressArbiter address_arbiter; 300 AddressArbiter address_arbiter;
329 301
302 /// The per-process mutex lock instance used for handling various
303 /// forms of services, such as lock arbitration, and condition
304 /// variable related facilities.
305 Mutex mutex;
306
330 /// Random values for svcGetInfo RandomEntropy 307 /// Random values for svcGetInfo RandomEntropy
331 std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy; 308 std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy;
332 309
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp
index e524509df..cc189cc64 100644
--- a/src/core/hle/kernel/scheduler.cpp
+++ b/src/core/hle/kernel/scheduler.cpp
@@ -96,7 +96,7 @@ void Scheduler::SwitchContext(Thread* new_thread) {
96 auto* const thread_owner_process = current_thread->GetOwnerProcess(); 96 auto* const thread_owner_process = current_thread->GetOwnerProcess();
97 if (previous_process != thread_owner_process) { 97 if (previous_process != thread_owner_process) {
98 system.Kernel().MakeCurrentProcess(thread_owner_process); 98 system.Kernel().MakeCurrentProcess(thread_owner_process);
99 SetCurrentPageTable(&thread_owner_process->VMManager().page_table); 99 Memory::SetCurrentPageTable(&thread_owner_process->VMManager().page_table);
100 } 100 }
101 101
102 cpu_core.LoadContext(new_thread->GetContext()); 102 cpu_core.LoadContext(new_thread->GetContext());
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 047fa0c19..a6a17efe7 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -551,9 +551,9 @@ static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr,
551 return ERR_INVALID_ADDRESS; 551 return ERR_INVALID_ADDRESS;
552 } 552 }
553 553
554 auto& handle_table = Core::CurrentProcess()->GetHandleTable(); 554 auto* const current_process = Core::System::GetInstance().Kernel().CurrentProcess();
555 return Mutex::TryAcquire(handle_table, mutex_addr, holding_thread_handle, 555 return current_process->GetMutex().TryAcquire(mutex_addr, holding_thread_handle,
556 requesting_thread_handle); 556 requesting_thread_handle);
557} 557}
558 558
559/// Unlock a mutex 559/// Unlock a mutex
@@ -571,7 +571,8 @@ static ResultCode ArbitrateUnlock(VAddr mutex_addr) {
571 return ERR_INVALID_ADDRESS; 571 return ERR_INVALID_ADDRESS;
572 } 572 }
573 573
574 return Mutex::Release(mutex_addr); 574 auto* const current_process = Core::System::GetInstance().Kernel().CurrentProcess();
575 return current_process->GetMutex().Release(mutex_addr);
575} 576}
576 577
577enum class BreakType : u32 { 578enum class BreakType : u32 {
@@ -1340,11 +1341,15 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var
1340 "called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}", 1341 "called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}",
1341 mutex_addr, condition_variable_addr, thread_handle, nano_seconds); 1342 mutex_addr, condition_variable_addr, thread_handle, nano_seconds);
1342 1343
1343 const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); 1344 auto* const current_process = Core::System::GetInstance().Kernel().CurrentProcess();
1345 const auto& handle_table = current_process->GetHandleTable();
1344 SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); 1346 SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
1345 ASSERT(thread); 1347 ASSERT(thread);
1346 1348
1347 CASCADE_CODE(Mutex::Release(mutex_addr)); 1349 const auto release_result = current_process->GetMutex().Release(mutex_addr);
1350 if (release_result.IsError()) {
1351 return release_result;
1352 }
1348 1353
1349 SharedPtr<Thread> current_thread = GetCurrentThread(); 1354 SharedPtr<Thread> current_thread = GetCurrentThread();
1350 current_thread->SetCondVarWaitAddress(condition_variable_addr); 1355 current_thread->SetCondVarWaitAddress(condition_variable_addr);
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 89f180bd9..3b22e8e0d 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -7,8 +7,6 @@
7#include <optional> 7#include <optional>
8#include <vector> 8#include <vector>
9 9
10#include <boost/range/algorithm_ext/erase.hpp>
11
12#include "common/assert.h" 10#include "common/assert.h"
13#include "common/common_types.h" 11#include "common/common_types.h"
14#include "common/logging/log.h" 12#include "common/logging/log.h"
@@ -258,8 +256,8 @@ void Thread::AddMutexWaiter(SharedPtr<Thread> thread) {
258 if (thread->lock_owner == this) { 256 if (thread->lock_owner == this) {
259 // If the thread is already waiting for this thread to release the mutex, ensure that the 257 // If the thread is already waiting for this thread to release the mutex, ensure that the
260 // waiters list is consistent and return without doing anything. 258 // waiters list is consistent and return without doing anything.
261 auto itr = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread); 259 const auto iter = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
262 ASSERT(itr != wait_mutex_threads.end()); 260 ASSERT(iter != wait_mutex_threads.end());
263 return; 261 return;
264 } 262 }
265 263
@@ -267,11 +265,16 @@ void Thread::AddMutexWaiter(SharedPtr<Thread> thread) {
267 ASSERT(thread->lock_owner == nullptr); 265 ASSERT(thread->lock_owner == nullptr);
268 266
269 // Ensure that the thread is not already in the list of mutex waiters 267 // Ensure that the thread is not already in the list of mutex waiters
270 auto itr = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread); 268 const auto iter = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
271 ASSERT(itr == wait_mutex_threads.end()); 269 ASSERT(iter == wait_mutex_threads.end());
272 270
271 // Keep the list in an ordered fashion
272 const auto insertion_point = std::find_if(
273 wait_mutex_threads.begin(), wait_mutex_threads.end(),
274 [&thread](const auto& entry) { return entry->GetPriority() > thread->GetPriority(); });
275 wait_mutex_threads.insert(insertion_point, thread);
273 thread->lock_owner = this; 276 thread->lock_owner = this;
274 wait_mutex_threads.emplace_back(std::move(thread)); 277
275 UpdatePriority(); 278 UpdatePriority();
276} 279}
277 280
@@ -279,32 +282,44 @@ void Thread::RemoveMutexWaiter(SharedPtr<Thread> thread) {
279 ASSERT(thread->lock_owner == this); 282 ASSERT(thread->lock_owner == this);
280 283
281 // Ensure that the thread is in the list of mutex waiters 284 // Ensure that the thread is in the list of mutex waiters
282 auto itr = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread); 285 const auto iter = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
283 ASSERT(itr != wait_mutex_threads.end()); 286 ASSERT(iter != wait_mutex_threads.end());
287
288 wait_mutex_threads.erase(iter);
284 289
285 boost::remove_erase(wait_mutex_threads, thread);
286 thread->lock_owner = nullptr; 290 thread->lock_owner = nullptr;
287 UpdatePriority(); 291 UpdatePriority();
288} 292}
289 293
290void Thread::UpdatePriority() { 294void Thread::UpdatePriority() {
291 // Find the highest priority among all the threads that are waiting for this thread's lock 295 // If any of the threads waiting on the mutex have a higher priority
296 // (taking into account priority inheritance), then this thread inherits
297 // that thread's priority.
292 u32 new_priority = nominal_priority; 298 u32 new_priority = nominal_priority;
293 for (const auto& thread : wait_mutex_threads) { 299 if (!wait_mutex_threads.empty()) {
294 if (thread->nominal_priority < new_priority) 300 if (wait_mutex_threads.front()->current_priority < new_priority) {
295 new_priority = thread->nominal_priority; 301 new_priority = wait_mutex_threads.front()->current_priority;
302 }
296 } 303 }
297 304
298 if (new_priority == current_priority) 305 if (new_priority == current_priority) {
299 return; 306 return;
307 }
300 308
301 scheduler->SetThreadPriority(this, new_priority); 309 scheduler->SetThreadPriority(this, new_priority);
302
303 current_priority = new_priority; 310 current_priority = new_priority;
304 311
312 if (!lock_owner) {
313 return;
314 }
315
316 // Ensure that the thread is within the correct location in the waiting list.
317 auto old_owner = lock_owner;
318 lock_owner->RemoveMutexWaiter(this);
319 old_owner->AddMutexWaiter(this);
320
305 // Recursively update the priority of the thread that depends on the priority of this one. 321 // Recursively update the priority of the thread that depends on the priority of this one.
306 if (lock_owner) 322 lock_owner->UpdatePriority();
307 lock_owner->UpdatePriority();
308} 323}
309 324
310void Thread::ChangeCore(u32 core, u64 mask) { 325void Thread::ChangeCore(u32 core, u64 mask) {
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index ccdefeecc..faad5f391 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -401,8 +401,14 @@ private:
401 VAddr entry_point = 0; 401 VAddr entry_point = 0;
402 VAddr stack_top = 0; 402 VAddr stack_top = 0;
403 403
404 u32 nominal_priority = 0; ///< Nominal thread priority, as set by the emulated application 404 /// Nominal thread priority, as set by the emulated application.
405 u32 current_priority = 0; ///< Current thread priority, can be temporarily changed 405 /// The nominal priority is the thread priority without priority
406 /// inheritance taken into account.
407 u32 nominal_priority = 0;
408
409 /// Current thread priority. This may change over the course of the
410 /// thread's lifetime in order to facilitate priority inheritance.
411 u32 current_priority = 0;
406 412
407 u64 total_cpu_time_ticks = 0; ///< Total CPU running ticks. 413 u64 total_cpu_time_ticks = 0; ///< Total CPU running ticks.
408 u64 last_running_ticks = 0; ///< CPU tick when thread was last running 414 u64 last_running_ticks = 0; ///< CPU tick when thread was last running
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index 05c59af34..22bf55ce7 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -7,29 +7,29 @@
7#include <utility> 7#include <utility>
8#include "common/assert.h" 8#include "common/assert.h"
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "common/memory_hook.h"
10#include "core/arm/arm_interface.h" 11#include "core/arm/arm_interface.h"
11#include "core/core.h" 12#include "core/core.h"
12#include "core/file_sys/program_metadata.h" 13#include "core/file_sys/program_metadata.h"
13#include "core/hle/kernel/errors.h" 14#include "core/hle/kernel/errors.h"
14#include "core/hle/kernel/vm_manager.h" 15#include "core/hle/kernel/vm_manager.h"
15#include "core/memory.h" 16#include "core/memory.h"
16#include "core/memory_hook.h"
17#include "core/memory_setup.h" 17#include "core/memory_setup.h"
18 18
19namespace Kernel { 19namespace Kernel {
20namespace { 20namespace {
21const char* GetMemoryStateName(MemoryState state) { 21const char* GetMemoryStateName(MemoryState state) {
22 static constexpr const char* names[] = { 22 static constexpr const char* names[] = {
23 "Unmapped", "Io", 23 "Unmapped", "Io",
24 "Normal", "CodeStatic", 24 "Normal", "Code",
25 "CodeMutable", "Heap", 25 "CodeData", "Heap",
26 "Shared", "Unknown1", 26 "Shared", "Unknown1",
27 "ModuleCodeStatic", "ModuleCodeMutable", 27 "ModuleCode", "ModuleCodeData",
28 "IpcBuffer0", "Stack", 28 "IpcBuffer0", "Stack",
29 "ThreadLocal", "TransferMemoryIsolated", 29 "ThreadLocal", "TransferMemoryIsolated",
30 "TransferMemory", "ProcessMemory", 30 "TransferMemory", "ProcessMemory",
31 "Inaccessible", "IpcBuffer1", 31 "Inaccessible", "IpcBuffer1",
32 "IpcBuffer3", "KernelStack", 32 "IpcBuffer3", "KernelStack",
33 }; 33 };
34 34
35 return names[ToSvcMemoryState(state)]; 35 return names[ToSvcMemoryState(state)];
@@ -177,7 +177,7 @@ ResultVal<VAddr> VMManager::FindFreeRegion(u64 size) const {
177 177
178ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u64 size, 178ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u64 size,
179 MemoryState state, 179 MemoryState state,
180 Memory::MemoryHookPointer mmio_handler) { 180 Common::MemoryHookPointer mmio_handler) {
181 // This is the appropriately sized VMA that will turn into our allocation. 181 // This is the appropriately sized VMA that will turn into our allocation.
182 CASCADE_RESULT(VMAIter vma_handle, CarveVMA(target, size)); 182 CASCADE_RESULT(VMAIter vma_handle, CarveVMA(target, size));
183 VirtualMemoryArea& final_vma = vma_handle->second; 183 VirtualMemoryArea& final_vma = vma_handle->second;
@@ -624,7 +624,7 @@ void VMManager::ClearPageTable() {
624 std::fill(page_table.pointers.begin(), page_table.pointers.end(), nullptr); 624 std::fill(page_table.pointers.begin(), page_table.pointers.end(), nullptr);
625 page_table.special_regions.clear(); 625 page_table.special_regions.clear();
626 std::fill(page_table.attributes.begin(), page_table.attributes.end(), 626 std::fill(page_table.attributes.begin(), page_table.attributes.end(),
627 Memory::PageType::Unmapped); 627 Common::PageType::Unmapped);
628} 628}
629 629
630VMManager::CheckResults VMManager::CheckRangeState(VAddr address, u64 size, MemoryState state_mask, 630VMManager::CheckResults VMManager::CheckRangeState(VAddr address, u64 size, MemoryState state_mask,
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
index 88e0b3c02..7cdff6094 100644
--- a/src/core/hle/kernel/vm_manager.h
+++ b/src/core/hle/kernel/vm_manager.h
@@ -9,9 +9,10 @@
9#include <tuple> 9#include <tuple>
10#include <vector> 10#include <vector>
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/memory_hook.h"
13#include "common/page_table.h"
12#include "core/hle/result.h" 14#include "core/hle/result.h"
13#include "core/memory.h" 15#include "core/memory.h"
14#include "core/memory_hook.h"
15 16
16namespace FileSys { 17namespace FileSys {
17enum class ProgramAddressSpaceType : u8; 18enum class ProgramAddressSpaceType : u8;
@@ -164,12 +165,12 @@ enum class MemoryState : u32 {
164 Unmapped = 0x00, 165 Unmapped = 0x00,
165 Io = 0x01 | FlagMapped, 166 Io = 0x01 | FlagMapped,
166 Normal = 0x02 | FlagMapped | FlagQueryPhysicalAddressAllowed, 167 Normal = 0x02 | FlagMapped | FlagQueryPhysicalAddressAllowed,
167 CodeStatic = 0x03 | CodeFlags | FlagMapProcess, 168 Code = 0x03 | CodeFlags | FlagMapProcess,
168 CodeMutable = 0x04 | CodeFlags | FlagMapProcess | FlagCodeMemory, 169 CodeData = 0x04 | DataFlags | FlagMapProcess | FlagCodeMemory,
169 Heap = 0x05 | DataFlags | FlagCodeMemory, 170 Heap = 0x05 | DataFlags | FlagCodeMemory,
170 Shared = 0x06 | FlagMapped | FlagMemoryPoolAllocated, 171 Shared = 0x06 | FlagMapped | FlagMemoryPoolAllocated,
171 ModuleCodeStatic = 0x08 | CodeFlags | FlagModule | FlagMapProcess, 172 ModuleCode = 0x08 | CodeFlags | FlagModule | FlagMapProcess,
172 ModuleCodeMutable = 0x09 | DataFlags | FlagModule | FlagMapProcess | FlagCodeMemory, 173 ModuleCodeData = 0x09 | DataFlags | FlagModule | FlagMapProcess | FlagCodeMemory,
173 174
174 IpcBuffer0 = 0x0A | FlagMapped | FlagQueryPhysicalAddressAllowed | FlagMemoryPoolAllocated | 175 IpcBuffer0 = 0x0A | FlagMapped | FlagQueryPhysicalAddressAllowed | FlagMemoryPoolAllocated |
175 IPCFlags | FlagSharedDevice | FlagSharedDeviceAligned, 176 IPCFlags | FlagSharedDevice | FlagSharedDeviceAligned,
@@ -290,7 +291,7 @@ struct VirtualMemoryArea {
290 // Settings for type = MMIO 291 // Settings for type = MMIO
291 /// Physical address of the register area this VMA maps to. 292 /// Physical address of the register area this VMA maps to.
292 PAddr paddr = 0; 293 PAddr paddr = 0;
293 Memory::MemoryHookPointer mmio_handler = nullptr; 294 Common::MemoryHookPointer mmio_handler = nullptr;
294 295
295 /// Tests if this area can be merged to the right with `next`. 296 /// Tests if this area can be merged to the right with `next`.
296 bool CanBeMergedWith(const VirtualMemoryArea& next) const; 297 bool CanBeMergedWith(const VirtualMemoryArea& next) const;
@@ -368,7 +369,7 @@ public:
368 * @param mmio_handler The handler that will implement read and write for this MMIO region. 369 * @param mmio_handler The handler that will implement read and write for this MMIO region.
369 */ 370 */
370 ResultVal<VMAHandle> MapMMIO(VAddr target, PAddr paddr, u64 size, MemoryState state, 371 ResultVal<VMAHandle> MapMMIO(VAddr target, PAddr paddr, u64 size, MemoryState state,
371 Memory::MemoryHookPointer mmio_handler); 372 Common::MemoryHookPointer mmio_handler);
372 373
373 /// Unmaps a range of addresses, splitting VMAs as necessary. 374 /// Unmaps a range of addresses, splitting VMAs as necessary.
374 ResultCode UnmapRange(VAddr target, u64 size); 375 ResultCode UnmapRange(VAddr target, u64 size);
@@ -509,7 +510,7 @@ public:
509 510
510 /// Each VMManager has its own page table, which is set as the main one when the owning process 511 /// Each VMManager has its own page table, which is set as the main one when the owning process
511 /// is scheduled. 512 /// is scheduled.
512 Memory::PageTable page_table; 513 Common::PageTable page_table{Memory::PAGE_BITS};
513 514
514private: 515private:
515 using VMAIter = VMAMap::iterator; 516 using VMAIter = VMAMap::iterator;
@@ -616,6 +617,9 @@ private:
616 VAddr new_map_region_base = 0; 617 VAddr new_map_region_base = 0;
617 VAddr new_map_region_end = 0; 618 VAddr new_map_region_end = 0;
618 619
620 VAddr main_code_region_base = 0;
621 VAddr main_code_region_end = 0;
622
619 VAddr tls_io_region_base = 0; 623 VAddr tls_io_region_base = 0;
620 VAddr tls_io_region_end = 0; 624 VAddr tls_io_region_end = 0;
621 625
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 3f009d2b7..9c44e27c6 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -2,10 +2,10 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm>
5#include <array> 6#include <array>
6#include <cinttypes> 7#include <cinttypes>
7#include <cstring> 8#include <cstring>
8#include <stack>
9#include "audio_core/audio_renderer.h" 9#include "audio_core/audio_renderer.h"
10#include "core/core.h" 10#include "core/core.h"
11#include "core/file_sys/savedata_factory.h" 11#include "core/file_sys/savedata_factory.h"
@@ -93,38 +93,84 @@ void IWindowController::AcquireForegroundRights(Kernel::HLERequestContext& ctx)
93} 93}
94 94
95IAudioController::IAudioController() : ServiceFramework("IAudioController") { 95IAudioController::IAudioController() : ServiceFramework("IAudioController") {
96 // clang-format off
96 static const FunctionInfo functions[] = { 97 static const FunctionInfo functions[] = {
97 {0, &IAudioController::SetExpectedMasterVolume, "SetExpectedMasterVolume"}, 98 {0, &IAudioController::SetExpectedMasterVolume, "SetExpectedMasterVolume"},
98 {1, &IAudioController::GetMainAppletExpectedMasterVolume, 99 {1, &IAudioController::GetMainAppletExpectedMasterVolume, "GetMainAppletExpectedMasterVolume"},
99 "GetMainAppletExpectedMasterVolume"}, 100 {2, &IAudioController::GetLibraryAppletExpectedMasterVolume, "GetLibraryAppletExpectedMasterVolume"},
100 {2, &IAudioController::GetLibraryAppletExpectedMasterVolume, 101 {3, &IAudioController::ChangeMainAppletMasterVolume, "ChangeMainAppletMasterVolume"},
101 "GetLibraryAppletExpectedMasterVolume"}, 102 {4, &IAudioController::SetTransparentAudioRate, "SetTransparentVolumeRate"},
102 {3, nullptr, "ChangeMainAppletMasterVolume"},
103 {4, nullptr, "SetTransparentVolumeRate"},
104 }; 103 };
104 // clang-format on
105
105 RegisterHandlers(functions); 106 RegisterHandlers(functions);
106} 107}
107 108
108IAudioController::~IAudioController() = default; 109IAudioController::~IAudioController() = default;
109 110
110void IAudioController::SetExpectedMasterVolume(Kernel::HLERequestContext& ctx) { 111void IAudioController::SetExpectedMasterVolume(Kernel::HLERequestContext& ctx) {
111 LOG_WARNING(Service_AM, "(STUBBED) called"); 112 IPC::RequestParser rp{ctx};
113 const float main_applet_volume_tmp = rp.Pop<float>();
114 const float library_applet_volume_tmp = rp.Pop<float>();
115
116 LOG_DEBUG(Service_AM, "called. main_applet_volume={}, library_applet_volume={}",
117 main_applet_volume_tmp, library_applet_volume_tmp);
118
119 // Ensure the volume values remain within the 0-100% range
120 main_applet_volume = std::clamp(main_applet_volume_tmp, min_allowed_volume, max_allowed_volume);
121 library_applet_volume =
122 std::clamp(library_applet_volume_tmp, min_allowed_volume, max_allowed_volume);
123
112 IPC::ResponseBuilder rb{ctx, 2}; 124 IPC::ResponseBuilder rb{ctx, 2};
113 rb.Push(RESULT_SUCCESS); 125 rb.Push(RESULT_SUCCESS);
114} 126}
115 127
116void IAudioController::GetMainAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx) { 128void IAudioController::GetMainAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx) {
117 LOG_WARNING(Service_AM, "(STUBBED) called"); 129 LOG_DEBUG(Service_AM, "called. main_applet_volume={}", main_applet_volume);
118 IPC::ResponseBuilder rb{ctx, 3}; 130 IPC::ResponseBuilder rb{ctx, 3};
119 rb.Push(RESULT_SUCCESS); 131 rb.Push(RESULT_SUCCESS);
120 rb.Push(volume); 132 rb.Push(main_applet_volume);
121} 133}
122 134
123void IAudioController::GetLibraryAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx) { 135void IAudioController::GetLibraryAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx) {
124 LOG_WARNING(Service_AM, "(STUBBED) called"); 136 LOG_DEBUG(Service_AM, "called. library_applet_volume={}", library_applet_volume);
125 IPC::ResponseBuilder rb{ctx, 3}; 137 IPC::ResponseBuilder rb{ctx, 3};
126 rb.Push(RESULT_SUCCESS); 138 rb.Push(RESULT_SUCCESS);
127 rb.Push(volume); 139 rb.Push(library_applet_volume);
140}
141
142void IAudioController::ChangeMainAppletMasterVolume(Kernel::HLERequestContext& ctx) {
143 struct Parameters {
144 float volume;
145 s64 fade_time_ns;
146 };
147 static_assert(sizeof(Parameters) == 16);
148
149 IPC::RequestParser rp{ctx};
150 const auto parameters = rp.PopRaw<Parameters>();
151
152 LOG_DEBUG(Service_AM, "called. volume={}, fade_time_ns={}", parameters.volume,
153 parameters.fade_time_ns);
154
155 main_applet_volume = std::clamp(parameters.volume, min_allowed_volume, max_allowed_volume);
156 fade_time_ns = std::chrono::nanoseconds{parameters.fade_time_ns};
157
158 IPC::ResponseBuilder rb{ctx, 2};
159 rb.Push(RESULT_SUCCESS);
160}
161
162void IAudioController::SetTransparentAudioRate(Kernel::HLERequestContext& ctx) {
163 IPC::RequestParser rp{ctx};
164 const float transparent_volume_rate_tmp = rp.Pop<float>();
165
166 LOG_DEBUG(Service_AM, "called. transparent_volume_rate={}", transparent_volume_rate_tmp);
167
168 // Clamp volume range to 0-100%.
169 transparent_volume_rate =
170 std::clamp(transparent_volume_rate_tmp, min_allowed_volume, max_allowed_volume);
171
172 IPC::ResponseBuilder rb{ctx, 2};
173 rb.Push(RESULT_SUCCESS);
128} 174}
129 175
130IDisplayController::IDisplayController() : ServiceFramework("IDisplayController") { 176IDisplayController::IDisplayController() : ServiceFramework("IDisplayController") {
@@ -169,7 +215,21 @@ IDisplayController::IDisplayController() : ServiceFramework("IDisplayController"
169 215
170IDisplayController::~IDisplayController() = default; 216IDisplayController::~IDisplayController() = default;
171 217
172IDebugFunctions::IDebugFunctions() : ServiceFramework("IDebugFunctions") {} 218IDebugFunctions::IDebugFunctions() : ServiceFramework{"IDebugFunctions"} {
219 // clang-format off
220 static const FunctionInfo functions[] = {
221 {0, nullptr, "NotifyMessageToHomeMenuForDebug"},
222 {1, nullptr, "OpenMainApplication"},
223 {10, nullptr, "EmulateButtonEvent"},
224 {20, nullptr, "InvalidateTransitionLayer"},
225 {30, nullptr, "RequestLaunchApplicationWithUserAndArgumentForDebug"},
226 {40, nullptr, "GetAppletResourceUsageInfo"},
227 };
228 // clang-format on
229
230 RegisterHandlers(functions);
231}
232
173IDebugFunctions::~IDebugFunctions() = default; 233IDebugFunctions::~IDebugFunctions() = default;
174 234
175ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) 235ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index b6113cfdd..565dd8e9e 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <chrono>
7#include <memory> 8#include <memory>
8#include <queue> 9#include <queue>
9#include "core/hle/kernel/writable_event.h" 10#include "core/hle/kernel/writable_event.h"
@@ -81,8 +82,21 @@ private:
81 void SetExpectedMasterVolume(Kernel::HLERequestContext& ctx); 82 void SetExpectedMasterVolume(Kernel::HLERequestContext& ctx);
82 void GetMainAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx); 83 void GetMainAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx);
83 void GetLibraryAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx); 84 void GetLibraryAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx);
85 void ChangeMainAppletMasterVolume(Kernel::HLERequestContext& ctx);
86 void SetTransparentAudioRate(Kernel::HLERequestContext& ctx);
84 87
85 u32 volume{100}; 88 static constexpr float min_allowed_volume = 0.0f;
89 static constexpr float max_allowed_volume = 1.0f;
90
91 float main_applet_volume{0.25f};
92 float library_applet_volume{max_allowed_volume};
93 float transparent_volume_rate{min_allowed_volume};
94
95 // Volume transition fade time in nanoseconds.
96 // e.g. If the main applet volume was 0% and was changed to 50%
97 // with a fade of 50ns, then over the course of 50ns,
98 // the volume will gradually fade up to 50%
99 std::chrono::nanoseconds fade_time_ns{0};
86}; 100};
87 101
88class IDisplayController final : public ServiceFramework<IDisplayController> { 102class IDisplayController final : public ServiceFramework<IDisplayController> {
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp
index 377e12cfa..cb4a1160d 100644
--- a/src/core/hle/service/audio/hwopus.cpp
+++ b/src/core/hle/service/audio/hwopus.cpp
@@ -8,6 +8,7 @@
8#include <vector> 8#include <vector>
9 9
10#include <opus.h> 10#include <opus.h>
11#include <opus_multistream.h>
11 12
12#include "common/assert.h" 13#include "common/assert.h"
13#include "common/logging/log.h" 14#include "common/logging/log.h"
@@ -18,12 +19,12 @@
18namespace Service::Audio { 19namespace Service::Audio {
19namespace { 20namespace {
20struct OpusDeleter { 21struct OpusDeleter {
21 void operator()(void* ptr) const { 22 void operator()(OpusMSDecoder* ptr) const {
22 operator delete(ptr); 23 opus_multistream_decoder_destroy(ptr);
23 } 24 }
24}; 25};
25 26
26using OpusDecoderPtr = std::unique_ptr<OpusDecoder, OpusDeleter>; 27using OpusDecoderPtr = std::unique_ptr<OpusMSDecoder, OpusDeleter>;
27 28
28struct OpusPacketHeader { 29struct OpusPacketHeader {
29 // Packet size in bytes. 30 // Packet size in bytes.
@@ -33,7 +34,7 @@ struct OpusPacketHeader {
33}; 34};
34static_assert(sizeof(OpusPacketHeader) == 0x8, "OpusHeader is an invalid size"); 35static_assert(sizeof(OpusPacketHeader) == 0x8, "OpusHeader is an invalid size");
35 36
36class OpusDecoderStateBase { 37class OpusDecoderState {
37public: 38public:
38 /// Describes extra behavior that may be asked of the decoding context. 39 /// Describes extra behavior that may be asked of the decoding context.
39 enum class ExtraBehavior { 40 enum class ExtraBehavior {
@@ -49,22 +50,13 @@ public:
49 Enabled, 50 Enabled,
50 }; 51 };
51 52
52 virtual ~OpusDecoderStateBase() = default;
53
54 // Decodes interleaved Opus packets. Optionally allows reporting time taken to
55 // perform the decoding, as well as any relevant extra behavior.
56 virtual void DecodeInterleaved(Kernel::HLERequestContext& ctx, PerfTime perf_time,
57 ExtraBehavior extra_behavior) = 0;
58};
59
60// Represents the decoder state for a non-multistream decoder.
61class OpusDecoderState final : public OpusDecoderStateBase {
62public:
63 explicit OpusDecoderState(OpusDecoderPtr decoder, u32 sample_rate, u32 channel_count) 53 explicit OpusDecoderState(OpusDecoderPtr decoder, u32 sample_rate, u32 channel_count)
64 : decoder{std::move(decoder)}, sample_rate{sample_rate}, channel_count{channel_count} {} 54 : decoder{std::move(decoder)}, sample_rate{sample_rate}, channel_count{channel_count} {}
65 55
56 // Decodes interleaved Opus packets. Optionally allows reporting time taken to
57 // perform the decoding, as well as any relevant extra behavior.
66 void DecodeInterleaved(Kernel::HLERequestContext& ctx, PerfTime perf_time, 58 void DecodeInterleaved(Kernel::HLERequestContext& ctx, PerfTime perf_time,
67 ExtraBehavior extra_behavior) override { 59 ExtraBehavior extra_behavior) {
68 if (perf_time == PerfTime::Disabled) { 60 if (perf_time == PerfTime::Disabled) {
69 DecodeInterleavedHelper(ctx, nullptr, extra_behavior); 61 DecodeInterleavedHelper(ctx, nullptr, extra_behavior);
70 } else { 62 } else {
@@ -135,7 +127,7 @@ private:
135 127
136 const int frame_size = (static_cast<int>(raw_output_sz / sizeof(s16) / channel_count)); 128 const int frame_size = (static_cast<int>(raw_output_sz / sizeof(s16) / channel_count));
137 const auto out_sample_count = 129 const auto out_sample_count =
138 opus_decode(decoder.get(), frame, hdr.size, output.data(), frame_size, 0); 130 opus_multistream_decode(decoder.get(), frame, hdr.size, output.data(), frame_size, 0);
139 if (out_sample_count < 0) { 131 if (out_sample_count < 0) {
140 LOG_ERROR(Audio, 132 LOG_ERROR(Audio,
141 "Incorrect sample count received from opus_decode, " 133 "Incorrect sample count received from opus_decode, "
@@ -158,7 +150,7 @@ private:
158 void ResetDecoderContext() { 150 void ResetDecoderContext() {
159 ASSERT(decoder != nullptr); 151 ASSERT(decoder != nullptr);
160 152
161 opus_decoder_ctl(decoder.get(), OPUS_RESET_STATE); 153 opus_multistream_decoder_ctl(decoder.get(), OPUS_RESET_STATE);
162 } 154 }
163 155
164 OpusDecoderPtr decoder; 156 OpusDecoderPtr decoder;
@@ -168,7 +160,7 @@ private:
168 160
169class IHardwareOpusDecoderManager final : public ServiceFramework<IHardwareOpusDecoderManager> { 161class IHardwareOpusDecoderManager final : public ServiceFramework<IHardwareOpusDecoderManager> {
170public: 162public:
171 explicit IHardwareOpusDecoderManager(std::unique_ptr<OpusDecoderStateBase> decoder_state) 163 explicit IHardwareOpusDecoderManager(OpusDecoderState decoder_state)
172 : ServiceFramework("IHardwareOpusDecoderManager"), decoder_state{std::move(decoder_state)} { 164 : ServiceFramework("IHardwareOpusDecoderManager"), decoder_state{std::move(decoder_state)} {
173 // clang-format off 165 // clang-format off
174 static const FunctionInfo functions[] = { 166 static const FunctionInfo functions[] = {
@@ -190,35 +182,51 @@ private:
190 void DecodeInterleavedOld(Kernel::HLERequestContext& ctx) { 182 void DecodeInterleavedOld(Kernel::HLERequestContext& ctx) {
191 LOG_DEBUG(Audio, "called"); 183 LOG_DEBUG(Audio, "called");
192 184
193 decoder_state->DecodeInterleaved(ctx, OpusDecoderStateBase::PerfTime::Disabled, 185 decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Disabled,
194 OpusDecoderStateBase::ExtraBehavior::None); 186 OpusDecoderState::ExtraBehavior::None);
195 } 187 }
196 188
197 void DecodeInterleavedWithPerfOld(Kernel::HLERequestContext& ctx) { 189 void DecodeInterleavedWithPerfOld(Kernel::HLERequestContext& ctx) {
198 LOG_DEBUG(Audio, "called"); 190 LOG_DEBUG(Audio, "called");
199 191
200 decoder_state->DecodeInterleaved(ctx, OpusDecoderStateBase::PerfTime::Enabled, 192 decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Enabled,
201 OpusDecoderStateBase::ExtraBehavior::None); 193 OpusDecoderState::ExtraBehavior::None);
202 } 194 }
203 195
204 void DecodeInterleaved(Kernel::HLERequestContext& ctx) { 196 void DecodeInterleaved(Kernel::HLERequestContext& ctx) {
205 LOG_DEBUG(Audio, "called"); 197 LOG_DEBUG(Audio, "called");
206 198
207 IPC::RequestParser rp{ctx}; 199 IPC::RequestParser rp{ctx};
208 const auto extra_behavior = rp.Pop<bool>() 200 const auto extra_behavior = rp.Pop<bool>() ? OpusDecoderState::ExtraBehavior::ResetContext
209 ? OpusDecoderStateBase::ExtraBehavior::ResetContext 201 : OpusDecoderState::ExtraBehavior::None;
210 : OpusDecoderStateBase::ExtraBehavior::None;
211 202
212 decoder_state->DecodeInterleaved(ctx, OpusDecoderStateBase::PerfTime::Enabled, 203 decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Enabled, extra_behavior);
213 extra_behavior);
214 } 204 }
215 205
216 std::unique_ptr<OpusDecoderStateBase> decoder_state; 206 OpusDecoderState decoder_state;
217}; 207};
218 208
219std::size_t WorkerBufferSize(u32 channel_count) { 209std::size_t WorkerBufferSize(u32 channel_count) {
220 ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); 210 ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count");
221 return opus_decoder_get_size(static_cast<int>(channel_count)); 211 constexpr int num_streams = 1;
212 const int num_stereo_streams = channel_count == 2 ? 1 : 0;
213 return opus_multistream_decoder_get_size(num_streams, num_stereo_streams);
214}
215
216// Creates the mapping table that maps the input channels to the particular
217// output channels. In the stereo case, we map the left and right input channels
218// to the left and right output channels respectively.
219//
220// However, in the monophonic case, we only map the one available channel
221// to the sole output channel. We specify 255 for the would-be right channel
222// as this is a special value defined by Opus to indicate to the decoder to
223// ignore that channel.
224std::array<u8, 2> CreateMappingTable(u32 channel_count) {
225 if (channel_count == 2) {
226 return {{0, 1}};
227 }
228
229 return {{0, 255}};
222} 230}
223} // Anonymous namespace 231} // Anonymous namespace
224 232
@@ -259,9 +267,15 @@ void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) {
259 const std::size_t worker_sz = WorkerBufferSize(channel_count); 267 const std::size_t worker_sz = WorkerBufferSize(channel_count);
260 ASSERT_MSG(buffer_sz >= worker_sz, "Worker buffer too large"); 268 ASSERT_MSG(buffer_sz >= worker_sz, "Worker buffer too large");
261 269
262 OpusDecoderPtr decoder{static_cast<OpusDecoder*>(operator new(worker_sz))}; 270 const int num_stereo_streams = channel_count == 2 ? 1 : 0;
263 if (const int err = opus_decoder_init(decoder.get(), sample_rate, channel_count)) { 271 const auto mapping_table = CreateMappingTable(channel_count);
264 LOG_ERROR(Audio, "Failed to init opus decoder with error={}", err); 272
273 int error = 0;
274 OpusDecoderPtr decoder{
275 opus_multistream_decoder_create(sample_rate, static_cast<int>(channel_count), 1,
276 num_stereo_streams, mapping_table.data(), &error)};
277 if (error != OPUS_OK || decoder == nullptr) {
278 LOG_ERROR(Audio, "Failed to create Opus decoder (error={}).", error);
265 IPC::ResponseBuilder rb{ctx, 2}; 279 IPC::ResponseBuilder rb{ctx, 2};
266 // TODO(ogniK): Use correct error code 280 // TODO(ogniK): Use correct error code
267 rb.Push(ResultCode(-1)); 281 rb.Push(ResultCode(-1));
@@ -271,7 +285,7 @@ void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) {
271 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 285 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
272 rb.Push(RESULT_SUCCESS); 286 rb.Push(RESULT_SUCCESS);
273 rb.PushIpcInterface<IHardwareOpusDecoderManager>( 287 rb.PushIpcInterface<IHardwareOpusDecoderManager>(
274 std::make_unique<OpusDecoderState>(std::move(decoder), sample_rate, channel_count)); 288 OpusDecoderState{std::move(decoder), sample_rate, channel_count});
275} 289}
276 290
277HwOpus::HwOpus() : ServiceFramework("hwopus") { 291HwOpus::HwOpus() : ServiceFramework("hwopus") {
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index 54959edd8..f03fb629c 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -733,7 +733,10 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
733FSP_SRV::~FSP_SRV() = default; 733FSP_SRV::~FSP_SRV() = default;
734 734
735void FSP_SRV::SetCurrentProcess(Kernel::HLERequestContext& ctx) { 735void FSP_SRV::SetCurrentProcess(Kernel::HLERequestContext& ctx) {
736 LOG_WARNING(Service_FS, "(STUBBED) called"); 736 IPC::RequestParser rp{ctx};
737 current_process_id = rp.Pop<u64>();
738
739 LOG_DEBUG(Service_FS, "called. current_process_id=0x{:016X}", current_process_id);
737 740
738 IPC::ResponseBuilder rb{ctx, 2}; 741 IPC::ResponseBuilder rb{ctx, 2};
739 rb.Push(RESULT_SUCCESS); 742 rb.Push(RESULT_SUCCESS);
diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h
index 3a5f4e200..d7572ba7a 100644
--- a/src/core/hle/service/filesystem/fsp_srv.h
+++ b/src/core/hle/service/filesystem/fsp_srv.h
@@ -32,6 +32,7 @@ private:
32 void OpenPatchDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx); 32 void OpenPatchDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx);
33 33
34 FileSys::VirtualFile romfs; 34 FileSys::VirtualFile romfs;
35 u64 current_process_id = 0;
35}; 36};
36 37
37} // namespace Service::FileSystem 38} // namespace Service::FileSystem
diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h
index 929035034..e584b92ec 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.h
+++ b/src/core/hle/service/hid/controllers/debug_pad.h
@@ -41,20 +41,20 @@ private:
41 struct PadState { 41 struct PadState {
42 union { 42 union {
43 u32_le raw{}; 43 u32_le raw{};
44 BitField<0, 1, u32_le> a; 44 BitField<0, 1, u32> a;
45 BitField<1, 1, u32_le> b; 45 BitField<1, 1, u32> b;
46 BitField<2, 1, u32_le> x; 46 BitField<2, 1, u32> x;
47 BitField<3, 1, u32_le> y; 47 BitField<3, 1, u32> y;
48 BitField<4, 1, u32_le> l; 48 BitField<4, 1, u32> l;
49 BitField<5, 1, u32_le> r; 49 BitField<5, 1, u32> r;
50 BitField<6, 1, u32_le> zl; 50 BitField<6, 1, u32> zl;
51 BitField<7, 1, u32_le> zr; 51 BitField<7, 1, u32> zr;
52 BitField<8, 1, u32_le> plus; 52 BitField<8, 1, u32> plus;
53 BitField<9, 1, u32_le> minus; 53 BitField<9, 1, u32> minus;
54 BitField<10, 1, u32_le> d_left; 54 BitField<10, 1, u32> d_left;
55 BitField<11, 1, u32_le> d_up; 55 BitField<11, 1, u32> d_up;
56 BitField<12, 1, u32_le> d_right; 56 BitField<12, 1, u32> d_right;
57 BitField<13, 1, u32_le> d_down; 57 BitField<13, 1, u32> d_down;
58 }; 58 };
59 }; 59 };
60 static_assert(sizeof(PadState) == 0x4, "PadState is an invalid size"); 60 static_assert(sizeof(PadState) == 0x4, "PadState is an invalid size");
@@ -62,7 +62,7 @@ private:
62 struct Attributes { 62 struct Attributes {
63 union { 63 union {
64 u32_le raw{}; 64 u32_le raw{};
65 BitField<0, 1, u32_le> connected; 65 BitField<0, 1, u32> connected;
66 }; 66 };
67 }; 67 };
68 static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size"); 68 static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size");
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index 18c7a94e6..4ff50b3cd 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -39,13 +39,13 @@ public:
39 union { 39 union {
40 u32_le raw{}; 40 u32_le raw{};
41 41
42 BitField<0, 1, u32_le> pro_controller; 42 BitField<0, 1, u32> pro_controller;
43 BitField<1, 1, u32_le> handheld; 43 BitField<1, 1, u32> handheld;
44 BitField<2, 1, u32_le> joycon_dual; 44 BitField<2, 1, u32> joycon_dual;
45 BitField<3, 1, u32_le> joycon_left; 45 BitField<3, 1, u32> joycon_left;
46 BitField<4, 1, u32_le> joycon_right; 46 BitField<4, 1, u32> joycon_right;
47 47
48 BitField<6, 1, u32_le> pokeball; // TODO(ogniK): Confirm when possible 48 BitField<6, 1, u32> pokeball; // TODO(ogniK): Confirm when possible
49 }; 49 };
50 }; 50 };
51 static_assert(sizeof(NPadType) == 4, "NPadType is an invalid size"); 51 static_assert(sizeof(NPadType) == 4, "NPadType is an invalid size");
@@ -150,43 +150,43 @@ private:
150 union { 150 union {
151 u64_le raw{}; 151 u64_le raw{};
152 // Button states 152 // Button states
153 BitField<0, 1, u64_le> a; 153 BitField<0, 1, u64> a;
154 BitField<1, 1, u64_le> b; 154 BitField<1, 1, u64> b;
155 BitField<2, 1, u64_le> x; 155 BitField<2, 1, u64> x;
156 BitField<3, 1, u64_le> y; 156 BitField<3, 1, u64> y;
157 BitField<4, 1, u64_le> l_stick; 157 BitField<4, 1, u64> l_stick;
158 BitField<5, 1, u64_le> r_stick; 158 BitField<5, 1, u64> r_stick;
159 BitField<6, 1, u64_le> l; 159 BitField<6, 1, u64> l;
160 BitField<7, 1, u64_le> r; 160 BitField<7, 1, u64> r;
161 BitField<8, 1, u64_le> zl; 161 BitField<8, 1, u64> zl;
162 BitField<9, 1, u64_le> zr; 162 BitField<9, 1, u64> zr;
163 BitField<10, 1, u64_le> plus; 163 BitField<10, 1, u64> plus;
164 BitField<11, 1, u64_le> minus; 164 BitField<11, 1, u64> minus;
165 165
166 // D-Pad 166 // D-Pad
167 BitField<12, 1, u64_le> d_left; 167 BitField<12, 1, u64> d_left;
168 BitField<13, 1, u64_le> d_up; 168 BitField<13, 1, u64> d_up;
169 BitField<14, 1, u64_le> d_right; 169 BitField<14, 1, u64> d_right;
170 BitField<15, 1, u64_le> d_down; 170 BitField<15, 1, u64> d_down;
171 171
172 // Left JoyStick 172 // Left JoyStick
173 BitField<16, 1, u64_le> l_stick_left; 173 BitField<16, 1, u64> l_stick_left;
174 BitField<17, 1, u64_le> l_stick_up; 174 BitField<17, 1, u64> l_stick_up;
175 BitField<18, 1, u64_le> l_stick_right; 175 BitField<18, 1, u64> l_stick_right;
176 BitField<19, 1, u64_le> l_stick_down; 176 BitField<19, 1, u64> l_stick_down;
177 177
178 // Right JoyStick 178 // Right JoyStick
179 BitField<20, 1, u64_le> r_stick_left; 179 BitField<20, 1, u64> r_stick_left;
180 BitField<21, 1, u64_le> r_stick_up; 180 BitField<21, 1, u64> r_stick_up;
181 BitField<22, 1, u64_le> r_stick_right; 181 BitField<22, 1, u64> r_stick_right;
182 BitField<23, 1, u64_le> r_stick_down; 182 BitField<23, 1, u64> r_stick_down;
183 183
184 // Not always active? 184 // Not always active?
185 BitField<24, 1, u64_le> left_sl; 185 BitField<24, 1, u64> left_sl;
186 BitField<25, 1, u64_le> left_sr; 186 BitField<25, 1, u64> left_sr;
187 187
188 BitField<26, 1, u64_le> right_sl; 188 BitField<26, 1, u64> right_sl;
189 BitField<27, 1, u64_le> right_sr; 189 BitField<27, 1, u64> right_sr;
190 }; 190 };
191 }; 191 };
192 static_assert(sizeof(ControllerPadState) == 8, "ControllerPadState is an invalid size"); 192 static_assert(sizeof(ControllerPadState) == 8, "ControllerPadState is an invalid size");
@@ -200,12 +200,12 @@ private:
200 struct ConnectionState { 200 struct ConnectionState {
201 union { 201 union {
202 u32_le raw{}; 202 u32_le raw{};
203 BitField<0, 1, u32_le> IsConnected; 203 BitField<0, 1, u32> IsConnected;
204 BitField<1, 1, u32_le> IsWired; 204 BitField<1, 1, u32> IsWired;
205 BitField<2, 1, u32_le> IsLeftJoyConnected; 205 BitField<2, 1, u32> IsLeftJoyConnected;
206 BitField<3, 1, u32_le> IsLeftJoyWired; 206 BitField<3, 1, u32> IsLeftJoyWired;
207 BitField<4, 1, u32_le> IsRightJoyConnected; 207 BitField<4, 1, u32> IsRightJoyConnected;
208 BitField<5, 1, u32_le> IsRightJoyWired; 208 BitField<5, 1, u32> IsRightJoyWired;
209 }; 209 };
210 }; 210 };
211 static_assert(sizeof(ConnectionState) == 4, "ConnectionState is an invalid size"); 211 static_assert(sizeof(ConnectionState) == 4, "ConnectionState is an invalid size");
@@ -240,23 +240,23 @@ private:
240 struct NPadProperties { 240 struct NPadProperties {
241 union { 241 union {
242 s64_le raw{}; 242 s64_le raw{};
243 BitField<11, 1, s64_le> is_vertical; 243 BitField<11, 1, s64> is_vertical;
244 BitField<12, 1, s64_le> is_horizontal; 244 BitField<12, 1, s64> is_horizontal;
245 BitField<13, 1, s64_le> use_plus; 245 BitField<13, 1, s64> use_plus;
246 BitField<14, 1, s64_le> use_minus; 246 BitField<14, 1, s64> use_minus;
247 }; 247 };
248 }; 248 };
249 249
250 struct NPadDevice { 250 struct NPadDevice {
251 union { 251 union {
252 u32_le raw{}; 252 u32_le raw{};
253 BitField<0, 1, s32_le> pro_controller; 253 BitField<0, 1, s32> pro_controller;
254 BitField<1, 1, s32_le> handheld; 254 BitField<1, 1, s32> handheld;
255 BitField<2, 1, s32_le> handheld_left; 255 BitField<2, 1, s32> handheld_left;
256 BitField<3, 1, s32_le> handheld_right; 256 BitField<3, 1, s32> handheld_right;
257 BitField<4, 1, s32_le> joycon_left; 257 BitField<4, 1, s32> joycon_left;
258 BitField<5, 1, s32_le> joycon_right; 258 BitField<5, 1, s32> joycon_right;
259 BitField<6, 1, s32_le> pokeball; 259 BitField<6, 1, s32> pokeball;
260 }; 260 };
261 }; 261 };
262 262
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h
index 012b6e0dd..76fc340e9 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.h
+++ b/src/core/hle/service/hid/controllers/touchscreen.h
@@ -33,8 +33,8 @@ private:
33 struct Attributes { 33 struct Attributes {
34 union { 34 union {
35 u32 raw{}; 35 u32 raw{};
36 BitField<0, 1, u32_le> start_touch; 36 BitField<0, 1, u32> start_touch;
37 BitField<1, 1, u32_le> end_touch; 37 BitField<1, 1, u32> end_touch;
38 }; 38 };
39 }; 39 };
40 static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size"); 40 static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size");
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index 7cc58db4c..498602de5 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -4,6 +4,9 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "core/hle/service/hid/controllers/controller_base.h"
8#include "core/hle/service/service.h"
9
7#include "controllers/controller_base.h" 10#include "controllers/controller_base.h"
8#include "core/hle/service/service.h" 11#include "core/hle/service/service.h"
9 12
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index 9df7ac50f..d65693fc7 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -319,15 +319,14 @@ public:
319 } 319 }
320 320
321 ASSERT(vm_manager 321 ASSERT(vm_manager
322 .MirrorMemory(*map_address, nro_addr, nro_size, 322 .MirrorMemory(*map_address, nro_addr, nro_size, Kernel::MemoryState::ModuleCode)
323 Kernel::MemoryState::ModuleCodeStatic)
324 .IsSuccess()); 323 .IsSuccess());
325 ASSERT(vm_manager.UnmapRange(nro_addr, nro_size).IsSuccess()); 324 ASSERT(vm_manager.UnmapRange(nro_addr, nro_size).IsSuccess());
326 325
327 if (bss_size > 0) { 326 if (bss_size > 0) {
328 ASSERT(vm_manager 327 ASSERT(vm_manager
329 .MirrorMemory(*map_address + nro_size, bss_addr, bss_size, 328 .MirrorMemory(*map_address + nro_size, bss_addr, bss_size,
330 Kernel::MemoryState::ModuleCodeStatic) 329 Kernel::MemoryState::ModuleCode)
331 .IsSuccess()); 330 .IsSuccess());
332 ASSERT(vm_manager.UnmapRange(bss_addr, bss_size).IsSuccess()); 331 ASSERT(vm_manager.UnmapRange(bss_addr, bss_size).IsSuccess());
333 } 332 }
@@ -388,8 +387,7 @@ public:
388 const auto& nro_size = iter->second.size; 387 const auto& nro_size = iter->second.size;
389 388
390 ASSERT(vm_manager 389 ASSERT(vm_manager
391 .MirrorMemory(heap_addr, mapped_addr, nro_size, 390 .MirrorMemory(heap_addr, mapped_addr, nro_size, Kernel::MemoryState::ModuleCode)
392 Kernel::MemoryState::ModuleCodeStatic)
393 .IsSuccess()); 391 .IsSuccess());
394 ASSERT(vm_manager.UnmapRange(mapped_addr, nro_size).IsSuccess()); 392 ASSERT(vm_manager.UnmapRange(mapped_addr, nro_size).IsSuccess());
395 393
diff --git a/src/core/hle/service/lm/lm.cpp b/src/core/hle/service/lm/lm.cpp
index 1f462e087..2a61593e2 100644
--- a/src/core/hle/service/lm/lm.cpp
+++ b/src/core/hle/service/lm/lm.cpp
@@ -42,7 +42,7 @@ private:
42 union { 42 union {
43 BitField<0, 16, Flags> flags; 43 BitField<0, 16, Flags> flags;
44 BitField<16, 8, Severity> severity; 44 BitField<16, 8, Severity> severity;
45 BitField<24, 8, u32_le> verbosity; 45 BitField<24, 8, u32> verbosity;
46 }; 46 };
47 u32_le payload_size; 47 u32_le payload_size;
48 48
diff --git a/src/core/hle/service/nvdrv/devices/nvdevice.h b/src/core/hle/service/nvdrv/devices/nvdevice.h
index 0f02a1a18..4f6042b00 100644
--- a/src/core/hle/service/nvdrv/devices/nvdevice.h
+++ b/src/core/hle/service/nvdrv/devices/nvdevice.h
@@ -19,11 +19,11 @@ public:
19 virtual ~nvdevice() = default; 19 virtual ~nvdevice() = default;
20 union Ioctl { 20 union Ioctl {
21 u32_le raw; 21 u32_le raw;
22 BitField<0, 8, u32_le> cmd; 22 BitField<0, 8, u32> cmd;
23 BitField<8, 8, u32_le> group; 23 BitField<8, 8, u32> group;
24 BitField<16, 14, u32_le> length; 24 BitField<16, 14, u32> length;
25 BitField<30, 1, u32_le> is_in; 25 BitField<30, 1, u32> is_in;
26 BitField<31, 1, u32_le> is_out; 26 BitField<31, 1, u32> is_out;
27 }; 27 };
28 28
29 /** 29 /**
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
index b031ebc66..af62d33d2 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -89,7 +89,7 @@ u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output)
89 for (const auto& entry : entries) { 89 for (const auto& entry : entries) {
90 LOG_WARNING(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}", 90 LOG_WARNING(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}",
91 entry.offset, entry.nvmap_handle, entry.pages); 91 entry.offset, entry.nvmap_handle, entry.pages);
92 Tegra::GPUVAddr offset = static_cast<Tegra::GPUVAddr>(entry.offset) << 0x10; 92 GPUVAddr offset = static_cast<GPUVAddr>(entry.offset) << 0x10;
93 auto object = nvmap_dev->GetObject(entry.nvmap_handle); 93 auto object = nvmap_dev->GetObject(entry.nvmap_handle);
94 if (!object) { 94 if (!object) {
95 LOG_CRITICAL(Service_NVDRV, "nvmap {} is an invalid handle!", entry.nvmap_handle); 95 LOG_CRITICAL(Service_NVDRV, "nvmap {} is an invalid handle!", entry.nvmap_handle);
@@ -102,7 +102,7 @@ u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output)
102 u64 size = static_cast<u64>(entry.pages) << 0x10; 102 u64 size = static_cast<u64>(entry.pages) << 0x10;
103 ASSERT(size <= object->size); 103 ASSERT(size <= object->size);
104 104
105 Tegra::GPUVAddr returned = gpu.MemoryManager().MapBufferEx(object->addr, offset, size); 105 GPUVAddr returned = gpu.MemoryManager().MapBufferEx(object->addr, offset, size);
106 ASSERT(returned == offset); 106 ASSERT(returned == offset);
107 } 107 }
108 std::memcpy(output.data(), entries.data(), output.size()); 108 std::memcpy(output.data(), entries.data(), output.size());
@@ -173,16 +173,8 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou
173 return 0; 173 return 0;
174 } 174 }
175 175
176 auto& system_instance = Core::System::GetInstance(); 176 params.offset = Core::System::GetInstance().GPU().MemoryManager().UnmapBuffer(params.offset,
177 177 itr->second.size);
178 // Remove this memory region from the rasterizer cache.
179 auto& gpu = system_instance.GPU();
180 auto cpu_addr = gpu.MemoryManager().GpuToCpuAddress(params.offset);
181 ASSERT(cpu_addr);
182 gpu.FlushAndInvalidateRegion(ToCacheAddr(Memory::GetPointer(*cpu_addr)), itr->second.size);
183
184 params.offset = gpu.MemoryManager().UnmapBuffer(params.offset, itr->second.size);
185
186 buffer_mappings.erase(itr->second.offset); 178 buffer_mappings.erase(itr->second.offset);
187 179
188 std::memcpy(output.data(), &params, output.size()); 180 std::memcpy(output.data(), &params, output.size());
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp
index 6057c7f26..8b1920f22 100644
--- a/src/core/loader/elf.cpp
+++ b/src/core/loader/elf.cpp
@@ -9,6 +9,7 @@
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/file_util.h" 10#include "common/file_util.h"
11#include "common/logging/log.h" 11#include "common/logging/log.h"
12#include "core/hle/kernel/code_set.h"
12#include "core/hle/kernel/process.h" 13#include "core/hle/kernel/process.h"
13#include "core/hle/kernel/vm_manager.h" 14#include "core/hle/kernel/vm_manager.h"
14#include "core/loader/elf.h" 15#include "core/loader/elf.h"
diff --git a/src/core/loader/linker.cpp b/src/core/loader/linker.cpp
deleted file mode 100644
index 57ca8c3ee..000000000
--- a/src/core/loader/linker.cpp
+++ /dev/null
@@ -1,147 +0,0 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <vector>
6
7#include "common/common_funcs.h"
8#include "common/logging/log.h"
9#include "common/swap.h"
10#include "core/loader/linker.h"
11#include "core/memory.h"
12
13namespace Loader {
14
15enum class RelocationType : u32 { ABS64 = 257, GLOB_DAT = 1025, JUMP_SLOT = 1026, RELATIVE = 1027 };
16
17enum DynamicType : u32 {
18 DT_NULL = 0,
19 DT_PLTRELSZ = 2,
20 DT_STRTAB = 5,
21 DT_SYMTAB = 6,
22 DT_RELA = 7,
23 DT_RELASZ = 8,
24 DT_STRSZ = 10,
25 DT_JMPREL = 23,
26};
27
28struct Elf64_Rela {
29 u64_le offset;
30 RelocationType type;
31 u32_le symbol;
32 s64_le addend;
33};
34static_assert(sizeof(Elf64_Rela) == 0x18, "Elf64_Rela has incorrect size.");
35
36struct Elf64_Dyn {
37 u64_le tag;
38 u64_le value;
39};
40static_assert(sizeof(Elf64_Dyn) == 0x10, "Elf64_Dyn has incorrect size.");
41
42struct Elf64_Sym {
43 u32_le name;
44 INSERT_PADDING_BYTES(0x2);
45 u16_le shndx;
46 u64_le value;
47 u64_le size;
48};
49static_assert(sizeof(Elf64_Sym) == 0x18, "Elf64_Sym has incorrect size.");
50
51void Linker::WriteRelocations(std::vector<u8>& program_image, const std::vector<Symbol>& symbols,
52 u64 relocation_offset, u64 size, VAddr load_base) {
53 for (u64 i = 0; i < size; i += sizeof(Elf64_Rela)) {
54 Elf64_Rela rela;
55 std::memcpy(&rela, &program_image[relocation_offset + i], sizeof(Elf64_Rela));
56
57 const Symbol& symbol = symbols[rela.symbol];
58 switch (rela.type) {
59 case RelocationType::RELATIVE: {
60 const u64 value = load_base + rela.addend;
61 if (!symbol.name.empty()) {
62 exports[symbol.name] = value;
63 }
64 std::memcpy(&program_image[rela.offset], &value, sizeof(u64));
65 break;
66 }
67 case RelocationType::JUMP_SLOT:
68 case RelocationType::GLOB_DAT:
69 if (!symbol.value) {
70 imports[symbol.name] = {rela.offset + load_base, 0};
71 } else {
72 exports[symbol.name] = symbol.value;
73 std::memcpy(&program_image[rela.offset], &symbol.value, sizeof(u64));
74 }
75 break;
76 case RelocationType::ABS64:
77 if (!symbol.value) {
78 imports[symbol.name] = {rela.offset + load_base, rela.addend};
79 } else {
80 const u64 value = symbol.value + rela.addend;
81 exports[symbol.name] = value;
82 std::memcpy(&program_image[rela.offset], &value, sizeof(u64));
83 }
84 break;
85 default:
86 LOG_CRITICAL(Loader, "Unknown relocation type: {}", static_cast<int>(rela.type));
87 break;
88 }
89 }
90}
91
92void Linker::Relocate(std::vector<u8>& program_image, u32 dynamic_section_offset, VAddr load_base) {
93 std::map<u64, u64> dynamic;
94 while (dynamic_section_offset < program_image.size()) {
95 Elf64_Dyn dyn;
96 std::memcpy(&dyn, &program_image[dynamic_section_offset], sizeof(Elf64_Dyn));
97 dynamic_section_offset += sizeof(Elf64_Dyn);
98
99 if (dyn.tag == DT_NULL) {
100 break;
101 }
102 dynamic[dyn.tag] = dyn.value;
103 }
104
105 u64 offset = dynamic[DT_SYMTAB];
106 std::vector<Symbol> symbols;
107 while (offset < program_image.size()) {
108 Elf64_Sym sym;
109 std::memcpy(&sym, &program_image[offset], sizeof(Elf64_Sym));
110 offset += sizeof(Elf64_Sym);
111
112 if (sym.name >= dynamic[DT_STRSZ]) {
113 break;
114 }
115
116 std::string name = reinterpret_cast<char*>(&program_image[dynamic[DT_STRTAB] + sym.name]);
117 if (sym.value) {
118 exports[name] = load_base + sym.value;
119 symbols.emplace_back(std::move(name), load_base + sym.value);
120 } else {
121 symbols.emplace_back(std::move(name), 0);
122 }
123 }
124
125 if (dynamic.find(DT_RELA) != dynamic.end()) {
126 WriteRelocations(program_image, symbols, dynamic[DT_RELA], dynamic[DT_RELASZ], load_base);
127 }
128
129 if (dynamic.find(DT_JMPREL) != dynamic.end()) {
130 WriteRelocations(program_image, symbols, dynamic[DT_JMPREL], dynamic[DT_PLTRELSZ],
131 load_base);
132 }
133}
134
135void Linker::ResolveImports() {
136 // Resolve imports
137 for (const auto& import : imports) {
138 const auto& search = exports.find(import.first);
139 if (search != exports.end()) {
140 Memory::Write64(import.second.ea, search->second + import.second.addend);
141 } else {
142 LOG_ERROR(Loader, "Unresolved import: {}", import.first);
143 }
144 }
145}
146
147} // namespace Loader
diff --git a/src/core/loader/linker.h b/src/core/loader/linker.h
deleted file mode 100644
index 107625837..000000000
--- a/src/core/loader/linker.h
+++ /dev/null
@@ -1,36 +0,0 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <map>
8#include <string>
9#include "common/common_types.h"
10
11namespace Loader {
12
13class Linker {
14protected:
15 struct Symbol {
16 Symbol(std::string&& name, u64 value) : name(std::move(name)), value(value) {}
17 std::string name;
18 u64 value;
19 };
20
21 struct Import {
22 VAddr ea;
23 s64 addend;
24 };
25
26 void WriteRelocations(std::vector<u8>& program_image, const std::vector<Symbol>& symbols,
27 u64 relocation_offset, u64 size, VAddr load_base);
28 void Relocate(std::vector<u8>& program_image, u32 dynamic_section_offset, VAddr load_base);
29
30 void ResolveImports();
31
32 std::map<std::string, Import> imports;
33 std::map<std::string, VAddr> exports;
34};
35
36} // namespace Loader
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index 4fad0c0dd..5de02a94b 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -14,6 +14,7 @@
14#include "core/file_sys/romfs_factory.h" 14#include "core/file_sys/romfs_factory.h"
15#include "core/file_sys/vfs_offset.h" 15#include "core/file_sys/vfs_offset.h"
16#include "core/gdbstub/gdbstub.h" 16#include "core/gdbstub/gdbstub.h"
17#include "core/hle/kernel/code_set.h"
17#include "core/hle/kernel/process.h" 18#include "core/hle/kernel/process.h"
18#include "core/hle/kernel/vm_manager.h" 19#include "core/hle/kernel/vm_manager.h"
19#include "core/hle/service/filesystem/filesystem.h" 20#include "core/hle/service/filesystem/filesystem.h"
diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h
index 013d629c0..85b0ed644 100644
--- a/src/core/loader/nro.h
+++ b/src/core/loader/nro.h
@@ -4,10 +4,10 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <memory>
7#include <string> 8#include <string>
8#include <vector> 9#include <vector>
9#include "common/common_types.h" 10#include "common/common_types.h"
10#include "core/loader/linker.h"
11#include "core/loader/loader.h" 11#include "core/loader/loader.h"
12 12
13namespace FileSys { 13namespace FileSys {
@@ -21,7 +21,7 @@ class Process;
21namespace Loader { 21namespace Loader {
22 22
23/// Loads an NRO file 23/// Loads an NRO file
24class AppLoader_NRO final : public AppLoader, Linker { 24class AppLoader_NRO final : public AppLoader {
25public: 25public:
26 explicit AppLoader_NRO(FileSys::VirtualFile file); 26 explicit AppLoader_NRO(FileSys::VirtualFile file);
27 ~AppLoader_NRO() override; 27 ~AppLoader_NRO() override;
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 6ded0b707..714d85a59 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -7,10 +7,13 @@
7#include <lz4.h> 7#include <lz4.h>
8#include "common/common_funcs.h" 8#include "common/common_funcs.h"
9#include "common/file_util.h" 9#include "common/file_util.h"
10#include "common/hex_util.h"
10#include "common/logging/log.h" 11#include "common/logging/log.h"
11#include "common/swap.h" 12#include "common/swap.h"
13#include "core/core.h"
12#include "core/file_sys/patch_manager.h" 14#include "core/file_sys/patch_manager.h"
13#include "core/gdbstub/gdbstub.h" 15#include "core/gdbstub/gdbstub.h"
16#include "core/hle/kernel/code_set.h"
14#include "core/hle/kernel/process.h" 17#include "core/hle/kernel/process.h"
15#include "core/hle/kernel/vm_manager.h" 18#include "core/hle/kernel/vm_manager.h"
16#include "core/loader/nso.h" 19#include "core/loader/nso.h"
@@ -18,36 +21,8 @@
18#include "core/settings.h" 21#include "core/settings.h"
19 22
20namespace Loader { 23namespace Loader {
21 24namespace {
22struct NsoSegmentHeader { 25struct MODHeader {
23 u32_le offset;
24 u32_le location;
25 u32_le size;
26 union {
27 u32_le alignment;
28 u32_le bss_size;
29 };
30};
31static_assert(sizeof(NsoSegmentHeader) == 0x10, "NsoSegmentHeader has incorrect size.");
32
33struct NsoHeader {
34 u32_le magic;
35 u32_le version;
36 INSERT_PADDING_WORDS(1);
37 u8 flags;
38 std::array<NsoSegmentHeader, 3> segments; // Text, RoData, Data (in that order)
39 std::array<u8, 0x20> build_id;
40 std::array<u32_le, 3> segments_compressed_size;
41
42 bool IsSegmentCompressed(size_t segment_num) const {
43 ASSERT_MSG(segment_num < 3, "Invalid segment {}", segment_num);
44 return ((flags >> segment_num) & 1);
45 }
46};
47static_assert(sizeof(NsoHeader) == 0x6c, "NsoHeader has incorrect size.");
48static_assert(std::is_trivially_copyable_v<NsoHeader>, "NsoHeader isn't trivially copyable.");
49
50struct ModHeader {
51 u32_le magic; 26 u32_le magic;
52 u32_le dynamic_offset; 27 u32_le dynamic_offset;
53 u32_le bss_start_offset; 28 u32_le bss_start_offset;
@@ -56,25 +31,10 @@ struct ModHeader {
56 u32_le eh_frame_hdr_end_offset; 31 u32_le eh_frame_hdr_end_offset;
57 u32_le module_offset; // Offset to runtime-generated module object. typically equal to .bss base 32 u32_le module_offset; // Offset to runtime-generated module object. typically equal to .bss base
58}; 33};
59static_assert(sizeof(ModHeader) == 0x1c, "ModHeader has incorrect size."); 34static_assert(sizeof(MODHeader) == 0x1c, "MODHeader has incorrect size.");
60
61AppLoader_NSO::AppLoader_NSO(FileSys::VirtualFile file) : AppLoader(std::move(file)) {}
62 35
63FileType AppLoader_NSO::IdentifyType(const FileSys::VirtualFile& file) { 36std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data,
64 u32 magic = 0; 37 const NSOSegmentHeader& header) {
65 if (file->ReadObject(&magic) != sizeof(magic)) {
66 return FileType::Error;
67 }
68
69 if (Common::MakeMagic('N', 'S', 'O', '0') != magic) {
70 return FileType::Error;
71 }
72
73 return FileType::NSO;
74}
75
76static std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data,
77 const NsoSegmentHeader& header) {
78 std::vector<u8> uncompressed_data(header.size); 38 std::vector<u8> uncompressed_data(header.size);
79 const int bytes_uncompressed = 39 const int bytes_uncompressed =
80 LZ4_decompress_safe(reinterpret_cast<const char*>(compressed_data.data()), 40 LZ4_decompress_safe(reinterpret_cast<const char*>(compressed_data.data()),
@@ -88,23 +48,47 @@ static std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data,
88 return uncompressed_data; 48 return uncompressed_data;
89} 49}
90 50
91static constexpr u32 PageAlignSize(u32 size) { 51constexpr u32 PageAlignSize(u32 size) {
92 return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; 52 return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
93} 53}
54} // Anonymous namespace
55
56bool NSOHeader::IsSegmentCompressed(size_t segment_num) const {
57 ASSERT_MSG(segment_num < 3, "Invalid segment {}", segment_num);
58 return ((flags >> segment_num) & 1) != 0;
59}
60
61AppLoader_NSO::AppLoader_NSO(FileSys::VirtualFile file) : AppLoader(std::move(file)) {}
62
63FileType AppLoader_NSO::IdentifyType(const FileSys::VirtualFile& file) {
64 u32 magic = 0;
65 if (file->ReadObject(&magic) != sizeof(magic)) {
66 return FileType::Error;
67 }
68
69 if (Common::MakeMagic('N', 'S', 'O', '0') != magic) {
70 return FileType::Error;
71 }
72
73 return FileType::NSO;
74}
94 75
95std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process, 76std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
96 const FileSys::VfsFile& file, VAddr load_base, 77 const FileSys::VfsFile& file, VAddr load_base,
97 bool should_pass_arguments, 78 bool should_pass_arguments,
98 std::optional<FileSys::PatchManager> pm) { 79 std::optional<FileSys::PatchManager> pm) {
99 if (file.GetSize() < sizeof(NsoHeader)) 80 if (file.GetSize() < sizeof(NSOHeader)) {
100 return {}; 81 return {};
82 }
101 83
102 NsoHeader nso_header{}; 84 NSOHeader nso_header{};
103 if (sizeof(NsoHeader) != file.ReadObject(&nso_header)) 85 if (sizeof(NSOHeader) != file.ReadObject(&nso_header)) {
104 return {}; 86 return {};
87 }
105 88
106 if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) 89 if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) {
107 return {}; 90 return {};
91 }
108 92
109 // Build program image 93 // Build program image
110 Kernel::CodeSet codeset; 94 Kernel::CodeSet codeset;
@@ -140,10 +124,10 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
140 std::memcpy(&module_offset, program_image.data() + 4, sizeof(u32)); 124 std::memcpy(&module_offset, program_image.data() + 4, sizeof(u32));
141 125
142 // Read MOD header 126 // Read MOD header
143 ModHeader mod_header{}; 127 MODHeader mod_header{};
144 // Default .bss to size in segment header if MOD0 section doesn't exist 128 // Default .bss to size in segment header if MOD0 section doesn't exist
145 u32 bss_size{PageAlignSize(nso_header.segments[2].bss_size)}; 129 u32 bss_size{PageAlignSize(nso_header.segments[2].bss_size)};
146 std::memcpy(&mod_header, program_image.data() + module_offset, sizeof(ModHeader)); 130 std::memcpy(&mod_header, program_image.data() + module_offset, sizeof(MODHeader));
147 const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')}; 131 const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')};
148 if (has_mod_header) { 132 if (has_mod_header) {
149 // Resize program image to include .bss section and page align each section 133 // Resize program image to include .bss section and page align each section
@@ -155,13 +139,25 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
155 139
156 // Apply patches if necessary 140 // Apply patches if necessary
157 if (pm && (pm->HasNSOPatch(nso_header.build_id) || Settings::values.dump_nso)) { 141 if (pm && (pm->HasNSOPatch(nso_header.build_id) || Settings::values.dump_nso)) {
158 std::vector<u8> pi_header(program_image.size() + 0x100); 142 std::vector<u8> pi_header(sizeof(NSOHeader) + program_image.size());
159 std::memcpy(pi_header.data(), &nso_header, sizeof(NsoHeader)); 143 pi_header.insert(pi_header.begin(), reinterpret_cast<u8*>(&nso_header),
160 std::memcpy(pi_header.data() + 0x100, program_image.data(), program_image.size()); 144 reinterpret_cast<u8*>(&nso_header) + sizeof(NSOHeader));
145 pi_header.insert(pi_header.begin() + sizeof(NSOHeader), program_image.begin(),
146 program_image.end());
161 147
162 pi_header = pm->PatchNSO(pi_header); 148 pi_header = pm->PatchNSO(pi_header);
163 149
164 std::memcpy(program_image.data(), pi_header.data() + 0x100, program_image.size()); 150 std::copy(pi_header.begin() + sizeof(NSOHeader), pi_header.end(), program_image.begin());
151 }
152
153 // Apply cheats if they exist and the program has a valid title ID
154 if (pm) {
155 auto& system = Core::System::GetInstance();
156 const auto cheats = pm->CreateCheatList(system, nso_header.build_id);
157 if (!cheats.empty()) {
158 system.RegisterCheatList(cheats, Common::HexArrayToString(nso_header.build_id),
159 load_base, load_base + program_image.size());
160 }
165 } 161 }
166 162
167 // Load codeset for current process 163 // Load codeset for current process
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h
index 135b6ea5a..4674c3724 100644
--- a/src/core/loader/nso.h
+++ b/src/core/loader/nso.h
@@ -4,10 +4,12 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
7#include <optional> 8#include <optional>
9#include <type_traits>
8#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/swap.h"
9#include "core/file_sys/patch_manager.h" 12#include "core/file_sys/patch_manager.h"
10#include "core/loader/linker.h"
11#include "core/loader/loader.h" 13#include "core/loader/loader.h"
12 14
13namespace Kernel { 15namespace Kernel {
@@ -16,6 +18,43 @@ class Process;
16 18
17namespace Loader { 19namespace Loader {
18 20
21struct NSOSegmentHeader {
22 u32_le offset;
23 u32_le location;
24 u32_le size;
25 union {
26 u32_le alignment;
27 u32_le bss_size;
28 };
29};
30static_assert(sizeof(NSOSegmentHeader) == 0x10, "NsoSegmentHeader has incorrect size.");
31
32struct NSOHeader {
33 using SHA256Hash = std::array<u8, 0x20>;
34
35 struct RODataRelativeExtent {
36 u32_le data_offset;
37 u32_le size;
38 };
39
40 u32_le magic;
41 u32_le version;
42 u32 reserved;
43 u32_le flags;
44 std::array<NSOSegmentHeader, 3> segments; // Text, RoData, Data (in that order)
45 std::array<u8, 0x20> build_id;
46 std::array<u32_le, 3> segments_compressed_size;
47 std::array<u8, 0x1C> padding;
48 RODataRelativeExtent api_info_extent;
49 RODataRelativeExtent dynstr_extent;
50 RODataRelativeExtent dynsyn_extent;
51 std::array<SHA256Hash, 3> segment_hashes;
52
53 bool IsSegmentCompressed(size_t segment_num) const;
54};
55static_assert(sizeof(NSOHeader) == 0x100, "NSOHeader has incorrect size.");
56static_assert(std::is_trivially_copyable_v<NSOHeader>, "NSOHeader must be trivially copyable.");
57
19constexpr u64 NSO_ARGUMENT_DATA_ALLOCATION_SIZE = 0x9000; 58constexpr u64 NSO_ARGUMENT_DATA_ALLOCATION_SIZE = 0x9000;
20 59
21struct NSOArgumentHeader { 60struct NSOArgumentHeader {
@@ -26,7 +65,7 @@ struct NSOArgumentHeader {
26static_assert(sizeof(NSOArgumentHeader) == 0x20, "NSOArgumentHeader has incorrect size."); 65static_assert(sizeof(NSOArgumentHeader) == 0x20, "NSOArgumentHeader has incorrect size.");
27 66
28/// Loads an NSO file 67/// Loads an NSO file
29class AppLoader_NSO final : public AppLoader, Linker { 68class AppLoader_NSO final : public AppLoader {
30public: 69public:
31 explicit AppLoader_NSO(FileSys::VirtualFile file); 70 explicit AppLoader_NSO(FileSys::VirtualFile file);
32 71
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index e0cc5175f..332c1037c 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -10,6 +10,7 @@
10#include "common/assert.h" 10#include "common/assert.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/logging/log.h" 12#include "common/logging/log.h"
13#include "common/page_table.h"
13#include "common/swap.h" 14#include "common/swap.h"
14#include "core/arm/arm_interface.h" 15#include "core/arm/arm_interface.h"
15#include "core/core.h" 16#include "core/core.h"
@@ -23,9 +24,9 @@
23 24
24namespace Memory { 25namespace Memory {
25 26
26static PageTable* current_page_table = nullptr; 27static Common::PageTable* current_page_table = nullptr;
27 28
28void SetCurrentPageTable(PageTable* page_table) { 29void SetCurrentPageTable(Common::PageTable* page_table) {
29 current_page_table = page_table; 30 current_page_table = page_table;
30 31
31 auto& system = Core::System::GetInstance(); 32 auto& system = Core::System::GetInstance();
@@ -37,39 +38,17 @@ void SetCurrentPageTable(PageTable* page_table) {
37 } 38 }
38} 39}
39 40
40PageTable* GetCurrentPageTable() { 41Common::PageTable* GetCurrentPageTable() {
41 return current_page_table; 42 return current_page_table;
42} 43}
43 44
44PageTable::PageTable() = default; 45static void MapPages(Common::PageTable& page_table, VAddr base, u64 size, u8* memory,
45 46 Common::PageType type) {
46PageTable::PageTable(std::size_t address_space_width_in_bits) {
47 Resize(address_space_width_in_bits);
48}
49
50PageTable::~PageTable() = default;
51
52void PageTable::Resize(std::size_t address_space_width_in_bits) {
53 const std::size_t num_page_table_entries = 1ULL << (address_space_width_in_bits - PAGE_BITS);
54
55 pointers.resize(num_page_table_entries);
56 attributes.resize(num_page_table_entries);
57
58 // The default is a 39-bit address space, which causes an initial 1GB allocation size. If the
59 // vector size is subsequently decreased (via resize), the vector might not automatically
60 // actually reallocate/resize its underlying allocation, which wastes up to ~800 MB for
61 // 36-bit titles. Call shrink_to_fit to reduce capacity to what's actually in use.
62
63 pointers.shrink_to_fit();
64 attributes.shrink_to_fit();
65}
66
67static void MapPages(PageTable& page_table, VAddr base, u64 size, u8* memory, PageType type) {
68 LOG_DEBUG(HW_Memory, "Mapping {} onto {:016X}-{:016X}", fmt::ptr(memory), base * PAGE_SIZE, 47 LOG_DEBUG(HW_Memory, "Mapping {} onto {:016X}-{:016X}", fmt::ptr(memory), base * PAGE_SIZE,
69 (base + size) * PAGE_SIZE); 48 (base + size) * PAGE_SIZE);
70 49
71 // During boot, current_page_table might not be set yet, in which case we need not flush 50 // During boot, current_page_table might not be set yet, in which case we need not flush
72 if (current_page_table) { 51 if (Core::System::GetInstance().IsPoweredOn()) {
73 Core::System::GetInstance().GPU().FlushAndInvalidateRegion(base << PAGE_BITS, 52 Core::System::GetInstance().GPU().FlushAndInvalidateRegion(base << PAGE_BITS,
74 size * PAGE_SIZE); 53 size * PAGE_SIZE);
75 } 54 }
@@ -92,41 +71,47 @@ static void MapPages(PageTable& page_table, VAddr base, u64 size, u8* memory, Pa
92 } 71 }
93} 72}
94 73
95void MapMemoryRegion(PageTable& page_table, VAddr base, u64 size, u8* target) { 74void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, u8* target) {
96 ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size); 75 ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
97 ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base); 76 ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
98 MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, PageType::Memory); 77 MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, Common::PageType::Memory);
99} 78}
100 79
101void MapIoRegion(PageTable& page_table, VAddr base, u64 size, MemoryHookPointer mmio_handler) { 80void MapIoRegion(Common::PageTable& page_table, VAddr base, u64 size,
81 Common::MemoryHookPointer mmio_handler) {
102 ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size); 82 ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
103 ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base); 83 ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
104 MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Special); 84 MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, Common::PageType::Special);
105 85
106 auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1); 86 auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
107 SpecialRegion region{SpecialRegion::Type::IODevice, std::move(mmio_handler)}; 87 Common::SpecialRegion region{Common::SpecialRegion::Type::IODevice, std::move(mmio_handler)};
108 page_table.special_regions.add(std::make_pair(interval, std::set<SpecialRegion>{region})); 88 page_table.special_regions.add(
89 std::make_pair(interval, std::set<Common::SpecialRegion>{region}));
109} 90}
110 91
111void UnmapRegion(PageTable& page_table, VAddr base, u64 size) { 92void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) {
112 ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size); 93 ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
113 ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base); 94 ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
114 MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Unmapped); 95 MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, Common::PageType::Unmapped);
115 96
116 auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1); 97 auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
117 page_table.special_regions.erase(interval); 98 page_table.special_regions.erase(interval);
118} 99}
119 100
120void AddDebugHook(PageTable& page_table, VAddr base, u64 size, MemoryHookPointer hook) { 101void AddDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
102 Common::MemoryHookPointer hook) {
121 auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1); 103 auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
122 SpecialRegion region{SpecialRegion::Type::DebugHook, std::move(hook)}; 104 Common::SpecialRegion region{Common::SpecialRegion::Type::DebugHook, std::move(hook)};
123 page_table.special_regions.add(std::make_pair(interval, std::set<SpecialRegion>{region})); 105 page_table.special_regions.add(
106 std::make_pair(interval, std::set<Common::SpecialRegion>{region}));
124} 107}
125 108
126void RemoveDebugHook(PageTable& page_table, VAddr base, u64 size, MemoryHookPointer hook) { 109void RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
110 Common::MemoryHookPointer hook) {
127 auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1); 111 auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
128 SpecialRegion region{SpecialRegion::Type::DebugHook, std::move(hook)}; 112 Common::SpecialRegion region{Common::SpecialRegion::Type::DebugHook, std::move(hook)};
129 page_table.special_regions.subtract(std::make_pair(interval, std::set<SpecialRegion>{region})); 113 page_table.special_regions.subtract(
114 std::make_pair(interval, std::set<Common::SpecialRegion>{region}));
130} 115}
131 116
132/** 117/**
@@ -175,15 +160,15 @@ T Read(const VAddr vaddr) {
175 return value; 160 return value;
176 } 161 }
177 162
178 PageType type = current_page_table->attributes[vaddr >> PAGE_BITS]; 163 Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
179 switch (type) { 164 switch (type) {
180 case PageType::Unmapped: 165 case Common::PageType::Unmapped:
181 LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, vaddr); 166 LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, vaddr);
182 return 0; 167 return 0;
183 case PageType::Memory: 168 case Common::PageType::Memory:
184 ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr); 169 ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);
185 break; 170 break;
186 case PageType::RasterizerCachedMemory: { 171 case Common::PageType::RasterizerCachedMemory: {
187 auto host_ptr{GetPointerFromVMA(vaddr)}; 172 auto host_ptr{GetPointerFromVMA(vaddr)};
188 Core::System::GetInstance().GPU().FlushRegion(ToCacheAddr(host_ptr), sizeof(T)); 173 Core::System::GetInstance().GPU().FlushRegion(ToCacheAddr(host_ptr), sizeof(T));
189 T value; 174 T value;
@@ -205,16 +190,16 @@ void Write(const VAddr vaddr, const T data) {
205 return; 190 return;
206 } 191 }
207 192
208 PageType type = current_page_table->attributes[vaddr >> PAGE_BITS]; 193 Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
209 switch (type) { 194 switch (type) {
210 case PageType::Unmapped: 195 case Common::PageType::Unmapped:
211 LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8, 196 LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8,
212 static_cast<u32>(data), vaddr); 197 static_cast<u32>(data), vaddr);
213 return; 198 return;
214 case PageType::Memory: 199 case Common::PageType::Memory:
215 ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr); 200 ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);
216 break; 201 break;
217 case PageType::RasterizerCachedMemory: { 202 case Common::PageType::RasterizerCachedMemory: {
218 auto host_ptr{GetPointerFromVMA(vaddr)}; 203 auto host_ptr{GetPointerFromVMA(vaddr)};
219 Core::System::GetInstance().GPU().InvalidateRegion(ToCacheAddr(host_ptr), sizeof(T)); 204 Core::System::GetInstance().GPU().InvalidateRegion(ToCacheAddr(host_ptr), sizeof(T));
220 std::memcpy(host_ptr, &data, sizeof(T)); 205 std::memcpy(host_ptr, &data, sizeof(T));
@@ -232,10 +217,10 @@ bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) {
232 if (page_pointer) 217 if (page_pointer)
233 return true; 218 return true;
234 219
235 if (page_table.attributes[vaddr >> PAGE_BITS] == PageType::RasterizerCachedMemory) 220 if (page_table.attributes[vaddr >> PAGE_BITS] == Common::PageType::RasterizerCachedMemory)
236 return true; 221 return true;
237 222
238 if (page_table.attributes[vaddr >> PAGE_BITS] != PageType::Special) 223 if (page_table.attributes[vaddr >> PAGE_BITS] != Common::PageType::Special)
239 return false; 224 return false;
240 225
241 return false; 226 return false;
@@ -255,7 +240,8 @@ u8* GetPointer(const VAddr vaddr) {
255 return page_pointer + (vaddr & PAGE_MASK); 240 return page_pointer + (vaddr & PAGE_MASK);
256 } 241 }
257 242
258 if (current_page_table->attributes[vaddr >> PAGE_BITS] == PageType::RasterizerCachedMemory) { 243 if (current_page_table->attributes[vaddr >> PAGE_BITS] ==
244 Common::PageType::RasterizerCachedMemory) {
259 return GetPointerFromVMA(vaddr); 245 return GetPointerFromVMA(vaddr);
260 } 246 }
261 247
@@ -289,20 +275,20 @@ void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
289 275
290 u64 num_pages = ((vaddr + size - 1) >> PAGE_BITS) - (vaddr >> PAGE_BITS) + 1; 276 u64 num_pages = ((vaddr + size - 1) >> PAGE_BITS) - (vaddr >> PAGE_BITS) + 1;
291 for (unsigned i = 0; i < num_pages; ++i, vaddr += PAGE_SIZE) { 277 for (unsigned i = 0; i < num_pages; ++i, vaddr += PAGE_SIZE) {
292 PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS]; 278 Common::PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS];
293 279
294 if (cached) { 280 if (cached) {
295 // Switch page type to cached if now cached 281 // Switch page type to cached if now cached
296 switch (page_type) { 282 switch (page_type) {
297 case PageType::Unmapped: 283 case Common::PageType::Unmapped:
298 // It is not necessary for a process to have this region mapped into its address 284 // It is not necessary for a process to have this region mapped into its address
299 // space, for example, a system module need not have a VRAM mapping. 285 // space, for example, a system module need not have a VRAM mapping.
300 break; 286 break;
301 case PageType::Memory: 287 case Common::PageType::Memory:
302 page_type = PageType::RasterizerCachedMemory; 288 page_type = Common::PageType::RasterizerCachedMemory;
303 current_page_table->pointers[vaddr >> PAGE_BITS] = nullptr; 289 current_page_table->pointers[vaddr >> PAGE_BITS] = nullptr;
304 break; 290 break;
305 case PageType::RasterizerCachedMemory: 291 case Common::PageType::RasterizerCachedMemory:
306 // There can be more than one GPU region mapped per CPU region, so it's common that 292 // There can be more than one GPU region mapped per CPU region, so it's common that
307 // this area is already marked as cached. 293 // this area is already marked as cached.
308 break; 294 break;
@@ -312,23 +298,23 @@ void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
312 } else { 298 } else {
313 // Switch page type to uncached if now uncached 299 // Switch page type to uncached if now uncached
314 switch (page_type) { 300 switch (page_type) {
315 case PageType::Unmapped: 301 case Common::PageType::Unmapped:
316 // It is not necessary for a process to have this region mapped into its address 302 // It is not necessary for a process to have this region mapped into its address
317 // space, for example, a system module need not have a VRAM mapping. 303 // space, for example, a system module need not have a VRAM mapping.
318 break; 304 break;
319 case PageType::Memory: 305 case Common::PageType::Memory:
320 // There can be more than one GPU region mapped per CPU region, so it's common that 306 // There can be more than one GPU region mapped per CPU region, so it's common that
321 // this area is already unmarked as cached. 307 // this area is already unmarked as cached.
322 break; 308 break;
323 case PageType::RasterizerCachedMemory: { 309 case Common::PageType::RasterizerCachedMemory: {
324 u8* pointer = GetPointerFromVMA(vaddr & ~PAGE_MASK); 310 u8* pointer = GetPointerFromVMA(vaddr & ~PAGE_MASK);
325 if (pointer == nullptr) { 311 if (pointer == nullptr) {
326 // It's possible that this function has been called while updating the pagetable 312 // It's possible that this function has been called while updating the pagetable
327 // after unmapping a VMA. In that case the underlying VMA will no longer exist, 313 // after unmapping a VMA. In that case the underlying VMA will no longer exist,
328 // and we should just leave the pagetable entry blank. 314 // and we should just leave the pagetable entry blank.
329 page_type = PageType::Unmapped; 315 page_type = Common::PageType::Unmapped;
330 } else { 316 } else {
331 page_type = PageType::Memory; 317 page_type = Common::PageType::Memory;
332 current_page_table->pointers[vaddr >> PAGE_BITS] = pointer; 318 current_page_table->pointers[vaddr >> PAGE_BITS] = pointer;
333 } 319 }
334 break; 320 break;
@@ -370,21 +356,21 @@ void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_
370 const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); 356 const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
371 357
372 switch (page_table.attributes[page_index]) { 358 switch (page_table.attributes[page_index]) {
373 case PageType::Unmapped: { 359 case Common::PageType::Unmapped: {
374 LOG_ERROR(HW_Memory, 360 LOG_ERROR(HW_Memory,
375 "Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", 361 "Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
376 current_vaddr, src_addr, size); 362 current_vaddr, src_addr, size);
377 std::memset(dest_buffer, 0, copy_amount); 363 std::memset(dest_buffer, 0, copy_amount);
378 break; 364 break;
379 } 365 }
380 case PageType::Memory: { 366 case Common::PageType::Memory: {
381 DEBUG_ASSERT(page_table.pointers[page_index]); 367 DEBUG_ASSERT(page_table.pointers[page_index]);
382 368
383 const u8* src_ptr = page_table.pointers[page_index] + page_offset; 369 const u8* src_ptr = page_table.pointers[page_index] + page_offset;
384 std::memcpy(dest_buffer, src_ptr, copy_amount); 370 std::memcpy(dest_buffer, src_ptr, copy_amount);
385 break; 371 break;
386 } 372 }
387 case PageType::RasterizerCachedMemory: { 373 case Common::PageType::RasterizerCachedMemory: {
388 const auto& host_ptr{GetPointerFromVMA(process, current_vaddr)}; 374 const auto& host_ptr{GetPointerFromVMA(process, current_vaddr)};
389 Core::System::GetInstance().GPU().FlushRegion(ToCacheAddr(host_ptr), copy_amount); 375 Core::System::GetInstance().GPU().FlushRegion(ToCacheAddr(host_ptr), copy_amount);
390 std::memcpy(dest_buffer, host_ptr, copy_amount); 376 std::memcpy(dest_buffer, host_ptr, copy_amount);
@@ -434,20 +420,20 @@ void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const voi
434 const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); 420 const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
435 421
436 switch (page_table.attributes[page_index]) { 422 switch (page_table.attributes[page_index]) {
437 case PageType::Unmapped: { 423 case Common::PageType::Unmapped: {
438 LOG_ERROR(HW_Memory, 424 LOG_ERROR(HW_Memory,
439 "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", 425 "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
440 current_vaddr, dest_addr, size); 426 current_vaddr, dest_addr, size);
441 break; 427 break;
442 } 428 }
443 case PageType::Memory: { 429 case Common::PageType::Memory: {
444 DEBUG_ASSERT(page_table.pointers[page_index]); 430 DEBUG_ASSERT(page_table.pointers[page_index]);
445 431
446 u8* dest_ptr = page_table.pointers[page_index] + page_offset; 432 u8* dest_ptr = page_table.pointers[page_index] + page_offset;
447 std::memcpy(dest_ptr, src_buffer, copy_amount); 433 std::memcpy(dest_ptr, src_buffer, copy_amount);
448 break; 434 break;
449 } 435 }
450 case PageType::RasterizerCachedMemory: { 436 case Common::PageType::RasterizerCachedMemory: {
451 const auto& host_ptr{GetPointerFromVMA(process, current_vaddr)}; 437 const auto& host_ptr{GetPointerFromVMA(process, current_vaddr)};
452 Core::System::GetInstance().GPU().InvalidateRegion(ToCacheAddr(host_ptr), copy_amount); 438 Core::System::GetInstance().GPU().InvalidateRegion(ToCacheAddr(host_ptr), copy_amount);
453 std::memcpy(host_ptr, src_buffer, copy_amount); 439 std::memcpy(host_ptr, src_buffer, copy_amount);
@@ -480,20 +466,20 @@ void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const std:
480 const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); 466 const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
481 467
482 switch (page_table.attributes[page_index]) { 468 switch (page_table.attributes[page_index]) {
483 case PageType::Unmapped: { 469 case Common::PageType::Unmapped: {
484 LOG_ERROR(HW_Memory, 470 LOG_ERROR(HW_Memory,
485 "Unmapped ZeroBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", 471 "Unmapped ZeroBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
486 current_vaddr, dest_addr, size); 472 current_vaddr, dest_addr, size);
487 break; 473 break;
488 } 474 }
489 case PageType::Memory: { 475 case Common::PageType::Memory: {
490 DEBUG_ASSERT(page_table.pointers[page_index]); 476 DEBUG_ASSERT(page_table.pointers[page_index]);
491 477
492 u8* dest_ptr = page_table.pointers[page_index] + page_offset; 478 u8* dest_ptr = page_table.pointers[page_index] + page_offset;
493 std::memset(dest_ptr, 0, copy_amount); 479 std::memset(dest_ptr, 0, copy_amount);
494 break; 480 break;
495 } 481 }
496 case PageType::RasterizerCachedMemory: { 482 case Common::PageType::RasterizerCachedMemory: {
497 const auto& host_ptr{GetPointerFromVMA(process, current_vaddr)}; 483 const auto& host_ptr{GetPointerFromVMA(process, current_vaddr)};
498 Core::System::GetInstance().GPU().InvalidateRegion(ToCacheAddr(host_ptr), copy_amount); 484 Core::System::GetInstance().GPU().InvalidateRegion(ToCacheAddr(host_ptr), copy_amount);
499 std::memset(host_ptr, 0, copy_amount); 485 std::memset(host_ptr, 0, copy_amount);
@@ -522,20 +508,20 @@ void CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr,
522 const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); 508 const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
523 509
524 switch (page_table.attributes[page_index]) { 510 switch (page_table.attributes[page_index]) {
525 case PageType::Unmapped: { 511 case Common::PageType::Unmapped: {
526 LOG_ERROR(HW_Memory, 512 LOG_ERROR(HW_Memory,
527 "Unmapped CopyBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", 513 "Unmapped CopyBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
528 current_vaddr, src_addr, size); 514 current_vaddr, src_addr, size);
529 ZeroBlock(process, dest_addr, copy_amount); 515 ZeroBlock(process, dest_addr, copy_amount);
530 break; 516 break;
531 } 517 }
532 case PageType::Memory: { 518 case Common::PageType::Memory: {
533 DEBUG_ASSERT(page_table.pointers[page_index]); 519 DEBUG_ASSERT(page_table.pointers[page_index]);
534 const u8* src_ptr = page_table.pointers[page_index] + page_offset; 520 const u8* src_ptr = page_table.pointers[page_index] + page_offset;
535 WriteBlock(process, dest_addr, src_ptr, copy_amount); 521 WriteBlock(process, dest_addr, src_ptr, copy_amount);
536 break; 522 break;
537 } 523 }
538 case PageType::RasterizerCachedMemory: { 524 case Common::PageType::RasterizerCachedMemory: {
539 const auto& host_ptr{GetPointerFromVMA(process, current_vaddr)}; 525 const auto& host_ptr{GetPointerFromVMA(process, current_vaddr)};
540 Core::System::GetInstance().GPU().FlushRegion(ToCacheAddr(host_ptr), copy_amount); 526 Core::System::GetInstance().GPU().FlushRegion(ToCacheAddr(host_ptr), copy_amount);
541 WriteBlock(process, dest_addr, host_ptr, copy_amount); 527 WriteBlock(process, dest_addr, host_ptr, copy_amount);
diff --git a/src/core/memory.h b/src/core/memory.h
index c2c6643ee..1d38cdca8 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -6,11 +6,11 @@
6 6
7#include <cstddef> 7#include <cstddef>
8#include <string> 8#include <string>
9#include <tuple>
10#include <vector>
11#include <boost/icl/interval_map.hpp>
12#include "common/common_types.h" 9#include "common/common_types.h"
13#include "core/memory_hook.h" 10
11namespace Common {
12struct PageTable;
13}
14 14
15namespace Kernel { 15namespace Kernel {
16class Process; 16class Process;
@@ -26,71 +26,6 @@ constexpr std::size_t PAGE_BITS = 12;
26constexpr u64 PAGE_SIZE = 1ULL << PAGE_BITS; 26constexpr u64 PAGE_SIZE = 1ULL << PAGE_BITS;
27constexpr u64 PAGE_MASK = PAGE_SIZE - 1; 27constexpr u64 PAGE_MASK = PAGE_SIZE - 1;
28 28
29enum class PageType : u8 {
30 /// Page is unmapped and should cause an access error.
31 Unmapped,
32 /// Page is mapped to regular memory. This is the only type you can get pointers to.
33 Memory,
34 /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
35 /// invalidation
36 RasterizerCachedMemory,
37 /// Page is mapped to a I/O region. Writing and reading to this page is handled by functions.
38 Special,
39};
40
41struct SpecialRegion {
42 enum class Type {
43 DebugHook,
44 IODevice,
45 } type;
46
47 MemoryHookPointer handler;
48
49 bool operator<(const SpecialRegion& other) const {
50 return std::tie(type, handler) < std::tie(other.type, other.handler);
51 }
52
53 bool operator==(const SpecialRegion& other) const {
54 return std::tie(type, handler) == std::tie(other.type, other.handler);
55 }
56};
57
58/**
59 * A (reasonably) fast way of allowing switchable and remappable process address spaces. It loosely
60 * mimics the way a real CPU page table works.
61 */
62struct PageTable {
63 explicit PageTable();
64 explicit PageTable(std::size_t address_space_width_in_bits);
65 ~PageTable();
66
67 /**
68 * Resizes the page table to be able to accomodate enough pages within
69 * a given address space.
70 *
71 * @param address_space_width_in_bits The address size width in bits.
72 */
73 void Resize(std::size_t address_space_width_in_bits);
74
75 /**
76 * Vector of memory pointers backing each page. An entry can only be non-null if the
77 * corresponding entry in the `attributes` vector is of type `Memory`.
78 */
79 std::vector<u8*> pointers;
80
81 /**
82 * Contains MMIO handlers that back memory regions whose entries in the `attribute` vector is
83 * of type `Special`.
84 */
85 boost::icl::interval_map<VAddr, std::set<SpecialRegion>> special_regions;
86
87 /**
88 * Vector of fine grained page attributes. If it is set to any value other than `Memory`, then
89 * the corresponding entry in `pointers` MUST be set to null.
90 */
91 std::vector<PageType> attributes;
92};
93
94/// Virtual user-space memory regions 29/// Virtual user-space memory regions
95enum : VAddr { 30enum : VAddr {
96 /// Read-only page containing kernel and system configuration values. 31 /// Read-only page containing kernel and system configuration values.
@@ -116,8 +51,8 @@ enum : VAddr {
116}; 51};
117 52
118/// Currently active page table 53/// Currently active page table
119void SetCurrentPageTable(PageTable* page_table); 54void SetCurrentPageTable(Common::PageTable* page_table);
120PageTable* GetCurrentPageTable(); 55Common::PageTable* GetCurrentPageTable();
121 56
122/// Determines if the given VAddr is valid for the specified process. 57/// Determines if the given VAddr is valid for the specified process.
123bool IsValidVirtualAddress(const Kernel::Process& process, VAddr vaddr); 58bool IsValidVirtualAddress(const Kernel::Process& process, VAddr vaddr);
diff --git a/src/core/memory_setup.h b/src/core/memory_setup.h
index 9a1a4f4be..5225ee8e2 100644
--- a/src/core/memory_setup.h
+++ b/src/core/memory_setup.h
@@ -5,7 +5,11 @@
5#pragma once 5#pragma once
6 6
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "core/memory_hook.h" 8#include "common/memory_hook.h"
9
10namespace Common {
11struct PageTable;
12}
9 13
10namespace Memory { 14namespace Memory {
11 15
@@ -17,7 +21,7 @@ namespace Memory {
17 * @param size The amount of bytes to map. Must be page-aligned. 21 * @param size The amount of bytes to map. Must be page-aligned.
18 * @param target Buffer with the memory backing the mapping. Must be of length at least `size`. 22 * @param target Buffer with the memory backing the mapping. Must be of length at least `size`.
19 */ 23 */
20void MapMemoryRegion(PageTable& page_table, VAddr base, u64 size, u8* target); 24void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, u8* target);
21 25
22/** 26/**
23 * Maps a region of the emulated process address space as a IO region. 27 * Maps a region of the emulated process address space as a IO region.
@@ -26,11 +30,14 @@ void MapMemoryRegion(PageTable& page_table, VAddr base, u64 size, u8* target);
26 * @param size The amount of bytes to map. Must be page-aligned. 30 * @param size The amount of bytes to map. Must be page-aligned.
27 * @param mmio_handler The handler that backs the mapping. 31 * @param mmio_handler The handler that backs the mapping.
28 */ 32 */
29void MapIoRegion(PageTable& page_table, VAddr base, u64 size, MemoryHookPointer mmio_handler); 33void MapIoRegion(Common::PageTable& page_table, VAddr base, u64 size,
34 Common::MemoryHookPointer mmio_handler);
30 35
31void UnmapRegion(PageTable& page_table, VAddr base, u64 size); 36void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size);
32 37
33void AddDebugHook(PageTable& page_table, VAddr base, u64 size, MemoryHookPointer hook); 38void AddDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
34void RemoveDebugHook(PageTable& page_table, VAddr base, u64 size, MemoryHookPointer hook); 39 Common::MemoryHookPointer hook);
40void RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
41 Common::MemoryHookPointer hook);
35 42
36} // namespace Memory 43} // namespace Memory
diff --git a/src/input_common/sdl/sdl.h b/src/input_common/sdl/sdl.h
index 02a8d2e2c..d7f24c68a 100644
--- a/src/input_common/sdl/sdl.h
+++ b/src/input_common/sdl/sdl.h
@@ -24,17 +24,19 @@ namespace InputCommon::SDL {
24 24
25class State { 25class State {
26public: 26public:
27 /// Unresisters SDL device factories and shut them down. 27 using Pollers = std::vector<std::unique_ptr<Polling::DevicePoller>>;
28
29 /// Unregisters SDL device factories and shut them down.
28 virtual ~State() = default; 30 virtual ~State() = default;
29 31
30 virtual std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> GetPollers( 32 virtual Pollers GetPollers(Polling::DeviceType type) = 0;
31 InputCommon::Polling::DeviceType type) = 0;
32}; 33};
33 34
34class NullState : public State { 35class NullState : public State {
35public: 36public:
36 std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> GetPollers( 37 Pollers GetPollers(Polling::DeviceType type) override {
37 InputCommon::Polling::DeviceType type) override {} 38 return {};
39 }
38}; 40};
39 41
40std::unique_ptr<State> Init(); 42std::unique_ptr<State> Init();
diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp
index 934339d3b..b132d77f5 100644
--- a/src/input_common/sdl/sdl_impl.cpp
+++ b/src/input_common/sdl/sdl_impl.cpp
@@ -475,12 +475,11 @@ SDLState::SDLState() {
475 475
476 initialized = true; 476 initialized = true;
477 if (start_thread) { 477 if (start_thread) {
478 poll_thread = std::thread([&] { 478 poll_thread = std::thread([this] {
479 using namespace std::chrono_literals; 479 using namespace std::chrono_literals;
480 SDL_Event event;
481 while (initialized) { 480 while (initialized) {
482 SDL_PumpEvents(); 481 SDL_PumpEvents();
483 std::this_thread::sleep_for(std::chrono::duration(10ms)); 482 std::this_thread::sleep_for(10ms);
484 } 483 }
485 }); 484 });
486 } 485 }
@@ -651,9 +650,9 @@ private:
651}; 650};
652} // namespace Polling 651} // namespace Polling
653 652
654std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> SDLState::GetPollers( 653SDLState::Pollers SDLState::GetPollers(InputCommon::Polling::DeviceType type) {
655 InputCommon::Polling::DeviceType type) { 654 Pollers pollers;
656 std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> pollers; 655
657 switch (type) { 656 switch (type) {
658 case InputCommon::Polling::DeviceType::Analog: 657 case InputCommon::Polling::DeviceType::Analog:
659 pollers.emplace_back(std::make_unique<Polling::SDLAnalogPoller>(*this)); 658 pollers.emplace_back(std::make_unique<Polling::SDLAnalogPoller>(*this));
@@ -661,8 +660,9 @@ std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> SDLState::GetPo
661 case InputCommon::Polling::DeviceType::Button: 660 case InputCommon::Polling::DeviceType::Button:
662 pollers.emplace_back(std::make_unique<Polling::SDLButtonPoller>(*this)); 661 pollers.emplace_back(std::make_unique<Polling::SDLButtonPoller>(*this));
663 break; 662 break;
664 return pollers;
665 } 663 }
664
665 return pollers;
666} 666}
667 667
668} // namespace SDL 668} // namespace SDL
diff --git a/src/input_common/sdl/sdl_impl.h b/src/input_common/sdl/sdl_impl.h
index fec82fbe6..2579741d6 100644
--- a/src/input_common/sdl/sdl_impl.h
+++ b/src/input_common/sdl/sdl_impl.h
@@ -25,7 +25,7 @@ public:
25 /// Initializes and registers SDL device factories 25 /// Initializes and registers SDL device factories
26 SDLState(); 26 SDLState();
27 27
28 /// Unresisters SDL device factories and shut them down. 28 /// Unregisters SDL device factories and shut them down.
29 ~SDLState() override; 29 ~SDLState() override;
30 30
31 /// Handle SDL_Events for joysticks from SDL_PollEvent 31 /// Handle SDL_Events for joysticks from SDL_PollEvent
@@ -35,8 +35,7 @@ public:
35 std::shared_ptr<SDLJoystick> GetSDLJoystickByGUID(const std::string& guid, int port); 35 std::shared_ptr<SDLJoystick> GetSDLJoystickByGUID(const std::string& guid, int port);
36 36
37 /// Get all DevicePoller that use the SDL backend for a specific device type 37 /// Get all DevicePoller that use the SDL backend for a specific device type
38 std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> GetPollers( 38 Pollers GetPollers(Polling::DeviceType type) override;
39 InputCommon::Polling::DeviceType type) override;
40 39
41 /// Used by the Pollers during config 40 /// Used by the Pollers during config
42 std::atomic<bool> polling = false; 41 std::atomic<bool> polling = false;
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index 37f09ce5f..d0284bdf4 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -1,4 +1,5 @@
1add_executable(tests 1add_executable(tests
2 common/bit_field.cpp
2 common/param_package.cpp 3 common/param_package.cpp
3 common/ring_buffer.cpp 4 common/ring_buffer.cpp
4 core/arm/arm_test_common.cpp 5 core/arm/arm_test_common.cpp
diff --git a/src/tests/common/bit_field.cpp b/src/tests/common/bit_field.cpp
new file mode 100644
index 000000000..8ca1889f9
--- /dev/null
+++ b/src/tests/common/bit_field.cpp
@@ -0,0 +1,90 @@
1// Copyright 2019 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <array>
6#include <cstring>
7#include <type_traits>
8#include <catch2/catch.hpp>
9#include "common/bit_field.h"
10
11TEST_CASE("BitField", "[common]") {
12 enum class TestEnum : u32 {
13 A = 0b10111101,
14 B = 0b10101110,
15 C = 0b00001111,
16 };
17
18 union LEBitField {
19 u32_le raw;
20 BitField<0, 6, u32> a;
21 BitField<6, 4, s32> b;
22 BitField<10, 8, TestEnum> c;
23 BitField<18, 14, u32> d;
24 } le_bitfield;
25
26 union BEBitField {
27 u32_be raw;
28 BitFieldBE<0, 6, u32> a;
29 BitFieldBE<6, 4, s32> b;
30 BitFieldBE<10, 8, TestEnum> c;
31 BitFieldBE<18, 14, u32> d;
32 } be_bitfield;
33
34 static_assert(sizeof(LEBitField) == sizeof(u32));
35 static_assert(sizeof(BEBitField) == sizeof(u32));
36 static_assert(std::is_trivially_copyable_v<LEBitField>);
37 static_assert(std::is_trivially_copyable_v<BEBitField>);
38
39 std::array<u8, 4> raw{{
40 0b01101100,
41 0b11110110,
42 0b10111010,
43 0b11101100,
44 }};
45
46 std::memcpy(&le_bitfield, &raw, sizeof(raw));
47 std::memcpy(&be_bitfield, &raw, sizeof(raw));
48
49 // bit fields: 11101100101110'10111101'1001'101100
50 REQUIRE(le_bitfield.raw == 0b11101100'10111010'11110110'01101100);
51 REQUIRE(le_bitfield.a == 0b101100);
52 REQUIRE(le_bitfield.b == -7); // 1001 as two's complement
53 REQUIRE(le_bitfield.c == TestEnum::A);
54 REQUIRE(le_bitfield.d == 0b11101100101110);
55
56 le_bitfield.a.Assign(0b000111);
57 le_bitfield.b.Assign(-1);
58 le_bitfield.c.Assign(TestEnum::C);
59 le_bitfield.d.Assign(0b01010101010101);
60 std::memcpy(&raw, &le_bitfield, sizeof(raw));
61 // bit fields: 01010101010101'00001111'1111'000111
62 REQUIRE(le_bitfield.raw == 0b01010101'01010100'00111111'11000111);
63 REQUIRE(raw == std::array<u8, 4>{{
64 0b11000111,
65 0b00111111,
66 0b01010100,
67 0b01010101,
68 }});
69
70 // bit fields: 01101100111101'10101110'1011'101100
71 REQUIRE(be_bitfield.raw == 0b01101100'11110110'10111010'11101100);
72 REQUIRE(be_bitfield.a == 0b101100);
73 REQUIRE(be_bitfield.b == -5); // 1011 as two's complement
74 REQUIRE(be_bitfield.c == TestEnum::B);
75 REQUIRE(be_bitfield.d == 0b01101100111101);
76
77 be_bitfield.a.Assign(0b000111);
78 be_bitfield.b.Assign(-1);
79 be_bitfield.c.Assign(TestEnum::C);
80 be_bitfield.d.Assign(0b01010101010101);
81 std::memcpy(&raw, &be_bitfield, sizeof(raw));
82 // bit fields: 01010101010101'00001111'1111'000111
83 REQUIRE(be_bitfield.raw == 0b01010101'01010100'00111111'11000111);
84 REQUIRE(raw == std::array<u8, 4>{{
85 0b01010101,
86 0b01010100,
87 0b00111111,
88 0b11000111,
89 }});
90}
diff --git a/src/tests/core/arm/arm_test_common.cpp b/src/tests/core/arm/arm_test_common.cpp
index 6fe56833d..3e1a735c3 100644
--- a/src/tests/core/arm/arm_test_common.cpp
+++ b/src/tests/core/arm/arm_test_common.cpp
@@ -4,6 +4,7 @@
4 4
5#include <algorithm> 5#include <algorithm>
6 6
7#include "common/page_table.h"
7#include "core/core.h" 8#include "core/core.h"
8#include "core/hle/kernel/process.h" 9#include "core/hle/kernel/process.h"
9#include "core/memory.h" 10#include "core/memory.h"
@@ -22,7 +23,7 @@ TestEnvironment::TestEnvironment(bool mutable_memory_)
22 std::fill(page_table->pointers.begin(), page_table->pointers.end(), nullptr); 23 std::fill(page_table->pointers.begin(), page_table->pointers.end(), nullptr);
23 page_table->special_regions.clear(); 24 page_table->special_regions.clear();
24 std::fill(page_table->attributes.begin(), page_table->attributes.end(), 25 std::fill(page_table->attributes.begin(), page_table->attributes.end(),
25 Memory::PageType::Unmapped); 26 Common::PageType::Unmapped);
26 27
27 Memory::MapIoRegion(*page_table, 0x00000000, 0x80000000, test_memory); 28 Memory::MapIoRegion(*page_table, 0x00000000, 0x80000000, test_memory);
28 Memory::MapIoRegion(*page_table, 0x80000000, 0x80000000, test_memory); 29 Memory::MapIoRegion(*page_table, 0x80000000, 0x80000000, test_memory);
diff --git a/src/tests/core/arm/arm_test_common.h b/src/tests/core/arm/arm_test_common.h
index 0b7539601..d145dbfcc 100644
--- a/src/tests/core/arm/arm_test_common.h
+++ b/src/tests/core/arm/arm_test_common.h
@@ -9,10 +9,10 @@
9#include <vector> 9#include <vector>
10 10
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/memory_hook.h"
12#include "core/hle/kernel/kernel.h" 13#include "core/hle/kernel/kernel.h"
13#include "core/memory_hook.h"
14 14
15namespace Memory { 15namespace Common {
16struct PageTable; 16struct PageTable;
17} 17}
18 18
@@ -58,7 +58,7 @@ public:
58 58
59private: 59private:
60 friend struct TestMemory; 60 friend struct TestMemory;
61 struct TestMemory final : Memory::MemoryHook { 61 struct TestMemory final : Common::MemoryHook {
62 explicit TestMemory(TestEnvironment* env_) : env(env_) {} 62 explicit TestMemory(TestEnvironment* env_) : env(env_) {}
63 TestEnvironment* env; 63 TestEnvironment* env;
64 64
@@ -86,7 +86,7 @@ private:
86 bool mutable_memory; 86 bool mutable_memory;
87 std::shared_ptr<TestMemory> test_memory; 87 std::shared_ptr<TestMemory> test_memory;
88 std::vector<WriteRecord> write_records; 88 std::vector<WriteRecord> write_records;
89 Memory::PageTable* page_table = nullptr; 89 Common::PageTable* page_table = nullptr;
90 Kernel::KernelCore kernel; 90 Kernel::KernelCore kernel;
91}; 91};
92 92
diff --git a/src/video_core/dma_pusher.h b/src/video_core/dma_pusher.h
index 27a36348c..6ab06518f 100644
--- a/src/video_core/dma_pusher.h
+++ b/src/video_core/dma_pusher.h
@@ -9,7 +9,6 @@
9 9
10#include "common/bit_field.h" 10#include "common/bit_field.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "video_core/memory_manager.h"
13 12
14namespace Tegra { 13namespace Tegra {
15 14
diff --git a/src/video_core/engines/kepler_memory.cpp b/src/video_core/engines/kepler_memory.cpp
index 0931b9626..e259bf46b 100644
--- a/src/video_core/engines/kepler_memory.cpp
+++ b/src/video_core/engines/kepler_memory.cpp
@@ -46,7 +46,7 @@ void KeplerMemory::ProcessData(u32 data) {
46 // contain a dirty surface that will have to be written back to memory. 46 // contain a dirty surface that will have to be written back to memory.
47 const GPUVAddr address{regs.dest.Address() + state.write_offset * sizeof(u32)}; 47 const GPUVAddr address{regs.dest.Address() + state.write_offset * sizeof(u32)};
48 rasterizer.InvalidateRegion(ToCacheAddr(memory_manager.GetPointer(address)), sizeof(u32)); 48 rasterizer.InvalidateRegion(ToCacheAddr(memory_manager.GetPointer(address)), sizeof(u32));
49 memory_manager.Write32(address, data); 49 memory_manager.Write<u32>(address, data);
50 50
51 system.GPU().Maxwell3D().dirty_flags.OnMemoryWrite(); 51 system.GPU().Maxwell3D().dirty_flags.OnMemoryWrite();
52 52
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index c5d5be4ef..defcfbd3f 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -307,7 +307,7 @@ void Maxwell3D::ProcessQueryGet() {
307 // Write the current query sequence to the sequence address. 307 // Write the current query sequence to the sequence address.
308 // TODO(Subv): Find out what happens if you use a long query type but mark it as a short 308 // TODO(Subv): Find out what happens if you use a long query type but mark it as a short
309 // query. 309 // query.
310 memory_manager.Write32(sequence_address, sequence); 310 memory_manager.Write<u32>(sequence_address, sequence);
311 } else { 311 } else {
312 // Write the 128-bit result structure in long mode. Note: We emulate an infinitely fast 312 // Write the 128-bit result structure in long mode. Note: We emulate an infinitely fast
313 // GPU, this command may actually take a while to complete in real hardware due to GPU 313 // GPU, this command may actually take a while to complete in real hardware due to GPU
@@ -395,7 +395,7 @@ void Maxwell3D::ProcessCBData(u32 value) {
395 395
396 u8* ptr{memory_manager.GetPointer(address)}; 396 u8* ptr{memory_manager.GetPointer(address)};
397 rasterizer.InvalidateRegion(ToCacheAddr(ptr), sizeof(u32)); 397 rasterizer.InvalidateRegion(ToCacheAddr(ptr), sizeof(u32));
398 memory_manager.Write32(address, value); 398 memory_manager.Write<u32>(address, value);
399 399
400 dirty_flags.OnMemoryWrite(); 400 dirty_flags.OnMemoryWrite();
401 401
@@ -447,7 +447,7 @@ std::vector<Texture::FullTextureInfo> Maxwell3D::GetStageTextures(Regs::ShaderSt
447 for (GPUVAddr current_texture = tex_info_buffer.address + TextureInfoOffset; 447 for (GPUVAddr current_texture = tex_info_buffer.address + TextureInfoOffset;
448 current_texture < tex_info_buffer_end; current_texture += sizeof(Texture::TextureHandle)) { 448 current_texture < tex_info_buffer_end; current_texture += sizeof(Texture::TextureHandle)) {
449 449
450 const Texture::TextureHandle tex_handle{memory_manager.Read32(current_texture)}; 450 const Texture::TextureHandle tex_handle{memory_manager.Read<u32>(current_texture)};
451 451
452 Texture::FullTextureInfo tex_info{}; 452 Texture::FullTextureInfo tex_info{};
453 // TODO(Subv): Use the shader to determine which textures are actually accessed. 453 // TODO(Subv): Use the shader to determine which textures are actually accessed.
@@ -482,7 +482,7 @@ Texture::FullTextureInfo Maxwell3D::GetStageTexture(Regs::ShaderStage stage,
482 482
483 ASSERT(tex_info_address < tex_info_buffer.address + tex_info_buffer.size); 483 ASSERT(tex_info_address < tex_info_buffer.address + tex_info_buffer.size);
484 484
485 const Texture::TextureHandle tex_handle{memory_manager.Read32(tex_info_address)}; 485 const Texture::TextureHandle tex_handle{memory_manager.Read<u32>(tex_info_address)};
486 486
487 Texture::FullTextureInfo tex_info{}; 487 Texture::FullTextureInfo tex_info{};
488 tex_info.index = static_cast<u32>(offset); 488 tex_info.index = static_cast<u32>(offset);
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index a0ded4c25..5cca5c29a 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -88,6 +88,16 @@ void MaxwellDMA::HandleCopy() {
88 auto source_ptr{memory_manager.GetPointer(source)}; 88 auto source_ptr{memory_manager.GetPointer(source)};
89 auto dst_ptr{memory_manager.GetPointer(dest)}; 89 auto dst_ptr{memory_manager.GetPointer(dest)};
90 90
91 if (!source_ptr) {
92 LOG_ERROR(HW_GPU, "source_ptr is invalid");
93 return;
94 }
95
96 if (!dst_ptr) {
97 LOG_ERROR(HW_GPU, "dst_ptr is invalid");
98 return;
99 }
100
91 const auto FlushAndInvalidate = [&](u32 src_size, u64 dst_size) { 101 const auto FlushAndInvalidate = [&](u32 src_size, u64 dst_size) {
92 // TODO(Subv): For now, manually flush the regions until we implement GPU-accelerated 102 // TODO(Subv): For now, manually flush the regions until we implement GPU-accelerated
93 // copying. 103 // copying.
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 66c690494..267a03f2d 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -12,6 +12,7 @@
12#include "video_core/engines/maxwell_3d.h" 12#include "video_core/engines/maxwell_3d.h"
13#include "video_core/engines/maxwell_dma.h" 13#include "video_core/engines/maxwell_dma.h"
14#include "video_core/gpu.h" 14#include "video_core/gpu.h"
15#include "video_core/memory_manager.h"
15#include "video_core/renderer_base.h" 16#include "video_core/renderer_base.h"
16 17
17namespace Tegra { 18namespace Tegra {
@@ -287,7 +288,7 @@ void GPU::ProcessSemaphoreTriggerMethod() {
287 block.timestamp = Core::System::GetInstance().CoreTiming().GetTicks(); 288 block.timestamp = Core::System::GetInstance().CoreTiming().GetTicks();
288 memory_manager->WriteBlock(regs.smaphore_address.SmaphoreAddress(), &block, sizeof(block)); 289 memory_manager->WriteBlock(regs.smaphore_address.SmaphoreAddress(), &block, sizeof(block));
289 } else { 290 } else {
290 const u32 word{memory_manager->Read32(regs.smaphore_address.SmaphoreAddress())}; 291 const u32 word{memory_manager->Read<u32>(regs.smaphore_address.SmaphoreAddress())};
291 if ((op == GpuSemaphoreOperation::AcquireEqual && word == regs.semaphore_sequence) || 292 if ((op == GpuSemaphoreOperation::AcquireEqual && word == regs.semaphore_sequence) ||
292 (op == GpuSemaphoreOperation::AcquireGequal && 293 (op == GpuSemaphoreOperation::AcquireGequal &&
293 static_cast<s32>(word - regs.semaphore_sequence) > 0) || 294 static_cast<s32>(word - regs.semaphore_sequence) > 0) ||
@@ -314,11 +315,11 @@ void GPU::ProcessSemaphoreTriggerMethod() {
314} 315}
315 316
316void GPU::ProcessSemaphoreRelease() { 317void GPU::ProcessSemaphoreRelease() {
317 memory_manager->Write32(regs.smaphore_address.SmaphoreAddress(), regs.semaphore_release); 318 memory_manager->Write<u32>(regs.smaphore_address.SmaphoreAddress(), regs.semaphore_release);
318} 319}
319 320
320void GPU::ProcessSemaphoreAcquire() { 321void GPU::ProcessSemaphoreAcquire() {
321 const u32 word = memory_manager->Read32(regs.smaphore_address.SmaphoreAddress()); 322 const u32 word = memory_manager->Read<u32>(regs.smaphore_address.SmaphoreAddress());
322 const auto value = regs.semaphore_acquire; 323 const auto value = regs.semaphore_acquire;
323 if (word != value) { 324 if (word != value) {
324 regs.acquire_active = true; 325 regs.acquire_active = true;
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index a14b95c30..c1830ac8d 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -9,7 +9,6 @@
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "core/hle/service/nvflinger/buffer_queue.h" 10#include "core/hle/service/nvflinger/buffer_queue.h"
11#include "video_core/dma_pusher.h" 11#include "video_core/dma_pusher.h"
12#include "video_core/memory_manager.h"
13 12
14using CacheAddr = std::uintptr_t; 13using CacheAddr = std::uintptr_t;
15inline CacheAddr ToCacheAddr(const void* host_ptr) { 14inline CacheAddr ToCacheAddr(const void* host_ptr) {
@@ -124,6 +123,8 @@ enum class EngineID {
124 MAXWELL_DMA_COPY_A = 0xB0B5, 123 MAXWELL_DMA_COPY_A = 0xB0B5,
125}; 124};
126 125
126class MemoryManager;
127
127class GPU { 128class GPU {
128public: 129public:
129 explicit GPU(Core::System& system, VideoCore::RendererBase& renderer); 130 explicit GPU(Core::System& system, VideoCore::RendererBase& renderer);
@@ -244,9 +245,8 @@ protected:
244private: 245private:
245 std::unique_ptr<Tegra::MemoryManager> memory_manager; 246 std::unique_ptr<Tegra::MemoryManager> memory_manager;
246 247
247 /// Mapping of command subchannels to their bound engine ids. 248 /// Mapping of command subchannels to their bound engine ids
248 std::array<EngineID, 8> bound_engines = {}; 249 std::array<EngineID, 8> bound_engines = {};
249
250 /// 3D engine 250 /// 3D engine
251 std::unique_ptr<Engines::Maxwell3D> maxwell_3d; 251 std::unique_ptr<Engines::Maxwell3D> maxwell_3d;
252 /// 2D engine 252 /// 2D engine
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index 8e8f36f28..e76b59842 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -5,218 +5,446 @@
5#include "common/alignment.h" 5#include "common/alignment.h"
6#include "common/assert.h" 6#include "common/assert.h"
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "core/core.h"
8#include "core/memory.h" 9#include "core/memory.h"
10#include "video_core/gpu.h"
9#include "video_core/memory_manager.h" 11#include "video_core/memory_manager.h"
12#include "video_core/rasterizer_interface.h"
13#include "video_core/renderer_base.h"
10 14
11namespace Tegra { 15namespace Tegra {
12 16
13MemoryManager::MemoryManager() { 17MemoryManager::MemoryManager() {
14 // Mark the first page as reserved, so that 0 is not a valid GPUVAddr. Otherwise, games might 18 std::fill(page_table.pointers.begin(), page_table.pointers.end(), nullptr);
15 // try to use 0 as a valid address, which is also used to mean nullptr. This fixes a bug with 19 std::fill(page_table.attributes.begin(), page_table.attributes.end(),
16 // Undertale using 0 for a render target. 20 Common::PageType::Unmapped);
17 PageSlot(0) = static_cast<u64>(PageStatus::Reserved); 21 page_table.Resize(address_space_width);
18}
19
20GPUVAddr MemoryManager::AllocateSpace(u64 size, u64 align) {
21 const std::optional<GPUVAddr> gpu_addr{FindFreeBlock(0, size, align, PageStatus::Unmapped)};
22 22
23 ASSERT_MSG(gpu_addr, "unable to find available GPU memory"); 23 // Initialize the map with a single free region covering the entire managed space.
24 VirtualMemoryArea initial_vma;
25 initial_vma.size = address_space_end;
26 vma_map.emplace(initial_vma.base, initial_vma);
24 27
25 for (u64 offset{}; offset < size; offset += PAGE_SIZE) { 28 UpdatePageTableForVMA(initial_vma);
26 VAddr& slot{PageSlot(*gpu_addr + offset)}; 29}
27 30
28 ASSERT(slot == static_cast<u64>(PageStatus::Unmapped)); 31GPUVAddr MemoryManager::AllocateSpace(u64 size, u64 align) {
32 const u64 aligned_size{Common::AlignUp(size, page_size)};
33 const GPUVAddr gpu_addr{FindFreeRegion(address_space_base, aligned_size)};
29 34
30 slot = static_cast<u64>(PageStatus::Allocated); 35 AllocateMemory(gpu_addr, 0, aligned_size);
31 }
32 36
33 return *gpu_addr; 37 return gpu_addr;
34} 38}
35 39
36GPUVAddr MemoryManager::AllocateSpace(GPUVAddr gpu_addr, u64 size, u64 align) { 40GPUVAddr MemoryManager::AllocateSpace(GPUVAddr gpu_addr, u64 size, u64 align) {
37 for (u64 offset{}; offset < size; offset += PAGE_SIZE) { 41 const u64 aligned_size{Common::AlignUp(size, page_size)};
38 VAddr& slot{PageSlot(gpu_addr + offset)};
39 42
40 ASSERT(slot == static_cast<u64>(PageStatus::Unmapped)); 43 AllocateMemory(gpu_addr, 0, aligned_size);
41
42 slot = static_cast<u64>(PageStatus::Allocated);
43 }
44 44
45 return gpu_addr; 45 return gpu_addr;
46} 46}
47 47
48GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, u64 size) { 48GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, u64 size) {
49 const std::optional<GPUVAddr> gpu_addr{FindFreeBlock(0, size, PAGE_SIZE, PageStatus::Unmapped)}; 49 const u64 aligned_size{Common::AlignUp(size, page_size)};
50 const GPUVAddr gpu_addr{FindFreeRegion(address_space_base, aligned_size)};
50 51
51 ASSERT_MSG(gpu_addr, "unable to find available GPU memory"); 52 MapBackingMemory(gpu_addr, Memory::GetPointer(cpu_addr), aligned_size, cpu_addr);
52 53
53 for (u64 offset{}; offset < size; offset += PAGE_SIZE) { 54 return gpu_addr;
54 VAddr& slot{PageSlot(*gpu_addr + offset)}; 55}
55 56
56 ASSERT(slot == static_cast<u64>(PageStatus::Unmapped)); 57GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size) {
58 ASSERT((gpu_addr & page_mask) == 0);
57 59
58 slot = cpu_addr + offset; 60 const u64 aligned_size{Common::AlignUp(size, page_size)};
59 }
60 61
61 const MappedRegion region{cpu_addr, *gpu_addr, size}; 62 MapBackingMemory(gpu_addr, Memory::GetPointer(cpu_addr), aligned_size, cpu_addr);
62 mapped_regions.push_back(region);
63 63
64 return *gpu_addr; 64 return gpu_addr;
65} 65}
66 66
67GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size) { 67GPUVAddr MemoryManager::UnmapBuffer(GPUVAddr gpu_addr, u64 size) {
68 ASSERT((gpu_addr & PAGE_MASK) == 0); 68 ASSERT((gpu_addr & page_mask) == 0);
69 69
70 if (PageSlot(gpu_addr) != static_cast<u64>(PageStatus::Allocated)) { 70 const u64 aligned_size{Common::AlignUp(size, page_size)};
71 // Page has been already mapped. In this case, we must find a new area of memory to use that 71 const CacheAddr cache_addr{ToCacheAddr(GetPointer(gpu_addr))};
72 // is different than the specified one. Super Mario Odyssey hits this scenario when changing
73 // areas, but we do not want to overwrite the old pages.
74 // TODO(bunnei): We need to write a hardware test to confirm this behavior.
75 72
76 LOG_ERROR(HW_GPU, "attempting to map addr 0x{:016X}, which is not available!", gpu_addr); 73 Core::System::GetInstance().Renderer().Rasterizer().FlushAndInvalidateRegion(cache_addr,
74 aligned_size);
75 UnmapRange(gpu_addr, aligned_size);
77 76
78 const std::optional<GPUVAddr> new_gpu_addr{ 77 return gpu_addr;
79 FindFreeBlock(gpu_addr, size, PAGE_SIZE, PageStatus::Allocated)}; 78}
80 79
81 ASSERT_MSG(new_gpu_addr, "unable to find available GPU memory"); 80GPUVAddr MemoryManager::FindFreeRegion(GPUVAddr region_start, u64 size) {
81 // Find the first Free VMA.
82 const VMAHandle vma_handle{std::find_if(vma_map.begin(), vma_map.end(), [&](const auto& vma) {
83 if (vma.second.type != VirtualMemoryArea::Type::Unmapped) {
84 return false;
85 }
82 86
83 gpu_addr = *new_gpu_addr; 87 const VAddr vma_end{vma.second.base + vma.second.size};
88 return vma_end > region_start && vma_end >= region_start + size;
89 })};
90
91 if (vma_handle == vma_map.end()) {
92 return {};
84 } 93 }
85 94
86 for (u64 offset{}; offset < size; offset += PAGE_SIZE) { 95 return std::max(region_start, vma_handle->second.base);
87 VAddr& slot{PageSlot(gpu_addr + offset)}; 96}
88 97
89 ASSERT(slot == static_cast<u64>(PageStatus::Allocated)); 98bool MemoryManager::IsAddressValid(GPUVAddr addr) const {
99 return (addr >> page_bits) < page_table.pointers.size();
100}
90 101
91 slot = cpu_addr + offset; 102std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr addr) {
103 if (!IsAddressValid(addr)) {
104 return {};
92 } 105 }
93 106
94 const MappedRegion region{cpu_addr, gpu_addr, size}; 107 VAddr cpu_addr{page_table.backing_addr[addr >> page_bits]};
95 mapped_regions.push_back(region); 108 if (cpu_addr) {
109 return cpu_addr + (addr & page_mask);
110 }
96 111
97 return gpu_addr; 112 return {};
98} 113}
99 114
100GPUVAddr MemoryManager::UnmapBuffer(GPUVAddr gpu_addr, u64 size) { 115template <typename T>
101 ASSERT((gpu_addr & PAGE_MASK) == 0); 116T MemoryManager::Read(GPUVAddr addr) {
117 if (!IsAddressValid(addr)) {
118 return {};
119 }
102 120
103 for (u64 offset{}; offset < size; offset += PAGE_SIZE) { 121 const u8* page_pointer{page_table.pointers[addr >> page_bits]};
104 VAddr& slot{PageSlot(gpu_addr + offset)}; 122 if (page_pointer) {
123 // NOTE: Avoid adding any extra logic to this fast-path block
124 T value;
125 std::memcpy(&value, &page_pointer[addr & page_mask], sizeof(T));
126 return value;
127 }
105 128
106 ASSERT(slot != static_cast<u64>(PageStatus::Allocated) && 129 switch (page_table.attributes[addr >> page_bits]) {
107 slot != static_cast<u64>(PageStatus::Unmapped)); 130 case Common::PageType::Unmapped:
131 LOG_ERROR(HW_GPU, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, addr);
132 return 0;
133 case Common::PageType::Memory:
134 ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", addr);
135 break;
136 default:
137 UNREACHABLE();
138 }
139 return {};
140}
108 141
109 slot = static_cast<u64>(PageStatus::Unmapped); 142template <typename T>
143void MemoryManager::Write(GPUVAddr addr, T data) {
144 if (!IsAddressValid(addr)) {
145 return;
110 } 146 }
111 147
112 // Delete the region mappings that are contained within the unmapped region 148 u8* page_pointer{page_table.pointers[addr >> page_bits]};
113 mapped_regions.erase(std::remove_if(mapped_regions.begin(), mapped_regions.end(), 149 if (page_pointer) {
114 [&](const MappedRegion& region) { 150 // NOTE: Avoid adding any extra logic to this fast-path block
115 return region.gpu_addr <= gpu_addr && 151 std::memcpy(&page_pointer[addr & page_mask], &data, sizeof(T));
116 region.gpu_addr + region.size < gpu_addr + size; 152 return;
117 }), 153 }
118 mapped_regions.end());
119 return gpu_addr;
120}
121 154
122GPUVAddr MemoryManager::GetRegionEnd(GPUVAddr region_start) const { 155 switch (page_table.attributes[addr >> page_bits]) {
123 for (const auto& region : mapped_regions) { 156 case Common::PageType::Unmapped:
124 const GPUVAddr region_end{region.gpu_addr + region.size}; 157 LOG_ERROR(HW_GPU, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8,
125 if (region_start >= region.gpu_addr && region_start < region_end) { 158 static_cast<u32>(data), addr);
126 return region_end; 159 return;
127 } 160 case Common::PageType::Memory:
161 ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", addr);
162 break;
163 default:
164 UNREACHABLE();
128 } 165 }
129 return {};
130} 166}
131 167
132std::optional<GPUVAddr> MemoryManager::FindFreeBlock(GPUVAddr region_start, u64 size, u64 align, 168template u8 MemoryManager::Read<u8>(GPUVAddr addr);
133 PageStatus status) { 169template u16 MemoryManager::Read<u16>(GPUVAddr addr);
134 GPUVAddr gpu_addr{region_start}; 170template u32 MemoryManager::Read<u32>(GPUVAddr addr);
135 u64 free_space{}; 171template u64 MemoryManager::Read<u64>(GPUVAddr addr);
136 align = (align + PAGE_MASK) & ~PAGE_MASK; 172template void MemoryManager::Write<u8>(GPUVAddr addr, u8 data);
137 173template void MemoryManager::Write<u16>(GPUVAddr addr, u16 data);
138 while (gpu_addr + free_space < MAX_ADDRESS) { 174template void MemoryManager::Write<u32>(GPUVAddr addr, u32 data);
139 if (PageSlot(gpu_addr + free_space) == static_cast<u64>(status)) { 175template void MemoryManager::Write<u64>(GPUVAddr addr, u64 data);
140 free_space += PAGE_SIZE; 176
141 if (free_space >= size) { 177u8* MemoryManager::GetPointer(GPUVAddr addr) {
142 return gpu_addr; 178 if (!IsAddressValid(addr)) {
143 } 179 return {};
144 } else {
145 gpu_addr += free_space + PAGE_SIZE;
146 free_space = 0;
147 gpu_addr = Common::AlignUp(gpu_addr, align);
148 }
149 } 180 }
150 181
182 u8* page_pointer{page_table.pointers[addr >> page_bits]};
183 if (page_pointer) {
184 return page_pointer + (addr & page_mask);
185 }
186
187 LOG_ERROR(HW_GPU, "Unknown GetPointer @ 0x{:016X}", addr);
151 return {}; 188 return {};
152} 189}
153 190
154std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr gpu_addr) { 191void MemoryManager::ReadBlock(GPUVAddr src_addr, void* dest_buffer, std::size_t size) {
155 const VAddr base_addr{PageSlot(gpu_addr)}; 192 std::memcpy(dest_buffer, GetPointer(src_addr), size);
193}
194void MemoryManager::WriteBlock(GPUVAddr dest_addr, const void* src_buffer, std::size_t size) {
195 std::memcpy(GetPointer(dest_addr), src_buffer, size);
196}
197
198void MemoryManager::CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, std::size_t size) {
199 std::memcpy(GetPointer(dest_addr), GetPointer(src_addr), size);
200}
156 201
157 if (base_addr == static_cast<u64>(PageStatus::Allocated) || 202void MemoryManager::MapPages(GPUVAddr base, u64 size, u8* memory, Common::PageType type,
158 base_addr == static_cast<u64>(PageStatus::Unmapped) || 203 VAddr backing_addr) {
159 base_addr == static_cast<u64>(PageStatus::Reserved)) { 204 LOG_DEBUG(HW_GPU, "Mapping {} onto {:016X}-{:016X}", fmt::ptr(memory), base * page_size,
160 return {}; 205 (base + size) * page_size);
206
207 const VAddr end{base + size};
208 ASSERT_MSG(end <= page_table.pointers.size(), "out of range mapping at {:016X}",
209 base + page_table.pointers.size());
210
211 std::fill(page_table.attributes.begin() + base, page_table.attributes.begin() + end, type);
212
213 if (memory == nullptr) {
214 std::fill(page_table.pointers.begin() + base, page_table.pointers.begin() + end, memory);
215 std::fill(page_table.backing_addr.begin() + base, page_table.backing_addr.begin() + end,
216 backing_addr);
217 } else {
218 while (base != end) {
219 page_table.pointers[base] = memory;
220 page_table.backing_addr[base] = backing_addr;
221
222 base += 1;
223 memory += page_size;
224 backing_addr += page_size;
225 }
161 } 226 }
227}
162 228
163 return base_addr + (gpu_addr & PAGE_MASK); 229void MemoryManager::MapMemoryRegion(GPUVAddr base, u64 size, u8* target, VAddr backing_addr) {
230 ASSERT_MSG((size & page_mask) == 0, "non-page aligned size: {:016X}", size);
231 ASSERT_MSG((base & page_mask) == 0, "non-page aligned base: {:016X}", base);
232 MapPages(base / page_size, size / page_size, target, Common::PageType::Memory, backing_addr);
164} 233}
165 234
166u8 MemoryManager::Read8(GPUVAddr addr) { 235void MemoryManager::UnmapRegion(GPUVAddr base, u64 size) {
167 return Memory::Read8(*GpuToCpuAddress(addr)); 236 ASSERT_MSG((size & page_mask) == 0, "non-page aligned size: {:016X}", size);
237 ASSERT_MSG((base & page_mask) == 0, "non-page aligned base: {:016X}", base);
238 MapPages(base / page_size, size / page_size, nullptr, Common::PageType::Unmapped);
168} 239}
169 240
170u16 MemoryManager::Read16(GPUVAddr addr) { 241bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const {
171 return Memory::Read16(*GpuToCpuAddress(addr)); 242 ASSERT(base + size == next.base);
243 if (type != next.type) {
244 return {};
245 }
246 if (type == VirtualMemoryArea::Type::Allocated && (offset + size != next.offset)) {
247 return {};
248 }
249 if (type == VirtualMemoryArea::Type::Mapped && backing_memory + size != next.backing_memory) {
250 return {};
251 }
252 return true;
172} 253}
173 254
174u32 MemoryManager::Read32(GPUVAddr addr) { 255MemoryManager::VMAHandle MemoryManager::FindVMA(GPUVAddr target) const {
175 return Memory::Read32(*GpuToCpuAddress(addr)); 256 if (target >= address_space_end) {
257 return vma_map.end();
258 } else {
259 return std::prev(vma_map.upper_bound(target));
260 }
176} 261}
177 262
178u64 MemoryManager::Read64(GPUVAddr addr) { 263MemoryManager::VMAIter MemoryManager::Allocate(VMAIter vma_handle) {
179 return Memory::Read64(*GpuToCpuAddress(addr)); 264 VirtualMemoryArea& vma{vma_handle->second};
265
266 vma.type = VirtualMemoryArea::Type::Allocated;
267 vma.backing_addr = 0;
268 vma.backing_memory = {};
269 UpdatePageTableForVMA(vma);
270
271 return MergeAdjacent(vma_handle);
180} 272}
181 273
182void MemoryManager::Write8(GPUVAddr addr, u8 data) { 274MemoryManager::VMAHandle MemoryManager::AllocateMemory(GPUVAddr target, std::size_t offset,
183 Memory::Write8(*GpuToCpuAddress(addr), data); 275 u64 size) {
276
277 // This is the appropriately sized VMA that will turn into our allocation.
278 VMAIter vma_handle{CarveVMA(target, size)};
279 VirtualMemoryArea& vma{vma_handle->second};
280
281 ASSERT(vma.size == size);
282
283 vma.offset = offset;
284
285 return Allocate(vma_handle);
184} 286}
185 287
186void MemoryManager::Write16(GPUVAddr addr, u16 data) { 288MemoryManager::VMAHandle MemoryManager::MapBackingMemory(GPUVAddr target, u8* memory, u64 size,
187 Memory::Write16(*GpuToCpuAddress(addr), data); 289 VAddr backing_addr) {
290 // This is the appropriately sized VMA that will turn into our allocation.
291 VMAIter vma_handle{CarveVMA(target, size)};
292 VirtualMemoryArea& vma{vma_handle->second};
293
294 ASSERT(vma.size == size);
295
296 vma.type = VirtualMemoryArea::Type::Mapped;
297 vma.backing_memory = memory;
298 vma.backing_addr = backing_addr;
299 UpdatePageTableForVMA(vma);
300
301 return MergeAdjacent(vma_handle);
188} 302}
189 303
190void MemoryManager::Write32(GPUVAddr addr, u32 data) { 304void MemoryManager::UnmapRange(GPUVAddr target, u64 size) {
191 Memory::Write32(*GpuToCpuAddress(addr), data); 305 VMAIter vma{CarveVMARange(target, size)};
306 const VAddr target_end{target + size};
307 const VMAIter end{vma_map.end()};
308
309 // The comparison against the end of the range must be done using addresses since VMAs can be
310 // merged during this process, causing invalidation of the iterators.
311 while (vma != end && vma->second.base < target_end) {
312 // Unmapped ranges return to allocated state and can be reused
313 // This behavior is used by Super Mario Odyssey, Sonic Forces, and likely other games
314 vma = std::next(Allocate(vma));
315 }
316
317 ASSERT(FindVMA(target)->second.size >= size);
192} 318}
193 319
194void MemoryManager::Write64(GPUVAddr addr, u64 data) { 320MemoryManager::VMAIter MemoryManager::StripIterConstness(const VMAHandle& iter) {
195 Memory::Write64(*GpuToCpuAddress(addr), data); 321 // This uses a neat C++ trick to convert a const_iterator to a regular iterator, given
322 // non-const access to its container.
323 return vma_map.erase(iter, iter); // Erases an empty range of elements
196} 324}
197 325
198u8* MemoryManager::GetPointer(GPUVAddr addr) { 326MemoryManager::VMAIter MemoryManager::CarveVMA(GPUVAddr base, u64 size) {
199 return Memory::GetPointer(*GpuToCpuAddress(addr)); 327 ASSERT_MSG((size & page_mask) == 0, "non-page aligned size: 0x{:016X}", size);
328 ASSERT_MSG((base & page_mask) == 0, "non-page aligned base: 0x{:016X}", base);
329
330 VMAIter vma_handle{StripIterConstness(FindVMA(base))};
331 if (vma_handle == vma_map.end()) {
332 // Target address is outside the managed range
333 return {};
334 }
335
336 const VirtualMemoryArea& vma{vma_handle->second};
337 if (vma.type == VirtualMemoryArea::Type::Mapped) {
338 // Region is already allocated
339 return {};
340 }
341
342 const VAddr start_in_vma{base - vma.base};
343 const VAddr end_in_vma{start_in_vma + size};
344
345 ASSERT_MSG(end_in_vma <= vma.size, "region size 0x{:016X} is less than required size 0x{:016X}",
346 vma.size, end_in_vma);
347
348 if (end_in_vma < vma.size) {
349 // Split VMA at the end of the allocated region
350 SplitVMA(vma_handle, end_in_vma);
351 }
352 if (start_in_vma != 0) {
353 // Split VMA at the start of the allocated region
354 vma_handle = SplitVMA(vma_handle, start_in_vma);
355 }
356
357 return vma_handle;
200} 358}
201 359
202void MemoryManager::ReadBlock(GPUVAddr src_addr, void* dest_buffer, std::size_t size) { 360MemoryManager::VMAIter MemoryManager::CarveVMARange(GPUVAddr target, u64 size) {
203 std::memcpy(dest_buffer, GetPointer(src_addr), size); 361 ASSERT_MSG((size & page_mask) == 0, "non-page aligned size: 0x{:016X}", size);
362 ASSERT_MSG((target & page_mask) == 0, "non-page aligned base: 0x{:016X}", target);
363
364 const VAddr target_end{target + size};
365 ASSERT(target_end >= target);
366 ASSERT(size > 0);
367
368 VMAIter begin_vma{StripIterConstness(FindVMA(target))};
369 const VMAIter i_end{vma_map.lower_bound(target_end)};
370 if (std::any_of(begin_vma, i_end, [](const auto& entry) {
371 return entry.second.type == VirtualMemoryArea::Type::Unmapped;
372 })) {
373 return {};
374 }
375
376 if (target != begin_vma->second.base) {
377 begin_vma = SplitVMA(begin_vma, target - begin_vma->second.base);
378 }
379
380 VMAIter end_vma{StripIterConstness(FindVMA(target_end))};
381 if (end_vma != vma_map.end() && target_end != end_vma->second.base) {
382 end_vma = SplitVMA(end_vma, target_end - end_vma->second.base);
383 }
384
385 return begin_vma;
204} 386}
205void MemoryManager::WriteBlock(GPUVAddr dest_addr, const void* src_buffer, std::size_t size) { 387
206 std::memcpy(GetPointer(dest_addr), src_buffer, size); 388MemoryManager::VMAIter MemoryManager::SplitVMA(VMAIter vma_handle, u64 offset_in_vma) {
389 VirtualMemoryArea& old_vma{vma_handle->second};
390 VirtualMemoryArea new_vma{old_vma}; // Make a copy of the VMA
391
392 // For now, don't allow no-op VMA splits (trying to split at a boundary) because it's probably
393 // a bug. This restriction might be removed later.
394 ASSERT(offset_in_vma < old_vma.size);
395 ASSERT(offset_in_vma > 0);
396
397 old_vma.size = offset_in_vma;
398 new_vma.base += offset_in_vma;
399 new_vma.size -= offset_in_vma;
400
401 switch (new_vma.type) {
402 case VirtualMemoryArea::Type::Unmapped:
403 break;
404 case VirtualMemoryArea::Type::Allocated:
405 new_vma.offset += offset_in_vma;
406 break;
407 case VirtualMemoryArea::Type::Mapped:
408 new_vma.backing_memory += offset_in_vma;
409 break;
410 }
411
412 ASSERT(old_vma.CanBeMergedWith(new_vma));
413
414 return vma_map.emplace_hint(std::next(vma_handle), new_vma.base, new_vma);
207} 415}
208 416
209void MemoryManager::CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, std::size_t size) { 417MemoryManager::VMAIter MemoryManager::MergeAdjacent(VMAIter iter) {
210 std::memcpy(GetPointer(dest_addr), GetPointer(src_addr), size); 418 const VMAIter next_vma{std::next(iter)};
419 if (next_vma != vma_map.end() && iter->second.CanBeMergedWith(next_vma->second)) {
420 iter->second.size += next_vma->second.size;
421 vma_map.erase(next_vma);
422 }
423
424 if (iter != vma_map.begin()) {
425 VMAIter prev_vma{std::prev(iter)};
426 if (prev_vma->second.CanBeMergedWith(iter->second)) {
427 prev_vma->second.size += iter->second.size;
428 vma_map.erase(iter);
429 iter = prev_vma;
430 }
431 }
432
433 return iter;
211} 434}
212 435
213VAddr& MemoryManager::PageSlot(GPUVAddr gpu_addr) { 436void MemoryManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) {
214 auto& block{page_table[(gpu_addr >> (PAGE_BITS + PAGE_TABLE_BITS)) & PAGE_TABLE_MASK]}; 437 switch (vma.type) {
215 if (!block) { 438 case VirtualMemoryArea::Type::Unmapped:
216 block = std::make_unique<PageBlock>(); 439 UnmapRegion(vma.base, vma.size);
217 block->fill(static_cast<VAddr>(PageStatus::Unmapped)); 440 break;
441 case VirtualMemoryArea::Type::Allocated:
442 MapMemoryRegion(vma.base, vma.size, nullptr, vma.backing_addr);
443 break;
444 case VirtualMemoryArea::Type::Mapped:
445 MapMemoryRegion(vma.base, vma.size, vma.backing_memory, vma.backing_addr);
446 break;
218 } 447 }
219 return (*block)[(gpu_addr >> PAGE_BITS) & PAGE_BLOCK_MASK];
220} 448}
221 449
222} // namespace Tegra 450} // namespace Tegra
diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h
index 425e2f31c..34744bb27 100644
--- a/src/video_core/memory_manager.h
+++ b/src/video_core/memory_manager.h
@@ -1,82 +1,148 @@
1// Copyright 2018 yuzu emulator team 1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <map>
8#include <memory>
9#include <optional> 8#include <optional>
10#include <vector>
11 9
12#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/page_table.h"
13 12
14namespace Tegra { 13namespace Tegra {
15 14
16/// Virtual addresses in the GPU's memory map are 64 bit. 15/**
17using GPUVAddr = u64; 16 * Represents a VMA in an address space. A VMA is a contiguous region of virtual addressing space
17 * with homogeneous attributes across its extents. In this particular implementation each VMA is
18 * also backed by a single host memory allocation.
19 */
20struct VirtualMemoryArea {
21 enum class Type : u8 {
22 Unmapped,
23 Allocated,
24 Mapped,
25 };
26
27 /// Virtual base address of the region.
28 GPUVAddr base{};
29 /// Size of the region.
30 u64 size{};
31 /// Memory area mapping type.
32 Type type{Type::Unmapped};
33 /// CPU memory mapped address corresponding to this memory area.
34 VAddr backing_addr{};
35 /// Offset into the backing_memory the mapping starts from.
36 std::size_t offset{};
37 /// Pointer backing this VMA.
38 u8* backing_memory{};
39
40 /// Tests if this area can be merged to the right with `next`.
41 bool CanBeMergedWith(const VirtualMemoryArea& next) const;
42};
18 43
19class MemoryManager final { 44class MemoryManager final {
20public: 45public:
21 MemoryManager(); 46 MemoryManager();
22 47
23 GPUVAddr AllocateSpace(u64 size, u64 align); 48 GPUVAddr AllocateSpace(u64 size, u64 align);
24 GPUVAddr AllocateSpace(GPUVAddr gpu_addr, u64 size, u64 align); 49 GPUVAddr AllocateSpace(GPUVAddr addr, u64 size, u64 align);
25 GPUVAddr MapBufferEx(VAddr cpu_addr, u64 size); 50 GPUVAddr MapBufferEx(VAddr cpu_addr, u64 size);
26 GPUVAddr MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size); 51 GPUVAddr MapBufferEx(VAddr cpu_addr, GPUVAddr addr, u64 size);
27 GPUVAddr UnmapBuffer(GPUVAddr gpu_addr, u64 size); 52 GPUVAddr UnmapBuffer(GPUVAddr addr, u64 size);
28 GPUVAddr GetRegionEnd(GPUVAddr region_start) const; 53 std::optional<VAddr> GpuToCpuAddress(GPUVAddr addr);
29 std::optional<VAddr> GpuToCpuAddress(GPUVAddr gpu_addr);
30
31 static constexpr u64 PAGE_BITS = 16;
32 static constexpr u64 PAGE_SIZE = 1 << PAGE_BITS;
33 static constexpr u64 PAGE_MASK = PAGE_SIZE - 1;
34 54
35 u8 Read8(GPUVAddr addr); 55 template <typename T>
36 u16 Read16(GPUVAddr addr); 56 T Read(GPUVAddr addr);
37 u32 Read32(GPUVAddr addr);
38 u64 Read64(GPUVAddr addr);
39 57
40 void Write8(GPUVAddr addr, u8 data); 58 template <typename T>
41 void Write16(GPUVAddr addr, u16 data); 59 void Write(GPUVAddr addr, T data);
42 void Write32(GPUVAddr addr, u32 data);
43 void Write64(GPUVAddr addr, u64 data);
44 60
45 u8* GetPointer(GPUVAddr vaddr); 61 u8* GetPointer(GPUVAddr addr);
46 62
47 void ReadBlock(GPUVAddr src_addr, void* dest_buffer, std::size_t size); 63 void ReadBlock(GPUVAddr src_addr, void* dest_buffer, std::size_t size);
48 void WriteBlock(GPUVAddr dest_addr, const void* src_buffer, std::size_t size); 64 void WriteBlock(GPUVAddr dest_addr, const void* src_buffer, std::size_t size);
49 void CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size); 65 void CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, std::size_t size);
50 66
51private: 67private:
52 enum class PageStatus : u64 { 68 using VMAMap = std::map<GPUVAddr, VirtualMemoryArea>;
53 Unmapped = 0xFFFFFFFFFFFFFFFFULL, 69 using VMAHandle = VMAMap::const_iterator;
54 Allocated = 0xFFFFFFFFFFFFFFFEULL, 70 using VMAIter = VMAMap::iterator;
55 Reserved = 0xFFFFFFFFFFFFFFFDULL, 71
56 }; 72 bool IsAddressValid(GPUVAddr addr) const;
57 73 void MapPages(GPUVAddr base, u64 size, u8* memory, Common::PageType type,
58 std::optional<GPUVAddr> FindFreeBlock(GPUVAddr region_start, u64 size, u64 align, 74 VAddr backing_addr = 0);
59 PageStatus status); 75 void MapMemoryRegion(GPUVAddr base, u64 size, u8* target, VAddr backing_addr);
60 VAddr& PageSlot(GPUVAddr gpu_addr); 76 void UnmapRegion(GPUVAddr base, u64 size);
61 77
62 static constexpr u64 MAX_ADDRESS{0x10000000000ULL}; 78 /// Finds the VMA in which the given address is included in, or `vma_map.end()`.
63 static constexpr u64 PAGE_TABLE_BITS{10}; 79 VMAHandle FindVMA(GPUVAddr target) const;
64 static constexpr u64 PAGE_TABLE_SIZE{1 << PAGE_TABLE_BITS}; 80
65 static constexpr u64 PAGE_TABLE_MASK{PAGE_TABLE_SIZE - 1}; 81 VMAHandle AllocateMemory(GPUVAddr target, std::size_t offset, u64 size);
66 static constexpr u64 PAGE_BLOCK_BITS{14}; 82
67 static constexpr u64 PAGE_BLOCK_SIZE{1 << PAGE_BLOCK_BITS}; 83 /**
68 static constexpr u64 PAGE_BLOCK_MASK{PAGE_BLOCK_SIZE - 1}; 84 * Maps an unmanaged host memory pointer at a given address.
69 85 *
70 using PageBlock = std::array<VAddr, PAGE_BLOCK_SIZE>; 86 * @param target The guest address to start the mapping at.
71 std::array<std::unique_ptr<PageBlock>, PAGE_TABLE_SIZE> page_table{}; 87 * @param memory The memory to be mapped.
72 88 * @param size Size of the mapping.
73 struct MappedRegion { 89 * @param state MemoryState tag to attach to the VMA.
74 VAddr cpu_addr; 90 */
75 GPUVAddr gpu_addr; 91 VMAHandle MapBackingMemory(GPUVAddr target, u8* memory, u64 size, VAddr backing_addr);
76 u64 size; 92
77 }; 93 /// Unmaps a range of addresses, splitting VMAs as necessary.
94 void UnmapRange(GPUVAddr target, u64 size);
95
96 /// Converts a VMAHandle to a mutable VMAIter.
97 VMAIter StripIterConstness(const VMAHandle& iter);
98
99 /// Marks as the specfied VMA as allocated.
100 VMAIter Allocate(VMAIter vma);
101
102 /**
103 * Carves a VMA of a specific size at the specified address by splitting Free VMAs while doing
104 * the appropriate error checking.
105 */
106 VMAIter CarveVMA(GPUVAddr base, u64 size);
107
108 /**
109 * Splits the edges of the given range of non-Free VMAs so that there is a VMA split at each
110 * end of the range.
111 */
112 VMAIter CarveVMARange(GPUVAddr base, u64 size);
113
114 /**
115 * Splits a VMA in two, at the specified offset.
116 * @returns the right side of the split, with the original iterator becoming the left side.
117 */
118 VMAIter SplitVMA(VMAIter vma, u64 offset_in_vma);
119
120 /**
121 * Checks for and merges the specified VMA with adjacent ones if possible.
122 * @returns the merged VMA or the original if no merging was possible.
123 */
124 VMAIter MergeAdjacent(VMAIter vma);
125
126 /// Updates the pages corresponding to this VMA so they match the VMA's attributes.
127 void UpdatePageTableForVMA(const VirtualMemoryArea& vma);
128
129 /// Finds a free (unmapped region) of the specified size starting at the specified address.
130 GPUVAddr FindFreeRegion(GPUVAddr region_start, u64 size);
78 131
79 std::vector<MappedRegion> mapped_regions; 132private:
133 static constexpr u64 page_bits{16};
134 static constexpr u64 page_size{1 << page_bits};
135 static constexpr u64 page_mask{page_size - 1};
136
137 /// Address space in bits, this is fairly arbitrary but sufficiently large.
138 static constexpr u32 address_space_width{39};
139 /// Start address for mapping, this is fairly arbitrary but must be non-zero.
140 static constexpr GPUVAddr address_space_base{0x100000};
141 /// End of address space, based on address space in bits.
142 static constexpr GPUVAddr address_space_end{1ULL << address_space_width};
143
144 Common::PageTable page_table{page_bits};
145 VMAMap vma_map;
80}; 146};
81 147
82} // namespace Tegra 148} // namespace Tegra
diff --git a/src/video_core/rasterizer_cache.h b/src/video_core/rasterizer_cache.h
index ecd9986a0..9fc9f3056 100644
--- a/src/video_core/rasterizer_cache.h
+++ b/src/video_core/rasterizer_cache.h
@@ -132,7 +132,7 @@ protected:
132 } 132 }
133 133
134 /// Register an object into the cache 134 /// Register an object into the cache
135 void Register(const T& object) { 135 virtual void Register(const T& object) {
136 std::lock_guard<std::recursive_mutex> lock{mutex}; 136 std::lock_guard<std::recursive_mutex> lock{mutex};
137 137
138 object->SetIsRegistered(true); 138 object->SetIsRegistered(true);
@@ -142,7 +142,7 @@ protected:
142 } 142 }
143 143
144 /// Unregisters an object from the cache 144 /// Unregisters an object from the cache
145 void Unregister(const T& object) { 145 virtual void Unregister(const T& object) {
146 std::lock_guard<std::recursive_mutex> lock{mutex}; 146 std::lock_guard<std::recursive_mutex> lock{mutex};
147 147
148 object->SetIsRegistered(false); 148 object->SetIsRegistered(false);
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h
index 76e292e87..d7b86df38 100644
--- a/src/video_core/rasterizer_interface.h
+++ b/src/video_core/rasterizer_interface.h
@@ -9,7 +9,6 @@
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "video_core/engines/fermi_2d.h" 10#include "video_core/engines/fermi_2d.h"
11#include "video_core/gpu.h" 11#include "video_core/gpu.h"
12#include "video_core/memory_manager.h"
13 12
14namespace VideoCore { 13namespace VideoCore {
15 14
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
index 5048ed6ce..f75c65825 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
@@ -21,8 +21,8 @@ CachedBufferEntry::CachedBufferEntry(VAddr cpu_addr, std::size_t size, GLintptr
21OGLBufferCache::OGLBufferCache(RasterizerOpenGL& rasterizer, std::size_t size) 21OGLBufferCache::OGLBufferCache(RasterizerOpenGL& rasterizer, std::size_t size)
22 : RasterizerCache{rasterizer}, stream_buffer(size, true) {} 22 : RasterizerCache{rasterizer}, stream_buffer(size, true) {}
23 23
24GLintptr OGLBufferCache::UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size, 24GLintptr OGLBufferCache::UploadMemory(GPUVAddr gpu_addr, std::size_t size, std::size_t alignment,
25 std::size_t alignment, bool cache) { 25 bool cache) {
26 auto& memory_manager = Core::System::GetInstance().GPU().MemoryManager(); 26 auto& memory_manager = Core::System::GetInstance().GPU().MemoryManager();
27 27
28 // Cache management is a big overhead, so only cache entries with a given size. 28 // Cache management is a big overhead, so only cache entries with a given size.
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h
index 1de1f84ae..fc33aa433 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.h
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.h
@@ -58,7 +58,7 @@ public:
58 58
59 /// Uploads data from a guest GPU address. Returns host's buffer offset where it's been 59 /// Uploads data from a guest GPU address. Returns host's buffer offset where it's been
60 /// allocated. 60 /// allocated.
61 GLintptr UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size, std::size_t alignment = 4, 61 GLintptr UploadMemory(GPUVAddr gpu_addr, std::size_t size, std::size_t alignment = 4,
62 bool cache = true); 62 bool cache = true);
63 63
64 /// Uploads from a host memory. Returns host's buffer offset where it's been allocated. 64 /// Uploads from a host memory. Returns host's buffer offset where it's been allocated.
diff --git a/src/video_core/renderer_opengl/gl_global_cache.cpp b/src/video_core/renderer_opengl/gl_global_cache.cpp
index c8dbcacbd..0fbfbad55 100644
--- a/src/video_core/renderer_opengl/gl_global_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_global_cache.cpp
@@ -46,7 +46,7 @@ GlobalRegion GlobalRegionCacheOpenGL::TryGetReservedGlobalRegion(CacheAddr addr,
46 return search->second; 46 return search->second;
47} 47}
48 48
49GlobalRegion GlobalRegionCacheOpenGL::GetUncachedGlobalRegion(Tegra::GPUVAddr addr, u32 size, 49GlobalRegion GlobalRegionCacheOpenGL::GetUncachedGlobalRegion(GPUVAddr addr, u32 size,
50 u8* host_ptr) { 50 u8* host_ptr) {
51 GlobalRegion region{TryGetReservedGlobalRegion(ToCacheAddr(host_ptr), size)}; 51 GlobalRegion region{TryGetReservedGlobalRegion(ToCacheAddr(host_ptr), size)};
52 if (!region) { 52 if (!region) {
@@ -76,8 +76,8 @@ GlobalRegion GlobalRegionCacheOpenGL::GetGlobalRegion(
76 const auto cbufs{gpu.Maxwell3D().state.shader_stages[static_cast<u64>(stage)]}; 76 const auto cbufs{gpu.Maxwell3D().state.shader_stages[static_cast<u64>(stage)]};
77 const auto addr{cbufs.const_buffers[global_region.GetCbufIndex()].address + 77 const auto addr{cbufs.const_buffers[global_region.GetCbufIndex()].address +
78 global_region.GetCbufOffset()}; 78 global_region.GetCbufOffset()};
79 const auto actual_addr{memory_manager.Read64(addr)}; 79 const auto actual_addr{memory_manager.Read<u64>(addr)};
80 const auto size{memory_manager.Read32(addr + 8)}; 80 const auto size{memory_manager.Read<u32>(addr + 8)};
81 81
82 // Look up global region in the cache based on address 82 // Look up global region in the cache based on address
83 const auto& host_ptr{memory_manager.GetPointer(actual_addr)}; 83 const auto& host_ptr{memory_manager.GetPointer(actual_addr)};
diff --git a/src/video_core/renderer_opengl/gl_global_cache.h b/src/video_core/renderer_opengl/gl_global_cache.h
index a840491f7..5a21ab66f 100644
--- a/src/video_core/renderer_opengl/gl_global_cache.h
+++ b/src/video_core/renderer_opengl/gl_global_cache.h
@@ -66,7 +66,7 @@ public:
66 66
67private: 67private:
68 GlobalRegion TryGetReservedGlobalRegion(CacheAddr addr, u32 size) const; 68 GlobalRegion TryGetReservedGlobalRegion(CacheAddr addr, u32 size) const;
69 GlobalRegion GetUncachedGlobalRegion(Tegra::GPUVAddr addr, u32 size, u8* host_ptr); 69 GlobalRegion GetUncachedGlobalRegion(GPUVAddr addr, u32 size, u8* host_ptr);
70 void ReserveGlobalRegion(GlobalRegion region); 70 void ReserveGlobalRegion(GlobalRegion region);
71 71
72 std::unordered_map<CacheAddr, GlobalRegion> reserve; 72 std::unordered_map<CacheAddr, GlobalRegion> reserve;
diff --git a/src/video_core/renderer_opengl/gl_primitive_assembler.cpp b/src/video_core/renderer_opengl/gl_primitive_assembler.cpp
index 75d816795..2bcbd3da2 100644
--- a/src/video_core/renderer_opengl/gl_primitive_assembler.cpp
+++ b/src/video_core/renderer_opengl/gl_primitive_assembler.cpp
@@ -40,8 +40,7 @@ GLintptr PrimitiveAssembler::MakeQuadArray(u32 first, u32 count) {
40 return index_offset; 40 return index_offset;
41} 41}
42 42
43GLintptr PrimitiveAssembler::MakeQuadIndexed(Tegra::GPUVAddr gpu_addr, std::size_t index_size, 43GLintptr PrimitiveAssembler::MakeQuadIndexed(GPUVAddr gpu_addr, std::size_t index_size, u32 count) {
44 u32 count) {
45 const std::size_t map_size{CalculateQuadSize(count)}; 44 const std::size_t map_size{CalculateQuadSize(count)};
46 auto [dst_pointer, index_offset] = buffer_cache.ReserveMemory(map_size); 45 auto [dst_pointer, index_offset] = buffer_cache.ReserveMemory(map_size);
47 46
diff --git a/src/video_core/renderer_opengl/gl_primitive_assembler.h b/src/video_core/renderer_opengl/gl_primitive_assembler.h
index a8cb88eb5..0e2e7dc36 100644
--- a/src/video_core/renderer_opengl/gl_primitive_assembler.h
+++ b/src/video_core/renderer_opengl/gl_primitive_assembler.h
@@ -24,7 +24,7 @@ public:
24 24
25 GLintptr MakeQuadArray(u32 first, u32 count); 25 GLintptr MakeQuadArray(u32 first, u32 count);
26 26
27 GLintptr MakeQuadIndexed(Tegra::GPUVAddr gpu_addr, std::size_t index_size, u32 count); 27 GLintptr MakeQuadIndexed(GPUVAddr gpu_addr, std::size_t index_size, u32 count);
28 28
29private: 29private:
30 OGLBufferCache& buffer_cache; 30 OGLBufferCache& buffer_cache;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 198c54872..e06dfe43f 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -225,8 +225,8 @@ void RasterizerOpenGL::SetupVertexBuffer(GLuint vao) {
225 if (!vertex_array.IsEnabled()) 225 if (!vertex_array.IsEnabled())
226 continue; 226 continue;
227 227
228 const Tegra::GPUVAddr start = vertex_array.StartAddress(); 228 const GPUVAddr start = vertex_array.StartAddress();
229 const Tegra::GPUVAddr end = regs.vertex_array_limit[index].LimitAddress(); 229 const GPUVAddr end = regs.vertex_array_limit[index].LimitAddress();
230 230
231 ASSERT(end > start); 231 ASSERT(end > start);
232 const u64 size = end - start + 1; 232 const u64 size = end - start + 1;
@@ -421,8 +421,8 @@ std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const {
421 if (!regs.vertex_array[index].IsEnabled()) 421 if (!regs.vertex_array[index].IsEnabled())
422 continue; 422 continue;
423 423
424 const Tegra::GPUVAddr start = regs.vertex_array[index].StartAddress(); 424 const GPUVAddr start = regs.vertex_array[index].StartAddress();
425 const Tegra::GPUVAddr end = regs.vertex_array_limit[index].LimitAddress(); 425 const GPUVAddr end = regs.vertex_array_limit[index].LimitAddress();
426 426
427 ASSERT(end > start); 427 ASSERT(end > start);
428 size += end - start + 1; 428 size += end - start + 1;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index 57329cd61..0235317c0 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -55,7 +55,7 @@ static void ApplyTextureDefaults(GLuint texture, u32 max_mip_level) {
55 } 55 }
56} 56}
57 57
58void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) { 58void SurfaceParams::InitCacheParameters(GPUVAddr gpu_addr_) {
59 auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()}; 59 auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()};
60 60
61 gpu_addr = gpu_addr_; 61 gpu_addr = gpu_addr_;
@@ -222,7 +222,7 @@ std::size_t SurfaceParams::InnerMemorySize(bool force_gl, bool layer_only,
222} 222}
223 223
224/*static*/ SurfaceParams SurfaceParams::CreateForDepthBuffer( 224/*static*/ SurfaceParams SurfaceParams::CreateForDepthBuffer(
225 u32 zeta_width, u32 zeta_height, Tegra::GPUVAddr zeta_address, Tegra::DepthFormat format, 225 u32 zeta_width, u32 zeta_height, GPUVAddr zeta_address, Tegra::DepthFormat format,
226 u32 block_width, u32 block_height, u32 block_depth, 226 u32 block_width, u32 block_height, u32 block_depth,
227 Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type) { 227 Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type) {
228 SurfaceParams params{}; 228 SurfaceParams params{};
@@ -564,6 +564,12 @@ void RasterizerCacheOpenGL::CopySurface(const Surface& src_surface, const Surfac
564CachedSurface::CachedSurface(const SurfaceParams& params) 564CachedSurface::CachedSurface(const SurfaceParams& params)
565 : params{params}, gl_target{SurfaceTargetToGL(params.target)}, 565 : params{params}, gl_target{SurfaceTargetToGL(params.target)},
566 cached_size_in_bytes{params.size_in_bytes}, RasterizerCacheObject{params.host_ptr} { 566 cached_size_in_bytes{params.size_in_bytes}, RasterizerCacheObject{params.host_ptr} {
567
568 const auto optional_cpu_addr{
569 Core::System::GetInstance().GPU().MemoryManager().GpuToCpuAddress(params.gpu_addr)};
570 ASSERT_MSG(optional_cpu_addr, "optional_cpu_addr is invalid");
571 cpu_addr = *optional_cpu_addr;
572
567 texture.Create(gl_target); 573 texture.Create(gl_target);
568 574
569 // TODO(Rodrigo): Using params.GetRect() returns a different size than using its Mip*(0) 575 // TODO(Rodrigo): Using params.GetRect() returns a different size than using its Mip*(0)
@@ -603,20 +609,6 @@ CachedSurface::CachedSurface(const SurfaceParams& params)
603 ApplyTextureDefaults(texture.handle, params.max_mip_level); 609 ApplyTextureDefaults(texture.handle, params.max_mip_level);
604 610
605 OpenGL::LabelGLObject(GL_TEXTURE, texture.handle, params.gpu_addr, params.IdentityString()); 611 OpenGL::LabelGLObject(GL_TEXTURE, texture.handle, params.gpu_addr, params.IdentityString());
606
607 // Clamp size to mapped GPU memory region
608 // TODO(bunnei): Super Mario Odyssey maps a 0x40000 byte region and then uses it for a 0x80000
609 // R32F render buffer. We do not yet know if this is a game bug or something else, but this
610 // check is necessary to prevent flushing from overwriting unmapped memory.
611
612 auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()};
613 const u64 max_size{memory_manager.GetRegionEnd(params.gpu_addr) - params.gpu_addr};
614 if (cached_size_in_bytes > max_size) {
615 LOG_ERROR(HW_GPU, "Surface size {} exceeds region size {}", params.size_in_bytes, max_size);
616 cached_size_in_bytes = max_size;
617 }
618
619 cpu_addr = *memory_manager.GpuToCpuAddress(params.gpu_addr);
620} 612}
621 613
622MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 192, 64)); 614MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 192, 64));
@@ -925,7 +917,7 @@ void RasterizerCacheOpenGL::LoadSurface(const Surface& surface) {
925} 917}
926 918
927Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, bool preserve_contents) { 919Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, bool preserve_contents) {
928 if (params.gpu_addr == 0 || params.height * params.width == 0) { 920 if (!params.IsValid()) {
929 return {}; 921 return {};
930 } 922 }
931 923
@@ -941,7 +933,7 @@ Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, bool pres
941 // If surface parameters changed and we care about keeping the previous data, recreate 933 // If surface parameters changed and we care about keeping the previous data, recreate
942 // the surface from the old one 934 // the surface from the old one
943 Surface new_surface{RecreateSurface(surface, params)}; 935 Surface new_surface{RecreateSurface(surface, params)};
944 UnregisterSurface(surface); 936 Unregister(surface);
945 Register(new_surface); 937 Register(new_surface);
946 if (new_surface->IsUploaded()) { 938 if (new_surface->IsUploaded()) {
947 RegisterReinterpretSurface(new_surface); 939 RegisterReinterpretSurface(new_surface);
@@ -949,7 +941,7 @@ Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, bool pres
949 return new_surface; 941 return new_surface;
950 } else { 942 } else {
951 // Delete the old surface before creating a new one to prevent collisions. 943 // Delete the old surface before creating a new one to prevent collisions.
952 UnregisterSurface(surface); 944 Unregister(surface);
953 } 945 }
954 } 946 }
955 947
@@ -980,11 +972,11 @@ void RasterizerCacheOpenGL::FastLayeredCopySurface(const Surface& src_surface,
980 const auto& init_params{src_surface->GetSurfaceParams()}; 972 const auto& init_params{src_surface->GetSurfaceParams()};
981 const auto& dst_params{dst_surface->GetSurfaceParams()}; 973 const auto& dst_params{dst_surface->GetSurfaceParams()};
982 auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()}; 974 auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()};
983 Tegra::GPUVAddr address{init_params.gpu_addr}; 975 GPUVAddr address{init_params.gpu_addr};
984 const std::size_t layer_size{dst_params.LayerMemorySize()}; 976 const std::size_t layer_size{dst_params.LayerMemorySize()};
985 for (u32 layer = 0; layer < dst_params.depth; layer++) { 977 for (u32 layer = 0; layer < dst_params.depth; layer++) {
986 for (u32 mipmap = 0; mipmap < dst_params.max_mip_level; mipmap++) { 978 for (u32 mipmap = 0; mipmap < dst_params.max_mip_level; mipmap++) {
987 const Tegra::GPUVAddr sub_address{address + dst_params.GetMipmapLevelOffset(mipmap)}; 979 const GPUVAddr sub_address{address + dst_params.GetMipmapLevelOffset(mipmap)};
988 const Surface& copy{TryGet(memory_manager.GetPointer(sub_address))}; 980 const Surface& copy{TryGet(memory_manager.GetPointer(sub_address))};
989 if (!copy) { 981 if (!copy) {
990 continue; 982 continue;
@@ -1244,10 +1236,9 @@ static std::optional<u32> TryFindBestMipMap(std::size_t memory, const SurfacePar
1244 return {}; 1236 return {};
1245} 1237}
1246 1238
1247static std::optional<u32> TryFindBestLayer(Tegra::GPUVAddr addr, const SurfaceParams params, 1239static std::optional<u32> TryFindBestLayer(GPUVAddr addr, const SurfaceParams params, u32 mipmap) {
1248 u32 mipmap) {
1249 const std::size_t size{params.LayerMemorySize()}; 1240 const std::size_t size{params.LayerMemorySize()};
1250 Tegra::GPUVAddr start{params.gpu_addr + params.GetMipmapLevelOffset(mipmap)}; 1241 GPUVAddr start{params.gpu_addr + params.GetMipmapLevelOffset(mipmap)};
1251 for (u32 i = 0; i < params.depth; i++) { 1242 for (u32 i = 0; i < params.depth; i++) {
1252 if (start == addr) { 1243 if (start == addr) {
1253 return {i}; 1244 return {i};
@@ -1304,12 +1295,12 @@ static bool IsReinterpretInvalidSecond(const Surface render_surface,
1304bool RasterizerCacheOpenGL::PartialReinterpretSurface(Surface triggering_surface, 1295bool RasterizerCacheOpenGL::PartialReinterpretSurface(Surface triggering_surface,
1305 Surface intersect) { 1296 Surface intersect) {
1306 if (IsReinterpretInvalid(triggering_surface, intersect)) { 1297 if (IsReinterpretInvalid(triggering_surface, intersect)) {
1307 UnregisterSurface(intersect); 1298 Unregister(intersect);
1308 return false; 1299 return false;
1309 } 1300 }
1310 if (!LayerFitReinterpretSurface(*this, triggering_surface, intersect)) { 1301 if (!LayerFitReinterpretSurface(*this, triggering_surface, intersect)) {
1311 if (IsReinterpretInvalidSecond(triggering_surface, intersect)) { 1302 if (IsReinterpretInvalidSecond(triggering_surface, intersect)) {
1312 UnregisterSurface(intersect); 1303 Unregister(intersect);
1313 return false; 1304 return false;
1314 } 1305 }
1315 FlushObject(intersect); 1306 FlushObject(intersect);
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index 9366f47f2..c644271d0 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -109,6 +109,11 @@ struct SurfaceParams {
109 return size; 109 return size;
110 } 110 }
111 111
112 /// Returns true if the parameters constitute a valid rasterizer surface.
113 bool IsValid() const {
114 return gpu_addr && host_ptr && height && width;
115 }
116
112 /// Returns the exact size of the memory occupied by a layer in a texture in VRAM, including 117 /// Returns the exact size of the memory occupied by a layer in a texture in VRAM, including
113 /// mipmaps. 118 /// mipmaps.
114 std::size_t LayerMemorySize() const { 119 std::size_t LayerMemorySize() const {
@@ -210,7 +215,7 @@ struct SurfaceParams {
210 215
211 /// Creates SurfaceParams for a depth buffer configuration 216 /// Creates SurfaceParams for a depth buffer configuration
212 static SurfaceParams CreateForDepthBuffer( 217 static SurfaceParams CreateForDepthBuffer(
213 u32 zeta_width, u32 zeta_height, Tegra::GPUVAddr zeta_address, Tegra::DepthFormat format, 218 u32 zeta_width, u32 zeta_height, GPUVAddr zeta_address, Tegra::DepthFormat format,
214 u32 block_width, u32 block_height, u32 block_depth, 219 u32 block_width, u32 block_height, u32 block_depth,
215 Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type); 220 Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type);
216 221
@@ -232,7 +237,7 @@ struct SurfaceParams {
232 } 237 }
233 238
234 /// Initializes parameters for caching, should be called after everything has been initialized 239 /// Initializes parameters for caching, should be called after everything has been initialized
235 void InitCacheParameters(Tegra::GPUVAddr gpu_addr); 240 void InitCacheParameters(GPUVAddr gpu_addr);
236 241
237 std::string TargetName() const { 242 std::string TargetName() const {
238 switch (target) { 243 switch (target) {
@@ -297,7 +302,7 @@ struct SurfaceParams {
297 bool srgb_conversion; 302 bool srgb_conversion;
298 // Parameters used for caching 303 // Parameters used for caching
299 u8* host_ptr; 304 u8* host_ptr;
300 Tegra::GPUVAddr gpu_addr; 305 GPUVAddr gpu_addr;
301 std::size_t size_in_bytes; 306 std::size_t size_in_bytes;
302 std::size_t size_in_bytes_gl; 307 std::size_t size_in_bytes_gl;
303 308
@@ -533,13 +538,17 @@ private:
533 return nullptr; 538 return nullptr;
534 } 539 }
535 540
541 void Register(const Surface& object) {
542 RasterizerCache<Surface>::Register(object);
543 }
544
536 /// Unregisters an object from the cache 545 /// Unregisters an object from the cache
537 void UnregisterSurface(const Surface& object) { 546 void Unregister(const Surface& object) {
538 if (object->IsReinterpreted()) { 547 if (object->IsReinterpreted()) {
539 auto interval = GetReinterpretInterval(object); 548 auto interval = GetReinterpretInterval(object);
540 reinterpreted_surfaces.erase(interval); 549 reinterpreted_surfaces.erase(interval);
541 } 550 }
542 Unregister(object); 551 RasterizerCache<Surface>::Unregister(object);
543 } 552 }
544}; 553};
545 554
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 1ed740877..1f8eca6f0 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -32,7 +32,7 @@ struct UnspecializedShader {
32namespace { 32namespace {
33 33
34/// Gets the address for the specified shader stage program 34/// Gets the address for the specified shader stage program
35Tegra::GPUVAddr GetShaderAddress(Maxwell::ShaderProgram program) { 35GPUVAddr GetShaderAddress(Maxwell::ShaderProgram program) {
36 const auto& gpu{Core::System::GetInstance().GPU().Maxwell3D()}; 36 const auto& gpu{Core::System::GetInstance().GPU().Maxwell3D()};
37 const auto& shader_config{gpu.regs.shader_config[static_cast<std::size_t>(program)]}; 37 const auto& shader_config{gpu.regs.shader_config[static_cast<std::size_t>(program)]};
38 return gpu.regs.code_address.CodeAddress() + shader_config.offset; 38 return gpu.regs.code_address.CodeAddress() + shader_config.offset;
@@ -486,7 +486,7 @@ Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
486 } 486 }
487 487
488 auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()}; 488 auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()};
489 const Tegra::GPUVAddr program_addr{GetShaderAddress(program)}; 489 const GPUVAddr program_addr{GetShaderAddress(program)};
490 490
491 // Look up shader in the cache based on address 491 // Look up shader in the cache based on address
492 const auto& host_ptr{memory_manager.GetPointer(program_addr)}; 492 const auto& host_ptr{memory_manager.GetPointer(program_addr)};
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
index 95eab3fec..eac51ecb3 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
@@ -39,8 +39,7 @@ VKBufferCache::VKBufferCache(Tegra::MemoryManager& tegra_memory_manager,
39 39
40VKBufferCache::~VKBufferCache() = default; 40VKBufferCache::~VKBufferCache() = default;
41 41
42u64 VKBufferCache::UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size, u64 alignment, 42u64 VKBufferCache::UploadMemory(GPUVAddr gpu_addr, std::size_t size, u64 alignment, bool cache) {
43 bool cache) {
44 const auto cpu_addr{tegra_memory_manager.GpuToCpuAddress(gpu_addr)}; 43 const auto cpu_addr{tegra_memory_manager.GpuToCpuAddress(gpu_addr)};
45 ASSERT_MSG(cpu_addr, "Invalid GPU address"); 44 ASSERT_MSG(cpu_addr, "Invalid GPU address");
46 45
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h
index 8b415744b..08b786aad 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.h
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h
@@ -68,8 +68,7 @@ public:
68 68
69 /// Uploads data from a guest GPU address. Returns host's buffer offset where it's been 69 /// Uploads data from a guest GPU address. Returns host's buffer offset where it's been
70 /// allocated. 70 /// allocated.
71 u64 UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size, u64 alignment = 4, 71 u64 UploadMemory(GPUVAddr gpu_addr, std::size_t size, u64 alignment = 4, bool cache = true);
72 bool cache = true);
73 72
74 /// Uploads from a host memory. Returns host's buffer offset where it's been allocated. 73 /// Uploads from a host memory. Returns host's buffer offset where it's been allocated.
75 u64 UploadHostMemory(const u8* raw_pointer, std::size_t size, u64 alignment = 4); 74 u64 UploadHostMemory(const u8* raw_pointer, std::size_t size, u64 alignment = 4);
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index d2c97b1f8..05ad19e1d 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -24,8 +24,6 @@ void EmuThread::run() {
24 24
25 MicroProfileOnThreadCreate("EmuThread"); 25 MicroProfileOnThreadCreate("EmuThread");
26 26
27 stop_run = false;
28
29 emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); 27 emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
30 28
31 Core::System::GetInstance().Renderer().Rasterizer().LoadDiskResources( 29 Core::System::GetInstance().Renderer().Rasterizer().LoadDiskResources(
@@ -40,7 +38,7 @@ void EmuThread::run() {
40 render_window->DoneCurrent(); 38 render_window->DoneCurrent();
41 } 39 }
42 40
43 // holds whether the cpu was running during the last iteration, 41 // Holds whether the cpu was running during the last iteration,
44 // so that the DebugModeLeft signal can be emitted before the 42 // so that the DebugModeLeft signal can be emitted before the
45 // next execution step 43 // next execution step
46 bool was_active = false; 44 bool was_active = false;
diff --git a/src/yuzu/debugger/graphics/graphics_surface.cpp b/src/yuzu/debugger/graphics/graphics_surface.cpp
index 29f01dfb2..11023ed63 100644
--- a/src/yuzu/debugger/graphics/graphics_surface.cpp
+++ b/src/yuzu/debugger/graphics/graphics_surface.cpp
@@ -261,7 +261,7 @@ void GraphicsSurfaceWidget::OnSurfaceSourceChanged(int new_value) {
261 261
262void GraphicsSurfaceWidget::OnSurfaceAddressChanged(qint64 new_value) { 262void GraphicsSurfaceWidget::OnSurfaceAddressChanged(qint64 new_value) {
263 if (surface_address != new_value) { 263 if (surface_address != new_value) {
264 surface_address = static_cast<Tegra::GPUVAddr>(new_value); 264 surface_address = static_cast<GPUVAddr>(new_value);
265 265
266 surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom)); 266 surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
267 emit Update(); 267 emit Update();
diff --git a/src/yuzu/debugger/graphics/graphics_surface.h b/src/yuzu/debugger/graphics/graphics_surface.h
index 323e39d94..89445b18f 100644
--- a/src/yuzu/debugger/graphics/graphics_surface.h
+++ b/src/yuzu/debugger/graphics/graphics_surface.h
@@ -87,7 +87,7 @@ private:
87 QPushButton* save_surface; 87 QPushButton* save_surface;
88 88
89 Source surface_source; 89 Source surface_source;
90 Tegra::GPUVAddr surface_address; 90 GPUVAddr surface_address;
91 unsigned surface_width; 91 unsigned surface_width;
92 unsigned surface_height; 92 unsigned surface_height;
93 Tegra::Texture::TextureFormat surface_format; 93 Tegra::Texture::TextureFormat surface_format;
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index c6c66a787..245f25847 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -114,9 +114,9 @@ int main(int argc, char** argv) {
114 }; 114 };
115 115
116 while (optind < argc) { 116 while (optind < argc) {
117 char arg = getopt_long(argc, argv, "g:fhvp::", long_options, &option_index); 117 int arg = getopt_long(argc, argv, "g:fhvp::", long_options, &option_index);
118 if (arg != -1) { 118 if (arg != -1) {
119 switch (arg) { 119 switch (static_cast<char>(arg)) {
120 case 'g': 120 case 'g':
121 errno = 0; 121 errno = 0;
122 gdb_port = strtoul(optarg, &endarg, 0); 122 gdb_port = strtoul(optarg, &endarg, 0);