Stratosphere: extract common code to libstratosphere.

This commit is contained in:
Michael Scire 2018-04-21 20:31:06 -06:00
commit 5a8df9f128
10 changed files with 1028 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
debug
release
lib
*.bz2

150
Makefile Normal file
View File

@ -0,0 +1,150 @@
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITPRO)),)
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
endif
TOPDIR ?= $(CURDIR)
include $(DEVKITPRO)/libnx/switch_rules
#---------------------------------------------------------------------------------
# TARGET is the name of the output
# SOURCES is a list of directories containing source code
# DATA is a list of directories containing data files
# INCLUDES is a list of directories containing header files
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
SOURCES := source
DATA := data
INCLUDES := include
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS := -g -Wall -O2 -ffunction-sections \
$(ARCH) $(DEFINES)
CFLAGS += $(INCLUDE) -D__SWITCH__
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++17
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
LIBS := -lnx
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
LIBDIRS := $(PORTLIBS) $(LIBNX)
#---------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
# rules for different file extensions
#---------------------------------------------------------------------------------
ifneq ($(BUILD),$(notdir $(CURDIR)))
#---------------------------------------------------------------------------------
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
#---------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C
#---------------------------------------------------------------------------------
ifeq ($(strip $(CPPFILES)),)
#---------------------------------------------------------------------------------
export LD := $(CC)
#---------------------------------------------------------------------------------
else
#---------------------------------------------------------------------------------
export LD := $(CXX)
#---------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
export HFILES := $(addsuffix .h,$(subst .,_,$(BINFILES)))
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
-I. \
-iquote $(CURDIR)/include/switch/
.PHONY: clean all
#---------------------------------------------------------------------------------
all: lib/$(TARGET).a lib/$(TARGET)d.a
lib:
@[ -d $@ ] || mkdir -p $@
release:
@[ -d $@ ] || mkdir -p $@
debug:
@[ -d $@ ] || mkdir -p $@
lib/$(TARGET).a : lib release $(SOURCES) $(INCLUDES)
@$(MAKE) BUILD=release OUTPUT=$(CURDIR)/$@ \
BUILD_CFLAGS="-DNDEBUG=1 -O2" \
DEPSDIR=$(CURDIR)/release \
--no-print-directory -C release \
-f $(CURDIR)/Makefile
lib/$(TARGET)d.a : lib debug $(SOURCES) $(INCLUDES)
@$(MAKE) BUILD=debug OUTPUT=$(CURDIR)/$@ \
BUILD_CFLAGS="-DDEBUG=1 -Og" \
DEPSDIR=$(CURDIR)/debug \
--no-print-directory -C debug \
-f $(CURDIR)/Makefile
dist-bin: all
@tar --exclude=*~ -cjf $(TARGET).tar.bz2 include lib
dist-src:
@tar --exclude=*~ -cjf $(TARGET)-src.tar.bz2 include source Makefile
dist: dist-src dist-bin
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr release debug lib *.bz2
#---------------------------------------------------------------------------------
else
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
$(OUTPUT) : $(OFILES)
$(OFILES_SRC) : $(HFILES)
#---------------------------------------------------------------------------------
%_bin.h %.bin.o : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

8
include/stratosphere.hpp Normal file
View File

@ -0,0 +1,8 @@
#pragma once
#include "stratosphere/ipc_templating.hpp"
#include "stratosphere/iwaitable.hpp"
#include "stratosphere/iserviceobject.hpp"
#include "stratosphere/servicesession.hpp"
#include "stratosphere/serviceserver.hpp"
#include "stratosphere/waitablemanager.hpp"

View File

