Integrate 3ds_pxi and 3ds_sm
This commit is contained in:
parent
76dde0e6db
commit
8258a98647
17
Makefile
17
Makefile
@ -47,6 +47,8 @@ dir_k11_extension := k11_extension
|
|||||||
dir_sysmodules := sysmodules
|
dir_sysmodules := sysmodules
|
||||||
dir_loader := $(dir_sysmodules)/loader
|
dir_loader := $(dir_sysmodules)/loader
|
||||||
dir_rosalina := $(dir_sysmodules)/rosalina
|
dir_rosalina := $(dir_sysmodules)/rosalina
|
||||||
|
dir_sm := $(dir_sysmodules)/sm
|
||||||
|
dir_pxi := $(dir_sysmodules)/pxi
|
||||||
dir_build := build
|
dir_build := build
|
||||||
dir_out := out
|
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
|
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
|
define bin2o
|
||||||
bin2s $< | $(AS) -o $(@)
|
bin2s $< | $(AS) -o $(@)
|
||||||
@ -83,6 +85,8 @@ clean:
|
|||||||
@$(MAKE) -C $(dir_k11_extension) clean
|
@$(MAKE) -C $(dir_k11_extension) clean
|
||||||
@$(MAKE) -C $(dir_loader) clean
|
@$(MAKE) -C $(dir_loader) clean
|
||||||
@$(MAKE) -C $(dir_rosalina) clean
|
@$(MAKE) -C $(dir_rosalina) clean
|
||||||
|
@$(MAKE) -C $(dir_sm) clean
|
||||||
|
@$(MAKE) -C $(dir_pxi) clean
|
||||||
@rm -rf $(dir_out) $(dir_build)
|
@rm -rf $(dir_out) $(dir_build)
|
||||||
|
|
||||||
.PRECIOUS: $(dir_build)/%.bin
|
.PRECIOUS: $(dir_build)/%.bin
|
||||||
@ -93,6 +97,9 @@ clean:
|
|||||||
.PHONY: $(dir_k11_extension)
|
.PHONY: $(dir_k11_extension)
|
||||||
.PHONY: $(dir_loader)
|
.PHONY: $(dir_loader)
|
||||||
.PHONY: $(dir_rosalina)
|
.PHONY: $(dir_rosalina)
|
||||||
|
.PHONY: $(dir_sm)
|
||||||
|
.PHONY: $(dir_pxi)
|
||||||
|
|
||||||
|
|
||||||
$(dir_out)/$(name)$(revision).7z: all
|
$(dir_out)/$(name)$(revision).7z: all
|
||||||
@mkdir -p "$(@D)"
|
@mkdir -p "$(@D)"
|
||||||
@ -125,6 +132,14 @@ $(dir_build)/rosalina.cxi: $(dir_rosalina)
|
|||||||
@mkdir -p "$(@D)"
|
@mkdir -p "$(@D)"
|
||||||
@$(MAKE) -C $<
|
@$(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
|
$(dir_build)/%.bin.o: $(dir_build)/%.bin
|
||||||
@$(bin2o)
|
@$(bin2o)
|
||||||
|
|
||||||
|
21
sysmodules/pxi/LICENSE
Normal file
21
sysmodules/pxi/LICENSE
Normal 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
52
sysmodules/pxi/Makefile
Normal 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
15
sysmodules/pxi/README.md
Normal 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
123
sysmodules/pxi/pxi.rsf
Normal 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
|
42
sysmodules/pxi/source/MyThread.c
Normal file
42
sysmodules/pxi/source/MyThread.c
Normal 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();
|
||||||
|
}
|
28
sysmodules/pxi/source/MyThread.h
Normal file
28
sysmodules/pxi/source/MyThread.h
Normal 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
139
sysmodules/pxi/source/PXI.c
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
47
sysmodules/pxi/source/PXI.h
Normal file
47
sysmodules/pxi/source/PXI.h
Normal 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);
|
94
sysmodules/pxi/source/common.h
Normal file
94
sysmodules/pxi/source/common.h
Normal 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;
|
||||||
|
}
|
211
sysmodules/pxi/source/main.c
Normal file
211
sysmodules/pxi/source/main.c
Normal 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(¬ificationId));
|
||||||
|
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;
|
||||||
|
}
|
19
sysmodules/pxi/source/memory.c
Normal file
19
sysmodules/pxi/source/memory.c
Normal 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];
|
||||||
|
}
|
13
sysmodules/pxi/source/memory.h
Normal file
13
sysmodules/pxi/source/memory.h
Normal 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);
|
69
sysmodules/pxi/source/receiver.c
Normal file
69
sysmodules/pxi/source/receiver.c
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
13
sysmodules/pxi/source/receiver.h
Normal file
13
sysmodules/pxi/source/receiver.h
Normal 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);
|
300
sysmodules/pxi/source/sender.c
Normal file
300
sysmodules/pxi/source/sender.c
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
15
sysmodules/pxi/source/sender.h
Normal file
15
sysmodules/pxi/source/sender.h
Normal 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
21
sysmodules/sm/LICENSE
Normal 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
52
sysmodules/sm/Makefile
Normal 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
9
sysmodules/sm/README.md
Normal 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
111
sysmodules/sm/sm.rsf
Normal 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
|
51
sysmodules/sm/source/common.h
Normal file
51
sysmodules/sm/source/common.h
Normal 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
106
sysmodules/sm/source/list.c
Normal 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;
|
||||||
|
}
|
14
sysmodules/sm/source/list.h
Normal file
14
sysmodules/sm/source/list.h
Normal 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
299
sysmodules/sm/source/main.c
Normal 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;
|
||||||
|
}
|
27
sysmodules/sm/source/memory.c
Normal file
27
sysmodules/sm/source/memory.c
Normal 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;
|
||||||
|
}
|
18
sysmodules/sm/source/memory.h
Normal file
18
sysmodules/sm/source/memory.h
Normal 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);
|
191
sysmodules/sm/source/notifications.c
Normal file
191
sysmodules/sm/source/notifications.c
Normal 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;
|
||||||
|
}
|
19
sysmodules/sm/source/notifications.h
Normal file
19
sysmodules/sm/source/notifications.h
Normal 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);
|
88
sysmodules/sm/source/processes.c
Normal file
88
sysmodules/sm/source/processes.c
Normal 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;
|
||||||
|
}
|
48
sysmodules/sm/source/processes.h
Normal file
48
sysmodules/sm/source/processes.h
Normal 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);
|
217
sysmodules/sm/source/services.c
Normal file
217
sysmodules/sm/source/services.c
Normal 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;
|
||||||
|
}
|
32
sysmodules/sm/source/services.h
Normal file
32
sysmodules/sm/source/services.h
Normal 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
181
sysmodules/sm/source/srv.c
Normal 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, ¬ificationSemaphore);
|
||||||
|
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, ¬ificationId);
|
||||||
|
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;
|
||||||
|
}
|
12
sysmodules/sm/source/srv.h
Normal file
12
sysmodules/sm/source/srv.h
Normal 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);
|
81
sysmodules/sm/source/srv_pm.c
Normal file
81
sysmodules/sm/source/srv_pm.c
Normal 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;
|
||||||
|
}
|
||||||
|
|
12
sysmodules/sm/source/srv_pm.h
Normal file
12
sysmodules/sm/source/srv_pm.h
Normal 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);
|
Reference in New Issue
Block a user