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

@ -47,6 +47,8 @@ dir_k11_extension := k11_extension
dir_sysmodules := sysmodules
dir_loader := $(dir_sysmodules)/loader
dir_rosalina := $(dir_sysmodules)/rosalina
dir_sm := $(dir_sysmodules)/sm
dir_pxi := $(dir_sysmodules)/pxi
dir_build := build
dir_out := out
@ -60,7 +62,7 @@ objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
bundled = $(dir_build)/reboot.bin.o $(dir_build)/emunand.bin.o $(dir_build)/chainloader.bin.o $(dir_build)/arm9_exceptions.bin.o
modules = $(dir_build)/loader.cxi $(dir_build)/rosalina.cxi
modules = $(dir_build)/loader.cxi $(dir_build)/rosalina.cxi $(dir_build)/sm.cxi $(dir_build)/pxi.cxi
define bin2o
bin2s $< | $(AS) -o $(@)
@ -83,6 +85,8 @@ clean:
@$(MAKE) -C $(dir_k11_extension) clean
@$(MAKE) -C $(dir_loader) clean
@$(MAKE) -C $(dir_rosalina) clean
@$(MAKE) -C $(dir_sm) clean
@$(MAKE) -C $(dir_pxi) clean
@rm -rf $(dir_out) $(dir_build)
.PRECIOUS: $(dir_build)/%.bin
@ -93,6 +97,9 @@ clean:
.PHONY: $(dir_k11_extension)
.PHONY: $(dir_loader)
.PHONY: $(dir_rosalina)
.PHONY: $(dir_sm)
.PHONY: $(dir_pxi)
$(dir_out)/$(name)$(revision).7z: all
@mkdir -p "$(@D)"
@ -125,6 +132,14 @@ $(dir_build)/rosalina.cxi: $(dir_rosalina)
@mkdir -p "$(@D)"
@$(MAKE) -C $<
$(dir_build)/sm.cxi: $(dir_sm)
@mkdir -p "$(@D)"
@$(MAKE) -C $<
$(dir_build)/pxi.cxi: $(dir_pxi)
@mkdir -p "$(@D)"
@$(MAKE) -C $<
$(dir_build)/%.bin.o: $(dir_build)/%.bin
@$(bin2o)

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

21
sysmodules/sm/LICENSE Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2017 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/sm/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 := sm
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 -fno-strict-aliasing $(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)

9
sysmodules/sm/README.md Normal file
View File

@ -0,0 +1,9 @@
# 3ds_sm
Open source replacement of the ARM11 SM 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/), build this project and copy sm.cxi to /luma/sysmodules/.
# Credits
Everyone that helped me fix some of stupid bugs I had been making: @fincs, @Hikari-chin, etc.

111
sysmodules/sm/sm.rsf Normal file
View File

@ -0,0 +1,111 @@
BasicInfo:
Title : sm
CompanyCode : "00"
ProductCode : lennybuilder
ContentType : Application
Logo : None
TitleInfo:
UniqueId : 0x10
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 : 20
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: 0x200
IORegisterMapping:
SystemCallAccess:
AcceptSession: 74
ArbitrateAddress: 34
Break: 60
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
CreateSession: 73
CreateThread: 8
CreateTimer: 26
DuplicateHandle: 39
ExitProcess: 3
ExitThread: 9
GetCurrentProcessorNumber: 17
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
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
UnmapMemoryBlock: 32
WaitSynchronization1: 36
WaitSynchronizationN: 37
InterruptNumbers:
ServiceAccessControl:
FileSystemAccess:
SystemControlInfo:
SaveDataSize: 0KB # It doesn't use any save data.
RemasterVersion: 0
StackSize: 0x1000

View File

@ -0,0 +1,51 @@
/*
common.h
(c) TuxSH, 2017
This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for details).
*/
#pragma once
#include <3ds.h>
#include "memory.h"
#define IS_PRE_7X (osGetFirmVersion() < SYSTEM_VERSION(2, 39, 4))
#define IS_PRE_93 (osGetFirmVersion() < SYSTEM_VERSION(2, 48, 3))
extern u32 nbSection0Modules;
extern Handle resumeGetServiceHandleOrPortRegisteredSemaphore;
struct SessionDataList;
typedef struct SessionData
{
struct SessionData *prev, *next;
struct SessionDataList *parent;
u32 pid;
u32 replayCmdbuf[4];
Handle busyClientPortHandle;
Handle handle;
bool isSrvPm;
} SessionData;
typedef struct SessionDataList
{
SessionData *first, *last;
} SessionDataList;
extern SessionDataList sessionDataInUseList, freeSessionDataList;
extern SessionDataList sessionDataWaitingForServiceOrPortRegisterList, sessionDataToWakeUpAfterServiceOrPortRegisterList;
extern SessionDataList sessionDataWaitingPortReadyList;
static inline void panic(void)
{
svcBreak(USERBREAK_PANIC);
for(;;) svcSleepThread(0);
}
static inline void assertSuccess(Result res)
{
if(R_FAILED(res))
panic();
}

106
sysmodules/sm/source/list.c Normal file
View File

