diff --git a/include/kernel/kernel.hpp b/include/kernel/kernel.hpp index 9f9dfc33..c602d480 100644 --- a/include/kernel/kernel.hpp +++ b/include/kernel/kernel.hpp @@ -46,6 +46,10 @@ class Kernel { Handle makeSession(Handle port); Handle makeThread(u32 entrypoint, u32 initialSP, u32 priority, s32 id, u32 arg,ThreadStatus status = ThreadStatus::Dormant); +public: + Handle makeMutex(bool locked = false); // Needs to be public to be accessible to the APT/DSP services + +private: void signalArbiter(u32 waitingAddress, s32 threadCount); void sleepThread(s64 ns); void sleepThreadOnArbiter(u32 waitingAddress); diff --git a/include/kernel/kernel_types.hpp b/include/kernel/kernel_types.hpp index e0547cfb..ab8a1af8 100644 --- a/include/kernel/kernel_types.hpp +++ b/include/kernel/kernel_types.hpp @@ -150,6 +150,13 @@ static const char* kernelObjectTypeToString(KernelObjectType t) { } } +struct Mutex { + Handle ownerThread = 0; // Index of the thread that holds the mutex if it's locked + bool locked; + + Mutex(bool lock = false) : locked(lock) {} +}; + // Generic kernel object class struct KernelObject { Handle handle = 0; // A u32 the OS will use to identify objects @@ -166,4 +173,8 @@ struct KernelObject { T* getData() { return static_cast(data); } + + const char* getTypeName() { + return kernelObjectTypeToString(type); + } }; \ No newline at end of file diff --git a/include/services/apt.hpp b/include/services/apt.hpp index 3dc80019..99a1e813 100644 --- a/include/services/apt.hpp +++ b/include/services/apt.hpp @@ -1,12 +1,20 @@ #pragma once +#include #include "helpers.hpp" #include "kernel_types.hpp" #include "logger.hpp" #include "memory.hpp" +// Yay, more circular dependencies +class Kernel; + class APTService { Handle handle = KernelHandles::APT; Memory& mem; + Kernel& kernel; + + std::optional lockHandle = std::nullopt; + MAKE_LOG_FUNCTION(log, aptLogger) // Service commands @@ -25,7 +33,7 @@ class APTService { u32 cpuTimeLimit; public: - APTService(Memory& mem) : mem(mem) {} + APTService(Memory& mem, Kernel& kernel) : mem(mem), kernel(kernel) {} void reset(); void handleSyncRequest(u32 messagePointer); }; \ No newline at end of file diff --git a/src/core/kernel/events.cpp b/src/core/kernel/events.cpp index f3132473..a914865f 100644 --- a/src/core/kernel/events.cpp +++ b/src/core/kernel/events.cpp @@ -79,7 +79,7 @@ void Kernel::waitSynchronization1() { } if (!isWaitable(object)) [[unlikely]] { - Helpers::panic("Tried to wait on a non waitable object. Type: %s, handle: %X\n", kernelObjectTypeToString(object->type), handle); + Helpers::panic("Tried to wait on a non waitable object. Type: %s, handle: %X\n", object->getTypeName(), handle); } regs[0] = SVCResult::Success; @@ -124,7 +124,7 @@ void Kernel::waitSynchronizationN() { } if (!isWaitable(object)) [[unlikely]] { - Helpers::panic("Tried to wait on a non waitable object. Type: %s, handle: %X\n", kernelObjectTypeToString(object->type), handle); + Helpers::panic("Tried to wait on a non waitable object. Type: %s, handle: %X\n", object->getTypeName(), handle); } } diff --git a/src/core/kernel/threads.cpp b/src/core/kernel/threads.cpp index ff4ae9e6..ae697e48 100644 --- a/src/core/kernel/threads.cpp +++ b/src/core/kernel/threads.cpp @@ -137,6 +137,18 @@ Handle Kernel::makeThread(u32 entrypoint, u32 initialSP, u32 priority, s32 id, u return ret; } +Handle Kernel::makeMutex(bool locked) { + Handle ret = makeObject(KernelObjectType::Mutex); + objects[ret].data = new Mutex(locked); + + // If the mutex is initially locked, store the index of the thread that owns it + if (locked) { + objects[ret].getData()->ownerThread = currentThreadIndex; + } + + return ret; +} + void Kernel::sleepThreadOnArbiter(u32 waitingAddress) { Thread& t = threads[currentThreadIndex]; t.status = ThreadStatus::WaitArbiter; @@ -260,10 +272,9 @@ void Kernel::setThreadPriority() { void Kernel::createMutex() { bool locked = regs[1] != 0; -// Helpers::panic("CreateMutex (initially locked: %s)\n", locked ? "yes" : "no"); regs[0] = SVCResult::Success; - regs[1] = makeObject(KernelObjectType::Mutex); + regs[1] = makeMutex(locked); } void Kernel::releaseMutex() { diff --git a/src/core/services/apt.cpp b/src/core/services/apt.cpp index 5e41c7b7..8fd86eaa 100644 --- a/src/core/services/apt.cpp +++ b/src/core/services/apt.cpp @@ -1,4 +1,5 @@ #include "services/apt.hpp" +#include "kernel.hpp" namespace APTCommands { enum : u32 { @@ -32,6 +33,8 @@ namespace Result { void APTService::reset() { // Set the default CPU time limit to 30%. Seems safe, as this is what Metroid 2 uses by default cpuTimeLimit = 30; + + lockHandle = std::nullopt; } void APTService::handleSyncRequest(u32 messagePointer) { @@ -80,12 +83,18 @@ void APTService::enable(u32 messagePointer) { } void APTService::getLockHandle(u32 messagePointer) { - log("APT::GetLockHandle"); + log("APT::GetLockHandle\n"); + + // Create a lock handle if none exists + if (!lockHandle.has_value() || kernel.getObject(lockHandle.value(), KernelObjectType::Mutex) == nullptr) { + lockHandle = kernel.makeMutex(); + } + mem.write32(messagePointer + 4, Result::Success); // Result code mem.write32(messagePointer + 8, 0); // AppletAttr mem.write32(messagePointer + 12, 0); // APT State (bit0 = Power Button State, bit1 = Order To Close State) mem.write32(messagePointer + 16, 0); // Translation descriptor - mem.write32(messagePointer + 20, 0x34563456); // Lock handle + mem.write32(messagePointer + 20, lockHandle.value()); // Lock handle } // This apparently does nothing on the original kernel either? diff --git a/src/core/services/service_manager.cpp b/src/core/services/service_manager.cpp index c22c3680..3a701852 100644 --- a/src/core/services/service_manager.cpp +++ b/src/core/services/service_manager.cpp @@ -1,7 +1,7 @@ #include "services/service_manager.hpp" ServiceManager::ServiceManager(std::array& regs, Memory& mem, GPU& gpu, u32& currentPID, Kernel& kernel) - : regs(regs), mem(mem), apt(mem), cecd(mem), cfg(mem), dsp(mem), hid(mem), fs(mem, kernel), gsp_gpu(mem, gpu, currentPID), + : regs(regs), mem(mem), apt(mem, kernel), cecd(mem), cfg(mem), dsp(mem), hid(mem), fs(mem, kernel), gsp_gpu(mem, gpu, currentPID), gsp_lcd(mem), mic(mem), ndm(mem), ptm(mem) {} void ServiceManager::reset() {