diff --git a/include/kernel/kernel_types.hpp b/include/kernel/kernel_types.hpp index c7a5e26e..302841a4 100644 --- a/include/kernel/kernel_types.hpp +++ b/include/kernel/kernel_types.hpp @@ -182,10 +182,10 @@ struct Mutex { struct Semaphore { u64 waitlist; // Refer to the getWaitlist function below for documentation - u32 initialCount; - u32 maximumCount; + s32 availableCount; + s32 maximumCount; - Semaphore(u32 initialCount, u32 maximumCount) : initialCount(initialCount), maximumCount(maximumCount), waitlist(0) {} + Semaphore(s32 initialCount, s32 maximumCount) : availableCount(initialCount), maximumCount(maximumCount), waitlist(0) {} }; struct MemoryBlock { diff --git a/include/services/dsp.hpp b/include/services/dsp.hpp index 5ddf6e14..f3e4d51f 100644 --- a/include/services/dsp.hpp +++ b/include/services/dsp.hpp @@ -1,4 +1,6 @@ #pragma once +#include +#include #include "helpers.hpp" #include "logger.hpp" #include "memory.hpp" @@ -41,18 +43,38 @@ public: } }; +// Circular dependencies! +class Kernel; + class DSPService { Handle handle = KernelHandles::DSP; Memory& mem; + Kernel& kernel; MAKE_LOG_FUNCTION(log, dspServiceLogger) + // Number of DSP pipes + static constexpr size_t pipeCount = 8; DSPPipe audioPipe; + // DSP service event handles + using DSPEvent = std::optional; + + DSPEvent semaphoreEvent; + DSPEvent interrupt0; + DSPEvent interrupt1; + std::array pipeEvents; + + DSPEvent& getEventRef(u32 type, u32 pipe); + static constexpr size_t maxEventCount = 6; + + // Total number of DSP service events registered with registerInterruptEvents + size_t totalEventCount; + // Service functions void convertProcessAddressFromDspDram(u32 messagePointer); // Nice function name void flushDataCache(u32 messagePointer); void getHeadphoneStatus(u32 messagePointer); - void getSemaphoreHandle(u32 messagePointer); + void getSemaphoreEventHandle(u32 messagePointer); void invalidateDCache(u32 messagePointer); void loadComponent(u32 messagePointer); void readPipeIfPossible(u32 messagePointer); @@ -62,7 +84,7 @@ class DSPService { void writeProcessPipe(u32 messagePointer); public: - DSPService(Memory& mem) : mem(mem) {} + DSPService(Memory& mem, Kernel& kernel) : mem(mem), kernel(kernel) {} void reset(); void handleSyncRequest(u32 messagePointer); diff --git a/src/core/kernel/address_arbiter.cpp b/src/core/kernel/address_arbiter.cpp index d8be5d66..daaab00e 100644 --- a/src/core/kernel/address_arbiter.cpp +++ b/src/core/kernel/address_arbiter.cpp @@ -25,6 +25,7 @@ Handle Kernel::makeArbiter() { // Result CreateAddressArbiter(Handle* arbiter) void Kernel::createAddressArbiter() { + logSVC("CreateAddressArbiter\n"); regs[0] = SVCResult::Success; regs[1] = makeArbiter(); } diff --git a/src/core/kernel/events.cpp b/src/core/kernel/events.cpp index 1ab195c0..7edb358c 100644 --- a/src/core/kernel/events.cpp +++ b/src/core/kernel/events.cpp @@ -28,8 +28,8 @@ bool Kernel::signalEvent(Handle handle) { Event* event = object->getData(); event->fired = true; - // One shot events go back to being not fired once they wake up one or more thread - if (event->waitlist != 0 && event->resetType == ResetType::OneShot) { + // One shot events go back to being not fired once they are signaled + if (event->resetType == ResetType::Pulse) { event->fired = false; } @@ -165,9 +165,6 @@ void Kernel::waitSynchronizationN() { logSVC("WaitSynchronizationN (handle pointer: %08X, count: %d, timeout = %lld)\n", handles, handleCount, ns); - if (waitAll && handleCount > 1) - Helpers::panic("Trying to wait on more than 1 object"); - if (handleCount < 0) Helpers::panic("WaitSyncN: Invalid handle count"); diff --git a/src/core/kernel/threads.cpp b/src/core/kernel/threads.cpp index c796fa1f..27f87b05 100644 --- a/src/core/kernel/threads.cpp +++ b/src/core/kernel/threads.cpp @@ -272,7 +272,7 @@ void Kernel::createThread() { // void SleepThread(s64 nanoseconds) void Kernel::svcSleepThread() { const s64 ns = s64(u64(regs[0]) | (u64(regs[1]) << 32)); - logSVC("SleepThread(ns = %lld)\n", ns); + //logSVC("SleepThread(ns = %lld)\n", ns); regs[0] = SVCResult::Success; sleepThread(ns); @@ -417,8 +417,8 @@ bool Kernel::shouldWaitOnObject(KernelObject* object) { case KernelObjectType::Thread: // Waiting on a thread waits until it's dead. If it's dead then no need to wait return object->getData()->status != ThreadStatus::Dead; - case KernelObjectType::Semaphore: - Helpers::panic("No semaphore :("); + case KernelObjectType::Semaphore: // Wait if the semaphore count <= 0 + return object->getData()->availableCount <= 0; default: Helpers::panic("Not sure whether to wait on object (type: %s)", object->getTypeName()); diff --git a/src/core/services/dsp.cpp b/src/core/services/dsp.cpp index 8635f43e..7309463f 100644 --- a/src/core/services/dsp.cpp +++ b/src/core/services/dsp.cpp @@ -1,5 +1,6 @@ #include "services/dsp.hpp" #include "ipc.hpp" +#include "kernel.hpp" namespace DSPCommands { enum : u32 { @@ -11,7 +12,7 @@ namespace DSPCommands { FlushDataCache = 0x00130082, InvalidateDataCache = 0x00140082, RegisterInterruptEvents = 0x00150082, - GetSemaphoreHandle = 0x00160000, + GetSemaphoreEventHandle = 0x00160000, SetSemaphoreMask = 0x00170040, GetHeadphoneStatus = 0x001F0000 }; @@ -27,6 +28,15 @@ namespace Result { void DSPService::reset() { audioPipe.reset(); + totalEventCount = 0; + + semaphoreEvent = std::nullopt; + interrupt0 = std::nullopt; + interrupt1 = std::nullopt; + + for (DSPEvent& e : pipeEvents) { + e = std::nullopt; + } } void DSPService::handleSyncRequest(u32 messagePointer) { @@ -36,7 +46,7 @@ void DSPService::handleSyncRequest(u32 messagePointer) { case DSPCommands::FlushDataCache: flushDataCache(messagePointer); break; case DSPCommands::InvalidateDataCache: invalidateDCache(messagePointer); break; case DSPCommands::GetHeadphoneStatus: getHeadphoneStatus(messagePointer); break; - case DSPCommands::GetSemaphoreHandle: getSemaphoreHandle(messagePointer); break; + case DSPCommands::GetSemaphoreEventHandle: getSemaphoreEventHandle(messagePointer); break; case DSPCommands::LoadComponent: loadComponent(messagePointer); break; case DSPCommands::ReadPipeIfPossible: readPipeIfPossible(messagePointer); break; case DSPCommands::RegisterInterruptEvents: registerInterruptEvents(messagePointer); break; @@ -97,14 +107,47 @@ void DSPService::readPipeIfPossible(u32 messagePointer) { mem.write16(messagePointer + 8, i); // Number of bytes read } -void DSPService::registerInterruptEvents(u32 messagePointer) { - u32 interrupt = mem.read32(messagePointer + 4); - u32 channel = mem.read32(messagePointer + 8); - u32 event = mem.read32(messagePointer + 16); +DSPService::DSPEvent& DSPService::getEventRef(u32 type, u32 pipe) { + switch (type) { + case 0: return interrupt0; + case 1: return interrupt1; - log("DSP::RegisterInterruptEvents (interrupt = %d, channel = %d, event = %d)\n", interrupt, channel, event); - mem.write32(messagePointer, IPC::responseHeader(0x15, 1, 0)); - mem.write32(messagePointer + 4, Result::Success); + case 2: + if (pipe >= pipeCount) + Helpers::panic("Tried to access the event of an invalid pipe"); + return pipeEvents[pipe]; + + default: + Helpers::panic("Unknown type for DSP::getEventRef"); + } +} + +void DSPService::registerInterruptEvents(u32 messagePointer) { + const u32 interrupt = mem.read32(messagePointer + 4); + const u32 channel = mem.read32(messagePointer + 8); + const u32 eventHandle = mem.read32(messagePointer + 16); + log("DSP::RegisterInterruptEvents (interrupt = %d, channel = %d, event = %d)\n", interrupt, channel, eventHandle); + + // The event handle being 0 means we're removing an event + if (eventHandle == 0) { + Helpers::panic("DSP::DSP::RegisterinterruptEvents Trying to remove a registered interrupt"); + } else { + const KernelObject* object = kernel.getObject(eventHandle, KernelObjectType::Event); + if (!object) { + Helpers::panic("DSP::DSP::RegisterInterruptEvents with invalid event handle"); + } + + if (totalEventCount >= maxEventCount) + Helpers::panic("DSP::RegisterInterruptEvents overflowed total number of allowed events"); + else { + getEventRef(interrupt, channel) = eventHandle; + mem.write32(messagePointer, IPC::responseHeader(0x15, 1, 0)); + mem.write32(messagePointer + 4, Result::Success); + + totalEventCount++; + kernel.signalEvent(eventHandle); + } + } } void DSPService::getHeadphoneStatus(u32 messagePointer) { @@ -115,13 +158,17 @@ void DSPService::getHeadphoneStatus(u32 messagePointer) { mem.write32(messagePointer + 8, Result::HeadphonesInserted); // This should be toggleable for shits and giggles } -void DSPService::getSemaphoreHandle(u32 messagePointer) { - log("DSP::GetSemaphoreHandle\n"); +void DSPService::getSemaphoreEventHandle(u32 messagePointer) { + log("DSP::GetSemaphoreEventHandle\n"); + + if (!semaphoreEvent.has_value()) { + semaphoreEvent = kernel.makeEvent(ResetType::OneShot); + } mem.write32(messagePointer, IPC::responseHeader(0x16, 1, 2)); mem.write32(messagePointer + 4, Result::Success); // TODO: Translation descriptor here? - mem.write32(messagePointer + 12, 0xF9991234); // Semaphore handle (stubbed with random, obvious number) + mem.write32(messagePointer + 12, semaphoreEvent.value()); // Semaphore event handle } void DSPService::setSemaphore(u32 messagePointer) { diff --git a/src/core/services/service_manager.cpp b/src/core/services/service_manager.cpp index 091e3575..64b7be5d 100644 --- a/src/core/services/service_manager.cpp +++ b/src/core/services/service_manager.cpp @@ -5,8 +5,8 @@ ServiceManager::ServiceManager(std::array& regs, Memory& mem, GPU& gpu, u32& currentPID, Kernel& kernel) : regs(regs), mem(mem), kernel(kernel), ac(mem), am(mem), boss(mem), apt(mem, kernel), cam(mem), cecd(mem), cfg(mem), - dsp(mem), hid(mem), frd(mem), fs(mem, kernel), gsp_gpu(mem, gpu, kernel, currentPID), gsp_lcd(mem), ldr(mem), mic(mem), - nim(mem), ndm(mem), ptm(mem), y2r(mem, kernel) {} + dsp(mem, kernel), hid(mem), frd(mem), fs(mem, kernel), gsp_gpu(mem, gpu, kernel, currentPID), gsp_lcd(mem), ldr(mem), + mic(mem), nim(mem), ndm(mem), ptm(mem), y2r(mem, kernel) {} static constexpr int MAX_NOTIFICATION_COUNT = 16;