@ -0,0 +1,411 @@
#pragma once
#include <switch.h>
#include <cstdlib>
#include <cstring>
#include <tuple>
#include <boost/callable_traits.hpp>
#include <type_traits>
/* Represents an A descriptor. */
template <typename T>
struct InBuffer {
T *buffer;
size_t num_elements;
BufferType type;
InBuffer(void *b, size_t n) : buffer((T *)b), num_elements(n/sizeof(T)) { }
};
/* Represents a B descriptor. */
template <typename T>
struct OutBuffer {
T *buffer;
size_t num_elements;
OutBuffer(void *b, size_t n) : buffer((T *)b), num_elements(n/sizeof(T)) { }
};
/* Represents an X descriptor. */
template <typename T>
struct InPointer {
T *pointer;
size_t num_elements;
InPointer(void *p, size_t n) : pointer((T *)p), num_elements(n/sizeof(T)) { }
};
/* Represents a C descriptor. */
struct OutPointerWithServerSizeBase {};
template <typename T, size_t n>
struct OutPointerWithServerSize : OutPointerWithServerSizeBase {
T *pointer;
static const size_t num_elements = n;
OutPointerWithServerSize(void *p) : pointer((T *)p) { }
};
/* Represents a C descriptor with size in raw data. */
template <typename T>
struct OutPointerWithClientSize {
T *pointer;
size_t num_elements;
OutPointerWithClientSize(void *p, size_t n) : pointer((T *)p), num_elements(n/sizeof(T)) { }
};
/* Represents an input PID. */
struct PidDescriptor {
u64 pid;
PidDescriptor(u64 p) : pid(p) { }
};
/* Represents a moved handle. */
struct MovedHandle {
Handle handle;
MovedHandle(Handle h) : handle(h) { }
};
/* Represents a copied handle. */
struct CopiedHandle {
Handle handle;
CopiedHandle(Handle h) : handle(h) { }
};
/* Utilities. */
template <typename T, template <typename...> class Template>
struct is_specialization_of {
static const bool value = false;
};
template <template <typename...> class Template, typename... Args>
struct is_specialization_of<Template<Args...>, Template> {
static const bool value = true;
};
template<typename Tuple>
struct pop_front;
template<typename Head, typename... Tail>
struct pop_front<std::tuple<Head, Tail...>> {
using type = std::tuple<Tail...>;
};
template <typename T>
struct is_ipc_buffer {
static const bool value = is_specialization_of<T, InBuffer>::value
|| is_specialization_of<T, OutBuffer>::value
|| is_specialization_of<T, InPointer>::value
|| std::is_base_of<OutPointerWithServerSizeBase, T>::value
|| is_specialization_of<T, OutPointerWithClientSize>::value;
};
template <typename T>
struct is_ipc_handle {
static const size_t value = (std::is_same<T, MovedHandle>::value || std::is_same<T, CopiedHandle>::value) ? 1 : 0;
};
template <typename T>
struct size_in_raw_data {
static const size_t value = (is_ipc_buffer<T>::value || is_ipc_handle<T>::value) ? 0 : ((sizeof(T) < sizeof(u32)) ? sizeof(u32) : (sizeof(T) + 3) & (~3));
};
template <typename ...Args>
struct size_in_raw_data_for_arguments {
static const size_t value = (size_in_raw_data<Args>::value + ... + 0);
};
template <typename T>
struct size_in_raw_data_with_out_pointers {
static const size_t value = is_specialization_of<T, OutPointerWithClientSize>::value ? 2 : size_in_raw_data<T>::value;
};
template <typename ...Args>
struct size_in_raw_data_with_out_pointers_for_arguments {
static const size_t value = ((size_in_raw_data_with_out_pointers<Args>::value + ... + 0) + 3) & ~3;
};
template <typename T>
struct is_ipc_inbuffer {
static const size_t value = (is_specialization_of<T, InBuffer>::value) ? 1 : 0;
};
template <typename ...Args>
struct num_inbuffers_in_arguments {
static const size_t value = (is_ipc_inbuffer<Args>::value + ... + 0);
};
template <typename T>
struct is_ipc_inpointer {
static const size_t value = (is_specialization_of<T, InPointer>::value) ? 1 : 0;
};
template <typename ...Args>
struct num_inpointers_in_arguments {
static const size_t value = (is_ipc_inpointer<Args>::value + ... + 0);
};
template <typename T>
struct is_ipc_outpointer {
static const size_t value = (is_specialization_of<T, OutPointerWithClientSize>::value || std::is_base_of<OutPointerWithServerSizeBase, T>::value) ? 1 : 0;
};
template <typename ...Args>
struct num_outpointers_in_arguments {
static const size_t value = (is_ipc_outpointer<Args>::value + ... + 0);
};
template <typename T>
struct is_ipc_inoutbuffer {
static const size_t value = (is_specialization_of<T, InBuffer>::value || is_specialization_of<T, OutBuffer>::value) ? 1 : 0;
};
template <typename ...Args>
struct num_inoutbuffers_in_arguments {
static const size_t value = (is_ipc_inoutbuffer<Args>::value + ... + 0);
};
template <typename ...Args>
struct num_handles_in_arguments {
static const size_t value = (is_ipc_handle<Args>::value + ... + 0);
};
template <typename ...Args>
struct num_pids_in_arguments {
static const size_t value = ((std::is_same<Args, PidDescriptor>::value ? 1 : 0) + ... + 0);
};
template<typename T>
T GetValueFromIpcParsedCommand(IpcParsedCommand& r, IpcCommand& out_c, u8 *pointer_buffer, size_t& pointer_buffer_offset, size_t& cur_rawdata_index, size_t& cur_c_size_offset, size_t& a_index, size_t& b_index, size_t& x_index, size_t& c_index, size_t& h_index) {
const size_t old_rawdata_index = cur_rawdata_index;
const size_t old_c_size_offset = cur_c_size_offset;
const size_t old_pointer_buffer_offset = pointer_buffer_offset;
if constexpr (is_specialization_of<T, InBuffer>::value) {
return T(r.Buffers[a_index], r.BufferSizes[a_index++]);
} else if constexpr (is_specialization_of<T, OutBuffer>::value) {
return T(r.Buffers[b_index], r.BufferSizes[b_index++]);
} else if constexpr (is_specialization_of<T, InPointer>::value) {
return T(r.Statics[x_index], r.StaticSizes[x_index++]);
} else if constexpr (std::is_base_of<OutPointerWithServerSizeBase, T>::value) {
T t = T(pointer_buffer + old_pointer_buffer_offset);
ipcAddSendStatic(&out_c, pointer_buffer + old_pointer_buffer_offset, t.num_elements * sizeof(*t.pointer), c_index++);
return t;
} else if constexpr (is_specialization_of<T, OutPointerWithClientSize>::value) {
cur_c_size_offset += sizeof(u16);
u16 sz = *((u16 *)((u8 *)(r.Raw) + old_c_size_offset));
pointer_buffer_offset += sz;
ipcAddSendStatic(&out_c, pointer_buffer + old_pointer_buffer_offset, sz, c_index++);
return T(pointer_buffer + old_pointer_buffer_offset, sz);
} else if constexpr (is_ipc_handle<T>::value) {
return r.Handles[h_index++];
} else if constexpr (std::is_same<T, PidDescriptor>::value) {
return PidDescriptor(r.Pid);
} else {
cur_rawdata_index += size_in_raw_data<T>::value / sizeof(u32);
return *((T *)((u32 *)r.Raw + old_rawdata_index));
}
}
template <typename T>
bool ValidateIpcParsedCommandArgument(IpcParsedCommand& r, size_t& cur_rawdata_index, size_t& cur_c_size_offset, size_t& a_index, size_t& b_index, size_t& x_index, size_t& c_index, size_t& h_index, size_t& total_c_size) {
const size_t old_c_size_offset = cur_c_size_offset;
if constexpr (is_specialization_of<T, InBuffer>::value) {
return r.Buffers[a_index] != NULL && r.BufferDirections[a_index++] == BufferDirection_Send;
} else if constexpr (is_specialization_of<T, OutBuffer>::value) {
return r.Buffers[b_index] != NULL && r.BufferDirections[b_index++] == BufferDirection_Recv;
} else if constexpr (is_specialization_of<T, InPointer>::value) {
return r.Statics[x_index] != NULL;
} else if constexpr (std::is_base_of<OutPointerWithServerSizeBase, T>::value) {
total_c_size += T::num_elements;
return true;
} else if constexpr (is_specialization_of<T, OutPointerWithClientSize>::value) {
cur_c_size_offset += sizeof(u16);
u16 sz = *((u16 *)((u8 *)(r.Raw) + old_c_size_offset));
total_c_size += sz;
return true;
} else if constexpr (std::is_same<T, MovedHandle>::value) {
return !r.WasHandleCopied[h_index++];
} else if constexpr (std::is_same<T, CopiedHandle>::value) {
return r.WasHandleCopied[h_index++];
} else {
return true;
}
}
/* Validator. */
template <typename ArgsTuple>
struct Validator;
template<typename... Args>
struct Validator<std::tuple<Args...>> {
IpcParsedCommand &r;
size_t pointer_buffer_size;
Result operator()() {
if (r.RawSize < size_in_raw_data_with_out_pointers_for_arguments<Args... >::value) {
return 0xF601;
}
if (r.NumBuffers != num_inoutbuffers_in_arguments<Args... >::value) {
return 0xF601;
}
if (r.NumStatics != num_inpointers_in_arguments<Args... >::value) {
return 0xF601;
}
if (r.NumStaticsOut != num_outpointers_in_arguments<Args... >::value) {
return 0xF601;
}
if (r.NumHandles != num_handles_in_arguments<Args... >::value) {
return 0xF601;
}
constexpr size_t num_pids = num_pids_in_arguments<Args... >::value;
static_assert(num_pids <= 1, "Number of PID descriptors in IpcCommandImpl cannot be > 1");
if ((r.HasPid && num_pids == 0) || (!r.HasPid && num_pids)) {
return 0xF601;
}
size_t a_index = 0, b_index = num_inbuffers_in_arguments<Args ...>::value, x_index = 0, c_index = 0, h_index = 0;
size_t cur_rawdata_index = 4;
size_t cur_c_size_offset = 0x10 + size_in_raw_data_for_arguments<Args... >::value + (0x10 - ((uintptr_t)r.Raw - (uintptr_t)r.RawWithoutPadding));
size_t total_c_size = 0;
if (!(ValidateIpcParsedCommandArgument<Args>(r, cur_rawdata_index, cur_c_size_offset, a_index, b_index, x_index, c_index, h_index, total_c_size) && ...)) {
return 0xF601;
}
if (total_c_size > pointer_buffer_size) {
return 0xF601;
}
return 0;
}
};
/* Decoder. */
template<typename OutTuple, typename ArgsTuple>
struct Decoder;
template<typename OutTuple, typename... Args>
struct Decoder<OutTuple, std::tuple<Args...>> {
static std::tuple<Args...> Decode(IpcParsedCommand& r, IpcCommand &out_c, u8 *pointer_buffer) {
size_t a_index = 0, b_index = num_inbuffers_in_arguments<Args ...>::value, x_index = 0, c_index = 0, h_index = 0;
size_t cur_rawdata_index = 4;
size_t cur_c_size_offset = 0x10 + size_in_raw_data_for_arguments<Args... >::value + (0x10 - ((uintptr_t)r.Raw - (uintptr_t)r.RawWithoutPadding));
size_t pointer_buffer_offset = 0;
return std::tuple<Args... > {
GetValueFromIpcParsedCommand<Args>(r, out_c, pointer_buffer, pointer_buffer_offset, cur_rawdata_index, cur_c_size_offset, a_index, b_index, x_index, c_index, h_index)
...
};
}
};
/* Encoder. */
template<typename ArgsTuple>
struct Encoder;
template<typename T>
constexpr size_t GetAndUpdateOffsetIntoRawData(size_t& offset) {
auto old = offset;
if (old == 0) {
offset += sizeof(u64);
} else {
offset += size_in_raw_data<T>::value;
}
return old;
}
template<typename T>
void EncodeValueIntoIpcMessageBeforePrepare(IpcCommand *c, T value) {
if constexpr (std::is_same<T, MovedHandle>::value) {
ipcSendHandleMove(c, value.handle);
} else if constexpr (std::is_same<T, CopiedHandle>::value) {
ipcSendHandleCopy(c, value.handle);
} else if constexpr (std::is_same<T, PidDescriptor>::value) {
ipcSendPid(c);
}
}
template<typename T>
void EncodeValueIntoIpcMessageAfterPrepare(u8 *cur_out, T value) {
if constexpr (is_ipc_handle<T>::value || std::is_same<T, PidDescriptor>::value) {
/* Do nothing. */
} else {
*((T *)(cur_out)) = value;
}
}
template<typename... Args>
struct Encoder<std::tuple<Args...>> {
IpcCommand &out_command;
auto operator()(Args... args) {
static_assert(sizeof...(Args) > 0, "IpcCommandImpls must return std::tuple<Result, ...>");
size_t offset = 0;
u8 *tls = (u8 *)armGetTls();
std::fill(tls, tls + 0x100, 0x00);
((EncodeValueIntoIpcMessageBeforePrepare<Args>(&out_command, args)), ...);
/* Remove the extra space resulting from first Result type. */
struct {
u64 magic;
u64 result;
} *raw = (decltype(raw))ipcPrepareHeader(&out_command, sizeof(*raw) + size_in_raw_data_for_arguments<Args... >::value - sizeof(Result));
raw->magic = SFCO_MAGIC;
u8 *raw_data = (u8 *)&raw->result;
((EncodeValueIntoIpcMessageAfterPrepare<Args>(raw_data + GetAndUpdateOffsetIntoRawData<Args>(offset), args)), ...);
Result rc = raw->result;
if (R_FAILED(rc)) {
std::fill(tls, tls + 0x100, 0x00);
ipcInitialize(&out_command);
raw = (decltype(raw))ipcPrepareHeader(&out_command, sizeof(raw));
raw->magic = SFCO_MAGIC;
raw->result = rc;
}
return rc;
}
};
template<auto IpcCommandImpl, typename Class>
Result WrapIpcCommandImpl(Class *myfancypointer, IpcParsedCommand& r, IpcCommand &out_command, u8 *pointer_buffer, size_t pointer_buffer_size) {
using InArgs = typename boost::callable_traits::args_t<decltype(IpcCommandImpl)>;
using InArgsWithoutThis = typename pop_front<InArgs>::type;
using OutArgs = typename boost::callable_traits::return_type_t<decltype(IpcCommandImpl)>;
static_assert(is_specialization_of<OutArgs, std::tuple>::value, "IpcCommandImpls must return std::tuple<Result, ...>");
static_assert(std::is_same_v<std::tuple_element_t<0, OutArgs>, Result>, "IpcCommandImpls must return std::tuple<Result, ...>");
ipcInitialize(&out_command);
Result rc = Validator<InArgsWithoutThis>{r, pointer_buffer_size}();
if (R_FAILED(rc)) {
return 0xF601;
}
auto args = Decoder<OutArgs, InArgsWithoutThis>::Decode(r, out_command, pointer_buffer);
auto result = std::apply( [=](auto&&... args) { return (myfancypointer->*IpcCommandImpl)(args...); }, args);
return std::apply(Encoder<OutArgs>{out_command}, result);
}

