Integrate 3ds_pxi and 3ds_sm

This commit is contained in:
TuxSH
2017-11-02 15:11:55 +01:00
parent 76dde0e6db
commit 8258a98647
37 changed files with 2806 additions and 1 deletions

View File

@@ -0,0 +1,42 @@
/*
MyThread.c:
Small threading library, based off ctrulib.
(c) TuxSH, 2016-2017
This is part of 3ds_pxi, which is licensed under the MIT license (see LICENSE for details).
*/
#include "MyThread.h"
#include "memory.h"
static void _thread_begin(void* arg)
{
MyThread *t = (MyThread *)arg;
t->ep();
MyThread_Exit();
}
Result MyThread_Create(MyThread *t, void (*entrypoint)(void), void *stack, u32 stackSize, int prio, int affinity)
{
t->ep = entrypoint;
t->stacktop = (u8 *)stack + stackSize;
return svcCreateThread(&t->handle, _thread_begin, (u32)t, (u32*)t->stacktop, prio, affinity);
}
Result MyThread_Join(MyThread *thread, s64 timeout_ns)
{
if (thread == NULL) return 0;
Result res = svcWaitSynchronization(thread->handle, timeout_ns);
if(R_FAILED(res)) return res;
svcCloseHandle(thread->handle);
thread->handle = (Handle)0;
return res;
}
void MyThread_Exit(void)
{
svcExitThread();
}

View File

@@ -0,0 +1,28 @@
/*
MyThread.h:
Small threading library, based off ctrulib.
(c) TuxSH, 2016-2017
This is part of 3ds_pxi, which is licensed under the MIT license (see LICENSE for details).
*/
#pragma once
#include <3ds/types.h>
#include <3ds/result.h>
#include <3ds/svc.h>
#include <3ds/synchronization.h>
#define THREAD_STACK_SIZE 0x1000
typedef struct MyThread
{
Handle handle;
void (*ep)(void);
bool finished;
void* stacktop;
} MyThread;
Result MyThread_Create(MyThread *t, void (*entrypoint)(void), void *stack, u32 stackSize, int prio, int affinity);
Result MyThread_Join(MyThread *thread, s64 timeout_ns);
void MyThread_Exit(void);

139
sysmodules/pxi/source/PXI.c Normal file
View File