@ -0,0 +1,106 @@
/*
list.c
(c) TuxSH, 2017
This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for details).
*/
#include "list.h"
#include "memory.h"
#include "common.h"
struct ListBase;
typedef struct ListNodeBase
{
struct ListNodeBase *prev, *next;
struct ListBase *parent;
} ListNodeBase;
typedef struct ListBase
{
ListNodeBase *first, *last;
} ListBase;
void buildList(void *list, void *pool, u32 nb, u32 elementSize)
{
ListBase *listB = (ListBase *)list;
for(u32 i = 0; i < nb; i++)
{
ListNodeBase *node = (ListNodeBase *)((u8 *)pool + i * elementSize);
node->prev = i == 0 ? NULL : (ListNodeBase *)((u8 *)pool + (i - 1) * elementSize);
node->next = i == (nb - 1) ? NULL : (ListNodeBase *)((u8 *)pool + (i + 1) * elementSize);
node->parent = list;
}
listB->first = (ListNodeBase *)pool;
listB->last = (ListNodeBase *)((u8 *)pool + (nb - 1) * elementSize);
}
void moveNode(void *node, void *dst, bool back)
{
ListNodeBase *nodeB = (ListNodeBase *)node;
ListBase *dstB = (ListBase *)dst;
ListBase *srcB = nodeB->parent;
if(dstB == srcB)
return;
// Remove the node in the source list
if(nodeB->prev != NULL)
nodeB->prev->next = nodeB->next;
if(nodeB->next != NULL)
nodeB->next->prev = nodeB->prev;
// Update the source list if needed
if(nodeB == srcB->first)
srcB->first = nodeB->next;
if(nodeB == srcB->last)
srcB->last = nodeB->prev;
// Insert the node in the destination list
if(back)
{
if(dstB->last != NULL)
dstB->last->next = nodeB;
nodeB->prev = dstB->last;
nodeB->next = NULL;
dstB->last = nodeB;
}
else
{
if(dstB->first != NULL)
dstB->first->prev = nodeB;
nodeB->next = dstB->first;
nodeB->prev = NULL;
dstB->first = nodeB;
}
// Normalize the destination list
if(dstB->first != NULL && dstB->last == NULL)
dstB->last = dstB->first;
else if(dstB->first == NULL && dstB->last != NULL)
dstB->first = dstB->last;
nodeB->parent = dstB;
}
void *allocateNode(void *inUseList, void *freeList, u32 elementSize, bool back)
{
ListBase *freeListB = (ListBase *)freeList;
if(freeListB->first == NULL)
panic();
ListNodeBase *node = freeListB->first;
ListNodeBase nodeBk = *node;
memset(node, 0, elementSize);
*node = nodeBk;
moveNode(node, inUseList, back);
return node;
}

View File

@ -0,0 +1,14 @@
/*
list.h
(c) TuxSH, 2017
This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for details).
*/
#pragma once
#include <3ds/types.h>
void buildList(void *list, void *pool, u32 nb, u32 elementSize);
void moveNode(void *node, void *dst, bool back);
void *allocateNode(void *inUseList, void *freeList, u32 elementSize, bool back);

299
sysmodules/sm/source/main.c Normal file
View File