View File

@ -0,0 +1,10 @@
#pragma once
#include <switch.h>
#include "ipc_templating.hpp"
class IServiceObject {
public:
virtual ~IServiceObject() { }
virtual Result dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size) = 0;
};

View File

@ -0,0 +1,40 @@
#pragma once
#include <switch.h>
class IWaitable {
u64 wait_priority = 0;
IWaitable *parent_waitable;
public:
virtual ~IWaitable() { }
virtual unsigned int get_num_waitables() = 0;
virtual void get_waitables(IWaitable **dst) = 0;
virtual void delete_child(IWaitable *child) = 0;
virtual Handle get_handle() = 0;
virtual Result handle_signaled(u64 timeout) = 0;
bool has_parent() {
return this->parent_waitable != NULL;
}
void set_parent(IWaitable *p) {
if (has_parent()) {
/* TODO: Panic? */
}
this->parent_waitable = p;
}
IWaitable *get_parent() {
return this->parent_waitable;
}
void update_priority() {
static u64 g_cur_priority = 0;
this->wait_priority = g_cur_priority++;
}
static bool compare(IWaitable *a, IWaitable *b) {
return (a->wait_priority < b->wait_priority);
}
};

View File

@ -0,0 +1,112 @@
#pragma once
#include <switch.h>
#include <type_traits>
#include "iserviceobject.hpp"
#include "iwaitable.hpp"
#include "servicesession.hpp"
template <typename T>
class ServiceSession;
template <typename T>
class ServiceServer : public IWaitable {
static_assert(std::is_base_of<IServiceObject, T>::value, "Service Objects must derive from IServiceObject");
Handle port_handle;
unsigned int max_sessions;
unsigned int num_sessions;
ServiceSession<T> **sessions;
public:
ServiceServer(const char *service_name, unsigned int max_s) : max_sessions(max_s) {
if (R_FAILED(smRegisterService(&this->port_handle, service_name, false, this->max_sessions))) {
/* TODO: Panic. */
}
this->sessions = new ServiceSession<T> *[this->max_sessions];
for (unsigned int i = 0; i < this->max_sessions; i++) {
this->sessions[i] = NULL;
}
this->num_sessions = 0;
}
virtual ~ServiceServer() {
for (unsigned int i = 0; i < this->max_sessions; i++) {
if (this->sessions[i]) {
delete this->sessions[i];
}
delete this->sessions;
}
if (port_handle) {
svcCloseHandle(port_handle);
}
}
/* IWaitable */
virtual unsigned int get_num_waitables() {
unsigned int n = 1;
for (unsigned int i = 0; i < this->max_sessions; i++) {
if (this->sessions[i]) {
n += this->sessions[i]->get_num_waitables();
}
}
return n;
}
virtual void get_waitables(IWaitable **dst) {
dst[0] = this;
unsigned int n = 0;
for (unsigned int i = 0; i < this->max_sessions; i++) {
if (this->sessions[i]) {
this->sessions[i]->get_waitables(&dst[1 + n]);
n += this->sessions[i]->get_num_waitables();
}
}
}
virtual void delete_child(IWaitable *child) {
unsigned int i;
for (i = 0; i < this->max_sessions; i++) {
if (this->sessions[i] == child) {
break;
}
}
if (i == this->max_sessions) {
/* TODO: Panic, because this isn't our child. */
} else {
delete this->sessions[i];
this->sessions[i] = NULL;
this->num_sessions--;
}
}
virtual Handle get_handle() {
return this->port_handle;
}
virtual Result handle_signaled(u64 timeout) {
/* If this server's port was signaled, accept a new session. */
Handle session_h;
svcAcceptSession(&session_h, this->port_handle);
if (this->num_sessions >= this->max_sessions) {
svcCloseHandle(session_h);
return 0x10601;
}
unsigned int i;
for (i = 0; i < this->max_sessions; i++) {
if (this->sessions[i] == NULL) {
break;
}
}
this->sessions[i] = new ServiceSession<T>(this, session_h, 0);
this->sessions[i]->set_parent(this);
this->num_sessions++;
return 0;
}
};

