diff options
Diffstat (limited to 'src')
35 files changed, 1375 insertions, 229 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 81eaf0942..b681d21a7 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -13,6 +13,8 @@ add_library(core STATIC | |||
| 13 | arm/dynarmic/arm_exclusive_monitor.h | 13 | arm/dynarmic/arm_exclusive_monitor.h |
| 14 | arm/exclusive_monitor.cpp | 14 | arm/exclusive_monitor.cpp |
| 15 | arm/exclusive_monitor.h | 15 | arm/exclusive_monitor.h |
| 16 | arm/symbols.cpp | ||
| 17 | arm/symbols.h | ||
| 16 | constants.cpp | 18 | constants.cpp |
| 17 | constants.h | 19 | constants.h |
| 18 | core.cpp | 20 | core.cpp |
| @@ -458,6 +460,8 @@ add_library(core STATIC | |||
| 458 | hle/service/hid/controllers/touchscreen.h | 460 | hle/service/hid/controllers/touchscreen.h |
| 459 | hle/service/hid/controllers/xpad.cpp | 461 | hle/service/hid/controllers/xpad.cpp |
| 460 | hle/service/hid/controllers/xpad.h | 462 | hle/service/hid/controllers/xpad.h |
| 463 | hle/service/jit/jit_context.cpp | ||
| 464 | hle/service/jit/jit_context.h | ||
| 461 | hle/service/jit/jit.cpp | 465 | hle/service/jit/jit.cpp |
| 462 | hle/service/jit/jit.h | 466 | hle/service/jit/jit.h |
| 463 | hle/service/lbl/lbl.cpp | 467 | hle/service/lbl/lbl.cpp |
diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp index 0951e1976..08bf1201d 100644 --- a/src/core/arm/arm_interface.cpp +++ b/src/core/arm/arm_interface.cpp | |||
| @@ -8,134 +8,13 @@ | |||
| 8 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 9 | #include "common/logging/log.h" | 9 | #include "common/logging/log.h" |
| 10 | #include "core/arm/arm_interface.h" | 10 | #include "core/arm/arm_interface.h" |
| 11 | #include "core/arm/symbols.h" | ||
| 11 | #include "core/core.h" | 12 | #include "core/core.h" |
| 13 | #include "core/hle/kernel/k_process.h" | ||
| 12 | #include "core/loader/loader.h" | 14 | #include "core/loader/loader.h" |
| 13 | #include "core/memory.h" | 15 | #include "core/memory.h" |
| 14 | 16 | ||
| 15 | namespace Core { | 17 | namespace Core { |
| 16 | namespace { | ||
| 17 | |||
| 18 | constexpr u64 ELF_DYNAMIC_TAG_NULL = 0; | ||
| 19 | constexpr u64 ELF_DYNAMIC_TAG_STRTAB = 5; | ||
| 20 | constexpr u64 ELF_DYNAMIC_TAG_SYMTAB = 6; | ||
| 21 | constexpr u64 ELF_DYNAMIC_TAG_SYMENT = 11; | ||
| 22 | |||
| 23 | enum class ELFSymbolType : u8 { | ||
| 24 | None = 0, | ||
| 25 | Object = 1, | ||
| 26 | Function = 2, | ||
| 27 | Section = 3, | ||
| 28 | File = 4, | ||
| 29 | Common = 5, | ||
| 30 | TLS = 6, | ||
| 31 | }; | ||
| 32 | |||
| 33 | enum class ELFSymbolBinding : u8 { | ||
| 34 | Local = 0, | ||
| 35 | Global = 1, | ||
| 36 | Weak = 2, | ||
| 37 | }; | ||
| 38 | |||
| 39 | enum class ELFSymbolVisibility : u8 { | ||
| 40 | Default = 0, | ||
| 41 | Internal = 1, | ||
| 42 | Hidden = 2, | ||
| 43 | Protected = 3, | ||
| 44 | }; | ||
| 45 | |||
| 46 | struct ELFSymbol { | ||
| 47 | u32 name_index; | ||
| 48 | union { | ||
| 49 | u8 info; | ||
| 50 | |||
| 51 | BitField<0, 4, ELFSymbolType> type; | ||
| 52 | BitField<4, 4, ELFSymbolBinding> binding; | ||
| 53 | }; | ||
| 54 | ELFSymbolVisibility visibility; | ||
| 55 | u16 sh_index; | ||
| 56 | u64 value; | ||
| 57 | u64 size; | ||
| 58 | }; | ||
| 59 | static_assert(sizeof(ELFSymbol) == 0x18, "ELFSymbol has incorrect size."); | ||
| 60 | |||
| 61 | using Symbols = std::vector<std::pair<ELFSymbol, std::string>>; | ||
| 62 | |||
| 63 | Symbols GetSymbols(VAddr text_offset, Core::Memory::Memory& memory) { | ||
| 64 | const auto mod_offset = text_offset + memory.Read32(text_offset + 4); | ||
| 65 | |||
| 66 | if (mod_offset < text_offset || (mod_offset & 0b11) != 0 || | ||
| 67 | memory.Read32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) { | ||
| 68 | return {}; | ||
| 69 | } | ||
| 70 | |||
| 71 | const auto dynamic_offset = memory.Read32(mod_offset + 0x4) + mod_offset; | ||
| 72 | |||
| 73 | VAddr string_table_offset{}; | ||
| 74 | VAddr symbol_table_offset{}; | ||
| 75 | u64 symbol_entry_size{}; | ||
| 76 | |||
| 77 | VAddr dynamic_index = dynamic_offset; | ||
| 78 | while (true) { | ||
| 79 | const u64 tag = memory.Read64(dynamic_index); | ||
| 80 | const u64 value = memory.Read64(dynamic_index + 0x8); | ||
| 81 | dynamic_index += 0x10; | ||
| 82 | |||
| 83 | if (tag == ELF_DYNAMIC_TAG_NULL) { | ||
| 84 | break; | ||
| 85 | } | ||
| 86 | |||
| 87 | if (tag == ELF_DYNAMIC_TAG_STRTAB) { | ||
| 88 | string_table_offset = value; | ||
| 89 | } else if (tag == ELF_DYNAMIC_TAG_SYMTAB) { | ||
| 90 | symbol_table_offset = value; | ||
| 91 | } else if (tag == ELF_DYNAMIC_TAG_SYMENT) { | ||
| 92 | symbol_entry_size = value; | ||
| 93 | } | ||
| 94 | } | ||
| 95 | |||
| 96 | if (string_table_offset == 0 || symbol_table_offset == 0 || symbol_entry_size == 0) { | ||
| 97 | return {}; | ||
| 98 | } | ||
| 99 | |||
| 100 | const auto string_table_address = text_offset + string_table_offset; | ||
| 101 | const auto symbol_table_address = text_offset + symbol_table_offset; | ||
| 102 | |||
| 103 | Symbols out; | ||
| 104 | |||
| 105 | VAddr symbol_index = symbol_table_address; | ||
| 106 | while (symbol_index < string_table_address) { | ||
| 107 | ELFSymbol symbol{}; | ||
| 108 | memory.ReadBlock(symbol_index, &symbol, sizeof(ELFSymbol)); | ||
| 109 | |||
| 110 | VAddr string_offset = string_table_address + symbol.name_index; | ||
| 111 | std::string name; | ||
| 112 | for (u8 c = memory.Read8(string_offset); c != 0; c = memory.Read8(++string_offset)) { | ||
| 113 | name += static_cast<char>(c); | ||
| 114 | } | ||
| 115 | |||
| 116 | symbol_index += symbol_entry_size; | ||
| 117 | out.push_back({symbol, name}); | ||
| 118 | } | ||
| 119 | |||
| 120 | return out; | ||
| 121 | } | ||
| 122 | |||
| 123 | std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr func_address) { | ||
| 124 | const auto iter = | ||
| 125 | std::find_if(symbols.begin(), symbols.end(), [func_address](const auto& pair) { | ||
| 126 | const auto& symbol = pair.first; | ||
| 127 | const auto end_address = symbol.value + symbol.size; | ||
| 128 | return func_address >= symbol.value && func_address < end_address; | ||
| 129 | }); | ||
| 130 | |||
| 131 | if (iter == symbols.end()) { | ||
| 132 | return std::nullopt; | ||
| 133 | } | ||
| 134 | |||
| 135 | return iter->second; | ||
| 136 | } | ||
| 137 | |||
| 138 | } // Anonymous namespace | ||
| 139 | 18 | ||
| 140 | constexpr u64 SEGMENT_BASE = 0x7100000000ull; | 19 | constexpr u64 SEGMENT_BASE = 0x7100000000ull; |
| 141 | 20 | ||
| @@ -169,9 +48,11 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContex | |||
| 169 | return {}; | 48 | return {}; |
| 170 | } | 49 | } |
| 171 | 50 | ||
| 172 | std::map<std::string, Symbols> symbols; | 51 | std::map<std::string, Symbols::Symbols> symbols; |
| 173 | for (const auto& module : modules) { | 52 | for (const auto& module : modules) { |
| 174 | symbols.insert_or_assign(module.second, GetSymbols(module.first, memory)); | 53 | symbols.insert_or_assign(module.second, |
| 54 | Symbols::GetSymbols(module.first, system.Memory(), | ||
| 55 | system.CurrentProcess()->Is64BitProcess())); | ||
| 175 | } | 56 | } |
| 176 | 57 | ||
| 177 | for (auto& entry : out) { | 58 | for (auto& entry : out) { |
| @@ -193,7 +74,7 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContex | |||
| 193 | 74 | ||
| 194 | const auto symbol_set = symbols.find(entry.module); | 75 | const auto symbol_set = symbols.find(entry.module); |
| 195 | if (symbol_set != symbols.end()) { | 76 | if (symbol_set != symbols.end()) { |
| 196 | const auto symbol = GetSymbolName(symbol_set->second, entry.offset); | 77 | const auto symbol = Symbols::GetSymbolName(symbol_set->second, entry.offset); |
| 197 | if (symbol.has_value()) { | 78 | if (symbol.has_value()) { |
| 198 | // TODO(DarkLordZach): Add demangling of symbol names. | 79 | // TODO(DarkLordZach): Add demangling of symbol names. |
| 199 | entry.name = *symbol; | 80 | entry.name = *symbol; |
| @@ -225,9 +106,11 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const { | |||
| 225 | return {}; | 106 | return {}; |
| 226 | } | 107 | } |
| 227 | 108 | ||
| 228 | std::map<std::string, Symbols> symbols; | 109 | std::map<std::string, Symbols::Symbols> symbols; |
| 229 | for (const auto& module : modules) { | 110 | for (const auto& module : modules) { |
| 230 | symbols.insert_or_assign(module.second, GetSymbols(module.first, memory)); | 111 | symbols.insert_or_assign(module.second, |
| 112 | Symbols::GetSymbols(module.first, system.Memory(), | ||
| 113 | system.CurrentProcess()->Is64BitProcess())); | ||
| 231 | } | 114 | } |
| 232 | 115 | ||
| 233 | for (auto& entry : out) { | 116 | for (auto& entry : out) { |
| @@ -249,7 +132,7 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const { | |||
| 249 | 132 | ||
| 250 | const auto symbol_set = symbols.find(entry.module); | 133 | const auto symbol_set = symbols.find(entry.module); |
| 251 | if (symbol_set != symbols.end()) { | 134 | if (symbol_set != symbols.end()) { |
| 252 | const auto symbol = GetSymbolName(symbol_set->second, entry.offset); | 135 | const auto symbol = Symbols::GetSymbolName(symbol_set->second, entry.offset); |
| 253 | if (symbol.has_value()) { | 136 | if (symbol.has_value()) { |
| 254 | // TODO(DarkLordZach): Add demangling of symbol names. | 137 | // TODO(DarkLordZach): Add demangling of symbol names. |
| 255 | entry.name = *symbol; | 138 | entry.name = *symbol; |
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index ab3210d84..6f3d53dad 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp | |||
| @@ -232,7 +232,7 @@ void ARM_Dynarmic_32::Run() { | |||
| 232 | if (Has(hr, svc_call)) { | 232 | if (Has(hr, svc_call)) { |
| 233 | Kernel::Svc::Call(system, svc_swi); | 233 | Kernel::Svc::Call(system, svc_swi); |
| 234 | } | 234 | } |
| 235 | if (Has(hr, break_loop)) { | 235 | if (Has(hr, break_loop) || !uses_wall_clock) { |
| 236 | break; | 236 | break; |
| 237 | } | 237 | } |
| 238 | } | 238 | } |
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 68822a1fc..1fcb2b891 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp | |||
| @@ -293,7 +293,7 @@ void ARM_Dynarmic_64::Run() { | |||
| 293 | if (Has(hr, svc_call)) { | 293 | if (Has(hr, svc_call)) { |
| 294 | Kernel::Svc::Call(system, svc_swi); | 294 | Kernel::Svc::Call(system, svc_swi); |
| 295 | } | 295 | } |
| 296 | if (Has(hr, break_loop)) { | 296 | if (Has(hr, break_loop) || !uses_wall_clock) { |
| 297 | break; | 297 | break; |
| 298 | } | 298 | } |
| 299 | } | 299 | } |
diff --git a/src/core/arm/symbols.cpp b/src/core/arm/symbols.cpp new file mode 100644 index 000000000..26c44f0c7 --- /dev/null +++ b/src/core/arm/symbols.cpp | |||
| @@ -0,0 +1,190 @@ | |||
| 1 | // Copyright 2022 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/bit_field.h" | ||
| 6 | #include "common/common_funcs.h" | ||
| 7 | #include "core/arm/symbols.h" | ||
| 8 | #include "core/core.h" | ||
| 9 | #include "core/memory.h" | ||
| 10 | |||
| 11 | namespace Core { | ||
| 12 | namespace { | ||
| 13 | |||
| 14 | constexpr u64 ELF_DYNAMIC_TAG_NULL = 0; | ||
| 15 | constexpr u64 ELF_DYNAMIC_TAG_STRTAB = 5; | ||
| 16 | constexpr u64 ELF_DYNAMIC_TAG_SYMTAB = 6; | ||
| 17 | constexpr u64 ELF_DYNAMIC_TAG_SYMENT = 11; | ||
| 18 | |||
| 19 | enum class ELFSymbolType : u8 { | ||
| 20 | None = 0, | ||
| 21 | Object = 1, | ||
| 22 | Function = 2, | ||
| 23 | Section = 3, | ||
| 24 | File = 4, | ||
| 25 | Common = 5, | ||
| 26 | TLS = 6, | ||
| 27 | }; | ||
| 28 | |||
| 29 | enum class ELFSymbolBinding : u8 { | ||
| 30 | Local = 0, | ||
| 31 | Global = 1, | ||
| 32 | Weak = 2, | ||
| 33 | }; | ||
| 34 | |||
| 35 | enum class ELFSymbolVisibility : u8 { | ||
| 36 | Default = 0, | ||
| 37 | Internal = 1, | ||
| 38 | Hidden = 2, | ||
| 39 | Protected = 3, | ||
| 40 | }; | ||
| 41 | |||
| 42 | struct ELF64Symbol { | ||
| 43 | u32 name_index; | ||
| 44 | union { | ||
| 45 | u8 info; | ||
| 46 | |||
| 47 | BitField<0, 4, ELFSymbolType> type; | ||
| 48 | BitField<4, 4, ELFSymbolBinding> binding; | ||
| 49 | }; | ||
| 50 | ELFSymbolVisibility visibility; | ||
| 51 | u16 sh_index; | ||
| 52 | u64 value; | ||
| 53 | u64 size; | ||
| 54 | }; | ||
| 55 | static_assert(sizeof(ELF64Symbol) == 0x18, "ELF64Symbol has incorrect size."); | ||
| 56 | |||
| 57 | struct ELF32Symbol { | ||
| 58 | u32 name_index; | ||
| 59 | u32 value; | ||
| 60 | u32 size; | ||
| 61 | union { | ||
| 62 | u8 info; | ||
| 63 | |||
| 64 | BitField<0, 4, ELFSymbolType> type; | ||
| 65 | BitField<4, 4, ELFSymbolBinding> binding; | ||
| 66 | }; | ||
| 67 | ELFSymbolVisibility visibility; | ||
| 68 | u16 sh_index; | ||
| 69 | }; | ||
| 70 | static_assert(sizeof(ELF32Symbol) == 0x10, "ELF32Symbol has incorrect size."); | ||
| 71 | |||
| 72 | } // Anonymous namespace | ||
| 73 | |||
| 74 | namespace Symbols { | ||
| 75 | |||
| 76 | template <typename Word, typename ELFSymbol, typename ByteReader> | ||
| 77 | static Symbols GetSymbols(ByteReader ReadBytes) { | ||
| 78 | const auto Read8{[&](u64 index) { | ||
| 79 | u8 ret; | ||
| 80 | ReadBytes(&ret, index, sizeof(u8)); | ||
| 81 | return ret; | ||
| 82 | }}; | ||
| 83 | |||
| 84 | const auto Read32{[&](u64 index) { | ||
| 85 | u32 ret; | ||
| 86 | ReadBytes(&ret, index, sizeof(u32)); | ||
| 87 | return ret; | ||
| 88 | }}; | ||
| 89 | |||
| 90 | const auto ReadWord{[&](u64 index) { | ||
| 91 | Word ret; | ||
| 92 | ReadBytes(&ret, index, sizeof(Word)); | ||
| 93 | return ret; | ||
| 94 | }}; | ||
| 95 | |||
| 96 | const u32 mod_offset = Read32(4); | ||
| 97 | |||
| 98 | if (Read32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) { | ||
| 99 | return {}; | ||
| 100 | } | ||
| 101 | |||
| 102 | VAddr string_table_offset{}; | ||
| 103 | VAddr symbol_table_offset{}; | ||
| 104 | u64 symbol_entry_size{}; | ||
| 105 | |||
| 106 | const auto dynamic_offset = Read32(mod_offset + 0x4) + mod_offset; | ||
| 107 | |||
| 108 | VAddr dynamic_index = dynamic_offset; | ||
| 109 | while (true) { | ||
| 110 | const Word tag = ReadWord(dynamic_index); | ||
| 111 | const Word value = ReadWord(dynamic_index + sizeof(Word)); | ||
| 112 | dynamic_index += 2 * sizeof(Word); | ||
| 113 | |||
| 114 | if (tag == ELF_DYNAMIC_TAG_NULL) { | ||
| 115 | break; | ||
| 116 | } | ||
| 117 | |||
| 118 | if (tag == ELF_DYNAMIC_TAG_STRTAB) { | ||
| 119 | string_table_offset = value; | ||
| 120 | } else if (tag == ELF_DYNAMIC_TAG_SYMTAB) { | ||
| 121 | symbol_table_offset = value; | ||
| 122 | } else if (tag == ELF_DYNAMIC_TAG_SYMENT) { | ||
| 123 | symbol_entry_size = value; | ||
| 124 | } | ||
| 125 | } | ||
| 126 | |||
| 127 | if (string_table_offset == 0 || symbol_table_offset == 0 || symbol_entry_size == 0) { | ||
| 128 | return {}; | ||
| 129 | } | ||
| 130 | |||
| 131 | Symbols out; | ||
| 132 | |||
| 133 | VAddr symbol_index = symbol_table_offset; | ||
| 134 | while (symbol_index < string_table_offset) { | ||
| 135 | ELFSymbol symbol{}; | ||
| 136 | ReadBytes(&symbol, symbol_index, sizeof(ELFSymbol)); | ||
| 137 | |||
| 138 | VAddr string_offset = string_table_offset + symbol.name_index; | ||
| 139 | std::string name; | ||
| 140 | for (u8 c = Read8(string_offset); c != 0; c = Read8(++string_offset)) { | ||
| 141 | name += static_cast<char>(c); | ||
| 142 | } | ||
| 143 | |||
| 144 | symbol_index += symbol_entry_size; | ||
| 145 | out[name] = std::make_pair(symbol.value, symbol.size); | ||
| 146 | } | ||
| 147 | |||
| 148 | return out; | ||
| 149 | } | ||
| 150 | |||
| 151 | Symbols GetSymbols(VAddr base, Core::Memory::Memory& memory, bool is_64) { | ||
| 152 | const auto ReadBytes{ | ||
| 153 | [&](void* ptr, size_t offset, size_t size) { memory.ReadBlock(base + offset, ptr, size); }}; | ||
| 154 | |||
| 155 | if (is_64) { | ||
| 156 | return GetSymbols<u64, ELF64Symbol>(ReadBytes); | ||
| 157 | } else { | ||
| 158 | return GetSymbols<u32, ELF32Symbol>(ReadBytes); | ||
| 159 | } | ||
| 160 | } | ||
| 161 | |||
| 162 | Symbols GetSymbols(std::span<const u8> data, bool is_64) { | ||
| 163 | const auto ReadBytes{[&](void* ptr, size_t offset, size_t size) { | ||
| 164 | std::memcpy(ptr, data.data() + offset, size); | ||
| 165 | }}; | ||
| 166 | |||
| 167 | if (is_64) { | ||
| 168 | return GetSymbols<u64, ELF64Symbol>(ReadBytes); | ||
| 169 | } else { | ||
| 170 | return GetSymbols<u32, ELF32Symbol>(ReadBytes); | ||
| 171 | } | ||
| 172 | } | ||
| 173 | |||
| 174 | std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr addr) { | ||
| 175 | const auto iter = std::find_if(symbols.cbegin(), symbols.cend(), [addr](const auto& pair) { | ||
| 176 | const auto& [name, sym_info] = pair; | ||
| 177 | const auto& [start_address, size] = sym_info; | ||
| 178 | const auto end_address = start_address + size; | ||
| 179 | return addr >= start_address && addr < end_address; | ||
| 180 | }); | ||
| 181 | |||
| 182 | if (iter == symbols.cend()) { | ||
| 183 | return std::nullopt; | ||
| 184 | } | ||
| 185 | |||
| 186 | return iter->first; | ||
| 187 | } | ||
| 188 | |||
| 189 | } // namespace Symbols | ||
| 190 | } // namespace Core | ||
diff --git a/src/core/arm/symbols.h b/src/core/arm/symbols.h new file mode 100644 index 000000000..99e6a9f8e --- /dev/null +++ b/src/core/arm/symbols.h | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | // Copyright 2022 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 <map> | ||
| 8 | #include <optional> | ||
| 9 | #include <span> | ||
| 10 | #include <string> | ||
| 11 | #include <utility> | ||
| 12 | |||
| 13 | #include "common/common_types.h" | ||
| 14 | |||
| 15 | namespace Core::Memory { | ||
| 16 | class Memory; | ||
| 17 | } // namespace Core::Memory | ||
| 18 | |||
| 19 | namespace Core::Symbols { | ||
| 20 | |||
| 21 | using Symbols = std::map<std::string, std::pair<VAddr, std::size_t>, std::less<>>; | ||
| 22 | |||
| 23 | Symbols GetSymbols(VAddr base, Core::Memory::Memory& memory, bool is_64 = true); | ||
| 24 | Symbols GetSymbols(std::span<const u8> data, bool is_64 = true); | ||
| 25 | std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr addr); | ||
| 26 | |||
| 27 | } // namespace Core::Symbols | ||
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index c4e185757..73e724f3d 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp | |||
| @@ -148,29 +148,33 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { | |||
| 148 | 148 | ||
| 149 | // LayeredExeFS | 149 | // LayeredExeFS |
| 150 | const auto load_dir = fs_controller.GetModificationLoadRoot(title_id); | 150 | const auto load_dir = fs_controller.GetModificationLoadRoot(title_id); |
| 151 | const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id); | ||
| 152 | |||
| 153 | std::vector<VirtualDir> patch_dirs = {sdmc_load_dir}; | ||
| 151 | if (load_dir != nullptr && load_dir->GetSize() > 0) { | 154 | if (load_dir != nullptr && load_dir->GetSize() > 0) { |
| 152 | auto patch_dirs = load_dir->GetSubdirectories(); | 155 | const auto load_patch_dirs = load_dir->GetSubdirectories(); |
| 153 | std::sort( | 156 | patch_dirs.insert(patch_dirs.end(), load_patch_dirs.begin(), load_patch_dirs.end()); |
| 154 | patch_dirs.begin(), patch_dirs.end(), | 157 | } |
| 155 | [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); | ||
| 156 | |||
| 157 | std::vector<VirtualDir> layers; | ||
| 158 | layers.reserve(patch_dirs.size() + 1); | ||
| 159 | for (const auto& subdir : patch_dirs) { | ||
| 160 | if (std::find(disabled.begin(), disabled.end(), subdir->GetName()) != disabled.end()) | ||
| 161 | continue; | ||
| 162 | 158 | ||
| 163 | auto exefs_dir = FindSubdirectoryCaseless(subdir, "exefs"); | 159 | std::sort(patch_dirs.begin(), patch_dirs.end(), |
| 164 | if (exefs_dir != nullptr) | 160 | [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); |
| 165 | layers.push_back(std::move(exefs_dir)); | ||
| 166 | } | ||
| 167 | layers.push_back(exefs); | ||
| 168 | 161 | ||
| 169 | auto layered = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers)); | 162 | std::vector<VirtualDir> layers; |
| 170 | if (layered != nullptr) { | 163 | layers.reserve(patch_dirs.size() + 1); |
| 171 | LOG_INFO(Loader, " ExeFS: LayeredExeFS patches applied successfully"); | 164 | for (const auto& subdir : patch_dirs) { |
| 172 | exefs = std::move(layered); | 165 | if (std::find(disabled.begin(), disabled.end(), subdir->GetName()) != disabled.end()) |
| 173 | } | 166 | continue; |
| 167 | |||
| 168 | auto exefs_dir = FindSubdirectoryCaseless(subdir, "exefs"); | ||
| 169 | if (exefs_dir != nullptr) | ||
| 170 | layers.push_back(std::move(exefs_dir)); | ||
| 171 | } | ||
| 172 | layers.push_back(exefs); | ||
| 173 | |||
| 174 | auto layered = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers)); | ||
| 175 | if (layered != nullptr) { | ||
| 176 | LOG_INFO(Loader, " ExeFS: LayeredExeFS patches applied successfully"); | ||
| 177 | exefs = std::move(layered); | ||
| 174 | } | 178 | } |
| 175 | 179 | ||
| 176 | if (Settings::values.dump_exefs) { | 180 | if (Settings::values.dump_exefs) { |
| @@ -536,11 +540,20 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u | |||
| 536 | 540 | ||
| 537 | // SDMC mod directory (RomFS LayeredFS) | 541 | // SDMC mod directory (RomFS LayeredFS) |
| 538 | const auto sdmc_mod_dir = fs_controller.GetSDMCModificationLoadRoot(title_id); | 542 | const auto sdmc_mod_dir = fs_controller.GetSDMCModificationLoadRoot(title_id); |
| 539 | if (sdmc_mod_dir != nullptr && sdmc_mod_dir->GetSize() > 0 && | 543 | if (sdmc_mod_dir != nullptr && sdmc_mod_dir->GetSize() > 0) { |
| 540 | IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "romfs"))) { | 544 | std::string types; |
| 541 | const auto mod_disabled = | 545 | if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "exefs"))) { |
| 542 | std::find(disabled.begin(), disabled.end(), "SDMC") != disabled.end(); | 546 | AppendCommaIfNotEmpty(types, "LayeredExeFS"); |
| 543 | out.insert_or_assign(mod_disabled ? "[D] SDMC" : "SDMC", "LayeredFS"); | 547 | } |
| 548 | if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "romfs"))) { | ||
| 549 | AppendCommaIfNotEmpty(types, "LayeredFS"); | ||
| 550 | } | ||
| 551 | |||
| 552 | if (!types.empty()) { | ||
| 553 | const auto mod_disabled = | ||
| 554 | std::find(disabled.begin(), disabled.end(), "SDMC") != disabled.end(); | ||
| 555 | out.insert_or_assign(mod_disabled ? "[D] SDMC" : "SDMC", types); | ||
| 556 | } | ||
| 544 | } | 557 | } |
| 545 | 558 | ||
| 546 | // DLC | 559 | // DLC |
diff --git a/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp b/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp index 8027bec00..7765e7848 100644 --- a/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp +++ b/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp | |||
| @@ -148,9 +148,9 @@ u64 GenerateUniformRange(u64 min, u64 max, F f) { | |||
| 148 | } // Anonymous namespace | 148 | } // Anonymous namespace |
| 149 | 149 | ||
| 150 | u64 KSystemControl::GenerateRandomU64() { | 150 | u64 KSystemControl::GenerateRandomU64() { |
| 151 | static std::random_device device; | 151 | std::random_device device; |
| 152 | static std::mt19937 gen(device()); | 152 | std::mt19937 gen(device()); |
| 153 | static std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max()); | 153 | std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max()); |
| 154 | return distribution(gen); | 154 | return distribution(gen); |
| 155 | } | 155 | } |
| 156 | 156 | ||
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index b547a3463..5828ac923 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp | |||
| @@ -51,7 +51,7 @@ bool SessionRequestManager::HasSessionRequestHandler(const HLERequestContext& co | |||
| 51 | LOG_CRITICAL(IPC, "object_id {} is too big!", object_id); | 51 | LOG_CRITICAL(IPC, "object_id {} is too big!", object_id); |
| 52 | return false; | 52 | return false; |
| 53 | } | 53 | } |
| 54 | return DomainHandler(object_id - 1).lock() != nullptr; | 54 | return !DomainHandler(object_id - 1).expired(); |
| 55 | } else { | 55 | } else { |
| 56 | return session_handler != nullptr; | 56 | return session_handler != nullptr; |
| 57 | } | 57 | } |
| @@ -59,6 +59,9 @@ bool SessionRequestManager::HasSessionRequestHandler(const HLERequestContext& co | |||
| 59 | 59 | ||
| 60 | void SessionRequestHandler::ClientConnected(KServerSession* session) { | 60 | void SessionRequestHandler::ClientConnected(KServerSession* session) { |
| 61 | session->ClientConnected(shared_from_this()); | 61 | session->ClientConnected(shared_from_this()); |
| 62 | |||
| 63 | // Ensure our server session is tracked globally. | ||
| 64 | kernel.RegisterServerObject(session); | ||
| 62 | } | 65 | } |
| 63 | 66 | ||
| 64 | void SessionRequestHandler::ClientDisconnected(KServerSession* session) { | 67 | void SessionRequestHandler::ClientDisconnected(KServerSession* session) { |
diff --git a/src/core/hle/kernel/k_auto_object.h b/src/core/hle/kernel/k_auto_object.h index 05779f2d5..423e8d8f5 100644 --- a/src/core/hle/kernel/k_auto_object.h +++ b/src/core/hle/kernel/k_auto_object.h | |||
| @@ -89,9 +89,7 @@ public: | |||
| 89 | explicit KAutoObject(KernelCore& kernel_) : kernel(kernel_) { | 89 | explicit KAutoObject(KernelCore& kernel_) : kernel(kernel_) { |
| 90 | RegisterWithKernel(); | 90 | RegisterWithKernel(); |
| 91 | } | 91 | } |
| 92 | virtual ~KAutoObject() { | 92 | virtual ~KAutoObject() = default; |
| 93 | UnregisterWithKernel(); | ||
| 94 | } | ||
| 95 | 93 | ||
| 96 | static KAutoObject* Create(KAutoObject* ptr); | 94 | static KAutoObject* Create(KAutoObject* ptr); |
| 97 | 95 | ||
| @@ -163,11 +161,12 @@ public: | |||
| 163 | do { | 161 | do { |
| 164 | ASSERT(cur_ref_count > 0); | 162 | ASSERT(cur_ref_count > 0); |
| 165 | } while (!m_ref_count.compare_exchange_weak(cur_ref_count, cur_ref_count - 1, | 163 | } while (!m_ref_count.compare_exchange_weak(cur_ref_count, cur_ref_count - 1, |
| 166 | std::memory_order_relaxed)); | 164 | std::memory_order_acq_rel)); |
| 167 | 165 | ||
| 168 | // If ref count hits zero, destroy the object. | 166 | // If ref count hits zero, destroy the object. |
| 169 | if (cur_ref_count - 1 == 0) { | 167 | if (cur_ref_count - 1 == 0) { |
| 170 | this->Destroy(); | 168 | this->Destroy(); |
| 169 | this->UnregisterWithKernel(); | ||
| 171 | } | 170 | } |
| 172 | } | 171 | } |
| 173 | 172 | ||
diff --git a/src/core/hle/kernel/k_code_memory.cpp b/src/core/hle/kernel/k_code_memory.cpp index 63bbe02e9..09eaf004c 100644 --- a/src/core/hle/kernel/k_code_memory.cpp +++ b/src/core/hle/kernel/k_code_memory.cpp | |||
| @@ -35,9 +35,14 @@ ResultCode KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr | |||
| 35 | R_TRY(page_table.LockForCodeMemory(addr, size)) | 35 | R_TRY(page_table.LockForCodeMemory(addr, size)) |
| 36 | 36 | ||
| 37 | // Clear the memory. | 37 | // Clear the memory. |
| 38 | for (const auto& block : m_page_group.Nodes()) { | 38 | // |
| 39 | std::memset(device_memory.GetPointer(block.GetAddress()), 0xFF, block.GetSize()); | 39 | // FIXME: this ends up clobbering address ranges outside the scope of the mapping within |
| 40 | } | 40 | // guest memory, and is not specifically required if the guest program is correctly |
| 41 | // written, so disable until this is further investigated. | ||
| 42 | // | ||
| 43 | // for (const auto& block : m_page_group.Nodes()) { | ||
| 44 | // std::memset(device_memory.GetPointer(block.GetAddress()), 0xFF, block.GetSize()); | ||
| 45 | // } | ||
| 41 | 46 | ||
| 42 | // Set remaining tracking members. | 47 | // Set remaining tracking members. |
| 43 | m_address = addr; | 48 | m_address = addr; |
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp index 599013cf6..47ea3c89c 100644 --- a/src/core/hle/kernel/k_page_table.cpp +++ b/src/core/hle/kernel/k_page_table.cpp | |||
| @@ -346,7 +346,8 @@ ResultCode KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, std:: | |||
| 346 | return ResultSuccess; | 346 | return ResultSuccess; |
| 347 | } | 347 | } |
| 348 | 348 | ||
| 349 | ResultCode KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size) { | 349 | ResultCode KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size, |
| 350 | ICacheInvalidationStrategy icache_invalidation_strategy) { | ||
| 350 | // Validate the mapping request. | 351 | // Validate the mapping request. |
| 351 | R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode), | 352 | R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode), |
| 352 | ResultInvalidMemoryRegion); | 353 | ResultInvalidMemoryRegion); |
| @@ -396,7 +397,11 @@ ResultCode KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std | |||
| 396 | bool reprotected_pages = false; | 397 | bool reprotected_pages = false; |
| 397 | SCOPE_EXIT({ | 398 | SCOPE_EXIT({ |
| 398 | if (reprotected_pages && any_code_pages) { | 399 | if (reprotected_pages && any_code_pages) { |
| 399 | system.InvalidateCpuInstructionCacheRange(dst_address, size); | 400 | if (icache_invalidation_strategy == ICacheInvalidationStrategy::InvalidateRange) { |
| 401 | system.InvalidateCpuInstructionCacheRange(dst_address, size); | ||
| 402 | } else { | ||
| 403 | system.InvalidateCpuInstructionCaches(); | ||
| 404 | } | ||
| 400 | } | 405 | } |
| 401 | }); | 406 | }); |
| 402 | 407 | ||
| @@ -563,6 +568,8 @@ ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size, | |||
| 563 | block_manager->Update(dst_addr, num_pages, KMemoryState::Free, KMemoryPermission::None, | 568 | block_manager->Update(dst_addr, num_pages, KMemoryState::Free, KMemoryPermission::None, |
| 564 | KMemoryAttribute::None); | 569 | KMemoryAttribute::None); |
| 565 | 570 | ||
| 571 | system.InvalidateCpuInstructionCaches(); | ||
| 572 | |||
| 566 | return ResultSuccess; | 573 | return ResultSuccess; |
| 567 | } | 574 | } |
| 568 | 575 | ||
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h index bfabdf38c..dd6022975 100644 --- a/src/core/hle/kernel/k_page_table.h +++ b/src/core/hle/kernel/k_page_table.h | |||
| @@ -26,6 +26,8 @@ class KMemoryBlockManager; | |||
| 26 | 26 | ||
| 27 | class KPageTable final { | 27 | class KPageTable final { |
| 28 | public: | 28 | public: |
| 29 | enum class ICacheInvalidationStrategy : u32 { InvalidateRange, InvalidateAll }; | ||
| 30 | |||
| 29 | YUZU_NON_COPYABLE(KPageTable); | 31 | YUZU_NON_COPYABLE(KPageTable); |
| 30 | YUZU_NON_MOVEABLE(KPageTable); | 32 | YUZU_NON_MOVEABLE(KPageTable); |
| 31 | 33 | ||
| @@ -38,7 +40,8 @@ public: | |||
| 38 | ResultCode MapProcessCode(VAddr addr, std::size_t pages_count, KMemoryState state, | 40 | ResultCode MapProcessCode(VAddr addr, std::size_t pages_count, KMemoryState state, |
| 39 | KMemoryPermission perm); | 41 | KMemoryPermission perm); |
| 40 | ResultCode MapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size); | 42 | ResultCode MapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size); |
| 41 | ResultCode UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size); | 43 | ResultCode UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size, |
| 44 | ICacheInvalidationStrategy icache_invalidation_strategy); | ||
| 42 | ResultCode UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table, | 45 | ResultCode UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table, |
| 43 | VAddr src_addr); | 46 | VAddr src_addr); |
| 44 | ResultCode MapPhysicalMemory(VAddr addr, std::size_t size); | 47 | ResultCode MapPhysicalMemory(VAddr addr, std::size_t size); |
diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h index 48b17fc74..9f171e3da 100644 --- a/src/core/hle/kernel/k_process.h +++ b/src/core/hle/kernel/k_process.h | |||
| @@ -422,7 +422,7 @@ private: | |||
| 422 | bool is_64bit_process = true; | 422 | bool is_64bit_process = true; |
| 423 | 423 | ||
| 424 | /// Total running time for the process in ticks. | 424 | /// Total running time for the process in ticks. |
| 425 | u64 total_process_running_time_ticks = 0; | 425 | std::atomic<u64> total_process_running_time_ticks = 0; |
| 426 | 426 | ||
| 427 | /// Per-process handle table for storing created object handles in. | 427 | /// Per-process handle table for storing created object handles in. |
| 428 | KHandleTable handle_table; | 428 | KHandleTable handle_table; |
diff --git a/src/core/hle/kernel/k_scheduler_lock.h b/src/core/hle/kernel/k_scheduler_lock.h index 93c47f1b1..016e0a818 100644 --- a/src/core/hle/kernel/k_scheduler_lock.h +++ b/src/core/hle/kernel/k_scheduler_lock.h | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <atomic> | ||
| 7 | #include "common/assert.h" | 8 | #include "common/assert.h" |
| 8 | #include "core/hle/kernel/k_spin_lock.h" | 9 | #include "core/hle/kernel/k_spin_lock.h" |
| 9 | #include "core/hle/kernel/k_thread.h" | 10 | #include "core/hle/kernel/k_thread.h" |
| @@ -75,7 +76,7 @@ private: | |||
| 75 | KernelCore& kernel; | 76 | KernelCore& kernel; |
| 76 | KAlignedSpinLock spin_lock{}; | 77 | KAlignedSpinLock spin_lock{}; |
| 77 | s32 lock_count{}; | 78 | s32 lock_count{}; |
| 78 | KThread* owner_thread{}; | 79 | std::atomic<KThread*> owner_thread{}; |
| 79 | }; | 80 | }; |
| 80 | 81 | ||
| 81 | } // namespace Kernel | 82 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/k_server_port.cpp b/src/core/hle/kernel/k_server_port.cpp index 433fc98e1..e66c0c992 100644 --- a/src/core/hle/kernel/k_server_port.cpp +++ b/src/core/hle/kernel/k_server_port.cpp | |||
| @@ -62,6 +62,12 @@ void KServerPort::Destroy() { | |||
| 62 | 62 | ||
| 63 | // Close our reference to our parent. | 63 | // Close our reference to our parent. |
| 64 | parent->Close(); | 64 | parent->Close(); |
| 65 | |||
| 66 | // Release host emulation members. | ||
| 67 | session_handler.reset(); | ||
| 68 | |||
| 69 | // Ensure that the global list tracking server objects does not hold on to a reference. | ||
| 70 | kernel.UnregisterServerObject(this); | ||
| 65 | } | 71 | } |
| 66 | 72 | ||
| 67 | bool KServerPort::IsSignaled() const { | 73 | bool KServerPort::IsSignaled() const { |
diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp index 30c56ff29..7ac2ef254 100644 --- a/src/core/hle/kernel/k_server_session.cpp +++ b/src/core/hle/kernel/k_server_session.cpp | |||
| @@ -49,6 +49,9 @@ void KServerSession::Destroy() { | |||
| 49 | 49 | ||
| 50 | // Release host emulation members. | 50 | // Release host emulation members. |
| 51 | manager.reset(); | 51 | manager.reset(); |
| 52 | |||
| 53 | // Ensure that the global list tracking server objects does not hold on to a reference. | ||
| 54 | kernel.UnregisterServerObject(this); | ||
| 52 | } | 55 | } |
| 53 | 56 | ||
| 54 | void KServerSession::OnClientClosed() { | 57 | void KServerSession::OnClientClosed() { |
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp index 94c8faf68..d3bb1c871 100644 --- a/src/core/hle/kernel/k_thread.cpp +++ b/src/core/hle/kernel/k_thread.cpp | |||
| @@ -723,7 +723,7 @@ void KThread::UpdateState() { | |||
| 723 | ASSERT(kernel.GlobalSchedulerContext().IsLocked()); | 723 | ASSERT(kernel.GlobalSchedulerContext().IsLocked()); |
| 724 | 724 | ||
| 725 | // Set our suspend flags in state. | 725 | // Set our suspend flags in state. |
| 726 | const auto old_state = thread_state; | 726 | const ThreadState old_state = thread_state; |
| 727 | const auto new_state = | 727 | const auto new_state = |
| 728 | static_cast<ThreadState>(this->GetSuspendFlags()) | (old_state & ThreadState::Mask); | 728 | static_cast<ThreadState>(this->GetSuspendFlags()) | (old_state & ThreadState::Mask); |
| 729 | thread_state = new_state; | 729 | thread_state = new_state; |
| @@ -738,7 +738,7 @@ void KThread::Continue() { | |||
| 738 | ASSERT(kernel.GlobalSchedulerContext().IsLocked()); | 738 | ASSERT(kernel.GlobalSchedulerContext().IsLocked()); |
| 739 | 739 | ||
| 740 | // Clear our suspend flags in state. | 740 | // Clear our suspend flags in state. |
| 741 | const auto old_state = thread_state; | 741 | const ThreadState old_state = thread_state; |
| 742 | thread_state = old_state & ThreadState::Mask; | 742 | thread_state = old_state & ThreadState::Mask; |
| 743 | 743 | ||
| 744 | // Note the state change in scheduler. | 744 | // Note the state change in scheduler. |
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index f46db7298..d0fd85130 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <atomic> | ||
| 8 | #include <span> | 9 | #include <span> |
| 9 | #include <string> | 10 | #include <string> |
| 10 | #include <utility> | 11 | #include <utility> |
| @@ -751,7 +752,7 @@ private: | |||
| 751 | KAffinityMask original_physical_affinity_mask{}; | 752 | KAffinityMask original_physical_affinity_mask{}; |
| 752 | s32 original_physical_ideal_core_id{}; | 753 | s32 original_physical_ideal_core_id{}; |
| 753 | s32 num_core_migration_disables{}; | 754 | s32 num_core_migration_disables{}; |
| 754 | ThreadState thread_state{}; | 755 | std::atomic<ThreadState> thread_state{}; |
| 755 | std::atomic<bool> termination_requested{}; | 756 | std::atomic<bool> termination_requested{}; |
| 756 | bool wait_cancelled{}; | 757 | bool wait_cancelled{}; |
| 757 | bool cancellable{}; | 758 | bool cancellable{}; |
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 134a0b8e9..d840d44e6 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -85,7 +85,7 @@ struct KernelCore::Impl { | |||
| 85 | 85 | ||
| 86 | void InitializeCores() { | 86 | void InitializeCores() { |
| 87 | for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { | 87 | for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { |
| 88 | cores[core_id].Initialize(current_process->Is64BitProcess()); | 88 | cores[core_id].Initialize((*current_process).Is64BitProcess()); |
| 89 | system.Memory().SetCurrentPageTable(*current_process, core_id); | 89 | system.Memory().SetCurrentPageTable(*current_process, core_id); |
| 90 | } | 90 | } |
| 91 | } | 91 | } |
| @@ -96,15 +96,15 @@ struct KernelCore::Impl { | |||
| 96 | 96 | ||
| 97 | process_list.clear(); | 97 | process_list.clear(); |
| 98 | 98 | ||
| 99 | // Close all open server ports. | 99 | // Close all open server sessions and ports. |
| 100 | std::unordered_set<KServerPort*> server_ports_; | 100 | std::unordered_set<KAutoObject*> server_objects_; |
| 101 | { | 101 | { |
| 102 | std::scoped_lock lk{server_ports_lock}; | 102 | std::scoped_lock lk(server_objects_lock); |
| 103 | server_ports_ = server_ports; | 103 | server_objects_ = server_objects; |
| 104 | server_ports.clear(); | 104 | server_objects.clear(); |
| 105 | } | 105 | } |
| 106 | for (auto* server_port : server_ports_) { | 106 | for (auto* server_object : server_objects_) { |
| 107 | server_port->Close(); | 107 | server_object->Close(); |
| 108 | } | 108 | } |
| 109 | 109 | ||
| 110 | // Ensures all service threads gracefully shutdown. | 110 | // Ensures all service threads gracefully shutdown. |
| @@ -168,11 +168,11 @@ struct KernelCore::Impl { | |||
| 168 | 168 | ||
| 169 | // Shutdown all processes. | 169 | // Shutdown all processes. |
| 170 | if (current_process) { | 170 | if (current_process) { |
| 171 | current_process->Finalize(); | 171 | (*current_process).Finalize(); |
| 172 | // current_process->Close(); | 172 | // current_process->Close(); |
| 173 | // TODO: The current process should be destroyed based on accurate ref counting after | 173 | // TODO: The current process should be destroyed based on accurate ref counting after |
| 174 | // calling Close(). Adding a manual Destroy() call instead to avoid a memory leak. | 174 | // calling Close(). Adding a manual Destroy() call instead to avoid a memory leak. |
| 175 | current_process->Destroy(); | 175 | (*current_process).Destroy(); |
| 176 | current_process = nullptr; | 176 | current_process = nullptr; |
| 177 | } | 177 | } |
| 178 | 178 | ||
| @@ -659,13 +659,20 @@ struct KernelCore::Impl { | |||
| 659 | } | 659 | } |
| 660 | 660 | ||
| 661 | KClientPort* port = &search->second(system.ServiceManager(), system); | 661 | KClientPort* port = &search->second(system.ServiceManager(), system); |
| 662 | { | 662 | RegisterServerObject(&port->GetParent()->GetServerPort()); |
| 663 | std::scoped_lock lk{server_ports_lock}; | ||
| 664 | server_ports.insert(&port->GetParent()->GetServerPort()); | ||
| 665 | } | ||
| 666 | return port; | 663 | return port; |
| 667 | } | 664 | } |
| 668 | 665 | ||
| 666 | void RegisterServerObject(KAutoObject* server_object) { | ||
| 667 | std::scoped_lock lk(server_objects_lock); | ||
| 668 | server_objects.insert(server_object); | ||
| 669 | } | ||
| 670 | |||
| 671 | void UnregisterServerObject(KAutoObject* server_object) { | ||
| 672 | std::scoped_lock lk(server_objects_lock); | ||
| 673 | server_objects.erase(server_object); | ||
| 674 | } | ||
| 675 | |||
| 669 | std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(KernelCore& kernel, | 676 | std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(KernelCore& kernel, |
| 670 | const std::string& name) { | 677 | const std::string& name) { |
| 671 | auto service_thread = std::make_shared<Kernel::ServiceThread>(kernel, 1, name); | 678 | auto service_thread = std::make_shared<Kernel::ServiceThread>(kernel, 1, name); |
| @@ -693,7 +700,7 @@ struct KernelCore::Impl { | |||
| 693 | service_threads_manager.QueueWork([this]() { service_threads.clear(); }); | 700 | service_threads_manager.QueueWork([this]() { service_threads.clear(); }); |
| 694 | } | 701 | } |
| 695 | 702 | ||
| 696 | std::mutex server_ports_lock; | 703 | std::mutex server_objects_lock; |
| 697 | std::mutex registered_objects_lock; | 704 | std::mutex registered_objects_lock; |
| 698 | std::mutex registered_in_use_objects_lock; | 705 | std::mutex registered_in_use_objects_lock; |
| 699 | 706 | ||
| @@ -704,7 +711,7 @@ struct KernelCore::Impl { | |||
| 704 | 711 | ||
| 705 | // Lists all processes that exist in the current session. | 712 | // Lists all processes that exist in the current session. |
| 706 | std::vector<KProcess*> process_list; | 713 | std::vector<KProcess*> process_list; |
| 707 | KProcess* current_process{}; | 714 | std::atomic<KProcess*> current_process{}; |
| 708 | std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context; | 715 | std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context; |
| 709 | Kernel::TimeManager time_manager; | 716 | Kernel::TimeManager time_manager; |
| 710 | 717 | ||
| @@ -723,7 +730,7 @@ struct KernelCore::Impl { | |||
| 723 | /// the ConnectToPort SVC. | 730 | /// the ConnectToPort SVC. |
| 724 | std::unordered_map<std::string, ServiceInterfaceFactory> service_interface_factory; | 731 | std::unordered_map<std::string, ServiceInterfaceFactory> service_interface_factory; |
| 725 | NamedPortTable named_ports; | 732 | NamedPortTable named_ports; |
| 726 | std::unordered_set<KServerPort*> server_ports; | 733 | std::unordered_set<KAutoObject*> server_objects; |
| 727 | std::unordered_set<KAutoObject*> registered_objects; | 734 | std::unordered_set<KAutoObject*> registered_objects; |
| 728 | std::unordered_set<KAutoObject*> registered_in_use_objects; | 735 | std::unordered_set<KAutoObject*> registered_in_use_objects; |
| 729 | 736 | ||
| @@ -928,6 +935,14 @@ KClientPort* KernelCore::CreateNamedServicePort(std::string name) { | |||
| 928 | return impl->CreateNamedServicePort(std::move(name)); | 935 | return impl->CreateNamedServicePort(std::move(name)); |
| 929 | } | 936 | } |
| 930 | 937 | ||
| 938 | void KernelCore::RegisterServerObject(KAutoObject* server_object) { | ||
| 939 | impl->RegisterServerObject(server_object); | ||
| 940 | } | ||
| 941 | |||
| 942 | void KernelCore::UnregisterServerObject(KAutoObject* server_object) { | ||
| 943 | impl->UnregisterServerObject(server_object); | ||
| 944 | } | ||
| 945 | |||
| 931 | void KernelCore::RegisterKernelObject(KAutoObject* object) { | 946 | void KernelCore::RegisterKernelObject(KAutoObject* object) { |
| 932 | std::scoped_lock lk{impl->registered_objects_lock}; | 947 | std::scoped_lock lk{impl->registered_objects_lock}; |
| 933 | impl->registered_objects.insert(object); | 948 | impl->registered_objects.insert(object); |
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 24e26fa44..d709c368b 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h | |||
| @@ -195,6 +195,14 @@ public: | |||
| 195 | /// Opens a port to a service previously registered with RegisterNamedService. | 195 | /// Opens a port to a service previously registered with RegisterNamedService. |
| 196 | KClientPort* CreateNamedServicePort(std::string name); | 196 | KClientPort* CreateNamedServicePort(std::string name); |
| 197 | 197 | ||
| 198 | /// Registers a server session or port with the gobal emulation state, to be freed on shutdown. | ||
| 199 | /// This is necessary because we do not emulate processes for HLE sessions and ports. | ||
| 200 | void RegisterServerObject(KAutoObject* server_object); | ||
| 201 | |||
| 202 | /// Unregisters a server session or port previously registered with RegisterServerSession when | ||
| 203 | /// it was destroyed during the current emulation session. | ||
| 204 | void UnregisterServerObject(KAutoObject* server_object); | ||
| 205 | |||
| 198 | /// Registers all kernel objects with the global emulation state, this is purely for tracking | 206 | /// Registers all kernel objects with the global emulation state, this is purely for tracking |
| 199 | /// leaks after emulation has been shutdown. | 207 | /// leaks after emulation has been shutdown. |
| 200 | void RegisterKernelObject(KAutoObject* object); | 208 | void RegisterKernelObject(KAutoObject* object); |
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 976d63234..0c86435b5 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -1713,7 +1713,8 @@ static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_ha | |||
| 1713 | return ResultInvalidMemoryRegion; | 1713 | return ResultInvalidMemoryRegion; |
| 1714 | } | 1714 | } |
| 1715 | 1715 | ||
| 1716 | return page_table.UnmapCodeMemory(dst_address, src_address, size); | 1716 | return page_table.UnmapCodeMemory(dst_address, src_address, size, |
| 1717 | KPageTable::ICacheInvalidationStrategy::InvalidateAll); | ||
| 1717 | } | 1718 | } |
| 1718 | 1719 | ||
| 1719 | /// Exits the current process | 1720 | /// Exits the current process |
diff --git a/src/core/hle/service/jit/jit.cpp b/src/core/hle/service/jit/jit.cpp index c8ebd2e3f..0f9e33ef6 100644 --- a/src/core/hle/service/jit/jit.cpp +++ b/src/core/hle/service/jit/jit.cpp | |||
| @@ -2,27 +2,256 @@ | |||
| 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/arm/symbols.h" | ||
| 6 | #include "core/core.h" | ||
| 5 | #include "core/hle/ipc_helpers.h" | 7 | #include "core/hle/ipc_helpers.h" |
| 8 | #include "core/hle/kernel/k_code_memory.h" | ||
| 9 | #include "core/hle/kernel/k_transfer_memory.h" | ||
| 6 | #include "core/hle/result.h" | 10 | #include "core/hle/result.h" |
| 7 | #include "core/hle/service/jit/jit.h" | 11 | #include "core/hle/service/jit/jit.h" |
| 12 | #include "core/hle/service/jit/jit_context.h" | ||
| 8 | #include "core/hle/service/service.h" | 13 | #include "core/hle/service/service.h" |
| 14 | #include "core/memory.h" | ||
| 9 | 15 | ||
| 10 | namespace Service::JIT { | 16 | namespace Service::JIT { |
| 11 | 17 | ||
| 18 | struct CodeRange { | ||
| 19 | u64 offset; | ||
| 20 | u64 size; | ||
| 21 | }; | ||
| 22 | |||
| 12 | class IJitEnvironment final : public ServiceFramework<IJitEnvironment> { | 23 | class IJitEnvironment final : public ServiceFramework<IJitEnvironment> { |
| 13 | public: | 24 | public: |
| 14 | explicit IJitEnvironment(Core::System& system_) : ServiceFramework{system_, "IJitEnvironment"} { | 25 | explicit IJitEnvironment(Core::System& system_, CodeRange user_rx, CodeRange user_ro) |
| 26 | : ServiceFramework{system_, "IJitEnvironment", ServiceThreadType::CreateNew}, | ||
| 27 | context{system_.Memory()} { | ||
| 15 | // clang-format off | 28 | // clang-format off |
| 16 | static const FunctionInfo functions[] = { | 29 | static const FunctionInfo functions[] = { |
| 17 | {0, nullptr, "GenerateCode"}, | 30 | {0, &IJitEnvironment::GenerateCode, "GenerateCode"}, |
| 18 | {1, nullptr, "Control"}, | 31 | {1, &IJitEnvironment::Control, "Control"}, |
| 19 | {1000, nullptr, "LoadPlugin"}, | 32 | {1000, &IJitEnvironment::LoadPlugin, "LoadPlugin"}, |
| 20 | {1001, nullptr, "GetCodeAddress"}, | 33 | {1001, &IJitEnvironment::GetCodeAddress, "GetCodeAddress"}, |
| 21 | }; | 34 | }; |
| 22 | // clang-format on | 35 | // clang-format on |
| 23 | 36 | ||
| 24 | RegisterHandlers(functions); | 37 | RegisterHandlers(functions); |
| 38 | |||
| 39 | // Identity map user code range into sysmodule context | ||
| 40 | configuration.user_ro_memory = user_ro; | ||
| 41 | configuration.user_rx_memory = user_rx; | ||
| 42 | configuration.sys_ro_memory = user_ro; | ||
| 43 | configuration.sys_rx_memory = user_rx; | ||
| 25 | } | 44 | } |
| 45 | |||
| 46 | void GenerateCode(Kernel::HLERequestContext& ctx) { | ||
| 47 | struct Parameters { | ||
| 48 | u32 data_size; | ||
| 49 | u64 command; | ||
| 50 | CodeRange cr1; | ||
| 51 | CodeRange cr2; | ||
| 52 | Struct32 data; | ||
| 53 | }; | ||
| 54 | |||
| 55 | IPC::RequestParser rp{ctx}; | ||
| 56 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 57 | std::vector<u8> input_buffer{ctx.CanReadBuffer() ? ctx.ReadBuffer() : std::vector<u8>()}; | ||
| 58 | std::vector<u8> output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0); | ||
| 59 | |||
| 60 | const VAddr return_ptr{context.AddHeap(0u)}; | ||
| 61 | const VAddr cr1_in_ptr{context.AddHeap(parameters.cr1)}; | ||
| 62 | const VAddr cr2_in_ptr{context.AddHeap(parameters.cr2)}; | ||
| 63 | const VAddr cr1_out_ptr{ | ||
| 64 | context.AddHeap(CodeRange{.offset = parameters.cr1.offset, .size = 0})}; | ||
| 65 | const VAddr cr2_out_ptr{ | ||
| 66 | context.AddHeap(CodeRange{.offset = parameters.cr2.offset, .size = 0})}; | ||
| 67 | const VAddr input_ptr{context.AddHeap(input_buffer.data(), input_buffer.size())}; | ||
| 68 | const VAddr output_ptr{context.AddHeap(output_buffer.data(), output_buffer.size())}; | ||
| 69 | const VAddr data_ptr{context.AddHeap(parameters.data)}; | ||
| 70 | const VAddr configuration_ptr{context.AddHeap(configuration)}; | ||
| 71 | |||
| 72 | context.CallFunction(callbacks.GenerateCode, return_ptr, cr1_out_ptr, cr2_out_ptr, | ||
| 73 | configuration_ptr, parameters.command, input_ptr, input_buffer.size(), | ||
| 74 | cr1_in_ptr, cr2_in_ptr, data_ptr, parameters.data_size, output_ptr, | ||
| 75 | output_buffer.size()); | ||
| 76 | |||
| 77 | const s32 return_value{context.GetHeap<s32>(return_ptr)}; | ||
| 78 | |||
| 79 | if (return_value == 0) { | ||
| 80 | system.InvalidateCpuInstructionCacheRange(configuration.user_rx_memory.offset, | ||
| 81 | configuration.user_rx_memory.size); | ||
| 82 | |||
| 83 | if (ctx.CanWriteBuffer()) { | ||
| 84 | context.GetHeap(output_ptr, output_buffer.data(), output_buffer.size()); | ||
| 85 | ctx.WriteBuffer(output_buffer.data(), output_buffer.size()); | ||
| 86 | } | ||
| 87 | const auto cr1_out{context.GetHeap<CodeRange>(cr1_out_ptr)}; | ||
| 88 | const auto cr2_out{context.GetHeap<CodeRange>(cr2_out_ptr)}; | ||
| 89 | |||
| 90 | IPC::ResponseBuilder rb{ctx, 8}; | ||
| 91 | rb.Push(ResultSuccess); | ||
| 92 | rb.Push<u64>(return_value); | ||
| 93 | rb.PushRaw(cr1_out); | ||
| 94 | rb.PushRaw(cr2_out); | ||
| 95 | } else { | ||
| 96 | LOG_WARNING(Service_JIT, "plugin GenerateCode callback failed"); | ||
| 97 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 98 | rb.Push(ResultUnknown); | ||
| 99 | } | ||
| 100 | }; | ||
| 101 | |||
| 102 | void Control(Kernel::HLERequestContext& ctx) { | ||
| 103 | IPC::RequestParser rp{ctx}; | ||
| 104 | const auto command{rp.PopRaw<u64>()}; | ||
| 105 | const auto input_buffer{ctx.ReadBuffer()}; | ||
| 106 | std::vector<u8> output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0); | ||
| 107 | |||
| 108 | const VAddr return_ptr{context.AddHeap(0u)}; | ||
| 109 | const VAddr configuration_ptr{context.AddHeap(configuration)}; | ||
| 110 | const VAddr input_ptr{context.AddHeap(input_buffer.data(), input_buffer.size())}; | ||
| 111 | const VAddr output_ptr{context.AddHeap(output_buffer.data(), output_buffer.size())}; | ||
| 112 | const u64 wrapper_value{ | ||
| 113 | context.CallFunction(callbacks.Control, return_ptr, configuration_ptr, command, | ||
| 114 | input_ptr, input_buffer.size(), output_ptr, output_buffer.size())}; | ||
| 115 | const s32 return_value{context.GetHeap<s32>(return_ptr)}; | ||
| 116 | |||
| 117 | if (wrapper_value == 0 && return_value == 0) { | ||
| 118 | if (ctx.CanWriteBuffer()) { | ||
| 119 | context.GetHeap(output_ptr, output_buffer.data(), output_buffer.size()); | ||
| 120 | ctx.WriteBuffer(output_buffer.data(), output_buffer.size()); | ||
| 121 | } | ||
| 122 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 123 | rb.Push(ResultSuccess); | ||
| 124 | rb.Push(return_value); | ||
| 125 | } else { | ||
| 126 | LOG_WARNING(Service_JIT, "plugin Control callback failed"); | ||
| 127 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 128 | rb.Push(ResultUnknown); | ||
| 129 | } | ||
| 130 | } | ||
| 131 | |||
| 132 | void LoadPlugin(Kernel::HLERequestContext& ctx) { | ||
| 133 | IPC::RequestParser rp{ctx}; | ||
| 134 | const auto tmem_size{rp.PopRaw<u64>()}; | ||
| 135 | if (tmem_size == 0) { | ||
| 136 | LOG_ERROR(Service_JIT, "attempted to load plugin with empty transfer memory"); | ||
| 137 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 138 | rb.Push(ResultUnknown); | ||
| 139 | return; | ||
| 140 | } | ||
| 141 | |||
| 142 | const auto tmem_handle{ctx.GetCopyHandle(0)}; | ||
| 143 | auto tmem{system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>( | ||
| 144 | tmem_handle)}; | ||
| 145 | if (tmem.IsNull()) { | ||
| 146 | LOG_ERROR(Service_JIT, "attempted to load plugin with invalid transfer memory handle"); | ||
| 147 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 148 | rb.Push(ResultUnknown); | ||
| 149 | return; | ||
| 150 | } | ||
| 151 | |||
| 152 | configuration.work_memory.offset = tmem->GetSourceAddress(); | ||
| 153 | configuration.work_memory.size = tmem_size; | ||
| 154 | |||
| 155 | const auto nro_plugin{ctx.ReadBuffer(1)}; | ||
| 156 | auto symbols{Core::Symbols::GetSymbols(nro_plugin, true)}; | ||
| 157 | const auto GetSymbol{[&](std::string name) { return symbols[name].first; }}; | ||
| 158 | |||
| 159 | callbacks = | ||
| 160 | GuestCallbacks{.rtld_fini = GetSymbol("_fini"), | ||
| 161 | .rtld_init = GetSymbol("_init"), | ||
| 162 | .Control = GetSymbol("nnjitpluginControl"), | ||
| 163 | .ResolveBasicSymbols = GetSymbol("nnjitpluginResolveBasicSymbols"), | ||
| 164 | .SetupDiagnostics = GetSymbol("nnjitpluginSetupDiagnostics"), | ||
| 165 | .Configure = GetSymbol("nnjitpluginConfigure"), | ||
| 166 | .GenerateCode = GetSymbol("nnjitpluginGenerateCode"), | ||
| 167 | .GetVersion = GetSymbol("nnjitpluginGetVersion"), | ||
| 168 | .Keeper = GetSymbol("nnjitpluginKeeper"), | ||
| 169 | .OnPrepared = GetSymbol("nnjitpluginOnPrepared")}; | ||
| 170 | |||
| 171 | if (callbacks.GetVersion == 0 || callbacks.Configure == 0 || callbacks.GenerateCode == 0 || | ||
| 172 | callbacks.OnPrepared == 0) { | ||
| 173 | LOG_ERROR(Service_JIT, "plugin does not implement all necessary functionality"); | ||
| 174 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 175 | rb.Push(ResultUnknown); | ||
| 176 | return; | ||
| 177 | } | ||
| 178 | |||
| 179 | if (!context.LoadNRO(nro_plugin)) { | ||
| 180 | LOG_ERROR(Service_JIT, "failed to load plugin"); | ||
| 181 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 182 | rb.Push(ResultUnknown); | ||
| 183 | return; | ||
| 184 | } | ||
| 185 | |||
| 186 | context.MapProcessMemory(configuration.sys_ro_memory.offset, | ||
| 187 | configuration.sys_ro_memory.size); | ||
| 188 | context.MapProcessMemory(configuration.sys_rx_memory.offset, | ||
| 189 | configuration.sys_rx_memory.size); | ||
| 190 | context.MapProcessMemory(configuration.work_memory.offset, configuration.work_memory.size); | ||
| 191 | |||
| 192 | if (callbacks.rtld_init != 0) { | ||
| 193 | context.CallFunction(callbacks.rtld_init); | ||
| 194 | } | ||
| 195 | |||
| 196 | const auto version{context.CallFunction(callbacks.GetVersion)}; | ||
| 197 | if (version != 1) { | ||
| 198 | LOG_ERROR(Service_JIT, "unknown plugin version {}", version); | ||
| 199 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 200 | rb.Push(ResultUnknown); | ||
| 201 | return; | ||
| 202 | } | ||
| 203 | |||
| 204 | const auto resolve{context.GetHelper("_resolve")}; | ||
| 205 | if (callbacks.ResolveBasicSymbols != 0) { | ||
| 206 | context.CallFunction(callbacks.ResolveBasicSymbols, resolve); | ||
| 207 | } | ||
| 208 | const auto resolve_ptr{context.AddHeap(resolve)}; | ||
| 209 | if (callbacks.SetupDiagnostics != 0) { | ||
| 210 | context.CallFunction(callbacks.SetupDiagnostics, 0u, resolve_ptr); | ||
| 211 | } | ||
| 212 | |||
| 213 | context.CallFunction(callbacks.Configure, 0u); | ||
| 214 | const auto configuration_ptr{context.AddHeap(configuration)}; | ||
| 215 | context.CallFunction(callbacks.OnPrepared, configuration_ptr); | ||
| 216 | |||
| 217 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 218 | rb.Push(ResultSuccess); | ||
| 219 | } | ||
| 220 | |||
| 221 | void GetCodeAddress(Kernel::HLERequestContext& ctx) { | ||
| 222 | IPC::ResponseBuilder rb{ctx, 6}; | ||
| 223 | rb.Push(ResultSuccess); | ||
| 224 | rb.Push(configuration.user_rx_memory.offset); | ||
| 225 | rb.Push(configuration.user_ro_memory.offset); | ||
| 226 | } | ||
| 227 | |||
| 228 | private: | ||
| 229 | using Struct32 = std::array<u8, 32>; | ||
| 230 | |||
| 231 | struct GuestCallbacks { | ||
| 232 | VAddr rtld_fini; | ||
| 233 | VAddr rtld_init; | ||
| 234 | VAddr Control; | ||
| 235 | VAddr ResolveBasicSymbols; | ||
| 236 | VAddr SetupDiagnostics; | ||
| 237 | VAddr Configure; | ||
| 238 | VAddr GenerateCode; | ||
| 239 | VAddr GetVersion; | ||
| 240 | VAddr Keeper; | ||
| 241 | VAddr OnPrepared; | ||
| 242 | }; | ||
| 243 | |||
| 244 | struct JITConfiguration { | ||
| 245 | CodeRange user_rx_memory; | ||
| 246 | CodeRange user_ro_memory; | ||
| 247 | CodeRange work_memory; | ||
| 248 | CodeRange sys_rx_memory; | ||
| 249 | CodeRange sys_ro_memory; | ||
| 250 | }; | ||
| 251 | |||
| 252 | GuestCallbacks callbacks; | ||
| 253 | JITConfiguration configuration; | ||
| 254 | JITContext context; | ||
| 26 | }; | 255 | }; |
| 27 | 256 | ||
| 28 | class JITU final : public ServiceFramework<JITU> { | 257 | class JITU final : public ServiceFramework<JITU> { |
| @@ -40,9 +269,59 @@ public: | |||
| 40 | void CreateJitEnvironment(Kernel::HLERequestContext& ctx) { | 269 | void CreateJitEnvironment(Kernel::HLERequestContext& ctx) { |
| 41 | LOG_DEBUG(Service_JIT, "called"); | 270 | LOG_DEBUG(Service_JIT, "called"); |
| 42 | 271 | ||
| 272 | struct Parameters { | ||
| 273 | u64 rx_size; | ||
| 274 | u64 ro_size; | ||
| 275 | }; | ||
| 276 | |||
| 277 | IPC::RequestParser rp{ctx}; | ||
| 278 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 279 | const auto executable_mem_handle{ctx.GetCopyHandle(1)}; | ||
| 280 | const auto readable_mem_handle{ctx.GetCopyHandle(2)}; | ||
| 281 | |||
| 282 | if (parameters.rx_size == 0 || parameters.ro_size == 0) { | ||
| 283 | LOG_ERROR(Service_JIT, "attempted to init with empty code regions"); | ||
| 284 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 285 | rb.Push(ResultUnknown); | ||
| 286 | return; | ||
| 287 | } | ||
| 288 | |||
| 289 | // The copy handle at index 0 is the process handle, but handle tables are | ||
| 290 | // per-process, so there is no point reading it here until we are multiprocess | ||
| 291 | const auto& process{*system.CurrentProcess()}; | ||
| 292 | |||
| 293 | auto executable_mem{ | ||
| 294 | process.GetHandleTable().GetObject<Kernel::KCodeMemory>(executable_mem_handle)}; | ||
| 295 | if (executable_mem.IsNull()) { | ||
| 296 | LOG_ERROR(Service_JIT, "executable_mem is null for handle=0x{:08X}", | ||
| 297 | executable_mem_handle); | ||
| 298 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 299 | rb.Push(ResultUnknown); | ||
| 300 | return; | ||
| 301 | } | ||
| 302 | |||
| 303 | auto readable_mem{ | ||
| 304 | process.GetHandleTable().GetObject<Kernel::KCodeMemory>(readable_mem_handle)}; | ||
| 305 | if (readable_mem.IsNull()) { | ||
| 306 | LOG_ERROR(Service_JIT, "readable_mem is null for handle=0x{:08X}", readable_mem_handle); | ||
| 307 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 308 | rb.Push(ResultUnknown); | ||
| 309 | return; | ||
| 310 | } | ||
| 311 | |||
| 312 | const CodeRange user_rx{ | ||
| 313 | .offset = executable_mem->GetSourceAddress(), | ||
| 314 | .size = parameters.rx_size, | ||
| 315 | }; | ||
| 316 | |||
| 317 | const CodeRange user_ro{ | ||
| 318 | .offset = readable_mem->GetSourceAddress(), | ||
| 319 | .size = parameters.ro_size, | ||
| 320 | }; | ||
| 321 | |||
| 43 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 322 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 44 | rb.Push(ResultSuccess); | 323 | rb.Push(ResultSuccess); |
| 45 | rb.PushIpcInterface<IJitEnvironment>(system); | 324 | rb.PushIpcInterface<IJitEnvironment>(system, user_rx, user_ro); |
| 46 | } | 325 | } |
| 47 | }; | 326 | }; |
| 48 | 327 | ||
diff --git a/src/core/hle/service/jit/jit_context.cpp b/src/core/hle/service/jit/jit_context.cpp new file mode 100644 index 000000000..630368fb3 --- /dev/null +++ b/src/core/hle/service/jit/jit_context.cpp | |||
| @@ -0,0 +1,424 @@ | |||
| 1 | // Copyright 2022 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <array> | ||
| 6 | #include <map> | ||
| 7 | #include <span> | ||
| 8 | #include <boost/icl/interval_set.hpp> | ||
| 9 | #include <dynarmic/interface/A64/a64.h> | ||
| 10 | #include <dynarmic/interface/A64/config.h> | ||
| 11 | |||
| 12 | #include "common/alignment.h" | ||
| 13 | #include "common/common_funcs.h" | ||
| 14 | #include "common/div_ceil.h" | ||
| 15 | #include "common/logging/log.h" | ||
| 16 | #include "core/hle/service/jit/jit_context.h" | ||
| 17 | #include "core/memory.h" | ||
| 18 | |||
| 19 | namespace Service::JIT { | ||
| 20 | |||
| 21 | constexpr std::array<u8, 4> STOP_ARM64 = { | ||
| 22 | 0x01, 0x00, 0x00, 0xd4, // svc #0 | ||
| 23 | }; | ||
| 24 | |||
| 25 | constexpr std::array<u8, 8> RESOLVE_ARM64 = { | ||
| 26 | 0x21, 0x00, 0x00, 0xd4, // svc #1 | ||
| 27 | 0xc0, 0x03, 0x5f, 0xd6, // ret | ||
| 28 | }; | ||
| 29 | |||
| 30 | constexpr std::array<u8, 4> PANIC_ARM64 = { | ||
| 31 | 0x41, 0x00, 0x00, 0xd4, // svc #2 | ||
| 32 | }; | ||
| 33 | |||
| 34 | constexpr std::array<u8, 60> MEMMOVE_ARM64 = { | ||
| 35 | 0x1f, 0x00, 0x01, 0xeb, // cmp x0, x1 | ||
| 36 | 0x83, 0x01, 0x00, 0x54, // b.lo #+34 | ||
| 37 | 0x42, 0x04, 0x00, 0xd1, // sub x2, x2, 1 | ||
| 38 | 0x22, 0x01, 0xf8, 0xb7, // tbnz x2, #63, #+36 | ||
| 39 | 0x23, 0x68, 0x62, 0x38, // ldrb w3, [x1, x2] | ||
| 40 | 0x03, 0x68, 0x22, 0x38, // strb w3, [x0, x2] | ||
| 41 | 0xfc, 0xff, 0xff, 0x17, // b #-16 | ||
| 42 | 0x24, 0x68, 0x63, 0x38, // ldrb w4, [x1, x3] | ||
| 43 | 0x04, 0x68, 0x23, 0x38, // strb w4, [x0, x3] | ||
| 44 | 0x63, 0x04, 0x00, 0x91, // add x3, x3, 1 | ||
| 45 | 0x7f, 0x00, 0x02, 0xeb, // cmp x3, x2 | ||
| 46 | 0x8b, 0xff, 0xff, 0x54, // b.lt #-16 | ||
| 47 | 0xc0, 0x03, 0x5f, 0xd6, // ret | ||
| 48 | 0x03, 0x00, 0x80, 0xd2, // mov x3, 0 | ||
| 49 | 0xfc, 0xff, 0xff, 0x17, // b #-16 | ||
| 50 | }; | ||
| 51 | |||
| 52 | constexpr std::array<u8, 28> MEMSET_ARM64 = { | ||
| 53 | 0x03, 0x00, 0x80, 0xd2, // mov x3, 0 | ||
| 54 | 0x7f, 0x00, 0x02, 0xeb, // cmp x3, x2 | ||
| 55 | 0x4b, 0x00, 0x00, 0x54, // b.lt #+8 | ||
| 56 | 0xc0, 0x03, 0x5f, 0xd6, // ret | ||
| 57 | 0x01, 0x68, 0x23, 0x38, // strb w1, [x0, x3] | ||
| 58 | 0x63, 0x04, 0x00, 0x91, // add x3, x3, 1 | ||
| 59 | 0xfb, 0xff, 0xff, 0x17, // b #-20 | ||
| 60 | }; | ||
| 61 | |||
| 62 | struct HelperFunction { | ||
| 63 | const char* name; | ||
| 64 | const std::span<const u8> data; | ||
| 65 | }; | ||
| 66 | |||
| 67 | constexpr std::array<HelperFunction, 6> HELPER_FUNCTIONS{{ | ||
| 68 | {"_stop", STOP_ARM64}, | ||
| 69 | {"_resolve", RESOLVE_ARM64}, | ||
| 70 | {"_panic", PANIC_ARM64}, | ||
| 71 | {"memcpy", MEMMOVE_ARM64}, | ||
| 72 | {"memmove", MEMMOVE_ARM64}, | ||
| 73 | {"memset", MEMSET_ARM64}, | ||
| 74 | }}; | ||
| 75 | |||
| 76 | struct Elf64_Dyn { | ||
| 77 | u64 d_tag; | ||
| 78 | u64 d_un; | ||
| 79 | }; | ||
| 80 | |||
| 81 | struct Elf64_Rela { | ||
| 82 | u64 r_offset; | ||
| 83 | u64 r_info; | ||
| 84 | s64 r_addend; | ||
| 85 | }; | ||
| 86 | |||
| 87 | static constexpr u32 Elf64_RelaType(const Elf64_Rela* rela) { | ||
| 88 | return static_cast<u32>(rela->r_info); | ||
| 89 | } | ||
| 90 | |||
| 91 | constexpr int DT_RELA = 7; /* Address of Rela relocs */ | ||
| 92 | constexpr int DT_RELASZ = 8; /* Total size of Rela relocs */ | ||
| 93 | constexpr int R_AARCH64_RELATIVE = 1027; /* Adjust by program base. */ | ||
| 94 | |||
| 95 | constexpr size_t STACK_ALIGN = 16; | ||
| 96 | |||
| 97 | class JITContextImpl; | ||
| 98 | |||
| 99 | using IntervalSet = boost::icl::interval_set<VAddr>::type; | ||
| 100 | using IntervalType = boost::icl::interval_set<VAddr>::interval_type; | ||
| 101 | |||
| 102 | class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks { | ||
| 103 | public: | ||
| 104 | explicit DynarmicCallbacks64(Core::Memory::Memory& memory_, std::vector<u8>& local_memory_, | ||
| 105 | IntervalSet& mapped_ranges_, JITContextImpl& parent_) | ||
| 106 | : memory{memory_}, local_memory{local_memory_}, | ||
| 107 | mapped_ranges{mapped_ranges_}, parent{parent_} {} | ||
| 108 | |||
| 109 | u8 MemoryRead8(u64 vaddr) override { | ||
| 110 | return ReadMemory<u8>(vaddr); | ||
| 111 | } | ||
| 112 | u16 MemoryRead16(u64 vaddr) override { | ||
| 113 | return ReadMemory<u16>(vaddr); | ||
| 114 | } | ||
| 115 | u32 MemoryRead32(u64 vaddr) override { | ||
| 116 | return ReadMemory<u32>(vaddr); | ||
| 117 | } | ||
| 118 | u64 MemoryRead64(u64 vaddr) override { | ||
| 119 | return ReadMemory<u64>(vaddr); | ||
| 120 | } | ||
| 121 | u128 MemoryRead128(u64 vaddr) override { | ||
| 122 | return ReadMemory<u128>(vaddr); | ||
| 123 | } | ||
| 124 | std::string MemoryReadCString(u64 vaddr) { | ||
| 125 | std::string result; | ||
| 126 | u8 next; | ||
| 127 | |||
| 128 | while ((next = MemoryRead8(vaddr++)) != 0) { | ||
| 129 | result += next; | ||
| 130 | } | ||
| 131 | |||
| 132 | return result; | ||
| 133 | } | ||
| 134 | |||
| 135 | void MemoryWrite8(u64 vaddr, u8 value) override { | ||
| 136 | WriteMemory<u8>(vaddr, value); | ||
| 137 | } | ||
| 138 | void MemoryWrite16(u64 vaddr, u16 value) override { | ||
| 139 | WriteMemory<u16>(vaddr, value); | ||
| 140 | } | ||
| 141 | void MemoryWrite32(u64 vaddr, u32 value) override { | ||
| 142 | WriteMemory<u32>(vaddr, value); | ||
| 143 | } | ||
| 144 | void MemoryWrite64(u64 vaddr, u64 value) override { | ||
| 145 | WriteMemory<u64>(vaddr, value); | ||
| 146 | } | ||
| 147 | void MemoryWrite128(u64 vaddr, u128 value) override { | ||
| 148 | WriteMemory<u128>(vaddr, value); | ||
| 149 | } | ||
| 150 | |||
| 151 | bool MemoryWriteExclusive8(u64 vaddr, u8 value, u8) override { | ||
| 152 | return WriteMemory<u8>(vaddr, value); | ||
| 153 | } | ||
| 154 | bool MemoryWriteExclusive16(u64 vaddr, u16 value, u16) override { | ||
| 155 | return WriteMemory<u16>(vaddr, value); | ||
| 156 | } | ||
| 157 | bool MemoryWriteExclusive32(u64 vaddr, u32 value, u32) override { | ||
| 158 | return WriteMemory<u32>(vaddr, value); | ||
| 159 | } | ||
| 160 | bool MemoryWriteExclusive64(u64 vaddr, u64 value, u64) override { | ||
| 161 | return WriteMemory<u64>(vaddr, value); | ||
| 162 | } | ||
| 163 | bool MemoryWriteExclusive128(u64 vaddr, u128 value, u128) override { | ||
| 164 | return WriteMemory<u128>(vaddr, value); | ||
| 165 | } | ||
| 166 | |||
| 167 | void CallSVC(u32 swi) override; | ||
| 168 | void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override; | ||
| 169 | void InterpreterFallback(u64 pc, size_t num_instructions) override; | ||
| 170 | |||
| 171 | void AddTicks(u64 ticks) override {} | ||
| 172 | u64 GetTicksRemaining() override { | ||
| 173 | return std::numeric_limits<u32>::max(); | ||
| 174 | } | ||
| 175 | u64 GetCNTPCT() override { | ||
| 176 | return 0; | ||
| 177 | } | ||
| 178 | |||
| 179 | template <class T> | ||
| 180 | T ReadMemory(u64 vaddr) { | ||
| 181 | T ret{}; | ||
| 182 | if (boost::icl::contains(mapped_ranges, vaddr)) { | ||
| 183 | memory.ReadBlock(vaddr, &ret, sizeof(T)); | ||
| 184 | } else if (vaddr + sizeof(T) > local_memory.size()) { | ||
| 185 | LOG_CRITICAL(Service_JIT, "plugin: unmapped read @ 0x{:016x}", vaddr); | ||
| 186 | } else { | ||
| 187 | std::memcpy(&ret, local_memory.data() + vaddr, sizeof(T)); | ||
| 188 | } | ||
| 189 | return ret; | ||
| 190 | } | ||
| 191 | |||
| 192 | template <class T> | ||
| 193 | bool WriteMemory(u64 vaddr, const T value) { | ||
| 194 | if (boost::icl::contains(mapped_ranges, vaddr)) { | ||
| 195 | memory.WriteBlock(vaddr, &value, sizeof(T)); | ||
| 196 | } else if (vaddr + sizeof(T) > local_memory.size()) { | ||
| 197 | LOG_CRITICAL(Service_JIT, "plugin: unmapped write @ 0x{:016x}", vaddr); | ||
| 198 | } else { | ||
| 199 | std::memcpy(local_memory.data() + vaddr, &value, sizeof(T)); | ||
| 200 | } | ||
| 201 | return true; | ||
| 202 | } | ||
| 203 | |||
| 204 | private: | ||
| 205 | Core::Memory::Memory& memory; | ||
| 206 | std::vector<u8>& local_memory; | ||
| 207 | IntervalSet& mapped_ranges; | ||
| 208 | JITContextImpl& parent; | ||
| 209 | }; | ||
| 210 | |||
| 211 | class JITContextImpl { | ||
| 212 | public: | ||
| 213 | explicit JITContextImpl(Core::Memory::Memory& memory_) : memory{memory_} { | ||
| 214 | callbacks = | ||
| 215 | std::make_unique<DynarmicCallbacks64>(memory, local_memory, mapped_ranges, *this); | ||
| 216 | user_config.callbacks = callbacks.get(); | ||
| 217 | jit = std::make_unique<Dynarmic::A64::Jit>(user_config); | ||
| 218 | } | ||
| 219 | |||
| 220 | bool LoadNRO(std::span<const u8> data) { | ||
| 221 | local_memory.clear(); | ||
| 222 | local_memory.insert(local_memory.end(), data.begin(), data.end()); | ||
| 223 | |||
| 224 | if (FixupRelocations()) { | ||
| 225 | InsertHelperFunctions(); | ||
| 226 | InsertStack(); | ||
| 227 | return true; | ||
| 228 | } else { | ||
| 229 | return false; | ||
| 230 | } | ||
| 231 | } | ||
| 232 | |||
| 233 | bool FixupRelocations() { | ||
| 234 | const VAddr mod_offset{callbacks->MemoryRead32(4)}; | ||
| 235 | if (callbacks->MemoryRead32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) { | ||
| 236 | return false; | ||
| 237 | } | ||
| 238 | |||
| 239 | VAddr dynamic_offset{mod_offset + callbacks->MemoryRead32(mod_offset + 4)}; | ||
| 240 | VAddr rela_dyn = 0; | ||
| 241 | size_t num_rela = 0; | ||
| 242 | while (true) { | ||
| 243 | const auto dyn{callbacks->ReadMemory<Elf64_Dyn>(dynamic_offset)}; | ||
| 244 | dynamic_offset += sizeof(Elf64_Dyn); | ||
| 245 | |||
| 246 | if (!dyn.d_tag) { | ||
| 247 | break; | ||
| 248 | } | ||
| 249 | if (dyn.d_tag == DT_RELA) { | ||
| 250 | rela_dyn = dyn.d_un; | ||
| 251 | } | ||
| 252 | if (dyn.d_tag == DT_RELASZ) { | ||
| 253 | num_rela = dyn.d_un / sizeof(Elf64_Rela); | ||
| 254 | } | ||
| 255 | } | ||
| 256 | |||
| 257 | for (size_t i = 0; i < num_rela; i++) { | ||
| 258 | const auto rela{callbacks->ReadMemory<Elf64_Rela>(rela_dyn + i * sizeof(Elf64_Rela))}; | ||
| 259 | if (Elf64_RelaType(&rela) != R_AARCH64_RELATIVE) { | ||
| 260 | continue; | ||
| 261 | } | ||
| 262 | const VAddr contents{callbacks->MemoryRead64(rela.r_offset)}; | ||
| 263 | callbacks->MemoryWrite64(rela.r_offset, contents + rela.r_addend); | ||
| 264 | } | ||
| 265 | |||
| 266 | return true; | ||
| 267 | } | ||
| 268 | |||
| 269 | void InsertHelperFunctions() { | ||
| 270 | for (const auto& [name, contents] : HELPER_FUNCTIONS) { | ||
| 271 | helpers[name] = local_memory.size(); | ||
| 272 | local_memory.insert(local_memory.end(), contents.begin(), contents.end()); | ||
| 273 | } | ||
| 274 | } | ||
| 275 | |||
| 276 | void InsertStack() { | ||
| 277 | const u64 pad_amount{Common::AlignUp(local_memory.size(), STACK_ALIGN) - | ||
| 278 | local_memory.size()}; | ||
| 279 | local_memory.insert(local_memory.end(), 0x10000 + pad_amount, 0); | ||
| 280 | top_of_stack = local_memory.size(); | ||
| 281 | heap_pointer = top_of_stack; | ||
| 282 | } | ||
| 283 | |||
| 284 | void MapProcessMemory(VAddr dest_address, std::size_t size) { | ||
| 285 | mapped_ranges.add(IntervalType{dest_address, dest_address + size}); | ||
| 286 | } | ||
| 287 | |||
| 288 | void PushArgument(const void* data, size_t size) { | ||
| 289 | const size_t num_words = Common::DivCeil(size, sizeof(u64)); | ||
| 290 | const size_t current_pos = argument_stack.size(); | ||
| 291 | argument_stack.insert(argument_stack.end(), num_words, 0); | ||
| 292 | std::memcpy(argument_stack.data() + current_pos, data, size); | ||
| 293 | } | ||
| 294 | |||
| 295 | void SetupArguments() { | ||
| 296 | for (size_t i = 0; i < 8 && i < argument_stack.size(); i++) { | ||
| 297 | jit->SetRegister(i, argument_stack[i]); | ||
| 298 | } | ||
| 299 | if (argument_stack.size() > 8) { | ||
| 300 | const VAddr new_sp = Common::AlignDown( | ||
| 301 | top_of_stack - (argument_stack.size() - 8) * sizeof(u64), STACK_ALIGN); | ||
| 302 | for (size_t i = 8; i < argument_stack.size(); i++) { | ||
| 303 | callbacks->MemoryWrite64(new_sp + (i - 8) * sizeof(u64), argument_stack[i]); | ||
| 304 | } | ||
| 305 | jit->SetSP(new_sp); | ||
| 306 | } | ||
| 307 | argument_stack.clear(); | ||
| 308 | heap_pointer = top_of_stack; | ||
| 309 | } | ||
| 310 | |||
| 311 | u64 CallFunction(VAddr func) { | ||
| 312 | jit->SetRegister(30, helpers["_stop"]); | ||
| 313 | jit->SetSP(top_of_stack); | ||
| 314 | SetupArguments(); | ||
| 315 | |||
| 316 | jit->SetPC(func); | ||
| 317 | jit->Run(); | ||
| 318 | return jit->GetRegister(0); | ||
| 319 | } | ||
| 320 | |||
| 321 | VAddr GetHelper(const std::string& name) { | ||
| 322 | return helpers[name]; | ||
| 323 | } | ||
| 324 | |||
| 325 | VAddr AddHeap(const void* data, size_t size) { | ||
| 326 | const size_t num_bytes{Common::AlignUp(size, STACK_ALIGN)}; | ||
| 327 | if (heap_pointer + num_bytes > local_memory.size()) { | ||
| 328 | local_memory.insert(local_memory.end(), | ||
| 329 | (heap_pointer + num_bytes) - local_memory.size(), 0); | ||
| 330 | } | ||
| 331 | const VAddr location{heap_pointer}; | ||
| 332 | std::memcpy(local_memory.data() + location, data, size); | ||
| 333 | heap_pointer += num_bytes; | ||
| 334 | return location; | ||
| 335 | } | ||
| 336 | |||
| 337 | void GetHeap(VAddr location, void* data, size_t size) { | ||
| 338 | std::memcpy(data, local_memory.data() + location, size); | ||
| 339 | } | ||
| 340 | |||
| 341 | std::unique_ptr<DynarmicCallbacks64> callbacks; | ||
| 342 | std::vector<u8> local_memory; | ||
| 343 | std::vector<u64> argument_stack; | ||
| 344 | IntervalSet mapped_ranges; | ||
| 345 | Dynarmic::A64::UserConfig user_config; | ||
| 346 | std::unique_ptr<Dynarmic::A64::Jit> jit; | ||
| 347 | std::map<std::string, VAddr, std::less<>> helpers; | ||
| 348 | Core::Memory::Memory& memory; | ||
| 349 | VAddr top_of_stack; | ||
| 350 | VAddr heap_pointer; | ||
| 351 | }; | ||
| 352 | |||
| 353 | void DynarmicCallbacks64::CallSVC(u32 swi) { | ||
| 354 | switch (swi) { | ||
| 355 | case 0: | ||
| 356 | parent.jit->HaltExecution(); | ||
| 357 | break; | ||
| 358 | |||
| 359 | case 1: { | ||
| 360 | // X0 contains a char* for a symbol to resolve | ||
| 361 | std::string name{MemoryReadCString(parent.jit->GetRegister(0))}; | ||
| 362 | const auto helper{parent.helpers[name]}; | ||
| 363 | |||
| 364 | if (helper != 0) { | ||
| 365 | parent.jit->SetRegister(0, helper); | ||
| 366 | } else { | ||
| 367 | LOG_WARNING(Service_JIT, "plugin requested unknown function {}", name); | ||
| 368 | parent.jit->SetRegister(0, parent.helpers["_panic"]); | ||
| 369 | } | ||
| 370 | break; | ||
| 371 | } | ||
| 372 | |||
| 373 | case 2: | ||
| 374 | default: | ||
| 375 | LOG_CRITICAL(Service_JIT, "plugin panicked!"); | ||
| 376 | parent.jit->HaltExecution(); | ||
| 377 | break; | ||
| 378 | } | ||
| 379 | } | ||
| 380 | |||
| 381 | void DynarmicCallbacks64::ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) { | ||
| 382 | LOG_CRITICAL(Service_JIT, "Illegal operation PC @ {:08x}", pc); | ||
| 383 | parent.jit->HaltExecution(); | ||
| 384 | } | ||
| 385 | |||
| 386 | void DynarmicCallbacks64::InterpreterFallback(u64 pc, size_t num_instructions) { | ||
| 387 | LOG_CRITICAL(Service_JIT, "Unimplemented instruction PC @ {:08x}", pc); | ||
| 388 | parent.jit->HaltExecution(); | ||
| 389 | } | ||
| 390 | |||
| 391 | JITContext::JITContext(Core::Memory::Memory& memory) | ||
| 392 | : impl{std::make_unique<JITContextImpl>(memory)} {} | ||
| 393 | |||
| 394 | JITContext::~JITContext() {} | ||
| 395 | |||
| 396 | bool JITContext::LoadNRO(std::span<const u8> data) { | ||
| 397 | return impl->LoadNRO(data); | ||
| 398 | } | ||
| 399 | |||
| 400 | void JITContext::MapProcessMemory(VAddr dest_address, std::size_t size) { | ||
| 401 | impl->MapProcessMemory(dest_address, size); | ||
| 402 | } | ||
| 403 | |||
| 404 | u64 JITContext::CallFunction(VAddr func) { | ||
| 405 | return impl->CallFunction(func); | ||
| 406 | } | ||
| 407 | |||
| 408 | void JITContext::PushArgument(const void* data, size_t size) { | ||
| 409 | impl->PushArgument(data, size); | ||
| 410 | } | ||
| 411 | |||
| 412 | VAddr JITContext::GetHelper(const std::string& name) { | ||
| 413 | return impl->GetHelper(name); | ||
| 414 | } | ||
| 415 | |||
| 416 | VAddr JITContext::AddHeap(const void* data, size_t size) { | ||
| 417 | return impl->AddHeap(data, size); | ||
| 418 | } | ||
| 419 | |||
| 420 | void JITContext::GetHeap(VAddr location, void* data, size_t size) { | ||
| 421 | impl->GetHeap(location, data, size); | ||
| 422 | } | ||
| 423 | |||
| 424 | } // namespace Service::JIT | ||
diff --git a/src/core/hle/service/jit/jit_context.h b/src/core/hle/service/jit/jit_context.h new file mode 100644 index 000000000..d8bf76cff --- /dev/null +++ b/src/core/hle/service/jit/jit_context.h | |||
| @@ -0,0 +1,65 @@ | |||
| 1 | // Copyright 2022 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 <memory> | ||
| 8 | #include <span> | ||
| 9 | #include <string> | ||
| 10 | |||
| 11 | #include "common/common_types.h" | ||
| 12 | |||
| 13 | namespace Core::Memory { | ||
| 14 | class Memory; | ||
| 15 | } | ||
| 16 | |||
| 17 | namespace Service::JIT { | ||
| 18 | |||
| 19 | class JITContextImpl; | ||
| 20 | |||
| 21 | class JITContext { | ||
| 22 | public: | ||
| 23 | explicit JITContext(Core::Memory::Memory& memory); | ||
| 24 | ~JITContext(); | ||
| 25 | |||
| 26 | [[nodiscard]] bool LoadNRO(std::span<const u8> data); | ||
| 27 | void MapProcessMemory(VAddr dest_address, std::size_t size); | ||
| 28 | |||
| 29 | template <typename T, typename... Ts> | ||
| 30 | u64 CallFunction(VAddr func, T argument, Ts... rest) { | ||
| 31 | static_assert(std::is_trivially_copyable_v<T>); | ||
| 32 | PushArgument(&argument, sizeof(argument)); | ||
| 33 | |||
| 34 | if constexpr (sizeof...(rest) > 0) { | ||
| 35 | return CallFunction(func, rest...); | ||
| 36 | } else { | ||
| 37 | return CallFunction(func); | ||
| 38 | } | ||
| 39 | } | ||
| 40 | |||
| 41 | u64 CallFunction(VAddr func); | ||
| 42 | VAddr GetHelper(const std::string& name); | ||
| 43 | |||
| 44 | template <typename T> | ||
| 45 | VAddr AddHeap(T argument) { | ||
| 46 | return AddHeap(&argument, sizeof(argument)); | ||
| 47 | } | ||
| 48 | VAddr AddHeap(const void* data, size_t size); | ||
| 49 | |||
| 50 | template <typename T> | ||
| 51 | T GetHeap(VAddr location) { | ||
| 52 | static_assert(std::is_trivially_copyable_v<T>); | ||
| 53 | T result; | ||
| 54 | GetHeap(location, &result, sizeof(result)); | ||
| 55 | return result; | ||
| 56 | } | ||
| 57 | void GetHeap(VAddr location, void* data, size_t size); | ||
| 58 | |||
| 59 | private: | ||
| 60 | std::unique_ptr<JITContextImpl> impl; | ||
| 61 | |||
| 62 | void PushArgument(const void* data, size_t size); | ||
| 63 | }; | ||
| 64 | |||
| 65 | } // namespace Service::JIT | ||
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index 2477c5612..cf727c167 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp | |||
| @@ -389,8 +389,12 @@ public: | |||
| 389 | 389 | ||
| 390 | if (bss_size) { | 390 | if (bss_size) { |
| 391 | auto block_guard = detail::ScopeExit([&] { | 391 | auto block_guard = detail::ScopeExit([&] { |
| 392 | page_table.UnmapCodeMemory(addr + nro_size, bss_addr, bss_size); | 392 | page_table.UnmapCodeMemory( |
| 393 | page_table.UnmapCodeMemory(addr, nro_addr, nro_size); | 393 | addr + nro_size, bss_addr, bss_size, |
| 394 | Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange); | ||
| 395 | page_table.UnmapCodeMemory( | ||
| 396 | addr, nro_addr, nro_size, | ||
| 397 | Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange); | ||
| 394 | }); | 398 | }); |
| 395 | 399 | ||
| 396 | const ResultCode result{ | 400 | const ResultCode result{ |
| @@ -570,17 +574,21 @@ public: | |||
| 570 | auto& page_table{system.CurrentProcess()->PageTable()}; | 574 | auto& page_table{system.CurrentProcess()->PageTable()}; |
| 571 | 575 | ||
| 572 | if (info.bss_size != 0) { | 576 | if (info.bss_size != 0) { |
| 573 | CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address + info.text_size + | 577 | CASCADE_CODE(page_table.UnmapCodeMemory( |
| 574 | info.ro_size + info.data_size, | 578 | info.nro_address + info.text_size + info.ro_size + info.data_size, info.bss_address, |
| 575 | info.bss_address, info.bss_size)); | 579 | info.bss_size, Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange)); |
| 576 | } | 580 | } |
| 577 | 581 | ||
| 578 | CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address + info.text_size + info.ro_size, | 582 | CASCADE_CODE(page_table.UnmapCodeMemory( |
| 579 | info.src_addr + info.text_size + info.ro_size, | 583 | info.nro_address + info.text_size + info.ro_size, |
| 580 | info.data_size)); | 584 | info.src_addr + info.text_size + info.ro_size, info.data_size, |
| 581 | CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address + info.text_size, | 585 | Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange)); |
| 582 | info.src_addr + info.text_size, info.ro_size)); | 586 | CASCADE_CODE(page_table.UnmapCodeMemory( |
| 583 | CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address, info.src_addr, info.text_size)); | 587 | info.nro_address + info.text_size, info.src_addr + info.text_size, info.ro_size, |
| 588 | Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange)); | ||
| 589 | CASCADE_CODE(page_table.UnmapCodeMemory( | ||
| 590 | info.nro_address, info.src_addr, info.text_size, | ||
| 591 | Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange)); | ||
| 584 | return ResultSuccess; | 592 | return ResultSuccess; |
| 585 | } | 593 | } |
| 586 | 594 | ||
diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index 97f895852..13f5e08ec 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp | |||
| @@ -153,7 +153,7 @@ ResultVal<Kernel::KClientSession*> SM::GetServiceImpl(Kernel::HLERequestContext& | |||
| 153 | auto& port = port_result.Unwrap(); | 153 | auto& port = port_result.Unwrap(); |
| 154 | SCOPE_EXIT({ port->GetClientPort().Close(); }); | 154 | SCOPE_EXIT({ port->GetClientPort().Close(); }); |
| 155 | 155 | ||
| 156 | server_ports.emplace_back(&port->GetServerPort()); | 156 | kernel.RegisterServerObject(&port->GetServerPort()); |
| 157 | 157 | ||
| 158 | // Create a new session. | 158 | // Create a new session. |
| 159 | Kernel::KClientSession* session{}; | 159 | Kernel::KClientSession* session{}; |
| @@ -224,10 +224,6 @@ SM::SM(ServiceManager& service_manager_, Core::System& system_) | |||
| 224 | }); | 224 | }); |
| 225 | } | 225 | } |
| 226 | 226 | ||
| 227 | SM::~SM() { | 227 | SM::~SM() = default; |
| 228 | for (auto& server_port : server_ports) { | ||
| 229 | server_port->Close(); | ||
| 230 | } | ||
| 231 | } | ||
| 232 | 228 | ||
| 233 | } // namespace Service::SM | 229 | } // namespace Service::SM |
diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h index 021eb51b4..f3ff7b27e 100644 --- a/src/core/hle/service/sm/sm.h +++ b/src/core/hle/service/sm/sm.h | |||
| @@ -22,7 +22,6 @@ class KClientPort; | |||
| 22 | class KClientSession; | 22 | class KClientSession; |
| 23 | class KernelCore; | 23 | class KernelCore; |
| 24 | class KPort; | 24 | class KPort; |
| 25 | class KServerPort; | ||
| 26 | class SessionRequestHandler; | 25 | class SessionRequestHandler; |
| 27 | } // namespace Kernel | 26 | } // namespace Kernel |
| 28 | 27 | ||
| @@ -48,7 +47,6 @@ private: | |||
| 48 | ServiceManager& service_manager; | 47 | ServiceManager& service_manager; |
| 49 | bool is_initialized{}; | 48 | bool is_initialized{}; |
| 50 | Kernel::KernelCore& kernel; | 49 | Kernel::KernelCore& kernel; |
| 51 | std::vector<Kernel::KServerPort*> server_ports; | ||
| 52 | }; | 50 | }; |
| 53 | 51 | ||
| 54 | class ServiceManager { | 52 | class ServiceManager { |
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp index d6702e4e1..d25b050e2 100644 --- a/src/core/hle/service/sockets/bsd.cpp +++ b/src/core/hle/service/sockets/bsd.cpp | |||
| @@ -689,6 +689,9 @@ Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, con | |||
| 689 | case OptName::REUSEADDR: | 689 | case OptName::REUSEADDR: |
| 690 | ASSERT(value == 0 || value == 1); | 690 | ASSERT(value == 0 || value == 1); |
| 691 | return Translate(socket->SetReuseAddr(value != 0)); | 691 | return Translate(socket->SetReuseAddr(value != 0)); |
| 692 | case OptName::KEEPALIVE: | ||
| 693 | ASSERT(value == 0 || value == 1); | ||
| 694 | return Translate(socket->SetKeepAlive(value != 0)); | ||
| 692 | case OptName::BROADCAST: | 695 | case OptName::BROADCAST: |
| 693 | ASSERT(value == 0 || value == 1); | 696 | ASSERT(value == 0 || value == 1); |
| 694 | return Translate(socket->SetBroadcast(value != 0)); | 697 | return Translate(socket->SetBroadcast(value != 0)); |
diff --git a/src/core/hle/service/sockets/sfdnsres.cpp b/src/core/hle/service/sockets/sfdnsres.cpp index fb6142c49..a193fb578 100644 --- a/src/core/hle/service/sockets/sfdnsres.cpp +++ b/src/core/hle/service/sockets/sfdnsres.cpp | |||
| @@ -2,8 +2,28 @@ | |||
| 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 <string_view> | ||
| 6 | #include <utility> | ||
| 7 | #include <vector> | ||
| 8 | |||
| 9 | #include "common/string_util.h" | ||
| 10 | #include "common/swap.h" | ||
| 11 | #include "core/core.h" | ||
| 5 | #include "core/hle/ipc_helpers.h" | 12 | #include "core/hle/ipc_helpers.h" |
| 6 | #include "core/hle/service/sockets/sfdnsres.h" | 13 | #include "core/hle/service/sockets/sfdnsres.h" |
| 14 | #include "core/memory.h" | ||
| 15 | |||
| 16 | #ifdef _WIN32 | ||
| 17 | #include <ws2tcpip.h> | ||
| 18 | #elif YUZU_UNIX | ||
| 19 | #include <arpa/inet.h> | ||
| 20 | #include <netdb.h> | ||
| 21 | #include <netinet/in.h> | ||
| 22 | #include <sys/socket.h> | ||
| 23 | #ifndef EAI_NODATA | ||
| 24 | #define EAI_NODATA EAI_NONAME | ||
| 25 | #endif | ||
| 26 | #endif | ||
| 7 | 27 | ||
| 8 | namespace Service::Sockets { | 28 | namespace Service::Sockets { |
| 9 | 29 | ||
| @@ -21,7 +41,7 @@ SFDNSRES::SFDNSRES(Core::System& system_) : ServiceFramework{system_, "sfdnsres" | |||
| 21 | {9, nullptr, "CancelRequest"}, | 41 | {9, nullptr, "CancelRequest"}, |
| 22 | {10, nullptr, "GetHostByNameRequestWithOptions"}, | 42 | {10, nullptr, "GetHostByNameRequestWithOptions"}, |
| 23 | {11, nullptr, "GetHostByAddrRequestWithOptions"}, | 43 | {11, nullptr, "GetHostByAddrRequestWithOptions"}, |
| 24 | {12, nullptr, "GetAddrInfoRequestWithOptions"}, | 44 | {12, &SFDNSRES::GetAddrInfoRequestWithOptions, "GetAddrInfoRequestWithOptions"}, |
| 25 | {13, nullptr, "GetNameInfoRequestWithOptions"}, | 45 | {13, nullptr, "GetNameInfoRequestWithOptions"}, |
| 26 | {14, nullptr, "ResolverSetOptionRequest"}, | 46 | {14, nullptr, "ResolverSetOptionRequest"}, |
| 27 | {15, nullptr, "ResolverGetOptionRequest"}, | 47 | {15, nullptr, "ResolverGetOptionRequest"}, |
| @@ -31,7 +51,142 @@ SFDNSRES::SFDNSRES(Core::System& system_) : ServiceFramework{system_, "sfdnsres" | |||
| 31 | 51 | ||
| 32 | SFDNSRES::~SFDNSRES() = default; | 52 | SFDNSRES::~SFDNSRES() = default; |
| 33 | 53 | ||
| 34 | void SFDNSRES::GetAddrInfoRequest(Kernel::HLERequestContext& ctx) { | 54 | enum class NetDbError : s32 { |
| 55 | Internal = -1, | ||
| 56 | Success = 0, | ||
| 57 | HostNotFound = 1, | ||
| 58 | TryAgain = 2, | ||
| 59 | NoRecovery = 3, | ||
| 60 | NoData = 4, | ||
| 61 | }; | ||
| 62 | |||
| 63 | static NetDbError AddrInfoErrorToNetDbError(s32 result) { | ||
| 64 | // Best effort guess to map errors | ||
| 65 | switch (result) { | ||
| 66 | case 0: | ||
| 67 | return NetDbError::Success; | ||
| 68 | case EAI_AGAIN: | ||
| 69 | return NetDbError::TryAgain; | ||
| 70 | case EAI_NODATA: | ||
| 71 | return NetDbError::NoData; | ||
| 72 | default: | ||
| 73 | return NetDbError::HostNotFound; | ||
| 74 | } | ||
| 75 | } | ||
| 76 | |||
| 77 | static std::vector<u8> SerializeAddrInfo(const addrinfo* addrinfo, s32 result_code, | ||
| 78 | std::string_view host) { | ||
| 79 | // Adapted from | ||
| 80 | // https://github.com/switchbrew/libnx/blob/c5a9a909a91657a9818a3b7e18c9b91ff0cbb6e3/nx/source/runtime/resolver.c#L190 | ||
| 81 | std::vector<u8> data; | ||
| 82 | |||
| 83 | auto* current = addrinfo; | ||
| 84 | while (current != nullptr) { | ||
| 85 | struct SerializedResponseHeader { | ||
| 86 | u32 magic; | ||
| 87 | s32 flags; | ||
| 88 | s32 family; | ||
| 89 | s32 socket_type; | ||
| 90 | s32 protocol; | ||
| 91 | u32 address_length; | ||
| 92 | }; | ||
| 93 | static_assert(sizeof(SerializedResponseHeader) == 0x18, | ||
| 94 | "Response header size must be 0x18 bytes"); | ||
| 95 | |||
| 96 | constexpr auto header_size = sizeof(SerializedResponseHeader); | ||
| 97 | const auto addr_size = | ||
| 98 | current->ai_addr && current->ai_addrlen > 0 ? current->ai_addrlen : 4; | ||
| 99 | const auto canonname_size = current->ai_canonname ? strlen(current->ai_canonname) + 1 : 1; | ||
| 100 | |||
| 101 | const auto last_size = data.size(); | ||
| 102 | data.resize(last_size + header_size + addr_size + canonname_size); | ||
| 103 | |||
| 104 | // Header in network byte order | ||
| 105 | SerializedResponseHeader header{}; | ||
| 106 | |||
| 107 | constexpr auto HEADER_MAGIC = 0xBEEFCAFE; | ||
| 108 | header.magic = htonl(HEADER_MAGIC); | ||
| 109 | header.family = htonl(current->ai_family); | ||
| 110 | header.flags = htonl(current->ai_flags); | ||
| 111 | header.socket_type = htonl(current->ai_socktype); | ||
| 112 | header.protocol = htonl(current->ai_protocol); | ||
| 113 | header.address_length = current->ai_addr ? htonl((u32)current->ai_addrlen) : 0; | ||
| 114 | |||
| 115 | auto* header_ptr = data.data() + last_size; | ||
| 116 | std::memcpy(header_ptr, &header, header_size); | ||
| 117 | |||
| 118 | if (header.address_length == 0) { | ||
| 119 | std::memset(header_ptr + header_size, 0, 4); | ||
| 120 | } else { | ||
| 121 | switch (current->ai_family) { | ||
| 122 | case AF_INET: { | ||
| 123 | struct SockAddrIn { | ||
| 124 | s16 sin_family; | ||
| 125 | u16 sin_port; | ||
| 126 | u32 sin_addr; | ||
| 127 | u8 sin_zero[8]; | ||
| 128 | }; | ||
| 129 | |||
| 130 | SockAddrIn serialized_addr{}; | ||
| 131 | const auto addr = *reinterpret_cast<sockaddr_in*>(current->ai_addr); | ||
| 132 | serialized_addr.sin_port = htons(addr.sin_port); | ||
| 133 | serialized_addr.sin_family = htons(addr.sin_family); | ||
| 134 | serialized_addr.sin_addr = htonl(addr.sin_addr.s_addr); | ||
| 135 | std::memcpy(header_ptr + header_size, &serialized_addr, sizeof(SockAddrIn)); | ||
| 136 | |||
| 137 | char addr_string_buf[64]{}; | ||
| 138 | inet_ntop(AF_INET, &addr.sin_addr, addr_string_buf, std::size(addr_string_buf)); | ||
| 139 | LOG_INFO(Service, "Resolved host '{}' to IPv4 address {}", host, addr_string_buf); | ||
| 140 | break; | ||
| 141 | } | ||
| 142 | case AF_INET6: { | ||
| 143 | struct SockAddrIn6 { | ||
| 144 | s16 sin6_family; | ||
| 145 | u16 sin6_port; | ||
| 146 | u32 sin6_flowinfo; | ||
| 147 | u8 sin6_addr[16]; | ||
| 148 | u32 sin6_scope_id; | ||
| 149 | }; | ||
| 150 | |||
| 151 | SockAddrIn6 serialized_addr{}; | ||
| 152 | const auto addr = *reinterpret_cast<sockaddr_in6*>(current->ai_addr); | ||
| 153 | serialized_addr.sin6_family = htons(addr.sin6_family); | ||
| 154 | serialized_addr.sin6_port = htons(addr.sin6_port); | ||
| 155 | serialized_addr.sin6_flowinfo = htonl(addr.sin6_flowinfo); | ||
| 156 | serialized_addr.sin6_scope_id = htonl(addr.sin6_scope_id); | ||
| 157 | std::memcpy(serialized_addr.sin6_addr, &addr.sin6_addr, | ||
| 158 | sizeof(SockAddrIn6::sin6_addr)); | ||
| 159 | std::memcpy(header_ptr + header_size, &serialized_addr, sizeof(SockAddrIn6)); | ||
| 160 | |||
| 161 | char addr_string_buf[64]{}; | ||
| 162 | inet_ntop(AF_INET6, &addr.sin6_addr, addr_string_buf, std::size(addr_string_buf)); | ||
| 163 | LOG_INFO(Service, "Resolved host '{}' to IPv6 address {}", host, addr_string_buf); | ||
| 164 | break; | ||
| 165 | } | ||
| 166 | default: | ||
| 167 | std::memcpy(header_ptr + header_size, current->ai_addr, addr_size); | ||
| 168 | break; | ||
| 169 | } | ||
| 170 | } | ||
| 171 | if (current->ai_canonname) { | ||
| 172 | std::memcpy(header_ptr + addr_size, current->ai_canonname, canonname_size); | ||
| 173 | } else { | ||
| 174 | *(header_ptr + header_size + addr_size) = 0; | ||
| 175 | } | ||
| 176 | |||
| 177 | current = current->ai_next; | ||
| 178 | } | ||
| 179 | |||
| 180 | // 4-byte sentinel value | ||
| 181 | data.push_back(0); | ||
| 182 | data.push_back(0); | ||
| 183 | data.push_back(0); | ||
| 184 | data.push_back(0); | ||
| 185 | |||
| 186 | return data; | ||
| 187 | } | ||
| 188 | |||
| 189 | static std::pair<u32, s32> GetAddrInfoRequestImpl(Kernel::HLERequestContext& ctx) { | ||
| 35 | struct Parameters { | 190 | struct Parameters { |
| 36 | u8 use_nsd_resolve; | 191 | u8 use_nsd_resolve; |
| 37 | u32 unknown; | 192 | u32 unknown; |
| @@ -42,11 +197,51 @@ void SFDNSRES::GetAddrInfoRequest(Kernel::HLERequestContext& ctx) { | |||
| 42 | const auto parameters = rp.PopRaw<Parameters>(); | 197 | const auto parameters = rp.PopRaw<Parameters>(); |
| 43 | 198 | ||
| 44 | LOG_WARNING(Service, | 199 | LOG_WARNING(Service, |
| 45 | "(STUBBED) called. use_nsd_resolve={}, unknown=0x{:08X}, process_id=0x{:016X}", | 200 | "called with ignored parameters: use_nsd_resolve={}, unknown={}, process_id={}", |
| 46 | parameters.use_nsd_resolve, parameters.unknown, parameters.process_id); | 201 | parameters.use_nsd_resolve, parameters.unknown, parameters.process_id); |
| 47 | 202 | ||
| 48 | IPC::ResponseBuilder rb{ctx, 2}; | 203 | const auto host_buffer = ctx.ReadBuffer(0); |
| 204 | const std::string host = Common::StringFromBuffer(host_buffer); | ||
| 205 | |||
| 206 | const auto service_buffer = ctx.ReadBuffer(1); | ||
| 207 | const std::string service = Common::StringFromBuffer(service_buffer); | ||
| 208 | |||
| 209 | addrinfo* addrinfo; | ||
| 210 | // Pass null for hints. Serialized hints are also passed in a buffer, but are ignored for now | ||
| 211 | s32 result_code = getaddrinfo(host.c_str(), service.c_str(), nullptr, &addrinfo); | ||
| 212 | |||
| 213 | u32 data_size = 0; | ||
| 214 | if (result_code == 0 && addrinfo != nullptr) { | ||
| 215 | const std::vector<u8>& data = SerializeAddrInfo(addrinfo, result_code, host); | ||
| 216 | data_size = static_cast<u32>(data.size()); | ||
| 217 | freeaddrinfo(addrinfo); | ||
| 218 | |||
| 219 | ctx.WriteBuffer(data, 0); | ||
| 220 | } | ||
| 221 | |||
| 222 | return std::make_pair(data_size, result_code); | ||
| 223 | } | ||
| 224 | |||
| 225 | void SFDNSRES::GetAddrInfoRequest(Kernel::HLERequestContext& ctx) { | ||
| 226 | auto [data_size, result_code] = GetAddrInfoRequestImpl(ctx); | ||
| 227 | |||
| 228 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 229 | rb.Push(ResultSuccess); | ||
| 230 | rb.Push(static_cast<s32>(AddrInfoErrorToNetDbError(result_code))); // NetDBErrorCode | ||
| 231 | rb.Push(result_code); // errno | ||
| 232 | rb.Push(data_size); // serialized size | ||
| 233 | } | ||
| 234 | |||
| 235 | void SFDNSRES::GetAddrInfoRequestWithOptions(Kernel::HLERequestContext& ctx) { | ||
| 236 | // Additional options are ignored | ||
| 237 | auto [data_size, result_code] = GetAddrInfoRequestImpl(ctx); | ||
| 238 | |||
| 239 | IPC::ResponseBuilder rb{ctx, 5}; | ||
| 49 | rb.Push(ResultSuccess); | 240 | rb.Push(ResultSuccess); |
| 241 | rb.Push(data_size); // serialized size | ||
| 242 | rb.Push(result_code); // errno | ||
| 243 | rb.Push(static_cast<s32>(AddrInfoErrorToNetDbError(result_code))); // NetDBErrorCode | ||
| 244 | rb.Push(0); | ||
| 50 | } | 245 | } |
| 51 | 246 | ||
| 52 | } // namespace Service::Sockets | 247 | } // namespace Service::Sockets \ No newline at end of file |
diff --git a/src/core/hle/service/sockets/sfdnsres.h b/src/core/hle/service/sockets/sfdnsres.h index 5d3b4dc2d..f0c57377d 100644 --- a/src/core/hle/service/sockets/sfdnsres.h +++ b/src/core/hle/service/sockets/sfdnsres.h | |||
| @@ -19,6 +19,7 @@ public: | |||
| 19 | 19 | ||
| 20 | private: | 20 | private: |
| 21 | void GetAddrInfoRequest(Kernel::HLERequestContext& ctx); | 21 | void GetAddrInfoRequest(Kernel::HLERequestContext& ctx); |
| 22 | void GetAddrInfoRequestWithOptions(Kernel::HLERequestContext& ctx); | ||
| 22 | }; | 23 | }; |
| 23 | 24 | ||
| 24 | } // namespace Service::Sockets | 25 | } // namespace Service::Sockets |
diff --git a/src/core/hle/service/sockets/sockets.h b/src/core/hle/service/sockets/sockets.h index 02dbbae40..d69c86431 100644 --- a/src/core/hle/service/sockets/sockets.h +++ b/src/core/hle/service/sockets/sockets.h | |||
| @@ -46,6 +46,7 @@ enum class Protocol : u32 { | |||
| 46 | 46 | ||
| 47 | enum class OptName : u32 { | 47 | enum class OptName : u32 { |
| 48 | REUSEADDR = 0x4, | 48 | REUSEADDR = 0x4, |
| 49 | KEEPALIVE = 0x8, | ||
| 49 | BROADCAST = 0x20, | 50 | BROADCAST = 0x20, |
| 50 | LINGER = 0x80, | 51 | LINGER = 0x80, |
| 51 | SNDBUF = 0x1001, | 52 | SNDBUF = 0x1001, |
diff --git a/src/core/network/network.cpp b/src/core/network/network.cpp index a3e0664b9..0784a165d 100644 --- a/src/core/network/network.cpp +++ b/src/core/network/network.cpp | |||
| @@ -600,6 +600,10 @@ Errno Socket::SetReuseAddr(bool enable) { | |||
| 600 | return SetSockOpt<u32>(fd, SO_REUSEADDR, enable ? 1 : 0); | 600 | return SetSockOpt<u32>(fd, SO_REUSEADDR, enable ? 1 : 0); |
| 601 | } | 601 | } |
| 602 | 602 | ||
| 603 | Errno Socket::SetKeepAlive(bool enable) { | ||
| 604 | return SetSockOpt<u32>(fd, SO_KEEPALIVE, enable ? 1 : 0); | ||
| 605 | } | ||
| 606 | |||
| 603 | Errno Socket::SetBroadcast(bool enable) { | 607 | Errno Socket::SetBroadcast(bool enable) { |
| 604 | return SetSockOpt<u32>(fd, SO_BROADCAST, enable ? 1 : 0); | 608 | return SetSockOpt<u32>(fd, SO_BROADCAST, enable ? 1 : 0); |
| 605 | } | 609 | } |
diff --git a/src/core/network/sockets.h b/src/core/network/sockets.h index 5e39e7c54..caaefce7c 100644 --- a/src/core/network/sockets.h +++ b/src/core/network/sockets.h | |||
| @@ -67,6 +67,8 @@ public: | |||
| 67 | 67 | ||
| 68 | Errno SetReuseAddr(bool enable); | 68 | Errno SetReuseAddr(bool enable); |
| 69 | 69 | ||
| 70 | Errno SetKeepAlive(bool enable); | ||
| 71 | |||
| 70 | Errno SetBroadcast(bool enable); | 72 | Errno SetBroadcast(bool enable); |
| 71 | 73 | ||
| 72 | Errno SetSndBuf(u32 value); | 74 | Errno SetSndBuf(u32 value); |
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp index 57f807826..ae2e62dc5 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp | |||
| @@ -123,14 +123,15 @@ void EmuWindow_SDL2::ShowCursor(bool show_cursor) { | |||
| 123 | } | 123 | } |
| 124 | 124 | ||
| 125 | void EmuWindow_SDL2::Fullscreen() { | 125 | void EmuWindow_SDL2::Fullscreen() { |
| 126 | SDL_DisplayMode display_mode; | ||
| 126 | switch (Settings::values.fullscreen_mode.GetValue()) { | 127 | switch (Settings::values.fullscreen_mode.GetValue()) { |
| 127 | case Settings::FullscreenMode::Exclusive: | 128 | case Settings::FullscreenMode::Exclusive: |
| 128 | // Set window size to render size before entering fullscreen -- SDL does not resize to | 129 | // Set window size to render size before entering fullscreen -- SDL2 does not resize window |
| 129 | // display dimensions in this mode. | 130 | // to display dimensions automatically in this mode. |
| 130 | // TODO: Multiply the window size by resolution_factor (for both docked modes) | 131 | if (SDL_GetDesktopDisplayMode(0, &display_mode) == 0) { |
| 131 | if (Settings::values.use_docked_mode) { | 132 | SDL_SetWindowSize(render_window, display_mode.w, display_mode.h); |
| 132 | SDL_SetWindowSize(render_window, Layout::ScreenDocked::Width, | 133 | } else { |
| 133 | Layout::ScreenDocked::Height); | 134 | LOG_ERROR(Frontend, "SDL_GetDesktopDisplayMode failed: {}", SDL_GetError()); |
| 134 | } | 135 | } |
| 135 | 136 | ||
| 136 | if (SDL_SetWindowFullscreen(render_window, SDL_WINDOW_FULLSCREEN) == 0) { | 137 | if (SDL_SetWindowFullscreen(render_window, SDL_WINDOW_FULLSCREEN) == 0) { |