@ -0,0 +1,299 @@
/*
main.c
(c) TuxSH, 2017
This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for details).
*/
#include "common.h"
#include "memory.h"
#include "services.h"
#include "processes.h"
#include "srv.h"
#include "srv_pm.h"
#include "list.h"
extern u32 __ctru_heap;
extern u32 __ctru_linear_heap;
extern char (*serviceAccessListBuffers)[34][8];
u32 __ctru_heap_size = 0x4000;
u32 __ctru_linear_heap_size = 0;
u32 nbSection0Modules;
Handle resumeGetServiceHandleOrPortRegisteredSemaphore;
SessionDataList sessionDataInUseList = {NULL, NULL}, freeSessionDataList = {NULL, NULL};
SessionDataList sessionDataWaitingForServiceOrPortRegisterList = {NULL, NULL}, sessionDataToWakeUpAfterServiceOrPortRegisterList = {NULL, NULL};
SessionDataList sessionDataWaitingPortReadyList = {NULL, NULL};
static SessionData sessionDataPool[76];
static ProcessData processDataPool[64];
static u8 ALIGN(4) serviceAccessListStaticBuffer[0x110];
void __appInit(void)
{
s64 out;
u32 *staticBuffers = getThreadStaticBuffers();
staticBuffers[0] = IPC_Desc_StaticBuffer(0x110, 0);
staticBuffers[1] = (u32)serviceAccessListStaticBuffer;
svcGetSystemInfo(&out, 26, 0);
nbSection0Modules = out;
assertSuccess(svcCreateSemaphore(&resumeGetServiceHandleOrPortRegisteredSemaphore, 0, 64));
serviceAccessListBuffers = (char (*)[34][8])__ctru_heap;
buildList(&freeSessionDataList, sessionDataPool, sizeof(sessionDataPool) / sizeof(SessionData), sizeof(SessionData));
buildList(&freeProcessDataList, processDataPool, sizeof(processDataPool) / sizeof(ProcessData), sizeof(ProcessData));
}
// this is called after main exits
void __appExit(void){}
void __system_allocateHeaps(void)
{
u32 tmp = 0;
// Allocate the application heap
__ctru_heap = 0x08000000;
svcControlMemory(&tmp, __ctru_heap, 0x0, __ctru_heap_size, MEMOP_ALLOC, MEMPERM_READ | MEMPERM_WRITE);
__ctru_linear_heap = 0;
}
void __system_initSyscalls(void){}
Result __sync_init(void);
Result __sync_fini(void);
void __ctru_exit(void){}
void initSystem(void)
{
__sync_init();
__system_allocateHeaps();
__appInit();
}
int main(void)
{
Result res;
u32 *cmdbuf = getThreadCommandBuffer();
u32 nbHandles = 3, nbSessions = 0, nbSrvPmSessions = 0;
Handle clientPortDummy;
Handle srvPort, srvPmPort;
Handle handles[0xE3] = { 0 };
Handle replyTarget = 0;
u32 smPid;
SessionData *sessionData;
assertSuccess(svcGetProcessId(&smPid, CUR_PROCESS_HANDLE));
assertSuccess(svcCreatePort(&srvPort, &clientPortDummy, "srv:", 64));
if(IS_PRE_7X)
assertSuccess(svcCreatePort(&srvPmPort, &clientPortDummy, "srv:pm", 64));
else
assertSuccess(doRegisterService(smPid, &srvPmPort, "srv:pm", 6, 64));
handles[0] = resumeGetServiceHandleOrPortRegisteredSemaphore;
handles[1] = srvPort;
handles[2] = srvPmPort;
for(;;)
{
s32 id;
if(replyTarget == 0)
cmdbuf[0] = 0xFFFF0000; // Kernel11
res = svcReplyAndReceive(&id, handles, nbHandles, replyTarget);
if(res == (Result)0xC920181A) // unreachable remote
{
// Note: if a process has ended, pm will call UnregisterProcess on it
if(id < 0)
{
for(id = 3; (u32)id < nbHandles && handles[id] != replyTarget; id++);
if((u32)id >= nbHandles)
panic();
}
if(id < 3)
panic();
else if((u32)id < 3 + nbSessions) // Session closed
{
sessionData = NULL;
for(u32 i = 0; i < sizeof(sessionDataPool) / sizeof(SessionData); i++)
{
if(sessionDataPool[i].handle == handles[id])
sessionData = &sessionDataPool[i];
}
if(sessionData != NULL)
{
if(sessionData->busyClientPortHandle != 0) // remove unreferenced client port handles from array
{
u32 refCount = 0;
for(u32 i = 0; i < sizeof(sessionDataPool) / sizeof(SessionData); i++)
{
if(sessionDataPool[i].busyClientPortHandle == sessionData->busyClientPortHandle)
++refCount;
}
if(refCount <= 1)
{
u32 i;
for(i = 3 + nbSessions; i < nbHandles && handles[i] == sessionData->busyClientPortHandle; i++);
if(i < nbHandles)
handles[i] = handles[--nbHandles];
}
}
--nbHandles;
for(u32 i = (u32)id; i < nbHandles; i++)
handles[i] = handles[i + 1];
--nbSessions;
svcCloseHandle(sessionData->handle);
if(sessionData->isSrvPm)
--nbSrvPmSessions;
moveNode(sessionData, &freeSessionDataList, false);
}
else
panic();
}
else // Port closed
{
SessionData *nextSessionData = NULL;
// Update the command postponing reason accordingly
for(sessionData = sessionDataWaitingPortReadyList.first; sessionData != NULL; sessionData = nextSessionData)
{
nextSessionData = sessionData->next;
if(sessionData->busyClientPortHandle == handles[id])
{
sessionData->replayCmdbuf[1] = 0xD0406401; // unregistered service or named port
moveNode(sessionData, &sessionDataWaitingForServiceOrPortRegisterList, true);
svcCloseHandle(handles[id]);
handles[id] = handles[--nbHandles];
sessionData->busyClientPortHandle = 0;
}
}
}
replyTarget = 0;
}
else if(R_FAILED(res))
panic();
else
{
replyTarget = 0;
if(id == 1) // New srv: session
{
Handle session;
assertSuccess(svcAcceptSession(&session, srvPort));
sessionData = (SessionData *)allocateNode(&sessionDataInUseList, &freeSessionDataList, sizeof(SessionData), false);
sessionData->pid = (u32)-1;
sessionData->handle = session;
for(u32 i = nbHandles; i > 3 + nbSessions; i--)
handles[i] = handles[i - 1];
handles[3 + nbSessions++] = session;
++nbHandles;
}
else if(id == 2) // New srv:pm session
{
Handle session;
if(!IS_PRE_7X && nbSrvPmSessions >= 1)
panic();
assertSuccess(svcAcceptSession(&session, srvPmPort));
sessionData = (SessionData *)allocateNode(&sessionDataInUseList, &freeSessionDataList, sizeof(SessionData), false);
sessionData->pid = (u32)-1;
sessionData->handle = session;
sessionData->isSrvPm = true;
for(u32 i = nbHandles; i > 3 + nbSessions; i--)
handles[i] = handles[i - 1];
handles[3 + nbSessions++] = session;
++nbHandles;
++nbSrvPmSessions;
}
else
{
if(id == 0) // Resume SRV:GetServiceHandle or GetPort due to service or named port not registered
{
if(sessionDataToWakeUpAfterServiceOrPortRegisterList.first == NULL)
panic();
sessionData = sessionDataToWakeUpAfterServiceOrPortRegisterList.first;
moveNode(sessionData, &sessionDataInUseList, false);
for(u32 i = nbHandles; i > 3 + nbSessions; i--)
handles[i] = handles[i - 1];
handles[3 + nbSessions++] = sessionData->handle;
++nbHandles;
if(sessionData->isSrvPm)
++nbSrvPmSessions;
memcpy(cmdbuf, sessionData->replayCmdbuf, 16);
}
else if((u32)id >= 3 + nbSessions) // Resume SRV:GetServiceHandle if service was full
{
SessionData *sTmp;
for(sessionData = sessionDataWaitingPortReadyList.first; sessionData != NULL && sessionData->busyClientPortHandle != handles[id];
sessionData = sessionData->next);
if(sessionData == NULL)
panic();
moveNode(sessionData, &sessionDataInUseList, false);
for(sTmp = sessionDataWaitingPortReadyList.first; sTmp != NULL && sTmp->busyClientPortHandle != handles[id]; sTmp = sTmp->next);
if(sTmp == NULL)
handles[id] = handles[--nbHandles];
for(u32 i = nbHandles + 1; i > 3 + nbSessions; i--)
handles[i] = handles[i - 1];
handles[3 + nbSessions++] = sessionData->handle;
++nbHandles;
if(sessionData->isSrvPm)
++nbSrvPmSessions;
memcpy(cmdbuf, sessionData->replayCmdbuf, 16);
sessionData->busyClientPortHandle = 0;
}
else
{
for(sessionData = sessionDataInUseList.first; sessionData != NULL && sessionData->handle != handles[id]; sessionData = sessionData->next);
if(sessionData == NULL)
panic();
}
for(id = 3; (u32)id < 3 + nbSessions && handles[id] != sessionData->handle; id++);
if((u32)id >= 3 + nbSessions)
panic();
res = sessionData->isSrvPm ? srvPmHandleCommands(sessionData) : srvHandleCommands(sessionData);
if(R_MODULE(res) == RM_SRV && R_SUMMARY(res) == RS_WOULDBLOCK)
{
SessionDataList *dstList = NULL;
if(res == (Result)0xD0406401) // service or named port not registered yet
dstList = &sessionDataWaitingForServiceOrPortRegisterList;
else if(res == (Result)0xD0406402) // service full
{
u32 i;
dstList = &sessionDataWaitingPortReadyList;
for(i = 3 + nbSessions; i < nbHandles && handles[i] != sessionData->busyClientPortHandle; i++);
if(i >= nbHandles)
handles[nbHandles++] = sessionData->busyClientPortHandle;
}
else
panic();
--nbHandles;
for(u32 i = (u32)id; i < nbHandles; i++)
handles[i] = handles[i + 1];
--nbSessions;
if(sessionData->isSrvPm)
--nbSrvPmSessions;
moveNode(sessionData, dstList, true);
}
else
replyTarget = sessionData->handle;
}
}
}
return 0;
}