View File

@ -0,0 +1,184 @@
#pragma once
#include <switch.h>
#include <type_traits>
#include "ipc_templating.hpp"
#include "iserviceobject.hpp"
#include "iwaitable.hpp"
#include "serviceserver.hpp"
enum IpcControlCommand {
IpcCtrl_Cmd_ConvertCurrentObjectToDomain = 0,
IpcCtrl_Cmd_CopyFromCurrentDomain = 1,
IpcCtrl_Cmd_CloneCurrentObject = 2,
IpcCtrl_Cmd_QueryPointerBufferSize = 3,
IpcCtrl_Cmd_CloneCurrentObjectEx = 4
};
#define POINTER_BUFFER_SIZE_MAX 0xFFFF
template <typename T>
class ServiceServer;
template <typename T>
class ServiceSession : public IWaitable {
static_assert(std::is_base_of<IServiceObject, T>::value, "Service Objects must derive from IServiceObject");
T *service_object;
ServiceServer<T> *server;
Handle server_handle;
Handle client_handle;
char pointer_buffer[0x400];
static_assert(sizeof(pointer_buffer) <= POINTER_BUFFER_SIZE_MAX, "Incorrect Size for PointerBuffer!");
public:
ServiceSession<T>(ServiceServer<T> *s, Handle s_h, Handle c_h) : server(s), server_handle(s_h), client_handle(c_h) {
this->service_object = new T();
}
virtual ~ServiceSession() {
delete this->service_object;
if (server_handle) {
svcCloseHandle(server_handle);
}
if (client_handle) {
svcCloseHandle(client_handle);
}
}
T *get_service_object() { return this->service_object; }
Handle get_server_handle() { return this->server_handle; }
Handle get_client_handle() { return this->client_handle; }
/* IWaitable */
virtual unsigned int get_num_waitables() {
return 1;
}
virtual void get_waitables(IWaitable **dst) {
dst[0] = this;
}
virtual void delete_child(IWaitable *child) {
/* TODO: Panic, because we can never have any children. */
}
virtual Handle get_handle() {
return this->server_handle;
}
virtual Result handle_signaled(u64 timeout) {
Result rc;
int handle_index;
/* Prepare pointer buffer... */
IpcCommand c_for_reply;
ipcInitialize(&c_for_reply);
ipcAddRecvStatic(&c_for_reply, this->pointer_buffer, sizeof(this->pointer_buffer), 0);
ipcPrepareHeader(&c_for_reply, 0);
if (R_SUCCEEDED(rc = svcReplyAndReceive(&handle_index, &this->server_handle, 1, 0, timeout))) {
if (handle_index != 0) {
/* TODO: Panic? */
}
u32 *cmdbuf = (u32 *)armGetTls();
Result retval = 0;
u32 *rawdata_start = cmdbuf;
IpcParsedCommand r;
IpcCommand c;
ipcInitialize(&c);
retval = ipcParse(&r);
if (R_SUCCEEDED(retval)) {
rawdata_start = (u32 *)r.Raw;
switch (r.CommandType) {
case IpcCommandType_Close:
/* TODO: This should close the session and clean up its resources. */
retval = 0xF601;
break;
case IpcCommandType_LegacyControl:
/* TODO: What does this allow one to do? */
retval = 0xF601;
break;
case IpcCommandType_LegacyRequest:
/* TODO: What does this allow one to do? */
retval = 0xF601;
break;
case IpcCommandType_Request:
case IpcCommandType_RequestWithContext:
retval = this->service_object->dispatch(r, c, rawdata_start[2], (u8 *)this->pointer_buffer, sizeof(this->pointer_buffer));
break;
case IpcCommandType_Control:
case IpcCommandType_ControlWithContext:
retval = this->dispatch_control_command(r, c, rawdata_start[2]);
break;
case IpcCommandType_Invalid:
default:
retval = 0xF601;
break;
}
}
if (retval != 0xF601) {
rc = svcReplyAndReceive(&handle_index, &this->server_handle, 1, this->server_handle, 0);
} else {
rc = retval;
}
}
return rc;
}
/* Control commands. */
std::tuple<Result> ConvertCurrentObjectToDomain() {
/* TODO */
return std::make_tuple(0xF601);
}
std::tuple<Result> CopyFromCurrentDomain() {
/* TODO */
return std::make_tuple(0xF601);
}
std::tuple<Result> CloneCurrentObject() {
/* TODO */
return std::make_tuple(0xF601);
}
std::tuple<Result, u32> QueryPointerBufferSize() {
return std::make_tuple(0x0, (u32)sizeof(this->pointer_buffer));
}
std::tuple<Result> CloneCurrentObjectEx() {
/* TODO */
return std::make_tuple(0xF601);
}
Result dispatch_control_command(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id) {
Result rc = 0xF601;
/* TODO: Implement. */
switch ((IpcControlCommand)cmd_id) {
case IpcCtrl_Cmd_ConvertCurrentObjectToDomain:
rc = WrapIpcCommandImpl<&ServiceSession::ConvertCurrentObjectToDomain>(this, r, out_c, (u8 *)this->pointer_buffer, sizeof(this->pointer_buffer));
break;
case IpcCtrl_Cmd_CopyFromCurrentDomain:
rc = WrapIpcCommandImpl<&ServiceSession::CopyFromCurrentDomain>(this, r, out_c, (u8 *)this->pointer_buffer, sizeof(this->pointer_buffer));
break;
case IpcCtrl_Cmd_CloneCurrentObject:
rc = WrapIpcCommandImpl<&ServiceSession::CloneCurrentObject>(this, r, out_c, (u8 *)this->pointer_buffer, sizeof(this->pointer_buffer));
break;
case IpcCtrl_Cmd_QueryPointerBufferSize:
rc = WrapIpcCommandImpl<&ServiceSession::QueryPointerBufferSize>(this, r, out_c, (u8 *)this->pointer_buffer, sizeof(this->pointer_buffer));
break;
case IpcCtrl_Cmd_CloneCurrentObjectEx:
rc = WrapIpcCommandImpl<&ServiceSession::CloneCurrentObjectEx>(this, r, out_c, (u8 *)this->pointer_buffer, sizeof(this->pointer_buffer));
break;
default:
break;
}
return rc;
}
};