@@ -0,0 +1,139 @@
/*
PXI.c:
PXI I/O functions.
(c) TuxSH, 2016-2017
This is part of 3ds_pxi, which is licensed under the MIT license (see LICENSE for details).
*/
#include "PXI.h"
void PXIReset(void)
{
REG_PXI_SYNC = 0;
REG_PXI_CNT = CNT_CLEAR_SEND_FIFO;
for(u32 i = 0; i < 16; i += 2)
{
REG_PXI_RECV;
REG_PXI_RECV;
}
REG_PXI_CNT = 0;
REG_PXI_CNT = CNT_ENABLE_FIFOs | CNT_ACKNOWLEDGE_FIFO_ERROR | CNT_CLEAR_SEND_FIFO;
}
void PXITriggerSync9IRQ(void)
{
REG_PXI_INTERRUPT_CNT |= SYNC_TRIGGER_SYNC9_IRQ;
}
bool PXIIsSendFIFOFull(void)
{
return (REG_PXI_CNT & CNT_SEND_FIFO_FULL_STATUS) != 0;
}
void PXISendByte(u8 byte)
{
REG_PXI_BYTE_SENT_TO_REMOTE = byte;
}
void PXISendWord(u32 word)
{
while(REG_PXI_CNT & CNT_SEND_FIFO_FULL_STATUS);
REG_PXI_SEND = word;
}
void PXISendBuffer(const u32 *buffer, u32 nbWords)
{
for(; nbWords > 0; nbWords--)
{
while(REG_PXI_CNT & CNT_SEND_FIFO_FULL_STATUS);
REG_PXI_SEND = *buffer++;
}
}
bool PXIIsReceiveFIFOEmpty(void)
{
return (REG_PXI_CNT & CNT_RECEIVE_FIFO_EMPTY_STATUS) != 0;
}
u8 PXIReceiveByte(void)
{
return REG_PXI_BYTE_RECEIVED_FROM_REMOTE;
}
u32 PXIReceiveWord(void)
{
while(REG_PXI_CNT & CNT_RECEIVE_FIFO_EMPTY_STATUS);
return REG_PXI_RECV;
}
void PXIReceiveBuffer(u32 *buffer, u32 nbWords)
{
for(; nbWords > 0; nbWords--)
{
while(REG_PXI_CNT & CNT_RECEIVE_FIFO_EMPTY_STATUS);
*buffer++ = REG_PXI_RECV;
}
}
Result bindPXIInterrupts(Handle *syncInterrupt, Handle *receiveFIFONotEmptyInterrupt, Handle *sendFIFOEmptyInterrupt)
{
Result res = 0;
u32 mask = CNT_ENABLE_FIFOs | CNT_ENABLE_RECEIVE_FIFO_NOT_EMPTY_IRQ | CNT_ENABLE_SEND_FIFO_EMPTY_IRQ;
if(receiveFIFONotEmptyInterrupt != NULL)
{
res = svcBindInterrupt(0x53, *receiveFIFONotEmptyInterrupt, 0, false);
if(R_FAILED(res))
{
unbindPXIInterrupts(syncInterrupt, receiveFIFONotEmptyInterrupt, sendFIFOEmptyInterrupt);
return res;
}
REG_PXI_CNT = (REG_PXI_CNT & mask) | CNT_ENABLE_RECEIVE_FIFO_NOT_EMPTY_IRQ;
}
if(sendFIFOEmptyInterrupt != NULL)
{
res = svcBindInterrupt(0x52, *sendFIFOEmptyInterrupt, 0, false);
if(R_FAILED(res))
{
unbindPXIInterrupts(syncInterrupt, receiveFIFONotEmptyInterrupt, sendFIFOEmptyInterrupt);
return res;
}
REG_PXI_CNT = (REG_PXI_CNT & mask) | CNT_ENABLE_SEND_FIFO_EMPTY_IRQ;
}
if(syncInterrupt != NULL)
{
res = svcBindInterrupt(0x50, *syncInterrupt, 0, false);
if(R_FAILED(res))
{
unbindPXIInterrupts(syncInterrupt, receiveFIFONotEmptyInterrupt, sendFIFOEmptyInterrupt);
return res;
}
REG_PXI_INTERRUPT_CNT |= SYNC_ENABLE_SYNC11_IRQ;
}
return res;
}
void unbindPXIInterrupts(Handle *syncInterrupt, Handle *receiveFIFONotEmptyInterrupt, Handle *sendFIFOEmptyInterrupt)
{
if(receiveFIFONotEmptyInterrupt != NULL)
{
REG_PXI_CNT &= CNT_ENABLE_FIFOs | CNT_ENABLE_SEND_FIFO_EMPTY_IRQ;
svcUnbindInterrupt(0x53, *receiveFIFONotEmptyInterrupt);
}
if(sendFIFOEmptyInterrupt != NULL)
{
REG_PXI_CNT &= CNT_ENABLE_FIFOs | CNT_ENABLE_RECEIVE_FIFO_NOT_EMPTY_IRQ;
svcUnbindInterrupt(0x52, *sendFIFOEmptyInterrupt);
}
if(syncInterrupt != NULL)
{
REG_PXI_INTERRUPT_CNT &= ~SYNC_ENABLE_SYNC11_IRQ;
svcUnbindInterrupt(0x50, *syncInterrupt);
}
}

View File

