/* sender.c Handles commands from arm11 processes, then sends them to Process9, and replies to arm11 processes the replies received from Process9 (=> receiver.c). (except for PXISRV11) (c) TuxSH, 2016-2017 This is part of 3ds_pxi, which is licensed under the MIT license (see LICENSE for details). */ #include "sender.h" #include "PXI.h" #include "memory.h" Result sendPXICmdbuf(Handle *additionalHandle, u32 serviceId, u32 *buffer) { Result res = 0; if(additionalHandle != NULL) { s32 index = 1; bool cancelled = false; Handle handles[2] = {PXITransferMutex, *additionalHandle}; res = assertSuccess(svcWaitSynchronization(PXITransferMutex, 0LL)); if(R_DESCRIPTION(res) == RD_TIMEOUT) { assertSuccess(svcWaitSynchronizationN(&index, handles, 2, false, -1LL)); cancelled = index == 1; } else cancelled = false; if(cancelled) return 0xD92043FB; // cancel requested } else assertSuccess(svcWaitSynchronization(PXITransferMutex, -1LL)); PXISendWord(serviceId & 0xFF); PXITriggerSync9IRQ(); //notify arm9 PXISendBuffer(buffer, (buffer[0] & 0x3F) + ((buffer[0] & 0xFC0) >> 6) + 1); svcReleaseMutex(PXITransferMutex); return 0; } static void updateTLSForStaticBuffers(void) { u32 *staticBufs = getThreadStaticBuffers(); u32 val = sessionManager.currentlyProvidedStaticBuffers; for(u32 i = 0; i < 4; i++) { s32 pos = getLSBPosition(val); if(pos != -1) { staticBufs[2 * i] = IPC_Desc_StaticBuffer(0x1000, 0); staticBufs[2 * i + 1] = (u32)&staticBuffers[pos]; val &= ~(1 << pos); } else { staticBufs[2 * i] = IPC_Desc_StaticBuffer(0, 0); staticBufs[2 * i + 1] = 0; } } } static void acquireStaticBuffers(void) { u32 freeStaticBuffersOrig = sessionManager.freeStaticBuffers; sessionManager.freeStaticBuffers = clearMSBs(sessionManager.freeStaticBuffers, 4); sessionManager.currentlyProvidedStaticBuffers = ~sessionManager.freeStaticBuffers & freeStaticBuffersOrig; updateTLSForStaticBuffers(); } static void releaseStaticBuffers(u32 *src, u32 nb) { u32 val = clearMSBs(*src, nb); sessionManager.freeStaticBuffers |= ~val & *src; *src = val; } void sender(void) { Handle handles[12] = {terminationRequestedEvent, sessionManager.sendAllBuffersToArm9Event, sessionManager.replySemaphore}; Handle replyTarget = 0; Result res = 0; s32 index; u32 *cmdbuf = getThreadCommandBuffer(); u32 nbIdleSessions = 0; u32 posToServiceId[9] = {0}; RecursiveLock_Lock(&sessionManager.senderLock); //Setting static buffers is needed for IPC translation types 2 and 3 (otherwise ReplyAndReceive will dereference NULL) sessionManager.freeStaticBuffers = (1 << NB_STATIC_BUFFERS) - 1; acquireStaticBuffers(); do { if(replyTarget == 0) //send to arm9 { for(u32 i = 0; i < 9; i++) { SessionData *data = &sessionManager.sessionData[i]; if(data->handle == 0 || data->state != STATE_RECEIVED_FROM_ARM11) continue; if(sessionManager.sendingDisabled) { if (sessionManager.pendingArm9Commands != 0 || sessionManager.latest_PXI_MC5_val == 0) continue; } else sessionManager.pendingArm9Commands++; RecursiveLock_Lock(&data->lock); data->state = STATE_SENT_TO_ARM9; res = sendPXICmdbuf(&terminationRequestedEvent, i, data->buffer); RecursiveLock_Unlock(&data->lock); if(R_FAILED(res)) goto terminate; } cmdbuf[0] = 0xFFFF0000; //Kernel11 } nbIdleSessions = 0; for(u32 i = 0; i < 9; i++) { if(sessionManager.sessionData[i].handle != 0 && sessionManager.sessionData[i].state == STATE_IDLE) { handles[3 + nbIdleSessions] = sessionManager.sessionData[i].handle; posToServiceId[nbIdleSessions++] = i; } } RecursiveLock_Unlock(&sessionManager.senderLock); res = svcReplyAndReceive(&index, handles, 3 + nbIdleSessions, replyTarget); RecursiveLock_Lock(&sessionManager.senderLock); if((u32)res == 0xC920181A) //session closed by remote { u32 i; if(index == -1) for(i = 0; i < 9 && replyTarget != sessionManager.sessionData[i].handle; i++); else i = posToServiceId[index - 3]; if(i >= 9) svcBreak(USERBREAK_PANIC); svcCloseHandle(sessionManager.sessionData[i].handle); sessionManager.sessionData[i].handle = replyTarget = 0; continue; } else if(R_FAILED(res) || (u32)index >= 3 + nbIdleSessions) svcBreak(USERBREAK_PANIC); switch(index) { case 0: //terminaton requested break; case 1: replyTarget = 0; continue; case 2: //arm9 reply { u32 sessionId = 0; for(sessionId = 0; sessionId < 9 && sessionManager.sessionData[sessionId].state != STATE_RECEIVED_FROM_ARM9; sessionId++); if(sessionId == 9) svcBreak(USERBREAK_PANIC); SessionData *data = &sessionManager.sessionData[sessionId]; RecursiveLock_Lock(&data->lock); if(data->state != STATE_RECEIVED_FROM_ARM9) svcBreak(USERBREAK_PANIC); if(sessionManager.latest_PXI_MC5_val == 2) { if(sessionManager.pendingArm9Commands != 0) svcBreak(USERBREAK_PANIC); sessionManager.sendingDisabled = false; } else if(sessionManager.latest_PXI_MC5_val == 0) (sessionManager.pendingArm9Commands)--; u32 bufSize = 4 * ((data->buffer[0] & 0x3F) + ((data->buffer[0] & 0xFC0) >> 6) + 1); if(bufSize > 0x100) svcBreak(USERBREAK_PANIC); memcpy(cmdbuf, data->buffer, bufSize); releaseStaticBuffers(&data->usedStaticBuffers, 4); data->state = STATE_IDLE; replyTarget = data->handle; RecursiveLock_Unlock(&data->lock); break; } default: //arm11 command received { u32 serviceId = posToServiceId[index - 3]; SessionData *data = &sessionManager.sessionData[serviceId]; RecursiveLock_Lock(&data->lock); if(data->state != STATE_IDLE) svcBreak(USERBREAK_PANIC); if(!(serviceId == 0 && (cmdbuf[0] >> 16) == 5)) //if not pxi:mc 5 sessionManager.latest_PXI_MC5_val = 0; else if((u8)(cmdbuf[1]) != 0) { sessionManager.latest_PXI_MC5_val = 1; if(sessionManager.sendingDisabled) svcBreak(USERBREAK_PANIC); sessionManager.sendingDisabled = true; } else { sessionManager.latest_PXI_MC5_val = 2; if(!sessionManager.sendingDisabled) svcBreak(USERBREAK_PANIC); } u32 bufSize = 4 * ((cmdbuf[0] & 0x3F) + ((cmdbuf[0] & 0xFC0) >> 6) + 1); if(bufSize > 0x100) svcBreak(USERBREAK_PANIC); memcpy(data->buffer, cmdbuf, bufSize); data->state = STATE_RECEIVED_FROM_ARM11; replyTarget = 0; releaseStaticBuffers(&sessionManager.currentlyProvidedStaticBuffers, 4 - nbStaticBuffersByService[serviceId]); data->usedStaticBuffers = sessionManager.currentlyProvidedStaticBuffers; acquireStaticBuffers(); RecursiveLock_Unlock(&data->lock); break; } } } while(index != 0); terminate: for(u32 i = 0; i < 9; i++) { if(sessionManager.sessionData[i].handle != 0) svcCloseHandle(sessionManager.sessionData[i].handle); } RecursiveLock_Unlock(&sessionManager.senderLock); } void PXISRV11Handler(void) { // Assumption: only 1 request is sent to this service at a time Handle handles[] = {sessionManager.PXISRV11CommandReceivedEvent, terminationRequestedEvent}; SessionData *data = &sessionManager.sessionData[9]; data->state = STATE_SENT_TO_ARM9; assertSuccess(svcSignalEvent(sessionManager.PXISRV11ReplySentEvent)); while(true) { s32 index; assertSuccess(svcWaitSynchronizationN(&index, handles, 2, false, -1LL)); if(index == 1) return; else { RecursiveLock_Lock(&data->lock); if(data->state != STATE_RECEIVED_FROM_ARM9) svcBreak(USERBREAK_PANIC); data->state = STATE_IDLE; if(data->buffer[0] >> 16 != 1) { data->buffer[0] = 0x40; data->buffer[1] = 0xD900182F; //unimplemented/invalid command } else { data->buffer[0] = 0x10040; data->buffer[1] = srvPublishToSubscriber(data->buffer[1], 1); data->state = STATE_RECEIVED_FROM_ARM11; if(data->buffer[1] == 0xD8606408) svcBreak(USERBREAK_PANIC); } assertSuccess(sendPXICmdbuf(&terminationRequestedEvent, 9, data->buffer)); data->state = STATE_SENT_TO_ARM9; assertSuccess(svcSignalEvent(sessionManager.PXISRV11ReplySentEvent)); RecursiveLock_Unlock(&data->lock); } } }