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

21
sysmodules/pxi/LICENSE Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015 TuxSH
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

52
sysmodules/pxi/Makefile Normal file
View File

@@ -0,0 +1,52 @@
rwildcard = $(foreach d, $(wildcard $1*), $(filter $(subst *, %, $2), $d) $(call rwildcard, $d/, $2))
ifeq ($(strip $(DEVKITARM)),)
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
endif
include $(DEVKITARM)/3ds_rules
CC := arm-none-eabi-gcc
AS := arm-none-eabi-as
LD := arm-none-eabi-ld
OC := arm-none-eabi-objcopy
name := pxi
dir_source := source
dir_build := build
dir_out := ../../$(dir_build)
LIBS := -lctru
LIBDIRS := $(CTRULIB)
LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
INCLUDE := $(foreach dir,$(LIBDIRS),-I$(dir)/include)
ARCH := -mcpu=mpcore -mfloat-abi=hard -mtp=soft
CFLAGS := -Wall -Wextra -MMD -MP -marm $(ARCH) -fno-builtin -std=c11 -O2 -g -ffast-math -mword-relocations \
-ffunction-sections -fdata-sections $(INCLUDE) -DARM11 -D_3DS
LDFLAGS := -specs=3dsx.specs -Wl,--gc-sections $(ARCH)
objects = $(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
$(call rwildcard, $(dir_source), *.c))
.PHONY: all
all: $(dir_out)/$(name).cxi
.PHONY: clean
clean:
@rm -rf $(dir_build)
$(dir_out)/$(name).cxi: $(dir_build)/$(name).elf
@makerom -f ncch -rsf $(name).rsf -nocodepadding -o $@ -elf $<
$(dir_build)/$(name).elf: $(objects)
$(LINK.o) $(OUTPUT_OPTION) $^ $(LIBPATHS) $(LIBS)
$(dir_build)/memory.o : CFLAGS += -O3
$(dir_build)/%.o: $(dir_source)/%.c
@mkdir -p "$(@D)"
$(COMPILE.c) $(OUTPUT_OPTION) $<
include $(call rwildcard, $(dir_build), *.d)

15
sysmodules/pxi/README.md Normal file
View File

@@ -0,0 +1,15 @@
# 3ds_pxi
Open source replacement of the ARM11 PXI system module.
This is licensed under the MIT license.
# Usage
To run this system module, use a recent release or commit of [Luma3DS](https://github.com/AuroraWright/Luma3DS/) and copy pxi.cxi to /luma/sysmodules/.
# Credits
This list is not complete at all:
* @Subv, for the process patch that used to be used in Luma3DS which I modified to assist me in PXI sysmodule reverse-engineering
* @yifanlu, for the work his own work on loader
* @Mrrraou, for intensive testing back in June/July 2016
* @jackron, for makerom support and help
* Many #Cakey and #3dsdev folks I haven't mentioned here, etc.

123
sysmodules/pxi/pxi.rsf Normal file
View File

@@ -0,0 +1,123 @@
BasicInfo:
Title : pxi
CompanyCode : "00"
ProductCode : lennybuilder
ContentType : Application
Logo : None
TitleInfo:
UniqueId : 0x14
Category : Base
Version : 2
Option:
UseOnSD : false
FreeProductCode : true # Removes limitations on ProductCode
MediaFootPadding : false # If true CCI files are created with padding
EnableCrypt : false # Enables encryption for NCCH and CIA
EnableCompress : true # Compresses exefs code
AccessControlInfo:
IdealProcessor : 1
AffinityMask : 3
Priority : 13
DisableDebug : false
EnableForceDebug : false
CanWriteSharedPage : false
CanUsePrivilegedPriority : false
CanUseNonAlphabetAndNumber : false
PermitMainFunctionArgument : false
CanShareDeviceMemory : false
RunnableOnSleep : true
SpecialMemoryArrange : false
ResourceLimitCategory : Other
CoreVersion : 2
DescVersion : 2
MemoryType : Base # Application / System / Base
HandleTableSize: 0
IORegisterMapping:
- 1ec63000
SystemCallAccess:
AcceptSession: 74
ArbitrateAddress: 34
Break: 60
BindInterrupt: 80
CancelTimer: 28
ClearEvent: 25
ClearTimer: 29
CloseHandle: 35
ConnectToPort: 45
ControlMemory: 1
CreateAddressArbiter: 33
CreateEvent: 23
CreateMemoryBlock: 30
CreateMutex: 19
CreatePort: 71
CreateSemaphore: 21
CreateSessionToPort: 72
CreateThread: 8
CreateTimer: 26
DuplicateHandle: 39
ExitProcess: 3
ExitThread: 9
FlushProcessDataCache: 84
GetCurrentProcessorNumber: 17
GetDmaState: 87
GetHandleInfo: 41
GetProcessId: 53
GetProcessIdealProcessor: 6
GetProcessIdOfThread: 54
GetProcessInfo: 43
GetResourceLimit: 56
GetResourceLimitCurrentValues: 58
GetResourceLimitLimitValues: 57
GetSystemInfo: 42
GetSystemTick: 40
GetThreadContext: 59
GetThreadId: 55
GetThreadIdealProcessor: 15
GetThreadInfo: 44
GetThreadPriority: 11
InvalidateProcessDataCache: 82
MapMemoryBlock: 31
OutputDebugString: 61
QueryMemory: 2
ReleaseMutex: 20
ReleaseSemaphore: 22
ReplyAndReceive1: 75
ReplyAndReceive2: 76
ReplyAndReceive3: 77
ReplyAndReceive4: 78
ReplyAndReceive: 79
SendSyncRequest1: 46
SendSyncRequest2: 47
SendSyncRequest3: 48
SendSyncRequest4: 49
SendSyncRequest: 50
SetThreadPriority: 12
SetTimer: 27
SignalEvent: 24
SleepThread: 10
StartInterProcessDma: 85
StopDma: 86
StoreProcessDataCache: 83
UnmapMemoryBlock: 32
UnbindInterrupt: 81
WaitSynchronization1: 36
WaitSynchronizationN: 37
InterruptNumbers:
- 0x50
- 0x51
- 0x52
- 0x53
ServiceAccessControl:
FileSystemAccess:
SystemControlInfo:
SaveDataSize: 0KB # It doesn't use any save data.
RemasterVersion: 0
StackSize: 0x1000

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);