@@ -0,0 +1,47 @@
/*
PXI.h:
PXI I/O functions.
(c) TuxSH, 2016-2017
This is part of 3ds_pxi, which is licensed under the MIT license (see LICENSE for details).
*/
#pragma once
#include <3ds.h>
#define PXI_REGS_BASE 0x1EC63000
#define REG_PXI_SYNC *(vu32 *)(PXI_REGS_BASE + 0)
#define REG_PXI_BYTE_RECEIVED_FROM_REMOTE *(vu8 *)(PXI_REGS_BASE)
#define REG_PXI_BYTE_SENT_TO_REMOTE *(vu8 *)(PXI_REGS_BASE + 1)
#define REG_PXI_INTERRUPT_CNT *(vu8 *)(PXI_REGS_BASE + 3)
#define SYNC_TRIGGER_SYNC9_IRQ (1U << 6)
#define SYNC_ENABLE_SYNC11_IRQ (1U << 7)
#define REG_PXI_CNT *(vu16 *)(PXI_REGS_BASE + 4)
#define CNT_SEND_FIFO_FULL_STATUS (1U << 1)
#define CNT_ENABLE_SEND_FIFO_EMPTY_IRQ (1U << 2)
#define CNT_CLEAR_SEND_FIFO (1U << 3)
#define CNT_RECEIVE_FIFO_EMPTY_STATUS (1U << 8)
#define CNT_ENABLE_RECEIVE_FIFO_NOT_EMPTY_IRQ (1U << 10)
#define CNT_ACKNOWLEDGE_FIFO_ERROR (1U << 14)
#define CNT_ENABLE_FIFOs (1U << 15)
#define REG_PXI_SEND *(vu32 *)(PXI_REGS_BASE + 8)
#define REG_PXI_RECV *(vu32 *)(PXI_REGS_BASE + 12)
void PXIReset(void);
void PXITriggerSync9IRQ(void);
bool PXIIsSendFIFOFull(void);
void PXISendByte(u8 byte);
void PXISendWord(u32 word);
void PXISendBuffer(const u32 *buffer, u32 nbWords);
bool PXIIsReceiveFIFOEmpty(void);
u8 PXIReceiveByte(void);
u32 PXIReceiveWord(void);
void PXIReceiveBuffer(u32 *buffer, u32 nbWords);
Result bindPXIInterrupts(Handle *syncInterrupt, Handle *receiveFIFONotEmptyInterrupt, Handle *sendFIFOEmptyInterrupt);
void unbindPXIInterrupts(Handle *syncInterrupt, Handle *receiveFIFONotEmptyInterrupt, Handle *sendFIFOEmptyInterrupt);

View File

@@ -0,0 +1,94 @@
/*
common.h:
Common types and global variables.
(c) TuxSH, 2016-2017
This is part of 3ds_pxi, which is licensed under the MIT license (see LICENSE for details).
*/
#pragma once
#include <3ds.h>
typedef enum SessionState
{
STATE_IDLE = 0,
STATE_RECEIVED_FROM_ARM11 = 1,
STATE_SENT_TO_ARM9 = 2,
STATE_RECEIVED_FROM_ARM9 = 3
} SessionState;
typedef struct SessionData
{
SessionState state;
u32 buffer[0x100/4];
Handle handle;
u32 usedStaticBuffers;
RecursiveLock lock;
} SessionData;
#define NB_STATIC_BUFFERS 21
typedef struct SessionManager
{
Handle sendAllBuffersToArm9Event, replySemaphore, PXISRV11CommandReceivedEvent, PXISRV11ReplySentEvent;
u32 latest_PXI_MC5_val, pendingArm9Commands;
u32 receivedServiceId;
RecursiveLock senderLock;
bool sendingDisabled;
SessionData sessionData[10];
u32 currentlyProvidedStaticBuffers, freeStaticBuffers;
} SessionManager;
//Page alignment is mandatory there
extern u32 ALIGN(0x1000) staticBuffers[NB_STATIC_BUFFERS][0x1000/4];
extern Handle PXISyncInterrupt, PXITransferMutex;
extern Handle terminationRequestedEvent;
extern bool shouldTerminate;
extern SessionManager sessionManager;
extern const u32 nbStaticBuffersByService[10];
static inline Result assertSuccess(Result res)
{
if(R_FAILED(res)) svcBreak(USERBREAK_PANIC);
return res;
}
static inline s32 getMSBPosition(u32 val)
{
return 31 - (s32) __builtin_clz(val);
}
static inline s32 getLSBPosition(u32 val)
{
return __builtin_ffs(val) - 1;
}
static inline u32 clearMSBs(u32 val, u32 nb)
{
for(u32 i = 0; i < nb; i++)
{
s32 pos = getMSBPosition(val);
if(pos == -1) break;
val &= ~(1 << pos);
}
return val;
}
static inline u32 countNbBitsSet(u32 val)
{
u32 nb = 0;
while(val != 0)
{
val = clearMSBs(val, 1);
nb++;
}
return nb;
}

