From 17f34f512777cc56b923beb18316722d47227dea Mon Sep 17 00:00:00 2001 From: S Gopal Rajagopal Date: Wed, 21 Jan 2015 00:47:20 +0530 Subject: [PATCH] SPURS: Implement cellSpursSendSignal, cellSpursSendWorkloadSignal and some cellSpursEventFlag functions --- rpcs3/Emu/SysCalls/Modules/cellSpurs.cpp | 370 +++++++++++++++++++++-- rpcs3/Emu/SysCalls/Modules/cellSpurs.h | 59 +++- 2 files changed, 399 insertions(+), 30 deletions(-) diff --git a/rpcs3/Emu/SysCalls/Modules/cellSpurs.cpp b/rpcs3/Emu/SysCalls/Modules/cellSpurs.cpp index 10c2348d4b..a9a50e2bbe 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellSpurs.cpp +++ b/rpcs3/Emu/SysCalls/Modules/cellSpurs.cpp @@ -1460,13 +1460,63 @@ s64 cellSpursGetWorkloadFlag(vm::ptr spurs, vm::ptr spurs, u32 workloadId) { + cellSpurs->Warning("%s(spurs=0x%x, workloadId=0x%x)", __FUNCTION__, spurs.addr(), workloadId); + #ifdef PRX_DEBUG - cellSpurs->Warning("%s()", __FUNCTION__); return GetCurrentPPUThread().FastCall2(libsre + 0xA658, libsre_rtoc); #else - UNIMPLEMENTED_FUNC(cellSpurs); + if (!spurs) + { + return CELL_SPURS_POLICY_MODULE_ERROR_NULL_POINTER; + } + + if (spurs.addr() % CellSpurs::align) + { + return CELL_SPURS_POLICY_MODULE_ERROR_ALIGN; + } + + if (workloadId >= CELL_SPURS_MAX_WORKLOAD2 || (workloadId >= CELL_SPURS_MAX_WORKLOAD && (spurs->m.flags1 & SF1_32_WORKLOADS) == 0)) + { + return CELL_SPURS_POLICY_MODULE_ERROR_INVAL; + } + + if ((spurs->m.wklMskA.read_relaxed() & (0x80000000u >> workloadId)) == 0) + { + return CELL_SPURS_POLICY_MODULE_ERROR_SRCH; + } + + if (spurs->m.exception) + { + return CELL_SPURS_POLICY_MODULE_ERROR_STAT; + } + + u8 state; + if (workloadId >= CELL_SPURS_MAX_WORKLOAD) + { + state = spurs->m.wklState2[workloadId & 0x0F].read_relaxed(); + } + else + { + state = spurs->m.wklState1[workloadId].read_relaxed(); + } + + if (state != SPURS_WKL_STATE_RUNNABLE) + { + return CELL_SPURS_POLICY_MODULE_ERROR_STAT; + } + + // TODO: Make the below block execute atomically + if (workloadId >= CELL_SPURS_MAX_WORKLOAD) + { + spurs->m.wklSignal2 |= be_t::make(0x8000 >> (workloadId & 0x0F)); + } + else + { + spurs->m.wklSignal1 |= be_t::make(0x8000 >> workloadId); + } + return CELL_OK; #endif } @@ -1611,64 +1661,250 @@ s64 cellSpursUnsetExceptionEventHandler() s64 _cellSpursEventFlagInitialize(vm::ptr spurs, vm::ptr taskset, vm::ptr eventFlag, u32 flagClearMode, u32 flagDirection) { -#ifdef PRX_DEBUG 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); + +#ifdef PRX_DEBUG return GetCurrentPPUThread().FastCall2(libsre + 0x1564C, libsre_rtoc); #else - UNIMPLEMENTED_FUNC(cellSpurs); + if (taskset.addr() == 0 || spurs.addr() == 0) + { + return CELL_SPURS_TASK_ERROR_NULL_POINTER; + } + + if (eventFlag.addr() == 0) + { + return CELL_SPURS_TASK_ERROR_NULL_POINTER; + } + + if (spurs.addr() % CellSpurs::align || taskset.addr() % CellSpursTaskset::align || eventFlag.addr() % CellSpursEventFlag::align) + { + return CELL_SPURS_TASK_ERROR_ALIGN; + } + + if (taskset.addr() && taskset->m.wid >= CELL_SPURS_MAX_WORKLOAD2) + { + return CELL_SPURS_TASK_ERROR_INVAL; + } + + if (flagDirection > CELL_SPURS_EVENT_FLAG_LAST || flagClearMode > CELL_SPURS_EVENT_FLAG_CLEAR_LAST) + { + return CELL_SPURS_TASK_ERROR_INVAL; + } + + memset(eventFlag.get_ptr(), 0, CellSpursEventFlag::size); + eventFlag->m.direction = flagDirection; + eventFlag->m.clearMode = flagClearMode; + eventFlag->m.spuPort = -1; + + if (taskset.addr()) + { + eventFlag->m.addr = taskset.addr(); + } + else + { + eventFlag->m.isIwl = 1; + eventFlag->m.addr = spurs.addr(); + } + return CELL_OK; #endif } s64 cellSpursEventFlagAttachLv2EventQueue(vm::ptr eventFlag) { -#ifdef PRX_DEBUG cellSpurs->Warning("cellSpursEventFlagAttachLv2EventQueue(eventFlag_addr=0x%x)", eventFlag.addr()); + +#ifdef PRX_DEBUG return GetCurrentPPUThread().FastCall2(libsre + 0x157B8, libsre_rtoc); #else - UNIMPLEMENTED_FUNC(cellSpurs); + if (!eventFlag) + { + return CELL_SPURS_TASK_ERROR_NULL_POINTER; + } + + if (eventFlag.addr() % CellSpursEventFlag::align) + { + return CELL_SPURS_TASK_ERROR_AGAIN; + } + + if (eventFlag->m.direction != CELL_SPURS_EVENT_FLAG_SPU2PPU && eventFlag->m.direction != CELL_SPURS_EVENT_FLAG_ANY2ANY) + { + return CELL_SPURS_TASK_ERROR_PERM; + } + + if (eventFlag->m.spuPort != -1) + { + return CELL_SPURS_TASK_ERROR_STAT; + } + + vm::ptr spurs; + if (eventFlag->m.isIwl == 1) + { + spurs.set((u32)eventFlag->m.addr); + } + else + { + auto taskset = vm::ptr::make((u32)eventFlag->m.addr); + spurs.set((u32)taskset->m.spurs.addr()); + } + + u32 eventQueueId; + auto _port = vm::ptr::make((u32)Memory.Alloc(1, 1)); + auto rc = spursCreateLv2EventQueue(spurs, eventQueueId, _port, 1, *((u64 *)"_spuEvF")); + auto port = *_port; + Memory.Free(_port.addr()); + if (rc != CELL_OK) + { + return (rc & 0x0FFF0000) == 0x00410000 ? rc : (0x80410900 | (rc & 0xFF)); + } + + if (eventFlag->m.direction == CELL_SPURS_EVENT_FLAG_ANY2ANY) + { + auto _eventPortId = vm::ptr::make((u32)Memory.Alloc(4, 4)); + rc = sys_event_port_create(_eventPortId, SYS_EVENT_PORT_LOCAL, 0); + auto eventPortId = *_eventPortId; + Memory.Free(_eventPortId.addr()); + if (rc == CELL_OK) + { + rc = sys_event_port_connect_local(eventPortId, eventQueueId); + if (rc == CELL_OK) + { + eventFlag->m.eventPortId = eventPortId; + goto success; + } + + sys_event_port_destroy(eventPortId); + } + + // TODO: Implement the following + // if (spursDetachLv2EventQueue(spurs, port, 1) == CELL_OK) + // { + // sys_event_queue_destroy(eventQueueId, SYS_EVENT_QUEUE_DESTROY_FORCE); + // } + return (rc & 0x0FFF0000) == 0x00410000 ? rc : (0x80410900 | (rc & 0xFF)); + } + +success: + eventFlag->m.eventQueueId = eventQueueId; + eventFlag->m.spuPort = port; return CELL_OK; #endif } s64 cellSpursEventFlagDetachLv2EventQueue(vm::ptr eventFlag) { -#ifdef PRX_DEBUG cellSpurs->Warning("cellSpursEventFlagDetachLv2EventQueue(eventFlag_addr=0x%x)", eventFlag.addr()); + +#ifdef PRX_DEBUG return GetCurrentPPUThread().FastCall2(libsre + 0x15998, libsre_rtoc); #else - UNIMPLEMENTED_FUNC(cellSpurs); + if (!eventFlag) + { + return CELL_SPURS_TASK_ERROR_NULL_POINTER; + } + + if (eventFlag.addr() % CellSpursEventFlag::align) + { + return CELL_SPURS_TASK_ERROR_AGAIN; + } + + if (eventFlag->m.direction != CELL_SPURS_EVENT_FLAG_SPU2PPU && eventFlag->m.direction != CELL_SPURS_EVENT_FLAG_ANY2ANY) + { + return CELL_SPURS_TASK_ERROR_PERM; + } + + if (eventFlag->m.spuPort == -1) + { + return CELL_SPURS_TASK_ERROR_STAT; + } + + if (eventFlag->m.x04 || eventFlag->m.x06 & 0xFF00) + { + return CELL_SPURS_TASK_ERROR_BUSY; + } + + auto port = eventFlag->m.spuPort; + eventFlag->m.spuPort = -1; // TODO: Make this execute atomically + + vm::ptr spurs; + if (eventFlag->m.isIwl == 1) + { + spurs.set((u32)eventFlag->m.addr); + } + else + { + auto taskset = vm::ptr::make((u32)eventFlag->m.addr); + spurs.set((u32)taskset->m.spurs.addr()); + } + + if(eventFlag->m.direction == CELL_SPURS_EVENT_FLAG_ANY2ANY) + { + sys_event_port_disconnect(eventFlag->m.eventPortId); + sys_event_port_destroy(eventFlag->m.eventPortId); + } + + s64 rc = CELL_OK; + // TODO: Implement the following + // auto rc = spursDetachLv2EventQueue(spurs, port, 1); + // if (rc == CELL_OK) + // { + // rc = sys_event_queue_destroy(eventFlag->m.eventQueueId, SYS_EVENT_QUEUE_DESTROY_FORCE); + // } + + if (rc != CELL_OK) + { + return (rc & 0x0FFF0000) == 0x00410000 ? rc : (0x80410900 | (rc & 0xFF)); + } + return CELL_OK; #endif } -s64 cellSpursEventFlagWait(vm::ptr eventFlag, vm::ptr mask, u32 mode) +s64 _cellSpursEventFlagWait(vm::ptr eventFlag, vm::ptr mask, u32 mode, u32 block) { -#ifdef PRX_DEBUG - cellSpurs->Warning("cellSpursEventFlagWait(eventFlag_addr=0x%x, mask_addr=0x%x, mode=%d)", eventFlag.addr(), mask.addr(), mode); - return GetCurrentPPUThread().FastCall2(libsre + 0x15E68, libsre_rtoc); -#else UNIMPLEMENTED_FUNC(cellSpurs); return CELL_OK; +} + +s64 cellSpursEventFlagWait(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); + +#ifdef PRX_DEBUG + return GetCurrentPPUThread().FastCall2(libsre + 0x15E68, libsre_rtoc); +#else + return _cellSpursEventFlagWait(eventFlag, mask, mode, 1/*block*/); #endif } s64 cellSpursEventFlagClear(vm::ptr eventFlag, u16 bits) { -#ifdef PRX_DEBUG cellSpurs->Warning("cellSpursEventFlagClear(eventFlag_addr=0x%x, bits=0x%x)", eventFlag.addr(), bits); + +#ifdef PRX_DEBUG return GetCurrentPPUThread().FastCall2(libsre + 0x15E9C, libsre_rtoc); #else - UNIMPLEMENTED_FUNC(cellSpurs); + if (eventFlag.addr() == 0) + { + return CELL_SPURS_TASK_ERROR_NULL_POINTER; + } + + if (eventFlag.addr() % CellSpursEventFlag::align) + { + return CELL_SPURS_TASK_ERROR_ALIGN; + } + + eventFlag->m.bits &= ~bits; // TODO: Make this execute atomically return CELL_OK; #endif } s64 cellSpursEventFlagSet(vm::ptr eventFlag, u16 bits) { -#ifdef PRX_DEBUG cellSpurs->Warning("cellSpursEventFlagSet(eventFlag_addr=0x%x, bits=0x%x)", eventFlag.addr(), bits); + +#ifdef PRX_DEBUG return GetCurrentPPUThread().FastCall2(libsre + 0x15F04, libsre_rtoc); #else UNIMPLEMENTED_FUNC(cellSpurs); @@ -1678,44 +1914,77 @@ s64 cellSpursEventFlagSet(vm::ptr eventFlag, u16 bits) s64 cellSpursEventFlagTryWait(vm::ptr eventFlag, vm::ptr mask, u32 mode) { -#ifdef PRX_DEBUG cellSpurs->Warning("cellSpursEventFlagTryWait(eventFlag_addr=0x%x, mask_addr=0x%x, mode=0x%x)", eventFlag.addr(), mask.addr(), mode); + +#ifdef PRX_DEBUG return GetCurrentPPUThread().FastCall2(libsre + 0x15E70, libsre_rtoc); #else - UNIMPLEMENTED_FUNC(cellSpurs); - return CELL_OK; + return _cellSpursEventFlagWait(eventFlag, mask, mode, 0/*block*/); #endif } s64 cellSpursEventFlagGetDirection(vm::ptr eventFlag, vm::ptr direction) { -#ifdef PRX_DEBUG cellSpurs->Warning("cellSpursEventFlagGetDirection(eventFlag_addr=0x%x, direction_addr=0x%x)", eventFlag.addr(), direction.addr()); + +#ifdef PRX_DEBUG return GetCurrentPPUThread().FastCall2(libsre + 0x162C4, libsre_rtoc); #else - UNIMPLEMENTED_FUNC(cellSpurs); + if (eventFlag.addr() == 0 || direction.addr() == 0) + { + return CELL_SPURS_TASK_ERROR_NULL_POINTER; + } + + if (eventFlag.addr() % CellSpursEventFlag::align) + { + return CELL_SPURS_TASK_ERROR_ALIGN; + } + + *direction = eventFlag->m.direction; return CELL_OK; #endif } s64 cellSpursEventFlagGetClearMode(vm::ptr eventFlag, vm::ptr clear_mode) { -#ifdef PRX_DEBUG cellSpurs->Warning("cellSpursEventFlagGetClearMode(eventFlag_addr=0x%x, clear_mode_addr=0x%x)", eventFlag.addr(), clear_mode.addr()); + +#ifdef PRX_DEBUG return GetCurrentPPUThread().FastCall2(libsre + 0x16310, libsre_rtoc); #else - UNIMPLEMENTED_FUNC(cellSpurs); + if (eventFlag.addr() == 0 || clear_mode.addr() == 0) + { + return CELL_SPURS_TASK_ERROR_NULL_POINTER; + } + + if (eventFlag.addr() % CellSpursEventFlag::align) + { + return CELL_SPURS_TASK_ERROR_ALIGN; + } + + *clear_mode = eventFlag->m.clearMode; return CELL_OK; #endif } s64 cellSpursEventFlagGetTasksetAddress(vm::ptr eventFlag, vm::ptr taskset) { -#ifdef PRX_DEBUG cellSpurs->Warning("cellSpursEventFlagGetTasksetAddress(eventFlag_addr=0x%x, taskset_addr=0x%x)", eventFlag.addr(), taskset.addr()); + +#ifdef PRX_DEBUG return GetCurrentPPUThread().FastCall2(libsre + 0x1635C, libsre_rtoc); #else - UNIMPLEMENTED_FUNC(cellSpurs); + if (eventFlag.addr() == 0 || taskset.addr() == 0) + { + return CELL_SPURS_TASK_ERROR_NULL_POINTER; + } + + if (eventFlag.addr() % CellSpursEventFlag::align) + { + return CELL_SPURS_TASK_ERROR_ALIGN; + } + + taskset.set(eventFlag->m.isIwl ? 0 : eventFlag->m.addr); return CELL_OK; #endif } @@ -2302,7 +2571,54 @@ s64 _cellSpursSendSignal(vm::ptr taskset, u32 taskID) cellSpurs->Warning("_cellSpursSendSignal(taskset_addr=0x%x, taskID=%d)", taskset.addr(), taskID); return GetCurrentPPUThread().FastCall2(libsre + 0x124CC, libsre_rtoc); #else - UNIMPLEMENTED_FUNC(cellSpurs); + if (!taskset) + { + return CELL_SPURS_TASK_ERROR_NULL_POINTER; + } + + if (taskset.addr() % CellSpursTaskset::align) + { + return CELL_SPURS_TASK_ERROR_ALIGN; + } + + if (taskID >= CELL_SPURS_MAX_TASK || taskset->m.wid >= CELL_SPURS_MAX_WORKLOAD2) + { + return CELL_SPURS_TASK_ERROR_INVAL; + } + + auto word = taskID >> 5; + auto mask = 0x80000000u >> (taskID & 0x1F); + auto disabled = taskset->m.enabled_set[word] & mask ? false : true; + auto running = taskset->m.running_set[word]; + auto ready = taskset->m.ready_set[word]; + auto ready2 = taskset->m.ready2_set[word]; + auto waiting = taskset->m.waiting_set[word]; + auto signalled = taskset->m.signal_received_set[word]; + auto enabled = taskset->m.enabled_set[word]; + auto invalid = (ready & ready2) || (running & waiting) || ((running | ready | ready2 | waiting | signalled) & ~enabled) || disabled; + + if (invalid) + { + return CELL_SPURS_TASK_ERROR_SRCH; + } + + auto shouldSignal = waiting & ~signalled & mask ? true : false; + taskset->m.signal_received_set[word] |= mask; // TODO: Make this execute atomically + if (shouldSignal) + { + cellSpursSendWorkloadSignal(vm::ptr::make((u32)taskset->m.spurs.addr()), taskset->m.wid); + auto rc = cellSpursWakeUp(GetCurrentPPUThread(), vm::ptr::make((u32)taskset->m.spurs.addr())); + if (rc == CELL_SPURS_POLICY_MODULE_ERROR_STAT) + { + return CELL_SPURS_TASK_ERROR_STAT; + } + + if (rc != CELL_OK) + { + assert(0); + } + } + return CELL_OK; #endif } diff --git a/rpcs3/Emu/SysCalls/Modules/cellSpurs.h b/rpcs3/Emu/SysCalls/Modules/cellSpurs.h index dab014f898..32798e6006 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellSpurs.h +++ b/rpcs3/Emu/SysCalls/Modules/cellSpurs.h @@ -199,6 +199,28 @@ enum SpursTaskConstants CELL_SPURS_TASK_EXECUTION_CONTEXT_SIZE = 1024, }; +enum CellSpursEventFlagWaitMode +{ + CELL_SPURS_EVENT_FLAG_OR = 0, + CELL_SPURS_EVENT_FLAG_AND = 1 +}; + +enum CellSpursEventFlagClearMode +{ + CELL_SPURS_EVENT_FLAG_CLEAR_AUTO = 0, + CELL_SPURS_EVENT_FLAG_CLEAR_MANUAL = 1, + CELL_SPURS_EVENT_FLAG_CLEAR_LAST = CELL_SPURS_EVENT_FLAG_CLEAR_MANUAL, +}; + +enum CellSpursEventFlagDirection +{ + CELL_SPURS_EVENT_FLAG_SPU2SPU, + CELL_SPURS_EVENT_FLAG_SPU2PPU, + CELL_SPURS_EVENT_FLAG_PPU2SPU, + CELL_SPURS_EVENT_FLAG_ANY2ANY, + CELL_SPURS_EVENT_FLAG_LAST = CELL_SPURS_EVENT_FLAG_ANY2ANY, +}; + class SPURSManager; class SPURSManagerEventFlag; class SPURSManagerTaskset; @@ -519,7 +541,38 @@ struct CellSpursWorkloadAttribute struct CellSpursEventFlag { - SPURSManagerEventFlag *eventFlag; + static const u32 align = 128; + static const u32 size = 128; + + union + { + // Raw data + u8 _u8[size]; + + // Real data + struct _CellSpursEventFlag + { + be_t bits; // 0x00 + be_t x02; // 0x02 + be_t x04; // 0x04 + be_t x06; // 0x06 + be_t x08; // 0x08 + be_t x0A; // 0x0A + u8 spuPort; // 0x0C + u8 isIwl; // 0x0D + u8 direction; // 0x0E + u8 clearMode; // 0x0F + u16 x10[16]; // 0x10 + u8 x30[0x70 - 0x30]; // 0x30 + be_t addr; // 0x70 + be_t eventPortId; // 0x78 + be_t eventQueueId; // 0x7C + } m; + + static_assert(sizeof(_CellSpursEventFlag) == size, "Wrong _CellSpursEventFlag size"); + + SPURSManagerEventFlag *eventFlag; + }; }; union CellSpursTaskArgument @@ -559,7 +612,7 @@ struct CellSpursTaskset { be_t running_set[4]; // 0x00 be_t ready_set[4]; // 0x10 - be_t unk_set[4]; // 0x20 - TODO: Find out what this is + be_t ready2_set[4]; // 0x20 - TODO: Find out what this is be_t enabled_set[4]; // 0x30 be_t signal_received_set[4]; // 0x40 be_t waiting_set[4]; // 0x50 @@ -668,7 +721,7 @@ struct CellSpursTaskset2 { be_t running_set[4]; // 0x00 be_t ready_set[4]; // 0x10 - be_t unk_set[4]; // 0x20 - TODO: Find out what this is + be_t ready2_set[4]; // 0x20 - TODO: Find out what this is be_t enabled_set[4]; // 0x30 be_t signal_received_set[4]; // 0x40 be_t waiting_set[4]; // 0x50