#pragma once #include "cellSync.h" #include "util/v128.hpp" struct CellSpurs; struct CellSpursTaskset; // Core return codes. enum CellSpursCoreError : u32 { CELL_SPURS_CORE_ERROR_AGAIN = 0x80410701, CELL_SPURS_CORE_ERROR_INVAL = 0x80410702, CELL_SPURS_CORE_ERROR_NOMEM = 0x80410704, CELL_SPURS_CORE_ERROR_SRCH = 0x80410705, CELL_SPURS_CORE_ERROR_PERM = 0x80410709, CELL_SPURS_CORE_ERROR_BUSY = 0x8041070A, CELL_SPURS_CORE_ERROR_STAT = 0x8041070F, CELL_SPURS_CORE_ERROR_ALIGN = 0x80410710, CELL_SPURS_CORE_ERROR_NULL_POINTER = 0x80410711, }; // enum CellSpursPolicyModuleError : u32 { CELL_SPURS_POLICY_MODULE_ERROR_AGAIN = 0x80410801, CELL_SPURS_POLICY_MODULE_ERROR_INVAL = 0x80410802, CELL_SPURS_POLICY_MODULE_ERROR_NOSYS = 0x80410803, CELL_SPURS_POLICY_MODULE_ERROR_NOMEM = 0x80410804, CELL_SPURS_POLICY_MODULE_ERROR_SRCH = 0x80410805, CELL_SPURS_POLICY_MODULE_ERROR_NOENT = 0x80410806, CELL_SPURS_POLICY_MODULE_ERROR_NOEXEC = 0x80410807, CELL_SPURS_POLICY_MODULE_ERROR_DEADLK = 0x80410808, CELL_SPURS_POLICY_MODULE_ERROR_PERM = 0x80410809, CELL_SPURS_POLICY_MODULE_ERROR_BUSY = 0x8041080A, CELL_SPURS_POLICY_MODULE_ERROR_ABORT = 0x8041080C, CELL_SPURS_POLICY_MODULE_ERROR_FAULT = 0x8041080D, CELL_SPURS_POLICY_MODULE_ERROR_CHILD = 0x8041080E, CELL_SPURS_POLICY_MODULE_ERROR_STAT = 0x8041080F, CELL_SPURS_POLICY_MODULE_ERROR_ALIGN = 0x80410810, CELL_SPURS_POLICY_MODULE_ERROR_NULL_POINTER = 0x80410811, }; // Task return codes. enum CellSpursTaskError : u32 { CELL_SPURS_TASK_ERROR_AGAIN = 0x80410901, CELL_SPURS_TASK_ERROR_INVAL = 0x80410902, CELL_SPURS_TASK_ERROR_NOSYS = 0x80410903, CELL_SPURS_TASK_ERROR_NOMEM = 0x80410904, CELL_SPURS_TASK_ERROR_SRCH = 0x80410905, CELL_SPURS_TASK_ERROR_NOEXEC = 0x80410907, CELL_SPURS_TASK_ERROR_PERM = 0x80410909, CELL_SPURS_TASK_ERROR_BUSY = 0x8041090A, CELL_SPURS_TASK_ERROR_FAULT = 0x8041090D, CELL_SPURS_TASK_ERROR_ALIGN = 0x80410910, CELL_SPURS_TASK_ERROR_STAT = 0x8041090F, CELL_SPURS_TASK_ERROR_NULL_POINTER = 0x80410911, CELL_SPURS_TASK_ERROR_FATAL = 0x80410914, CELL_SPURS_TASK_ERROR_SHUTDOWN = 0x80410920, }; enum CellSpursJobError : u32 { CELL_SPURS_JOB_ERROR_AGAIN = 0x80410A01, CELL_SPURS_JOB_ERROR_INVAL = 0x80410A02, CELL_SPURS_JOB_ERROR_NOSYS = 0x80410A03, CELL_SPURS_JOB_ERROR_NOMEM = 0x80410A04, CELL_SPURS_JOB_ERROR_SRCH = 0x80410A05, CELL_SPURS_JOB_ERROR_NOENT = 0x80410A06, CELL_SPURS_JOB_ERROR_NOEXEC = 0x80410A07, CELL_SPURS_JOB_ERROR_DEADLK = 0x80410A08, CELL_SPURS_JOB_ERROR_PERM = 0x80410A09, CELL_SPURS_JOB_ERROR_BUSY = 0x80410A0A, CELL_SPURS_JOB_ERROR_JOB_DESCRIPTOR = 0x80410A0B, CELL_SPURS_JOB_ERROR_JOB_DESCRIPTOR_SIZE = 0x80410A0C, CELL_SPURS_JOB_ERROR_FAULT = 0x80410A0D, CELL_SPURS_JOB_ERROR_CHILD = 0x80410A0E, CELL_SPURS_JOB_ERROR_STAT = 0x80410A0F, CELL_SPURS_JOB_ERROR_ALIGN = 0x80410A10, CELL_SPURS_JOB_ERROR_NULL_POINTER = 0x80410A11, CELL_SPURS_JOB_ERROR_MEMORY_CORRUPTED = 0x80410A12, CELL_SPURS_JOB_ERROR_MEMORY_SIZE = 0x80410A17, CELL_SPURS_JOB_ERROR_UNKNOWN_COMMAND = 0x80410A18, CELL_SPURS_JOB_ERROR_JOBLIST_ALIGNMENT = 0x80410A19, CELL_SPURS_JOB_ERROR_JOB_ALIGNMENT = 0x80410A1a, CELL_SPURS_JOB_ERROR_CALL_OVERFLOW = 0x80410A1b, CELL_SPURS_JOB_ERROR_ABORT = 0x80410A1c, CELL_SPURS_JOB_ERROR_DMALIST_ELEMENT = 0x80410A1d, CELL_SPURS_JOB_ERROR_NUM_CACHE = 0x80410A1e, CELL_SPURS_JOB_ERROR_INVALID_BINARY = 0x80410A1f, }; // SPURS defines. enum SPURSKernelInterfaces : u32 { 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_DYNAMIC_PORT_RANGE_BOTTOM = 63, }; enum SpursAttrFlags : u32 { 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, SAF_SYSTEM_WORKLOAD_ENABLED = 0x02000000, SAF_SPU_PRINTF_ENABLED = 0x10000000, SAF_SPU_TGT_EXCLUSIVE_NON_CONTEXT = 0x20000000, SAF_SPU_MEMORY_CONTAINER_SET = 0x40000000, SAF_UNKNOWN_FLAG_0 = 0x80000000, }; enum SpursFlags1 : u8 { SF1_NONE = 0x00, SF1_32_WORKLOADS = 0x40, SF1_EXIT_IF_NO_WORK = 0x80, }; enum SpursWorkloadState : u8 { // Workload states SPURS_WKL_STATE_NON_EXISTENT = 0, SPURS_WKL_STATE_PREPARING = 1, SPURS_WKL_STATE_RUNNABLE = 2, SPURS_WKL_STATE_SHUTTING_DOWN = 3, SPURS_WKL_STATE_REMOVABLE = 4, SPURS_WKL_STATE_INVALID = 5, }; enum SpursImgAddrConstants : u32 { // Image addresses SPURS_IMG_ADDR_SYS_SRV_WORKLOAD = 0x100, SPURS_IMG_ADDR_TASKSET_PM = 0x200, }; enum SpursWorkloadGUIDs : u64 { // GUID SPURS_GUID_SYS_WKL = 0x1BB841BF38F89D33ull, SPURS_GUID_TASKSET_PM = 0x836E915B2E654143ull, }; enum CellSpursModulePollStatus { CELL_SPURS_MODULE_POLL_STATUS_READYCOUNT = 1, CELL_SPURS_MODULE_POLL_STATUS_SIGNAL = 2, CELL_SPURS_MODULE_POLL_STATUS_FLAG = 4 }; enum SpursTraceConstants { // Trace tag types CELL_SPURS_TRACE_TAG_KERNEL = 0x20, CELL_SPURS_TRACE_TAG_SERVICE = 0x21, CELL_SPURS_TRACE_TAG_TASK = 0x22, CELL_SPURS_TRACE_TAG_JOB = 0x23, CELL_SPURS_TRACE_TAG_OVIS = 0x24, CELL_SPURS_TRACE_TAG_LOAD = 0x2a, CELL_SPURS_TRACE_TAG_MAP = 0x2b, CELL_SPURS_TRACE_TAG_START = 0x2c, CELL_SPURS_TRACE_TAG_STOP = 0x2d, CELL_SPURS_TRACE_TAG_USER = 0x2e, CELL_SPURS_TRACE_TAG_GUID = 0x2f, // Service incident CELL_SPURS_TRACE_SERVICE_INIT = 0x01, CELL_SPURS_TRACE_SERVICE_WAIT = 0x02, CELL_SPURS_TRACE_SERVICE_EXIT = 0x03, // Task incident CELL_SPURS_TRACE_TASK_DISPATCH = 0x01, CELL_SPURS_TRACE_TASK_YIELD = 0x03, CELL_SPURS_TRACE_TASK_WAIT = 0x04, CELL_SPURS_TRACE_TASK_EXIT = 0x05, // Trace mode flags CELL_SPURS_TRACE_MODE_FLAG_WRAP_BUFFER = 0x1, CELL_SPURS_TRACE_MODE_FLAG_SYNCHRONOUS_START_STOP = 0x2, CELL_SPURS_TRACE_MODE_FLAG_MASK = 0x3, }; // SPURS task constants enum SpursTaskConstants { CELL_SPURS_MAX_TASK = 128, CELL_SPURS_TASK_TOP = 0x3000, CELL_SPURS_TASK_BOTTOM = 0x40000, CELL_SPURS_MAX_TASK_NAME_LENGTH = 32, CELL_SPURS_TASK_ATTRIBUTE_REVISION = 1, CELL_SPURS_TASKSET_ATTRIBUTE_REVISION = 1, CELL_SPURS_TASK_EXECUTION_CONTEXT_SIZE = 1024, CELL_SPURS_TASKSET_PM_ENTRY_ADDR = 0xA00, CELL_SPURS_TASKSET_PM_SYSCALL_ADDR = 0xA70, // Task syscall numbers CELL_SPURS_TASK_SYSCALL_EXIT = 0, CELL_SPURS_TASK_SYSCALL_YIELD = 1, CELL_SPURS_TASK_SYSCALL_WAIT_SIGNAL = 2, CELL_SPURS_TASK_SYSCALL_POLL = 3, CELL_SPURS_TASK_SYSCALL_RECV_WKL_FLAG = 4, // Task poll status CELL_SPURS_TASK_POLL_FOUND_TASK = 1, CELL_SPURS_TASK_POLL_FOUND_WORKLOAD = 2, }; enum CellSpursEventFlagWaitMode { CELL_SPURS_EVENT_FLAG_OR = 0, CELL_SPURS_EVENT_FLAG_AND = 1, CELL_SPURS_EVENT_FLAG_WAIT_MODE_LAST = CELL_SPURS_EVENT_FLAG_AND, }; 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, }; enum CellSpursJobOpcode : u64 { CELL_SPURS_JOB_OPCODE_NOP = 0, CELL_SPURS_JOB_OPCODE_RESET_PC = 1, CELL_SPURS_JOB_OPCODE_SYNC = 2 | (0 << 3), CELL_SPURS_JOB_OPCODE_LWSYNC = 2 | (2 << 3), CELL_SPURS_JOB_OPCODE_SYNC_LABEL = 2 | (1 << 3), CELL_SPURS_JOB_OPCODE_LWSYNC_LABEL = 2 | (3 << 3), CELL_SPURS_JOB_OPCODE_NEXT = 3, CELL_SPURS_JOB_OPCODE_CALL = 4, CELL_SPURS_JOB_OPCODE_FLUSH = 5, CELL_SPURS_JOB_OPCODE_JOBLIST = 6, CELL_SPURS_JOB_OPCODE_ABORT = 7 | (0 << 3), CELL_SPURS_JOB_OPCODE_GUARD = 7 | (1 << 3), CELL_SPURS_JOB_OPCODE_SET_LABEL = 7 | (2 << 3), CELL_SPURS_JOB_OPCODE_RET = 7 | (14 << 3), CELL_SPURS_JOB_OPCODE_END = 7 | (15 << 3), CELL_SPURS_JOB_OPCODE_JTS = 0x800000000ull | CELL_SPURS_JOB_OPCODE_LWSYNC, }; enum CellSpursJobChainRevision : u32 { CELL_SPURS_JOB_REVISION_0 = 0, CELL_SPURS_JOB_REVISION_1 = 1, CELL_SPURS_JOB_REVISION_2 = 2, CELL_SPURS_JOB_REVISION_3 = 3, }; // Event flag constants enum SpursEventFlagConstants { CELL_SPURS_EVENT_FLAG_MAX_WAIT_SLOTS = 16, CELL_SPURS_EVENT_FLAG_INVALID_SPU_PORT = 0xFF, }; struct alignas(16) CellSpursWorkloadFlag { be_t unused0; be_t unused1; atomic_be_t flag; }; CHECK_SIZE_ALIGN(CellSpursWorkloadFlag, 16, 16); struct CellSpursInfo { be_t nSpus; // 0x00 be_t spuThreadGroupPriority; // 0x04 be_t ppuThreadPriority; // 0x08 b8 exitIfNoWork; // 0x0C b8 spurs2; // 0x0D u8 padding24[2]; // 0x0E vm::bptr traceBuffer; // 0x10 be_t padding32; // 0x14 be_t traceBufferSize; // 0x18 be_t traceMode; // 0x20 be_t spuThreadGroup; // 0x24 be_t spuThreads[8]; // 0x28 be_t spursHandlerThread0; // 0x48 be_t spursHandlerThread1; // 0x50 char namePrefix[16]; // 0x58 be_t namePrefixLength; // 0x68 be_t deadlineMissCounter; // 0x6C be_t deadlineMeetCounter; // 0x70 u8 padding[164]; // 0x74 }; CHECK_SIZE(CellSpursInfo, 280); struct alignas(8) CellSpursAttribute { be_t revision; // 0x0 be_t sdkVersion; // 0x4 be_t nSpus; // 0x8 be_t spuPriority; // 0xC be_t ppuPriority; // 0x10 b8 exitIfNoWork; // 0x14 char prefix[15]; // 0x15 (not a NTS) be_t prefixSize; // 0x24 be_t flags; // 0x28 (SpursAttrFlags) be_t container; // 0x2C be_t unk0; // 0x30 be_t unk1; // 0x34 u8 swlPriority[8]; // 0x38 be_t swlMaxSpu; // 0x40 be_t swlIsPreem; // 0x44 u8 padding[440]; }; CHECK_SIZE_ALIGN(CellSpursAttribute, 512, 8); using CellSpursShutdownCompletionEventHook = void(vm::ptr spurs, u32 wid, vm::ptr arg); struct alignas(16) CellSpursTraceInfo { be_t spuThread[8]; // 0x00 be_t count[8]; // 0x20 be_t spuThreadGroup; // 0x40 be_t numSpus; // 0x44 u8 padding[56]; }; CHECK_SIZE_ALIGN(CellSpursTraceInfo, 128, 16); struct CellSpursTraceHeader { u8 tag; u8 length; u8 spu; u8 workload; be_t time; }; struct CellSpursTraceControlData { be_t incident; be_t reserved; }; struct CellSpursTraceServiceData { be_t incident; be_t reserved; }; struct CellSpursTraceTaskData { be_t incident; be_t taskId; }; struct CellSpursTraceJobData { u8 reserved[3]; u8 binLSAhigh8; be_t jobDescriptor; }; struct CellSpursTraceLoadData { be_t ea; be_t ls; be_t size; }; struct CellSpursTraceMapData { be_t offset; be_t ls; be_t size; }; struct CellSpursTraceStartData { char _module[4]; be_t level; be_t ls; }; struct alignas(16) CellSpursTracePacket { CellSpursTraceHeader header; union { CellSpursTraceControlData control; CellSpursTraceServiceData service; CellSpursTraceTaskData task; CellSpursTraceJobData job; CellSpursTraceLoadData load; CellSpursTraceMapData map; CellSpursTraceStartData start; be_t stop; be_t user; be_t guid; be_t raw; } data; }; CHECK_SIZE_ALIGN(CellSpursTracePacket, 16, 16); struct CellSpursJobChain; struct CellSpursExceptionInfo; using CellSpursJobChainExceptionEventHandler = void(vm::ptr spurs, vm::ptr jobChain, vm::cptr info , vm::cptr eaJobBinary, u32 lsAddrJobBinary, vm::ptr arg); struct alignas(128) CellSpursJobChain { vm::bcptr pc; // 0x00 vm::bcptr linkRegister[3]; // 0x08 u8 unk0[0x3]; // 0x20 b8 isHalted; // 0x23 b8 autoReadyCount; // 0x24 u8 unk1[0x3]; // 0x25 u8 initSpuCount; // 0x28 u8 unk5; // 0x29 u8 tag1; // 0x2A u8 tag2; // 0x2B u8 val2C; // 0x2C u8 jmVer; // 0x2D u8 val2E; // 0x2E u8 val2F; // 0x2F atomic_be_t urgentCmds[4]; // 0x30 u8 unk2[0x20]; // 0x50 atomic_be_t maxGrabbedJob; // 0x70 be_t sizeJobDescriptor; // 0x72 be_t workloadId; // 0x74 vm::bptr spurs; // 0x78 be_t error; // 0x80 be_t unk3; // 0x84 vm::bptr cause; // 0x88 be_t sdkVer; // 0x90 u8 jobMemoryCheck; // 0x94 (unlike Attribute::jobMmeoryCheck it is not a boolean but a bitset it seems) u8 unk4[0x3]; // 0x95 vm::bptr exceptionEventHandler; // 0x98 vm::bptr exceptionEventHandlerArgument; // 0xA0 u8 unk6[0x100 - 0xA8]; }; struct alignas(128) CellSpursJobChain_x00 { vm::bcptr pc; // 0x00 vm::bcptr linkRegister[3]; // 0x08 u8 unk0[0x3]; // 0x20 b8 isHalted; // 0x23 b8 autoReadyCount; // 0x24 u8 unk1[0x3]; // 0x25 u8 initSpuCount; // 0x28 u8 unk5; // 0x29 u8 tag1; // 0x2A u8 tag2; // 0x2B u8 val2C; // 0x2C u8 jmVer; // 0x2D u8 val2E; // 0x2E u8 val2F; // 0x2F be_t urgentCmds[4]; // 0x30 u8 unk2[0x20]; // 0x50 be_t maxGrabbedJob; // 0x70 be_t sizeJobDescriptor; // 0x72 be_t workloadId; // 0x74 vm::bptr spurs; // 0x78 }; struct CellSpursJobChainInfo { be_t urgentCommandSlot[4]; // 0x00 vm::bcptr programCounter; // 0x20 vm::bcptr linkRegister[3]; // 0x24 vm::bcptr cause; // 0x30 be_t statusCode; // 0x34 be_t maxSizeJobDescriptor; // 0x38 be_t idWorkload; // 0x3C b8 autoReadyCount; // 0x40 b8 isHalted; // 0x41 b8 isFixedMemAlloc; // 0x42 u8 padding8; // 0x43 u16 maxGrabbedJob; // 0x44 u16 padding16; // 0x46 vm::bcptr name; // 0x48 vm::bptr exceptionEventHandler; // 0x4C vm::bptr exceptionEventHandlerArgument; // 0x50 }; struct alignas(8) CellSpursJobChainAttribute { be_t jmVer; // 0x00 be_t sdkVer; // 0x04 vm::bcptr jobChainEntry; // 0x08 be_t sizeJobDescriptor; // 0x0C be_t maxGrabbedJob; // 0x0E u8 priorities[8]; // 0x10 be_t maxContention; // 0x18 b8 autoSpuCount; // 0x1C u8 padding[3]; // 0x1D be_t tag1; // 0x20 be_t tag2; // 0x24 b8 isFixedMemAlloc; // 0x28 u8 padding1[3]; // 0x29 be_t maxSizeJobDescriptor; // 0x2C be_t initSpuCount; // 0x30 be_t haltOnError; // 0x34 vm::bcptr name; // 0x38 b8 jobMemoryCheck; // 0x3C }; struct CellSpursWorkloadInfo { be_t data; // 0x00 u8 priority[8]; // 0x08 vm::bcptr policyModule; // 0x10 be_t sizePolicyModule; // 0x14 vm::bcptr nameClass; // 0x18 vm::bcptr nameInstance; // 0x1C u8 contention; // 0x20 u8 minContention; // 0x21 u8 maxContention; // 0x22 u8 readyCount; // 0x23 u8 idleSpuRequest; // 0x24 u8 hasSignal; // 0x25 u8 padding[2]; // 0x26 vm::bptr shutdownCompletionEventHook; // 0x28 vm::bptr shutdownCompletionEventHookArgument; // 0x2C }; struct alignas(128) CellSpursJobGuard { atomic_be_t ncount0; // 0x00 be_t ncount1; // 0x04 vm::bptr jobChain; // 0x0C be_t unk0; be_t requestSpuCount; // 0x10 be_t unk1[3]; be_t autoReset; // 0x20 be_t unk2[3]; be_t zero; // 0x30 u8 unk3[0x80 - 0x34]; }; CHECK_SIZE_ALIGN(CellSpursJobGuard, 128, 128); struct alignas(128) CellSpursJobGuard_x00 { be_t ncount0; // 0x00 be_t ncount1; // 0x04 vm::bptr jobChain; // 0x0C be_t unk0; be_t requestSpuCount; // 0x10 be_t unk1[3]; be_t autoReset; // 0x20 be_t unk2[3]; be_t zero; // 0x30 u8 unk3[0x80 - 0x34]; }; CHECK_SIZE_ALIGN(CellSpursJobGuard_x00, 128, 128); // Core CellSpurs structures struct alignas(128) CellSpurs { struct _sub_str1 { u8 unk0[0x20]; // 0x00 - SPU exception handler 0x08 - SPU exception handler args be_t sem; // 0x20 atomic_be_t x28; // 0x28 be_t x2C; // 0x2C vm::bptr hook; // 0x30 vm::bptr hookArg; // 0x38 u8 unk2[0x40]; }; CHECK_SIZE(_sub_str1, 128); struct EventPortMux; using EventHandlerCallback = void(vm::ptr, u64 data); struct EventHandlerListNode { vm::bptr next; be_t data; vm::bptr handler; }; struct EventPortMux { atomic_be_t reqPending; // 0x00 be_t spuPort; // 0x04 be_t x08; // 0x08 be_t x0C; // 0x0C be_t eventPort; // 0x10 atomic_t> handlerList; // 0x18 u8 x20[0x80 - 0x20]; // 0x20 }; CHECK_SIZE(EventPortMux, 128); struct WorkloadInfo { vm::bcptr addr; // 0x00 Address of the executable be_t arg; // 0x08 Argument be_t size; // 0x10 Size of the executable atomic_t uniqueId; // 0x14 Unique id of the workload. It is the same for all workloads with the same addr. u8 pad[3]; union { atomic_t prio64; u8 priority[8]; // 0x18 Priority of the workload on each SPU }; }; CHECK_SIZE(WorkloadInfo, 32); struct _sub_str4 { vm::bcptr nameClass; vm::bcptr nameInstance; }; atomic_t wklReadyCount1[0x10]; // 0x00 Number of SPUs requested by each workload (0..15 wids). atomic_t wklIdleSpuCountOrReadyCount2[0x10]; // 0x10 SPURS1: Number of idle SPUs requested by each workload (0..15 wids). SPURS2: Number of SPUs requested by each workload (16..31 wids). u8 wklCurrentContention[0x10]; // 0x20 Number of SPUs used by each workload. SPURS1: index = wid. SPURS2: packed 4-bit data, index = wid % 16, internal index = wid / 16. u8 wklPendingContention[0x10]; // 0x30 Number of SPUs that are pending to context switch to the workload. SPURS1: index = wid. SPURS2: packed 4-bit data, index = wid % 16, internal index = wid / 16. u8 wklMinContention[0x10]; // 0x40 Min SPUs required for each workload. SPURS1: index = wid. SPURS2: Unused. atomic_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_t sysSrvMessage; // 0x72 u8 spuIdling; // 0x73 u8 flags1; // 0x74 Type is SpursFlags1 u8 sysSrvTraceControl; // 0x75 u8 nSpus; // 0x76 atomic_t wklFlagReceiver; // 0x77 atomic_be_t wklSignal2; // 0x78 Bitset for 16..32 wids u8 x7A[6]; // 0x7A atomic_t wklState1[0x10]; // 0x80 u8 wklStatus1[0x10]; // 0x90 atomic_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_t sysSrvMsgUpdateWorkload; // 0xBD u8 xBE; // 0xBE u8 sysSrvMsgTerminate; // 0xBF 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 u8 xCB; // 0xCB struct SrvTraceSyncVar { u8 sysSrvTraceInitialised; // 0xCC u8 sysSrvNotifyUpdateTraceComplete; // 0xCD u8 sysSrvMsgUpdateTrace; // 0xCE u8 xCF; }; atomic_t sysSrvTrace; // 0xCC atomic_t wklState2[0x10]; // 0xD0 u8 wklStatus2[0x10]; // 0xE0 atomic_t wklEvent2[0x10]; // 0xF0 _sub_str1 wklF1[0x10]; // 0x100 vm::bptr traceBuffer; // 0x900 be_t traceStartIndex[6]; // 0x908 u8 unknown7[0x948 - 0x920]; // 0x920 be_t traceDataSize; // 0x948 be_t traceMode; // 0x950 u8 unknown8[0x980 - 0x954]; // 0x954 be_t semPrv; // 0x980 be_t unk11; // 0x988 be_t unk12; // 0x98C be_t unk13; // 0x990 u8 unknown4[0xB00 - 0x998]; WorkloadInfo wklInfo1[0x10]; // 0xB00 WorkloadInfo wklInfoSysSrv; // 0xD00 be_t ppu0; // 0xD20 Handler thread be_t ppu1; // 0xD28 be_t spuTG; // 0xD30 SPU thread group be_t spus[8]; // 0xD34 u8 unknown3[0xD5C - 0xD54]; be_t eventQueue; // 0xD5C be_t eventPort; // 0xD60 atomic_t handlerDirty; // 0xD64 atomic_t handlerWaiting; // 0xD65 atomic_t handlerExiting; // 0xD66 u8 xD67; // 0xD67 atomic_be_t enableEH; // 0xD68 be_t exception; // 0xD6C sys_spu_image spuImg; // 0xD70 be_t flags; // 0xD80 be_t spuPriority; // 0xD84 be_t ppuPriority; // 0xD88 char prefix[0x0f]; // 0xD8C u8 prefixSize; // 0xD9B be_t unk5; // 0xD9C be_t revision; // 0xDA0 be_t sdkVersion; // 0xDA4 atomic_be_t spuPortBits; // 0xDA8 sys_lwmutex_t mutex; // 0xDB0 sys_lwcond_t cond; // 0xDC8 u8 unknown9[0xE00 - 0xDD0]; _sub_str4 wklH1[0x10]; // 0xE00 EventPortMux eventPortMux; // 0xF00 atomic_be_t globalSpuExceptionHandler; // 0xF80 be_t globalSpuExceptionHandlerArgs; // 0xF88 u8 unknown6[0x1000 - 0xF90]; WorkloadInfo wklInfo2[0x10]; // 0x1000 _sub_str1 wklF2[0x10]; // 0x1200 _sub_str4 wklH2[0x10]; // 0x1A00 u8 unknown_[0x2000 - 0x1B00]; u32 max_workloads() const { return (flags1 & SF1_32_WORKLOADS ? CELL_SPURS_MAX_WORKLOAD2 : CELL_SPURS_MAX_WORKLOAD); } atomic_t& wklState(u32 wid) { if (wid & 0x10) { return wklState2[wid & 0xf]; } else { return wklState1[wid & 0xf]; } } u8 wklStatus(u32 wid) { if (wid & 0x10) { return atomic_storage::load(wklStatus2[wid & 0xf]); } else { return atomic_storage::load(wklStatus1[wid & 0xf]); } } atomic_t& wklEvent(u32 wid) { if (wid & 0x10) { return wklEvent2[wid & 0xf]; } else { return wklEvent1[wid & 0xf]; } } atomic_t& readyCount(u32 wid) { if (wid & 0x10) { return wklReadyCount1[wid & 0xf]; } else { return wklIdleSpuCountOrReadyCount2[wid & 0xf]; } } _sub_str1& wklSyncInfo(u32 wid) { if (wid & 0x10) { return wklF2[wid & 0xf]; } else { return wklF1[wid & 0xf]; } } _sub_str4& wklName(u32 wid) { if (wid & 0x10) { return wklH2[wid & 0xf]; } else { return wklH1[wid & 0xf]; } } WorkloadInfo& wklInfo(u32 wid) { if (wid & 0x10) { return wklInfo2[wid & 0xf]; } else { return wklInfo1[wid & 0xf]; } } }; CHECK_SIZE_ALIGN(CellSpurs, 0x2000, 128); using CellSpurs2 = CellSpurs; struct CellSpursExceptionInfo { be_t spu_thread; be_t spu_npc; be_t cause; be_t option; }; // Exception handler using CellSpursGlobalExceptionEventHandler = void(vm::ptr spurs, vm::cptr info, u32 id, vm::ptr arg); struct CellSpursWorkloadAttribute { be_t revision; be_t sdkVersion; vm::bcptr pm; be_t size; be_t data; u8 priority[8]; be_t minContention; be_t maxContention; vm::bcptr nameClass; vm::bcptr nameInstance; vm::bptr hook; vm::bptr hookArg; u8 padding[456]; }; CHECK_SIZE_ALIGN(CellSpursWorkloadAttribute, 512, 8); struct alignas(128) CellSpursEventFlag { struct ControlSyncVar { be_t events; // 0x00 Event bits be_t spuTaskPendingRecv; // 0x02 A bit is set to 1 when the condition of the SPU task using the slot are met and back to 0 when the SPU task unblocks be_t ppuWaitMask; // 0x04 Wait mask for blocked PPU thread u8 ppuWaitSlotAndMode; // 0x06 Top 4 bits: Wait slot number of the blocked PPU threads, Bottom 4 bits: Wait mode of the blocked PPU thread u8 ppuPendingRecv; // 0x07 Set to 1 when the blocked PPU thread's conditions are met and back to 0 when the PPU thread is unblocked }; union { atomic_t ctrl; // 0x00 atomic_be_t events; // 0x00 }; be_t spuTaskUsedWaitSlots; // 0x08 A bit is set to 1 if the wait slot corresponding to the bit is used by an SPU task and 0 otherwise be_t spuTaskWaitMode; // 0x0A A bit is set to 1 if the wait mode for the SPU task corresponding to the bit is AND and 0 otherwise u8 spuPort; // 0x0C u8 isIwl; // 0x0D u8 direction; // 0x0E u8 clearMode; // 0x0F be_t spuTaskWaitMask[16]; // 0x10 Wait mask for blocked SPU tasks be_t pendingRecvTaskEvents[16]; // 0x30 The value of event flag when the wait condition for the thread/task was met u8 waitingTaskId[16]; // 0x50 Task id of waiting SPU threads u8 waitingTaskWklId[16]; // 0x60 Workload id of waiting SPU threads be_t addr; // 0x70 be_t eventPortId; // 0x78 be_t eventQueueId; // 0x7C }; CHECK_SIZE_ALIGN(CellSpursEventFlag, 128, 128); struct alignas(128) CellSpursEventFlag_x00 { struct alignas(8) ControlSyncVar { be_t events; // 0x00 Event bits be_t spuTaskPendingRecv; // 0x02 A bit is set to 1 when the condition of the SPU task using the slot are met and back to 0 when the SPU task unblocks be_t ppuWaitMask; // 0x04 Wait mask for blocked PPU thread u8 ppuWaitSlotAndMode; // 0x06 Top 4 bits: Wait slot number of the blocked PPU threads, Bottom 4 bits: Wait mode of the blocked PPU thread u8 ppuPendingRecv; // 0x07 Set to 1 when the blocked PPU thread's conditions are met and back to 0 when the PPU thread is unblocked }; ControlSyncVar ctrl; // 0x00 be_t spuTaskUsedWaitSlots; // 0x08 A bit is set to 1 if the wait slot corresponding to the bit is used by an SPU task and 0 otherwise be_t spuTaskWaitMode; // 0x0A A bit is set to 1 if the wait mode for the SPU task corresponding to the bit is AND and 0 otherwise u8 spuPort; // 0x0C u8 isIwl; // 0x0D u8 direction; // 0x0E u8 clearMode; // 0x0F be_t spuTaskWaitMask[16]; // 0x10 Wait mask for blocked SPU tasks be_t pendingRecvTaskEvents[16]; // 0x30 The value of event flag when the wait condition for the thread/task was met u8 waitingTaskId[16]; // 0x50 Task id of waiting SPU threads u8 waitingTaskWklId[16]; // 0x60 Workload id of waiting SPU threads be_t addr; // 0x70 be_t eventPortId; // 0x78 be_t eventQueueId; // 0x7C }; using CellSpursLFQueue = CellSyncLFQueue; union CellSpursTaskArgument { be_t _u32[4]; be_t _u64[2]; }; union CellSpursTaskLsPattern { be_t _u32[4]; be_t _u64[2]; }; struct alignas(16) CellSpursTaskAttribute { u8 reserved[256]; }; CHECK_SIZE_ALIGN(CellSpursTaskAttribute, 256, 16); struct alignas(16) CellSpursTaskAttribute2 { be_t revision; be_t sizeContext; be_t eaContext; CellSpursTaskLsPattern lsPattern; vm::bcptr name; u8 reserved[220]; }; CHECK_SIZE_ALIGN(CellSpursTaskAttribute2, 256, 16); // Exception handler using CellSpursTasksetExceptionEventHandler = void(vm::ptr spurs, vm::ptr taskset, u32 idTask, vm::cptr info, vm::ptr arg); struct alignas(128) CellSpursTaskExitCode { u8 skip[128]; }; CHECK_SIZE_ALIGN(CellSpursTaskExitCode, 128, 128); struct CellSpursTaskInfo { CellSpursTaskLsPattern lsPattern; CellSpursTaskArgument argument; vm::bptr eaElf; vm::bptr eaContext; be_t sizeContext; u8 state; u8 hasSignal; u8 padding[2]; vm::bcptr eaTaskExitCode; u8 guid[8]; u8 reserved[12]; }; CHECK_SIZE(CellSpursTaskInfo, 72); struct CellSpursTasksetInfo { CellSpursTaskInfo taskInfo[CELL_SPURS_MAX_TASK]; be_t argument; be_t idWorkload; be_t idLastScheduledTask; vm::bcptr name; vm::bptr exceptionEventHandler; vm::bptr exceptionEventHandlerArgument; be_t sizeTaskset; u8 reserved[112]; }; CHECK_SIZE(CellSpursTasksetInfo, 9360); struct alignas(8) CellSpursTasksetAttribute { be_t revision; // 0x00 be_t sdk_version; // 0x04 be_t args; // 0x08 u8 priority[8]; // 0x10 be_t max_contention; // 0x18 vm::bcptr name; // 0x1C be_t taskset_size; // 0x20 be_t enable_clear_ls; // 0x24 u8 reserved[472]; }; CHECK_SIZE_ALIGN(CellSpursTasksetAttribute, 512, 8); struct alignas(128) CellSpursTaskset { struct TaskInfo { CellSpursTaskArgument args; // 0x00 vm::bcptr elf; // 0x10 be_t context_save_storage_and_alloc_ls_blocks; // 0x18 This is (context_save_storage_addr | allocated_ls_blocks) CellSpursTaskLsPattern ls_pattern; // 0x20 }; CHECK_SIZE(TaskInfo, 48); struct atomic_tasks_bitset { atomic_be_t values[4]; u32 get_bit(u32 bit) const { return values[bit / 32] & ((1u << 31) >> (bit % 32)); } }; atomic_tasks_bitset running; // 0x00 atomic_tasks_bitset ready; // 0x10 atomic_tasks_bitset pending_ready; // 0x20 atomic_tasks_bitset enabled; // 0x30 atomic_tasks_bitset signalled; // 0x40 atomic_tasks_bitset waiting; // 0x50 vm::bptr spurs; // 0x60 be_t args; // 0x68 u8 enable_clear_ls; // 0x70 u8 x71; // 0x71 u8 wkl_flag_wait_task; // 0x72 u8 last_scheduled_task; // 0x73 be_t wid; // 0x74 be_t x78; // 0x78 TaskInfo task_info[128]; // 0x80 vm::bptr exception_handler; // 0x1880 vm::bptr exception_handler_arg; // 0x1888 be_t size; // 0x1890 u32 unk2; // 0x1894 u32 event_flag_id1; // 0x1898 u32 event_flag_id2; // 0x189C u8 unk3[0x60]; // 0x18A0 }; CHECK_SIZE_ALIGN(CellSpursTaskset, 128 * 50, 128); struct alignas(128) spurs_taskset_signal_op { be_t running[4]; // 0x00 be_t ready[4]; // 0x10 be_t pending_ready[4]; // 0x20 be_t enabled[4]; // 0x30 be_t signalled[4]; // 0x40 be_t waiting[4]; // 0x50 vm::bptr spurs; // 0x60 be_t args; // 0x68 u8 enable_clear_ls; // 0x70 u8 x71; // 0x71 u8 wkl_flag_wait_task; // 0x72 u8 last_scheduled_task; // 0x73 be_t wid; // 0x74 be_t x78; // 0x78 }; struct alignas(128) spurs_wkl_state_op { SpursWorkloadState wklState1[0x10]; // 0x00 u8 wklStatus1[0x10]; // 0x10 u8 wklEvent1[0x10]; // 0x20 be_t wklEnabled; // 0x30 be_t wklMskB; // 0x34 u8 uns[0x5]; // 0x38 u8 sysSrvMsgUpdateWorkload; // 0x3D u8 uns2[0x12]; // 0x3E SpursWorkloadState wklState2[0x10]; // 0x50 u8 wklStatus2[0x10]; // 0x60 u8 wklEvent2[0x10]; // 0x70 }; struct alignas(128) CellSpursTaskset2 { struct TaskInfo { CellSpursTaskArgument args; vm::bptr elf_addr; vm::bptr context_save_storage; // This is (context_save_storage_addr | allocated_ls_blocks) CellSpursTaskLsPattern ls_pattern; }; CHECK_SIZE(TaskInfo, 48); be_t running_set[4]; // 0x00 be_t ready_set[4]; // 0x10 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 vm::bptr spurs; // 0x60 be_t args; // 0x68 u8 enable_clear_ls; // 0x70 u8 x71; // 0x71 u8 x72; // 0x72 u8 last_scheduled_task; // 0x73 be_t wid; // 0x74 be_t x78; // 0x78 TaskInfo task_info[128]; // 0x80 vm::bptr exception_handler; // 0x1880 vm::bptr exception_handler_arg; // 0x1888 be_t size; // 0x1890 u32 unk2; // 0x1894 u32 event_flag_id1; // 0x1898 u32 event_flag_id2; // 0x189C u8 unk3[0x1980 - 0x18A0]; // 0x18A0 be_t task_exit_code[128]; // 0x1980 u8 unk4[0x2900 - 0x2180]; // 0x2180 }; CHECK_SIZE_ALIGN(CellSpursTaskset2, 128 * 82, 128); struct alignas(16) CellSpursTaskNameBuffer { char taskName[CELL_SPURS_MAX_TASK][CELL_SPURS_MAX_TASK_NAME_LENGTH]; }; struct alignas(8) CellSpursTasksetAttribute2 { be_t revision; // 0x00 vm::bcptr name; // 0x04 be_t args; // 0x08 u8 priority[8]; // 0x10 be_t max_contention; // 0x18 be_t enable_clear_ls; // 0x1C vm::bptr task_name_buffer; // 0x20 u8 reserved[472]; }; CHECK_SIZE_ALIGN(CellSpursTasksetAttribute2, 512, 8); struct alignas(16) CellSpursTaskBinInfo { be_t eaElf; be_t sizeContext; be_t reserved; CellSpursTaskLsPattern lsPattern; }; struct alignas(128) CellSpursBarrier { be_t zero; // 0x00 be_t remained; // 0x04 u8 unk0[0x34 - 0x8]; vm::bptr taskset; // 0x34 u8 unk1[0x80 - 0x38]; }; CHECK_SIZE_ALIGN(CellSpursBarrier, 128, 128); // The SPURS kernel context. This resides at 0x100 of the LS. struct SpursKernelContext { u8 tempArea[0x80]; // 0x100 u8 wklLocContention[0x10]; // 0x180 u8 wklLocPendingContention[0x10]; // 0x190 u8 priority[0x10]; // 0x1A0 u8 x1B0[0x10]; // 0x1B0 vm::bptr spurs; // 0x1C0 be_t spuNum; // 0x1C8 be_t dmaTagId; // 0x1CC vm::bcptr wklCurrentAddr; // 0x1D0 be_t wklCurrentUniqueId; // 0x1D8 be_t wklCurrentId; // 0x1DC be_t exitToKernelAddr; // 0x1E0 be_t selectWorkloadAddr; // 0x1E4 u8 moduleId[2]; // 0x1E8 u8 sysSrvInitialised; // 0x1EA u8 spuIdling; // 0x1EB be_t wklRunnable1; // 0x1EC be_t wklRunnable2; // 0x1EE be_t x1F0; // 0x1F0 be_t x1F4; // 0x1F4 be_t x1F8; // 0x1F8 be_t x1FC; // 0x1FC be_t x200; // 0x200 be_t x204; // 0x204 be_t x208; // 0x208 be_t x20C; // 0x20C be_t traceBuffer; // 0x210 be_t traceMsgCount; // 0x218 be_t traceMaxCount; // 0x21C u8 wklUniqueId[0x10]; // 0x220 u8 x230[0x280 - 0x230]; // 0x230 be_t guid[4]; // 0x280 }; CHECK_SIZE(SpursKernelContext, 0x190); // The SPURS taskset policy module context. This resides at 0x2700 of the LS. struct SpursTasksetContext { u8 tempAreaTaskset[0x80]; // 0x2700 u8 tempAreaTaskInfo[0x30]; // 0x2780 be_t x27B0; // 0x27B0 vm::bptr taskset; // 0x27B8 be_t kernelMgmtAddr; // 0x27C0 be_t syscallAddr; // 0x27C4 be_t x27C8; // 0x27C8 be_t spuNum; // 0x27CC be_t dmaTagId; // 0x27D0 be_t taskId; // 0x27D4 u8 x27D8[0x2840 - 0x27D8]; // 0x27D8 u8 moduleId[16]; // 0x2840 u8 stackArea[0x2C80 - 0x2850]; // 0x2850 be_t savedContextLr; // 0x2C80 be_t savedContextSp; // 0x2C90 be_t savedContextR80ToR127[48]; // 0x2CA0 be_t savedContextFpscr; // 0x2FA0 be_t savedWriteTagGroupQueryMask; // 0x2FB0 be_t savedSpuWriteEventMask; // 0x2FB4 be_t tasksetMgmtAddr; // 0x2FB8 be_t guidAddr; // 0x2FBC be_t x2FC0; // 0x2FC0 be_t x2FC8; // 0x2FC8 be_t taskExitCode; // 0x2FD0 be_t x2FD4; // 0x2FD4 u8 x2FD8[0x3000 - 0x2FD8]; // 0x2FD8 }; CHECK_SIZE(SpursTasksetContext, 0x900); struct SpursJobChainContext { u8 tempAreaJobChain[0x80]; // 0x4A00 u8 unk0[3]; // 0x4A80 b8 unkFlag0; // 0x4A83 u8 unk1[0x10]; // 0x4A84 vm::bptr jobChain; // 0x4A94 // TODO }; class SpursModuleExit { };