View File

@ -0,0 +1,25 @@
#pragma once
#include <switch.h>
#include <vector>
#include "iwaitable.hpp"
class WaitableManager {
std::vector<IWaitable *> waitables;
u64 timeout;
public:
WaitableManager(u64 t) : waitables(0), timeout(t) { }
~WaitableManager() {
/* This should call the destructor for every waitable. */
for (auto & waitable : waitables) {
delete waitable;
}
waitables.clear();
}
unsigned int get_num_signalable();
void add_waitable(IWaitable *waitable);
void process();
};

View File

@ -0,0 +1,84 @@
#include <switch.h>
#include <algorithm>
#include <cstdio>
#include <stratosphere/waitablemanager.hpp>
unsigned int WaitableManager::get_num_signalable() {
unsigned int n = 0;
for (auto & waitable : this->waitables) {
n += waitable->get_num_waitables();
}
return n;
}
void WaitableManager::add_waitable(IWaitable *waitable) {
this->waitables.push_back(waitable);
}
void WaitableManager::process() {
std::vector<IWaitable *> signalables;
std::vector<Handle> handles;
int handle_index = 0;
Result rc;
while (1) {
/* Create vector of signalable items. */
signalables.resize(this->get_num_signalable(), NULL);
unsigned int n = 0;
for (auto & waitable : this->waitables) {
waitable->get_waitables(signalables.data() + n);
n += waitable->get_num_waitables();
}
/* Sort signalables by priority. */
std::sort(signalables.begin(), signalables.end(), IWaitable::compare);
/* Copy out handles. */
handles.resize(signalables.size());
std::transform(signalables.begin(), signalables.end(), handles.begin(), [](IWaitable *w) { return w->get_handle(); });
rc = svcWaitSynchronization(&handle_index, handles.data(), handles.size(), this->timeout);
if (R_SUCCEEDED(rc)) {
/* Handle a signaled waitable. */
/* TODO: What timeout should be passed here? */
rc = signalables[handle_index]->handle_signaled(0);
for (int i = 0; i < handle_index; i++) {
signalables[i]->update_priority();
}
} else if (rc == 0xEA01) {
/* Timeout. */
for (auto & waitable : signalables) {
waitable->update_priority();
}
} else if (rc != 0xF601) {
/* TODO: Panic. When can this happen? */
}
if (rc == 0xF601) {
/* handles[handle_index] was closed! */
/* Close the handle. */
svcCloseHandle(handles[handle_index]);
/* If relevant, remove from waitables. */
this->waitables.erase(std::remove(this->waitables.begin(), this->waitables.end(), signalables[handle_index]), this->waitables.end());
/* Delete it. */
if (signalables[handle_index]->has_parent()) {
signalables[handle_index]->get_parent()->delete_child(signalables[handle_index]);
} else {
delete signalables[handle_index];
}
for (int i = 0; i < handle_index; i++) {
signalables[i]->update_priority();
}
}
}
}