summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/CMakeLists.txt2
-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/hle/kernel/hle_ipc.cpp5
-rw-r--r--src/core/hle/kernel/k_auto_object.h5
-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_server_port.cpp6
-rw-r--r--src/core/hle/kernel/k_server_session.cpp3
-rw-r--r--src/core/hle/kernel/kernel.cpp41
-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/yuzu/main.cpp8
-rw-r--r--src/yuzu/uisettings.cpp8
-rw-r--r--src/yuzu/uisettings.h2
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp13
23 files changed, 898 insertions, 57 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index ffbda7925..b681d21a7 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -460,6 +460,8 @@ add_library(core STATIC
460 hle/service/hid/controllers/touchscreen.h 460 hle/service/hid/controllers/touchscreen.h
461 hle/service/hid/controllers/xpad.cpp 461 hle/service/hid/controllers/xpad.cpp
462 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
463 hle/service/jit/jit.cpp 465 hle/service/jit/jit.cpp
464 hle/service/jit/jit.h 466 hle/service/jit/jit.h
465 hle/service/lbl/lbl.cpp 467 hle/service/lbl/lbl.cpp
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/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 abdb8ae7c..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
@@ -168,6 +166,7 @@ public:
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_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/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 481a0d7cb..d840d44e6 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -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.
@@ -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
@@ -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 8a6bd59ff..42f9cf811 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -390,8 +390,12 @@ public:
390 390
391 if (bss_size) { 391 if (bss_size) {
392 auto block_guard = detail::ScopeExit([&] { 392 auto block_guard = detail::ScopeExit([&] {
393 page_table.UnmapCodeMemory(addr + nro_size, bss_addr, bss_size); 393 page_table.UnmapCodeMemory(
394 page_table.UnmapCodeMemory(addr, nro_addr, nro_size); 394 addr + nro_size, bss_addr, bss_size,
395 Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange);
396 page_table.UnmapCodeMemory(
397 addr, nro_addr, nro_size,
398 Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange);
395 }); 399 });
396 400
397 const ResultCode result{ 401 const ResultCode result{
@@ -571,17 +575,21 @@ public:
571 auto& page_table{system.CurrentProcess()->PageTable()}; 575 auto& page_table{system.CurrentProcess()->PageTable()};
572 576
573 if (info.bss_size != 0) { 577 if (info.bss_size != 0) {
574 CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address + info.text_size + 578 CASCADE_CODE(page_table.UnmapCodeMemory(
575 info.ro_size + info.data_size, 579 info.nro_address + info.text_size + info.ro_size + info.data_size, info.bss_address,
576 info.bss_address, info.bss_size)); 580 info.bss_size, Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange));
577 } 581 }
578 582
579 CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address + info.text_size + info.ro_size, 583 CASCADE_CODE(page_table.UnmapCodeMemory(
580 info.src_addr + info.text_size + info.ro_size, 584 info.nro_address + info.text_size + info.ro_size,
581 info.data_size)); 585 info.src_addr + info.text_size + info.ro_size, info.data_size,
582 CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address + info.text_size, 586 Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange));
583 info.src_addr + info.text_size, info.ro_size)); 587 CASCADE_CODE(page_table.UnmapCodeMemory(
584 CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address, info.src_addr, info.text_size)); 588 info.nro_address + info.text_size, info.src_addr + info.text_size, info.ro_size,
589 Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange));
590 CASCADE_CODE(page_table.UnmapCodeMemory(
591 info.nro_address, info.src_addr, info.text_size,
592 Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange));
585 return ResultSuccess; 593 return ResultSuccess;
586 } 594 }
587 595
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/yuzu/main.cpp b/src/yuzu/main.cpp
index 62d15f8cd..52879a989 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -3652,6 +3652,14 @@ void GMainWindow::UpdateUITheme() {
3652 setStyleSheet({}); 3652 setStyleSheet({});
3653 } 3653 }
3654 3654
3655 QPalette new_pal(qApp->palette());
3656 if (UISettings::IsDarkTheme()) {
3657 new_pal.setColor(QPalette::Link, QColor(0, 190, 255, 255));
3658 } else {
3659 new_pal.setColor(QPalette::Link, QColor(0, 140, 200, 255));
3660 }
3661 qApp->setPalette(new_pal);
3662
3655 QIcon::setThemeName(current_theme); 3663 QIcon::setThemeName(current_theme);
3656 QIcon::setThemeSearchPaths(theme_paths); 3664 QIcon::setThemeSearchPaths(theme_paths);
3657} 3665}
diff --git a/src/yuzu/uisettings.cpp b/src/yuzu/uisettings.cpp
index 21683576c..f683b80f7 100644
--- a/src/yuzu/uisettings.cpp
+++ b/src/yuzu/uisettings.cpp
@@ -15,6 +15,14 @@ const Themes themes{{
15 {"Midnight Blue Colorful", "colorful_midnight_blue"}, 15 {"Midnight Blue Colorful", "colorful_midnight_blue"},
16}}; 16}};
17 17
18bool IsDarkTheme() {
19 const auto& theme = UISettings::values.theme;
20 return theme == QStringLiteral("qdarkstyle") ||
21 theme == QStringLiteral("qdarkstyle_midnight_blue") ||
22 theme == QStringLiteral("colorful_dark") ||
23 theme == QStringLiteral("colorful_midnight_blue");
24}
25
18Values values = {}; 26Values values = {};
19 27
20} // namespace UISettings 28} // namespace UISettings
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h
index cc5aee382..15ba9ea17 100644
--- a/src/yuzu/uisettings.h
+++ b/src/yuzu/uisettings.h
@@ -17,6 +17,8 @@
17 17
18namespace UISettings { 18namespace UISettings {
19 19
20bool IsDarkTheme();
21
20struct ContextualShortcut { 22struct ContextualShortcut {
21 QString keyseq; 23 QString keyseq;
22 QString controller_keyseq; 24 QString controller_keyseq;
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) {