View File

@ -0,0 +1,27 @@
/*
memory.c
(c) TuxSH, 2017
This is part of 3ds_sm, 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];
}*/
s32 strnlen(const char *string, s32 maxlen)
{
s32 size;
for(size = 0; size < maxlen && *string; string++, size++);
return size;
}

View File

@ -0,0 +1,18 @@
/*
memory.h
(c) TuxSH, 2017
This is part of 3ds_sm, 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);
#define memcpy __builtin_memcpy
#define memset __builtin_memset
#define strncmp __builtin_strncmp
#define strncpy __builtin_strncpy
s32 strnlen(const char *string, s32 maxlen);

View File

@ -0,0 +1,191 @@
/*
notifications.c
(c) TuxSH, 2017
This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for details).
*/
#include "notifications.h"
#include "processes.h"
static bool doPublishNotification(ProcessData *processData, u32 notificationId, u32 flags)
{
if((flags & 1) && processData->nbPendingNotifications != 0) // only send if not already pending
{
for(u16 i = 0; i < processData->nbPendingNotifications; i++)
{
if(processData->pendingNotifications[(processData->pendingNotificationIndex + i) % 16] == notificationId)
return true;
}
}
if(processData->nbPendingNotifications < 0x10)
{
s32 count;
processData->pendingNotifications[processData->pendingNotificationIndex] = notificationId;
processData->pendingNotificationIndex = (processData->pendingNotificationIndex + 1) % 16;
++processData->nbPendingNotifications;
assertSuccess(svcReleaseSemaphore(&count, processData->notificationSemaphore, 1));
return true;
}
else
return (flags & 2) != 0;
}
Result EnableNotification(SessionData *sessionData, Handle *notificationSemaphore)
{
ProcessData *processData = findProcessData(sessionData->pid);
if(processData == NULL)
{
// Section 0 modules have access to all services, so we need to register them here for notifications
if(sessionData->pid < nbSection0Modules)
processData = doRegisterProcess(sessionData->pid, NULL, 0);
else
return 0xD8806404;
}
processData->notificationEnabled = true;
*notificationSemaphore = processData->notificationSemaphore;
return 0;
}
Result Subscribe(SessionData *sessionData, u32 notificationId)
{
ProcessData *processData = findProcessData(sessionData->pid);
if(processData == NULL || !processData->notificationEnabled)
return 0xD8806404;
for(u16 i = 0; i < processData->nbSubscribed; i++)
{
if(processData->subscribedNotifications[i] == notificationId)
return 0xD9006403;
}
if(processData->nbSubscribed < 0x11)
{
processData->subscribedNotifications[processData->nbSubscribed++] = notificationId;
return 0;
}
else
return 0xD9006405;
}
Result Unsubscribe(SessionData *sessionData, u32 notificationId)
{
ProcessData *processData = findProcessData(sessionData->pid);
if(processData == NULL || !processData->notificationEnabled)
return 0xD8806404;
u16 i;
for(i = 0; i < processData->nbSubscribed && processData->subscribedNotifications[i] != notificationId; i++);
if(i == processData->nbSubscribed)
return 0xD8806404;
else
{
processData->subscribedNotifications[i] = processData->subscribedNotifications[--processData->nbSubscribed];
return 0;
}
}
Result ReceiveNotification(SessionData *sessionData, u32 *notificationId)
{
ProcessData *processData = findProcessData(sessionData->pid);
if(processData == NULL || !processData->notificationEnabled || processData->nbPendingNotifications == 0)
{
if(processData->nbPendingNotifications)
*notificationId = 0;
return 0xD8806404;
}
else
{
--processData->nbPendingNotifications;
*notificationId = processData->pendingNotifications[processData->receivedNotificationIndex];
processData->receivedNotificationIndex = (processData->receivedNotificationIndex + 1) % 16;
return 0;
}
}
Result PublishToSubscriber(u32 notificationId, u32 flags)
{
for(ProcessData *node = processDataInUseList.first; node != NULL; node = node->next)
{
if(!node->notificationEnabled)
continue;
u16 i;
for(i = 0; i < node->nbSubscribed && node->subscribedNotifications[i] != notificationId; i++);
if(i >= node->nbSubscribed)
continue;
if(!doPublishNotification(node, notificationId, flags))
return 0xD8606408;
}
return 0;
}
Result PublishAndGetSubscriber(u32 *pidCount, u32 *pidList, u32 notificationId, u32 flags)
{
u32 nb = 0;
for(ProcessData *node = processDataInUseList.first; node != NULL; node = node->next)
{
if(!node->notificationEnabled)
continue;
u16 i;
for(i = 0; i < node->nbSubscribed && node->subscribedNotifications[i] != notificationId; i++);
if(i >= node->nbSubscribed)
continue;
if(!doPublishNotification(node, notificationId, flags))
return 0xD8606408;
else if(pidList != NULL && nb < 60)
pidList[nb++] = node->pid;
}
if(pidCount != NULL)
*pidCount = nb;
return 0;
}
Result PublishToProcess(Handle process, u32 notificationId)
{
u32 pid;
Result res = svcGetProcessId(&pid, process);
if(R_FAILED(res))
return res;
ProcessData *processData = findProcessData(pid);
if(processData == NULL || !processData->notificationEnabled)
res = 0xD8806404;
else if(!doPublishNotification(processData, notificationId, 0))
res = 0xD8606408;
else
res = 0;
svcCloseHandle(process);
return res;
}
Result PublishToAll(u32 notificationId)
{
for(ProcessData *node = processDataInUseList.first; node != NULL; node = node->next)
{
if(!node->notificationEnabled)
continue;
else if(!doPublishNotification(node, notificationId, 0))
return 0xD8606408;
}
return 0;
}

