mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-22 20:31:14 +00:00
Delete libstratosphere in prep for submodule
This commit is contained in:
parent
3b8bb325e8
commit
5ef01edab5
36 changed files with 0 additions and 4320 deletions
4
stratosphere/libstratosphere/.gitignore
vendored
4
stratosphere/libstratosphere/.gitignore
vendored
|
@ -1,4 +0,0 @@
|
||||||
debug
|
|
||||||
release
|
|
||||||
lib
|
|
||||||
*.bz2
|
|
|
@ -1,150 +0,0 @@
|
||||||
#---------------------------------------------------------------------------------
|
|
||||||
.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 ../../common/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
|
|
||||||
#---------------------------------------------------------------------------------------
|
|
||||||
|
|
|
@ -1,53 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2018 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
namespace detail {
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
struct class_of;
|
|
||||||
|
|
||||||
template<typename Ret, typename C>
|
|
||||||
struct class_of<Ret C::*> {
|
|
||||||
using type = C;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
using class_of_t = typename class_of<T>::type;
|
|
||||||
|
|
||||||
template<typename Mem, typename T, typename C = class_of_t<Mem>>
|
|
||||||
struct member_equals_fn_helper {
|
|
||||||
T ref;
|
|
||||||
Mem mem_fn;
|
|
||||||
|
|
||||||
bool operator()(const C& val) const {
|
|
||||||
return (std::mem_fn(mem_fn)(val) == ref);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator()(C&& val) const {
|
|
||||||
return (std::mem_fn(mem_fn)(std::move(val)) == ref);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace detail
|
|
||||||
|
|
||||||
template<typename Mem, typename T>
|
|
||||||
auto member_equals_fn(Mem mem, T ref) {
|
|
||||||
return detail::member_equals_fn_helper<Mem, T>{std::move(ref), std::move(mem)};
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2018 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "stratosphere/version_check.hpp"
|
|
||||||
#include "stratosphere/scope_guard.hpp"
|
|
||||||
|
|
||||||
#include "stratosphere/hossynch.hpp"
|
|
||||||
#include "stratosphere/message_queue.hpp"
|
|
||||||
#include "stratosphere/iwaitable.hpp"
|
|
||||||
#include "stratosphere/event.hpp"
|
|
||||||
|
|
||||||
#include "stratosphere/waitable_manager.hpp"
|
|
||||||
|
|
||||||
#include "stratosphere/ipc.hpp"
|
|
||||||
|
|
||||||
#include "stratosphere/mitm.hpp"
|
|
||||||
|
|
||||||
#include "stratosphere/services.hpp"
|
|
|
@ -1,132 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2018 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <switch.h>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "iwaitable.hpp"
|
|
||||||
|
|
||||||
class IEvent : public IWaitable {
|
|
||||||
public:
|
|
||||||
/* Information members. */
|
|
||||||
Handle r_h;
|
|
||||||
Handle w_h;
|
|
||||||
bool autoclear;
|
|
||||||
public:
|
|
||||||
IEvent(bool a = false) : r_h(INVALID_HANDLE), w_h(INVALID_HANDLE), autoclear(a) { }
|
|
||||||
IEvent(Handle r, bool a = false) : r_h(r), w_h(INVALID_HANDLE), autoclear(a) { }
|
|
||||||
IEvent(Handle r, Handle w, bool a = false) : r_h(r), w_h(w), autoclear(a) { }
|
|
||||||
|
|
||||||
~IEvent() {
|
|
||||||
if (r_h != INVALID_HANDLE) {
|
|
||||||
svcCloseHandle(r_h);
|
|
||||||
}
|
|
||||||
if (w_h != INVALID_HANDLE) {
|
|
||||||
svcCloseHandle(w_h);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Make it non-copyable */
|
|
||||||
IEvent() = delete;
|
|
||||||
IEvent(const IEvent &) = delete;
|
|
||||||
IEvent& operator=(const IEvent&) = delete;
|
|
||||||
|
|
||||||
|
|
||||||
bool IsAutoClear() {
|
|
||||||
return this->autoclear;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Clear() {
|
|
||||||
std::scoped_lock<HosMutex> lock(this->sig_lock);
|
|
||||||
this->is_signaled = false;
|
|
||||||
if (this->r_h != INVALID_HANDLE) {
|
|
||||||
svcResetSignal(this->r_h);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Signal() {
|
|
||||||
std::scoped_lock<HosMutex> lock(this->sig_lock);
|
|
||||||
|
|
||||||
if (this->w_h == INVALID_HANDLE && this->r_h != INVALID_HANDLE) {
|
|
||||||
/* We can't signal an event if we only have a read handle. */
|
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->w_h == INVALID_HANDLE && this->is_signaled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->is_signaled = true;
|
|
||||||
|
|
||||||
if (this->w_h != INVALID_HANDLE) {
|
|
||||||
svcSignalEvent(this->w_h);
|
|
||||||
} else {
|
|
||||||
this->NotifyManagerSignaled();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result HandleSignaled(u64 timeout) = 0;
|
|
||||||
|
|
||||||
/* IWaitable */
|
|
||||||
virtual Handle GetHandle() override {
|
|
||||||
return this->r_h;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class F>
|
|
||||||
class HosEvent : public IEvent {
|
|
||||||
private:
|
|
||||||
F callback;
|
|
||||||
public:
|
|
||||||
HosEvent(F f, bool a = false) : IEvent(a), callback(std::move(f)) { }
|
|
||||||
HosEvent(Handle r, F f, bool a = false) : IEvent(r, a), callback(std::move(f)) { }
|
|
||||||
HosEvent(Handle r, Handle w, F f, bool a = false) : IEvent(r, w, a), callback(std::move(f)) { }
|
|
||||||
|
|
||||||
virtual Result HandleSignaled(u64 timeout) override {
|
|
||||||
if (this->IsAutoClear()) {
|
|
||||||
this->Clear();
|
|
||||||
}
|
|
||||||
return this->callback(timeout);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class F>
|
|
||||||
static IEvent *CreateHosEvent(F f, bool autoclear = false) {
|
|
||||||
return new HosEvent<F>(INVALID_HANDLE, INVALID_HANDLE, std::move(f), autoclear);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class F>
|
|
||||||
static IEvent *CreateSystemEvent(F f, bool autoclear = false) {
|
|
||||||
|
|
||||||
Handle w_h, r_h;
|
|
||||||
if (R_FAILED(svcCreateEvent(&w_h, &r_h))) {
|
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
return new HosEvent<F>(r_h, w_h, std::move(f), autoclear);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <bool a = false>
|
|
||||||
static IEvent *CreateWriteOnlySystemEvent() {
|
|
||||||
return CreateSystemEvent([](u64 timeout) { std::abort(); return 0; }, a);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class F>
|
|
||||||
static IEvent *LoadReadOnlySystemEvent(Handle r_h, F f, bool autoclear = false) {
|
|
||||||
return new HosEvent<F>(r_h, f, autoclear);
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2018 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
enum FirmwareVersion : u32 {
|
|
||||||
FirmwareVersion_Min = 0,
|
|
||||||
FirmwareVersion_100 = FirmwareVersion_Min,
|
|
||||||
FirmwareVersion_200 = 1,
|
|
||||||
FirmwareVersion_300 = 2,
|
|
||||||
FirmwareVersion_400 = 3,
|
|
||||||
FirmwareVersion_500 = 4,
|
|
||||||
FirmwareVersion_600 = 5,
|
|
||||||
FirmwareVersion_Current = FirmwareVersion_600,
|
|
||||||
FirmwareVersion_Max = 32,
|
|
||||||
};
|
|
||||||
|
|
||||||
static inline FirmwareVersion GetRuntimeFirmwareVersion() {
|
|
||||||
FirmwareVersion fw = FirmwareVersion_Min;
|
|
||||||
if (kernelAbove200()) {
|
|
||||||
fw = FirmwareVersion_200;
|
|
||||||
}
|
|
||||||
if (kernelAbove300()) {
|
|
||||||
fw = FirmwareVersion_300;
|
|
||||||
}
|
|
||||||
if (kernelAbove400()) {
|
|
||||||
fw = FirmwareVersion_400;
|
|
||||||
}
|
|
||||||
if (kernelAbove500()) {
|
|
||||||
fw = FirmwareVersion_500;
|
|
||||||
}
|
|
||||||
if (kernelAbove600()) {
|
|
||||||
fw = FirmwareVersion_600;
|
|
||||||
}
|
|
||||||
return fw;
|
|
||||||
}
|
|
|
@ -1,215 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2018 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <switch.h>
|
|
||||||
#include <switch/arm/counter.h>
|
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
class HosMutex {
|
|
||||||
private:
|
|
||||||
Mutex m;
|
|
||||||
Mutex *GetMutex() {
|
|
||||||
return &this->m;
|
|
||||||
}
|
|
||||||
public:
|
|
||||||
HosMutex() {
|
|
||||||
mutexInit(GetMutex());
|
|
||||||
}
|
|
||||||
|
|
||||||
void lock() {
|
|
||||||
mutexLock(GetMutex());
|
|
||||||
}
|
|
||||||
|
|
||||||
void unlock() {
|
|
||||||
mutexUnlock(GetMutex());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool try_lock() {
|
|
||||||
return mutexTryLock(GetMutex());
|
|
||||||
}
|
|
||||||
|
|
||||||
void Lock() {
|
|
||||||
lock();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Unlock() {
|
|
||||||
unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TryLock() {
|
|
||||||
return try_lock();
|
|
||||||
}
|
|
||||||
|
|
||||||
friend class HosCondVar;
|
|
||||||
};
|
|
||||||
|
|
||||||
class HosRecursiveMutex {
|
|
||||||
private:
|
|
||||||
RMutex m;
|
|
||||||
RMutex *GetMutex() {
|
|
||||||
return &this->m;
|
|
||||||
}
|
|
||||||
public:
|
|
||||||
HosRecursiveMutex() {
|
|
||||||
rmutexInit(GetMutex());
|
|
||||||
}
|
|
||||||
|
|
||||||
void lock() {
|
|
||||||
rmutexLock(GetMutex());
|
|
||||||
}
|
|
||||||
|
|
||||||
void unlock() {
|
|
||||||
rmutexUnlock(GetMutex());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool try_lock() {
|
|
||||||
return rmutexTryLock(GetMutex());
|
|
||||||
}
|
|
||||||
|
|
||||||
void Lock() {
|
|
||||||
lock();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Unlock() {
|
|
||||||
unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TryLock() {
|
|
||||||
return try_lock();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class HosCondVar {
|
|
||||||
private:
|
|
||||||
CondVar cv;
|
|
||||||
public:
|
|
||||||
HosCondVar() {
|
|
||||||
condvarInit(&cv);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result TimedWait(u64 timeout, HosMutex *hm) {
|
|
||||||
return TimedWait(timeout, hm->GetMutex());
|
|
||||||
}
|
|
||||||
|
|
||||||
Result Wait(HosMutex *hm) {
|
|
||||||
return Wait(hm->GetMutex());
|
|
||||||
}
|
|
||||||
|
|
||||||
Result TimedWait(u64 timeout, Mutex *m) {
|
|
||||||
return condvarWaitTimeout(&cv, m, timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result Wait(Mutex *m) {
|
|
||||||
return condvarWait(&cv, m);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result Wake(int num) {
|
|
||||||
return condvarWake(&cv, num);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result WakeOne() {
|
|
||||||
return condvarWakeOne(&cv);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result WakeAll() {
|
|
||||||
return condvarWakeAll(&cv);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class HosSemaphore {
|
|
||||||
private:
|
|
||||||
Semaphore s;
|
|
||||||
public:
|
|
||||||
HosSemaphore() {
|
|
||||||
semaphoreInit(&s, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
HosSemaphore(u64 c) {
|
|
||||||
semaphoreInit(&s, c);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Signal() {
|
|
||||||
semaphoreSignal(&s);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Wait() {
|
|
||||||
semaphoreWait(&s);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TryWait() {
|
|
||||||
return semaphoreTryWait(&s);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class TimeoutHelper {
|
|
||||||
private:
|
|
||||||
u64 end_tick;
|
|
||||||
public:
|
|
||||||
TimeoutHelper(u64 ns) {
|
|
||||||
/* Special case zero-time timeouts. */
|
|
||||||
if (ns == 0) {
|
|
||||||
end_tick = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 cur_tick = armGetSystemTick();
|
|
||||||
this->end_tick = cur_tick + NsToTick(ns) + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline u64 NsToTick(u64 ns) {
|
|
||||||
return (ns * 12) / 625;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline u64 TickToNs(u64 tick) {
|
|
||||||
return (tick * 625) / 12;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TimedOut() {
|
|
||||||
if (this->end_tick == 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return armGetSystemTick() >= this->end_tick;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class HosThread {
|
|
||||||
private:
|
|
||||||
Thread thr = {0};
|
|
||||||
public:
|
|
||||||
HosThread() {}
|
|
||||||
|
|
||||||
Result Initialize(ThreadFunc entry, void *arg, size_t stack_sz, int prio, int cpuid = -2) {
|
|
||||||
return threadCreate(&this->thr, entry, arg, stack_sz, prio, cpuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
Handle GetHandle() const {
|
|
||||||
return this->thr.handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result Start() {
|
|
||||||
return threadStart(&this->thr);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result Join() {
|
|
||||||
Result rc = threadWaitForExit(&this->thr);
|
|
||||||
if (R_SUCCEEDED(rc)) {
|
|
||||||
rc = threadClose(&this->thr);
|
|
||||||
}
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,22 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2018 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "ipc/ipc_service_object.hpp"
|
|
||||||
#include "ipc/ipc_serialization.hpp"
|
|
||||||
|
|
||||||
#include "ipc/ipc_service_session.hpp"
|
|
|
@ -1,117 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2018 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <switch.h>
|
|
||||||
#include <type_traits>
|
|
||||||
|
|
||||||
enum class IpcBufferType {
|
|
||||||
InBuffer,
|
|
||||||
OutBuffer,
|
|
||||||
InPointer,
|
|
||||||
OutPointer,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Base for In/Out Buffers. */
|
|
||||||
struct IpcBufferBase {};
|
|
||||||
|
|
||||||
struct InOutBufferBase : public IpcBufferBase {};
|
|
||||||
|
|
||||||
/* Represents an A descriptor. */
|
|
||||||
struct InBufferBase : public InOutBufferBase {};
|
|
||||||
|
|
||||||
template <typename T, BufferType e_t = BufferType_Normal>
|
|
||||||
struct InBuffer : public InBufferBase {
|
|
||||||
T *buffer;
|
|
||||||
size_t num_elements;
|
|
||||||
BufferType type;
|
|
||||||
static const BufferType expected_type = e_t;
|
|
||||||
|
|
||||||
/* Convenience. */
|
|
||||||
T& operator[](size_t i) const {
|
|
||||||
return buffer[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
InBuffer(void *b, size_t n, BufferType t) : buffer((T *)b), num_elements(n/sizeof(T)), type(t) { }
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Represents a B descriptor. */
|
|
||||||
struct OutBufferBase : public InOutBufferBase {};
|
|
||||||
|
|
||||||
template <typename T, BufferType e_t = BufferType_Normal>
|
|
||||||
struct OutBuffer : OutBufferBase {
|
|
||||||
T *buffer;
|
|
||||||
size_t num_elements;
|
|
||||||
BufferType type;
|
|
||||||
static const BufferType expected_type = e_t;
|
|
||||||
|
|
||||||
/* Convenience. */
|
|
||||||
T& operator[](size_t i) const {
|
|
||||||
return buffer[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
OutBuffer(void *b, size_t n, BufferType t) : buffer((T *)b), num_elements(n/sizeof(T)), type(t) { }
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Represents an X descriptor. */
|
|
||||||
struct InPointerBase : public IpcBufferBase {};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct InPointer : public InPointerBase {
|
|
||||||
T *pointer;
|
|
||||||
size_t num_elements;
|
|
||||||
|
|
||||||
/* Convenience. */
|
|
||||||
T& operator[](size_t i) const {
|
|
||||||
return pointer[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
InPointer(void *p, size_t n) : pointer((T *)p), num_elements(n/sizeof(T)) { }
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Represents a C descriptor. */
|
|
||||||
struct OutPointerWithServerSizeBase : public IpcBufferBase {};
|
|
||||||
|
|
||||||
template <typename T, size_t N>
|
|
||||||
struct OutPointerWithServerSize : public OutPointerWithServerSizeBase {
|
|
||||||
T *pointer;
|
|
||||||
static const size_t num_elements = N;
|
|
||||||
static const size_t element_size = sizeof(T);
|
|
||||||
|
|
||||||
/* Convenience. */
|
|
||||||
T& operator[](size_t i) const {
|
|
||||||
return pointer[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
OutPointerWithServerSize(void *p) : pointer((T *)p) { }
|
|
||||||
OutPointerWithServerSize(void *p, size_t n) : pointer((T *)p) { }
|
|
||||||
};
|
|
||||||
|
|
||||||
struct OutPointerWithClientSizeBase : public IpcBufferBase {};
|
|
||||||
|
|
||||||
/* Represents a C descriptor with size in raw data. */
|
|
||||||
template <typename T>
|
|
||||||
struct OutPointerWithClientSize : public OutPointerWithClientSizeBase {
|
|
||||||
T *pointer;
|
|
||||||
size_t num_elements;
|
|
||||||
|
|
||||||
/* Convenience. */
|
|
||||||
T& operator[](size_t i) const {
|
|
||||||
return pointer[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
OutPointerWithClientSize(void *p, size_t n) : pointer((T *)p), num_elements(n/sizeof(T)) { }
|
|
||||||
};
|
|
|
@ -1,128 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2018 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <switch.h>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <memory>
|
|
||||||
#include <type_traits>
|
|
||||||
|
|
||||||
#include "ipc_service_object.hpp"
|
|
||||||
|
|
||||||
class IDomainObject;
|
|
||||||
|
|
||||||
class DomainManager {
|
|
||||||
public:
|
|
||||||
virtual std::shared_ptr<IDomainObject> AllocateDomain() = 0;
|
|
||||||
virtual void FreeDomain(IDomainObject *domain) = 0;
|
|
||||||
virtual Result ReserveObject(IDomainObject *domain, u32 *out_object_id) = 0;
|
|
||||||
virtual Result ReserveSpecificObject(IDomainObject *domain, u32 object_id) = 0;
|
|
||||||
virtual void SetObject(IDomainObject *domain, u32 object_id, ServiceObjectHolder&& holder) = 0;
|
|
||||||
virtual ServiceObjectHolder *GetObject(IDomainObject *domain, u32 object_id) = 0;
|
|
||||||
virtual Result FreeObject(IDomainObject *domain, u32 object_id) = 0;
|
|
||||||
virtual Result ForceFreeObject(u32 object_id) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class IDomainObject : public IServiceObject {
|
|
||||||
private:
|
|
||||||
DomainManager *manager;
|
|
||||||
public:
|
|
||||||
IDomainObject(DomainManager *m) : manager(m) {}
|
|
||||||
|
|
||||||
virtual ~IDomainObject() override {
|
|
||||||
this->manager->FreeDomain(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
DomainManager *GetManager() {
|
|
||||||
return this->manager;
|
|
||||||
}
|
|
||||||
|
|
||||||
ServiceObjectHolder *GetObject(u32 object_id) {
|
|
||||||
return this->manager->GetObject(this, object_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result ReserveObject(u32 *out_object_id) {
|
|
||||||
return this->manager->ReserveObject(this, out_object_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result ReserveSpecificObject(u32 object_id) {
|
|
||||||
return this->manager->ReserveSpecificObject(this, object_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetObject(u32 object_id, ServiceObjectHolder&& holder) {
|
|
||||||
this->manager->SetObject(this, object_id, std::move(holder));
|
|
||||||
}
|
|
||||||
|
|
||||||
Result FreeObject(u32 object_id) {
|
|
||||||
return this->manager->FreeObject(this, object_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result ForceFreeObject(u32 object_id) {
|
|
||||||
return this->manager->ForceFreeObject(object_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
DEFINE_SERVICE_DISPATCH_TABLE {
|
|
||||||
/* IDomainObject has no callable functions. */
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
static constexpr bool IsDomainObject(ServiceObjectHolder &holder) {
|
|
||||||
return holder.GetServiceId() == ServiceObjectId<IDomainObject>();
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr bool IsDomainObject(ServiceObjectHolder *holder) {
|
|
||||||
return holder->GetServiceId() == ServiceObjectId<IDomainObject>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Out for service impl. */
|
|
||||||
template <typename ServiceImpl>
|
|
||||||
class Out<std::shared_ptr<ServiceImpl>> : public OutSessionTag {
|
|
||||||
static_assert(std::is_base_of_v<IServiceObject, ServiceImpl>, "OutSessions must be shared_ptr<IServiceObject>!");
|
|
||||||
|
|
||||||
template<typename, typename>
|
|
||||||
friend class Out;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::shared_ptr<ServiceImpl> *srv;
|
|
||||||
IDomainObject *domain = nullptr;
|
|
||||||
u32 *object_id = nullptr;
|
|
||||||
public:
|
|
||||||
Out<std::shared_ptr<ServiceImpl>>(std::shared_ptr<IServiceObject> *s, IDomainObject *dm, u32 *o) : srv(reinterpret_cast<std::shared_ptr<ServiceImpl> *>(s)), domain(dm), object_id(o) { }
|
|
||||||
|
|
||||||
ServiceObjectHolder GetHolder() {
|
|
||||||
std::shared_ptr<ServiceImpl> clone = *srv;
|
|
||||||
return ServiceObjectHolder(std::move(clone));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsDomain() {
|
|
||||||
return domain != nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 GetObjectId() {
|
|
||||||
return *object_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ChangeObjectId(u32 o) {
|
|
||||||
domain->ForceFreeObject(*object_id);
|
|
||||||
domain->ReserveSpecificObject(o);
|
|
||||||
*object_id = o;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetValue(std::shared_ptr<ServiceImpl> &&s) {
|
|
||||||
*this->srv = std::move(s);
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,75 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2018 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <switch.h>
|
|
||||||
#include <type_traits>
|
|
||||||
|
|
||||||
/* Declare false allowed struct. */
|
|
||||||
template <typename>
|
|
||||||
struct AllowedOut : std::false_type {};
|
|
||||||
|
|
||||||
struct OutDataTag{};
|
|
||||||
struct OutHandleTag{};
|
|
||||||
struct OutSessionTag{};
|
|
||||||
|
|
||||||
/* Define out struct, so that we can get errors on enable_if */
|
|
||||||
template <typename T, typename Allowed = void>
|
|
||||||
class Out {
|
|
||||||
static_assert(std::is_pod<T>::value && !std::is_pod<T>::value, "Invalid IPC Out Type!");
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
class Out<T, typename std::enable_if<std::is_trivial<T>::value || AllowedOut<T>::value>::type> : public OutDataTag {
|
|
||||||
private:
|
|
||||||
T *obj;
|
|
||||||
public:
|
|
||||||
Out(T *o) : obj(o) { }
|
|
||||||
|
|
||||||
void SetValue(const T& t) {
|
|
||||||
*obj = t;
|
|
||||||
}
|
|
||||||
|
|
||||||
const T& GetValue() {
|
|
||||||
return *obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
T *GetPointer() {
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Convenience operators. */
|
|
||||||
T& operator*() {
|
|
||||||
return *obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
T* operator->() {
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
class Out<T*> {
|
|
||||||
static_assert(std::is_pod<T>::value && !std::is_pod<T>::value, "Invalid IPC Out Type (Raw Pointer)!");
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct OutHelper;
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct OutHelper<Out<T>> {
|
|
||||||
using type = T;
|
|
||||||
};
|
|
|
@ -1,44 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2018 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <switch.h>
|
|
||||||
|
|
||||||
#include "ipc_service_object.hpp"
|
|
||||||
#include "ipc_domain_object.hpp"
|
|
||||||
|
|
||||||
#include "ipc_special.hpp"
|
|
||||||
|
|
||||||
#include "ipc_session_manager_base.hpp"
|
|
||||||
|
|
||||||
struct IpcResponseContext {
|
|
||||||
/* Request/Reply data. */
|
|
||||||
IpcParsedCommand request;
|
|
||||||
IpcCommand reply;
|
|
||||||
u8 out_data[0x100];
|
|
||||||
std::shared_ptr<IServiceObject> *out_objs[8];
|
|
||||||
Handle out_object_server_handles[8];
|
|
||||||
IpcHandle out_handles[8];
|
|
||||||
u32 out_object_ids[8];
|
|
||||||
IpcCommandType cmd_type;
|
|
||||||
u64 cmd_id;
|
|
||||||
Result rc;
|
|
||||||
/* Context. */
|
|
||||||
SessionManagerBase *manager;
|
|
||||||
ServiceObjectHolder *obj_holder;
|
|
||||||
unsigned char *pb;
|
|
||||||
size_t pb_size;
|
|
||||||
};
|
|
|
@ -1,666 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2018 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <switch.h>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <cstring>
|
|
||||||
#include <tuple>
|
|
||||||
#include <boost/callable_traits.hpp>
|
|
||||||
#include <type_traits>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include "ipc_out.hpp"
|
|
||||||
#include "ipc_buffers.hpp"
|
|
||||||
#include "ipc_special.hpp"
|
|
||||||
|
|
||||||
#include "ipc_domain_object.hpp"
|
|
||||||
|
|
||||||
#include "ipc_response_context.hpp"
|
|
||||||
|
|
||||||
#pragma GCC diagnostic push
|
|
||||||
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
|
|
||||||
|
|
||||||
template<typename Tuple>
|
|
||||||
struct PopFront;
|
|
||||||
|
|
||||||
template<typename Head, typename... Tail>
|
|
||||||
struct PopFront<std::tuple<Head, Tail...>> {
|
|
||||||
using type = std::tuple<Tail...>;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename ...> struct WhichType;
|
|
||||||
|
|
||||||
template <typename...>
|
|
||||||
struct TypeList{};
|
|
||||||
|
|
||||||
template <typename... T1s, typename... T2s>
|
|
||||||
constexpr auto Concatenate(TypeList<T1s...>, TypeList<T2s...>) {
|
|
||||||
return TypeList<T1s..., T2s...>{};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <template <typename> typename Condition, typename R>
|
|
||||||
constexpr auto FilterTypes(R result, TypeList<>) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <template <typename> typename Condition, typename R, typename T, typename... Ts>
|
|
||||||
constexpr auto FilterTypes(R result, TypeList<T, Ts...>) {
|
|
||||||
if constexpr (Condition<T>{})
|
|
||||||
return FilterTypes<Condition>(Concatenate(result, TypeList<T>{}), TypeList<Ts...>{});
|
|
||||||
else
|
|
||||||
return FilterTypes<Condition>(result, TypeList<Ts...>{});
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Types> struct TypeListToTuple;
|
|
||||||
|
|
||||||
template<typename... Types>
|
|
||||||
struct TypeListToTuple<TypeList<Types...>> {
|
|
||||||
using type = std::tuple<Types...>;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <template <typename> typename Condition, typename... Types>
|
|
||||||
using FilteredTypes = typename TypeListToTuple<std::decay_t<decltype(FilterTypes<Condition>(TypeList<>{}, TypeList<Types...>{}))>>::type;
|
|
||||||
|
|
||||||
enum class ArgType {
|
|
||||||
InData,
|
|
||||||
OutData,
|
|
||||||
InHandle,
|
|
||||||
OutHandle,
|
|
||||||
InSession,
|
|
||||||
OutSession,
|
|
||||||
PidDesc,
|
|
||||||
InBuffer,
|
|
||||||
OutBuffer,
|
|
||||||
InPointer,
|
|
||||||
OutPointerClientSize,
|
|
||||||
OutPointerServerSize,
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename X>
|
|
||||||
constexpr ArgType GetArgType() {
|
|
||||||
if constexpr (std::is_base_of_v<OutDataTag, X>) {
|
|
||||||
return ArgType::OutData;
|
|
||||||
} else if constexpr (std::is_base_of_v<OutSessionTag, X>) {
|
|
||||||
return ArgType::OutSession;
|
|
||||||
} else if constexpr (std::is_base_of_v<OutHandleTag, X>) {
|
|
||||||
return ArgType::OutHandle;
|
|
||||||
} else if constexpr (std::is_base_of_v<InBufferBase, X>) {
|
|
||||||
return ArgType::InBuffer;
|
|
||||||
} else if constexpr (std::is_base_of_v<OutBufferBase, X>) {
|
|
||||||
return ArgType::OutBuffer;
|
|
||||||
} else if constexpr (std::is_base_of_v<InPointerBase, X>) {
|
|
||||||
return ArgType::InPointer;
|
|
||||||
} else if constexpr (std::is_base_of_v<OutPointerWithClientSizeBase, X>) {
|
|
||||||
return ArgType::OutPointerClientSize;
|
|
||||||
} else if constexpr (std::is_base_of_v<OutPointerWithServerSizeBase, X>) {
|
|
||||||
return ArgType::OutPointerServerSize;
|
|
||||||
} else if constexpr (std::is_base_of_v<PidDescriptorTag, X>) {
|
|
||||||
return ArgType::PidDesc;
|
|
||||||
} else if constexpr (std::is_base_of_v<IpcHandleTag, X>) {
|
|
||||||
return ArgType::InHandle;
|
|
||||||
} else if constexpr (std::is_trivial_v<X> && !std::is_pointer_v<X>) {
|
|
||||||
return ArgType::InData;
|
|
||||||
} else {
|
|
||||||
static_assert(std::is_pod_v<X> && !std::is_pod_v<X>, "Unhandled InSession!");
|
|
||||||
return ArgType::InSession;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<ArgType ArgT>
|
|
||||||
struct ArgTypeFilter {
|
|
||||||
template<typename X>
|
|
||||||
using type = std::conditional_t<GetArgType<X>() == ArgT, std::true_type, std::false_type>;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<ArgType ArgT>
|
|
||||||
struct IsArgTypeBuffer {
|
|
||||||
static constexpr bool value = ArgT == ArgType::InBuffer || ArgT == ArgType::OutBuffer || ArgT == ArgType::InPointer || ArgT == ArgType::OutPointerClientSize || ArgT == ArgType::OutPointerServerSize;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ArgTypeBufferFilter {
|
|
||||||
template<typename X>
|
|
||||||
using type = std::conditional_t<IsArgTypeBuffer<GetArgType<X>()>::value, std::true_type, std::false_type>;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<ArgType ArgT>
|
|
||||||
struct IsArgTypeInData {
|
|
||||||
static constexpr bool value = ArgT == ArgType::InData || ArgT == ArgType::PidDesc;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ArgTypeInDataFilter {
|
|
||||||
template<typename X>
|
|
||||||
using type = std::conditional_t<IsArgTypeInData<GetArgType<X>()>::value, std::true_type, std::false_type>;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
struct RawDataHelper {
|
|
||||||
static_assert(GetArgType<T>() == ArgType::InData || GetArgType<T>() == ArgType::PidDesc);
|
|
||||||
static constexpr size_t align = (GetArgType<T>() == ArgType::InData) ? __alignof__(T) : __alignof__(u64);
|
|
||||||
static constexpr size_t size = (GetArgType<T>() == ArgType::InData) ? sizeof(T) : sizeof(u64);
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
struct RawDataHelper<Out<T>> {
|
|
||||||
static_assert(GetArgType<T>() == ArgType::InData);
|
|
||||||
static constexpr size_t align = __alignof(T);
|
|
||||||
static constexpr size_t size = sizeof(T);
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename Ts>
|
|
||||||
struct RawDataComputer;
|
|
||||||
|
|
||||||
template<typename... Ts>
|
|
||||||
struct RawDataComputer<std::tuple<Ts...>> {
|
|
||||||
/* https://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs,2604 */
|
|
||||||
static constexpr void QuickSort(std::array<size_t, sizeof...(Ts)> &map, std::array<size_t, sizeof...(Ts)> &values, int left, int right) {
|
|
||||||
do {
|
|
||||||
int i = left;
|
|
||||||
int j = right;
|
|
||||||
int x = map[i + ((j - i) >> 1)];
|
|
||||||
do {
|
|
||||||
while (i < static_cast<int>(sizeof...(Ts)) && values[x] > values[map[i]]) i++;
|
|
||||||
while (j >= 0 && values[x] < values[map[j]]) j--;
|
|
||||||
if (i > j) break;
|
|
||||||
if (i < j) {
|
|
||||||
const size_t temp = map[i];
|
|
||||||
map[i] = map[j];
|
|
||||||
map[j] = temp;
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
j--;
|
|
||||||
} while (i <= j);
|
|
||||||
if (j - left <= right - i) {
|
|
||||||
if (left < j) QuickSort(map, values, left, j);
|
|
||||||
left = i;
|
|
||||||
} else {
|
|
||||||
if (i < right) QuickSort(map, values, i, right);
|
|
||||||
right = j;
|
|
||||||
}
|
|
||||||
} while (left < right);
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr void StableSort(std::array<size_t, sizeof...(Ts)> &map, std::array<size_t, sizeof...(Ts)> &values) {
|
|
||||||
/* First, quicksort a copy of the map. */
|
|
||||||
std::array<size_t, sizeof...(Ts)> map_unstable(map);
|
|
||||||
QuickSort(map_unstable, values, 0, sizeof...(Ts)-1);
|
|
||||||
|
|
||||||
/* Now, create stable sorted map from unstably quicksorted indices (via repeated insertion sort on element runs). */
|
|
||||||
for (size_t i = 0; i < sizeof...(Ts); i++) {
|
|
||||||
map[i] = map_unstable[i];
|
|
||||||
for (ssize_t j = i-1; j >= 0 && values[map[j]] == values[map[j+1]] && map[j] > map[j+1]; j--) {
|
|
||||||
const size_t temp = map[j];
|
|
||||||
map[j] = map[j+1];
|
|
||||||
map[j+1] = temp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr std::array<size_t, sizeof...(Ts)+1> GetOffsets() {
|
|
||||||
std::array<size_t, sizeof...(Ts)+1> offsets = {};
|
|
||||||
offsets[0] = 0;
|
|
||||||
if constexpr (sizeof...(Ts) > 0) {
|
|
||||||
/* Get size, alignment for each type. */
|
|
||||||
std::array<size_t, sizeof...(Ts)> sizes = { RawDataHelper<Ts>::size... };
|
|
||||||
std::array<size_t, sizeof...(Ts)> aligns = { RawDataHelper<Ts>::align... };
|
|
||||||
|
|
||||||
/* We want to sort...by alignment. */
|
|
||||||
std::array<size_t, sizeof...(Ts)> map = {};
|
|
||||||
for (size_t i = 0; i < sizeof...(Ts); i++) { map[i] = i; }
|
|
||||||
StableSort(map, aligns);
|
|
||||||
|
|
||||||
/* Iterate over sorted types. */
|
|
||||||
size_t cur_offset = 0;
|
|
||||||
for (size_t i = 0; i < sizeof...(Ts); i++) {
|
|
||||||
const size_t align = aligns[map[i]];
|
|
||||||
if (cur_offset % align != 0) {
|
|
||||||
cur_offset += align - (cur_offset % align);
|
|
||||||
}
|
|
||||||
offsets[map[i]] = cur_offset;
|
|
||||||
cur_offset += sizes[map[i]];
|
|
||||||
}
|
|
||||||
offsets[sizeof...(Ts)] = cur_offset;
|
|
||||||
}
|
|
||||||
return offsets;
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr std::array<size_t, sizeof...(Ts)+1> offsets = GetOffsets();
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename _Args, typename _ReturnType>
|
|
||||||
struct CommandMetaInfo;
|
|
||||||
|
|
||||||
template<typename... _Args, typename _ReturnType>
|
|
||||||
struct CommandMetaInfo<std::tuple<_Args...>, _ReturnType> {
|
|
||||||
using Args = std::tuple<_Args...>;
|
|
||||||
using ReturnType = _ReturnType;
|
|
||||||
|
|
||||||
static constexpr bool ReturnsResult = std::is_same_v<ReturnType, Result>;
|
|
||||||
static constexpr bool ReturnsVoid = std::is_same_v<ReturnType, void>;
|
|
||||||
|
|
||||||
using InDatas = FilteredTypes<ArgTypeInDataFilter::type, _Args...>;
|
|
||||||
using OutDatas = FilteredTypes<ArgTypeFilter<ArgType::OutData>::type, _Args...>;
|
|
||||||
using InHandles = FilteredTypes<ArgTypeFilter<ArgType::InHandle>::type, _Args...>;
|
|
||||||
using OutHandles = FilteredTypes<ArgTypeFilter<ArgType::OutHandle>::type, _Args...>;
|
|
||||||
using InSessions = FilteredTypes<ArgTypeFilter<ArgType::InSession>::type, _Args...>;
|
|
||||||
using OutSessions = FilteredTypes<ArgTypeFilter<ArgType::OutSession>::type, _Args...>;
|
|
||||||
using PidDescs = FilteredTypes<ArgTypeFilter<ArgType::PidDesc>::type, _Args...>;
|
|
||||||
|
|
||||||
using InBuffers = FilteredTypes<ArgTypeFilter<ArgType::InBuffer>::type, _Args...>;
|
|
||||||
using OutBuffers = FilteredTypes<ArgTypeFilter<ArgType::OutBuffer>::type, _Args...>;
|
|
||||||
using InPointers = FilteredTypes<ArgTypeFilter<ArgType::InPointer>::type, _Args...>;
|
|
||||||
using ClientSizeOutPointers = FilteredTypes<ArgTypeFilter<ArgType::OutPointerClientSize>::type, _Args...>;
|
|
||||||
using ServerSizeOutPointers = FilteredTypes<ArgTypeFilter<ArgType::OutPointerServerSize>::type, _Args...>;
|
|
||||||
using Buffers = FilteredTypes<ArgTypeBufferFilter::type, _Args...>;
|
|
||||||
|
|
||||||
static constexpr size_t NumInDatas = std::tuple_size_v<InDatas>;
|
|
||||||
static constexpr size_t NumOutDatas = std::tuple_size_v<OutDatas>;
|
|
||||||
static constexpr size_t NumInHandles = std::tuple_size_v<InHandles>;
|
|
||||||
static constexpr size_t NumOutHandles = std::tuple_size_v<OutHandles>;
|
|
||||||
static constexpr size_t NumInSessions = std::tuple_size_v<InSessions>;
|
|
||||||
static constexpr size_t NumOutSessions = std::tuple_size_v<OutSessions>;
|
|
||||||
static constexpr size_t NumPidDescs = std::tuple_size_v<PidDescs>;
|
|
||||||
|
|
||||||
static constexpr size_t NumInBuffers = std::tuple_size_v<InBuffers>;
|
|
||||||
static constexpr size_t NumOutBuffers = std::tuple_size_v<OutBuffers>;
|
|
||||||
static constexpr size_t NumInPointers = std::tuple_size_v<InPointers>;
|
|
||||||
static constexpr size_t NumClientSizeOutPointers = std::tuple_size_v<ClientSizeOutPointers>;
|
|
||||||
static constexpr size_t NumServerSizeOutPointers = std::tuple_size_v<ServerSizeOutPointers>;
|
|
||||||
static constexpr size_t NumBuffers = std::tuple_size_v<Buffers>;
|
|
||||||
|
|
||||||
static_assert(NumInSessions == 0, "InSessions not yet supported!");
|
|
||||||
static_assert(NumPidDescs == 0 || NumPidDescs == 1, "Methods can only take in 0 or 1 PIDs!");
|
|
||||||
static_assert(NumBuffers <= 8, "Methods can only take in <= 8 Buffers!");
|
|
||||||
static_assert(NumInHandles <= 8, "Methods can take in <= 8 Handles!");
|
|
||||||
static_assert(NumOutHandles + NumOutSessions <= 8, "Methods can only return <= 8 Handles+Sessions!");
|
|
||||||
|
|
||||||
static constexpr std::array<size_t, NumInDatas+1> InDataOffsets = RawDataComputer<InDatas>::offsets;
|
|
||||||
static constexpr size_t InRawArgSize = InDataOffsets[NumInDatas];
|
|
||||||
static constexpr size_t InRawArgSizeWithOutPointers = ((InRawArgSize + NumClientSizeOutPointers * sizeof(u16)) + 3) & ~3;
|
|
||||||
|
|
||||||
static constexpr std::array<size_t, NumOutDatas+1> OutDataOffsets = RawDataComputer<OutDatas>::offsets;
|
|
||||||
static constexpr size_t OutRawArgSize = OutDataOffsets[NumOutDatas];
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/* ================================================================================= */
|
|
||||||
/* Actual wrapping implementation goes here. */
|
|
||||||
|
|
||||||
/* Validator. */
|
|
||||||
struct Validator {
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
static constexpr bool ValidateCommandArgument(IpcResponseContext *ctx, size_t& a_index, size_t& b_index, size_t& x_index, size_t& h_index, size_t& cur_c_size_offset, size_t& total_c_size) {
|
|
||||||
constexpr ArgType argT = GetArgType<T>();
|
|
||||||
if constexpr (argT == ArgType::InBuffer) {
|
|
||||||
return (ctx->request.Buffers[a_index] != nullptr || ctx->request.BufferSizes[a_index] == 0) && ctx->request.BufferDirections[a_index] == BufferDirection_Send && ctx->request.BufferTypes[a_index++] == T::expected_type;
|
|
||||||
} else if constexpr (argT == ArgType::OutBuffer) {
|
|
||||||
return (ctx->request.Buffers[b_index] != nullptr || ctx->request.BufferSizes[b_index] == 0) && ctx->request.BufferDirections[b_index] == BufferDirection_Recv && ctx->request.BufferTypes[b_index++] == T::expected_type;
|
|
||||||
} else if constexpr (argT == ArgType::InPointer) {
|
|
||||||
return ctx->request.Statics[x_index++] != nullptr;
|
|
||||||
} else if constexpr (argT == ArgType::InHandle) {
|
|
||||||
if constexpr (std::is_same_v<T, MovedHandle>) {
|
|
||||||
return !ctx->request.WasHandleCopied[h_index++];
|
|
||||||
} else if constexpr (std::is_same_v<T, CopiedHandle>) {
|
|
||||||
return ctx->request.WasHandleCopied[h_index++];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if constexpr (argT == ArgType::OutPointerServerSize) {
|
|
||||||
total_c_size += T::num_elements * sizeof(T);
|
|
||||||
} else if constexpr (argT == ArgType::OutPointerServerSize) {
|
|
||||||
total_c_size += *((u16 *)((uintptr_t)(ctx->request.Raw) + 0x10 + cur_c_size_offset));
|
|
||||||
cur_c_size_offset += sizeof(u16);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Ts>
|
|
||||||
struct ValidateCommandTuple;
|
|
||||||
|
|
||||||
template <typename ...Ts>
|
|
||||||
struct ValidateCommandTuple<std::tuple<Ts...>> {
|
|
||||||
static constexpr bool IsValid(IpcResponseContext *ctx, size_t& a_index, size_t& b_index, size_t& x_index, size_t& h_index, size_t& cur_c_size_offset, size_t& total_c_size) {
|
|
||||||
return (ValidateCommandArgument<Ts>(ctx, a_index, b_index, x_index, h_index, cur_c_size_offset, total_c_size) && ...);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename MetaInfo>
|
|
||||||
static constexpr Result Validate(IpcResponseContext *ctx) {
|
|
||||||
if (ctx->request.RawSize < MetaInfo::InRawArgSizeWithOutPointers) {
|
|
||||||
return 0xF601;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctx->request.NumBuffers != MetaInfo::NumInBuffers + MetaInfo::NumOutBuffers) {
|
|
||||||
return 0xF601;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctx->request.NumStatics != MetaInfo::NumInPointers) {
|
|
||||||
return 0xF601;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctx->request.NumStaticsOut != MetaInfo::NumClientSizeOutPointers + MetaInfo::NumServerSizeOutPointers) {
|
|
||||||
return 0xF601;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctx->request.NumHandles != MetaInfo::NumInHandles) {
|
|
||||||
return 0xF601;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if ((ctx->request.HasPid && MetaInfo::NumPidDescs == 0) || (!ctx->request.HasPid && MetaInfo::NumPidDescs != 0)) {
|
|
||||||
return 0xF601;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (((u32 *)ctx->request.Raw)[0] != SFCI_MAGIC) {
|
|
||||||
return 0xF601;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t a_index = 0, b_index = MetaInfo::NumInBuffers, x_index = 0, h_index = 0;
|
|
||||||
size_t cur_c_size_offset = MetaInfo::InRawArgSize + (0x10 - ((uintptr_t)ctx->request.Raw - (uintptr_t)ctx->request.RawWithoutPadding));
|
|
||||||
size_t total_c_size = 0;
|
|
||||||
|
|
||||||
if (!ValidateCommandTuple<typename MetaInfo::Args>::IsValid(ctx, a_index, b_index, x_index, h_index, cur_c_size_offset, total_c_size)) {
|
|
||||||
return 0xF601;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (total_c_size > ctx->pb_size) {
|
|
||||||
return 0xF601;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/* ================================================================================= */
|
|
||||||
|
|
||||||
/* Decoder. */
|
|
||||||
template<typename MetaInfo>
|
|
||||||
struct Decoder {
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
static constexpr T DecodeCommandArgument(IpcResponseContext *ctx, size_t& a_index, size_t& b_index, size_t& x_index, size_t& c_index, size_t& in_h_index, size_t& out_h_index, size_t& out_obj_index, size_t& in_data_index, size_t& out_data_index, size_t& pb_offset, size_t& c_sz_offset) {
|
|
||||||
constexpr ArgType argT = GetArgType<T>();
|
|
||||||
if constexpr (argT == ArgType::InBuffer) {
|
|
||||||
const T& value = T(ctx->request.Buffers[a_index], ctx->request.BufferSizes[a_index], ctx->request.BufferTypes[a_index]);
|
|
||||||
++a_index;
|
|
||||||
return value;
|
|
||||||
} else if constexpr (argT == ArgType::OutBuffer) {
|
|
||||||
const T& value = T(ctx->request.Buffers[b_index], ctx->request.BufferSizes[b_index], ctx->request.BufferTypes[b_index]);
|
|
||||||
++b_index;
|
|
||||||
return value;
|
|
||||||
} else if constexpr (argT == ArgType::InPointer) {
|
|
||||||
const T& value = T(ctx->request.Statics[x_index], ctx->request.StaticSizes[x_index]);
|
|
||||||
++x_index;
|
|
||||||
return value;
|
|
||||||
} else if constexpr (argT == ArgType::InHandle) {
|
|
||||||
return T(ctx->request.Handles[in_h_index++]);
|
|
||||||
} else if constexpr (argT == ArgType::OutHandle) {
|
|
||||||
return T(&ctx->out_handles[out_h_index++]);
|
|
||||||
} else if constexpr (argT == ArgType::PidDesc) {
|
|
||||||
uintptr_t ptr = ((uintptr_t)ctx->request.Raw + 0x10 + MetaInfo::InDataOffsets[in_data_index++]);
|
|
||||||
*(u64 *)ptr = ctx->request.Pid;
|
|
||||||
return T(ctx->request.Pid);
|
|
||||||
} else if constexpr (argT == ArgType::InData) {
|
|
||||||
uintptr_t ptr = ((uintptr_t)ctx->request.Raw + 0x10 + MetaInfo::InDataOffsets[in_data_index++]);
|
|
||||||
if constexpr (std::is_same_v<bool, T>) {
|
|
||||||
return *((u8 *)ptr) & 1;
|
|
||||||
} else {
|
|
||||||
return *((T *)ptr);
|
|
||||||
}
|
|
||||||
} else if constexpr (argT == ArgType::OutData) {
|
|
||||||
uintptr_t ptr = ((uintptr_t)ctx->out_data + MetaInfo::OutDataOffsets[out_data_index++]);
|
|
||||||
return T(reinterpret_cast<typename OutHelper<T>::type *>(ptr));
|
|
||||||
} else if constexpr (argT == ArgType::OutPointerClientSize || argT == ArgType::OutPointerServerSize) {
|
|
||||||
u16 sz;
|
|
||||||
if constexpr(argT == ArgType::OutPointerServerSize) {
|
|
||||||
sz = T::element_size;
|
|
||||||
} else {
|
|
||||||
sz = *(const u16 *)((uintptr_t)ctx->request.Raw + 0x10 + c_sz_offset);
|
|
||||||
}
|
|
||||||
u8* buf = ctx->pb + pb_offset;
|
|
||||||
c_sz_offset += sizeof(u16);
|
|
||||||
pb_offset += sz;
|
|
||||||
ipcAddSendStatic(&ctx->reply, buf, sz, c_index++);
|
|
||||||
return T(buf, sz);
|
|
||||||
} else if constexpr (argT == ArgType::OutSession) {
|
|
||||||
if (IsDomainObject(ctx->obj_holder)) {
|
|
||||||
const T& value = T(ctx->out_objs[out_obj_index], ctx->obj_holder->GetServiceObject<IDomainObject>(), &ctx->out_object_ids[out_obj_index]);
|
|
||||||
out_obj_index++;
|
|
||||||
return value;
|
|
||||||
} else {
|
|
||||||
const T& value = T(ctx->out_objs[out_obj_index], nullptr, 0);
|
|
||||||
out_obj_index++;
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Ts>
|
|
||||||
struct DecodeTuple;
|
|
||||||
|
|
||||||
template <typename ...Ts>
|
|
||||||
struct DecodeTuple<std::tuple<Ts...>> {
|
|
||||||
static constexpr std::tuple<Ts...> GetArgs(IpcResponseContext *ctx, size_t& a_index, size_t& b_index, size_t& x_index, size_t& c_index, size_t& in_h_index, size_t& out_h_index, size_t& out_obj_index, size_t& in_data_index, size_t& out_data_index, size_t& pb_offset, size_t& c_sz_offset) {
|
|
||||||
return std::tuple<Ts... > {
|
|
||||||
DecodeCommandArgument<Ts>(ctx, a_index, b_index, x_index, c_index, in_h_index, out_h_index, out_obj_index, in_data_index, out_data_index, pb_offset, c_sz_offset)
|
|
||||||
...
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
static constexpr typename MetaInfo::Args Decode(IpcResponseContext *ctx) {
|
|
||||||
size_t a_index = 0, b_index = MetaInfo::NumInBuffers, x_index = 0, c_index = 0, in_h_index = 0, out_h_index = 0, out_obj_index = 0;
|
|
||||||
size_t in_data_index = 0x0, out_data_index = 0, pb_offset = 0;
|
|
||||||
size_t c_sz_offset = MetaInfo::InRawArgSize + (0x10 - ((uintptr_t)ctx->request.Raw - (uintptr_t)ctx->request.RawWithoutPadding));
|
|
||||||
return DecodeTuple<typename MetaInfo::Args>::GetArgs(ctx, a_index, b_index, x_index, c_index, in_h_index, out_h_index, out_obj_index, in_data_index, out_data_index, pb_offset, c_sz_offset);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/* ================================================================================= */
|
|
||||||
|
|
||||||
template<typename MetaInfo, typename T>
|
|
||||||
static constexpr void EncodeArgument(IpcResponseContext *ctx, size_t&out_obj_index, T& arg) {
|
|
||||||
constexpr ArgType argT = GetArgType<T>();
|
|
||||||
if constexpr (argT == ArgType::OutHandle) {
|
|
||||||
if constexpr (std::is_same_v<MovedHandle, typename OutHelper<T>::type>) {
|
|
||||||
ipcSendHandleMove(&ctx->reply, arg.GetValue().handle);
|
|
||||||
} else {
|
|
||||||
ipcSendHandleCopy(&ctx->reply, arg.GetValue().handle);
|
|
||||||
}
|
|
||||||
} else if constexpr (argT == ArgType::OutSession) {
|
|
||||||
if (IsDomainObject(ctx->obj_holder)) {
|
|
||||||
auto domain = ctx->obj_holder->GetServiceObject<IDomainObject>();
|
|
||||||
domain->SetObject(arg.GetObjectId(), std::move(arg.GetHolder()));
|
|
||||||
} else {
|
|
||||||
ctx->manager->AddSession(ctx->out_object_server_handles[out_obj_index++], std::move(arg.GetHolder()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename MetaInfo, typename ArgsTuple>
|
|
||||||
struct Encoder;
|
|
||||||
|
|
||||||
template <typename MetaInfo, typename ...Args>
|
|
||||||
struct Encoder<MetaInfo, std::tuple<Args...>> {
|
|
||||||
|
|
||||||
static constexpr void EncodeFailure(IpcResponseContext *ctx, Result rc) {
|
|
||||||
memset(armGetTls(), 0, 0x100);
|
|
||||||
ipcInitialize(&ctx->reply);
|
|
||||||
struct {
|
|
||||||
u64 magic;
|
|
||||||
u64 result;
|
|
||||||
} *raw;
|
|
||||||
|
|
||||||
if (IsDomainObject(ctx->obj_holder)) {
|
|
||||||
raw = (decltype(raw))ipcPrepareHeaderForDomain(&ctx->reply, sizeof(*raw), 0);
|
|
||||||
auto resp_header = (DomainResponseHeader *)((uintptr_t)raw - sizeof(DomainResponseHeader));
|
|
||||||
*resp_header = {0};
|
|
||||||
} else {
|
|
||||||
raw = (decltype(raw))ipcPrepareHeader(&ctx->reply, sizeof(*raw));
|
|
||||||
}
|
|
||||||
raw->magic = SFCO_MAGIC;
|
|
||||||
raw->result = rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static constexpr void EncodeSuccess(IpcResponseContext *ctx, Args... args) {
|
|
||||||
size_t out_obj_index = 0;
|
|
||||||
|
|
||||||
((EncodeArgument<MetaInfo, Args>(ctx, out_obj_index, args)), ...);
|
|
||||||
|
|
||||||
const bool is_domain = IsDomainObject(ctx->obj_holder);
|
|
||||||
|
|
||||||
if (!is_domain) {
|
|
||||||
for (unsigned int i = 0; i < MetaInfo::NumOutSessions; i++) {
|
|
||||||
ipcSendHandleMove(&ctx->reply, ctx->out_handles[MetaInfo::NumOutHandles + i].handle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct {
|
|
||||||
u64 magic;
|
|
||||||
u64 result;
|
|
||||||
} *raw;
|
|
||||||
if (is_domain) {
|
|
||||||
raw = (decltype(raw))ipcPrepareHeaderForDomain(&ctx->reply, sizeof(*raw) + MetaInfo::OutRawArgSize, 0);
|
|
||||||
auto resp_header = (DomainResponseHeader *)((uintptr_t)raw - sizeof(DomainResponseHeader));
|
|
||||||
*resp_header = {0};
|
|
||||||
resp_header->NumObjectIds = MetaInfo::NumOutSessions;
|
|
||||||
} else {
|
|
||||||
raw = (decltype(raw))ipcPrepareHeader(&ctx->reply, sizeof(*raw)+ MetaInfo::OutRawArgSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
raw->magic = SFCO_MAGIC;
|
|
||||||
raw->result = 0;
|
|
||||||
|
|
||||||
memcpy((void *)((uintptr_t)raw + sizeof(*raw)), ctx->out_data, MetaInfo::OutRawArgSize);
|
|
||||||
if (is_domain) {
|
|
||||||
memcpy((void *)((uintptr_t)raw + sizeof(*raw) + MetaInfo::OutRawArgSize), ctx->out_object_ids, sizeof(*ctx->out_object_ids) * MetaInfo::NumOutSessions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
/* ================================================================================= */
|
|
||||||
|
|
||||||
template<auto IpcCommandImpl>
|
|
||||||
constexpr Result WrapIpcCommandImpl(IpcResponseContext *ctx) {
|
|
||||||
using InArgs = typename PopFront<typename boost::callable_traits::args_t<decltype(IpcCommandImpl)>>::type;
|
|
||||||
using OutArgs = typename boost::callable_traits::return_type_t<decltype(IpcCommandImpl)>;
|
|
||||||
using ClassType = typename boost::callable_traits::class_of_t<decltype(IpcCommandImpl)>;
|
|
||||||
|
|
||||||
using CommandMetaData = CommandMetaInfo<InArgs, OutArgs>;
|
|
||||||
|
|
||||||
static_assert(CommandMetaData::ReturnsResult || CommandMetaData::ReturnsVoid, "IpcCommandImpls must return Result or void");
|
|
||||||
|
|
||||||
ipcInitialize(&ctx->reply);
|
|
||||||
memset(ctx->out_data, 0, CommandMetaData::OutRawArgSize);
|
|
||||||
|
|
||||||
Result rc = Validator::Validate<CommandMetaData>(ctx);
|
|
||||||
|
|
||||||
if (R_FAILED(rc)) {
|
|
||||||
return 0xAAEE;
|
|
||||||
}
|
|
||||||
|
|
||||||
ClassType *this_ptr = nullptr;
|
|
||||||
if (IsDomainObject(ctx->obj_holder)) {
|
|
||||||
this_ptr = ctx->obj_holder->GetServiceObject<IDomainObject>()->GetObject(ctx->request.InThisObjectId)->GetServiceObject<ClassType>();
|
|
||||||
} else {
|
|
||||||
this_ptr = ctx->obj_holder->GetServiceObject<ClassType>();
|
|
||||||
}
|
|
||||||
if (this_ptr == nullptr) {
|
|
||||||
return 0xBBEE;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<IServiceObject> out_objects[CommandMetaData::NumOutSessions];
|
|
||||||
|
|
||||||
/* Allocate out object IDs. */
|
|
||||||
size_t num_out_objects;
|
|
||||||
if (IsDomainObject(ctx->obj_holder)) {
|
|
||||||
for (num_out_objects = 0; num_out_objects < CommandMetaData::NumOutSessions; num_out_objects++) {
|
|
||||||
if (R_FAILED((rc = ctx->obj_holder->GetServiceObject<IDomainObject>()->ReserveObject(&ctx->out_object_ids[num_out_objects])))) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ctx->out_objs[num_out_objects] = &out_objects[num_out_objects];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (num_out_objects = 0; num_out_objects < CommandMetaData::NumOutSessions; num_out_objects++) {
|
|
||||||
Handle server_h, client_h;
|
|
||||||
if (R_FAILED((rc = SessionManagerBase::CreateSessionHandles(&server_h, &client_h)))) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ctx->out_object_server_handles[num_out_objects] = server_h;
|
|
||||||
ctx->out_handles[CommandMetaData::NumOutHandles + num_out_objects].handle = client_h;
|
|
||||||
ctx->out_objs[num_out_objects] = &out_objects[num_out_objects];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ON_SCOPE_EXIT {
|
|
||||||
/* Clean up objects as necessary. */
|
|
||||||
if (R_FAILED(rc)) {
|
|
||||||
if (IsDomainObject(ctx->obj_holder)) {
|
|
||||||
for (unsigned int i = 0; i < num_out_objects; i++) {
|
|
||||||
ctx->obj_holder->GetServiceObject<IDomainObject>()->FreeObject(ctx->out_object_ids[i]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (unsigned int i = 0; i < num_out_objects; i++) {
|
|
||||||
svcCloseHandle(ctx->out_object_server_handles[i]);
|
|
||||||
svcCloseHandle(ctx->out_handles[CommandMetaData::NumOutHandles + i].handle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (unsigned int i = 0; i < num_out_objects; i++) {
|
|
||||||
ctx->out_objs[i] = nullptr;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (R_SUCCEEDED(rc)) {
|
|
||||||
auto args = Decoder<CommandMetaData>::Decode(ctx);
|
|
||||||
|
|
||||||
if constexpr (CommandMetaData::ReturnsResult) {
|
|
||||||
rc = std::apply( [=](auto&&... args) { return (this_ptr->*IpcCommandImpl)(args...); }, args);
|
|
||||||
} else {
|
|
||||||
std::apply( [=](auto&&... args) { (this_ptr->*IpcCommandImpl)(args...); }, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (R_SUCCEEDED(rc)) {
|
|
||||||
std::apply(Encoder<CommandMetaData, decltype(args)>::EncodeSuccess, std::tuple_cat(std::make_tuple(ctx), args));
|
|
||||||
} else {
|
|
||||||
std::apply(Encoder<CommandMetaData, decltype(args)>::EncodeFailure, std::tuple_cat(std::make_tuple(ctx), std::make_tuple(rc)));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
std::apply(Encoder<CommandMetaData, typename CommandMetaData::Args>::EncodeFailure, std::tuple_cat(std::make_tuple(ctx), std::make_tuple(rc)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
template <u32 c, auto CommandImpl, FirmwareVersion l = FirmwareVersion_Min, FirmwareVersion h = FirmwareVersion_Max>
|
|
||||||
inline static constexpr ServiceCommandMeta MakeServiceCommandMeta() {
|
|
||||||
return {
|
|
||||||
.fw_low = l,
|
|
||||||
.fw_high = h,
|
|
||||||
.cmd_id = c,
|
|
||||||
.handler = WrapIpcCommandImpl<CommandImpl>,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#pragma GCC diagnostic pop
|
|
|
@ -1,130 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2018 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <switch.h>
|
|
||||||
#include <memory>
|
|
||||||
#include <type_traits>
|
|
||||||
|
|
||||||
#include "ipc_out.hpp"
|
|
||||||
|
|
||||||
#include "../firmware_version.hpp"
|
|
||||||
|
|
||||||
class IpcResponseContext;
|
|
||||||
|
|
||||||
struct ServiceCommandMeta {
|
|
||||||
FirmwareVersion fw_low = FirmwareVersion_Max;
|
|
||||||
FirmwareVersion fw_high = FirmwareVersion_Max;
|
|
||||||
u32 cmd_id = 0;
|
|
||||||
Result (*handler)(IpcResponseContext *) = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
class IServiceObject {
|
|
||||||
public:
|
|
||||||
virtual ~IServiceObject() { }
|
|
||||||
};
|
|
||||||
|
|
||||||
#define SERVICE_DISPATCH_TABLE_NAME s_DispatchTable
|
|
||||||
#define DEFINE_SERVICE_DISPATCH_TABLE static constexpr ServiceCommandMeta SERVICE_DISPATCH_TABLE_NAME[]
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
static constexpr size_t DispatchTableEntryCount() {
|
|
||||||
static_assert(std::is_base_of<IServiceObject, T>::value, "DispatchTable owners must derive from IServiceObject");
|
|
||||||
return sizeof(T::SERVICE_DISPATCH_TABLE_NAME)/sizeof(ServiceCommandMeta);
|
|
||||||
}
|
|
||||||
template <typename T>
|
|
||||||
static constexpr const ServiceCommandMeta* DispatchTable() {
|
|
||||||
static_assert(std::is_base_of<IServiceObject, T>::value, "DispatchTable owners must derive from IServiceObject");
|
|
||||||
return reinterpret_cast<const ServiceCommandMeta*>(&T::SERVICE_DISPATCH_TABLE_NAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
static constexpr uintptr_t ServiceObjectId() {
|
|
||||||
static_assert(std::is_base_of<IServiceObject, T>::value, "Service Objects must derive from IServiceObject");
|
|
||||||
return reinterpret_cast<uintptr_t>(&T::SERVICE_DISPATCH_TABLE_NAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
class ServiceObjectHolder {
|
|
||||||
private:
|
|
||||||
std::shared_ptr<IServiceObject> srv;
|
|
||||||
const ServiceCommandMeta *dispatch_table;
|
|
||||||
size_t entry_count;
|
|
||||||
|
|
||||||
/* Private copy constructor. */
|
|
||||||
ServiceObjectHolder(const ServiceObjectHolder& other) : srv(other.srv), dispatch_table(other.dispatch_table), entry_count(other.entry_count) { }
|
|
||||||
ServiceObjectHolder& operator=(const ServiceObjectHolder& other);
|
|
||||||
public:
|
|
||||||
/* Templated constructor ensures correct type id at runtime. */
|
|
||||||
template <typename ServiceImpl>
|
|
||||||
explicit ServiceObjectHolder(std::shared_ptr<ServiceImpl>&& s) : srv(std::move(s)), dispatch_table(DispatchTable<ServiceImpl>()), entry_count(DispatchTableEntryCount<ServiceImpl>()) { }
|
|
||||||
|
|
||||||
template <typename ServiceImpl>
|
|
||||||
ServiceImpl *GetServiceObject() const {
|
|
||||||
if (GetServiceId() == ServiceObjectId<ServiceImpl>()) {
|
|
||||||
return static_cast<ServiceImpl *>(this->srv.get());
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename ServiceImpl>
|
|
||||||
ServiceImpl *GetServiceObjectUnsafe() const {
|
|
||||||
return static_cast<ServiceImpl *>(this->srv.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
const ServiceCommandMeta *GetDispatchTable() const {
|
|
||||||
return this->dispatch_table;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t GetDispatchTableEntryCount() const {
|
|
||||||
return this->entry_count;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr uintptr_t GetServiceId() const {
|
|
||||||
return reinterpret_cast<uintptr_t>(this->dispatch_table);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Default constructor, move constructor, move assignment operator. */
|
|
||||||
ServiceObjectHolder() : srv(nullptr), dispatch_table(nullptr) { }
|
|
||||||
|
|
||||||
ServiceObjectHolder(ServiceObjectHolder&& other) : srv(std::move(other.srv)), dispatch_table(std::move(other.dispatch_table)), entry_count(std::move(other.entry_count)) { }
|
|
||||||
|
|
||||||
ServiceObjectHolder& operator=(ServiceObjectHolder&& other) {
|
|
||||||
this->srv = other.srv;
|
|
||||||
this->dispatch_table = other.dispatch_table;
|
|
||||||
this->entry_count = other.entry_count;
|
|
||||||
other.Reset();
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
explicit operator bool() const {
|
|
||||||
return this->srv != nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator!() const {
|
|
||||||
return this->srv == nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Reset() {
|
|
||||||
this->srv.reset();
|
|
||||||
this->dispatch_table = nullptr;
|
|
||||||
this->entry_count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ServiceObjectHolder Clone() const {
|
|
||||||
ServiceObjectHolder clone(*this);
|
|
||||||
return clone;
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,356 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2018 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <switch.h>
|
|
||||||
|
|
||||||
#include "../iwaitable.hpp"
|
|
||||||
#include "ipc_service_object.hpp"
|
|
||||||
#include "ipc_serialization.hpp"
|
|
||||||
|
|
||||||
enum HipcControlCommand : u32 {
|
|
||||||
HipcControlCommand_ConvertCurrentObjectToDomain = 0,
|
|
||||||
HipcControlCommand_CopyFromCurrentDomain = 1,
|
|
||||||
HipcControlCommand_CloneCurrentObject = 2,
|
|
||||||
HipcControlCommand_QueryPointerBufferSize = 3,
|
|
||||||
HipcControlCommand_CloneCurrentObjectEx = 4
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#define RESULT_DEFER_SESSION (0x6580A)
|
|
||||||
|
|
||||||
|
|
||||||
class ServiceSession : public IWaitable
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
Handle session_handle;
|
|
||||||
std::vector<unsigned char> pointer_buffer;
|
|
||||||
ServiceObjectHolder obj_holder;
|
|
||||||
ServiceObjectHolder control_holder = ServiceObjectHolder(std::make_shared<IHipcControlService>(this));
|
|
||||||
u8 backup_tls[0x100];
|
|
||||||
|
|
||||||
ServiceSession(Handle s_h) : session_handle(s_h) { }
|
|
||||||
public:
|
|
||||||
template<typename T>
|
|
||||||
ServiceSession(Handle s_h, size_t pbs) : session_handle(s_h), pointer_buffer(pbs), obj_holder(std::make_shared<T>()) { }
|
|
||||||
|
|
||||||
ServiceSession(Handle s_h, size_t pbs, ServiceObjectHolder &&h) : session_handle(s_h), pointer_buffer(pbs), obj_holder(std::move(h)) { }
|
|
||||||
|
|
||||||
virtual ~ServiceSession() override {
|
|
||||||
svcCloseHandle(this->session_handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
SessionManagerBase *GetSessionManager() {
|
|
||||||
return static_cast<SessionManagerBase *>(this->GetManager());
|
|
||||||
}
|
|
||||||
|
|
||||||
DomainManager *GetDomainManager() {
|
|
||||||
return static_cast<DomainManager *>(this->GetSessionManager());
|
|
||||||
}
|
|
||||||
|
|
||||||
Result Receive() {
|
|
||||||
int handle_index;
|
|
||||||
/* Prepare pointer buffer... */
|
|
||||||
IpcCommand c;
|
|
||||||
ipcInitialize(&c);
|
|
||||||
if (this->pointer_buffer.size() > 0) {
|
|
||||||
ipcAddRecvStatic(&c, this->pointer_buffer.data(), this->pointer_buffer.size(), 0);
|
|
||||||
ipcPrepareHeader(&c, 0);
|
|
||||||
|
|
||||||
/* Fix libnx bug in serverside C descriptor handling. */
|
|
||||||
((u32 *)armGetTls())[1] &= 0xFFFFC3FF;
|
|
||||||
((u32 *)armGetTls())[1] |= (2) << 10;
|
|
||||||
} else {
|
|
||||||
ipcPrepareHeader(&c, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Receive. */
|
|
||||||
Result rc = svcReplyAndReceive(&handle_index, &this->session_handle, 1, 0, U64_MAX);
|
|
||||||
if (R_SUCCEEDED(rc)) {
|
|
||||||
std::memcpy(this->backup_tls, armGetTls(), sizeof(this->backup_tls));
|
|
||||||
}
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result Reply() {
|
|
||||||
int handle_index;
|
|
||||||
return svcReplyAndReceive(&handle_index, &this->session_handle, 0, this->session_handle, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* For preparing basic replies. */
|
|
||||||
Result PrepareBasicResponse(IpcResponseContext *ctx, Result rc) {
|
|
||||||
ipcInitialize(&ctx->reply);
|
|
||||||
struct {
|
|
||||||
u64 magic;
|
|
||||||
u64 result;
|
|
||||||
} *raw = (decltype(raw))ipcPrepareHeader(&ctx->reply, sizeof(*raw));
|
|
||||||
|
|
||||||
raw->magic = SFCO_MAGIC;
|
|
||||||
raw->result = rc;
|
|
||||||
return raw->result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result PrepareBasicDomainResponse(IpcResponseContext *ctx, Result rc) {
|
|
||||||
ipcInitialize(&ctx->reply);
|
|
||||||
struct {
|
|
||||||
DomainResponseHeader hdr;
|
|
||||||
u64 magic;
|
|
||||||
u64 result;
|
|
||||||
} *raw = (decltype(raw))ipcPrepareHeader(&ctx->reply, sizeof(*raw));
|
|
||||||
|
|
||||||
raw->hdr = (DomainResponseHeader){0};
|
|
||||||
raw->magic = SFCO_MAGIC;
|
|
||||||
raw->result = rc;
|
|
||||||
return raw->result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* For making a new response context. */
|
|
||||||
void InitializeResponseContext(IpcResponseContext *ctx) {
|
|
||||||
std::memset(ctx, 0, sizeof(*ctx));
|
|
||||||
ctx->manager = this->GetSessionManager();
|
|
||||||
ctx->obj_holder = &this->obj_holder;
|
|
||||||
ctx->pb = this->pointer_buffer.data();
|
|
||||||
ctx->pb_size = this->pointer_buffer.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* IWaitable */
|
|
||||||
virtual Handle GetHandle() {
|
|
||||||
return this->session_handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result GetResponse(IpcResponseContext *ctx) {
|
|
||||||
Result rc = 0xF601;
|
|
||||||
FirmwareVersion fw = GetRuntimeFirmwareVersion();
|
|
||||||
|
|
||||||
const ServiceCommandMeta *dispatch_table = ctx->obj_holder->GetDispatchTable();
|
|
||||||
size_t entry_count = ctx->obj_holder->GetDispatchTableEntryCount();
|
|
||||||
|
|
||||||
if (IsDomainObject(ctx->obj_holder)) {
|
|
||||||
switch (ctx->request.InMessageType) {
|
|
||||||
case DomainMessageType_Invalid:
|
|
||||||
return 0xF601;
|
|
||||||
case DomainMessageType_Close:
|
|
||||||
return PrepareBasicDomainResponse(ctx, ctx->obj_holder->GetServiceObject<IDomainObject>()->FreeObject(ctx->request.InThisObjectId));
|
|
||||||
case DomainMessageType_SendMessage:
|
|
||||||
{
|
|
||||||
auto sub_obj = ctx->obj_holder->GetServiceObject<IDomainObject>()->GetObject(ctx->request.InThisObjectId);
|
|
||||||
if (sub_obj == nullptr) {
|
|
||||||
return PrepareBasicDomainResponse(ctx, 0x3D80B);
|
|
||||||
}
|
|
||||||
dispatch_table = sub_obj->GetDispatchTable();
|
|
||||||
entry_count = sub_obj->GetDispatchTableEntryCount();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < entry_count; i++) {
|
|
||||||
if (ctx->cmd_id == dispatch_table[i].cmd_id && dispatch_table[i].fw_low <= fw && fw <= dispatch_table[i].fw_high) {
|
|
||||||
rc = dispatch_table[i].handler(ctx);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result HandleReceived() {
|
|
||||||
IpcResponseContext ctx;
|
|
||||||
this->InitializeResponseContext(&ctx);
|
|
||||||
|
|
||||||
ctx.cmd_type = (IpcCommandType)(*(u16 *)(armGetTls()));
|
|
||||||
|
|
||||||
ctx.rc = 0;
|
|
||||||
|
|
||||||
/* Parse based on command type. */
|
|
||||||
switch (ctx.cmd_type) {
|
|
||||||
case IpcCommandType_Invalid:
|
|
||||||
case IpcCommandType_LegacyRequest:
|
|
||||||
case IpcCommandType_LegacyControl:
|
|
||||||
return 0xF601;
|
|
||||||
case IpcCommandType_Close:
|
|
||||||
{
|
|
||||||
/* Clean up gracefully. */
|
|
||||||
PrepareBasicResponse(&ctx, 0);
|
|
||||||
this->Reply();
|
|
||||||
}
|
|
||||||
return 0xF601;
|
|
||||||
case IpcCommandType_Control:
|
|
||||||
case IpcCommandType_ControlWithContext:
|
|
||||||
ctx.rc = ipcParse(&ctx.request);
|
|
||||||
ctx.obj_holder = &this->control_holder;
|
|
||||||
break;
|
|
||||||
case IpcCommandType_Request:
|
|
||||||
case IpcCommandType_RequestWithContext:
|
|
||||||
if (IsDomainObject(&this->obj_holder)) {
|
|
||||||
ctx.rc = ipcParseDomainRequest(&ctx.request);
|
|
||||||
} else {
|
|
||||||
ctx.rc = ipcParse(&ctx.request);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return 0xF601;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (R_SUCCEEDED(ctx.rc)) {
|
|
||||||
ctx.cmd_id = ((u32 *)ctx.request.Raw)[2];
|
|
||||||
this->PreProcessRequest(&ctx);
|
|
||||||
ctx.rc = this->GetResponse(&ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctx.rc == RESULT_DEFER_SESSION) {
|
|
||||||
/* Session defer. */
|
|
||||||
this->SetDeferred(true);
|
|
||||||
} else if (ctx.rc == 0xF601) {
|
|
||||||
/* Session close, nothing to do. */
|
|
||||||
} else {
|
|
||||||
if (R_SUCCEEDED(ctx.rc)) {
|
|
||||||
this->PostProcessResponse(&ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.rc = this->Reply();
|
|
||||||
|
|
||||||
if (ctx.rc == 0xEA01) {
|
|
||||||
ctx.rc = 0x0;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->CleanupResponse(&ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ctx.rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result HandleDeferred() override {
|
|
||||||
memcpy(armGetTls(), this->backup_tls, sizeof(this->backup_tls));
|
|
||||||
Result rc = this->HandleReceived();
|
|
||||||
|
|
||||||
if (rc != RESULT_DEFER_SESSION) {
|
|
||||||
this->SetDeferred(false);
|
|
||||||
}
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result HandleSignaled(u64 timeout) {
|
|
||||||
Result rc;
|
|
||||||
|
|
||||||
if (R_SUCCEEDED(rc = this->Receive())) {
|
|
||||||
rc = this->HandleReceived();
|
|
||||||
}
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void PreProcessRequest(IpcResponseContext *ctx) {
|
|
||||||
/* ... */
|
|
||||||
(void)(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void PostProcessResponse(IpcResponseContext *ctx) {
|
|
||||||
/* ... */
|
|
||||||
(void)(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void CleanupResponse(IpcResponseContext *ctx) {
|
|
||||||
std::memset(this->backup_tls, 0, sizeof(this->backup_tls));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public:
|
|
||||||
class IHipcControlService : public IServiceObject {
|
|
||||||
private:
|
|
||||||
ServiceSession *session;
|
|
||||||
public:
|
|
||||||
explicit IHipcControlService(ServiceSession *s) : session(s) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~IHipcControlService() override { }
|
|
||||||
|
|
||||||
Result ConvertCurrentObjectToDomain(Out<u32> object_id) {
|
|
||||||
/* Allocate new domain. */
|
|
||||||
auto new_domain = this->session->GetDomainManager()->AllocateDomain();
|
|
||||||
if (new_domain == nullptr) {
|
|
||||||
return 0x1900B;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reserve an object in the domain for our session. */
|
|
||||||
u32 reserved_id;
|
|
||||||
Result rc = new_domain->ReserveObject(&reserved_id);
|
|
||||||
if (R_FAILED(rc)) {
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
new_domain->SetObject(reserved_id, std::move(this->session->obj_holder));
|
|
||||||
this->session->obj_holder = std::move(ServiceObjectHolder(std::move(new_domain)));
|
|
||||||
|
|
||||||
/* Return the object id. */
|
|
||||||
object_id.SetValue(reserved_id);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result CopyFromCurrentDomain(Out<MovedHandle> out_h, u32 id) {
|
|
||||||
auto domain = this->session->obj_holder.GetServiceObject<IDomainObject>();
|
|
||||||
if (domain == nullptr) {
|
|
||||||
return 0x3D60B;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
auto object = domain->GetObject(id);
|
|
||||||
if (object == nullptr) {
|
|
||||||
return 0x3D80B;
|
|
||||||
}
|
|
||||||
|
|
||||||
Handle server_h, client_h;
|
|
||||||
if (R_FAILED(SessionManagerBase::CreateSessionHandles(&server_h, &client_h))) {
|
|
||||||
/* N aborts here. Should we error code? */
|
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
this->session->GetSessionManager()->AddSession(server_h, std::move(object->Clone()));
|
|
||||||
out_h.SetValue(client_h);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CloneCurrentObject(Out<MovedHandle> out_h) {
|
|
||||||
Handle server_h, client_h;
|
|
||||||
if (R_FAILED(SessionManagerBase::CreateSessionHandles(&server_h, &client_h))) {
|
|
||||||
/* N aborts here. Should we error code? */
|
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
this->session->GetSessionManager()->AddSession(server_h, std::move(this->session->obj_holder.Clone()));
|
|
||||||
out_h.SetValue(client_h);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QueryPointerBufferSize(Out<u16> size) {
|
|
||||||
size.SetValue(this->session->pointer_buffer.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
void CloneCurrentObjectEx(Out<MovedHandle> out_h, u32 which) {
|
|
||||||
/* TODO: Figure out what this u32 controls. */
|
|
||||||
return CloneCurrentObject(out_h);
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
DEFINE_SERVICE_DISPATCH_TABLE {
|
|
||||||
MakeServiceCommandMeta<HipcControlCommand_ConvertCurrentObjectToDomain, &ServiceSession::IHipcControlService::ConvertCurrentObjectToDomain>(),
|
|
||||||
MakeServiceCommandMeta<HipcControlCommand_CopyFromCurrentDomain, &ServiceSession::IHipcControlService::CopyFromCurrentDomain>(),
|
|
||||||
MakeServiceCommandMeta<HipcControlCommand_CloneCurrentObject, &ServiceSession::IHipcControlService::CloneCurrentObject>(),
|
|
||||||
MakeServiceCommandMeta<HipcControlCommand_QueryPointerBufferSize, &ServiceSession::IHipcControlService::QueryPointerBufferSize>(),
|
|
||||||
MakeServiceCommandMeta<HipcControlCommand_CloneCurrentObjectEx, &ServiceSession::IHipcControlService::CloneCurrentObjectEx>(),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
|
@ -1,34 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2018 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <atomic>
|
|
||||||
|
|
||||||
#include "../waitable_manager_base.hpp"
|
|
||||||
#include "ipc_service_object.hpp"
|
|
||||||
|
|
||||||
class SessionManagerBase : public WaitableManagerBase, public DomainManager {
|
|
||||||
public:
|
|
||||||
SessionManagerBase() = default;
|
|
||||||
virtual ~SessionManagerBase() = default;
|
|
||||||
|
|
||||||
virtual void AddSession(Handle server_h, ServiceObjectHolder &&service) = 0;
|
|
||||||
|
|
||||||
static Result CreateSessionHandles(Handle *server_h, Handle *client_h) {
|
|
||||||
return svcCreateSession(server_h, client_h, 0, 0);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,144 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2018 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <switch.h>
|
|
||||||
#include <type_traits>
|
|
||||||
|
|
||||||
#include "ipc_out.hpp"
|
|
||||||
|
|
||||||
/* Represents an input PID. */
|
|
||||||
struct PidDescriptorTag{};
|
|
||||||
|
|
||||||
struct PidDescriptor : public PidDescriptorTag {
|
|
||||||
u64 pid;
|
|
||||||
|
|
||||||
void operator=(u64 &p) {
|
|
||||||
pid = p;
|
|
||||||
}
|
|
||||||
|
|
||||||
PidDescriptor(u64 p) : pid(p) { }
|
|
||||||
};
|
|
||||||
|
|
||||||
struct IpcHandleTag{};
|
|
||||||
|
|
||||||
struct IpcHandle : public IpcHandleTag {
|
|
||||||
Handle handle;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Represents a moved handle. */
|
|
||||||
struct MovedHandle : public IpcHandle {
|
|
||||||
void operator=(const Handle &h) {
|
|
||||||
this->handle = h;
|
|
||||||
}
|
|
||||||
|
|
||||||
void operator=(const IpcHandle &o) {
|
|
||||||
this->handle = o.handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
MovedHandle(Handle h) {
|
|
||||||
this->handle = h;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Represents a copied handle. */
|
|
||||||
struct CopiedHandle : public IpcHandle {
|
|
||||||
void operator=(const Handle &h) {
|
|
||||||
handle = h;
|
|
||||||
}
|
|
||||||
|
|
||||||
void operator=(const IpcHandle &o) {
|
|
||||||
this->handle = o.handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
CopiedHandle(Handle h) {
|
|
||||||
this->handle = h;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
class Out<MovedHandle> : public OutHandleTag {
|
|
||||||
private:
|
|
||||||
MovedHandle *obj;
|
|
||||||
public:
|
|
||||||
Out(IpcHandle *o) : obj(static_cast<MovedHandle *>(o)) { }
|
|
||||||
|
|
||||||
void SetValue(const Handle& h) {
|
|
||||||
*obj = h;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetValue(const MovedHandle& o) {
|
|
||||||
*obj = o;
|
|
||||||
}
|
|
||||||
|
|
||||||
const MovedHandle& GetValue() {
|
|
||||||
return *obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
MovedHandle* GetPointer() {
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
Handle* GetHandlePointer() {
|
|
||||||
return &obj->handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Convenience operators. */
|
|
||||||
MovedHandle& operator*() {
|
|
||||||
return *obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
MovedHandle* operator->() {
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
class Out<CopiedHandle> : public OutHandleTag {
|
|
||||||
private:
|
|
||||||
CopiedHandle *obj;
|
|
||||||
public:
|
|
||||||
Out(IpcHandle *o) : obj(static_cast<CopiedHandle *>(o)) { }
|
|
||||||
|
|
||||||
void SetValue(const Handle& h) {
|
|
||||||
*obj = h;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetValue(const CopiedHandle& o) {
|
|
||||||
*obj = o;
|
|
||||||
}
|
|
||||||
|
|
||||||
const CopiedHandle& GetValue() {
|
|
||||||
return *obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
CopiedHandle* GetPointer() {
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
Handle* GetHandlePointer() {
|
|
||||||
return &obj->handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Convenience operators. */
|
|
||||||
CopiedHandle& operator*() {
|
|
||||||
return *obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
CopiedHandle* operator->() {
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,80 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2018 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <switch.h>
|
|
||||||
|
|
||||||
#include "waitable_manager_base.hpp"
|
|
||||||
#include "hossynch.hpp"
|
|
||||||
|
|
||||||
class IWaitable {
|
|
||||||
private:
|
|
||||||
u64 wait_priority = 0;
|
|
||||||
bool is_deferred = false;
|
|
||||||
WaitableManagerBase *manager = nullptr;
|
|
||||||
protected:
|
|
||||||
HosMutex sig_lock;
|
|
||||||
bool is_signaled = false;
|
|
||||||
public:
|
|
||||||
virtual ~IWaitable() = default;
|
|
||||||
|
|
||||||
virtual Result HandleDeferred() {
|
|
||||||
/* By default, HandleDeferred panics, because object shouldn't be deferrable. */
|
|
||||||
std::abort();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsSignaled() {
|
|
||||||
std::scoped_lock<HosMutex> lock(this->sig_lock);
|
|
||||||
return this->is_signaled;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Handle GetHandle() = 0;
|
|
||||||
virtual Result HandleSignaled(u64 timeout) = 0;
|
|
||||||
|
|
||||||
WaitableManagerBase *GetManager() {
|
|
||||||
return this->manager;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetManager(WaitableManagerBase *m) {
|
|
||||||
this->manager = m;
|
|
||||||
}
|
|
||||||
|
|
||||||
void UpdatePriority() {
|
|
||||||
if (manager) {
|
|
||||||
this->wait_priority = this->manager->GetNextPriority();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsDeferred() {
|
|
||||||
return this->is_deferred;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetDeferred(bool d) {
|
|
||||||
this->is_deferred = d;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool Compare(IWaitable *a, IWaitable *b) {
|
|
||||||
return (a->wait_priority < b->wait_priority) && !a->IsDeferred() && (a->GetHandle() != INVALID_HANDLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NotifyManagerSignaled() {
|
|
||||||
if (this->manager) {
|
|
||||||
this->manager->NotifySignaled(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,73 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2018 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <switch.h>
|
|
||||||
#include "hossynch.hpp"
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
class HosMessageQueue {
|
|
||||||
private:
|
|
||||||
HosMutex queue_lock;
|
|
||||||
HosCondVar cv_not_full;
|
|
||||||
HosCondVar cv_not_empty;
|
|
||||||
std::unique_ptr<uintptr_t[]> buffer;
|
|
||||||
size_t capacity;
|
|
||||||
|
|
||||||
size_t count = 0;
|
|
||||||
size_t offset = 0;
|
|
||||||
public:
|
|
||||||
HosMessageQueue(size_t c) : capacity(c) {
|
|
||||||
this->buffer = std::make_unique<uintptr_t[]>(this->capacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
HosMessageQueue(std::unique_ptr<uintptr_t[]> buf, size_t c) : buffer(std::move(buf)), capacity(c) { }
|
|
||||||
|
|
||||||
/* Sending (FIFO functionality) */
|
|
||||||
void Send(uintptr_t data);
|
|
||||||
bool TrySend(uintptr_t data);
|
|
||||||
bool TimedSend(uintptr_t data, u64 timeout);
|
|
||||||
|
|
||||||
/* Sending (LIFO functionality) */
|
|
||||||
void SendNext(uintptr_t data);
|
|
||||||
bool TrySendNext(uintptr_t data);
|
|
||||||
bool TimedSendNext(uintptr_t data, u64 timeout);
|
|
||||||
|
|
||||||
/* Receive functionality */
|
|
||||||
void Receive(uintptr_t *out);
|
|
||||||
bool TryReceive(uintptr_t *out);
|
|
||||||
bool TimedReceive(uintptr_t *out, u64 timeout);
|
|
||||||
|
|
||||||
/* Peek functionality */
|
|
||||||
void Peek(uintptr_t *out);
|
|
||||||
bool TryPeek(uintptr_t *out);
|
|
||||||
bool TimedPeek(uintptr_t *out, u64 timeout);
|
|
||||||
private:
|
|
||||||
bool IsFull() {
|
|
||||||
return this->count >= this->capacity;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsEmpty() {
|
|
||||||
return this->count == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SendInternal(uintptr_t data);
|
|
||||||
void SendNextInternal(uintptr_t data);
|
|
||||||
uintptr_t ReceiveInternal();
|
|
||||||
uintptr_t PeekInternal();
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2018 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "mitm/sm_mitm.h"
|
|
||||||
|
|
||||||
#include "ipc.hpp"
|
|
||||||
|
|
||||||
#include "mitm/imitmserviceobject.hpp"
|
|
||||||
#include "mitm/mitm_query_service.hpp"
|
|
||||||
#include "mitm/mitm_session.hpp"
|
|
||||||
#include "mitm/mitm_server.hpp"
|
|
|
@ -1,43 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2018 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <switch.h>
|
|
||||||
#include <atomic>
|
|
||||||
|
|
||||||
#include <stratosphere.hpp>
|
|
||||||
|
|
||||||
class IMitmServiceObject : public IServiceObject {
|
|
||||||
protected:
|
|
||||||
std::shared_ptr<Service> forward_service;
|
|
||||||
u64 process_id = 0;
|
|
||||||
u64 title_id = 0;
|
|
||||||
public:
|
|
||||||
IMitmServiceObject(std::shared_ptr<Service> s) : forward_service(s) {}
|
|
||||||
|
|
||||||
virtual u64 GetTitleId() {
|
|
||||||
return this->title_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual u64 GetProcessId() {
|
|
||||||
return this->process_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool ShouldMitm(u64 pid, u64 tid);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual ~IMitmServiceObject() = default;
|
|
||||||
};
|
|
|
@ -1,50 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2018 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <switch.h>
|
|
||||||
#include <stratosphere.hpp>
|
|
||||||
|
|
||||||
enum MitmQueryServiceCommand {
|
|
||||||
MQS_Cmd_ShouldMitm = 65000,
|
|
||||||
MQS_Cmd_AssociatePidTid = 65001
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace MitmQueryUtils {
|
|
||||||
Result GetAssociatedTidForPid(u64 pid, u64 *tid);
|
|
||||||
|
|
||||||
void AssociatePidToTid(u64 pid, u64 tid);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
class MitmQueryService : public IServiceObject {
|
|
||||||
protected:
|
|
||||||
void ShouldMitm(Out<bool> should_mitm, u64 pid) {
|
|
||||||
should_mitm.SetValue(false);
|
|
||||||
u64 tid = 0;
|
|
||||||
if (R_SUCCEEDED(MitmQueryUtils::GetAssociatedTidForPid(pid, &tid))) {
|
|
||||||
should_mitm.SetValue(T::ShouldMitm(pid, tid));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void AssociatePidToTid(u64 pid, u64 tid) {
|
|
||||||
MitmQueryUtils::AssociatePidToTid(pid, tid);
|
|
||||||
}
|
|
||||||
public:
|
|
||||||
DEFINE_SERVICE_DISPATCH_TABLE {
|
|
||||||
MakeServiceCommandMeta<MQS_Cmd_ShouldMitm, &MitmQueryService<T>::ShouldMitm>(),
|
|
||||||
MakeServiceCommandMeta<MQS_Cmd_AssociatePidTid, &MitmQueryService<T>::AssociatePidToTid>(),
|
|
||||||
};
|
|
||||||
};
|
|
|
@ -1,112 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2018 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <switch.h>
|
|
||||||
|
|
||||||
#include "mitm_query_service.hpp"
|
|
||||||
#include "sm_mitm.h"
|
|
||||||
#include "mitm_session.hpp"
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
class MitmServer : public IWaitable {
|
|
||||||
static_assert(std::is_base_of<IMitmServiceObject, T>::value, "MitM Service Objects must derive from IMitmServiceObject");
|
|
||||||
private:
|
|
||||||
Handle port_handle;
|
|
||||||
unsigned int max_sessions;
|
|
||||||
char mitm_name[9];
|
|
||||||
|
|
||||||
public:
|
|
||||||
MitmServer(Handle *out_query_h, const char *service_name, unsigned int max_s) : port_handle(0), max_sessions(max_s) {
|
|
||||||
Handle tmp_hnd;
|
|
||||||
Result rc = smMitMInitialize();
|
|
||||||
if (R_FAILED(rc)) {
|
|
||||||
fatalSimple(rc);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (R_SUCCEEDED((rc = smGetServiceOriginal(&tmp_hnd, smEncodeName(service_name))))) {
|
|
||||||
svcCloseHandle(tmp_hnd);
|
|
||||||
} else {
|
|
||||||
fatalSimple(rc);
|
|
||||||
}
|
|
||||||
strncpy(mitm_name, service_name, 8);
|
|
||||||
mitm_name[8] = '\x00';
|
|
||||||
if (R_FAILED((rc = smMitMInstall(&this->port_handle, out_query_h, mitm_name)))) {
|
|
||||||
fatalSimple(rc);
|
|
||||||
}
|
|
||||||
|
|
||||||
smMitMExit();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~MitmServer() override {
|
|
||||||
if (this->port_handle) {
|
|
||||||
if (R_FAILED(smMitMUninstall(this->mitm_name))) {
|
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
svcCloseHandle(port_handle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SessionManagerBase *GetSessionManager() {
|
|
||||||
return static_cast<SessionManagerBase *>(this->GetManager());
|
|
||||||
}
|
|
||||||
|
|
||||||
/* IWaitable */
|
|
||||||
virtual Handle GetHandle() override {
|
|
||||||
return this->port_handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result HandleSignaled(u64 timeout) override {
|
|
||||||
/* If this server's port was signaled, accept a new session. */
|
|
||||||
Handle session_h;
|
|
||||||
Result rc = svcAcceptSession(&session_h, this->port_handle);
|
|
||||||
if (R_FAILED(rc)) {
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Create a forward service for this instance. */
|
|
||||||
std::shared_ptr<Service>forward_service(new Service(), [](Service *s) {
|
|
||||||
/* Custom deleter to ensure service is open as long as necessary. */
|
|
||||||
serviceClose(s);
|
|
||||||
delete s;
|
|
||||||
});
|
|
||||||
|
|
||||||
rc = smMitMInitialize();
|
|
||||||
if (R_FAILED(rc)) {
|
|
||||||
fatalSimple(rc);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (R_FAILED(smMitMGetService(forward_service.get(), mitm_name))) {
|
|
||||||
/* TODO: Panic. */
|
|
||||||
}
|
|
||||||
|
|
||||||
smMitMExit();
|
|
||||||
|
|
||||||
this->GetSessionManager()->AddWaitable(new MitmSession(session_h, forward_service, std::make_shared<T>(forward_service)));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
static void AddMitmServerToManager(SessionManagerBase *manager, const char *srv_name, unsigned int max_sessions) {
|
|
||||||
Handle query_h;
|
|
||||||
auto *srv = new MitmServer<T>(&query_h, srv_name, max_sessions);
|
|
||||||
manager->AddSession(query_h, std::move(ServiceObjectHolder(std::move(std::make_shared<MitmQueryService<T>>()))));
|
|
||||||
manager->AddWaitable(srv);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -1,316 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2018 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <switch.h>
|
|
||||||
#include <stratosphere.hpp>
|
|
||||||
#include "imitmserviceobject.hpp"
|
|
||||||
|
|
||||||
#include "mitm_query_service.hpp"
|
|
||||||
|
|
||||||
#define RESULT_FORWARD_TO_SESSION 0xCAFEFC
|
|
||||||
|
|
||||||
class MitmSession final : public ServiceSession {
|
|
||||||
private:
|
|
||||||
/* This will be for the actual session. */
|
|
||||||
std::shared_ptr<Service> forward_service;
|
|
||||||
|
|
||||||
/* Store a handler for the service. */
|
|
||||||
void (*service_post_process_handler)(IMitmServiceObject *, IpcResponseContext *);
|
|
||||||
|
|
||||||
/* For cleanup usage. */
|
|
||||||
u32 num_fwd_copy_hnds = 0;
|
|
||||||
Handle fwd_copy_hnds[8];
|
|
||||||
public:
|
|
||||||
template<typename T>
|
|
||||||
MitmSession(Handle s_h, std::shared_ptr<Service> fs, std::shared_ptr<T> srv) : ServiceSession(s_h) {
|
|
||||||
this->forward_service = std::move(fs);
|
|
||||||
this->obj_holder = std::move(ServiceObjectHolder(std::move(srv)));
|
|
||||||
|
|
||||||
this->service_post_process_handler = T::PostProcess;
|
|
||||||
|
|
||||||
size_t pbs;
|
|
||||||
if (R_FAILED(ipcQueryPointerBufferSize(forward_service->handle, &pbs))) {
|
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
this->pointer_buffer.resize(pbs);
|
|
||||||
this->control_holder.Reset();
|
|
||||||
this->control_holder = std::move(ServiceObjectHolder(std::move(std::make_shared<IMitmHipcControlService>(this))));
|
|
||||||
}
|
|
||||||
|
|
||||||
MitmSession(Handle s_h, std::shared_ptr<Service> fs, ServiceObjectHolder &&h, void (*pph)(IMitmServiceObject *, IpcResponseContext *)) : ServiceSession(s_h) {
|
|
||||||
this->session_handle = s_h;
|
|
||||||
this->forward_service = std::move(fs);
|
|
||||||
this->obj_holder = std::move(h);
|
|
||||||
|
|
||||||
this->service_post_process_handler = pph;
|
|
||||||
|
|
||||||
size_t pbs;
|
|
||||||
if (R_FAILED(ipcQueryPointerBufferSize(forward_service->handle, &pbs))) {
|
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
this->pointer_buffer.resize(pbs);
|
|
||||||
this->control_holder.Reset();
|
|
||||||
this->control_holder = std::move(ServiceObjectHolder(std::move(std::make_shared<IMitmHipcControlService>(this))));
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void PreProcessRequest(IpcResponseContext *ctx) override {
|
|
||||||
u32 *cmdbuf = (u32 *)armGetTls();
|
|
||||||
u32 *backup_cmdbuf = (u32 *)this->backup_tls;
|
|
||||||
if (ctx->request.HasPid) {
|
|
||||||
/* [ctrl 0] [ctrl 1] [handle desc 0] [pid low] [pid high] */
|
|
||||||
cmdbuf[4] = 0xFFFE0000UL | (cmdbuf[4] & 0xFFFFUL);
|
|
||||||
backup_cmdbuf[4] = cmdbuf[4];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Result ForwardRequest(IpcResponseContext *ctx) {
|
|
||||||
IpcParsedCommand r;
|
|
||||||
Result rc = serviceIpcDispatch(this->forward_service.get());
|
|
||||||
if (R_SUCCEEDED(rc)) {
|
|
||||||
if (ctx->request.IsDomainRequest) {
|
|
||||||
/* We never work with out object ids, so this should be fine. */
|
|
||||||
ipcParseDomainResponse(&r, 0);
|
|
||||||
} else {
|
|
||||||
ipcParse(&r);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct {
|
|
||||||
u64 magic;
|
|
||||||
u64 result;
|
|
||||||
} *resp = (decltype(resp))r.Raw;
|
|
||||||
|
|
||||||
rc = resp->result;
|
|
||||||
|
|
||||||
for (unsigned int i = 0; i < r.NumHandles; i++) {
|
|
||||||
if (r.WasHandleCopied[i]) {
|
|
||||||
this->fwd_copy_hnds[num_fwd_copy_hnds++] = r.Handles[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result GetResponse(IpcResponseContext *ctx) {
|
|
||||||
Result rc = 0xF601;
|
|
||||||
FirmwareVersion fw = GetRuntimeFirmwareVersion();
|
|
||||||
|
|
||||||
const ServiceCommandMeta *dispatch_table = ctx->obj_holder->GetDispatchTable();
|
|
||||||
size_t entry_count = ctx->obj_holder->GetDispatchTableEntryCount();
|
|
||||||
|
|
||||||
if (IsDomainObject(ctx->obj_holder)) {
|
|
||||||
switch (ctx->request.InMessageType) {
|
|
||||||
case DomainMessageType_Invalid:
|
|
||||||
return 0xF601;
|
|
||||||
case DomainMessageType_Close:
|
|
||||||
rc = ForwardRequest(ctx);
|
|
||||||
if (R_SUCCEEDED(rc)) {
|
|
||||||
ctx->obj_holder->GetServiceObject<IDomainObject>()->FreeObject(ctx->request.InThisObjectId);
|
|
||||||
}
|
|
||||||
if (R_SUCCEEDED(rc) && ctx->request.InThisObjectId == serviceGetObjectId(this->forward_service.get())) {
|
|
||||||
/* If we're not longer MitMing anything, we don't need a mitm session. */
|
|
||||||
this->Reply();
|
|
||||||
this->GetSessionManager()->AddSession(this->session_handle, std::move(this->obj_holder));
|
|
||||||
this->session_handle = 0;
|
|
||||||
return 0xF601;
|
|
||||||
}
|
|
||||||
return rc;
|
|
||||||
case DomainMessageType_SendMessage:
|
|
||||||
{
|
|
||||||
auto sub_obj = ctx->obj_holder->GetServiceObject<IDomainObject>()->GetObject(ctx->request.InThisObjectId);
|
|
||||||
if (sub_obj == nullptr) {
|
|
||||||
rc = ForwardRequest(ctx);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
dispatch_table = sub_obj->GetDispatchTable();
|
|
||||||
entry_count = sub_obj->GetDispatchTableEntryCount();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool found_entry = false;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < entry_count; i++) {
|
|
||||||
if (ctx->cmd_id == dispatch_table[i].cmd_id && dispatch_table[i].fw_low <= fw && fw <= dispatch_table[i].fw_high) {
|
|
||||||
rc = dispatch_table[i].handler(ctx);
|
|
||||||
found_entry = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found_entry || rc == RESULT_FORWARD_TO_SESSION) {
|
|
||||||
memcpy(armGetTls(), this->backup_tls, sizeof(this->backup_tls));
|
|
||||||
rc = ForwardRequest(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void PostProcessResponse(IpcResponseContext *ctx) override {
|
|
||||||
if ((ctx->cmd_type == IpcCommandType_Request || ctx->cmd_type == IpcCommandType_RequestWithContext) && R_SUCCEEDED(ctx->rc)) {
|
|
||||||
if (!IsDomainObject(ctx->obj_holder) || ctx->request.InThisObjectId == serviceGetObjectId(this->forward_service.get())) {
|
|
||||||
IMitmServiceObject *obj;
|
|
||||||
if (!IsDomainObject(ctx->obj_holder)) {
|
|
||||||
obj = ctx->obj_holder->GetServiceObjectUnsafe<IMitmServiceObject>();
|
|
||||||
} else {
|
|
||||||
obj = ctx->obj_holder->GetServiceObject<IDomainObject>()->GetObject(ctx->request.InThisObjectId)->GetServiceObjectUnsafe<IMitmServiceObject>();
|
|
||||||
}
|
|
||||||
this->service_post_process_handler(obj, ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void CleanupResponse(IpcResponseContext *ctx) override {
|
|
||||||
/* Cleanup tls backup. */
|
|
||||||
std::memset(this->backup_tls, 0, sizeof(this->backup_tls));
|
|
||||||
|
|
||||||
/* Clean up copy handles. */
|
|
||||||
for (unsigned int i = 0; i < ctx->request.NumHandles; i++) {
|
|
||||||
if (ctx->request.WasHandleCopied[i]) {
|
|
||||||
svcCloseHandle(ctx->request.Handles[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (unsigned int i = 0; i < this->num_fwd_copy_hnds; i++) {
|
|
||||||
svcCloseHandle(this->fwd_copy_hnds[i]);
|
|
||||||
}
|
|
||||||
this->num_fwd_copy_hnds = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
class IMitmHipcControlService : public IServiceObject {
|
|
||||||
private:
|
|
||||||
MitmSession *session;
|
|
||||||
public:
|
|
||||||
explicit IMitmHipcControlService(MitmSession *s) : session(s) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~IMitmHipcControlService() override { }
|
|
||||||
|
|
||||||
public:
|
|
||||||
Result ConvertCurrentObjectToDomain(Out<u32> object_id) {
|
|
||||||
if (IsDomainObject(this->session->obj_holder)) {
|
|
||||||
return 0xF601;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result rc = serviceConvertToDomain(this->session->forward_service.get());
|
|
||||||
if (R_FAILED(rc)) {
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 expected_id = serviceGetObjectId(this->session->forward_service.get());
|
|
||||||
|
|
||||||
/* Allocate new domain. */
|
|
||||||
auto new_domain = this->session->GetDomainManager()->AllocateDomain();
|
|
||||||
if (new_domain == nullptr) {
|
|
||||||
/* If our domains mismatch, we're in trouble. */
|
|
||||||
return 0xF601;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reserve the expected object in the domain for our session. */
|
|
||||||
if (R_FAILED(new_domain->ReserveSpecificObject(expected_id))) {
|
|
||||||
return 0xF601;
|
|
||||||
}
|
|
||||||
new_domain->SetObject(expected_id, std::move(this->session->obj_holder));
|
|
||||||
this->session->obj_holder = std::move(ServiceObjectHolder(std::move(new_domain)));
|
|
||||||
|
|
||||||
/* Return the object id. */
|
|
||||||
object_id.SetValue(expected_id);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result CopyFromCurrentDomain(Out<MovedHandle> out_h, u32 id) {
|
|
||||||
auto domain = this->session->obj_holder.GetServiceObject<IDomainObject>();
|
|
||||||
if (domain == nullptr) {
|
|
||||||
return 0x3D60B;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
auto object = domain->GetObject(id);
|
|
||||||
if (object == nullptr) {
|
|
||||||
/* Forward onwards. */
|
|
||||||
u32 *buf = (u32 *)armGetTls();
|
|
||||||
buf[0] = IpcCommandType_Control;
|
|
||||||
buf[1] = 0xA;
|
|
||||||
buf[4] = SFCI_MAGIC;
|
|
||||||
buf[5] = 0;
|
|
||||||
buf[6] = 1;
|
|
||||||
buf[7] = 0;
|
|
||||||
buf[8] = id;
|
|
||||||
buf[9] = 0;
|
|
||||||
Result rc = ipcDispatch(this->session->forward_service->handle);
|
|
||||||
if (R_SUCCEEDED(rc)) {
|
|
||||||
IpcParsedCommand r;
|
|
||||||
ipcParse(&r);
|
|
||||||
struct {
|
|
||||||
u64 magic;
|
|
||||||
u64 result;
|
|
||||||
} *raw = (decltype(raw))r.Raw;
|
|
||||||
rc = raw->result;
|
|
||||||
|
|
||||||
if (R_SUCCEEDED(rc)) {
|
|
||||||
out_h.SetValue(r.Handles[0]);
|
|
||||||
this->session->fwd_copy_hnds[this->session->num_fwd_copy_hnds++] = r.Handles[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
Handle server_h, client_h;
|
|
||||||
if (R_FAILED(SessionManagerBase::CreateSessionHandles(&server_h, &client_h))) {
|
|
||||||
/* N aborts here. Should we error code? */
|
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
out_h.SetValue(client_h);
|
|
||||||
|
|
||||||
if (id == serviceGetObjectId(this->session->forward_service.get())) {
|
|
||||||
this->session->GetSessionManager()->AddWaitable(new MitmSession(server_h, this->session->forward_service, std::move(object->Clone()), this->session->service_post_process_handler));
|
|
||||||
} else {
|
|
||||||
this->session->GetSessionManager()->AddSession(server_h, std::move(object->Clone()));
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CloneCurrentObject(Out<MovedHandle> out_h) {
|
|
||||||
Handle server_h, client_h;
|
|
||||||
if (R_FAILED(SessionManagerBase::CreateSessionHandles(&server_h, &client_h))) {
|
|
||||||
/* N aborts here. Should we error code? */
|
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
this->session->GetSessionManager()->AddWaitable(new MitmSession(server_h, this->session->forward_service, std::move(this->session->obj_holder.Clone()), this->session->service_post_process_handler));
|
|
||||||
out_h.SetValue(client_h);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QueryPointerBufferSize(Out<u16> size) {
|
|
||||||
size.SetValue(this->session->pointer_buffer.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
void CloneCurrentObjectEx(Out<MovedHandle> out_h, u32 which) {
|
|
||||||
/* TODO: Figure out what this u32 controls. */
|
|
||||||
return CloneCurrentObject(out_h);
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
DEFINE_SERVICE_DISPATCH_TABLE {
|
|
||||||
MakeServiceCommandMeta<HipcControlCommand_ConvertCurrentObjectToDomain, &MitmSession::IMitmHipcControlService::ConvertCurrentObjectToDomain>(),
|
|
||||||
MakeServiceCommandMeta<HipcControlCommand_CopyFromCurrentDomain, &MitmSession::IMitmHipcControlService::CopyFromCurrentDomain>(),
|
|
||||||
MakeServiceCommandMeta<HipcControlCommand_CloneCurrentObject, &MitmSession::IMitmHipcControlService::CloneCurrentObject>(),
|
|
||||||
MakeServiceCommandMeta<HipcControlCommand_QueryPointerBufferSize, &MitmSession::IMitmHipcControlService::QueryPointerBufferSize>(),
|
|
||||||
MakeServiceCommandMeta<HipcControlCommand_CloneCurrentObjectEx, &MitmSession::IMitmHipcControlService::CloneCurrentObjectEx>(),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
|
@ -1,24 +0,0 @@
|
||||||
/**
|
|
||||||
* @file sm_mitm.h
|
|
||||||
* @brief Service manager (sm) IPC wrapper for Atmosphere extensions.
|
|
||||||
* @author SciresM
|
|
||||||
* @copyright libnx Authors
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
#include <switch.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
Result smMitMInitialize(void);
|
|
||||||
void smMitMExit(void);
|
|
||||||
Result smMitMGetService(Service* service_out, const char *name);
|
|
||||||
Result smMitMInstall(Handle *handle_out, Handle *query_out, const char *name);
|
|
||||||
Result smMitMUninstall(const char *name);
|
|
||||||
|
|
||||||
Result smMitMIsRegistered(const char *name);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
|
@ -1,61 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2018 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Scope guard logic lovingly taken from Andrei Alexandrescu's "Systemic Error Handling in C++" */
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
template<class F>
|
|
||||||
class ScopeGuard {
|
|
||||||
private:
|
|
||||||
F f;
|
|
||||||
bool active;
|
|
||||||
public:
|
|
||||||
ScopeGuard(F f) : f(std::move(f)), active(true) { }
|
|
||||||
~ScopeGuard() { if (active) { f(); } }
|
|
||||||
void Cancel() { active = false; }
|
|
||||||
|
|
||||||
ScopeGuard() = delete;
|
|
||||||
ScopeGuard(const ScopeGuard &) = delete;
|
|
||||||
ScopeGuard& operator=(const ScopeGuard&) = delete;
|
|
||||||
ScopeGuard(ScopeGuard&& rhs) : f(std::move(rhs.f)), active(rhs.active) {
|
|
||||||
rhs.Cancel();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class F>
|
|
||||||
ScopeGuard<F> MakeScopeGuard(F f) {
|
|
||||||
return ScopeGuard<F>(std::move(f));
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class ScopeGuardOnExit {};
|
|
||||||
|
|
||||||
template <typename F>
|
|
||||||
ScopeGuard<F> operator+(ScopeGuardOnExit, F&& f) {
|
|
||||||
return ScopeGuard<F>(std::forward<F>(f));
|
|
||||||
}
|
|
||||||
|
|
||||||
#define CONCATENATE_IMPL(S1, s2) s1##s2
|
|
||||||
#define CONCATENATE(s1, s2) CONCATENATE_IMPL(s1, s2)
|
|
||||||
|
|
||||||
#ifdef __COUNTER__
|
|
||||||
#define ANONYMOUS_VARIABLE(pref) CONCATENATE(pref, __COUNTER__)
|
|
||||||
#else
|
|
||||||
#define ANONYMOUS_VARIABLE(pref) CONCATENATE(pref, __LINE__)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define ON_SCOPE_EXIT auto ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE_) = ScopeGuardOnExit() + [&]()
|
|
|
@ -1,87 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2018 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <switch.h>
|
|
||||||
|
|
||||||
#include "iwaitable.hpp"
|
|
||||||
#include "ipc.hpp"
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
class IServer : public IWaitable {
|
|
||||||
static_assert(std::is_base_of<IServiceObject, T>::value, "Service Objects must derive from IServiceObject");
|
|
||||||
protected:
|
|
||||||
Handle port_handle;
|
|
||||||
unsigned int max_sessions;
|
|
||||||
|
|
||||||
public:
|
|
||||||
IServer(unsigned int max_s) : port_handle(0), max_sessions(max_s) { }
|
|
||||||
|
|
||||||
virtual ~IServer() {
|
|
||||||
if (port_handle) {
|
|
||||||
svcCloseHandle(port_handle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SessionManagerBase *GetSessionManager() {
|
|
||||||
return static_cast<SessionManagerBase *>(this->GetManager());
|
|
||||||
}
|
|
||||||
|
|
||||||
/* IWaitable */
|
|
||||||
virtual Handle GetHandle() override {
|
|
||||||
return this->port_handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result HandleSignaled(u64 timeout) override {
|
|
||||||
/* If this server's port was signaled, accept a new session. */
|
|
||||||
Handle session_h;
|
|
||||||
Result rc = svcAcceptSession(&session_h, this->port_handle);
|
|
||||||
if (R_FAILED(rc)) {
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->GetSessionManager()->AddSession(session_h, std::move(ServiceObjectHolder(std::move(std::make_shared<T>()))));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
class ServiceServer : public IServer<T> {
|
|
||||||
public:
|
|
||||||
ServiceServer(const char *service_name, unsigned int max_s) : IServer<T>(max_s) {
|
|
||||||
if (R_FAILED(smRegisterService(&this->port_handle, service_name, false, this->max_sessions))) {
|
|
||||||
/* TODO: Panic. */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
class ExistingPortServer : public IServer<T> {
|
|
||||||
public:
|
|
||||||
ExistingPortServer(Handle port_h, unsigned int max_s) : IServer<T>(max_s) {
|
|
||||||
this->port_handle = port_h;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
class ManagedPortServer : public IServer<T> {
|
|
||||||
public:
|
|
||||||
ManagedPortServer(const char *service_name, unsigned int max_s) : IServer<T>(max_s) {
|
|
||||||
if (R_FAILED(svcManageNamedPort(&this->port_handle, service_name, this->max_sessions))) {
|
|
||||||
/* TODO: panic */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,21 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2018 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "ipc.hpp"
|
|
||||||
|
|
||||||
#include "services/smm_ams.h"
|
|
|
@ -1,31 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2018 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <switch.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
Result smManagerAmsInitialize(void);
|
|
||||||
void smManagerAmsExit(void);
|
|
||||||
|
|
||||||
Result smManagerAmsEndInitialDefers(void);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
|
@ -1,66 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2018 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <switch.h>
|
|
||||||
#include <atmosphere/version.h>
|
|
||||||
|
|
||||||
static inline void GetAtmosphereApiVersion(u32 *major, u32 *minor, u32 *micro, u32 *target_fw, u32 *mkey_rev) {
|
|
||||||
if (R_FAILED(splInitialize())) {
|
|
||||||
fatalSimple(0xCAFE << 4 | 0xD);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check for exosphere API compatibility. */
|
|
||||||
u64 exosphere_cfg;
|
|
||||||
if (R_FAILED(splGetConfig((SplConfigItem)65000, &exosphere_cfg))) {
|
|
||||||
fatalSimple(0xCAFE << 4 | 0xE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mkey_rev) {
|
|
||||||
*mkey_rev = (u32)((exosphere_cfg >> 0x00) & 0xFF);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (target_fw) {
|
|
||||||
*target_fw = (u32)((exosphere_cfg >> 0x08) & 0xFF);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (micro) {
|
|
||||||
*micro = (u32)((exosphere_cfg >> 0x10) & 0xFF);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (minor) {
|
|
||||||
*minor = (u32)((exosphere_cfg >> 0x18) & 0xFF);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (major) {
|
|
||||||
*major = (u32)((exosphere_cfg >> 0x20) & 0xFF);
|
|
||||||
}
|
|
||||||
|
|
||||||
splExit();
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline u32 MakeAtmosphereVersion(u32 major, u32 minor, u32 micro) {
|
|
||||||
return (major << 16) | (minor << 8) | micro;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void CheckAtmosphereVersion() {
|
|
||||||
u32 major, minor, micro;
|
|
||||||
GetAtmosphereApiVersion(&major, &minor, µ, nullptr, nullptr);
|
|
||||||
|
|
||||||
if (MakeAtmosphereVersion(major, minor, micro) < MakeAtmosphereVersion(ATMOSPHERE_RELEASE_VERSION_MAJOR, ATMOSPHERE_RELEASE_VERSION_MINOR, ATMOSPHERE_RELEASE_VERSION_MICRO)) {
|
|
||||||
fatalSimple(0xCAFE << 4 | 0xF);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,404 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2018 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <switch.h>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include "../meta_tools.hpp"
|
|
||||||
|
|
||||||
#include "waitable_manager_base.hpp"
|
|
||||||
#include "event.hpp"
|
|
||||||
#include "ipc.hpp"
|
|
||||||
#include "servers.hpp"
|
|
||||||
|
|
||||||
#include "scope_guard.hpp"
|
|
||||||
|
|
||||||
static inline Handle GetCurrentThreadHandle() {
|
|
||||||
return threadGetCurHandle();
|
|
||||||
}
|
|
||||||
|
|
||||||
struct DefaultManagerOptions {
|
|
||||||
static constexpr size_t PointerBufferSize = 0;
|
|
||||||
static constexpr size_t MaxDomains = 0;
|
|
||||||
static constexpr size_t MaxDomainObjects = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct DomainEntry {
|
|
||||||
ServiceObjectHolder obj_holder;
|
|
||||||
IDomainObject *owner = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename ManagerOptions = DefaultManagerOptions>
|
|
||||||
class WaitableManager : public SessionManagerBase {
|
|
||||||
private:
|
|
||||||
/* Domain Manager */
|
|
||||||
HosMutex domain_lock;
|
|
||||||
std::array<std::weak_ptr<IDomainObject>, ManagerOptions::MaxDomains> domains;
|
|
||||||
std::array<bool, ManagerOptions::MaxDomains> is_domain_allocated;
|
|
||||||
std::array<DomainEntry, ManagerOptions::MaxDomainObjects> domain_objects;
|
|
||||||
|
|
||||||
/* Waitable Manager */
|
|
||||||
std::vector<IWaitable *> to_add_waitables;
|
|
||||||
std::vector<IWaitable *> waitables;
|
|
||||||
std::vector<IWaitable *> deferred_waitables;
|
|
||||||
|
|
||||||
u32 num_extra_threads = 0;
|
|
||||||
HosThread *threads = nullptr;
|
|
||||||
|
|
||||||
HosMutex process_lock;
|
|
||||||
HosMutex signal_lock;
|
|
||||||
HosMutex add_lock;
|
|
||||||
HosMutex cur_thread_lock;
|
|
||||||
HosMutex deferred_lock;
|
|
||||||
bool has_new_waitables = false;
|
|
||||||
std::atomic<bool> should_stop = false;
|
|
||||||
|
|
||||||
IWaitable *next_signaled = nullptr;
|
|
||||||
Handle main_thread_handle = INVALID_HANDLE;
|
|
||||||
Handle cur_thread_handle = INVALID_HANDLE;
|
|
||||||
public:
|
|
||||||
WaitableManager(u32 n, u32 ss = 0x8000) : num_extra_threads(n-1) {
|
|
||||||
u32 prio;
|
|
||||||
Result rc;
|
|
||||||
if (num_extra_threads) {
|
|
||||||
threads = new HosThread[num_extra_threads];
|
|
||||||
if (R_FAILED((rc = svcGetThreadPriority(&prio, CUR_THREAD_HANDLE)))) {
|
|
||||||
fatalSimple(rc);
|
|
||||||
}
|
|
||||||
for (unsigned int i = 0; i < num_extra_threads; i++) {
|
|
||||||
if (R_FAILED(threads[i].Initialize(&WaitableManager::ProcessLoop, this, ss, prio))) {
|
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
~WaitableManager() override {
|
|
||||||
/* This should call the destructor for every waitable. */
|
|
||||||
std::for_each(to_add_waitables.begin(), to_add_waitables.end(), std::default_delete<IWaitable>{});
|
|
||||||
std::for_each(waitables.begin(), waitables.end(), std::default_delete<IWaitable>{});
|
|
||||||
std::for_each(deferred_waitables.begin(), deferred_waitables.end(), std::default_delete<IWaitable>{});
|
|
||||||
|
|
||||||
/* If we've reached here, we should already have exited the threads. */
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void AddWaitable(IWaitable *w) override {
|
|
||||||
std::scoped_lock lk{this->add_lock};
|
|
||||||
this->to_add_waitables.push_back(w);
|
|
||||||
w->SetManager(this);
|
|
||||||
this->has_new_waitables = true;
|
|
||||||
this->CancelSynchronization();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void RequestStop() {
|
|
||||||
this->should_stop = true;
|
|
||||||
this->CancelSynchronization();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void CancelSynchronization() {
|
|
||||||
svcCancelSynchronization(GetProcessingThreadHandle());
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void NotifySignaled(IWaitable *w) override {
|
|
||||||
std::scoped_lock lk{this->signal_lock};
|
|
||||||
if (this->next_signaled == nullptr) {
|
|
||||||
this->next_signaled = w;
|
|
||||||
}
|
|
||||||
this->CancelSynchronization();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void Process() override {
|
|
||||||
/* Add initial set of waitables. */
|
|
||||||
AddWaitablesInternal();
|
|
||||||
|
|
||||||
/* Set main thread handle. */
|
|
||||||
this->main_thread_handle = GetCurrentThreadHandle();
|
|
||||||
|
|
||||||
Result rc;
|
|
||||||
for (unsigned int i = 0; i < num_extra_threads; i++) {
|
|
||||||
if (R_FAILED((rc = threads[i].Start()))) {
|
|
||||||
fatalSimple(rc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ProcessLoop(this);
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
void SetProcessingThreadHandle(Handle h) {
|
|
||||||
std::scoped_lock<HosMutex> lk{this->cur_thread_lock};
|
|
||||||
this->cur_thread_handle = h;
|
|
||||||
}
|
|
||||||
|
|
||||||
Handle GetProcessingThreadHandle() {
|
|
||||||
std::scoped_lock<HosMutex> lk{this->cur_thread_lock};
|
|
||||||
return this->cur_thread_handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ProcessLoop(void *t) {
|
|
||||||
WaitableManager *this_ptr = (WaitableManager *)t;
|
|
||||||
while (true) {
|
|
||||||
IWaitable *w = this_ptr->GetWaitable();
|
|
||||||
if (this_ptr->should_stop) {
|
|
||||||
if (GetCurrentThreadHandle() == this_ptr->main_thread_handle) {
|
|
||||||
/* Join all threads but the main one. */
|
|
||||||
for (unsigned int i = 0; i < this_ptr->num_extra_threads; i++) {
|
|
||||||
this_ptr->threads[i].Join();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
svcExitThread();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (w) {
|
|
||||||
Result rc = w->HandleSignaled(0);
|
|
||||||
if (rc == 0xF601) {
|
|
||||||
/* Close! */
|
|
||||||
delete w;
|
|
||||||
} else {
|
|
||||||
if (w->IsDeferred()) {
|
|
||||||
std::scoped_lock lk{this_ptr->deferred_lock};
|
|
||||||
this_ptr->deferred_waitables.push_back(w);
|
|
||||||
} else {
|
|
||||||
this_ptr->AddWaitable(w);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We finished processing, and maybe that means we can stop deferring an object. */
|
|
||||||
{
|
|
||||||
std::scoped_lock lk{this_ptr->deferred_lock};
|
|
||||||
|
|
||||||
for (auto it = this_ptr->deferred_waitables.begin(); it != this_ptr->deferred_waitables.end();) {
|
|
||||||
auto w = *it;
|
|
||||||
Result rc = w->HandleDeferred();
|
|
||||||
if (rc == 0xF601 || !w->IsDeferred()) {
|
|
||||||
/* Remove from the deferred list, set iterator. */
|
|
||||||
it = this_ptr->deferred_waitables.erase(it);
|
|
||||||
if (rc == 0xF601) {
|
|
||||||
/* Delete the closed waitable. */
|
|
||||||
delete w;
|
|
||||||
} else {
|
|
||||||
/* Add to the waitables list. */
|
|
||||||
this_ptr->AddWaitable(w);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* Move on to the next deferred waitable. */
|
|
||||||
it++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
IWaitable *GetWaitable() {
|
|
||||||
std::scoped_lock lk{this->process_lock};
|
|
||||||
|
|
||||||
/* Set processing thread handle while in scope. */
|
|
||||||
SetProcessingThreadHandle(GetCurrentThreadHandle());
|
|
||||||
ON_SCOPE_EXIT {
|
|
||||||
SetProcessingThreadHandle(INVALID_HANDLE);
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Prepare variables for result. */
|
|
||||||
this->next_signaled = nullptr;
|
|
||||||
IWaitable *result = nullptr;
|
|
||||||
|
|
||||||
if (this->should_stop) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add new waitables, if any. */
|
|
||||||
AddWaitablesInternal();
|
|
||||||
|
|
||||||
/* First, see if anything's already signaled. */
|
|
||||||
for (auto &w : this->waitables) {
|
|
||||||
if (w->IsSignaled()) {
|
|
||||||
result = w;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* It's possible somebody signaled us while we were iterating. */
|
|
||||||
{
|
|
||||||
std::scoped_lock lk{this->signal_lock};
|
|
||||||
if (this->next_signaled != nullptr) result = this->next_signaled;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result == nullptr) {
|
|
||||||
std::vector<Handle> handles;
|
|
||||||
std::vector<IWaitable *> wait_list;
|
|
||||||
|
|
||||||
int handle_index = 0;
|
|
||||||
Result rc;
|
|
||||||
while (result == nullptr) {
|
|
||||||
/* Sort waitables by priority. */
|
|
||||||
std::sort(this->waitables.begin(), this->waitables.end(), IWaitable::Compare);
|
|
||||||
|
|
||||||
/* Copy out handles. */
|
|
||||||
handles.resize(this->waitables.size());
|
|
||||||
wait_list.resize(this->waitables.size());
|
|
||||||
unsigned int num_handles = 0;
|
|
||||||
|
|
||||||
/* Try to add waitables to wait list. */
|
|
||||||
for (unsigned int i = 0; i < this->waitables.size(); i++) {
|
|
||||||
Handle h = this->waitables[i]->GetHandle();
|
|
||||||
if (h != INVALID_HANDLE) {
|
|
||||||
wait_list[num_handles] = this->waitables[i];
|
|
||||||
handles[num_handles++] = h;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Wait forever. */
|
|
||||||
rc = svcWaitSynchronization(&handle_index, handles.data(), num_handles, U64_MAX);
|
|
||||||
|
|
||||||
if (this->should_stop) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (R_SUCCEEDED(rc)) {
|
|
||||||
IWaitable *w = wait_list[handle_index];
|
|
||||||
size_t w_ind = std::distance(this->waitables.begin(), std::find(this->waitables.begin(), this->waitables.end(), w));
|
|
||||||
std::for_each(waitables.begin(), waitables.begin() + w_ind + 1, std::mem_fn(&IWaitable::UpdatePriority));
|
|
||||||
result = w;
|
|
||||||
} else if (rc == 0xEA01) {
|
|
||||||
/* Timeout: Just update priorities. */
|
|
||||||
std::for_each(waitables.begin(), waitables.end(), std::mem_fn(&IWaitable::UpdatePriority));
|
|
||||||
} else if (rc == 0xEC01) {
|
|
||||||
/* svcCancelSynchronization was called. */
|
|
||||||
AddWaitablesInternal();
|
|
||||||
{
|
|
||||||
std::scoped_lock lk{this->signal_lock};
|
|
||||||
if (this->next_signaled != nullptr) {
|
|
||||||
result = this->next_signaled;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* TODO: Consider the following cases that this covers: */
|
|
||||||
/* 7601: Thread termination requested. */
|
|
||||||
/* E401: Handle is dead. */
|
|
||||||
/* E601: Handle list address invalid. */
|
|
||||||
/* EE01: Too many handles. */
|
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this->waitables.erase(std::remove_if(this->waitables.begin(), this->waitables.end(), [&](IWaitable *w) { return w == result; }), this->waitables.end());
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddWaitablesInternal() {
|
|
||||||
std::scoped_lock lk{this->add_lock};
|
|
||||||
if (this->has_new_waitables) {
|
|
||||||
this->waitables.insert(this->waitables.end(), this->to_add_waitables.begin(), this->to_add_waitables.end());
|
|
||||||
this->to_add_waitables.clear();
|
|
||||||
this->has_new_waitables = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Session Manager */
|
|
||||||
public:
|
|
||||||
virtual void AddSession(Handle server_h, ServiceObjectHolder &&service) override {
|
|
||||||
this->AddWaitable(new ServiceSession(server_h, ManagerOptions::PointerBufferSize, std::move(service)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Domain Manager */
|
|
||||||
public:
|
|
||||||
virtual std::shared_ptr<IDomainObject> AllocateDomain() override {
|
|
||||||
std::scoped_lock lk{this->domain_lock};
|
|
||||||
for (size_t i = 0; i < ManagerOptions::MaxDomains; i++) {
|
|
||||||
if (!this->is_domain_allocated[i]) {
|
|
||||||
auto new_domain = std::make_shared<IDomainObject>(this);
|
|
||||||
this->domains[i] = new_domain;
|
|
||||||
this->is_domain_allocated[i] = true;
|
|
||||||
return new_domain;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FreeDomain(IDomainObject *domain) override {
|
|
||||||
std::scoped_lock lk{this->domain_lock};
|
|
||||||
for (size_t i = 0; i < ManagerOptions::MaxDomainObjects; i++) {
|
|
||||||
FreeObject(domain, i+1);
|
|
||||||
}
|
|
||||||
for (size_t i = 0; i < ManagerOptions::MaxDomains; i++) {
|
|
||||||
auto observe = this->domains[i].lock();
|
|
||||||
if (observe.get() == domain) {
|
|
||||||
this->is_domain_allocated[i] = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result ReserveObject(IDomainObject *domain, u32 *out_object_id) override {
|
|
||||||
std::scoped_lock lk{this->domain_lock};
|
|
||||||
for (size_t i = 0; i < ManagerOptions::MaxDomainObjects; i++) {
|
|
||||||
if (this->domain_objects[i].owner == nullptr) {
|
|
||||||
this->domain_objects[i].owner = domain;
|
|
||||||
*out_object_id = i+1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0x25A0A;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result ReserveSpecificObject(IDomainObject *domain, u32 object_id) override {
|
|
||||||
std::scoped_lock lk{this->domain_lock};
|
|
||||||
if (this->domain_objects[object_id-1].owner == nullptr) {
|
|
||||||
this->domain_objects[object_id-1].owner = domain;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return 0x25A0A;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void SetObject(IDomainObject *domain, u32 object_id, ServiceObjectHolder&& holder) override {
|
|
||||||
std::scoped_lock lk{this->domain_lock};
|
|
||||||
if (this->domain_objects[object_id-1].owner == domain) {
|
|
||||||
this->domain_objects[object_id-1].obj_holder = std::move(holder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ServiceObjectHolder *GetObject(IDomainObject *domain, u32 object_id) override {
|
|
||||||
std::scoped_lock lk{this->domain_lock};
|
|
||||||
if (this->domain_objects[object_id-1].owner == domain) {
|
|
||||||
return &this->domain_objects[object_id-1].obj_holder;
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result FreeObject(IDomainObject *domain, u32 object_id) override {
|
|
||||||
std::scoped_lock lk{this->domain_lock};
|
|
||||||
if (this->domain_objects[object_id-1].owner == domain) {
|
|
||||||
this->domain_objects[object_id-1].obj_holder.Reset();
|
|
||||||
this->domain_objects[object_id-1].owner = nullptr;
|
|
||||||
return 0x0;
|
|
||||||
}
|
|
||||||
return 0x3D80B;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result ForceFreeObject(u32 object_id) override {
|
|
||||||
std::scoped_lock lk{this->domain_lock};
|
|
||||||
if (this->domain_objects[object_id-1].owner != nullptr) {
|
|
||||||
this->domain_objects[object_id-1].obj_holder.Reset();
|
|
||||||
this->domain_objects[object_id-1].owner = nullptr;
|
|
||||||
return 0x0;
|
|
||||||
}
|
|
||||||
return 0x3D80B;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2018 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <atomic>
|
|
||||||
|
|
||||||
class IWaitable;
|
|
||||||
|
|
||||||
class WaitableManagerBase {
|
|
||||||
std::atomic<u64> cur_priority = 0;
|
|
||||||
public:
|
|
||||||
WaitableManagerBase() = default;
|
|
||||||
virtual ~WaitableManagerBase() = default;
|
|
||||||
|
|
||||||
u64 GetNextPriority() {
|
|
||||||
return std::atomic_fetch_add(&cur_priority, (u64)1);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void AddWaitable(IWaitable *w) = 0;
|
|
||||||
virtual void NotifySignaled(IWaitable *w) = 0;
|
|
||||||
|
|
||||||
virtual void RequestStop() = 0;
|
|
||||||
virtual void Process() = 0;
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,235 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2018 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <mutex>
|
|
||||||
#include <switch.h>
|
|
||||||
#include <stratosphere.hpp>
|
|
||||||
|
|
||||||
|
|
||||||
void HosMessageQueue::Send(uintptr_t data) {
|
|
||||||
/* Acquire mutex, wait sendable. */
|
|
||||||
std::scoped_lock<HosMutex> lock(this->queue_lock);
|
|
||||||
|
|
||||||
while (this->IsFull()) {
|
|
||||||
this->cv_not_full.Wait(&this->queue_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Send, signal. */
|
|
||||||
this->SendInternal(data);
|
|
||||||
this->cv_not_empty.WakeAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HosMessageQueue::TrySend(uintptr_t data) {
|
|
||||||
std::scoped_lock<HosMutex> lock(this->queue_lock);
|
|
||||||
if (this->IsFull()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Send, signal. */
|
|
||||||
this->SendInternal(data);
|
|
||||||
this->cv_not_empty.WakeAll();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HosMessageQueue::TimedSend(uintptr_t data, u64 timeout) {
|
|
||||||
std::scoped_lock<HosMutex> lock(this->queue_lock);
|
|
||||||
TimeoutHelper timeout_helper(timeout);
|
|
||||||
|
|
||||||
while (this->IsFull()) {
|
|
||||||
if (timeout_helper.TimedOut()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->cv_not_full.TimedWait(timeout, &this->queue_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Send, signal. */
|
|
||||||
this->SendInternal(data);
|
|
||||||
this->cv_not_empty.WakeAll();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HosMessageQueue::SendNext(uintptr_t data) {
|
|
||||||
/* Acquire mutex, wait sendable. */
|
|
||||||
std::scoped_lock<HosMutex> lock(this->queue_lock);
|
|
||||||
|
|
||||||
while (this->IsFull()) {
|
|
||||||
this->cv_not_full.Wait(&this->queue_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Send, signal. */
|
|
||||||
this->SendNextInternal(data);
|
|
||||||
this->cv_not_empty.WakeAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HosMessageQueue::TrySendNext(uintptr_t data) {
|
|
||||||
std::scoped_lock<HosMutex> lock(this->queue_lock);
|
|
||||||
if (this->IsFull()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Send, signal. */
|
|
||||||
this->SendNextInternal(data);
|
|
||||||
this->cv_not_empty.WakeAll();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HosMessageQueue::TimedSendNext(uintptr_t data, u64 timeout) {
|
|
||||||
std::scoped_lock<HosMutex> lock(this->queue_lock);
|
|
||||||
TimeoutHelper timeout_helper(timeout);
|
|
||||||
|
|
||||||
while (this->IsFull()) {
|
|
||||||
if (timeout_helper.TimedOut()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->cv_not_full.TimedWait(timeout, &this->queue_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Send, signal. */
|
|
||||||
this->SendNextInternal(data);
|
|
||||||
this->cv_not_empty.WakeAll();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HosMessageQueue::Receive(uintptr_t *out) {
|
|
||||||
/* Acquire mutex, wait receivable. */
|
|
||||||
std::scoped_lock<HosMutex> lock(this->queue_lock);
|
|
||||||
|
|
||||||
while (this->IsEmpty()) {
|
|
||||||
this->cv_not_empty.Wait(&this->queue_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Receive, signal. */
|
|
||||||
*out = this->ReceiveInternal();
|
|
||||||
this->cv_not_full.WakeAll();
|
|
||||||
}
|
|
||||||
bool HosMessageQueue::TryReceive(uintptr_t *out) {
|
|
||||||
/* Acquire mutex, wait receivable. */
|
|
||||||
std::scoped_lock<HosMutex> lock(this->queue_lock);
|
|
||||||
|
|
||||||
if (this->IsEmpty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Receive, signal. */
|
|
||||||
*out = this->ReceiveInternal();
|
|
||||||
this->cv_not_full.WakeAll();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HosMessageQueue::TimedReceive(uintptr_t *out, u64 timeout) {
|
|
||||||
std::scoped_lock<HosMutex> lock(this->queue_lock);
|
|
||||||
TimeoutHelper timeout_helper(timeout);
|
|
||||||
|
|
||||||
while (this->IsEmpty()) {
|
|
||||||
if (timeout_helper.TimedOut()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->cv_not_empty.TimedWait(timeout, &this->queue_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Receive, signal. */
|
|
||||||
*out = this->ReceiveInternal();
|
|
||||||
this->cv_not_full.WakeAll();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HosMessageQueue::Peek(uintptr_t *out) {
|
|
||||||
/* Acquire mutex, wait receivable. */
|
|
||||||
std::scoped_lock<HosMutex> lock(this->queue_lock);
|
|
||||||
|
|
||||||
while (this->IsEmpty()) {
|
|
||||||
this->cv_not_empty.Wait(&this->queue_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Peek. */
|
|
||||||
*out = this->PeekInternal();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HosMessageQueue::TryPeek(uintptr_t *out) {
|
|
||||||
/* Acquire mutex, wait receivable. */
|
|
||||||
std::scoped_lock<HosMutex> lock(this->queue_lock);
|
|
||||||
|
|
||||||
if (this->IsEmpty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Peek. */
|
|
||||||
*out = this->PeekInternal();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HosMessageQueue::TimedPeek(uintptr_t *out, u64 timeout) {
|
|
||||||
std::scoped_lock<HosMutex> lock(this->queue_lock);
|
|
||||||
TimeoutHelper timeout_helper(timeout);
|
|
||||||
|
|
||||||
while (this->IsEmpty()) {
|
|
||||||
if (timeout_helper.TimedOut()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->cv_not_empty.TimedWait(timeout, &this->queue_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Peek. */
|
|
||||||
*out = this->PeekInternal();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HosMessageQueue::SendInternal(uintptr_t data) {
|
|
||||||
/* Ensure we don't corrupt the queue, but this should never happen. */
|
|
||||||
if (this->count >= this->capacity) {
|
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Write data to tail of queue. */
|
|
||||||
this->buffer[(this->count++ + this->offset) % this->capacity] = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HosMessageQueue::SendNextInternal(uintptr_t data) {
|
|
||||||
/* Ensure we don't corrupt the queue, but this should never happen. */
|
|
||||||
if (this->count >= this->capacity) {
|
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Write data to head of queue. */
|
|
||||||
this->offset = (this->offset + this->capacity - 1) % this->capacity;
|
|
||||||
this->buffer[this->offset] = data;
|
|
||||||
this->count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
uintptr_t HosMessageQueue::ReceiveInternal() {
|
|
||||||
/* Ensure we don't corrupt the queue, but this should never happen. */
|
|
||||||
if (this->count == 0) {
|
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
uintptr_t data = this->buffer[this->offset];
|
|
||||||
this->offset = (this->offset + 1) % this->capacity;
|
|
||||||
this->count--;
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
uintptr_t HosMessageQueue::PeekInternal() {
|
|
||||||
/* Ensure we don't corrupt the queue, but this should never happen. */
|
|
||||||
if (this->count == 0) {
|
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
return this->buffer[this->offset];
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2018 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <mutex>
|
|
||||||
#include <switch.h>
|
|
||||||
#include <stratosphere.hpp>
|
|
||||||
|
|
||||||
static std::vector<u64> g_known_pids;
|
|
||||||
static std::vector<u64> g_known_tids;
|
|
||||||
static HosMutex g_pid_tid_mutex;
|
|
||||||
|
|
||||||
Result MitmQueryUtils::GetAssociatedTidForPid(u64 pid, u64 *tid) {
|
|
||||||
Result rc = 0xCAFE;
|
|
||||||
std::scoped_lock lk{g_pid_tid_mutex};
|
|
||||||
for (unsigned int i = 0; i < g_known_pids.size(); i++) {
|
|
||||||
if (g_known_pids[i] == pid) {
|
|
||||||
*tid = g_known_tids[i];
|
|
||||||
rc = 0x0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MitmQueryUtils::AssociatePidToTid(u64 pid, u64 tid) {
|
|
||||||
std::scoped_lock lk{g_pid_tid_mutex};
|
|
||||||
g_known_pids.push_back(pid);
|
|
||||||
g_known_tids.push_back(tid);
|
|
||||||
}
|
|
|
@ -1,190 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2018 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <switch.h>
|
|
||||||
#include <switch/arm/atomics.h>
|
|
||||||
#include <stratosphere/mitm/sm_mitm.h>
|
|
||||||
|
|
||||||
static Handle g_smMitmHandle = INVALID_HANDLE;
|
|
||||||
static u64 g_refCnt;
|
|
||||||
|
|
||||||
Result smMitMInitialize(void) {
|
|
||||||
atomicIncrement64(&g_refCnt);
|
|
||||||
|
|
||||||
if (g_smMitmHandle != INVALID_HANDLE)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
Result rc = svcConnectToNamedPort(&g_smMitmHandle, "sm:");
|
|
||||||
|
|
||||||
if (R_SUCCEEDED(rc)) {
|
|
||||||
IpcCommand c;
|
|
||||||
ipcInitialize(&c);
|
|
||||||
ipcSendPid(&c);
|
|
||||||
|
|
||||||
struct {
|
|
||||||
u64 magic;
|
|
||||||
u64 cmd_id;
|
|
||||||
u64 zero;
|
|
||||||
u64 reserved[2];
|
|
||||||
} *raw;
|
|
||||||
|
|
||||||
raw = ipcPrepareHeader(&c, sizeof(*raw));
|
|
||||||
|
|
||||||
raw->magic = SFCI_MAGIC;
|
|
||||||
raw->cmd_id = 0;
|
|
||||||
raw->zero = 0;
|
|
||||||
|
|
||||||
rc = ipcDispatch(g_smMitmHandle);
|
|
||||||
|
|
||||||
if (R_SUCCEEDED(rc)) {
|
|
||||||
IpcParsedCommand r;
|
|
||||||
ipcParse(&r);
|
|
||||||
|
|
||||||
struct {
|
|
||||||
u64 magic;
|
|
||||||
u64 result;
|
|
||||||
} *resp = r.Raw;
|
|
||||||
|
|
||||||
rc = resp->result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (R_FAILED(rc))
|
|
||||||
smExit();
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
void smMitMExit(void) {
|
|
||||||
if (atomicDecrement64(&g_refCnt) == 0) {
|
|
||||||
svcCloseHandle(g_smMitmHandle);
|
|
||||||
g_smMitmHandle = INVALID_HANDLE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Result smMitMGetService(Service* service_out, const char *name_str)
|
|
||||||
{
|
|
||||||
u64 name = smEncodeName(name_str);
|
|
||||||
IpcCommand c;
|
|
||||||
ipcInitialize(&c);
|
|
||||||
|
|
||||||
struct {
|
|
||||||
u64 magic;
|
|
||||||
u64 cmd_id;
|
|
||||||
u64 service_name;
|
|
||||||
u64 reserved[2];
|
|
||||||
} *raw;
|
|
||||||
|
|
||||||
raw = ipcPrepareHeader(&c, sizeof(*raw));
|
|
||||||
|
|
||||||
raw->magic = SFCI_MAGIC;
|
|
||||||
raw->cmd_id = 1;
|
|
||||||
raw->service_name = name;
|
|
||||||
|
|
||||||
Result rc = ipcDispatch(g_smMitmHandle);
|
|
||||||
|
|
||||||
if (R_SUCCEEDED(rc)) {
|
|
||||||
IpcParsedCommand r;
|
|
||||||
ipcParse(&r);
|
|
||||||
|
|
||||||
struct {
|
|
||||||
u64 magic;
|
|
||||||
u64 result;
|
|
||||||
} *resp = r.Raw;
|
|
||||||
|
|
||||||
rc = resp->result;
|
|
||||||
|
|
||||||
if (R_SUCCEEDED(rc)) {
|
|
||||||
service_out->type = ServiceType_Normal;
|
|
||||||
service_out->handle = r.Handles[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Result smMitMInstall(Handle *handle_out, Handle *query_out, const char *name) {
|
|
||||||
IpcCommand c;
|
|
||||||
ipcInitialize(&c);
|
|
||||||
|
|
||||||
struct {
|
|
||||||
u64 magic;
|
|
||||||
u64 cmd_id;
|
|
||||||
u64 service_name;
|
|
||||||
} *raw;
|
|
||||||
|
|
||||||
raw = ipcPrepareHeader(&c, sizeof(*raw));
|
|
||||||
|
|
||||||
raw->magic = SFCI_MAGIC;
|
|
||||||
raw->cmd_id = 65000;
|
|
||||||
raw->service_name = smEncodeName(name);
|
|
||||||
|
|
||||||
Result rc = ipcDispatch(g_smMitmHandle);
|
|
||||||
|
|
||||||
if (R_SUCCEEDED(rc)) {
|
|
||||||
IpcParsedCommand r;
|
|
||||||
ipcParse(&r);
|
|
||||||
|
|
||||||
struct {
|
|
||||||
u64 magic;
|
|
||||||
u64 result;
|
|
||||||
} *resp = r.Raw;
|
|
||||||
|
|
||||||
rc = resp->result;
|
|
||||||
|
|
||||||
if (R_SUCCEEDED(rc)) {
|
|
||||||
*handle_out = r.Handles[0];
|
|
||||||
*query_out = r.Handles[1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result smMitMUninstall(const char *name) {
|
|
||||||
IpcCommand c;
|
|
||||||
ipcInitialize(&c);
|
|
||||||
|
|
||||||
struct {
|
|
||||||
u64 magic;
|
|
||||||
u64 cmd_id;
|
|
||||||
u64 service_name;
|
|
||||||
u64 reserved;
|
|
||||||
} *raw;
|
|
||||||
|
|
||||||
raw = ipcPrepareHeader(&c, sizeof(*raw));
|
|
||||||
|
|
||||||
raw->magic = SFCI_MAGIC;
|
|
||||||
raw->cmd_id = 65001;
|
|
||||||
raw->service_name = smEncodeName(name);
|
|
||||||
|
|
||||||
Result rc = ipcDispatch(g_smMitmHandle);
|
|
||||||
|
|
||||||
if (R_SUCCEEDED(rc)) {
|
|
||||||
IpcParsedCommand r;
|
|
||||||
ipcParse(&r);
|
|
||||||
|
|
||||||
struct {
|
|
||||||
u64 magic;
|
|
||||||
u64 result;
|
|
||||||
} *resp = r.Raw;
|
|
||||||
|
|
||||||
rc = resp->result;
|
|
||||||
}
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
|
|
@ -1,69 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2018 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <switch.h>
|
|
||||||
#include <switch/arm/atomics.h>
|
|
||||||
#include <stratosphere/services/smm_ams.h>
|
|
||||||
|
|
||||||
static Service g_smManagerAmsSrv;
|
|
||||||
static u64 g_smManagerAmsRefcnt;
|
|
||||||
|
|
||||||
Result smManagerAmsInitialize(void) {
|
|
||||||
atomicIncrement64(&g_smManagerAmsRefcnt);
|
|
||||||
|
|
||||||
if (serviceIsActive(&g_smManagerAmsSrv))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return smGetService(&g_smManagerAmsSrv, "sm:m");
|
|
||||||
}
|
|
||||||
|
|
||||||
void smManagerAmsExit(void) {
|
|
||||||
if (atomicDecrement64(&g_smManagerAmsRefcnt) == 0)
|
|
||||||
serviceClose(&g_smManagerAmsSrv);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result smManagerAmsEndInitialDefers(void) {
|
|
||||||
IpcCommand c;
|
|
||||||
ipcInitialize(&c);
|
|
||||||
|
|
||||||
struct {
|
|
||||||
u64 magic;
|
|
||||||
u64 cmd_id;
|
|
||||||
} *raw;
|
|
||||||
|
|
||||||
raw = serviceIpcPrepareHeader(&g_smManagerAmsSrv, &c, sizeof(*raw));
|
|
||||||
raw->magic = SFCI_MAGIC;
|
|
||||||
raw->cmd_id = 65000;
|
|
||||||
|
|
||||||
|
|
||||||
Result rc = serviceIpcDispatch(&g_smManagerAmsSrv);
|
|
||||||
|
|
||||||
if (R_SUCCEEDED(rc)) {
|
|
||||||
IpcParsedCommand r;
|
|
||||||
struct {
|
|
||||||
u64 magic;
|
|
||||||
u64 result;
|
|
||||||
} *resp;
|
|
||||||
|
|
||||||
serviceIpcParse(&g_smManagerAmsSrv, &r, sizeof(*resp));
|
|
||||||
resp = r.Raw;
|
|
||||||
|
|
||||||
rc = resp->result;
|
|
||||||
}
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
}
|
|
Loading…
Reference in a new issue