View File

@@ -0,0 +1,211 @@
/*
main.c
(De)initialization stuff. It's also here where sessions are being accepted.
(c) TuxSH, 2016-2017
This is part of 3ds_pxi, which is licensed under the MIT license (see LICENSE for details).
*/
#include "PXI.h"
#include "common.h"
#include "MyThread.h"
#include "receiver.h"
#include "sender.h"
#include "memory.h"
Handle PXISyncInterrupt = 0, PXITransferMutex = 0;
Handle terminationRequestedEvent = 0;
bool shouldTerminate = false;
SessionManager sessionManager = {0};
const char *serviceNames[10] =
{
"pxi:mc",
"PxiFS0",
"PxiFS1",
"PxiFSB",
"PxiFSR",
"PxiPM",
"pxi:dev", //in the official PXI module maxSessions(pxi:dev) == 2. It doesn't matter anyways, since srvSysRegisterService is always called with 1
"pxi:am9",
"pxi:ps9",
"pxi:srv11",
};
const u32 nbStaticBuffersByService[10] = {0, 2, 2, 2, 2, 1, 4, 4, 4, 0};
u32 ALIGN(0x1000) staticBuffers[NB_STATIC_BUFFERS][0x400] = {{0}};
static inline void initPXI(void)
{
Result res;
Handle handles[2] = {0};
PXIReset();
if(PXISyncInterrupt != 0) svcBreak(USERBREAK_PANIC); //0xE0A0183B
assertSuccess(svcCreateEvent(&PXISyncInterrupt, RESET_ONESHOT));
if(PXITransferMutex != 0) svcBreak(USERBREAK_PANIC); //0xE0A0183B
assertSuccess(svcCreateMutex(&PXITransferMutex, false));
assertSuccess(svcCreateEvent(&handles[0], RESET_ONESHOT)); //receive FIFO not empty
assertSuccess(svcCreateEvent(&handles[1], RESET_ONESHOT)); //send FIFO empty
assertSuccess(bindPXIInterrupts(&PXISyncInterrupt, &handles[0], &handles[1]));
s32 handleIndex;
do
{
while(!PXIIsSendFIFOFull()) PXISendWord(0);
res = assertSuccess(svcWaitSynchronization(handles[0], 0LL));
if(R_DESCRIPTION(res) == RD_TIMEOUT)
assertSuccess(svcWaitSynchronizationN(&handleIndex, handles, 2, false, -1LL));
else
handleIndex = 0;
} while(handleIndex != 0);
unbindPXIInterrupts(NULL, &handles[0], &handles[1]);
PXISendByte(1);
while(PXIReceiveByte() < 1);
while (!PXIIsReceiveFIFOEmpty())
PXIReceiveWord();
PXISendByte(2);
while(PXIReceiveByte() < 2);
svcCloseHandle(handles[0]);
svcCloseHandle(handles[1]);
}
static inline void exitPXI(void)
{
unbindPXIInterrupts(&PXISyncInterrupt, NULL, NULL);
svcCloseHandle(PXITransferMutex);
svcCloseHandle(PXISyncInterrupt);
PXIReset();
}
static u8 ALIGN(8) receiverStack[THREAD_STACK_SIZE];
static u8 ALIGN(8) senderStack[THREAD_STACK_SIZE];
static u8 ALIGN(8) PXISRV11HandlerStack[THREAD_STACK_SIZE];
// this is called before main
void __appInit()
{
assertSuccess(svcCreateEvent(&terminationRequestedEvent, RESET_STICKY));
assertSuccess(svcCreateEvent(&sessionManager.sendAllBuffersToArm9Event, RESET_ONESHOT));
assertSuccess(svcCreateSemaphore(&sessionManager.replySemaphore, 0, 9));
assertSuccess(svcCreateEvent(&sessionManager.PXISRV11CommandReceivedEvent, RESET_ONESHOT));
assertSuccess(svcCreateEvent(&sessionManager.PXISRV11ReplySentEvent, RESET_ONESHOT));
initPXI();
for(Result res = 0xD88007FA; res == (Result)0xD88007FA; svcSleepThread(500 * 1000LL))
{
res = srvInit();
if(R_FAILED(res) && res != (Result)0xD88007FA)
svcBreak(USERBREAK_PANIC);
}
}
// this is called after main exits
void __appExit()
{
srvExit();
exitPXI();
svcCloseHandle(terminationRequestedEvent);
svcCloseHandle(sessionManager.sendAllBuffersToArm9Event);
svcCloseHandle(sessionManager.replySemaphore);
svcCloseHandle(sessionManager.PXISRV11CommandReceivedEvent);
svcCloseHandle(sessionManager.PXISRV11ReplySentEvent);
}
// stubs for non-needed pre-main functions
void __system_initSyscalls(){}
Result __sync_init(void);
Result __sync_fini(void);
void __ctru_exit()
{
__appExit();
__sync_fini();
svcExitProcess();
}
void initSystem()
{
__sync_init();
__system_initSyscalls();
__appInit();
}
int main(void)
{
Handle handles[10] = {0}; //notification handle + service handles
MyThread receiverThread = {0}, senderThread = {0}, PXISRV11HandlerThread = {0};
for(u32 i = 0; i < 9; i++)
assertSuccess(srvRegisterService(handles + 1 + i, serviceNames[i], 1));
assertSuccess(MyThread_Create(&receiverThread, receiver, receiverStack, THREAD_STACK_SIZE, 0x2D, -2));
assertSuccess(MyThread_Create(&senderThread, sender, senderStack, THREAD_STACK_SIZE, 0x2D, -2));
assertSuccess(MyThread_Create(&PXISRV11HandlerThread, PXISRV11Handler, PXISRV11HandlerStack, THREAD_STACK_SIZE, 0x2D, -2));
assertSuccess(srvEnableNotification(&handles[0]));
while(!shouldTerminate)
{
s32 index = 0;
assertSuccess(svcWaitSynchronizationN(&index, handles, 10, false, -1LL));
if(index == 0)
{
u32 notificationId;
assertSuccess(srvReceiveNotification(&notificationId));
if(notificationId == 0x100) shouldTerminate = true;
}
else
{
Handle session = 0;
SessionData *data = &sessionManager.sessionData[index - 1];
assertSuccess(svcAcceptSession(&session, handles[index]));
RecursiveLock_Lock(&sessionManager.senderLock);
if(data->handle != 0)
svcBreak(USERBREAK_PANIC);
data->handle = session;
assertSuccess(svcSignalEvent(sessionManager.sendAllBuffersToArm9Event));
RecursiveLock_Unlock(&sessionManager.senderLock);
}
}
u32 PXIMC_OnPXITerminate = 0x10000; //TODO: see if this is correct
sessionManager.sessionData[0].state = STATE_SENT_TO_ARM9;
sendPXICmdbuf(NULL, 0, &PXIMC_OnPXITerminate);
assertSuccess(MyThread_Join(&receiverThread, -1LL));
assertSuccess(MyThread_Join(&senderThread, -1LL));
assertSuccess(MyThread_Join(&PXISRV11HandlerThread, -1LL));
for(u32 i = 0; i < 10; i++)
svcCloseHandle(handles[i]);
return 0;
}