View File

@ -0,0 +1,19 @@
/*
notifications.h
(c) TuxSH, 2017
This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for details).
*/
#pragma once
#include "common.h"
Result EnableNotification(SessionData *sessionData, Handle *notificationSemaphore);
Result Subscribe(SessionData *sessionData, u32 notificationId);
Result Unsubscribe(SessionData *sessionData, u32 notificationId);
Result ReceiveNotification(SessionData *sessionData, u32 *notificationId);
Result PublishToSubscriber(u32 notificationId, u32 flags);
Result PublishAndGetSubscriber(u32 *pidCount, u32 *pidList, u32 notificationId, u32 flags);
Result PublishToProcess(Handle process, u32 notificationId);
Result PublishToAll(u32 notificationId);

View File

@ -0,0 +1,88 @@
/*
processes.c
(c) TuxSH, 2017
This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for details).
*/
#include "list.h"
#include "processes.h"
#include "services.h"
ProcessDataList processDataInUseList = { NULL, NULL }, freeProcessDataList = { NULL, NULL };
char (*serviceAccessListBuffers)[34][8];
// The kernel limits the number of processes to 47 anyways...
static u64 freeServiceAccessListBuffersIds = (1ULL << 59) - 1;
ProcessData *findProcessData(u32 pid)
{
for(ProcessData *node = processDataInUseList.first; node != NULL; node = node->next)
{
if(node->pid == pid)
return node;
}
return NULL;
}
ProcessData *doRegisterProcess(u32 pid, char (*serviceAccessList)[8], u32 serviceAccessListSize)
{
ProcessData *processData = (ProcessData *)allocateNode(&processDataInUseList, &freeProcessDataList, sizeof(ProcessData), false);
if(serviceAccessListSize != 0)
{
s32 bufferId = 63 - __builtin_clzll(freeServiceAccessListBuffersIds);
if(bufferId == -1)
panic();
else
{
freeServiceAccessListBuffersIds &= ~(1ULL << bufferId);
processData->serviceAccessList = serviceAccessListBuffers[bufferId];
processData->serviceAccessListSize = serviceAccessListSize;
memcpy(processData->serviceAccessList, serviceAccessList, serviceAccessListSize);
}
}
assertSuccess(svcCreateSemaphore(&processData->notificationSemaphore, 0, 0x10));
processData->pid = pid;
return processData;
}
Result RegisterProcess(u32 pid, char (*serviceAccessList)[8], u32 serviceAccessListSize)
{
serviceAccessListSize = serviceAccessListSize - (serviceAccessListSize % 8);
if(findProcessData(pid) != NULL)
return 0xD9006403;
else if(serviceAccessListSize > 8 * (IS_PRE_93 ? 32 : 34))
return 0xD9006405;
else
{
doRegisterProcess(pid, serviceAccessList, serviceAccessListSize);
return 0;
}
}
Result UnregisterProcess(u32 pid)
{
ProcessData *processData = findProcessData(pid);
if(processData == NULL)
return 0xD8806404;
svcCloseHandle(processData->notificationSemaphore);
// Unregister the services registered by the process
for(u32 i = 0; i < nbServices; i++)
{
if(servicesInfo[i].pid == pid)
{
svcCloseHandle(servicesInfo[i].clientPort);
servicesInfo[i] = servicesInfo[--nbServices];
}
}
freeServiceAccessListBuffersIds |= 1ULL << (u32)((processData->serviceAccessList - serviceAccessListBuffers[0]) / 34);
moveNode(processData, &freeProcessDataList, false);
return 0;
}

View File

@ -0,0 +1,48 @@
/*
processes.h
(c) TuxSH, 2017
This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for details).
*/
#pragma once
#include "common.h"
struct ProcessDataList;
typedef struct ProcessData
{
struct ProcessData *prev, *next;
struct ProcessDataList *parent;
u32 pid;
Handle notificationSemaphore;
char (*serviceAccessList)[8];
u32 serviceAccessListSize;
bool notificationEnabled;
// Circular buffer
u16 receivedNotificationIndex;
u16 pendingNotificationIndex;
u16 nbPendingNotifications;
u32 pendingNotifications[16];
u16 nbSubscribed;
u32 subscribedNotifications[17];
} ProcessData;
typedef struct ProcessDataList
{
ProcessData *first, *last;
} ProcessDataList;
extern ProcessDataList processDataInUseList, freeProcessDataList;
ProcessData *findProcessData(u32 pid);
ProcessData *doRegisterProcess(u32 pid, char (*serviceAccessList)[8], u32 serviceAccessListSize);
Result RegisterProcess(u32 pid, char (*serviceAccessList)[8], u32 serviceAccessListSize);
Result UnregisterProcess(u32 pid);

