diff --git a/rpcs3/Emu/SysCalls/Modules/cellSpurs.cpp b/rpcs3/Emu/SysCalls/Modules/cellSpurs.cpp index a98f319a4f..6d9bd97662 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellSpurs.cpp +++ b/rpcs3/Emu/SysCalls/Modules/cellSpurs.cpp @@ -20,359 +20,1045 @@ #include "sysPrxForUser.h" #include "cellSpurs.h" +//---------------------------------------------------------------------------- +// Externs +//---------------------------------------------------------------------------- + extern Module cellSpurs; +//---------------------------------------------------------------------------- +// Function prototypes +//---------------------------------------------------------------------------- + +// +// SPURS SPU functions +// bool spursKernelEntry(SPUThread & spu); -s32 cellSpursLookUpTasksetAddress(vm::ptr spurs, vm::ptr taskset, u32 id); -s32 _cellSpursSendSignal(vm::ptr taskset, u32 taskID); -s32 spursCreateLv2EventQueue(vm::ptr spurs, u32& queue_id, vm::ptr port, s32 size, u64 name_u64) +// +// SPURS utility functions +// +void spursPpuThreadExit(PPUThread& CPU, u64 errorStatus); +u32 spursGetSdkVersion(); + +// +// SPURS core functions +// + +// +// SPURS taskset functions +// + +s32 spursAttachLv2EventQueue(PPUThread& CPU, vm::ptr spurs, u32 queue, vm::ptr port, s32 isDynamic, bool spursCreated); +s32 cellSpursLookUpTasksetAddress(vm::ptr spurs, vm::pptr taskset, u32 id); +s32 _cellSpursSendSignal(PPUThread& CPU, vm::ptr taskset, u32 taskID); +s32 cellSpursWakeUp(PPUThread& CPU, vm::ptr spurs); +s32 cellSpursEnableExceptionEventHandler(vm::ptr spurs, bool flag); + +//---------------------------------------------------------------------------- +// SPURS utility functions +//---------------------------------------------------------------------------- + +/// Terminate a SPURS PPU thread +void spursPpuThreadExit(PPUThread& CPU, u64 errorStatus) { - auto queue = Emu.GetEventManager().MakeEventQueue(SYS_SYNC_FIFO, SYS_PPU_QUEUE, name_u64, 0, size); + sys_ppu_thread_exit(CPU, errorStatus); + throw SpursModuleExit(); +} - if (!queue) // rough +/// Get the version of SDK used by this process +u32 spursGetSdkVersion() +{ + s32 version; + + if (process_get_sdk_version(process_getpid(), version) != CELL_OK) { - return CELL_EAGAIN; + throw __FUNCTION__; } - queue_id = Emu.GetIdManager().add(std::move(queue)); + return version == -1 ? 0x465000 : version; +} - if (s32 res = spursAttachLv2EventQueue(spurs, queue_id, port, 1, true)) +//---------------------------------------------------------------------------- +// SPURS core functions +//---------------------------------------------------------------------------- + +/// Create an LV2 event queue and attach it to the SPURS instance +s32 spursCreateLv2EventQueue(PPUThread& CPU, vm::ptr spurs, vm::ptr queueId, vm::ptr port, s32 size, vm::cptr name) +{ + vm::stackvar attr(CPU); + + sys_event_queue_attribute_initialize(attr); + memcpy(attr->name, name.get_ptr(), sizeof(attr->name)); + auto rc = sys_event_queue_create(queueId, attr, SYS_EVENT_QUEUE_LOCAL, size); + if (rc != CELL_OK) { - assert(!"spursAttachLv2EventQueue() failed"); + return rc; + } + + vm::stackvar _port(CPU); + rc = spursAttachLv2EventQueue(CPU, spurs, *queueId, _port, 1 /*isDynamic*/, true /*spursCreated*/); + if (rc != CELL_OK) + { + sys_event_queue_destroy(*queueId, SYS_EVENT_QUEUE_DESTROY_FORCE); + } + + *port = _port; + return CELL_OK; +} + +/// Attach an LV2 event queue to the SPURS instance +s32 spursAttachLv2EventQueue(PPUThread& CPU, vm::ptr spurs, u32 queue, vm::ptr port, s32 isDynamic, bool spursCreated) +{ + if (!spurs || !port) + { + return CELL_SPURS_CORE_ERROR_NULL_POINTER; + } + + if (!spurs.aligned()) + { + return CELL_SPURS_CORE_ERROR_ALIGN; + } + + if (spurs->exception.data()) + { + return CELL_SPURS_CORE_ERROR_STAT; + } + + s32 sdkVer = spursGetSdkVersion(); + u8 _port = 0x3f; + u64 portMask = 0; + + if (isDynamic == 0) + { + _port = *port; + if (_port > 0x3f) + { + return CELL_SPURS_CORE_ERROR_INVAL; + } + + if (sdkVer >= 0x180000 && _port > 0xf) + { + return CELL_SPURS_CORE_ERROR_PERM; + } + } + + for (u32 i = isDynamic ? 0x10 : _port; i <= _port; i++) + { + portMask |= 1ull << (i); + } + + vm::stackvar connectedPort(CPU); + if (s32 res = sys_spu_thread_group_connect_event_all_threads(spurs->spuTG, queue, portMask, connectedPort)) + { + if (res == CELL_EISCONN) + { + return CELL_SPURS_CORE_ERROR_BUSY; + } + + return res; + } + + *port = connectedPort; + if (!spursCreated) + { + spurs->spuPortBits |= be_t::make(1ull << connectedPort); } return CELL_OK; } -s32 spursInit( - vm::ptr spurs, - const u32 revision, - const u32 sdkVersion, - const s32 nSpus, - const s32 spuPriority, - const s32 ppuPriority, - u32 flags, // SpursAttrFlags - const char prefix[], - const u32 prefixSize, - const u32 container, - const u8 swlPriority[], - const u32 swlMaxSpu, - const u32 swlIsPreem) + +/// Detach an LV2 event queue from the SPURS instance +s32 spursDetachLv2EventQueue(vm::ptr spurs, u8 spuPort, bool spursCreated) { - // SPURS initialization (asserts should actually rollback and return the error instead) + if (!spurs) + { + return CELL_SPURS_CORE_ERROR_NULL_POINTER; + } + + if (!spurs.aligned()) + { + return CELL_SPURS_CORE_ERROR_ALIGN; + } + + if (!spursCreated && spurs->exception.data()) + { + return CELL_SPURS_CORE_ERROR_STAT; + } + + if (spuPort > 0x3F) + { + return CELL_SPURS_CORE_ERROR_INVAL; + } + + auto sdkVer = spursGetSdkVersion(); + if (!spursCreated) + { + auto mask = 1ull << spuPort; + if (sdkVer >= 0x180000) + { + if ((spurs->spuPortBits.read_relaxed() & mask) == 0) + { + return CELL_SPURS_CORE_ERROR_SRCH; + } + } + + spurs->spuPortBits &= be_t::make(~mask); + } + + return CELL_OK; +} + +/// Wait until a workload in the SPURS instance becomes ready +void spursHandlerWaitReady(PPUThread& CPU, vm::ptr spurs) +{ + if (s32 rc = sys_lwmutex_lock(CPU, spurs.of(&CellSpurs::mutex), 0)) + { + throw __FUNCTION__; + } + + while (true) + { + if (Emu.IsStopped()) + { + spursPpuThreadExit(CPU, 0); + } + + if (spurs->handlerExiting.read_relaxed()) + { + if (s32 rc = sys_lwmutex_unlock(CPU, spurs.of(&CellSpurs::mutex))) + { + throw __FUNCTION__; + } + + spursPpuThreadExit(CPU, 0); + } + + // Find a runnable workload + spurs->handlerDirty.write_relaxed(0); + if (spurs->exception == 0) + { + bool foundRunnableWorkload = false; + for (u32 i = 0; i < 16; i++) + { + if (spurs->wklState1[i].read_relaxed() == SPURS_WKL_STATE_RUNNABLE && + *((u64*)spurs->wklInfo1[i].priority) != 0 && + spurs->wklMaxContention[i].read_relaxed() & 0x0F) + { + if (spurs->wklReadyCount1[i].read_relaxed() || + spurs->wklSignal1.read_relaxed() & (0x8000u >> i) || + (spurs->wklFlag.flag.read_relaxed() == 0 && + spurs->wklFlagReceiver.read_relaxed() == (u8)i)) + { + foundRunnableWorkload = true; + break; + } + } + } + + if (spurs->flags1 & SF1_32_WORKLOADS) + { + for (u32 i = 0; i < 16; i++) + { + if (spurs->wklState2[i].read_relaxed() == SPURS_WKL_STATE_RUNNABLE && + *((u64*)spurs->wklInfo2[i].priority) != 0 && + spurs->wklMaxContention[i].read_relaxed() & 0xF0) + { + if (spurs->wklIdleSpuCountOrReadyCount2[i].read_relaxed() || + spurs->wklSignal2.read_relaxed() & (0x8000u >> i) || + (spurs->wklFlag.flag.read_relaxed() == 0 && + spurs->wklFlagReceiver.read_relaxed() == (u8)i + 0x10)) + { + foundRunnableWorkload = true; + break; + } + } + } + } + + if (foundRunnableWorkload) { + break; + } + } + + // If we reach it means there are no runnable workloads in this SPURS instance. + // Wait until some workload becomes ready. + spurs->handlerWaiting.write_relaxed(1); + if (spurs->handlerDirty.read_relaxed() == 0) + { + if (s32 rc = sys_lwcond_wait(CPU, spurs.of(&CellSpurs::cond), 0)) + { + throw __FUNCTION__; + } + } + + spurs->handlerWaiting.write_relaxed(0); + } + + // If we reach here then a runnable workload was found + if (s32 rc = sys_lwmutex_unlock(CPU, spurs.of(&CellSpurs::mutex))) + { + throw __FUNCTION__; + } +} + +/// Entry point of the SPURS handler thread. This thread is responsible for +/// starting the SPURS SPU thread group. +void spursHandlerEntry(PPUThread& CPU) +{ + auto spurs = vm::ptr::make(vm::cast(CPU.GPR[3])); + + try + { + if (spurs->flags & SAF_UNKNOWN_FLAG_30) + { + spursPpuThreadExit(CPU, 0); + } + + while (true) + { + if (spurs->flags1 & SF1_EXIT_IF_NO_WORK) + { + spursHandlerWaitReady(CPU, spurs); + } + + if (s32 rc = sys_spu_thread_group_start(spurs->spuTG)) + { + throw __FUNCTION__; + } + + if (s32 rc = sys_spu_thread_group_join(spurs->spuTG, vm::null, vm::null)) + { + if (rc == CELL_ESTAT) + { + spursPpuThreadExit(CPU, 0); + } + + throw __FUNCTION__; + } + + if (Emu.IsStopped()) + { + continue; + } + + if ((spurs->flags1 & SF1_EXIT_IF_NO_WORK) == 0) + { + assert(spurs->handlerExiting.read_relaxed() == 1 || Emu.IsStopped()); + spursPpuThreadExit(CPU, 0); + } + } + } + catch(SpursModuleExit) + { + } +} + +/// Create the SPURS handler thread +s32 spursCreateHandler(vm::ptr spurs, u32 ppuPriority) +{ + // create joinable thread with stackSize = 0x4000 and custom task + spurs->ppu0 = ppu_thread_create(0, spurs.addr(), ppuPriority, 0x4000, true, false, std::string(spurs->prefix, spurs->prefixSize) + "SpursHdlr0", spursHandlerEntry); + return CELL_OK; +} + +/// Invoke event handlers +s32 spursInvokeEventHandlers(PPUThread& CPU, vm::ptr eventPortMux) +{ + if (eventPortMux->reqPending.exchange(be_t::make(0)).data()) + { + const vm::ptr handlerList = eventPortMux->handlerList.exchange(vm::null); + + for (auto node = handlerList; node; node = node->next) + { + node->handler(CPU, eventPortMux, node->data); + } + } + + return CELL_OK; +} + +// Invoke workload shutdown completion callbacks +s32 spursWakeUpShutdownCompletionWaiter(PPUThread& CPU, vm::ptr spurs, u32 wid) +{ + if (!spurs) + { + return CELL_SPURS_POLICY_MODULE_ERROR_NULL_POINTER; + } + + if (!spurs.aligned()) + { + return CELL_SPURS_POLICY_MODULE_ERROR_ALIGN; + } + + if (wid >= (u32)(spurs->flags1 & SF1_32_WORKLOADS ? CELL_SPURS_MAX_WORKLOAD2 : CELL_SPURS_MAX_WORKLOAD)) + { + return CELL_SPURS_POLICY_MODULE_ERROR_INVAL; + } + + if ((spurs->wklEnabled.read_relaxed() & (0x80000000u >> wid)) == 0) + { + return CELL_SPURS_POLICY_MODULE_ERROR_SRCH; + } + + const u8 wklState = wid < CELL_SPURS_MAX_WORKLOAD ? spurs->wklState1[wid].read_relaxed() : spurs->wklState2[wid & 0x0F].read_relaxed(); + + if (wklState != SPURS_WKL_STATE_REMOVABLE) + { + return CELL_SPURS_POLICY_MODULE_ERROR_STAT; + } + + auto& wklF = wid < CELL_SPURS_MAX_WORKLOAD ? spurs->wklF1[wid] : spurs->wklF2[wid & 0x0F]; + auto& wklEvent = wid < CELL_SPURS_MAX_WORKLOAD ? spurs->wklEvent1[wid] : spurs->wklEvent2[wid & 0x0F]; + + if (wklF.hook) + { + wklF.hook(CPU, spurs, wid, wklF.hookArg); + + assert(wklEvent.read_relaxed() & 0x01); + assert(wklEvent.read_relaxed() & 0x02); + assert((wklEvent.read_relaxed() & 0x20) == 0); + wklEvent |= 0x20; + } + + s32 rc = CELL_OK; + if (!wklF.hook || wklEvent.read_relaxed() & 0x10) + { + assert(wklF.x28 == 2); + rc = sys_semaphore_post((u32)wklF.sem, 1); + } + + return rc; +} + +/// Entry point of the SPURS event helper thread +void spursEventHelperEntry(PPUThread& CPU) +{ + const auto spurs = vm::ptr::make(vm::cast(CPU.GPR[3])); + + bool terminate = false; + + vm::stackvar eventArray(CPU, sizeof32(sys_event_t) * 8); + vm::stackvar> count(CPU); + + vm::ptr events = eventArray; + + while (!terminate) + { + if (s32 rc = sys_event_queue_receive(CPU, spurs->eventQueue, vm::null, 0 /*timeout*/)) + { + throw __FUNCTION__; + } + + const u64 event_src = CPU.GPR[4]; + const u64 event_data1 = CPU.GPR[5]; + const u64 event_data2 = CPU.GPR[6]; + const u64 event_data3 = CPU.GPR[7]; + + if (event_src == SYS_SPU_THREAD_EVENT_EXCEPTION_KEY) + { + spurs->exception = 1; + + events[0].source = event_src; + events[0].data1 = event_data1; + events[0].data2 = event_data2; + events[0].data3 = event_data3; + + if (sys_event_queue_tryreceive(spurs->eventQueue, events + 1, 7, count) != CELL_OK) + { + continue; + } + + // TODO: Examine LS and dump exception details + + for (auto i = 0; i < CELL_SPURS_MAX_WORKLOAD; i++) + { + sys_semaphore_post((u32)spurs->wklF1[i].sem, 1); + if (spurs->flags1 & SF1_32_WORKLOADS) + { + sys_semaphore_post((u32)spurs->wklF2[i].sem, 1); + } + } + } + else + { + const u32 data0 = event_data2 & 0x00FFFFFF; + + if (data0 == 1) + { + terminate = true; + } + else if (data0 < 1) + { + const u32 shutdownMask = event_data3; + + for (auto wid = 0; wid < CELL_SPURS_MAX_WORKLOAD; wid++) + { + if (shutdownMask & (0x80000000u >> wid)) + { + if (s32 rc = spursWakeUpShutdownCompletionWaiter(CPU, spurs, wid)) + { + throw __FUNCTION__; + } + } + + if ((spurs->flags1 & SF1_32_WORKLOADS) && (shutdownMask & (0x8000 >> wid))) + { + if (s32 rc = spursWakeUpShutdownCompletionWaiter(CPU, spurs, wid + 0x10)) + { + throw __FUNCTION__; + } + } + } + } + else if (data0 == 2) + { + if (s32 rc = sys_semaphore_post((u32)spurs->semPrv, 1)) + { + throw __FUNCTION__; + } + } + else if (data0 == 3) + { + if (s32 rc = spursInvokeEventHandlers(CPU, spurs.of(&CellSpurs::eventPortMux))) + { + throw __FUNCTION__; + } + } + else + { + throw __FUNCTION__; + } + } + } +} + +/// Create the SPURS event helper thread +s32 spursCreateSpursEventHelper(PPUThread& CPU, vm::ptr spurs, u32 ppuPriority) +{ + vm::stackvar evqName(CPU, 8); + memcpy(evqName.get_ptr(), "_spuPrv", 8); + + if (s32 rc = spursCreateLv2EventQueue(CPU, spurs, spurs.of(&CellSpurs::eventQueue), spurs.of(&CellSpurs::spuPort), 0x2A /*size*/, evqName)) + { + return rc; + } + + if (s32 rc = sys_event_port_create(spurs.of(&CellSpurs::eventPort), SYS_EVENT_PORT_LOCAL, SYS_EVENT_PORT_NO_NAME)) + { + if (s32 rc2 = spursDetachLv2EventQueue(spurs, spurs->spuPort, true /*spursCreated*/)) + { + return CELL_SPURS_CORE_ERROR_AGAIN; + } + + sys_event_queue_destroy(spurs->eventQueue, SYS_EVENT_QUEUE_DESTROY_FORCE); + return CELL_SPURS_CORE_ERROR_AGAIN; + } + + if (s32 rc = sys_event_port_connect_local(spurs->eventPort, spurs->eventQueue)) + { + sys_event_port_destroy(spurs->eventPort); + + if (s32 rc2 = spursDetachLv2EventQueue(spurs, spurs->spuPort, true /*spursCreated*/)) + { + return CELL_SPURS_CORE_ERROR_STAT; + } + + sys_event_queue_destroy(spurs->eventQueue, SYS_EVENT_QUEUE_DESTROY_FORCE); + return CELL_SPURS_CORE_ERROR_STAT; + } + + // create joinable thread with stackSize = 0x8000 and custom task + const u32 tid = ppu_thread_create(0, spurs.addr(), ppuPriority, 0x8000, true, false, std::string(spurs->prefix, spurs->prefixSize) + "SpursHdlr1", spursEventHelperEntry); + + // cannot happen in current implementation + if (tid == 0) + { + sys_event_port_disconnect(spurs->eventPort); + sys_event_port_destroy(spurs->eventPort); + + if (s32 rc = spursDetachLv2EventQueue(spurs, spurs->spuPort, true /*spursCreated*/)) + { + return CELL_SPURS_CORE_ERROR_STAT; + } + + sys_event_queue_destroy(spurs->eventQueue, SYS_EVENT_QUEUE_DESTROY_FORCE); + return CELL_SPURS_CORE_ERROR_STAT; + } + + spurs->ppu1 = tid; + return CELL_OK; +} + +/// Initialise the event port multiplexor structure +void spursInitialiseEventPortMux(vm::ptr eventPortMux, u8 spuPort, u32 eventPort, u32 unknown) +{ + memset(eventPortMux.get_ptr(), 0, sizeof(CellSpurs::EventPortMux)); + eventPortMux->spuPort = spuPort; + eventPortMux->eventPort = eventPort; + eventPortMux->x08 = unknown; +} + +/// Enable the system workload +s32 spursAddDefaultSystemWorkload(vm::ptr spurs, vm::ptr swlPriority, u32 swlMaxSpu, u32 swlIsPreem) +{ + return CELL_OK; +} + +/// Destroy the SPURS SPU threads and thread group +s32 spursFinalizeSpu(vm::ptr spurs) +{ + if (spurs->flags & SAF_UNKNOWN_FLAG_7 || spurs->flags & SAF_UNKNOWN_FLAG_8) + { + while (true) + { + if (s32 rc = sys_spu_thread_group_join(spurs->spuTG, vm::null, vm::null)) + { + throw __FUNCTION__; + } + + if (s32 rc = sys_spu_thread_group_destroy(spurs->spuTG)) + { + if (rc != CELL_EBUSY) + { + throw __FUNCTION__; + } + + continue; + } + + break; + } + } + else + { + if (s32 rc = sys_spu_thread_group_destroy(spurs->spuTG)) + { + return rc; + } + } + + if (s32 rc = sys_spu_image_close(spurs.of(&CellSpurs::spuImg))) + { + throw __FUNCTION__; + } + + return CELL_OK; +} + +/// Stop the event helper thread +s32 spursStopEventHelper(PPUThread& CPU, vm::ptr spurs) +{ + if (spurs->ppu1 == 0xFFFFFFFF) + { + return CELL_SPURS_CORE_ERROR_STAT; + } + + if (sys_event_port_send(spurs->eventPort, 0, 1, 0) != CELL_OK) + { + return CELL_SPURS_CORE_ERROR_STAT; + } + + if (sys_ppu_thread_join(spurs->ppu1, vm::stackvar>(CPU)) != CELL_OK) + { + return CELL_SPURS_CORE_ERROR_STAT; + } + + spurs->ppu1 = 0xFFFFFFFF; + + if (s32 rc = sys_event_port_disconnect(spurs->eventPort)) + { + throw __FUNCTION__; + } + + if (s32 rc = sys_event_port_destroy(spurs->eventPort)) + { + throw __FUNCTION__; + } + + if (s32 rc = spursDetachLv2EventQueue(spurs, spurs->spuPort, true /*spursCreated*/)) + { + throw __FUNCTION__; + } + + if (s32 rc = sys_event_queue_destroy(spurs->eventQueue, SYS_EVENT_QUEUE_DESTROY_FORCE)) + { + throw __FUNCTION__; + } + + return CELL_OK; +} + +/// Signal to the SPURS handler thread +s32 spursSignalToHandlerThread(PPUThread& CPU, vm::ptr spurs) +{ + if (s32 rc = sys_lwmutex_lock(CPU, spurs.of(&CellSpurs::mutex), 0 /* forever */)) + { + throw __FUNCTION__; + } + + if (s32 rc = sys_lwcond_signal(CPU, spurs.of(&CellSpurs::cond))) + { + throw __FUNCTION__; + } + + if (s32 rc = sys_lwmutex_unlock(CPU, spurs.of(&CellSpurs::mutex))) + { + throw __FUNCTION__; + } + + return CELL_OK; +} + +/// Join the SPURS handler thread +s32 spursJoinHandlerThread(PPUThread& CPU, vm::ptr spurs) +{ + if (spurs->ppu0 == 0xFFFFFFFF) + { + return CELL_SPURS_CORE_ERROR_STAT; + } + + if (s32 rc = sys_ppu_thread_join(spurs->ppu0, vm::stackvar>(CPU))) + { + throw __FUNCTION__; + } + + spurs->ppu0 = 0xFFFFFFFF; + return CELL_OK; +} + +/// Initialise SPURS +s32 spursInit( + PPUThread& CPU, + vm::ptr spurs, + u32 revision, + u32 sdkVersion, + s32 nSpus, + s32 spuPriority, + s32 ppuPriority, + u32 flags, // SpursAttrFlags + vm::cptr prefix, + u32 prefixSize, + u32 container, + vm::cptr swlPriority, + u32 swlMaxSpu, + u32 swlIsPreem) +{ + vm::stackvar> sem(CPU); + vm::stackvar semAttr(CPU); + vm::stackvar lwCondAttr(CPU); + vm::stackvar lwCond(CPU); + vm::stackvar lwMutextAttr(CPU); + vm::stackvar lwMutex(CPU); + vm::stackvar> spuTgId(CPU); + vm::stackvar spuTgName(CPU, 128); + vm::stackvar spuTgAttr(CPU); + vm::stackvar spuThArgs(CPU); + vm::stackvar> spuThreadId(CPU); + vm::stackvar spuThAttr(CPU); + vm::stackvar spuThName(CPU, 128); if (!spurs) { return CELL_SPURS_CORE_ERROR_NULL_POINTER; } + if (!spurs.aligned()) { return CELL_SPURS_CORE_ERROR_ALIGN; } + if (prefixSize > CELL_SPURS_NAME_MAX_LENGTH) { return CELL_SPURS_CORE_ERROR_INVAL; } - if (process_is_spu_lock_line_reservation_address(spurs.addr(), SYS_MEMORY_ACCESS_RIGHT_SPU_THR) != CELL_OK) + + if (sys_process_is_spu_lock_line_reservation_address(spurs.addr(), SYS_MEMORY_ACCESS_RIGHT_SPU_THR) != CELL_OK) { return CELL_SPURS_CORE_ERROR_PERM; } + // Intialise SPURS context const bool isSecond = (flags & SAF_SECOND_VERSION) != 0; - memset(spurs.get_ptr(), 0, isSecond ? CELL_SPURS_SIZE2 : CELL_SPURS_SIZE); - spurs->revision = revision; - spurs->sdkVersion = sdkVersion; - spurs->ppu0 = 0xffffffffull; - spurs->ppu1 = 0xffffffffull; - spurs->flags = flags; - memcpy(spurs->prefix, prefix, prefixSize); - spurs->prefixSize = (u8)prefixSize; - std::string name(prefix, prefixSize); // initialize name string + auto rollback = [=] + { + if (spurs->semPrv) + { + sys_semaphore_destroy((u32)spurs->semPrv); + } + + for (u32 i = 0; i < CELL_SPURS_MAX_WORKLOAD; i++) + { + if (spurs->wklF1[i].sem) + { + sys_semaphore_destroy((u32)spurs->wklF1[i].sem); + } + + if (isSecond) + { + if (spurs->wklF2[i].sem) + { + sys_semaphore_destroy((u32)spurs->wklF2[i].sem); + } + } + } + }; + + memset(spurs.get_ptr(), 0, isSecond ? CELL_SPURS_SIZE2 : CELL_SPURS_SIZE); + spurs->revision = revision; + spurs->sdkVersion = sdkVersion; + spurs->ppu0 = 0xffffffffull; + spurs->ppu1 = 0xffffffffull; + spurs->flags = flags; + spurs->prefixSize = (u8)prefixSize; + memcpy(spurs->prefix, prefix.get_ptr(), prefixSize); if (!isSecond) { - spurs->wklMskA.write_relaxed(be_t::make(0xffff)); + spurs->wklEnabled.write_relaxed(be_t::make(0xffff)); } - spurs->xCC = 0; - spurs->xCD = 0; + + // Initialise trace + spurs->xCC = 0; + spurs->xCD = 0; spurs->sysSrvMsgUpdateTrace = 0; for (u32 i = 0; i < 8; i++) { - spurs->sysSrvWorkload[i] = -1; + spurs->sysSrvPreemptWklId[i] = -1; } - // default or system workload: + // Import default system workload spurs->wklInfoSysSrv.addr.set(SPURS_IMG_ADDR_SYS_SRV_WORKLOAD); - spurs->wklInfoSysSrv.arg = 0; + spurs->wklInfoSysSrv.size = 0x2200; + spurs->wklInfoSysSrv.arg = 0; spurs->wklInfoSysSrv.uniqueId.write_relaxed(0xff); - u32 sem; - for (u32 i = 0; i < 0x10; i++) + + + + // Create semaphores for each workload + // TODO: Find out why these semaphores are needed + sys_semaphore_attribute_initialize(semAttr); + memcpy(semAttr->name, "_spuWkl", 8); + for (u32 i = 0; i < CELL_SPURS_MAX_WORKLOAD; i++) { - sem = Emu.GetIdManager().make(SYS_SYNC_PRIORITY, 1, *(u64*)"_spuWkl", 0); - assert(sem && ~sem); // should rollback if semaphore creation failed and return the error - spurs->wklF1[i].sem = sem; - } - if (isSecond) - { - for (u32 i = 0; i < 0x10; i++) + if (s32 rc = sys_semaphore_create(sem, semAttr, 0, 1)) // Emu.GetIdManager().make(SYS_SYNC_PRIORITY, 1, *(u64*)"_spuWkl", 0) { - sem = Emu.GetIdManager().make(SYS_SYNC_PRIORITY, 1, *(u64*)"_spuWkl", 0); - assert(sem && ~sem); - spurs->wklF2[i].sem = sem; + return rollback(), rc; + } + + spurs->wklF1[i].sem = sem.value(); + + if (isSecond) + { + if (s32 rc = sys_semaphore_create(sem, semAttr, 0, 1)) // Emu.GetIdManager().make(SYS_SYNC_PRIORITY, 1, *(u64*)"_spuWkl", 0) + { + return rollback(), rc; + } + + spurs->wklF2[i].sem = sem.value(); } } - sem = Emu.GetIdManager().make(SYS_SYNC_PRIORITY, 1, *(u64*)"_spuPrv", 0); - assert(sem && ~sem); - spurs->semPrv = sem; - spurs->unk11 = -1; - spurs->unk12 = -1; - spurs->unk13 = 0; - spurs->nSpus = nSpus; + + // Create semaphore + // TODO: Figure out why this semaphore is needed + memcpy(semAttr->name, "_spuPrv", 8); + if (s32 rc = sys_semaphore_create(sem, semAttr, 0, 1)) // Emu.GetIdManager().make(SYS_SYNC_PRIORITY, 1, *(u64*)"_spuPrv", 0); + { + return rollback(), rc; + } + + spurs->semPrv = sem.value(); + + spurs->unk11 = -1; + spurs->unk12 = -1; + spurs->unk13 = 0; + spurs->nSpus = nSpus; spurs->spuPriority = spuPriority; - spurs->spuImg.addr = Memory.MainMem.AllocAlign(0x40000, 4096); + // Import SPURS kernel + spurs->spuImg.type = SYS_SPU_IMAGE_TYPE_USER; + spurs->spuImg.addr = (u32)Memory.Alloc(0x40000, 4096); spurs->spuImg.entry_point = isSecond ? CELL_SPURS_KERNEL2_ENTRY_ADDR : CELL_SPURS_KERNEL1_ENTRY_ADDR; + spurs->spuImg.nsegs = 1; - s32 tgt = SYS_SPU_THREAD_GROUP_TYPE_NORMAL; - if (flags & SAF_SPU_TGT_EXCLUSIVE_NON_CONTEXT) - { - tgt = SYS_SPU_THREAD_GROUP_TYPE_EXCLUSIVE_NON_CONTEXT; - } - else if (flags & SAF_UNKNOWN_FLAG_0) - { - tgt = 0xC02; - } - if (flags & SAF_SPU_MEMORY_CONTAINER_SET) tgt |= SYS_SPU_THREAD_GROUP_TYPE_MEMORY_FROM_CONTAINER; - if (flags & SAF_SYSTEM_WORKLOAD_ENABLED) tgt |= SYS_SPU_THREAD_GROUP_TYPE_COOPERATE_WITH_SYSTEM; - if (flags & SAF_UNKNOWN_FLAG_7) tgt |= 0x102; - if (flags & SAF_UNKNOWN_FLAG_8) tgt |= 0xC02; - if (flags & SAF_UNKNOWN_FLAG_9) tgt |= 0x800; - spurs->spuTG = spu_thread_group_create(name + "CellSpursKernelGroup", nSpus, spuPriority, tgt, container); - assert(spurs->spuTG.data()); + // Create a thread group for this SPURS context + memcpy(spuTgName.get_ptr(), spurs->prefix, spurs->prefixSize); + spuTgName[spurs->prefixSize] = '\0'; + strcat(spuTgName.get_ptr(), "CellSpursKernelGroup"); - name += "CellSpursKernel0"; - for (s32 num = 0; num < nSpus; num++, name[name.size() - 1]++) + sys_spu_thread_group_attribute_initialize(spuTgAttr); + spuTgAttr->name = spuTgName; + spuTgAttr->nsize = (u32)strlen(spuTgAttr->name.get_ptr()) + 1; + + if (spurs->flags & SAF_UNKNOWN_FLAG_0) { - spurs->spus[num] = spu_thread_initialize(spurs->spuTG, num, vm::ptr::make(spurs.addr() + offsetof(CellSpurs, spuImg)), - name, SYS_SPU_THREAD_OPTION_DEC_SYNC_TB_ENABLE, (u64)num << 32, spurs.addr(), 0, 0, [spurs](SPUThread& SPU) + spuTgAttr->type = 0x0C00 | SYS_SPU_THREAD_GROUP_TYPE_SYSTEM; + } + else if (flags & SAF_SPU_TGT_EXCLUSIVE_NON_CONTEXT) + { + spuTgAttr->type = SYS_SPU_THREAD_GROUP_TYPE_EXCLUSIVE_NON_CONTEXT; + } + else + { + spuTgAttr->type = SYS_SPU_THREAD_GROUP_TYPE_NORMAL; + } + + if (spurs->flags & SAF_SPU_MEMORY_CONTAINER_SET) + { + spuTgAttr->type |= SYS_SPU_THREAD_GROUP_TYPE_MEMORY_FROM_CONTAINER; + spuTgAttr->ct = container; + } + + if (flags & SAF_UNKNOWN_FLAG_7) spuTgAttr->type |= 0x0100 | SYS_SPU_THREAD_GROUP_TYPE_SYSTEM; + if (flags & SAF_UNKNOWN_FLAG_8) spuTgAttr->type |= 0x0C00 | SYS_SPU_THREAD_GROUP_TYPE_SYSTEM; + if (flags & SAF_UNKNOWN_FLAG_9) spuTgAttr->type |= 0x0800; + if (flags & SAF_SYSTEM_WORKLOAD_ENABLED) spuTgAttr->type |= SYS_SPU_THREAD_GROUP_TYPE_COOPERATE_WITH_SYSTEM; + + if (s32 rc = sys_spu_thread_group_create(spuTgId, nSpus, spuPriority, spuTgAttr)) + { + sys_spu_image_close(spurs.of(&CellSpurs::spuImg)); + return rollback(), rc; + } + + spurs->spuTG = spuTgId.value(); + + // Initialise all SPUs in the SPU thread group + memcpy(spuThName.get_ptr(), spurs->prefix, spurs->prefixSize); + spuThName[spurs->prefixSize] = '\0'; + strcat(spuThName.get_ptr(), "CellSpursKernel"); + + spuThAttr->name = spuThName; + spuThAttr->name_len = (u32)strlen(spuThName.get_ptr()) + 2; + spuThAttr->option = SYS_SPU_THREAD_OPTION_DEC_SYNC_TB_ENABLE; + spuThName[spuThAttr->name_len - 1] = '\0'; + + for (s32 num = 0; num < nSpus; num++) + { + spuThName[spuThAttr->name_len - 2] = '0' + num; + spuThArgs->arg1 = (u64)num << 32; + spuThArgs->arg2 = (u64)spurs.addr(); + + if (s32 rc = sys_spu_thread_initialize(spuThreadId, spurs->spuTG, num, spurs.of(&CellSpurs::spuImg), spuThAttr, spuThArgs)) + { + sys_spu_thread_group_destroy(spurs->spuTG); + sys_spu_image_close(spurs.of(&CellSpurs::spuImg)); + return rollback(), rc; + } + + const auto spuThread = std::static_pointer_cast(Emu.GetCPU().GetThread(spurs->spus[num] = spuThreadId.value())); + + // entry point cannot be initialized immediately because SPU LS will be rewritten by sys_spu_thread_group_start() + spuThread->m_custom_task = [spurs](SPUThread& SPU) { SPU.RegisterHleFunction(spurs->spuImg.entry_point, spursKernelEntry); SPU.FastCall(spurs->spuImg.entry_point); - }); + }; } + // Start the SPU printf server if required if (flags & SAF_SPU_PRINTF_ENABLED) { // spu_printf: attach group - if (!spu_printf_agcb || spu_printf_agcb(spurs->spuTG) != CELL_OK) + if (!spu_printf_agcb || spu_printf_agcb(CPU, spurs->spuTG) != CELL_OK) { // remove flag if failed spurs->flags &= ~SAF_SPU_PRINTF_ENABLED; } } - lwmutex_create(spurs->mutex, false, SYS_SYNC_PRIORITY, *(u64*)"_spuPrv"); - lwcond_create(spurs->cond, spurs->mutex, *(u64*)"_spuPrv"); + // Create a mutex to protect access to SPURS handler thread data + sys_lwmutex_attribute_initialize(lwMutextAttr); + memcpy(lwMutextAttr->name, "_spuPrv", 8); + if (s32 rc = sys_lwmutex_create(lwMutex, lwMutextAttr)) + { + spursFinalizeSpu(spurs); + return rollback(), rc; + } + + spurs->mutex = lwMutex.value(); + + // Create condition variable to signal the SPURS handler thread + memcpy(lwCondAttr->name, "_spuPrv", 8); + if (s32 rc = sys_lwcond_create(lwCond, lwMutex, lwCondAttr)) + { + sys_lwmutex_destroy(CPU, lwMutex); + spursFinalizeSpu(spurs); + return rollback(), rc; + } + + spurs->cond = lwCond; spurs->flags1 = (flags & SAF_EXIT_IF_NO_WORK ? SF1_EXIT_IF_NO_WORK : 0) | (isSecond ? SF1_32_WORKLOADS : 0); spurs->wklFlagReceiver.write_relaxed(0xff); spurs->wklFlag.flag.write_relaxed(be_t::make(-1)); - spurs->xD64 = {}; - spurs->xD65 = {}; - spurs->xD66 = {}; + spurs->handlerDirty.write_relaxed(0); + spurs->handlerWaiting.write_relaxed(0); + spurs->handlerExiting.write_relaxed(0); spurs->ppuPriority = ppuPriority; - u32 queue; - if (s32 res = spursCreateLv2EventQueue(spurs, queue, vm::ptr::make(spurs.addr() + 0xc9), 0x2a, *(u64*)"_spuPrv")) + // Create the SPURS event helper thread + if (s32 rc = spursCreateSpursEventHelper(CPU, spurs, ppuPriority)) { - assert(!"spursCreateLv2EventQueue() failed"); - } - spurs->queue = queue; - - u32 port = Emu.GetIdManager().make(SYS_EVENT_PORT_LOCAL, 0); - assert(port && ~port); - spurs->port = port; - - if (s32 res = sys_event_port_connect_local(port, queue)) - { - assert(!"sys_event_port_connect_local() failed"); + sys_lwcond_destroy(lwCond); + sys_lwmutex_destroy(CPU, lwMutex); + spursFinalizeSpu(spurs); + return rollback(), rc; } - name = std::string(prefix, prefixSize); - - spurs->ppu0 = ppu_thread_create(0, 0, ppuPriority, 0x4000, true, false, name + "SpursHdlr0", [spurs](PPUThread& CPU) + // Create the SPURS handler thread + if (s32 rc = spursCreateHandler(spurs, ppuPriority)) { - if (spurs->flags & SAF_UNKNOWN_FLAG_30) + spursStopEventHelper(CPU, spurs); + sys_lwcond_destroy(lwCond); + sys_lwmutex_destroy(CPU, lwMutex); + spursFinalizeSpu(spurs); + return rollback(), rc; + } + + // Enable SPURS exception handler + if (s32 rc = cellSpursEnableExceptionEventHandler(spurs, true /*enable*/)) + { + spursSignalToHandlerThread(CPU, spurs); + spursJoinHandlerThread(CPU, spurs); + spursStopEventHelper(CPU, spurs); + sys_lwcond_destroy(lwCond); + sys_lwmutex_destroy(CPU, lwMutex); + spursFinalizeSpu(spurs); + return rollback(), rc; + } + + spurs->traceBuffer = vm::null; + // TODO: Register libprof for user trace + + // Initialise the event port multiplexor + spursInitialiseEventPortMux(spurs.of(&CellSpurs::eventPortMux), spurs->spuPort, spurs->eventPort, 3); + + // Enable the default system workload if required + if (flags & SAF_SYSTEM_WORKLOAD_ENABLED) + { + if (s32 rc = spursAddDefaultSystemWorkload(spurs, swlPriority, swlMaxSpu, swlIsPreem)) { - return; - } - - while (true) - { - if (Emu.IsStopped()) - { - cellSpurs.Warning("SPURS Handler Thread 0 aborted"); - return; - } - - if (spurs->flags1 & SF1_EXIT_IF_NO_WORK) - { - if (s32 res = sys_lwmutex_lock(CPU, spurs->get_lwmutex(), 0)) - { - assert(!"sys_lwmutex_lock() failed"); - } - if (spurs->xD66.read_relaxed()) - { - if (s32 res = sys_lwmutex_unlock(CPU, spurs->get_lwmutex())) - { - assert(!"sys_lwmutex_unlock() failed"); - } - return; - } - else while (true) - { - if (Emu.IsStopped()) break; - - spurs->xD64.exchange(0); - if (spurs->exception.data() == 0) - { - bool do_break = false; - for (u32 i = 0; i < 16; i++) - { - if (spurs->wklState1[i].read_relaxed() == 2 && - *((u64 *)spurs->wklInfo1[i].priority) != 0 && - spurs->wklMaxContention[i].read_relaxed() & 0xf - ) - { - if (spurs->wklReadyCount1[i].read_relaxed() || - spurs->wklSignal1.read_relaxed() & (0x8000u >> i) || - (spurs->wklFlag.flag.read_relaxed() == 0 && - spurs->wklFlagReceiver.read_relaxed() == (u8)i - )) - { - do_break = true; - break; - } - } - } - if (spurs->flags1 & SF1_32_WORKLOADS) for (u32 i = 0; i < 16; i++) - { - if (spurs->wklState2[i].read_relaxed() == 2 && - *((u64 *)spurs->wklInfo2[i].priority) != 0 && - spurs->wklMaxContention[i].read_relaxed() & 0xf0 - ) - { - if (spurs->wklIdleSpuCountOrReadyCount2[i].read_relaxed() || - spurs->wklSignal2.read_relaxed() & (0x8000u >> i) || - (spurs->wklFlag.flag.read_relaxed() == 0 && - spurs->wklFlagReceiver.read_relaxed() == (u8)i + 0x10 - )) - { - do_break = true; - break; - } - } - } - if (do_break) break; // from while - } - - spurs->xD65.exchange(1); - if (spurs->xD64.read_relaxed() == 0) - { - if (s32 res = sys_lwcond_wait(CPU, spurs->get_lwcond(), 0)) - { - assert(!"sys_lwcond_wait() failed"); - } - } - spurs->xD65.exchange(0); - if (spurs->xD66.read_relaxed()) - { - if (s32 res = sys_lwmutex_unlock(CPU, spurs->get_lwmutex())) - { - assert(!"sys_lwmutex_unlock() failed"); - } - return; - } - } - - if (Emu.IsStopped()) continue; - - if (s32 res = sys_lwmutex_unlock(CPU, spurs->get_lwmutex())) - { - assert(!"sys_lwmutex_unlock() failed"); - } - } - - if (Emu.IsStopped()) continue; - - if (s32 res = sys_spu_thread_group_start(spurs->spuTG)) - { - assert(!"sys_spu_thread_group_start() failed"); - } - if (s32 res = sys_spu_thread_group_join(spurs->spuTG, vm::null, vm::null)) - { - if (res == CELL_ESTAT) - { - return; - } - assert(!"sys_spu_thread_group_join() failed"); - } - - if (Emu.IsStopped()) continue; - - if ((spurs->flags1 & SF1_EXIT_IF_NO_WORK) == 0) - { - assert(spurs->xD66.read_relaxed() == 1 || Emu.IsStopped()); - return; - } - } - }); - - spurs->ppu1 = ppu_thread_create(0, 0, ppuPriority, 0x8000, true, false, name + "SpursHdlr1", [spurs](PPUThread& CPU) - { - // TODO - - }); - - // enable exception event handler - if (spurs->enableEH.compare_and_swap_test(be_t::make(0), be_t::make(1))) - { - if (s32 res = sys_spu_thread_group_connect_event(spurs->spuTG, spurs->queue, SYS_SPU_THREAD_GROUP_EVENT_EXCEPTION)) - { - assert(!"sys_spu_thread_group_connect_event() failed"); + throw __FUNCTION__; } + + return CELL_OK; } - - spurs->traceBuffer.set(0); - // can also use cellLibprof if available (omitted) - - // some unknown subroutine - spurs->sub3.unk1 = spurs.addr() + 0xc9; - spurs->sub3.unk2 = 3; // unknown const - spurs->sub3.port = (u64)spurs->port; - - if (flags & SAF_SYSTEM_WORKLOAD_ENABLED) // initialize system workload (disabled) + else if (flags & SAF_EXIT_IF_NO_WORK) { - s32 res = CELL_OK; - // TODO - assert(res == CELL_OK); - } - else if (flags & SAF_EXIT_IF_NO_WORK) // wakeup - { - return spursWakeUp(GetCurrentPPUThread(), spurs); + return cellSpursWakeUp(CPU, spurs); } - return CELL_OK; + return CELL_OK; } -s32 cellSpursInitialize(vm::ptr spurs, s32 nSpus, s32 spuPriority, s32 ppuPriority, bool exitIfNoWork) +s32 cellSpursInitialize(PPUThread& CPU, vm::ptr spurs, s32 nSpus, s32 spuPriority, s32 ppuPriority, bool exitIfNoWork) { - cellSpurs.Warning("cellSpursInitialize(spurs_addr=0x%x, nSpus=%d, spuPriority=%d, ppuPriority=%d, exitIfNoWork=%d)", - spurs.addr(), nSpus, spuPriority, ppuPriority, exitIfNoWork ? 1 : 0); + cellSpurs.Warning("cellSpursInitialize(spurs=*0x%x, nSpus=%d, spuPriority=%d, ppuPriority=%d, exitIfNoWork=%d)", spurs, nSpus, spuPriority, ppuPriority, exitIfNoWork); return spursInit( + CPU, spurs, 0, 0, @@ -380,32 +1066,35 @@ s32 cellSpursInitialize(vm::ptr spurs, s32 nSpus, s32 spuPriority, s3 spuPriority, ppuPriority, exitIfNoWork ? SAF_EXIT_IF_NO_WORK : SAF_NONE, - nullptr, + vm::null, 0, 0, - nullptr, + vm::null, 0, 0); } -s32 cellSpursInitializeWithAttribute(vm::ptr spurs, vm::cptr attr) +s32 cellSpursInitializeWithAttribute(PPUThread& CPU, vm::ptr spurs, vm::cptr attr) { - cellSpurs.Warning("cellSpursInitializeWithAttribute(spurs_addr=0x%x, attr_addr=0x%x)", spurs.addr(), attr.addr()); + cellSpurs.Warning("cellSpursInitializeWithAttribute(spurs=*0x%x, attr=*0x%x)", spurs, attr); if (!attr) { return CELL_SPURS_CORE_ERROR_NULL_POINTER; } + if (!attr.aligned()) { return CELL_SPURS_CORE_ERROR_ALIGN; } + if (attr->revision > 2) { return CELL_SPURS_CORE_ERROR_INVAL; } - + return spursInit( + CPU, spurs, attr->revision, attr->sdkVersion, @@ -413,32 +1102,35 @@ s32 cellSpursInitializeWithAttribute(vm::ptr spurs, vm::cptrspuPriority, attr->ppuPriority, attr->flags | (attr->exitIfNoWork ? SAF_EXIT_IF_NO_WORK : 0), - attr->prefix, + attr.of(&CellSpursAttribute::prefix, 0), attr->prefixSize, attr->container, - attr->swlPriority, + attr.of(&CellSpursAttribute::swlPriority, 0), attr->swlMaxSpu, attr->swlIsPreem); } -s32 cellSpursInitializeWithAttribute2(vm::ptr spurs, vm::cptr attr) +s32 cellSpursInitializeWithAttribute2(PPUThread& CPU, vm::ptr spurs, vm::cptr attr) { - cellSpurs.Warning("cellSpursInitializeWithAttribute2(spurs_addr=0x%x, attr_addr=0x%x)", spurs.addr(), attr.addr()); + cellSpurs.Warning("cellSpursInitializeWithAttribute2(spurs=*0x%x, attr=*0x%x)", spurs, attr); if (!attr) { return CELL_SPURS_CORE_ERROR_NULL_POINTER; } + if (!attr.aligned()) { return CELL_SPURS_CORE_ERROR_ALIGN; } + if (attr->revision > 2) { return CELL_SPURS_CORE_ERROR_INVAL; } return spursInit( + CPU, spurs, attr->revision, attr->sdkVersion, @@ -446,46 +1138,48 @@ s32 cellSpursInitializeWithAttribute2(vm::ptr spurs, vm::cptrspuPriority, attr->ppuPriority, attr->flags | (attr->exitIfNoWork ? SAF_EXIT_IF_NO_WORK : 0) | SAF_SECOND_VERSION, - attr->prefix, + attr.of(&CellSpursAttribute::prefix, 0), attr->prefixSize, attr->container, - attr->swlPriority, + attr.of(&CellSpursAttribute::swlPriority, 0), attr->swlMaxSpu, attr->swlIsPreem); } s32 _cellSpursAttributeInitialize(vm::ptr attr, u32 revision, u32 sdkVersion, u32 nSpus, s32 spuPriority, s32 ppuPriority, bool exitIfNoWork) { - cellSpurs.Warning("_cellSpursAttributeInitialize(attr_addr=0x%x, revision=%d, sdkVersion=0x%x, nSpus=%d, spuPriority=%d, ppuPriority=%d, exitIfNoWork=%d)", - attr.addr(), revision, sdkVersion, nSpus, spuPriority, ppuPriority, exitIfNoWork ? 1 : 0); + cellSpurs.Warning("_cellSpursAttributeInitialize(attr=*0x%x, revision=%d, sdkVersion=0x%x, nSpus=%d, spuPriority=%d, ppuPriority=%d, exitIfNoWork=%d)", + attr, revision, sdkVersion, nSpus, spuPriority, ppuPriority, exitIfNoWork); if (!attr) { return CELL_SPURS_CORE_ERROR_NULL_POINTER; } + if (!attr.aligned()) { return CELL_SPURS_CORE_ERROR_ALIGN; } memset(attr.get_ptr(), 0, sizeof(CellSpursAttribute)); - attr->revision = revision; - attr->sdkVersion = sdkVersion; - attr->nSpus = nSpus; - attr->spuPriority = spuPriority; - attr->ppuPriority = ppuPriority; + attr->revision = revision; + attr->sdkVersion = sdkVersion; + attr->nSpus = nSpus; + attr->spuPriority = spuPriority; + attr->ppuPriority = ppuPriority; attr->exitIfNoWork = exitIfNoWork; return CELL_OK; } s32 cellSpursAttributeSetMemoryContainerForSpuThread(vm::ptr attr, u32 container) { - cellSpurs.Warning("cellSpursAttributeSetMemoryContainerForSpuThread(attr_addr=0x%x, container=0x%x)", attr.addr(), container); + cellSpurs.Warning("cellSpursAttributeSetMemoryContainerForSpuThread(attr=*0x%x, container=0x%x)", attr, container); if (!attr) { return CELL_SPURS_CORE_ERROR_NULL_POINTER; } + if (!attr.aligned()) { return CELL_SPURS_CORE_ERROR_ALIGN; @@ -496,19 +1190,20 @@ s32 cellSpursAttributeSetMemoryContainerForSpuThread(vm::ptr return CELL_SPURS_CORE_ERROR_STAT; } - attr->container = container; - attr->flags |= SAF_SPU_MEMORY_CONTAINER_SET; + attr->container = container; + attr->flags |= SAF_SPU_MEMORY_CONTAINER_SET; return CELL_OK; } s32 cellSpursAttributeSetNamePrefix(vm::ptr attr, vm::cptr prefix, u32 size) { - cellSpurs.Warning("cellSpursAttributeSetNamePrefix(attr_addr=0x%x, prefix_addr=0x%x, size=%d)", attr.addr(), prefix.addr(), size); + cellSpurs.Warning("cellSpursAttributeSetNamePrefix(attr=*0x%x, prefix=*0x%x, size=%d)", attr, prefix, size); if (!attr || !prefix) { return CELL_SPURS_CORE_ERROR_NULL_POINTER; } + if (!attr.aligned()) { return CELL_SPURS_CORE_ERROR_ALIGN; @@ -526,12 +1221,13 @@ s32 cellSpursAttributeSetNamePrefix(vm::ptr attr, vm::cptr attr) { - cellSpurs.Warning("cellSpursAttributeEnableSpuPrintfIfAvailable(attr_addr=0x%x)", attr.addr()); + cellSpurs.Warning("cellSpursAttributeEnableSpuPrintfIfAvailable(attr=*0x%x)", attr); if (!attr) { return CELL_SPURS_CORE_ERROR_NULL_POINTER; } + if (!attr.aligned()) { return CELL_SPURS_CORE_ERROR_ALIGN; @@ -543,12 +1239,13 @@ s32 cellSpursAttributeEnableSpuPrintfIfAvailable(vm::ptr att s32 cellSpursAttributeSetSpuThreadGroupType(vm::ptr attr, s32 type) { - cellSpurs.Warning("cellSpursAttributeSetSpuThreadGroupType(attr_addr=0x%x, type=%d)", attr.addr(), type); + cellSpurs.Warning("cellSpursAttributeSetSpuThreadGroupType(attr=*0x%x, type=%d)", attr, type); if (!attr) { return CELL_SPURS_CORE_ERROR_NULL_POINTER; } + if (!attr.aligned()) { return CELL_SPURS_CORE_ERROR_ALIGN; @@ -570,18 +1267,19 @@ s32 cellSpursAttributeSetSpuThreadGroupType(vm::ptr attr, s3 { return CELL_SPURS_CORE_ERROR_INVAL; } + return CELL_OK; } s32 cellSpursAttributeEnableSystemWorkload(vm::ptr attr, vm::cptr priority, u32 maxSpu, vm::cptr isPreemptible) { - cellSpurs.Warning("cellSpursAttributeEnableSystemWorkload(attr_addr=0x%x, priority_addr=0x%x, maxSpu=%d, isPreemptible_addr=0x%x)", - attr.addr(), priority.addr(), maxSpu, isPreemptible.addr()); + cellSpurs.Warning("cellSpursAttributeEnableSystemWorkload(attr=*0x%x, priority=*0x%x, maxSpu=%d, isPreemptible=*0x%x)", attr, priority, maxSpu, isPreemptible); if (!attr) { return CELL_SPURS_CORE_ERROR_NULL_POINTER; } + if (!attr.aligned()) { return CELL_SPURS_CORE_ERROR_ALIGN; @@ -602,10 +1300,12 @@ s32 cellSpursAttributeEnableSystemWorkload(vm::ptr attr, vm: { return CELL_SPURS_CORE_ERROR_INVAL; } + if (nSpus == 1 || attr->exitIfNoWork) { return CELL_SPURS_CORE_ERROR_PERM; } + if (attr->flags & SAF_SYSTEM_WORKLOAD_ENABLED) { return CELL_SPURS_CORE_ERROR_BUSY; @@ -622,108 +1322,61 @@ s32 cellSpursAttributeEnableSystemWorkload(vm::ptr attr, vm: isPreem |= (1 << j); } } - attr->swlMaxSpu = maxSpu; // write max spu for system workload + + attr->swlMaxSpu = maxSpu; // write max spu for system workload attr->swlIsPreem = isPreem; // write isPreemptible mask return CELL_OK; } } + return CELL_SPURS_CORE_ERROR_INVAL; } s32 cellSpursFinalize(vm::ptr spurs) { - cellSpurs.Todo("cellSpursFinalize(spurs_addr=0x%x)", spurs.addr()); + cellSpurs.Todo("cellSpursFinalize(spurs=*0x%x)", spurs); if (!spurs) { return CELL_SPURS_CORE_ERROR_NULL_POINTER; } + if (!spurs.aligned()) { return CELL_SPURS_CORE_ERROR_ALIGN; } - if (spurs->xD66.read_relaxed()) + + if (spurs->handlerExiting.read_relaxed()) { return CELL_SPURS_CORE_ERROR_STAT; } - // TODO + u32 wklEnabled = spurs->wklEnabled.read_relaxed(); + + if (spurs->flags1 & SF1_32_WORKLOADS) + { + wklEnabled &= 0xFFFF0000; + } + + if (spurs->flags & SAF_SYSTEM_WORKLOAD_ENABLED) + { + } return CELL_OK; } -s32 spursAttachLv2EventQueue(vm::ptr spurs, u32 queue, vm::ptr port, s32 isDynamic, bool wasCreated) +s32 cellSpursAttachLv2EventQueue(PPUThread& CPU, vm::ptr spurs, u32 queue, vm::ptr port, s32 isDynamic) { - if (!spurs || !port) - { - return CELL_SPURS_CORE_ERROR_NULL_POINTER; - } - if (!spurs.aligned()) - { - return CELL_SPURS_CORE_ERROR_ALIGN; - } - if (spurs->exception.data()) - { - return CELL_SPURS_CORE_ERROR_STAT; - } + cellSpurs.Warning("cellSpursAttachLv2EventQueue(spurs=*0x%x, queue=0x%x, port=*0x%x, isDynamic=%d)", spurs, queue, port, isDynamic); - s32 sdk_ver; - if (s32 res = process_get_sdk_version(process_getpid(), sdk_ver)) - { - assert(!"process_get_sdk_version() failed"); - } - if (sdk_ver == -1) sdk_ver = 0x460000; - - u8 _port = 0x3f; - u64 port_mask = 0; - - if (isDynamic == 0) - { - _port = *port; - if (_port > 0x3f) - { - return CELL_SPURS_CORE_ERROR_INVAL; - } - if (sdk_ver > 0x17ffff && _port > 0xf) - { - return CELL_SPURS_CORE_ERROR_PERM; - } - } - - for (u32 i = isDynamic ? 0x10 : _port; i <= _port; i++) - { - port_mask |= 1ull << (i); - } - - assert(port_mask); // zero mask will return CELL_EINVAL - if (s32 res = sys_spu_thread_group_connect_event_all_threads(spurs->spuTG, queue, port_mask, port)) - { - if (res == CELL_EISCONN) - { - return CELL_SPURS_CORE_ERROR_BUSY; - } - return res; - } - - if (!wasCreated) - { - spurs->spups |= be_t::make(1ull << *port); // atomic bitwise or - } - return CELL_OK; -} - -s32 cellSpursAttachLv2EventQueue(vm::ptr spurs, u32 queue, vm::ptr port, s32 isDynamic) -{ - cellSpurs.Warning("cellSpursAttachLv2EventQueue(spurs_addr=0x%x, queue=0x%x, port_addr=0x%x, isDynamic=%d)", - spurs.addr(), queue, port.addr(), isDynamic); - - return spursAttachLv2EventQueue(spurs, queue, port, isDynamic, false); + return spursAttachLv2EventQueue(CPU, spurs, queue, port, isDynamic, false /*spursCreated*/); } s32 cellSpursDetachLv2EventQueue(vm::ptr spurs, u8 port) { - UNIMPLEMENTED_FUNC(cellSpurs); - return CELL_OK; + cellSpurs.Warning("cellSpursDetachLv2EventQueue(spurs=*0x%x, port=%d)", spurs, port); + + return spursDetachLv2EventQueue(spurs, port, false /*spursCreated*/); } s32 cellSpursGetSpuGuid() @@ -734,12 +1387,13 @@ s32 cellSpursGetSpuGuid() s32 cellSpursGetSpuThreadGroupId(vm::ptr spurs, vm::ptr group) { - cellSpurs.Warning("cellSpursGetSpuThreadGroupId(spurs_addr=0x%x, group_addr=0x%x)", spurs.addr(), group.addr()); + cellSpurs.Warning("cellSpursGetSpuThreadGroupId(spurs=*0x%x, group=*0x%x)", spurs, group); if (!spurs || !group) { return CELL_SPURS_CORE_ERROR_NULL_POINTER; } + if (!spurs.aligned()) { return CELL_SPURS_CORE_ERROR_ALIGN; @@ -751,39 +1405,43 @@ s32 cellSpursGetSpuThreadGroupId(vm::ptr spurs, vm::ptr group) s32 cellSpursGetNumSpuThread(vm::ptr spurs, vm::ptr nThreads) { - cellSpurs.Warning("cellSpursGetNumSpuThread(spurs_addr=0x%x, nThreads_addr=0x%x)", spurs.addr(), nThreads.addr()); + cellSpurs.Warning("cellSpursGetNumSpuThread(spurs=*0x%x, nThreads=*0x%x)", spurs, nThreads); if (!spurs || !nThreads) { return CELL_SPURS_CORE_ERROR_NULL_POINTER; } + if (!spurs.aligned()) { return CELL_SPURS_CORE_ERROR_ALIGN; } - *nThreads = (u32)spurs->nSpus; + *nThreads = spurs->nSpus; return CELL_OK; } s32 cellSpursGetSpuThreadId(vm::ptr spurs, vm::ptr thread, vm::ptr nThreads) { - cellSpurs.Warning("cellSpursGetSpuThreadId(spurs_addr=0x%x, thread_addr=0x%x, nThreads_addr=0x%x)", spurs.addr(), thread.addr(), nThreads.addr()); + cellSpurs.Warning("cellSpursGetSpuThreadId(spurs=*0x%x, thread=*0x%x, nThreads=*0x%x)", spurs, thread, nThreads); if (!spurs || !thread || !nThreads) { return CELL_SPURS_CORE_ERROR_NULL_POINTER; } + if (!spurs.aligned()) { return CELL_SPURS_CORE_ERROR_ALIGN; } const u32 count = std::min(*nThreads, spurs->nSpus); + for (u32 i = 0; i < count; i++) { thread[i] = spurs->spus[i]; } + *nThreads = count; return CELL_OK; } @@ -845,28 +1503,32 @@ s32 spursWakeUp(PPUThread& CPU, vm::ptr spurs) return CELL_SPURS_POLICY_MODULE_ERROR_STAT; } - spurs->xD64.exchange(1); - if (spurs->xD65.read_sync()) + spurs->handlerDirty.exchange(1); + + if (spurs->handlerWaiting.read_sync()) { - if (s32 res = sys_lwmutex_lock(CPU, spurs->get_lwmutex(), 0)) + if (s32 res = sys_lwmutex_lock(CPU, spurs.of(&CellSpurs::mutex), 0)) { - assert(!"sys_lwmutex_lock() failed"); + throw __FUNCTION__; } - if (s32 res = sys_lwcond_signal(CPU, spurs->get_lwcond())) + + if (s32 res = sys_lwcond_signal(CPU, spurs.of(&CellSpurs::cond))) { - assert(!"sys_lwcond_signal() failed"); + throw __FUNCTION__; } - if (s32 res = sys_lwmutex_unlock(CPU, spurs->get_lwmutex())) + + if (s32 res = sys_lwmutex_unlock(CPU, spurs.of(&CellSpurs::mutex))) { - assert(!"sys_lwmutex_unlock() failed"); + throw __FUNCTION__; } } + return CELL_OK; } s32 cellSpursWakeUp(PPUThread& CPU, vm::ptr spurs) { - cellSpurs.Warning("%s(spurs_addr=0x%x)", __FUNCTION__, spurs.addr()); + cellSpurs.Warning("cellSpursWakeUp(spurs=*0x%x)", spurs); return spursWakeUp(CPU, spurs); } @@ -903,8 +1565,9 @@ s32 spursAddWorkload( } u32 wnum; + const u32 wmax = spurs->flags1 & SF1_32_WORKLOADS ? 0x20u : 0x10u; // TODO: check if can be changed - spurs->wklMskA.atomic_op([spurs, wmax, &wnum](be_t& value) + spurs->wklEnabled.atomic_op([spurs, wmax, &wnum](be_t& value) { wnum = cntlz32(~(u32)value); // found empty position if (wnum < wmax) @@ -924,31 +1587,37 @@ s32 spursAddWorkload( { assert((spurs->wklCurrentContention[wnum] & 0xf) == 0); assert((spurs->wklPendingContention[wnum] & 0xf) == 0); + spurs->wklState1[wnum].write_relaxed(1); spurs->wklStatus1[wnum] = 0; - spurs->wklEvent1[wnum] = 0; + spurs->wklEvent1[wnum].write_relaxed(0); spurs->wklInfo1[wnum].addr = pm; spurs->wklInfo1[wnum].arg = data; spurs->wklInfo1[wnum].size = size; + for (u32 i = 0; i < 8; i++) { spurs->wklInfo1[wnum].priority[i] = priorityTable[i]; } + spurs->wklH1[wnum].nameClass = nameClass; spurs->wklH1[wnum].nameInstance = nameInstance; memset(spurs->wklF1[wnum].unk0, 0, 0x20); // clear struct preserving semaphore id - memset(spurs->wklF1[wnum].unk1, 0, 0x58); + memset(&spurs->wklF1[wnum].x28, 0, 0x58); + if (hook) { spurs->wklF1[wnum].hook = hook; spurs->wklF1[wnum].hookArg = hookArg; spurs->wklEvent1[wnum] |= 2; } + if ((spurs->flags1 & SF1_32_WORKLOADS) == 0) { spurs->wklIdleSpuCountOrReadyCount2[wnum].write_relaxed(0); spurs->wklMinContention[wnum] = minContention > 8 ? 8 : minContention; } + spurs->wklReadyCount1[wnum].write_relaxed(0); } else @@ -957,24 +1626,28 @@ s32 spursAddWorkload( assert((spurs->wklPendingContention[index] & 0xf0) == 0); spurs->wklState2[index].write_relaxed(1); spurs->wklStatus2[index] = 0; - spurs->wklEvent2[index] = 0; + spurs->wklEvent2[index].write_relaxed(0); spurs->wklInfo2[index].addr = pm; spurs->wklInfo2[index].arg = data; spurs->wklInfo2[index].size = size; + for (u32 i = 0; i < 8; i++) { spurs->wklInfo2[index].priority[i] = priorityTable[i]; } + spurs->wklH2[index].nameClass = nameClass; spurs->wklH2[index].nameInstance = nameInstance; memset(spurs->wklF2[index].unk0, 0, 0x20); // clear struct preserving semaphore id - memset(spurs->wklF2[index].unk1, 0, 0x58); + memset(&spurs->wklF2[index].x28, 0, 0x58); + if (hook) { spurs->wklF2[index].hook = hook; spurs->wklF2[index].hookArg = hookArg; spurs->wklEvent2[index] |= 2; } + spurs->wklIdleSpuCountOrReadyCount2[wnum].write_relaxed(0); } @@ -1011,7 +1684,7 @@ s32 spursAddWorkload( if (mask & m) { CellSpurs::WorkloadInfo& current = i <= 15 ? spurs->wklInfo1[i] : spurs->wklInfo2[i & 0xf]; - if (current.addr.addr() == wkl.addr.addr()) + if (current.addr == wkl.addr) { // if a workload with identical policy module found res_wkl = current.uniqueId.read_relaxed(); @@ -1036,65 +1709,40 @@ s32 spursAddWorkload( return CELL_OK; } -s32 cellSpursAddWorkload( - vm::ptr spurs, - vm::ptr wid, - vm::cptr pm, - u32 size, - u64 data, - vm::cptr priorityTable, - u32 minContention, - u32 maxContention) +s32 cellSpursAddWorkload(vm::ptr spurs, vm::ptr wid, vm::cptr pm, u32 size, u64 data, vm::cptr priority, u32 minCnt, u32 maxCnt) { - cellSpurs.Warning("%s(spurs_addr=0x%x, wid_addr=0x%x, pm_addr=0x%x, size=0x%x, data=0x%llx, priorityTable_addr=0x%x, minContention=0x%x, maxContention=0x%x)", - __FUNCTION__, spurs.addr(), wid.addr(), pm.addr(), size, data, priorityTable.addr(), minContention, maxContention); + cellSpurs.Warning("cellSpursAddWorkload(spurs=*0x%x, wid=*0x%x, pm=*0x%x, size=0x%x, data=0x%llx, priority=*0x%x, minCnt=0x%x, maxCnt=0x%x)", + spurs, wid, pm, size, data, priority, minCnt, maxCnt); - return spursAddWorkload( - spurs, - wid, - pm, - size, - data, - *priorityTable, - minContention, - maxContention, - vm::null, - vm::null, - vm::null, - vm::null); + return spursAddWorkload(spurs, wid, pm, size, data, *priority, minCnt, maxCnt, vm::null, vm::null, vm::null, vm::null); } -s32 _cellSpursWorkloadAttributeInitialize( - vm::ptr attr, - u32 revision, - u32 sdkVersion, - vm::cptr pm, - u32 size, - u64 data, - vm::cptr priorityTable, - u32 minContention, - u32 maxContention) +s32 _cellSpursWorkloadAttributeInitialize(vm::ptr attr, u32 revision, u32 sdkVersion, vm::cptr pm, u32 size, u64 data, vm::cptr priority, u32 minCnt, u32 maxCnt) { - cellSpurs.Warning("%s(attr_addr=0x%x, revision=%d, sdkVersion=0x%x, pm_addr=0x%x, size=0x%x, data=0x%llx, priorityTable_addr=0x%x, minContention=0x%x, maxContention=0x%x)", - __FUNCTION__, attr.addr(), revision, sdkVersion, pm.addr(), size, data, priorityTable.addr(), minContention, maxContention); + cellSpurs.Warning("_cellSpursWorkloadAttributeInitialize(attr=*0x%x, revision=%d, sdkVersion=0x%x, pm=*0x%x, size=0x%x, data=0x%llx, priority=*0x%x, minCnt=0x%x, maxCnt=0x%x)", + attr, revision, sdkVersion, pm, size, data, priority, minCnt, maxCnt); if (!attr) { return CELL_SPURS_POLICY_MODULE_ERROR_NULL_POINTER; } + if (!attr.aligned()) { return CELL_SPURS_POLICY_MODULE_ERROR_ALIGN; } + if (!pm) { return CELL_SPURS_POLICY_MODULE_ERROR_NULL_POINTER; } + if (pm % 16) { return CELL_SPURS_POLICY_MODULE_ERROR_ALIGN; } - if (minContention == 0 || *(u64*)*priorityTable & 0xf0f0f0f0f0f0f0f0ull) // check if some priority > 15 + + if (minCnt == 0 || *(u64*)*priority & 0xf0f0f0f0f0f0f0f0ull) // check if some priority > 15 { return CELL_SPURS_POLICY_MODULE_ERROR_INVAL; } @@ -1105,20 +1753,21 @@ s32 _cellSpursWorkloadAttributeInitialize( attr->pm = pm; attr->size = size; attr->data = data; - *(u64*)attr->priority = *(u64*)*priorityTable; - attr->minContention = minContention; - attr->maxContention = maxContention; + *(u64*)attr->priority = *(u64*)*priority; + attr->minContention = minCnt; + attr->maxContention = maxCnt; return CELL_OK; } s32 cellSpursWorkloadAttributeSetName(vm::ptr attr, vm::cptr nameClass, vm::cptr nameInstance) { - cellSpurs.Warning("%s(attr_addr=0x%x, nameClass_addr=0x%x, nameInstance_addr=0x%x)", __FUNCTION__, attr.addr(), nameClass.addr(), nameInstance.addr()); + cellSpurs.Warning("cellSpursWorkloadAttributeSetName(attr=*0x%x, nameClass=*0x%x, nameInstance=*0x%x)", attr, nameClass, nameInstance); if (!attr) { return CELL_SPURS_POLICY_MODULE_ERROR_NULL_POINTER; } + if (!attr.aligned()) { return CELL_SPURS_POLICY_MODULE_ERROR_ALIGN; @@ -1131,12 +1780,13 @@ s32 cellSpursWorkloadAttributeSetName(vm::ptr attr, s32 cellSpursWorkloadAttributeSetShutdownCompletionEventHook(vm::ptr attr, vm::ptr hook, vm::ptr arg) { - cellSpurs.Warning("%s(attr_addr=0x%x, hook_addr=0x%x, arg=0x%x)", __FUNCTION__, attr.addr(), hook.addr(), arg.addr()); + cellSpurs.Warning("cellSpursWorkloadAttributeSetShutdownCompletionEventHook(attr=*0x%x, hook=*0x%x, arg=*0x%x)", attr, hook, arg); if (!attr || !hook) { return CELL_SPURS_POLICY_MODULE_ERROR_NULL_POINTER; } + if (!attr.aligned()) { return CELL_SPURS_POLICY_MODULE_ERROR_ALIGN; @@ -1147,36 +1797,26 @@ s32 cellSpursWorkloadAttributeSetShutdownCompletionEventHook(vm::ptr spurs, const vm::ptr wid, vm::cptr attr) +s32 cellSpursAddWorkloadWithAttribute(vm::ptr spurs, vm::ptr wid, vm::cptr attr) { - cellSpurs.Warning("%s(spurs_addr=0x%x, wid_addr=0x%x, attr_addr=0x%x)", __FUNCTION__, spurs.addr(), wid.addr(), attr.addr()); + cellSpurs.Warning("cellSpursAddWorkloadWithAttribute(spurs=*0x%x, wid=*0x%x, attr=*0x%x)", spurs, wid, attr); if (!attr) { return CELL_SPURS_POLICY_MODULE_ERROR_NULL_POINTER; } + if (!attr.aligned()) { return CELL_SPURS_POLICY_MODULE_ERROR_ALIGN; } - if (attr->revision != be_t::make(1)) + + if (attr->revision.data() != se32(1)) { return CELL_SPURS_POLICY_MODULE_ERROR_INVAL; } - return spursAddWorkload( - spurs, - wid, - attr->pm, - attr->size, - attr->data, - attr->priority, - attr->minContention, - attr->maxContention, - attr->nameClass, - attr->nameInstance, - attr->hook, - attr->hookArg); + return spursAddWorkload(spurs, wid, attr->pm, attr->size, attr->data, attr->priority, attr->minContention, attr->maxContention, attr->nameClass, attr->nameInstance, attr->hook, attr->hookArg); } s32 cellSpursRemoveWorkload() @@ -1199,28 +1839,33 @@ s32 cellSpursShutdownWorkload() s32 _cellSpursWorkloadFlagReceiver(vm::ptr spurs, u32 wid, u32 is_set) { - cellSpurs.Warning("%s(spurs_addr=0x%x, wid=%d, is_set=%d)", __FUNCTION__, spurs.addr(), wid, is_set); + cellSpurs.Warning("_cellSpursWorkloadFlagReceiver(spurs=*0x%x, wid=%d, is_set=%d)", spurs, wid, is_set); if (!spurs) { return CELL_SPURS_POLICY_MODULE_ERROR_NULL_POINTER; } + if (!spurs.aligned()) { return CELL_SPURS_POLICY_MODULE_ERROR_ALIGN; } + if (wid >= (spurs->flags1 & SF1_32_WORKLOADS ? 0x20u : 0x10u)) { return CELL_SPURS_POLICY_MODULE_ERROR_INVAL; } - if ((spurs->wklMskA.read_relaxed() & (0x80000000u >> wid)) == 0) + + if ((spurs->wklEnabled.read_relaxed() & (0x80000000u >> wid)) == 0) { return CELL_SPURS_POLICY_MODULE_ERROR_SRCH; } + if (spurs->exception.data()) { return CELL_SPURS_POLICY_MODULE_ERROR_STAT; } + if (s32 res = spurs->wklFlag.flag.atomic_op_sync(0, [spurs, wid, is_set](be_t& flag) -> s32 { if (is_set) @@ -1266,24 +1911,25 @@ s32 _cellSpursWorkloadFlagReceiver(vm::ptr spurs, u32 wid, u32 is_set s32 cellSpursGetWorkloadFlag(vm::ptr spurs, vm::pptr flag) { - cellSpurs.Warning("%s(spurs_addr=0x%x, flag_addr=0x%x)", __FUNCTION__, spurs.addr(), flag.addr()); + cellSpurs.Warning("cellSpursGetWorkloadFlag(spurs=*0x%x, flag=**0x%x)", spurs, flag); if (!spurs || !flag) { return CELL_SPURS_POLICY_MODULE_ERROR_NULL_POINTER; } + if (!spurs.aligned()) { return CELL_SPURS_POLICY_MODULE_ERROR_ALIGN; } - flag->set(vm::get_addr(&spurs->wklFlag)); + *flag = spurs.of(&CellSpurs::wklFlag); return CELL_OK; } -s32 cellSpursSendWorkloadSignal(vm::ptr spurs, u32 workloadId) +s32 cellSpursSendWorkloadSignal(vm::ptr spurs, u32 wid) { - cellSpurs.Warning("%s(spurs=0x%x, workloadId=0x%x)", __FUNCTION__, spurs.addr(), workloadId); + cellSpurs.Warning("cellSpursSendWorkloadSignal(spurs=*0x%x, wid=0x%x)", spurs, wid); if (!spurs) { @@ -1295,12 +1941,12 @@ s32 cellSpursSendWorkloadSignal(vm::ptr spurs, u32 workloadId) return CELL_SPURS_POLICY_MODULE_ERROR_ALIGN; } - if (workloadId >= CELL_SPURS_MAX_WORKLOAD2 || (workloadId >= CELL_SPURS_MAX_WORKLOAD && (spurs->flags1 & SF1_32_WORKLOADS) == 0)) + if (wid >= CELL_SPURS_MAX_WORKLOAD2 || (wid >= CELL_SPURS_MAX_WORKLOAD && (spurs->flags1 & SF1_32_WORKLOADS) == 0)) { return CELL_SPURS_POLICY_MODULE_ERROR_INVAL; } - if ((spurs->wklMskA.read_relaxed() & (0x80000000u >> workloadId)) == 0) + if ((spurs->wklEnabled.read_relaxed() & (0x80000000u >> wid)) == 0) { return CELL_SPURS_POLICY_MODULE_ERROR_SRCH; } @@ -1310,28 +1956,18 @@ s32 cellSpursSendWorkloadSignal(vm::ptr spurs, u32 workloadId) return CELL_SPURS_POLICY_MODULE_ERROR_STAT; } - u8 state; - if (workloadId >= CELL_SPURS_MAX_WORKLOAD) - { - state = spurs->wklState2[workloadId & 0x0F].read_relaxed(); - } - else - { - state = spurs->wklState1[workloadId].read_relaxed(); - } - - if (state != SPURS_WKL_STATE_RUNNABLE) + if (spurs->wklState(wid).read_relaxed() != SPURS_WKL_STATE_RUNNABLE) { return CELL_SPURS_POLICY_MODULE_ERROR_STAT; } - if (workloadId >= CELL_SPURS_MAX_WORKLOAD) + if (wid >= CELL_SPURS_MAX_WORKLOAD) { - spurs->wklSignal2 |= be_t::make(0x8000 >> (workloadId & 0x0F)); + spurs->wklSignal2 |= be_t::make(0x8000 >> (wid & 0x0F)); } else { - spurs->wklSignal1 |= be_t::make(0x8000 >> workloadId); + spurs->wklSignal1 |= be_t::make(0x8000 >> wid); } return CELL_OK; @@ -1339,7 +1975,7 @@ s32 cellSpursSendWorkloadSignal(vm::ptr spurs, u32 workloadId) s32 cellSpursGetWorkloadData(vm::ptr spurs, vm::ptr data, u32 workloadId) { - cellSpurs.Warning("%s(spurs_addr=0x%x, data=0x%x, workloadId=%d)", __FUNCTION__, spurs.addr(), data.addr(), workloadId); + cellSpurs.Warning("cellSpursGetWorkloadData(spurs=*0x%x, data=*0x%x, workloadId=%d)", spurs, data, workloadId); if (!spurs || !data) { @@ -1356,7 +1992,7 @@ s32 cellSpursGetWorkloadData(vm::ptr spurs, vm::ptr data, u32 wo return CELL_SPURS_POLICY_MODULE_ERROR_INVAL; } - if ((spurs->wklMskA.read_relaxed() & (0x80000000u >> workloadId)) == 0) + if ((spurs->wklEnabled.read_relaxed() & (0x80000000u >> workloadId)) == 0) { return CELL_SPURS_POLICY_MODULE_ERROR_SRCH; } @@ -1380,24 +2016,28 @@ s32 cellSpursGetWorkloadData(vm::ptr spurs, vm::ptr data, u32 wo s32 cellSpursReadyCountStore(vm::ptr spurs, u32 wid, u32 value) { - cellSpurs.Warning("%s(spurs_addr=0x%x, wid=%d, value=0x%x)", __FUNCTION__, spurs.addr(), wid, value); + cellSpurs.Warning("cellSpursReadyCountStore(spurs=*0x%x, wid=%d, value=0x%x)", spurs, wid, value); if (!spurs) { return CELL_SPURS_POLICY_MODULE_ERROR_NULL_POINTER; } + if (!spurs.aligned()) { return CELL_SPURS_POLICY_MODULE_ERROR_ALIGN; } + if (wid >= (spurs->flags1 & SF1_32_WORKLOADS ? 0x20u : 0x10u) || value > 0xff) { return CELL_SPURS_POLICY_MODULE_ERROR_INVAL; } - if ((spurs->wklMskA.read_relaxed() & (0x80000000u >> wid)) == 0) + + if ((spurs->wklEnabled.read_relaxed() & (0x80000000u >> wid)) == 0) { return CELL_SPURS_POLICY_MODULE_ERROR_SRCH; } + if (spurs->exception.data() || spurs->wklState(wid).read_relaxed() != 2) { return CELL_SPURS_POLICY_MODULE_ERROR_STAT; @@ -1464,8 +2104,7 @@ s32 cellSpursUnsetExceptionEventHandler() s32 _cellSpursEventFlagInitialize(vm::ptr spurs, vm::ptr taskset, vm::ptr eventFlag, u32 flagClearMode, u32 flagDirection) { - cellSpurs.Warning("_cellSpursEventFlagInitialize(spurs_addr=0x%x, taskset_addr=0x%x, eventFlag_addr=0x%x, flagClearMode=%d, flagDirection=%d)", - spurs.addr(), taskset.addr(), eventFlag.addr(), flagClearMode, flagDirection); + cellSpurs.Warning("_cellSpursEventFlagInitialize(spurs=*0x%x, taskset=*0x%x, eventFlag=*0x%x, flagClearMode=%d, flagDirection=%d)", spurs, taskset, eventFlag, flagClearMode, flagDirection); if ((!taskset && !spurs) || !eventFlag) { @@ -1477,7 +2116,7 @@ s32 _cellSpursEventFlagInitialize(vm::ptr spurs, vm::ptrwid >= CELL_SPURS_MAX_WORKLOAD2) + if (taskset && taskset->wid >= CELL_SPURS_MAX_WORKLOAD2) { return CELL_SPURS_TASK_ERROR_INVAL; } @@ -1492,7 +2131,7 @@ s32 _cellSpursEventFlagInitialize(vm::ptr spurs, vm::ptrclearMode = flagClearMode; eventFlag->spuPort = CELL_SPURS_EVENT_FLAG_INVALID_SPU_PORT; - if (taskset.addr()) + if (taskset) { eventFlag->addr = taskset.addr(); } @@ -1505,9 +2144,9 @@ s32 _cellSpursEventFlagInitialize(vm::ptr spurs, vm::ptr eventFlag) +s32 cellSpursEventFlagAttachLv2EventQueue(PPUThread& CPU, vm::ptr eventFlag) { - cellSpurs.Warning("cellSpursEventFlagAttachLv2EventQueue(eventFlag_addr=0x%x)", eventFlag.addr()); + cellSpurs.Warning("cellSpursEventFlagAttachLv2EventQueue(eventFlag=*0x%x)", eventFlag); if (!eventFlag) { @@ -1537,53 +2176,62 @@ s32 cellSpursEventFlagAttachLv2EventQueue(vm::ptr eventFlag) else { auto taskset = vm::ptr::make((u32)eventFlag->addr); - spurs.set((u32)taskset->spurs.addr()); + spurs = taskset->spurs; } - u32 eventQueueId; - vm::var port; - auto rc = spursCreateLv2EventQueue(spurs, eventQueueId, port, 1, *((u64 *)"_spuEvF")); - if (rc != CELL_OK) + vm::stackvar> eventQueueId(CPU); + vm::stackvar port(CPU); + vm::stackvar evqName(CPU, 8); + memcpy(evqName.get_ptr(), "_spuEvF", 8); + + auto failure = [](s32 rc) -> s32 { // Return rc if its an error code from SPURS otherwise convert the error code to a SPURS task error code return (rc & 0x0FFF0000) == 0x00410000 ? rc : (0x80410900 | (rc & 0xFF)); + }; + + if (s32 rc = spursCreateLv2EventQueue(CPU, spurs, eventQueueId, port, 1, evqName)) + { + return failure(rc); } + auto success = [&] + { + eventFlag->eventQueueId = eventQueueId; + eventFlag->spuPort = port; + }; + if (eventFlag->direction == CELL_SPURS_EVENT_FLAG_ANY2ANY) { - vm::var> eventPortId; - rc = sys_event_port_create(vm::ptr::make(eventPortId.addr()), SYS_EVENT_PORT_LOCAL, 0); + vm::stackvar> eventPortId(CPU); + + s32 rc = sys_event_port_create(eventPortId, SYS_EVENT_PORT_LOCAL, 0); if (rc == CELL_OK) { - rc = sys_event_port_connect_local(eventPortId.value(), eventQueueId); + rc = sys_event_port_connect_local(eventPortId.value(), eventQueueId.value()); if (rc == CELL_OK) { eventFlag->eventPortId = eventPortId; - goto success; + return success(), CELL_OK; } sys_event_port_destroy(eventPortId.value()); } - // TODO: Implement the following - // if (spursDetachLv2EventQueue(spurs, port, 1) == CELL_OK) - // { - // sys_event_queue_destroy(eventQueueId, SYS_EVENT_QUEUE_DESTROY_FORCE); - // } + if (spursDetachLv2EventQueue(spurs, port, true /*spursCreated*/) == CELL_OK) + { + sys_event_queue_destroy(eventQueueId.value(), SYS_EVENT_QUEUE_DESTROY_FORCE); + } - // Return rc if its an error code from SPURS otherwise convert the error code to a SPURS task error code - return (rc & 0x0FFF0000) == 0x00410000 ? rc : (0x80410900 | (rc & 0xFF)); + return failure(rc); } -success: - eventFlag->eventQueueId = eventQueueId; - eventFlag->spuPort = port; - return CELL_OK; + return success(), CELL_OK; } s32 cellSpursEventFlagDetachLv2EventQueue(vm::ptr eventFlag) { - cellSpurs.Warning("cellSpursEventFlagDetachLv2EventQueue(eventFlag_addr=0x%x)", eventFlag.addr()); + cellSpurs.Warning("cellSpursEventFlagDetachLv2EventQueue(eventFlag=*0x%x)", eventFlag); if (!eventFlag) { @@ -1610,7 +2258,8 @@ s32 cellSpursEventFlagDetachLv2EventQueue(vm::ptr eventFlag) return CELL_SPURS_TASK_ERROR_BUSY; } - auto port = eventFlag->spuPort; + const u8 port = eventFlag->spuPort; + eventFlag->spuPort = CELL_SPURS_EVENT_FLAG_INVALID_SPU_PORT; vm::ptr spurs; @@ -1621,22 +2270,21 @@ s32 cellSpursEventFlagDetachLv2EventQueue(vm::ptr eventFlag) else { auto taskset = vm::ptr::make((u32)eventFlag->addr); - spurs.set((u32)taskset->spurs.addr()); + spurs = taskset->spurs; } - if(eventFlag->direction == CELL_SPURS_EVENT_FLAG_ANY2ANY) + if (eventFlag->direction == CELL_SPURS_EVENT_FLAG_ANY2ANY) { sys_event_port_disconnect(eventFlag->eventPortId); sys_event_port_destroy(eventFlag->eventPortId); } - s32 rc = CELL_OK; - // TODO: Implement the following - // auto rc = spursDetachLv2EventQueue(spurs, port, 1); - // if (rc == CELL_OK) - // { - // rc = sys_event_queue_destroy(eventFlag->eventQueueId, SYS_EVENT_QUEUE_DESTROY_FORCE); - // } + auto rc = spursDetachLv2EventQueue(spurs, port, true /*spursCreated*/); + + if (rc == CELL_OK) + { + rc = sys_event_queue_destroy(eventFlag->eventQueueId, SYS_EVENT_QUEUE_DESTROY_FORCE); + } if (rc != CELL_OK) { @@ -1647,7 +2295,7 @@ s32 cellSpursEventFlagDetachLv2EventQueue(vm::ptr eventFlag) return CELL_OK; } -s32 _cellSpursEventFlagWait(vm::ptr eventFlag, vm::ptr mask, u32 mode, u32 block) +s32 spursEventFlagWait(PPUThread& CPU, vm::ptr eventFlag, vm::ptr mask, u32 mode, u32 block) { if (!eventFlag || !mask) { @@ -1764,14 +2412,12 @@ s32 _cellSpursEventFlagWait(vm::ptr eventFlag, vm::ptr recv = true; } - u16 receivedEventFlag; - if (recv) { + if (recv) + { // Block till something happens - vm::var data; - auto rc = sys_event_queue_receive(GetCurrentPPUThread(), eventFlag->eventQueueId, data, 0); - if (rc != CELL_OK) + if (s32 rc = sys_event_queue_receive(CPU, eventFlag->eventQueueId, vm::null, 0)) { - assert(0); + throw __FUNCTION__; } int i = 0; @@ -1780,24 +2426,23 @@ s32 _cellSpursEventFlagWait(vm::ptr eventFlag, vm::ptr i = eventFlag->ppuWaitSlotAndMode >> 4; } - receivedEventFlag = eventFlag->pendingRecvTaskEvents[i]; + *mask = eventFlag->pendingRecvTaskEvents[i]; eventFlag->ppuPendingRecv = 0; } - *mask = receivedEventFlag; return CELL_OK; } -s32 cellSpursEventFlagWait(vm::ptr eventFlag, vm::ptr mask, u32 mode) +s32 cellSpursEventFlagWait(PPUThread& CPU, vm::ptr eventFlag, vm::ptr mask, u32 mode) { - cellSpurs.Warning("cellSpursEventFlagWait(eventFlag_addr=0x%x, mask_addr=0x%x, mode=%d)", eventFlag.addr(), mask.addr(), mode); + cellSpurs.Warning("cellSpursEventFlagWait(eventFlag=*0x%x, mask=*0x%x, mode=%d)", eventFlag, mask, mode); - return _cellSpursEventFlagWait(eventFlag, mask, mode, 1/*block*/); + return spursEventFlagWait(CPU, eventFlag, mask, mode, 1 /*block*/); } s32 cellSpursEventFlagClear(vm::ptr eventFlag, u16 bits) { - cellSpurs.Warning("cellSpursEventFlagClear(eventFlag_addr=0x%x, bits=0x%x)", eventFlag.addr(), bits); + cellSpurs.Warning("cellSpursEventFlagClear(eventFlag=*0x%x, bits=0x%x)", eventFlag, bits); if (!eventFlag) { @@ -1813,9 +2458,9 @@ s32 cellSpursEventFlagClear(vm::ptr eventFlag, u16 bits) return CELL_OK; } -s32 cellSpursEventFlagSet(vm::ptr eventFlag, u16 bits) +s32 cellSpursEventFlagSet(PPUThread& CPU, vm::ptr eventFlag, u16 bits) { - cellSpurs.Warning("cellSpursEventFlagSet(eventFlag_addr=0x%x, bits=0x%x)", eventFlag.addr(), bits); + cellSpurs.Warning("cellSpursEventFlagSet(eventFlag=*0x%x, bits=0x%x)", eventFlag, bits); if (!eventFlag) { @@ -1908,19 +2553,17 @@ s32 cellSpursEventFlagSet(vm::ptr eventFlag, u16 bits) if (spuTaskPendingRecv & (0x8000 >> i)) { eventFlag->pendingRecvTaskEvents[i] = pendingRecvTaskEvents[i]; - vm::var taskset; + vm::stackvar> taskset(CPU); if (eventFlag->isIwl) { - cellSpursLookUpTasksetAddress(vm::ptr::make((u32)eventFlag->addr), - vm::ptr::make(taskset.addr()), - eventFlag->waitingTaskWklId[i]); + cellSpursLookUpTasksetAddress(vm::ptr::make((u32)eventFlag->addr), taskset, eventFlag->waitingTaskWklId[i]); } else { - taskset.value() = (u32)eventFlag->addr; + taskset->set((u32)eventFlag->addr); } - auto rc = _cellSpursSendSignal(vm::ptr::make(taskset.addr()), eventFlag->waitingTaskId[i]); + auto rc = _cellSpursSendSignal(CPU, taskset.value(), eventFlag->waitingTaskId[i]); if (rc == CELL_SPURS_TASK_ERROR_INVAL || rc == CELL_SPURS_TASK_ERROR_STAT) { return CELL_SPURS_TASK_ERROR_FATAL; @@ -1928,7 +2571,7 @@ s32 cellSpursEventFlagSet(vm::ptr eventFlag, u16 bits) if (rc != CELL_OK) { - assert(0); + throw __FUNCTION__; } } } @@ -1937,16 +2580,16 @@ s32 cellSpursEventFlagSet(vm::ptr eventFlag, u16 bits) return CELL_OK; } -s32 cellSpursEventFlagTryWait(vm::ptr eventFlag, vm::ptr mask, u32 mode) +s32 cellSpursEventFlagTryWait(PPUThread& CPU, vm::ptr eventFlag, vm::ptr mask, u32 mode) { - cellSpurs.Warning("cellSpursEventFlagTryWait(eventFlag_addr=0x%x, mask_addr=0x%x, mode=0x%x)", eventFlag.addr(), mask.addr(), mode); + cellSpurs.Warning("cellSpursEventFlagTryWait(eventFlag=*0x%x, mask=*0x%x, mode=0x%x)", eventFlag, mask, mode); - return _cellSpursEventFlagWait(eventFlag, mask, mode, 0/*block*/); + return spursEventFlagWait(CPU, eventFlag, mask, mode, 0 /*block*/); } s32 cellSpursEventFlagGetDirection(vm::ptr eventFlag, vm::ptr direction) { - cellSpurs.Warning("cellSpursEventFlagGetDirection(eventFlag_addr=0x%x, direction_addr=0x%x)", eventFlag.addr(), direction.addr()); + cellSpurs.Warning("cellSpursEventFlagGetDirection(eventFlag=*0x%x, direction=*0x%x)", eventFlag, direction); if (!eventFlag || !direction) { @@ -1964,7 +2607,7 @@ s32 cellSpursEventFlagGetDirection(vm::ptr eventFlag, vm::pt s32 cellSpursEventFlagGetClearMode(vm::ptr eventFlag, vm::ptr clear_mode) { - cellSpurs.Warning("cellSpursEventFlagGetClearMode(eventFlag_addr=0x%x, clear_mode_addr=0x%x)", eventFlag.addr(), clear_mode.addr()); + cellSpurs.Warning("cellSpursEventFlagGetClearMode(eventFlag=*0x%x, clear_mode=*0x%x)", eventFlag, clear_mode); if (!eventFlag || !clear_mode) { @@ -1980,9 +2623,9 @@ s32 cellSpursEventFlagGetClearMode(vm::ptr eventFlag, vm::pt return CELL_OK; } -s32 cellSpursEventFlagGetTasksetAddress(vm::ptr eventFlag, vm::ptr taskset) +s32 cellSpursEventFlagGetTasksetAddress(vm::ptr eventFlag, vm::pptr taskset) { - cellSpurs.Warning("cellSpursEventFlagGetTasksetAddress(eventFlag_addr=0x%x, taskset_addr=0x%x)", eventFlag.addr(), taskset.addr()); + cellSpurs.Warning("cellSpursEventFlagGetTasksetAddress(eventFlag=*0x%x, taskset=**0x%x)", eventFlag, taskset); if (!eventFlag || !taskset) { @@ -1994,7 +2637,7 @@ s32 cellSpursEventFlagGetTasksetAddress(vm::ptr eventFlag, v return CELL_SPURS_TASK_ERROR_ALIGN; } - taskset.set(eventFlag->isIwl ? 0 : eventFlag->addr); + taskset->set(eventFlag->isIwl ? 0 : eventFlag->addr); return CELL_OK; } @@ -2160,8 +2803,7 @@ s32 cellSpursJobChainGetSpursAddress() return CELL_OK; } -s32 spursCreateTaskset(vm::ptr spurs, vm::ptr taskset, u64 args, vm::cptr priority, - u32 max_contention, vm::cptr name, u32 size, s32 enable_clear_ls) +s32 spursCreateTaskset(PPUThread& CPU, vm::ptr spurs, vm::ptr taskset, u64 args, vm::cptr priority, u32 max_contention, vm::cptr name, u32 size, s32 enable_clear_ls) { if (!spurs || !taskset) { @@ -2180,7 +2822,7 @@ s32 spursCreateTaskset(vm::ptr spurs, vm::ptr tasks taskset->enable_clear_ls = enable_clear_ls > 0 ? 1 : 0; taskset->size = size; - vm::var wkl_attr; + vm::stackvar wkl_attr(CPU); _cellSpursWorkloadAttributeInitialize(wkl_attr, 1 /*revision*/, 0x33 /*sdk_version*/, vm::cptr::make(SPURS_IMG_ADDR_TASKSET_PM), 0x1E40 /*pm_size*/, taskset.addr(), priority, 8 /*min_contention*/, max_contention); // TODO: Check return code @@ -2191,8 +2833,8 @@ s32 spursCreateTaskset(vm::ptr spurs, vm::ptr tasks // TODO: cellSpursWorkloadAttributeSetShutdownCompletionEventHook(wkl_attr, hook, taskset); // TODO: Check return code - vm::var> wid; - cellSpursAddWorkloadWithAttribute(spurs, vm::ptr::make(wid.addr()), wkl_attr); + vm::stackvar> wid(CPU); + cellSpursAddWorkloadWithAttribute(spurs, wid, wkl_attr); // TODO: Check return code taskset->wkl_flag_wait_task = 0x80; @@ -2203,9 +2845,9 @@ s32 spursCreateTaskset(vm::ptr spurs, vm::ptr tasks return CELL_OK; } -s32 cellSpursCreateTasksetWithAttribute(vm::ptr spurs, vm::ptr taskset, vm::ptr attr) +s32 cellSpursCreateTasksetWithAttribute(PPUThread& CPU, vm::ptr spurs, vm::ptr taskset, vm::ptr attr) { - cellSpurs.Warning("%s(spurs=0x%x, taskset=0x%x, attr=0x%x)", __FUNCTION__, spurs.addr(), taskset.addr(), attr.addr()); + cellSpurs.Warning("cellSpursCreateTasksetWithAttribute(spurs=*0x%x, taskset=*0x%x, attr=*0x%x)", spurs, taskset, attr); if (!attr) { @@ -2222,8 +2864,7 @@ s32 cellSpursCreateTasksetWithAttribute(vm::ptr spurs, vm::ptrargs, vm::cptr::make(attr.addr() + offsetof(CellSpursTasksetAttribute, priority)), - attr->max_contention, vm::cptr::make(attr->name.addr()), attr->taskset_size, attr->enable_clear_ls); + auto rc = spursCreateTaskset(CPU, spurs, taskset, attr->args, attr.of(&CellSpursTasksetAttribute::priority), attr->max_contention, attr->name, attr->taskset_size, attr->enable_clear_ls); if (attr->taskset_size >= sizeof32(CellSpursTaskset2)) { @@ -2233,17 +2874,16 @@ s32 cellSpursCreateTasksetWithAttribute(vm::ptr spurs, vm::ptr spurs, vm::ptr taskset, u64 args, vm::cptr priority, u32 maxContention) +s32 cellSpursCreateTaskset(PPUThread& CPU, vm::ptr spurs, vm::ptr taskset, u64 args, vm::cptr priority, u32 maxContention) { - cellSpurs.Warning("cellSpursCreateTaskset(spurs_addr=0x%x, taskset_addr=0x%x, args=0x%llx, priority_addr=0x%x, maxContention=%d)", - spurs.addr(), taskset.addr(), args, priority.addr(), maxContention); + cellSpurs.Warning("cellSpursCreateTaskset(spurs=*0x%x, taskset=*0x%x, args=0x%llx, priority=*0x%x, maxContention=%d)", spurs, taskset, args, priority, maxContention); - return spursCreateTaskset(spurs, taskset, args, priority, maxContention, vm::null, 6400/*CellSpursTaskset::size*/, 0); + return spursCreateTaskset(CPU, spurs, taskset, args, priority, maxContention, vm::null, sizeof32(CellSpursTaskset), 0); } s32 cellSpursJoinTaskset(vm::ptr taskset) { - cellSpurs.Warning("cellSpursJoinTaskset(taskset_addr=0x%x)", taskset.addr()); + cellSpurs.Warning("cellSpursJoinTaskset(taskset=*0x%x)", taskset); UNIMPLEMENTED_FUNC(cellSpurs); return CELL_OK; @@ -2251,7 +2891,7 @@ s32 cellSpursJoinTaskset(vm::ptr taskset) s32 cellSpursGetTasksetId(vm::ptr taskset, vm::ptr wid) { - cellSpurs.Warning("cellSpursGetTasksetId(taskset_addr=0x%x, wid=0x%x)", taskset.addr(), wid.addr()); + cellSpurs.Warning("cellSpursGetTasksetId(taskset=*0x%x, wid=*0x%x)", taskset, wid); if (!taskset || !wid) { @@ -2278,22 +2918,6 @@ s32 cellSpursShutdownTaskset(vm::ptr taskset) return CELL_OK; } -u32 _cellSpursGetSdkVersion() -{ - // Commenting this out for now since process_get_sdk_version does not return - // the correct SDK version and instead returns a version too high for the game - // and causes SPURS to fail. - //s32 sdk_version; - - //if (process_get_sdk_version(process_getpid(), sdk_version) != CELL_OK) - //{ - // throw __FUNCTION__; - //} - - //return sdk_version; - return 1; -} - s32 spursCreateTask(vm::ptr taskset, vm::ptr task_id, vm::ptr elf_addr, vm::ptr context_addr, u32 context_size, vm::ptr ls_pattern, vm::ptr arg) { if (!taskset || !elf_addr) @@ -2306,7 +2930,7 @@ s32 spursCreateTask(vm::ptr taskset, vm::ptr task_id, vm: return CELL_SPURS_TASK_ERROR_ALIGN; } - auto sdk_version = _cellSpursGetSdkVersion(); + auto sdk_version = spursGetSdkVersion(); if (sdk_version < 0x27FFFF) { if (context_addr % 16) @@ -2323,7 +2947,7 @@ s32 spursCreateTask(vm::ptr taskset, vm::ptr task_id, vm: } u32 alloc_ls_blocks = 0; - if (context_addr.addr() != 0) + if (context_addr) { if (context_size < CELL_SPURS_TASK_EXECUTION_CONTEXT_SIZE) { @@ -2331,7 +2955,7 @@ s32 spursCreateTask(vm::ptr taskset, vm::ptr task_id, vm: } alloc_ls_blocks = context_size > 0x3D400 ? 0x7A : ((context_size - 0x400) >> 11); - if (ls_pattern.addr() != 0) + if (ls_pattern) { u128 ls_pattern_128 = u128::from64r(ls_pattern->_u64[0], ls_pattern->_u64[1]); u32 ls_blocks = 0; @@ -2383,7 +3007,7 @@ s32 spursCreateTask(vm::ptr taskset, vm::ptr task_id, vm: taskset->task_info[tmp_task_id].elf_addr.set(elf_addr.addr()); taskset->task_info[tmp_task_id].context_save_storage_and_alloc_ls_blocks = (context_addr.addr() | alloc_ls_blocks); taskset->task_info[tmp_task_id].args = *arg; - if (ls_pattern.addr()) + if (ls_pattern) { taskset->task_info[tmp_task_id].ls_pattern = *ls_pattern; } @@ -2392,15 +3016,15 @@ s32 spursCreateTask(vm::ptr taskset, vm::ptr task_id, vm: return CELL_OK; } -s32 spursTaskStart(vm::ptr taskset, u32 taskId) +s32 spursTaskStart(PPUThread& CPU, vm::ptr taskset, u32 taskId) { auto pendingReady = taskset->pending_ready.value(); pendingReady._bit[taskId] = true; taskset->pending_ready = pendingReady; cellSpursSendWorkloadSignal(taskset->spurs, taskset->wid); - auto rc = cellSpursWakeUp(GetCurrentPPUThread(), taskset->spurs); - if (rc != CELL_OK) + + if (s32 rc = cellSpursWakeUp(CPU, taskset->spurs)) { if (rc == CELL_SPURS_POLICY_MODULE_ERROR_STAT) { @@ -2408,17 +3032,17 @@ s32 spursTaskStart(vm::ptr taskset, u32 taskId) } else { - assert(0); + throw __FUNCTION__; } } - return rc; + return CELL_OK; } -s32 cellSpursCreateTask(vm::ptr taskset, vm::ptr taskId, u32 elf_addr, u32 context_addr, u32 context_size, vm::ptr lsPattern, - vm::ptr argument) +s32 cellSpursCreateTask(PPUThread& CPU, vm::ptr taskset, vm::ptr taskId, u32 elf_addr, u32 context_addr, u32 context_size, + vm::ptr lsPattern, vm::ptr argument) { - cellSpurs.Warning("cellSpursCreateTask(taskset_addr=0x%x, taskID_addr=0x%x, elf_addr_addr=0x%x, context_addr_addr=0x%x, context_size=%d, lsPattern_addr=0x%x, argument_addr=0x%x)", + cellSpurs.Warning("cellSpursCreateTask(taskset=*0x%x, taskID=*0x%x, elf_addr=0x%x, context_addr=0x%x, context_size=%d, lsPattern_addr=0x%x, argument_addr=0x%x)", taskset.addr(), taskId.addr(), elf_addr, context_addr, context_size, lsPattern.addr(), argument.addr()); if (!taskset) @@ -2438,7 +3062,7 @@ s32 cellSpursCreateTask(vm::ptr taskset, vm::ptr taskId, return rc; } - rc = spursTaskStart(taskset, tmpTaskId->value()); + rc = spursTaskStart(CPU, taskset, tmpTaskId->value()); if (rc != CELL_OK) { return rc; @@ -2448,7 +3072,7 @@ s32 cellSpursCreateTask(vm::ptr taskset, vm::ptr taskId, return CELL_OK; } -s32 _cellSpursSendSignal(vm::ptr taskset, u32 taskId) +s32 _cellSpursSendSignal(PPUThread& CPU, vm::ptr taskset, u32 taskId) { if (!taskset) { @@ -2482,7 +3106,7 @@ s32 _cellSpursSendSignal(vm::ptr taskset, u32 taskId) if (shouldSignal) { cellSpursSendWorkloadSignal(taskset->spurs, taskset->wid); - auto rc = cellSpursWakeUp(GetCurrentPPUThread(), taskset->spurs); + auto rc = cellSpursWakeUp(CPU, taskset->spurs); if (rc == CELL_SPURS_POLICY_MODULE_ERROR_STAT) { return CELL_SPURS_TASK_ERROR_STAT; @@ -2654,7 +3278,7 @@ s32 cellSpursTaskGetContextSaveAreaSize() return CELL_OK; } -s32 cellSpursCreateTaskset2(vm::ptr spurs, vm::ptr taskset, vm::ptr attr) +s32 cellSpursCreateTaskset2(PPUThread& CPU, vm::ptr spurs, vm::ptr taskset, vm::ptr attr) { cellSpurs.Warning("%s(spurs=0x%x, taskset=0x%x, attr=0x%x)", __FUNCTION__, spurs.addr(), taskset.addr(), attr.addr()); @@ -2666,7 +3290,7 @@ s32 cellSpursCreateTaskset2(vm::ptr spurs, vm::ptr _cellSpursTasksetAttribute2Initialize(attr, 0); } - auto rc = spursCreateTaskset(spurs, vm::ptr::make(taskset.addr()), attr->args, + auto rc = spursCreateTaskset(CPU, spurs, vm::ptr::make(taskset.addr()), attr->args, vm::cptr::make(attr.addr() + offsetof(CellSpursTasksetAttribute, priority)), attr->max_contention, attr->name, sizeof32(CellSpursTaskset2), (u8)attr->enable_clear_ls); @@ -2767,24 +3391,23 @@ s32 cellSpursTasksetUnsetExceptionEventHandler(vm::ptr taskset return CELL_OK; } -s32 cellSpursLookUpTasksetAddress(vm::ptr spurs, vm::ptr taskset, u32 id) +s32 cellSpursLookUpTasksetAddress(vm::ptr spurs, vm::pptr taskset, u32 id) { - cellSpurs.Warning("%s(spurs=0x%x, taskset=0x%x, id=0x%x)", __FUNCTION__, spurs.addr(), taskset.addr(), id); + cellSpurs.Warning("cellSpursLookUpTasksetAddress(spurs=*0x%x, taskset=**0x%x, id=0x%x)", spurs, taskset, id); - if (taskset.addr() == 0) + if (!taskset) { return CELL_SPURS_TASK_ERROR_NULL_POINTER; } vm::var> data; - auto rc = cellSpursGetWorkloadData(spurs, data, id); - if (rc != CELL_OK) + if (s32 rc = cellSpursGetWorkloadData(spurs, data, id)) { // Convert policy module error code to a task error code return rc ^ 0x100; } - taskset.set((u32)data.value()); + taskset->set((u32)data.value()); return CELL_OK; } diff --git a/rpcs3/Emu/SysCalls/Modules/cellSpurs.h b/rpcs3/Emu/SysCalls/Modules/cellSpurs.h index 31e276d32a..4b2b10cfac 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellSpurs.h +++ b/rpcs3/Emu/SysCalls/Modules/cellSpurs.h @@ -94,43 +94,38 @@ enum // SPURS defines. enum SPURSKernelInterfaces { - CELL_SPURS_MAX_SPU = 8, - CELL_SPURS_MAX_WORKLOAD = 16, - CELL_SPURS_MAX_WORKLOAD2 = 32, - CELL_SPURS_SYS_SERVICE_WORKLOAD_ID = 32, - CELL_SPURS_MAX_PRIORITY = 16, - CELL_SPURS_NAME_MAX_LENGTH = 15, - CELL_SPURS_SIZE = 4096, - CELL_SPURS_SIZE2 = 8192, - CELL_SPURS_ALIGN = 128, - CELL_SPURS_ATTRIBUTE_SIZE = 512, - CELL_SPURS_ATTRIBUTE_ALIGN = 8, - CELL_SPURS_INTERRUPT_VECTOR = 0x0, - CELL_SPURS_LOCK_LINE = 0x80, - CELL_SPURS_KERNEL_DMA_TAG_ID = 31, - CELL_SPURS_KERNEL1_ENTRY_ADDR = 0x818, - CELL_SPURS_KERNEL2_ENTRY_ADDR = 0x848, - CELL_SPURS_KERNEL1_EXIT_ADDR = 0x808, - CELL_SPURS_KERNEL2_EXIT_ADDR = 0x838, + CELL_SPURS_MAX_SPU = 8, + CELL_SPURS_MAX_WORKLOAD = 16, + CELL_SPURS_MAX_WORKLOAD2 = 32, + CELL_SPURS_SYS_SERVICE_WORKLOAD_ID = 32, + CELL_SPURS_MAX_PRIORITY = 16, + CELL_SPURS_NAME_MAX_LENGTH = 15, + CELL_SPURS_SIZE = 4096, + CELL_SPURS_SIZE2 = 8192, + CELL_SPURS_INTERRUPT_VECTOR = 0x0, + CELL_SPURS_LOCK_LINE = 0x80, + CELL_SPURS_KERNEL_DMA_TAG_ID = 31, + CELL_SPURS_KERNEL1_ENTRY_ADDR = 0x818, + CELL_SPURS_KERNEL2_ENTRY_ADDR = 0x848, + CELL_SPURS_KERNEL1_EXIT_ADDR = 0x808, + CELL_SPURS_KERNEL2_EXIT_ADDR = 0x838, CELL_SPURS_KERNEL1_SELECT_WORKLOAD_ADDR = 0x290, CELL_SPURS_KERNEL2_SELECT_WORKLOAD_ADDR = 0x290, }; enum RangeofEventQueuePortNumbers { - CELL_SPURS_STATIC_PORT_RANGE_BOTTOM = 15, - CELL_SPURS_DYNAMIC_PORT_RANGE_TOP = 16, + CELL_SPURS_STATIC_PORT_RANGE_BOTTOM = 15, + CELL_SPURS_DYNAMIC_PORT_RANGE_TOP = 16, CELL_SPURS_DYNAMIC_PORT_RANGE_BOTTOM = 63, }; enum SpursAttrFlags : u32 { - SAF_NONE = 0x0, - - SAF_EXIT_IF_NO_WORK = 0x1, - SAF_UNKNOWN_FLAG_30 = 0x2, - SAF_SECOND_VERSION = 0x4, - + SAF_NONE = 0x00000000, + SAF_EXIT_IF_NO_WORK = 0x00000001, + SAF_UNKNOWN_FLAG_30 = 0x00000002, + SAF_SECOND_VERSION = 0x00000004, SAF_UNKNOWN_FLAG_9 = 0x00400000, SAF_UNKNOWN_FLAG_8 = 0x00800000, SAF_UNKNOWN_FLAG_7 = 0x01000000, @@ -143,8 +138,7 @@ enum SpursAttrFlags : u32 enum SpursFlags1 : u8 { - SF1_NONE = 0x0, - + SF1_NONE = 0x00, SF1_32_WORKLOADS = 0x40, SF1_EXIT_IF_NO_WORK = 0x80, }; @@ -416,9 +410,10 @@ struct set_alignment(128) CellSpurs { struct _sub_str1 { - u8 unk0[0x20]; // 0x00 - SPU exceptionh handler 0x08 - SPU exception handler args + u8 unk0[0x20]; // 0x00 - SPU exception handler 0x08 - SPU exception handler args be_t sem; // 0x20 - u8 unk1[0x8]; + be_t x28; // 0x28 + be_t x2C; // 0x2C vm::bptr hook; // 0x30 vm::bptr hookArg; // 0x38 u8 unk2[0x40]; @@ -426,26 +421,38 @@ struct set_alignment(128) CellSpurs CHECK_SIZE(_sub_str1, 128); - struct _sub_str2 // Event port multiplexer + struct EventPortMux; + + using EventHandlerCallback = func_def, u64 data)>; + + struct EventHandlerListNode { - be_t unk0; // 0x00 Outstanding requests - be_t unk1; // 0x04 - be_t unk2; // 0x08 - be_t unk3; // 0x0C - be_t port; // 0x10 - u8 unk_[0x68]; // 0x18 - The first u64 seems to be the start of a linked list. The linked list struct seems to be {u64 next; u64 data; u64 handler} + vm::bptr next; + be_t data; + vm::bptr handler; }; - CHECK_SIZE(_sub_str2, 128); + struct EventPortMux + { + atomic_be_t reqPending; // 0x00 + be_t spuPort; // 0x04 + be_t x08; // 0x08 + be_t x0C; // 0x0C + be_t eventPort; // 0x10 + atomic_be_t> handlerList; // 0x18 + u8 x20[0x80 - 0x20]; // 0x20 + }; + + CHECK_SIZE(EventPortMux, 128); struct WorkloadInfo { - vm::bcptr addr; // Address of the executable - be_t arg; // spu argument - be_t size; - atomic_be_t uniqueId; // The unique id is the same for all workloads with the same addr + vm::bcptr addr; // 0x00 Address of the executable + be_t arg; // 0x08 Argument + be_t size; // 0x10 Size of the executable + atomic_be_t uniqueId; // 0x14 Unique id of the workload. It is the same for all workloads with the same addr. u8 pad[3]; - u8 priority[8]; + u8 priority[8]; // 0x18 Priority of the workload on each SPU }; CHECK_SIZE(WorkloadInfo, 32); @@ -463,26 +470,26 @@ struct set_alignment(128) CellSpurs u8 wklMinContention[0x10]; // 0x40 Min SPUs required for each workload. SPURS1: index = wid. SPURS2: Unused. atomic_be_t wklMaxContention[0x10]; // 0x50 Max SPUs that may be allocated to each workload. SPURS1: index = wid. SPURS2: packed 4-bit data, index = wid % 16, internal index = wid / 16. CellSpursWorkloadFlag wklFlag; // 0x60 - atomic_be_t wklSignal1; // 0x70 (bitset for 0..15 wids) + atomic_be_t wklSignal1; // 0x70 Bitset for 0..15 wids atomic_be_t sysSrvMessage; // 0x72 u8 spuIdling; // 0x73 u8 flags1; // 0x74 Type is SpursFlags1 u8 sysSrvTraceControl; // 0x75 u8 nSpus; // 0x76 atomic_be_t wklFlagReceiver; // 0x77 - atomic_be_t wklSignal2; // 0x78 (bitset for 16..32 wids) + atomic_be_t wklSignal2; // 0x78 Bitset for 16..32 wids u8 x7A[6]; // 0x7A atomic_be_t wklState1[0x10]; // 0x80 SPURS_WKL_STATE_* u8 wklStatus1[0x10]; // 0x90 - u8 wklEvent1[0x10]; // 0xA0 - atomic_be_t wklMskA; // 0xB0 - System service - Available workloads (32*u1) + atomic_be_t wklEvent1[0x10]; // 0xA0 + atomic_be_t wklEnabled; // 0xB0 atomic_be_t wklMskB; // 0xB4 - System service - Available module id u32 xB8; // 0xB8 u8 sysSrvExitBarrier; // 0xBC atomic_be_t sysSrvMsgUpdateWorkload; // 0xBD u8 xBE; // 0xBE u8 sysSrvMsgTerminate; // 0xBF - u8 sysSrvWorkload[8]; // 0xC0 + u8 sysSrvPreemptWklId[8]; // 0xC0 Id of the workload that was preempted by the system workload on each SPU u8 sysSrvOnSpu; // 0xC8 u8 spuPort; // 0xC9 u8 xCA; // 0xCA @@ -493,7 +500,7 @@ struct set_alignment(128) CellSpurs u8 xCF; // 0xCF atomic_be_t wklState2[0x10]; // 0xD0 SPURS_WKL_STATE_* u8 wklStatus2[0x10]; // 0xE0 - u8 wklEvent2[0x10]; // 0xF0 + atomic_be_t wklEvent2[0x10]; // 0xF0 _sub_str1 wklF1[0x10]; // 0x100 vm::bptr traceBuffer; // 0x900 be_t traceStartIndex[6]; // 0x908 @@ -508,16 +515,16 @@ struct set_alignment(128) CellSpurs u8 unknown4[0xB00 - 0x998]; WorkloadInfo wklInfo1[0x10]; // 0xB00 WorkloadInfo wklInfoSysSrv; // 0xD00 - be_t ppu0; // 0xD20 + be_t ppu0; // 0xD20 Handler thread be_t ppu1; // 0xD28 - be_t spuTG; // 0xD30 - SPU thread group + be_t spuTG; // 0xD30 SPU thread group be_t spus[8]; // 0xD34 u8 unknown3[0xD5C - 0xD54]; - be_t queue; // 0xD5C - Event queue - be_t port; // 0xD60 - Event port - atomic_be_t xD64; // 0xD64 - SPURS handler dirty - atomic_be_t xD65; // 0xD65 - SPURS handler waiting - atomic_be_t xD66; // 0xD66 - SPURS handler exiting + be_t eventQueue; // 0xD5C + be_t eventPort; // 0xD60 + atomic_be_t handlerDirty; // 0xD64 + atomic_be_t handlerWaiting; // 0xD65 + atomic_be_t handlerExiting; // 0xD66 atomic_be_t enableEH; // 0xD68 be_t exception; // 0xD6C sys_spu_image spuImg; // 0xD70 @@ -529,12 +536,12 @@ struct set_alignment(128) CellSpurs be_t unk5; // 0xD9C be_t revision; // 0xDA0 be_t sdkVersion; // 0xDA4 - atomic_be_t spups; // 0xDA8 - SPU port bits + atomic_be_t spuPortBits; // 0xDA8 sys_lwmutex_t mutex; // 0xDB0 sys_lwcond_t cond; // 0xDC8 u8 unknown9[0xE00 - 0xDD0]; _sub_str4 wklH1[0x10]; // 0xE00 - _sub_str2 sub3; // 0xF00 + EventPortMux eventPortMux; // 0xF00 u8 unknown6[0x1000 - 0xF80]; // 0xF80 - Gloabl SPU exception handler 0xF88 - Gloabl SPU exception handlers args WorkloadInfo wklInfo2[0x10]; // 0x1000 _sub_str1 wklF2[0x10]; // 0x1200 @@ -552,16 +559,6 @@ struct set_alignment(128) CellSpurs return wklState1[wid & 0xf]; } } - - force_inline vm::ptr get_lwmutex() - { - return vm::ptr::make(vm::get_addr(&mutex)); - } - - force_inline vm::ptr get_lwcond() - { - return vm::ptr::make(vm::get_addr(&cond)); - } }; CHECK_SIZE_ALIGN(CellSpurs, 0x2000, 128); @@ -888,5 +885,6 @@ struct SpursTasksetContext CHECK_SIZE(SpursTasksetContext, 0x900); -s32 spursAttachLv2EventQueue(vm::ptr spurs, u32 queue, vm::ptr port, s32 isDynamic, bool wasCreated); -s32 spursWakeUp(PPUThread& CPU, vm::ptr spurs); +class SpursModuleExit +{ +}; diff --git a/rpcs3/Emu/SysCalls/Modules/cellSpursSpu.cpp b/rpcs3/Emu/SysCalls/Modules/cellSpursSpu.cpp index 4f5f5f8bd6..6c8580dfe4 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellSpursSpu.cpp +++ b/rpcs3/Emu/SysCalls/Modules/cellSpursSpu.cpp @@ -11,82 +11,75 @@ #include "Loader/ELF32.h" #include "Emu/FS/vfsStreamMemory.h" -////////////////////////////////////////////////////////////////////////////// -// Types -////////////////////////////////////////////////////////////////////////////// - -class SpursModuleExit { -}; - -////////////////////////////////////////////////////////////////////////////// -// Function prototypes -////////////////////////////////////////////////////////////////////////////// - -// -// SPURS utility functions -// -void cellSpursModulePutTrace(CellSpursTracePacket * packet, u32 dmaTagId); -u32 cellSpursModulePollStatus(SPUThread & spu, u32 * status); -void cellSpursModuleExit(SPUThread & spu); - -bool spursDma(SPUThread & spu, u32 cmd, u64 ea, u32 lsa, u32 size, u32 tag); -u32 spursDmaGetCompletionStatus(SPUThread & spu, u32 tagMask); -u32 spursDmaWaitForCompletion(SPUThread & spu, u32 tagMask, bool waitForAll = true); -void spursHalt(SPUThread & spu); - -// -// SPURS Kernel functions -// -bool spursKernel1SelectWorkload(SPUThread & spu); -bool spursKernel2SelectWorkload(SPUThread & spu); -void spursKernelDispatchWorkload(SPUThread & spu, u64 widAndPollStatus); -bool spursKernelWorkloadExit(SPUThread & spu); -bool spursKernelEntry(SPUThread & spu); - -// -// SPURS System Service functions -// -bool spursSysServiceEntry(SPUThread & spu); -// TODO: Exit -void spursSysServiceIdleHandler(SPUThread & spu, SpursKernelContext * ctxt); -void spursSysServiceMain(SPUThread & spu, u32 pollStatus); -void spursSysServiceProcessRequests(SPUThread & spu, SpursKernelContext * ctxt); -void spursSysServiceActivateWorkload(SPUThread & spu, SpursKernelContext * ctxt); -// TODO: Deactivate workload -void spursSysServiceUpdateShutdownCompletionEvents(SPUThread & spu, SpursKernelContext * ctxt, u32 wklShutdownBitSet); -void spursSysServiceTraceSaveCount(SPUThread & spu, SpursKernelContext * ctxt); -void spursSysServiceTraceUpdate(SPUThread & spu, SpursKernelContext * ctxt, u32 arg2, u32 arg3, u32 arg4); -// TODO: Deactivate trace -// TODO: System workload entry -void spursSysServiceCleanupAfterSystemWorkload(SPUThread & spu, SpursKernelContext * ctxt); - -// -// SPURS Taskset Policy Module functions -// -bool spursTasksetEntry(SPUThread & spu); -bool spursTasksetSyscallEntry(SPUThread & spu); -void spursTasksetResumeTask(SPUThread & spu); -void spursTasksetStartTask(SPUThread & spu, CellSpursTaskArgument & taskArgs); -s32 spursTasksetProcessRequest(SPUThread & spu, s32 request, u32 * taskId, u32 * isWaiting); -void spursTasksetProcessPollStatus(SPUThread & spu, u32 pollStatus); -bool spursTasksetPollStatus(SPUThread & spu); -void spursTasksetExit(SPUThread & spu); -void spursTasksetOnTaskExit(SPUThread & spu, u64 addr, u32 taskId, s32 exitCode, u64 args); -s32 spursTasketSaveTaskContext(SPUThread & spu); -void spursTasksetDispatch(SPUThread & spu); -s32 spursTasksetProcessSyscall(SPUThread & spu, u32 syscallNum, u32 args); -void spursTasksetInit(SPUThread & spu, u32 pollStatus); -s32 spursTasksetLoadElf(SPUThread & spu, u32 * entryPoint, u32 * lowestLoadAddr, u64 elfAddr, bool skipWriteableSegments); - -////////////////////////////////////////////////////////////////////////////// +//---------------------------------------------------------------------------- // Externs -////////////////////////////////////////////////////////////////////////////// +//---------------------------------------------------------------------------- extern Module cellSpurs; -////////////////////////////////////////////////////////////////////////////// +//---------------------------------------------------------------------------- +// Function prototypes +//---------------------------------------------------------------------------- + +// // SPURS utility functions -////////////////////////////////////////////////////////////////////////////// +// +static void cellSpursModulePutTrace(CellSpursTracePacket * packet, u32 dmaTagId); +static u32 cellSpursModulePollStatus(SPUThread & spu, u32 * status); +static void cellSpursModuleExit(SPUThread & spu); + +static bool spursDma(SPUThread & spu, u32 cmd, u64 ea, u32 lsa, u32 size, u32 tag); +static u32 spursDmaGetCompletionStatus(SPUThread & spu, u32 tagMask); +static u32 spursDmaWaitForCompletion(SPUThread & spu, u32 tagMask, bool waitForAll = true); +static void spursHalt(SPUThread & spu); + +// +// SPURS kernel functions +// +static bool spursKernel1SelectWorkload(SPUThread & spu); +static bool spursKernel2SelectWorkload(SPUThread & spu); +static void spursKernelDispatchWorkload(SPUThread & spu, u64 widAndPollStatus); +static bool spursKernelWorkloadExit(SPUThread & spu); +bool spursKernelEntry(SPUThread & spu); + +// +// SPURS system workload functions +// +static bool spursSysServiceEntry(SPUThread & spu); +// TODO: Exit +static void spursSysServiceIdleHandler(SPUThread & spu, SpursKernelContext * ctxt); +static void spursSysServiceMain(SPUThread & spu, u32 pollStatus); +static void spursSysServiceProcessRequests(SPUThread & spu, SpursKernelContext * ctxt); +static void spursSysServiceActivateWorkload(SPUThread & spu, SpursKernelContext * ctxt); +// TODO: Deactivate workload +static void spursSysServiceUpdateShutdownCompletionEvents(SPUThread & spu, SpursKernelContext * ctxt, u32 wklShutdownBitSet); +static void spursSysServiceTraceSaveCount(SPUThread & spu, SpursKernelContext * ctxt); +static void spursSysServiceTraceUpdate(SPUThread & spu, SpursKernelContext * ctxt, u32 arg2, u32 arg3, u32 arg4); +// TODO: Deactivate trace +// TODO: System workload entry +static void spursSysServiceCleanupAfterSystemWorkload(SPUThread & spu, SpursKernelContext * ctxt); + +// +// SPURS taskset policy module functions +// +static bool spursTasksetEntry(SPUThread & spu); +static bool spursTasksetSyscallEntry(SPUThread & spu); +static void spursTasksetResumeTask(SPUThread & spu); +static void spursTasksetStartTask(SPUThread & spu, CellSpursTaskArgument & taskArgs); +static s32 spursTasksetProcessRequest(SPUThread & spu, s32 request, u32 * taskId, u32 * isWaiting); +static void spursTasksetProcessPollStatus(SPUThread & spu, u32 pollStatus); +static bool spursTasksetPollStatus(SPUThread & spu); +static void spursTasksetExit(SPUThread & spu); +static void spursTasksetOnTaskExit(SPUThread & spu, u64 addr, u32 taskId, s32 exitCode, u64 args); +static s32 spursTasketSaveTaskContext(SPUThread & spu); +static void spursTasksetDispatch(SPUThread & spu); +static s32 spursTasksetProcessSyscall(SPUThread & spu, u32 syscallNum, u32 args); +static void spursTasksetInit(SPUThread & spu, u32 pollStatus); +static s32 spursTasksetLoadElf(SPUThread & spu, u32 * entryPoint, u32 * lowestLoadAddr, u64 elfAddr, bool skipWriteableSegments); + +//---------------------------------------------------------------------------- +// SPURS utility functions +//---------------------------------------------------------------------------- /// Output trace information void cellSpursModulePutTrace(CellSpursTracePacket * packet, u32 dmaTagId) { @@ -160,9 +153,9 @@ void spursHalt(SPUThread & spu) { spu.halt(); } -////////////////////////////////////////////////////////////////////////////// +//---------------------------------------------------------------------------- // SPURS kernel functions -////////////////////////////////////////////////////////////////////////////// +//---------------------------------------------------------------------------- /// Select a workload to run bool spursKernel1SelectWorkload(SPUThread & spu) { @@ -582,9 +575,9 @@ bool spursKernelEntry(SPUThread & spu) { return false; } -////////////////////////////////////////////////////////////////////////////// +//---------------------------------------------------------------------------- // SPURS system workload functions -////////////////////////////////////////////////////////////////////////////// +//---------------------------------------------------------------------------- /// Entry point of the system service bool spursSysServiceEntry(SPUThread & spu) { @@ -931,14 +924,14 @@ void spursSysServiceUpdateShutdownCompletionEvents(SPUThread & spu, SpursKernelC for (u32 i = 0; i < CELL_SPURS_MAX_WORKLOAD; i++) { if (wklShutdownBitSet & (0x80000000u >> i)) { spurs->wklEvent1[i] |= 0x01; - if (spurs->wklEvent1[i] & 0x02 || spurs->wklEvent1[i] & 0x10) { + if (spurs->wklEvent1[i].read_relaxed() & 0x02 || spurs->wklEvent1[i].read_relaxed() & 0x10) { wklNotifyBitSet |= 0x80000000u >> i; } } if (wklShutdownBitSet & (0x8000 >> i)) { spurs->wklEvent2[i] |= 0x01; - if (spurs->wklEvent2[i] & 0x02 || spurs->wklEvent2[i] & 0x10) { + if (spurs->wklEvent2[i].read_relaxed() & 0x02 || spurs->wklEvent2[i].read_relaxed() & 0x10) { wklNotifyBitSet |= 0x8000 >> i; } } @@ -1022,13 +1015,13 @@ void spursSysServiceCleanupAfterSystemWorkload(SPUThread & spu, SpursKernelConte vm::reservation_op(vm::cast(ctxt->spurs.addr() + offsetof(CellSpurs, wklState1)), 128, [&]() { auto spurs = ctxt->spurs.priv_ptr(); - if (spurs->sysSrvWorkload[ctxt->spuNum] == 0xFF) { + if (spurs->sysSrvPreemptWklId[ctxt->spuNum] == 0xFF) { do_return = true; return; } - wklId = spurs->sysSrvWorkload[ctxt->spuNum]; - spurs->sysSrvWorkload[ctxt->spuNum] = 0xFF; + wklId = spurs->sysSrvPreemptWklId[ctxt->spuNum]; + spurs->sysSrvPreemptWklId[ctxt->spuNum] = 0xFF; memcpy(vm::get_ptr(spu.offset + 0x2D80), spurs->wklState1, 128); }); @@ -1066,9 +1059,9 @@ void spursSysServiceCleanupAfterSystemWorkload(SPUThread & spu, SpursKernelConte ctxt->wklCurrentId = wklIdSaved; } -////////////////////////////////////////////////////////////////////////////// +//---------------------------------------------------------------------------- // SPURS taskset policy module functions -////////////////////////////////////////////////////////////////////////////// +//---------------------------------------------------------------------------- enum SpursTasksetRequest { SPURS_TASKSET_REQUEST_POLL_SIGNAL = -1, diff --git a/rpcs3/Emu/SysCalls/Modules/sysPrxForUser.h b/rpcs3/Emu/SysCalls/Modules/sysPrxForUser.h index bb9c3c23cf..f491f38465 100644 --- a/rpcs3/Emu/SysCalls/Modules/sysPrxForUser.h +++ b/rpcs3/Emu/SysCalls/Modules/sysPrxForUser.h @@ -41,3 +41,5 @@ s32 sys_lwcond_signal(PPUThread& CPU, vm::ptr lwcond); s32 sys_lwcond_signal_all(PPUThread& CPU, vm::ptr lwcond); s32 sys_lwcond_signal_to(PPUThread& CPU, vm::ptr lwcond, u32 ppu_thread_id); s32 sys_lwcond_wait(PPUThread& CPU, vm::ptr lwcond, u64 timeout); + +void sys_ppu_thread_exit(PPUThread& CPU, u64 val); diff --git a/rpcs3/Emu/SysCalls/lv2/sleep_queue.h b/rpcs3/Emu/SysCalls/lv2/sleep_queue.h index c1c8167f08..b4c9768144 100644 --- a/rpcs3/Emu/SysCalls/lv2/sleep_queue.h +++ b/rpcs3/Emu/SysCalls/lv2/sleep_queue.h @@ -28,6 +28,12 @@ enum SYS_SYNC_ATTR_RECURSIVE_MASK = 0xF0, //??? }; +// attr_pshared +enum +{ + SYS_SYNC_NOT_PROCESS_SHARED = 0x200 +}; + class sleep_queue_t { std::vector m_waiting; diff --git a/rpcs3/Emu/SysCalls/lv2/sys_event.cpp b/rpcs3/Emu/SysCalls/lv2/sys_event.cpp index a278bbaccf..394766b056 100644 --- a/rpcs3/Emu/SysCalls/lv2/sys_event.cpp +++ b/rpcs3/Emu/SysCalls/lv2/sys_event.cpp @@ -13,6 +13,13 @@ SysCallBase sys_event("sys_event"); +void sys_event_queue_attribute_initialize(vm::ptr attr) +{ + attr->protocol = SYS_SYNC_PRIORITY; + attr->type = SYS_PPU_QUEUE; + attr->name[0] = '\0'; +} + s32 sys_event_queue_create(vm::ptr equeue_id, vm::ptr attr, u64 event_queue_key, s32 size) { sys_event.Warning("sys_event_queue_create(equeue_id=*0x%x, attr=*0x%x, event_queue_key=0x%llx, size=%d)", equeue_id, attr, event_queue_key, size); diff --git a/rpcs3/Emu/SysCalls/lv2/sys_event.h b/rpcs3/Emu/SysCalls/lv2/sys_event.h index e6f8e44080..58fcb3c846 100644 --- a/rpcs3/Emu/SysCalls/lv2/sys_event.h +++ b/rpcs3/Emu/SysCalls/lv2/sys_event.h @@ -15,12 +15,24 @@ enum : s32 SYS_EVENT_QUEUE_DESTROY_FORCE = 1, }; +// Event Queue Ipc Key +enum : u64 +{ + SYS_EVENT_QUEUE_LOCAL = 0x00, +}; + // Event Port Type enum : s32 { SYS_EVENT_PORT_LOCAL = 1, }; +// Event Port Name +enum : u64 +{ + SYS_EVENT_PORT_NO_NAME = 0, +}; + // Event Source Type enum : u32 { @@ -31,8 +43,9 @@ enum : u32 // Event Source Key enum : u64 { - SYS_SPU_THREAD_EVENT_USER_KEY = 0xFFFFFFFF53505501, - SYS_SPU_THREAD_EVENT_DMA_KEY = 0xFFFFFFFF53505502, // ??? + SYS_SPU_THREAD_EVENT_USER_KEY = 0xFFFFFFFF53505501ull, + SYS_SPU_THREAD_EVENT_DMA_KEY = 0xFFFFFFFF53505502ull, + SYS_SPU_THREAD_EVENT_EXCEPTION_KEY = 0xFFFFFFFF53505503ull, }; struct sys_event_queue_attr @@ -129,6 +142,8 @@ REG_ID_TYPE(lv2_event_port_t, 0x0E); // SYS_EVENT_PORT_OBJECT class PPUThread; +void sys_event_queue_attribute_initialize(vm::ptr attr); + // SysCalls s32 sys_event_queue_create(vm::ptr equeue_id, vm::ptr attr, u64 event_queue_key, s32 size); s32 sys_event_queue_destroy(u32 equeue_id, s32 mode); diff --git a/rpcs3/Emu/SysCalls/lv2/sys_lwmutex.cpp b/rpcs3/Emu/SysCalls/lv2/sys_lwmutex.cpp index 2dd2fe39f2..6d2fa37cb3 100644 --- a/rpcs3/Emu/SysCalls/lv2/sys_lwmutex.cpp +++ b/rpcs3/Emu/SysCalls/lv2/sys_lwmutex.cpp @@ -12,6 +12,13 @@ SysCallBase sys_lwmutex("sys_lwmutex"); +void sys_lwmutex_attribute_initialize(vm::ptr attr) +{ + attr->protocol = SYS_SYNC_PRIORITY; + attr->recursive = SYS_SYNC_NOT_RECURSIVE; + attr->name[0] = '\0'; +} + void lwmutex_create(sys_lwmutex_t& lwmutex, bool recursive, u32 protocol, u64 name) { lwmutex.lock_var = { { lwmutex::free, lwmutex::zero } }; diff --git a/rpcs3/Emu/SysCalls/lv2/sys_lwmutex.h b/rpcs3/Emu/SysCalls/lv2/sys_lwmutex.h index 1870abc948..e07efe29c9 100644 --- a/rpcs3/Emu/SysCalls/lv2/sys_lwmutex.h +++ b/rpcs3/Emu/SysCalls/lv2/sys_lwmutex.h @@ -90,9 +90,7 @@ struct lv2_lwmutex_t REG_ID_TYPE(lv2_lwmutex_t, 0x95); // SYS_LWMUTEX_OBJECT // Aux -void lwmutex_create(sys_lwmutex_t& lwmutex, bool recursive, u32 protocol, u64 name); - -class PPUThread; +void sys_lwmutex_attribute_initialize(vm::ptr attr); // SysCalls s32 _sys_lwmutex_create(vm::ptr lwmutex_id, u32 protocol, vm::ptr control, u32 arg4, u64 name, u32 arg6); diff --git a/rpcs3/Emu/SysCalls/lv2/sys_semaphore.cpp b/rpcs3/Emu/SysCalls/lv2/sys_semaphore.cpp index 26528daa6b..eaf4210f33 100644 --- a/rpcs3/Emu/SysCalls/lv2/sys_semaphore.cpp +++ b/rpcs3/Emu/SysCalls/lv2/sys_semaphore.cpp @@ -12,6 +12,15 @@ SysCallBase sys_semaphore("sys_semaphore"); +void sys_semaphore_attribute_initialize(vm::ptr attr) +{ + attr->protocol = SYS_SYNC_PRIORITY; + attr->pshared = SYS_SYNC_NOT_PROCESS_SHARED; + attr->ipc_key = 0; + attr->flags = 0; + attr->name[0] = '\0'; +} + s32 sys_semaphore_create(vm::ptr sem, vm::ptr attr, s32 initial_val, s32 max_val) { sys_semaphore.Warning("sys_semaphore_create(sem=*0x%x, attr=*0x%x, initial_val=%d, max_val=%d)", sem, attr, initial_val, max_val); diff --git a/rpcs3/Emu/SysCalls/lv2/sys_semaphore.h b/rpcs3/Emu/SysCalls/lv2/sys_semaphore.h index 25a0071cc0..ccf9f6535b 100644 --- a/rpcs3/Emu/SysCalls/lv2/sys_semaphore.h +++ b/rpcs3/Emu/SysCalls/lv2/sys_semaphore.h @@ -41,6 +41,9 @@ struct lv2_sema_t REG_ID_TYPE(lv2_sema_t, 0x96); // SYS_SEMAPHORE_OBJECT +// Aux +void sys_semaphore_attribute_initialize(vm::ptr attr); + // SysCalls s32 sys_semaphore_create(vm::ptr sem, vm::ptr attr, s32 initial_val, s32 max_val); s32 sys_semaphore_destroy(u32 sem); diff --git a/rpcs3/Emu/SysCalls/lv2/sys_spu.cpp b/rpcs3/Emu/SysCalls/lv2/sys_spu.cpp index 68e9ca41c5..b366b8191c 100644 --- a/rpcs3/Emu/SysCalls/lv2/sys_spu.cpp +++ b/rpcs3/Emu/SysCalls/lv2/sys_spu.cpp @@ -465,6 +465,13 @@ s32 sys_spu_thread_group_yield(u32 id) return CELL_OK; } +void sys_spu_thread_group_attribute_initialize(vm::ptr attr) +{ + attr->name = vm::null; + attr->nsize = 0; + attr->type = SYS_SPU_THREAD_GROUP_TYPE_NORMAL; +} + s32 sys_spu_thread_group_terminate(u32 id, s32 value) { sys_spu.Warning("sys_spu_thread_group_terminate(id=0x%x, value=0x%x)", id, value); diff --git a/rpcs3/Emu/SysCalls/lv2/sys_spu.h b/rpcs3/Emu/SysCalls/lv2/sys_spu.h index d5e89258f3..07a4da499f 100644 --- a/rpcs3/Emu/SysCalls/lv2/sys_spu.h +++ b/rpcs3/Emu/SysCalls/lv2/sys_spu.h @@ -216,10 +216,12 @@ u32 LoadSpuImage(vfsStream& stream, u32& spu_ep); s32 spu_image_import(sys_spu_image& img, u32 src, u32 type); u32 spu_thread_group_create(const std::string& name, u32 num, s32 prio, s32 type, u32 container); u32 spu_thread_initialize(u32 group, u32 spu_num, vm::ptr img, const std::string& name, u32 option, u64 a1, u64 a2, u64 a3, u64 a4, std::function task = nullptr); +void sys_spu_thread_group_attribute_initialize(vm::ptr attr); // SysCalls s32 sys_spu_initialize(u32 max_usable_spu, u32 max_raw_spu); s32 sys_spu_image_open(vm::ptr img, vm::cptr path); +s32 sys_spu_image_close(vm::ptr img); s32 sys_spu_thread_initialize(vm::ptr thread, u32 group, u32 spu_num, vm::ptr img, vm::ptr attr, vm::ptr arg); s32 sys_spu_thread_set_argument(u32 id, vm::ptr arg); s32 sys_spu_thread_group_create(vm::ptr id, u32 num, s32 prio, vm::ptr attr);