View File

@@ -0,0 +1,19 @@
/*
memory.c:
Memory functions
(c) TuxSH, 2016-2017
This is part of 3ds_pxi, which is licensed under the MIT license (see LICENSE for details).
*/
#include "memory.h"
//Adpated from CakesFW
void memcpy(void *dest, const void *src, u32 size)
{
u8 *destc = (u8 *)dest;
const u8 *srcc = (const u8 *)src;
for(u32 i = 0; i < size; i++)
destc[i] = srcc[i];
}

View File

@@ -0,0 +1,13 @@
/*
memory.h:
Memory functions
(c) TuxSH, 2016-2017
This is part of 3ds_pxi, which is licensed under the MIT license (see LICENSE for details).
*/
#pragma once
#include <3ds/types.h>
void memcpy(void *dest, const void *src, u32 size);

View File

@@ -0,0 +1,69 @@
/*
receiver.c:
Fetches replies coming from Process9, writing them in the appropriate buffer.
(c) TuxSH, 2016-2017
This is part of 3ds_pxi, which is licensed under the MIT license (see LICENSE for details).
*/
#include "receiver.h"
#include "PXI.h"
#include "memory.h"
static inline void receiveFromArm9(void)
{
u32 serviceId = PXIReceiveWord();
//The offcical implementation can return 0xD90043FA
if(((serviceId >= 10)) || (sessionManager.sessionData[serviceId].state != STATE_SENT_TO_ARM9))
svcBreak(USERBREAK_PANIC);
sessionManager.receivedServiceId = serviceId;
RecursiveLock_Lock(&sessionManager.sessionData[serviceId].lock);
u32 replyHeader = PXIReceiveWord();
u32 replySizeWords = (replyHeader & 0x3F) + ((replyHeader & 0xFC0) >> 6) + 1;
if(replySizeWords > 0x40) svcBreak(USERBREAK_PANIC);
u32 *buf = sessionManager.sessionData[serviceId].buffer;
buf[0] = replyHeader;
PXIReceiveBuffer(buf + 1, replySizeWords - 1);
sessionManager.sessionData[serviceId].state = STATE_RECEIVED_FROM_ARM9;
RecursiveLock_Unlock(&sessionManager.sessionData[serviceId].lock);
if(serviceId == 0 && shouldTerminate)
{
assertSuccess(svcSignalEvent(terminationRequestedEvent));
return;
}
if(serviceId != 9)
{
s32 count;
assertSuccess(svcReleaseSemaphore(&count, sessionManager.replySemaphore, 1));
}
else
{
assertSuccess(svcSignalEvent(sessionManager.PXISRV11CommandReceivedEvent));
assertSuccess(svcWaitSynchronization(sessionManager.PXISRV11ReplySentEvent, -1LL));
if( (sessionManager.sessionData[serviceId].state != STATE_SENT_TO_ARM9))
svcBreak(USERBREAK_PANIC);
}
}
void receiver(void)
{
Handle handles[] = {PXISyncInterrupt, terminationRequestedEvent};
assertSuccess(svcWaitSynchronization(sessionManager.PXISRV11ReplySentEvent, -1LL));
while(true)
{
s32 index;
assertSuccess(svcWaitSynchronizationN(&index, handles, 2, false, -1LL));
if(index == 1) return;
while(!PXIIsReceiveFIFOEmpty())
receiveFromArm9();
}
}

View File

@@ -0,0 +1,13 @@
/*
receiver.h:
Fetches replies coming from Process9, writing them in the appropriate buffer.
(c) TuxSH, 2016-2017
This is part of 3ds_pxi, which is licensed under the MIT license (see LICENSE for details).
*/
#pragma once
#include "common.h"
void receiver(void);

View File

@@ -0,0 +1,300 @@
/*
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);
}
}
}

View File

@@ -0,0 +1,15 @@
/*
sender.h
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).
*/
#pragma once
#include "common.h"
Result sendPXICmdbuf(Handle *additionalHandle, u32 serviceId, u32 *buffer);
void sender(void);
void PXISRV11Handler(void);