View File

@ -0,0 +1,217 @@
/*
services.c
(c) TuxSH, 2017
This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for details).
*/
#include "services.h"
#include "processes.h"
#include "memory.h"
#include "list.h"
ServiceInfo servicesInfo[0xA0] = { 0 };
u32 nbServices = 0; // including "ports" registered with getPort
static Result checkServiceName(const char *name, s32 nameSize)
{
if(nameSize <= 0 || nameSize > 8)
return 0xD9006405;
else if(strnlen(name, nameSize) < nameSize)
return 0xD9006407;
else
return 0;
}
static inline bool areServiceNamesEqual(const char *name, const char *name2, s32 nameSize)
{
return strncmp(name, name2, nameSize) == 0 && (nameSize == 8 || name[nameSize] == 0);
}
static s32 findServicePortByName(bool isNamedPort, const char *name, s32 nameSize)
{
ServiceInfo *info;
for(info = servicesInfo; info < servicesInfo + nbServices && (info->isNamedPort != isNamedPort || !areServiceNamesEqual(info->name, name, nameSize)); info++);
return info >= servicesInfo + nbServices ? -1 : info - servicesInfo;
}
static bool checkServiceAccess(SessionData *sessionData, const char *name, s32 nameSize)
{
if(sessionData->pid < nbSection0Modules)
return true;
ProcessData *processData = findProcessData(sessionData->pid);
if(processData == NULL)
return false;
for(u32 i = 0; i < processData->serviceAccessListSize; i++)
{
if(areServiceNamesEqual(processData->serviceAccessList[i], name, nameSize))
return true;
}
return false;
}
static Result doRegisterServiceOrPort(u32 pid, Handle *serverPort, Handle clientPort, const char *name, s32 nameSize, s32 maxSessions, bool isNamedPort)
{
Result res = checkServiceName(name, nameSize);
Handle portServer, portClient;
if(R_FAILED(res))
return res;
else if(findServicePortByName(isNamedPort, name, nameSize) != -1)
return 0xD9001BFC;
if(nbServices >= 0xA0)
return 0xD86067F3;
if(!isNamedPort)
{
res = svcCreatePort(&portServer, &portClient, NULL, maxSessions);
if(R_FAILED(res))
return 0xD9001BFC;
}
else
portClient = clientPort;
ServiceInfo *serviceInfo = &servicesInfo[nbServices++];
strncpy(serviceInfo->name, name, 8);
serviceInfo->pid = pid;
serviceInfo->clientPort = portClient;
serviceInfo->isNamedPort = isNamedPort;
SessionData *nextSessionData;
s32 n = 0;
for(SessionData *node = sessionDataWaitingForServiceOrPortRegisterList.first; node != NULL; node = nextSessionData)
{
nextSessionData = node->next;
if((node->replayCmdbuf[0] & 0xF0000) == (!isNamedPort ? 0x50000 : 0x80000) &&
areServiceNamesEqual((const char *)(node->replayCmdbuf + 1), name, (s32)node->replayCmdbuf[3]))
{
moveNode(node, &sessionDataToWakeUpAfterServiceOrPortRegisterList, true);
++n;
}
}
if(n > 0)
{
s32 count;
assertSuccess(svcReleaseSemaphore(&count, resumeGetServiceHandleOrPortRegisteredSemaphore, n));
}
if(!isNamedPort)
*serverPort = portServer;
return res;
}
Result doRegisterService(u32 pid, Handle *serverPort, const char *name, s32 nameSize, s32 maxSessions)
{
return doRegisterServiceOrPort(pid, serverPort, 0, name, nameSize, maxSessions, false);
}
Result RegisterService(SessionData *sessionData, Handle *serverPort, const char *name, s32 nameSize, s32 maxSessions)
{
return doRegisterService(sessionData->pid, serverPort, name, nameSize, maxSessions);
}
Result RegisterPort(SessionData *sessionData, Handle clientPort, const char *name, s32 nameSize)
{
return doRegisterServiceOrPort(sessionData->pid, NULL, clientPort, name, nameSize, -1, true);
}
static Result UnregisterServiceOrPort(SessionData *sessionData, const char *name, s32 nameSize, bool isNamedPort)
{
Result res = checkServiceName(name, nameSize);
s32 serviceId;
if(R_FAILED(res))
return res;
serviceId = findServicePortByName(isNamedPort, name, nameSize);
if(serviceId == -1)
return 0xD8801BFA;
else if(servicesInfo[serviceId].pid != sessionData->pid)
return 0xD8E06406;
else
{
svcCloseHandle(servicesInfo[serviceId].clientPort);
servicesInfo[serviceId] = servicesInfo[--nbServices];
return 0;
}
}
Result UnregisterService(SessionData *sessionData, const char *name, s32 nameSize)
{
return UnregisterServiceOrPort(sessionData, name, nameSize, false);
}
Result UnregisterPort(SessionData *sessionData, const char *name, s32 nameSize)
{
return UnregisterServiceOrPort(sessionData, name, nameSize, true);
}
Result IsServiceRegistered(SessionData *sessionData, bool *isRegistered, const char *name, s32 nameSize)
{
Result res = checkServiceName(name, nameSize);
if(R_FAILED(res))
return res;
else if(!checkServiceAccess(sessionData, name, nameSize))
return 0xD8E06406;
else
{
*isRegistered = findServicePortByName(false, name, nameSize) != -1;
return 0;
}
}
Result GetServiceHandle(SessionData *sessionData, Handle *session, const char *name, s32 nameSize, u32 flags)
{
Result res = checkServiceName(name, nameSize);
s32 serviceId;
if(R_FAILED(res))
return res;
else if(!checkServiceAccess(sessionData, name, nameSize))
return 0xD8E06406;
serviceId = findServicePortByName(false, name, nameSize);
if(serviceId == -1)
return 0xD0406401;
else
{
Handle port = servicesInfo[serviceId].clientPort;
res = svcCreateSessionToPort(session, port);
if(res == (Result)0xD0401834 && !(flags & 1))
{
sessionData->busyClientPortHandle = port;
return 0xD0406402;
}
return 0;
}
}
Result GetPort(SessionData *sessionData, Handle *port, const char *name, s32 nameSize, u8 flags)
{
Result res = checkServiceName(name, nameSize);
s32 serviceId;
if(R_FAILED(res))
return res;
else if(!checkServiceAccess(sessionData, name, nameSize))
return 0xD8E06406;
serviceId = findServicePortByName(true, name, nameSize);
if(serviceId == -1)
return flags != 0 ? 0xD0406401 : 0xD8801BFA;
else if(flags == 0)
*port = servicesInfo[serviceId].clientPort;
return 0;
}

