summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/CMakeLists.txt4
-rw-r--r--src/core/arm/arm_interface.cpp141
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp2
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp2
-rw-r--r--src/core/arm/symbols.cpp190
-rw-r--r--src/core/arm/symbols.h27
-rw-r--r--src/core/file_sys/patch_manager.cpp63
-rw-r--r--src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp6
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp5
-rw-r--r--src/core/hle/kernel/k_auto_object.h7
-rw-r--r--src/core/hle/kernel/k_code_memory.cpp11
-rw-r--r--src/core/hle/kernel/k_page_table.cpp11
-rw-r--r--src/core/hle/kernel/k_page_table.h5
-rw-r--r--src/core/hle/kernel/k_process.h2
-rw-r--r--src/core/hle/kernel/k_scheduler_lock.h3
-rw-r--r--src/core/hle/kernel/k_server_port.cpp6
-rw-r--r--src/core/hle/kernel/k_server_session.cpp3
-rw-r--r--src/core/hle/kernel/k_thread.cpp4
-rw-r--r--src/core/hle/kernel/k_thread.h3
-rw-r--r--src/core/hle/kernel/kernel.cpp49
-rw-r--r--src/core/hle/kernel/kernel.h8
-rw-r--r--src/core/hle/kernel/svc.cpp3
-rw-r--r--src/core/hle/service/jit/jit.cpp291
-rw-r--r--src/core/hle/service/jit/jit_context.cpp424
-rw-r--r--src/core/hle/service/jit/jit_context.h65
-rw-r--r--src/core/hle/service/ldr/ldr.cpp30
-rw-r--r--src/core/hle/service/sm/sm.cpp8
-rw-r--r--src/core/hle/service/sm/sm.h2
-rw-r--r--src/core/hle/service/sockets/bsd.cpp3
-rw-r--r--src/core/hle/service/sockets/sfdnsres.cpp205
-rw-r--r--src/core/hle/service/sockets/sfdnsres.h1
-rw-r--r--src/core/hle/service/sockets/sockets.h1
-rw-r--r--src/core/network/network.cpp4
-rw-r--r--src/core/network/sockets.h2
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp13
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
15namespace Core { 17namespace Core {
16namespace {
17
18constexpr u64 ELF_DYNAMIC_TAG_NULL = 0;
19constexpr u64 ELF_DYNAMIC_TAG_STRTAB = 5;
20constexpr u64 ELF_DYNAMIC_TAG_SYMTAB = 6;
21constexpr u64 ELF_DYNAMIC_TAG_SYMENT = 11;
22
23enum 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
33enum class ELFSymbolBinding : u8 {
34 Local = 0,
35 Global = 1,
36 Weak = 2,
37};
38
39enum class ELFSymbolVisibility : u8 {
40 Default = 0,
41 Internal = 1,
42 Hidden = 2,
43 Protected = 3,
44};
45
46struct 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};
59static_assert(sizeof(ELFSymbol) == 0x18, "ELFSymbol has incorrect size.");
60
61using Symbols = std::vector<std::pair<ELFSymbol, std::string>>;
62
63Symbols 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
123std::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
140constexpr u64 SEGMENT_BASE = 0x7100000000ull; 19constexpr 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
11namespace Core {
12namespace {
13
14constexpr u64 ELF_DYNAMIC_TAG_NULL = 0;
15constexpr u64 ELF_DYNAMIC_TAG_STRTAB = 5;
16constexpr u64 ELF_DYNAMIC_TAG_SYMTAB = 6;
17constexpr u64 ELF_DYNAMIC_TAG_SYMENT = 11;
18
19enum 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
29enum class ELFSymbolBinding : u8 {
30 Local = 0,
31 Global = 1,
32 Weak = 2,
33};
34
35enum class ELFSymbolVisibility : u8 {
36 Default = 0,
37 Internal = 1,
38 Hidden = 2,
39 Protected = 3,
40};
41
42struct 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};
55static_assert(sizeof(ELF64Symbol) == 0x18, "ELF64Symbol has incorrect size.");
56
57struct 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};
70static_assert(sizeof(ELF32Symbol) == 0x10, "ELF32Symbol has incorrect size.");
71
72} // Anonymous namespace
73
74namespace Symbols {
75
76template <typename Word, typename ELFSymbol, typename ByteReader>
77static 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
151Symbols 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
162Symbols 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
174std::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
15namespace Core::Memory {
16class Memory;
17} // namespace Core::Memory
18
19namespace Core::Symbols {
20
21using Symbols = std::map<std::string, std::pair<VAddr, std::size_t>, std::less<>>;
22
23Symbols GetSymbols(VAddr base, Core::Memory::Memory& memory, bool is_64 = true);
24Symbols GetSymbols(std::span<const u8> data, bool is_64 = true);
25std::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
150u64 KSystemControl::GenerateRandomU64() { 150u64 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
60void SessionRequestHandler::ClientConnected(KServerSession* session) { 60void 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
64void SessionRequestHandler::ClientDisconnected(KServerSession* session) { 67void 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
349ResultCode KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size) { 349ResultCode 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
27class KPageTable final { 27class KPageTable final {
28public: 28public:
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
67bool KServerPort::IsSignaled() const { 73bool 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
54void KServerSession::OnClientClosed() { 57void 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
938void KernelCore::RegisterServerObject(KAutoObject* server_object) {
939 impl->RegisterServerObject(server_object);
940}
941
942void KernelCore::UnregisterServerObject(KAutoObject* server_object) {
943 impl->UnregisterServerObject(server_object);
944}
945
931void KernelCore::RegisterKernelObject(KAutoObject* object) { 946void 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
10namespace Service::JIT { 16namespace Service::JIT {
11 17
18struct CodeRange {
19 u64 offset;
20 u64 size;
21};
22
12class IJitEnvironment final : public ServiceFramework<IJitEnvironment> { 23class IJitEnvironment final : public ServiceFramework<IJitEnvironment> {
13public: 24public:
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
228private:
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
28class JITU final : public ServiceFramework<JITU> { 257class 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
19namespace Service::JIT {
20
21constexpr std::array<u8, 4> STOP_ARM64 = {
22 0x01, 0x00, 0x00, 0xd4, // svc #0
23};
24
25constexpr std::array<u8, 8> RESOLVE_ARM64 = {
26 0x21, 0x00, 0x00, 0xd4, // svc #1
27 0xc0, 0x03, 0x5f, 0xd6, // ret
28};
29
30constexpr std::array<u8, 4> PANIC_ARM64 = {
31 0x41, 0x00, 0x00, 0xd4, // svc #2
32};
33
34constexpr 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
52constexpr 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
62struct HelperFunction {
63 const char* name;
64 const std::span<const u8> data;
65};
66
67constexpr 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
76struct Elf64_Dyn {
77 u64 d_tag;
78 u64 d_un;
79};
80
81struct Elf64_Rela {
82 u64 r_offset;
83 u64 r_info;
84 s64 r_addend;
85};
86
87static constexpr u32 Elf64_RelaType(const Elf64_Rela* rela) {
88 return static_cast<u32>(rela->r_info);
89}
90
91constexpr int DT_RELA = 7; /* Address of Rela relocs */
92constexpr int DT_RELASZ = 8; /* Total size of Rela relocs */
93constexpr int R_AARCH64_RELATIVE = 1027; /* Adjust by program base. */
94
95constexpr size_t STACK_ALIGN = 16;
96
97class JITContextImpl;
98
99using IntervalSet = boost::icl::interval_set<VAddr>::type;
100using IntervalType = boost::icl::interval_set<VAddr>::interval_type;
101
102class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
103public:
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
204private:
205 Core::Memory::Memory& memory;
206 std::vector<u8>& local_memory;
207 IntervalSet& mapped_ranges;
208 JITContextImpl& parent;
209};
210
211class JITContextImpl {
212public:
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
353void 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
381void DynarmicCallbacks64::ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) {
382 LOG_CRITICAL(Service_JIT, "Illegal operation PC @ {:08x}", pc);
383 parent.jit->HaltExecution();
384}
385
386void DynarmicCallbacks64::InterpreterFallback(u64 pc, size_t num_instructions) {
387 LOG_CRITICAL(Service_JIT, "Unimplemented instruction PC @ {:08x}", pc);
388 parent.jit->HaltExecution();
389}
390
391JITContext::JITContext(Core::Memory::Memory& memory)
392 : impl{std::make_unique<JITContextImpl>(memory)} {}
393
394JITContext::~JITContext() {}
395
396bool JITContext::LoadNRO(std::span<const u8> data) {
397 return impl->LoadNRO(data);
398}
399
400void JITContext::MapProcessMemory(VAddr dest_address, std::size_t size) {
401 impl->MapProcessMemory(dest_address, size);
402}
403
404u64 JITContext::CallFunction(VAddr func) {
405 return impl->CallFunction(func);
406}
407
408void JITContext::PushArgument(const void* data, size_t size) {
409 impl->PushArgument(data, size);
410}
411
412VAddr JITContext::GetHelper(const std::string& name) {
413 return impl->GetHelper(name);
414}
415
416VAddr JITContext::AddHeap(const void* data, size_t size) {
417 return impl->AddHeap(data, size);
418}
419
420void 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
13namespace Core::Memory {
14class Memory;
15}
16
17namespace Service::JIT {
18
19class JITContextImpl;
20
21class JITContext {
22public:
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
59private:
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
227SM::~SM() { 227SM::~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;
22class KClientSession; 22class KClientSession;
23class KernelCore; 23class KernelCore;
24class KPort; 24class KPort;
25class KServerPort;
26class SessionRequestHandler; 25class 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
54class ServiceManager { 52class 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
8namespace Service::Sockets { 28namespace 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
32SFDNSRES::~SFDNSRES() = default; 52SFDNSRES::~SFDNSRES() = default;
33 53
34void SFDNSRES::GetAddrInfoRequest(Kernel::HLERequestContext& ctx) { 54enum class NetDbError : s32 {
55 Internal = -1,
56 Success = 0,
57 HostNotFound = 1,
58 TryAgain = 2,
59 NoRecovery = 3,
60 NoData = 4,
61};
62
63static 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
77static 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
189static 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
225void 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
235void 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
20private: 20private:
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
47enum class OptName : u32 { 47enum 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
603Errno Socket::SetKeepAlive(bool enable) {
604 return SetSockOpt<u32>(fd, SO_KEEPALIVE, enable ? 1 : 0);
605}
606
603Errno Socket::SetBroadcast(bool enable) { 607Errno 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
125void EmuWindow_SDL2::Fullscreen() { 125void 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) {