View File

@ -0,0 +1,32 @@
/*
services.h
(c) TuxSH, 2017
This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for details).
*/
#pragma once
#include <3ds.h>
#include "common.h"
typedef struct ServiceInfo
{
char name[8];
Handle clientPort;
u32 pid;
bool isNamedPort;
} ServiceInfo;
extern ServiceInfo servicesInfo[0xA0];
extern u32 nbServices;
Result doRegisterService(u32 pid, Handle *serverPort, const char *name, s32 nameSize, s32 maxSessions);
Result RegisterService(SessionData *sessionData, Handle *serverPort, const char *name, s32 nameSize, s32 maxSessions);
Result RegisterPort(SessionData *sessionData, Handle clientPort, const char *name, s32 nameSize);
Result UnregisterService(SessionData *sessionData, const char *name, s32 nameSize);
Result UnregisterPort(SessionData *sessionData, const char *name, s32 nameSize);
Result IsServiceRegistered(SessionData *SessionData, bool *isRegistered, const char *name, s32 nameSize);
Result GetServiceHandle(SessionData *sessionData, Handle *session, const char *name, s32 nameSize, u32 flags);
Result GetPort(SessionData *sessionData, Handle *port, const char *name, s32 nameSize, u8 flags);

181
sysmodules/sm/source/srv.c Normal file
View File

@ -0,0 +1,181 @@
/*
srv.h
(c) TuxSH, 2017
This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for details).
*/
#include "srv.h"
#include "services.h"
#include "notifications.h"
#include "processes.h"
Result srvHandleCommands(SessionData *sessionData)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
switch(cmdbuf[0] >> 16)
{
case 1: // RegisterClient
{
if(cmdbuf[0] == IPC_MakeHeader(1, 0, 2) && cmdbuf[1] == IPC_Desc_CurProcessHandle())
{
sessionData->pid = cmdbuf[2];
cmdbuf[0] = IPC_MakeHeader(1, 1, 0);
cmdbuf[1] = 0;
}
else
goto invalid_command;
break;
}
case 2: // EnableNotification
{
Handle notificationSemaphore = 0;
res = EnableNotification(sessionData, &notificationSemaphore);
cmdbuf[0] = IPC_MakeHeader(2, 1, 2);
cmdbuf[1] = (u32)res;
cmdbuf[2] = IPC_Desc_SharedHandles(1);
cmdbuf[3] = notificationSemaphore;
break;
}
case 3: // RegisterService
{
Handle serverPort = 0;
res = RegisterService(sessionData, &serverPort, (const char *)(cmdbuf + 1), (s32)cmdbuf[3], (s32)cmdbuf[4]);
cmdbuf[0] = IPC_MakeHeader(3, 1, 2);
cmdbuf[1] = (u32)res;
cmdbuf[2] = IPC_Desc_MoveHandles(1);
cmdbuf[3] = serverPort;
break;
}
case 4: // UnregisterService
{
res = UnregisterService(sessionData, (const char *)(cmdbuf + 1), (s32)cmdbuf[3]);
cmdbuf[0] = IPC_MakeHeader(4, 1, 0);
cmdbuf[1] = (u32)res;
break;
}
case 5: // GetServiceHandle
{
Handle session = 0;
res = GetServiceHandle(sessionData, &session, (const char *)(cmdbuf + 1), (s32)cmdbuf[3], cmdbuf[4]);
if(R_MODULE(res) == RM_SRV && R_SUMMARY(res) == RS_WOULDBLOCK)
memcpy(sessionData->replayCmdbuf, cmdbuf, 16);
else
{
cmdbuf[0] = IPC_MakeHeader(5, 1, 2);
cmdbuf[1] = (u32)res;
cmdbuf[2] = IPC_Desc_MoveHandles(1);
cmdbuf[3] = session;
}
break;
}
case 6: // RegisterPort
{
if(cmdbuf[0] == IPC_MakeHeader(6, 3, 2) && cmdbuf[4] == IPC_Desc_SharedHandles(1))
{
res = RegisterPort(sessionData, (Handle)cmdbuf[5], (const char *)(cmdbuf + 1), (s32)cmdbuf[3]);
cmdbuf[0] = IPC_MakeHeader(6, 1, 0);
cmdbuf[1] = (u32)res;
}
else
goto invalid_command;
break;
}
case 7: // UnregisterPort
{
res = UnregisterPort(sessionData, (const char *)(cmdbuf + 1), (s32)cmdbuf[3]);
cmdbuf[0] = IPC_MakeHeader(7, 1, 0);
cmdbuf[1] = (u32)res;
break;
}
case 8: // GetPort
{
Handle port = 0;
res = GetPort(sessionData, &port, (const char *)(cmdbuf + 1), (s32)cmdbuf[3], (u8)cmdbuf[4]);
if(R_MODULE(res) == RM_SRV && R_SUMMARY(res) == RS_WOULDBLOCK)
memcpy(sessionData->replayCmdbuf, cmdbuf, 16);
else
{
cmdbuf[0] = IPC_MakeHeader(8, 1, 2);
cmdbuf[1] = (u32)res;
cmdbuf[2] = IPC_Desc_SharedHandles(1);
cmdbuf[3] = port;
}
break;
}
case 9: // Subscribe
{
res = Subscribe(sessionData, cmdbuf[1]);
cmdbuf[0] = IPC_MakeHeader(9, 1, 0);
cmdbuf[1] = (u32)res;
break;
}
case 10: // Unsubscribe
{
res = Unsubscribe(sessionData, cmdbuf[1]);
cmdbuf[0] = IPC_MakeHeader(10, 1, 0);
cmdbuf[1] = (u32)res;
break;
}
case 11: // ReceiveNotification
{
u32 notificationId;
res = ReceiveNotification(sessionData, &notificationId);
cmdbuf[0] = IPC_MakeHeader(11, 2, 0);;
cmdbuf[1] = (u32)res;
cmdbuf[2] = notificationId;
break;
}
case 12: // PublishToSubscriber
{
res = PublishToSubscriber(cmdbuf[1], cmdbuf[2]);
cmdbuf[0] = IPC_MakeHeader(12, 1, 0);;
cmdbuf[1] = (u32)res;
break;
}
case 13: // PublishAndGetSubscriber
{
u32 pidCount;
res = PublishAndGetSubscriber(&pidCount, cmdbuf + 3, cmdbuf[1], 0);
cmdbuf[0] = IPC_MakeHeader(13, 62, 0);
cmdbuf[1] = (u32)res;
cmdbuf[2] = pidCount;
break;
}
case 14: // IsServiceRegistered
{
bool isRegistered;
res = IsServiceRegistered(sessionData, &isRegistered, (const char *)(cmdbuf + 1), (s32)cmdbuf[3]);
cmdbuf[0] = IPC_MakeHeader(14, 2, 0);;
cmdbuf[1] = (u32)res;
cmdbuf[2] = isRegistered ? 1 : 0;
break;
}
default:
goto invalid_command;
break;
}
return res;
invalid_command:
cmdbuf[0] = IPC_MakeHeader(0, 1, 0);;
cmdbuf[1] = (u32)0xD9001830;
return 0xD9001830;
}

View File

@ -0,0 +1,12 @@
/*
srv.h
(c) TuxSH, 2017
This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for details).
*/
#pragma once
#include "common.h"
Result srvHandleCommands(SessionData *sessionData);

View File

@ -0,0 +1,81 @@
/*
srv_pm.c
(c) TuxSH, 2017
This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for details).
*/
#include "srv_pm.h"
#include "srv.h"
#include "services.h"
#include "notifications.h"
#include "processes.h"
Result srvPmHandleCommands(SessionData *sessionData)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
u32 mask = (cmdbuf[0] & 0x04000000) >> 16;
if(IS_PRE_7X && mask == 0)
return srvHandleCommands(sessionData);
else if(!IS_PRE_7X && mask != 0)
goto invalid_command;
switch((cmdbuf[0] >> 16) & ~mask)
{
case 1: // PublishToProcess
{
if(cmdbuf[0] == IPC_MakeHeader(mask | 1, 1, 2) && cmdbuf[2] == IPC_Desc_SharedHandles(1))
{
res = PublishToProcess((Handle)cmdbuf[3], cmdbuf[1]);
cmdbuf[0] = IPC_MakeHeader(mask | 1, 1, 0);
cmdbuf[1] = (u32)res;
}
else
goto invalid_command;
break;
}
case 2: // PublishToAll
{
res = PublishToAll(cmdbuf[1]);
cmdbuf[0] = IPC_MakeHeader(mask | 2, 1, 0);
cmdbuf[1] = (u32)res;
break;
}
case 3: // RegisterProcess
{
if(cmdbuf[0] == IPC_MakeHeader(mask | 3, 2, 2) && (cmdbuf[3] & 0x3C0F) == (IPC_Desc_StaticBuffer(0x110, 0) & 0x3C0F))
{
res = RegisterProcess(cmdbuf[1], (char (*)[8])cmdbuf[4], cmdbuf[3] >> 14);
cmdbuf[0] = IPC_MakeHeader(mask | 3, 1, 0);
cmdbuf[1] = (u32)res;
}
else
goto invalid_command;
break;
}
case 4:
{
res = UnregisterProcess(cmdbuf[1]);
cmdbuf[0] = IPC_MakeHeader(mask | 4, 1, 0);
cmdbuf[1] = (u32)res;
break;
}
default:
goto invalid_command;
break;
}
return res;
invalid_command:
cmdbuf[0] = IPC_MakeHeader(0, 1, 0);;
cmdbuf[1] = (u32)0xD900182F;
return 0xD900182F;
}

View File

@ -0,0 +1,12 @@
/*
srv_pm.h
(c) TuxSH, 2017
This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for details).
*/
#pragma once
#include "common.h"
Result srvPmHandleCommands(SessionData *sessionData);