mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-22 20:31:14 +00:00
libstratosphere: refactor everything
This commit is contained in:
parent
5c147e5188
commit
058f735031
41 changed files with 3184 additions and 1013 deletions
|
@ -16,22 +16,17 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "stratosphere/ipc_templating.hpp"
|
||||
#include "stratosphere/version_check.hpp"
|
||||
#include "stratosphere/scope_guard.hpp"
|
||||
|
||||
#include "stratosphere/iwaitable.hpp"
|
||||
#include "stratosphere/iserviceobject.hpp"
|
||||
#include "stratosphere/iserver.hpp"
|
||||
#include "stratosphere/ipcsession.hpp"
|
||||
#include "stratosphere/servicesession.hpp"
|
||||
#include "stratosphere/serviceserver.hpp"
|
||||
#include "stratosphere/managedportserver.hpp"
|
||||
#include "stratosphere/existingportserver.hpp"
|
||||
|
||||
#include "stratosphere/ievent.hpp"
|
||||
#include "stratosphere/systemevent.hpp"
|
||||
#include "stratosphere/hossynch.hpp"
|
||||
#include "stratosphere/iwaitable.hpp"
|
||||
#include "stratosphere/event.hpp"
|
||||
|
||||
#include "stratosphere/waitablemanager.hpp"
|
||||
#include "stratosphere/multithreadedwaitablemanager.hpp"
|
||||
#include "stratosphere/waitable_manager.hpp"
|
||||
|
||||
#include "stratosphere/version_check.hpp"
|
||||
#include "stratosphere/ipc.hpp"
|
||||
|
||||
#include "stratosphere/mitm.hpp"
|
||||
|
||||
#include "stratosphere/services.hpp"
|
|
@ -1,79 +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 "iserviceobject.hpp"
|
||||
|
||||
#define DOMAIN_ID_MAX 0x1000
|
||||
|
||||
class IServiceObject;
|
||||
|
||||
class DomainOwner {
|
||||
private:
|
||||
std::array<std::shared_ptr<IServiceObject>, DOMAIN_ID_MAX> domain_objects;
|
||||
public:
|
||||
/* Shared ptrs should auto delete here. */
|
||||
virtual ~DomainOwner() = default;
|
||||
|
||||
std::shared_ptr<IServiceObject> get_domain_object(unsigned int i) {
|
||||
if (i < DOMAIN_ID_MAX) {
|
||||
return domain_objects[i];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Result reserve_object(std::shared_ptr<IServiceObject> object, unsigned int *out_i) {
|
||||
auto object_it = std::find(domain_objects.begin() + 4, domain_objects.end(), nullptr);
|
||||
if (object_it == domain_objects.end()) {
|
||||
return 0x1900B;
|
||||
}
|
||||
|
||||
*out_i = std::distance(domain_objects.begin(), object_it);
|
||||
*object_it = object;
|
||||
object->set_owner(this);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result set_object(std::shared_ptr<IServiceObject> object, unsigned int i) {
|
||||
if (domain_objects[i] == nullptr) {
|
||||
domain_objects[i] = object;
|
||||
object->set_owner(this);
|
||||
return 0;
|
||||
}
|
||||
return 0x1900B;
|
||||
}
|
||||
|
||||
unsigned int get_object_id(std::shared_ptr<IServiceObject> object) {
|
||||
auto object_it = std::find(domain_objects.begin(), domain_objects.end(), object);
|
||||
return std::distance(domain_objects.begin(), object_it);
|
||||
}
|
||||
|
||||
void delete_object(unsigned int i) {
|
||||
domain_objects[i].reset();
|
||||
}
|
||||
|
||||
void delete_object(std::shared_ptr<IServiceObject> object) {
|
||||
auto object_it = std::find(domain_objects.begin(), domain_objects.end(), object);
|
||||
if (object_it != domain_objects.end()) {
|
||||
object_it->reset();
|
||||
}
|
||||
}
|
||||
};
|
127
stratosphere/libstratosphere/include/stratosphere/event.hpp
Normal file
127
stratosphere/libstratosphere/include/stratosphere/event.hpp
Normal file
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* 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);
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <mutex>
|
||||
|
||||
class HosMutex {
|
||||
private:
|
||||
|
@ -40,6 +41,18 @@ class HosMutex {
|
|||
return mutexTryLock(GetMutex());
|
||||
}
|
||||
|
||||
void Lock() {
|
||||
lock();
|
||||
}
|
||||
|
||||
void Unlock() {
|
||||
unlock();
|
||||
}
|
||||
|
||||
bool TryLock() {
|
||||
return try_lock();
|
||||
}
|
||||
|
||||
friend class HosCondVar;
|
||||
};
|
||||
|
||||
|
@ -65,6 +78,18 @@ class HosRecursiveMutex {
|
|||
bool try_lock() {
|
||||
return rmutexTryLock(GetMutex());
|
||||
}
|
||||
|
||||
void Lock() {
|
||||
lock();
|
||||
}
|
||||
|
||||
void Unlock() {
|
||||
unlock();
|
||||
}
|
||||
|
||||
bool TryLock() {
|
||||
return try_lock();
|
||||
}
|
||||
};
|
||||
|
||||
class HosCondVar {
|
||||
|
|
|
@ -1,68 +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"
|
||||
|
||||
typedef Result (*EventCallback)(void *arg, Handle *handles, size_t num_handles, u64 timeout);
|
||||
|
||||
class IEvent : public IWaitable {
|
||||
protected:
|
||||
std::vector<Handle> handles;
|
||||
EventCallback callback;
|
||||
void *arg;
|
||||
|
||||
public:
|
||||
IEvent(Handle wait_h, void *a, EventCallback callback) {
|
||||
if (wait_h) {
|
||||
this->handles.push_back(wait_h);
|
||||
}
|
||||
this->arg = a;
|
||||
this->callback = callback;
|
||||
}
|
||||
|
||||
~IEvent() {
|
||||
std::for_each(handles.begin(), handles.end(), svcCloseHandle);
|
||||
}
|
||||
|
||||
virtual Result signal_event() = 0;
|
||||
|
||||
/* IWaitable */
|
||||
virtual Handle get_handle() {
|
||||
if (handles.size() > 0) {
|
||||
return this->handles[0];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
virtual void handle_deferred() {
|
||||
/* TODO: Panic, because we can never defer an event. */
|
||||
}
|
||||
|
||||
virtual Result handle_signaled(u64 timeout) {
|
||||
return this->callback(this->arg, this->handles.data(), this->handles.size(), timeout);
|
||||
}
|
||||
|
||||
static Result PanicCallback(void *arg, Handle *handles, size_t num_handles, u64 timeout) {
|
||||
/* TODO: Panic. */
|
||||
return 0xCAFE;
|
||||
}
|
||||
};
|
22
stratosphere/libstratosphere/include/stratosphere/ipc.hpp
Normal file
22
stratosphere/libstratosphere/include/stratosphere/ipc.hpp
Normal file
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* 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"
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* 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)) { }
|
||||
};
|
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
* 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);
|
||||
}
|
||||
};
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* 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;
|
||||
};
|
|
@ -16,18 +16,29 @@
|
|||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include "iserver.hpp"
|
||||
|
||||
template <typename T>
|
||||
class ManagedPortServer : public IServer<T> {
|
||||
public:
|
||||
ManagedPortServer(const char *service_name, unsigned int max_s, bool s_d = false) : IServer<T>(service_name, max_s, s_d) {
|
||||
if (R_FAILED(svcManageNamedPort(&this->port_handle, service_name, this->max_sessions))) {
|
||||
/* TODO: panic */
|
||||
}
|
||||
}
|
||||
|
||||
ISession<T> *get_new_session(Handle session_h) override {
|
||||
return new ServiceSession<T>(this, session_h, 0);
|
||||
}
|
||||
#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;
|
||||
};
|
|
@ -0,0 +1,629 @@
|
|||
/*
|
||||
* 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 T>
|
||||
struct RawSizeElementAdder {
|
||||
static constexpr size_t GetUpdateElementSize(size_t &size) {
|
||||
constexpr size_t t_align = RawDataHelper<T>::align;
|
||||
constexpr size_t t_size = RawDataHelper<T>::size;
|
||||
if (size % t_align == 0) {
|
||||
size += t_align - (size % t_align);
|
||||
}
|
||||
size += t_size;
|
||||
return size;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Ts>
|
||||
struct GetRawDataSize;
|
||||
|
||||
template<typename... Ts>
|
||||
struct GetRawDataSize<std::tuple<Ts...>> {
|
||||
static constexpr size_t Size() {
|
||||
if constexpr (sizeof...(Ts) == 0) {
|
||||
return 0;
|
||||
} else {
|
||||
size_t s = 0;
|
||||
size_t ends[] = { RawSizeElementAdder<Ts>::GetUpdateElementSize(s)... };
|
||||
return (ends[sizeof...(Ts)-1] + 3) & ~3;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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 size_t InRawArgSize = GetRawDataSize<InDatas>::Size();
|
||||
static constexpr size_t InRawArgSizeWithOutPointers = ((InRawArgSize + NumClientSizeOutPointers * sizeof(u16)) + 3) & ~3;
|
||||
|
||||
static constexpr size_t OutRawArgSize = GetRawDataSize<OutDatas>::Size();
|
||||
};
|
||||
|
||||
|
||||
/* ================================================================================= */
|
||||
/* 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.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.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. */
|
||||
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_rd_offset, size_t& out_rd_offset, 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) {
|
||||
constexpr size_t t_align = RawDataHelper<T>::align;
|
||||
constexpr size_t t_size = RawDataHelper<T>::size;
|
||||
if (in_rd_offset % t_align) {
|
||||
in_rd_offset += (t_align - (in_rd_offset % t_align));
|
||||
}
|
||||
uintptr_t ptr = ((uintptr_t)ctx->request.Raw + 0x10 + in_rd_offset);
|
||||
in_rd_offset += t_size;
|
||||
*(u64 *)ptr = ctx->request.Pid;
|
||||
return T(ctx->request.Pid);
|
||||
} else if constexpr (argT == ArgType::InData) {
|
||||
constexpr size_t t_align = RawDataHelper<T>::align;
|
||||
constexpr size_t t_size = RawDataHelper<T>::size;
|
||||
if (in_rd_offset % t_align) {
|
||||
in_rd_offset += (t_align - (in_rd_offset % t_align));
|
||||
}
|
||||
uintptr_t ptr = ((uintptr_t)ctx->request.Raw + 0x10 + in_rd_offset);
|
||||
in_rd_offset += t_size;
|
||||
if constexpr (std::is_same_v<bool, T>) {
|
||||
return *((u8 *)ptr) & 1;
|
||||
} else {
|
||||
return *((T *)ptr);
|
||||
}
|
||||
} else if constexpr (argT == ArgType::OutData) {
|
||||
constexpr size_t t_align = RawDataHelper<T>::align;
|
||||
constexpr size_t t_size = RawDataHelper<T>::size;
|
||||
if (out_rd_offset % t_align) {
|
||||
out_rd_offset += (t_align - (out_rd_offset % t_align));
|
||||
}
|
||||
uintptr_t ptr = ((uintptr_t)ctx->out_data + out_rd_offset);
|
||||
out_rd_offset += t_size;
|
||||
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_rd_offset, size_t& out_rd_offset, 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_rd_offset, out_rd_offset, pb_offset, c_sz_offset)
|
||||
...
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<typename MetaInfo>
|
||||
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_rd_offset = 0x0, out_rd_offset = 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_rd_offset, out_rd_offset, 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 (IsDomainObject(ctx->obj_holder) && R_FAILED(rc)) {
|
||||
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::Decode<CommandMetaData>(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
|
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* 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() {
|
||||
if (GetServiceId() == ServiceObjectId<ServiceImpl>()) {
|
||||
return static_cast<ServiceImpl *>(this->srv.get());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename ServiceImpl>
|
||||
ServiceImpl *GetServiceObjectUnsafe() {
|
||||
return static_cast<ServiceImpl *>(this->srv.get());
|
||||
}
|
||||
|
||||
const ServiceCommandMeta *GetDispatchTable() {
|
||||
return this->dispatch_table;
|
||||
}
|
||||
|
||||
size_t GetDispatchTableEntryCount() {
|
||||
return this->entry_count;
|
||||
}
|
||||
|
||||
constexpr uintptr_t GetServiceId() {
|
||||
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() {
|
||||
return this->srv != nullptr;
|
||||
}
|
||||
|
||||
bool operator!() {
|
||||
return this->srv == nullptr;
|
||||
}
|
||||
|
||||
void Reset() {
|
||||
this->srv.reset();
|
||||
this->dispatch_table = nullptr;
|
||||
this->entry_count = 0;
|
||||
}
|
||||
|
||||
ServiceObjectHolder Clone() {
|
||||
return ServiceObjectHolder(*this);
|
||||
}
|
||||
};
|
|
@ -0,0 +1,358 @@
|
|||
/*
|
||||
* 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 void HandleDeferred() override {
|
||||
memcpy(armGetTls(), this->backup_tls, sizeof(this->backup_tls));
|
||||
Result rc = this->HandleReceived();
|
||||
|
||||
if (rc != RESULT_DEFER_SESSION) {
|
||||
this->SetDeferred(false);
|
||||
if (rc == 0xF601) {
|
||||
svcCloseHandle(this->GetHandle());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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>(),
|
||||
};
|
||||
};
|
||||
};
|
|
@ -15,19 +15,20 @@
|
|||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include "iserver.hpp"
|
||||
#include <atomic>
|
||||
|
||||
template <typename T>
|
||||
class ServiceServer : public IServer<T> {
|
||||
#include "../waitable_manager_base.hpp"
|
||||
#include "ipc_service_object.hpp"
|
||||
|
||||
class SessionManagerBase : public WaitableManagerBase, public DomainManager {
|
||||
public:
|
||||
ServiceServer(const char *service_name, unsigned int max_s, bool s_d = false) : IServer<T>(service_name, max_s, s_d) {
|
||||
if (R_FAILED(smRegisterService(&this->port_handle, service_name, false, this->max_sessions))) {
|
||||
/* TODO: Panic. */
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
ISession<T> *get_new_session(Handle session_h) override {
|
||||
return new ServiceSession<T>(this, session_h, 0);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
};
|
|
@ -337,7 +337,7 @@ struct Validator<std::tuple<Args...>> {
|
|||
}
|
||||
|
||||
if (((u32 *)r.Raw)[0] != SFCI_MAGIC) {
|
||||
//return 0xF601;
|
||||
return 0xF601;
|
||||
}
|
||||
|
||||
size_t a_index = 0, b_index = num_inbuffers_in_arguments<Args ...>::value, x_index = 0, c_index = 0, h_index = 0;
|
|
@ -1,47 +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_templating.hpp"
|
||||
#include "iserviceobject.hpp"
|
||||
#include "iwaitable.hpp"
|
||||
#include "isession.hpp"
|
||||
|
||||
template <typename T>
|
||||
class IPCSession final : public ISession<T> {
|
||||
static_assert(std::is_base_of<IServiceObject, T>::value, "Service Objects must derive from IServiceObject");
|
||||
|
||||
public:
|
||||
IPCSession<T>(size_t pbs = 0x400) : ISession<T>(NULL, 0, 0, 0) {
|
||||
Result rc;
|
||||
if (R_FAILED((rc = svcCreateSession(&this->server_handle, &this->client_handle, 0, 0)))) {
|
||||
fatalSimple(rc);
|
||||
}
|
||||
this->service_object = std::make_shared<T>();
|
||||
this->pointer_buffer.resize(pbs);
|
||||
}
|
||||
|
||||
IPCSession<T>(std::shared_ptr<T> so, size_t pbs = 0x400) : ISession<T>(NULL, 0, 0, so, 0) {
|
||||
Result rc;
|
||||
if (R_FAILED((rc = svcCreateSession(&this->server_handle, &this->client_handle, 0, 0)))) {
|
||||
fatalSimple(rc);
|
||||
}
|
||||
this->pointer_buffer.resize(pbs);
|
||||
}
|
||||
};
|
|
@ -1,40 +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>
|
||||
|
||||
template <typename T>
|
||||
class ISession;
|
||||
|
||||
class DomainOwner;
|
||||
|
||||
class IServiceObject {
|
||||
private:
|
||||
DomainOwner *owner = NULL;
|
||||
public:
|
||||
virtual ~IServiceObject() { }
|
||||
|
||||
virtual IServiceObject *clone() = 0;
|
||||
|
||||
bool is_domain() { return this->owner != NULL; }
|
||||
DomainOwner *get_owner() { return this->owner; }
|
||||
void set_owner(DomainOwner *owner) { this->owner = owner; }
|
||||
virtual Result dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size) = 0;
|
||||
virtual Result handle_deferred() = 0;
|
||||
};
|
||||
|
||||
#include "domainowner.hpp"
|
|
@ -1,294 +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_templating.hpp"
|
||||
#include "iserviceobject.hpp"
|
||||
#include "iwaitable.hpp"
|
||||
#include "iserver.hpp"
|
||||
|
||||
#include "domainowner.hpp"
|
||||
|
||||
enum IpcControlCommand {
|
||||
IpcCtrl_Cmd_ConvertCurrentObjectToDomain = 0,
|
||||
IpcCtrl_Cmd_CopyFromCurrentDomain = 1,
|
||||
IpcCtrl_Cmd_CloneCurrentObject = 2,
|
||||
IpcCtrl_Cmd_QueryPointerBufferSize = 3,
|
||||
IpcCtrl_Cmd_CloneCurrentObjectEx = 4
|
||||
};
|
||||
|
||||
#define RESULT_DEFER_SESSION (0x6580A)
|
||||
|
||||
|
||||
template <typename T>
|
||||
class IServer;
|
||||
|
||||
class IServiceObject;
|
||||
|
||||
template <typename T>
|
||||
class ISession : public IWaitable {
|
||||
static_assert(std::is_base_of<IServiceObject, T>::value, "Service Objects must derive from IServiceObject");
|
||||
protected:
|
||||
std::shared_ptr<T> service_object;
|
||||
IServer<T> *server;
|
||||
Handle server_handle;
|
||||
Handle client_handle;
|
||||
std::vector<char> pointer_buffer;
|
||||
|
||||
bool is_domain = false;
|
||||
std::shared_ptr<DomainOwner> domain;
|
||||
|
||||
|
||||
std::shared_ptr<IServiceObject> active_object;
|
||||
|
||||
public:
|
||||
ISession<T>(IServer<T> *s, Handle s_h, Handle c_h, size_t pbs = 0x400) : server(s), server_handle(s_h), client_handle(c_h), pointer_buffer(pbs) {
|
||||
this->service_object = std::make_shared<T>();
|
||||
}
|
||||
|
||||
ISession<T>(IServer<T> *s, Handle s_h, Handle c_h, std::shared_ptr<T> so, size_t pbs = 0x400) : service_object(so), server(s), server_handle(s_h), client_handle(c_h), pointer_buffer(pbs) {
|
||||
}
|
||||
|
||||
~ISession() override {
|
||||
if (server_handle) {
|
||||
svcCloseHandle(server_handle);
|
||||
}
|
||||
if (client_handle) {
|
||||
svcCloseHandle(client_handle);
|
||||
}
|
||||
}
|
||||
|
||||
void close_handles() {
|
||||
if (server_handle) {
|
||||
svcCloseHandle(server_handle);
|
||||
server_handle = 0;
|
||||
}
|
||||
if (client_handle) {
|
||||
svcCloseHandle(client_handle);
|
||||
client_handle = 0;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<T> get_service_object() { return this->service_object; }
|
||||
Handle get_server_handle() { return this->server_handle; }
|
||||
Handle get_client_handle() { return this->client_handle; }
|
||||
|
||||
|
||||
DomainOwner *get_owner() { return this->is_domain ? this->domain.get() : NULL; }
|
||||
|
||||
/* IWaitable */
|
||||
Handle get_handle() override {
|
||||
return this->server_handle;
|
||||
}
|
||||
|
||||
void handle_deferred() override {
|
||||
Result rc = this->service_object->handle_deferred();
|
||||
int handle_index;
|
||||
|
||||
if (rc != RESULT_DEFER_SESSION) {
|
||||
this->set_deferred(false);
|
||||
if (rc == 0xF601) {
|
||||
svcCloseHandle(this->get_handle());
|
||||
} else {
|
||||
rc = svcReplyAndReceive(&handle_index, &this->server_handle, 0, this->server_handle, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
virtual Result handle_message(IpcParsedCommand &r) {
|
||||
Result retval = 0xF601;
|
||||
|
||||
IpcCommand c;
|
||||
ipcInitialize(&c);
|
||||
|
||||
if (r.IsDomainRequest && this->active_object == NULL) {
|
||||
return 0xF601;
|
||||
}
|
||||
|
||||
|
||||
if (r.IsDomainRequest && r.InMessageType == DomainMessageType_Close) {
|
||||
this->domain->delete_object(this->active_object);
|
||||
this->active_object = NULL;
|
||||
struct {
|
||||
u64 magic;
|
||||
u64 result;
|
||||
} *raw = (decltype(raw))ipcPrepareHeader(&c, sizeof(*raw));
|
||||
|
||||
raw->magic = SFCO_MAGIC;
|
||||
raw->result = 0x0;
|
||||
return 0x0;
|
||||
}
|
||||
|
||||
u64 cmd_id = ((u32 *)r.Raw)[2];
|
||||
switch (r.CommandType) {
|
||||
case IpcCommandType_Close:
|
||||
/* TODO: This should close the session and clean up its resources. */
|
||||
retval = 0xF601;
|
||||
break;
|
||||
case IpcCommandType_LegacyControl:
|
||||
/* TODO: What does this allow one to do? */
|
||||
retval = 0xF601;
|
||||
break;
|
||||
case IpcCommandType_LegacyRequest:
|
||||
/* TODO: What does this allow one to do? */
|
||||
retval = 0xF601;
|
||||
break;
|
||||
case IpcCommandType_Request:
|
||||
case IpcCommandType_RequestWithContext:
|
||||
retval = this->active_object->dispatch(r, c, cmd_id, (u8 *)pointer_buffer.data(), pointer_buffer.size());
|
||||
break;
|
||||
case IpcCommandType_Control:
|
||||
case IpcCommandType_ControlWithContext:
|
||||
retval = this->dispatch_control_command(r, c, cmd_id);
|
||||
break;
|
||||
case IpcCommandType_Invalid:
|
||||
default:
|
||||
retval = 0xF601;
|
||||
break;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
virtual void postprocess(IpcParsedCommand &r, u64 cmd_id) {
|
||||
/* ... */
|
||||
(void)(r);
|
||||
(void)(cmd_id);
|
||||
}
|
||||
|
||||
virtual void cleanup() {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
Result handle_signaled(u64 timeout) override {
|
||||
Result rc;
|
||||
int handle_index;
|
||||
|
||||
/* Prepare pointer buffer... */
|
||||
IpcCommand c_for_reply;
|
||||
ipcInitialize(&c_for_reply);
|
||||
ipcAddRecvStatic(&c_for_reply, this->pointer_buffer.data(), this->pointer_buffer.size(), 0);
|
||||
ipcPrepareHeader(&c_for_reply, 0);
|
||||
|
||||
/* Fix libnx bug in serverside C descriptor handling. */
|
||||
((u32 *)armGetTls())[1] &= 0xFFFFC3FF;
|
||||
((u32 *)armGetTls())[1] |= (2) << 10;
|
||||
|
||||
if (R_SUCCEEDED(rc = svcReplyAndReceive(&handle_index, &this->server_handle, 1, 0, U64_MAX))) {
|
||||
if (handle_index != 0) {
|
||||
/* TODO: Panic? */
|
||||
}
|
||||
IpcParsedCommand r;
|
||||
u64 cmd_id = 0;
|
||||
|
||||
|
||||
Result retval = ipcParse(&r);
|
||||
if (R_SUCCEEDED(retval)) {
|
||||
if (this->is_domain && (r.CommandType == IpcCommandType_Request || r.CommandType == IpcCommandType_RequestWithContext)) {
|
||||
retval = ipcParseDomainRequest(&r);
|
||||
if (!r.IsDomainRequest || r.InThisObjectId >= DOMAIN_ID_MAX) {
|
||||
retval = 0xF601;
|
||||
} else {
|
||||
this->active_object = this->domain->get_domain_object(r.InThisObjectId);
|
||||
}
|
||||
} else {
|
||||
this->active_object = this->service_object;
|
||||
}
|
||||
}
|
||||
if (R_SUCCEEDED(retval)) {
|
||||
cmd_id = ((u32 *)r.Raw)[2];
|
||||
}
|
||||
if (R_SUCCEEDED(retval)) {
|
||||
retval = this->handle_message(r);
|
||||
}
|
||||
|
||||
if (retval == RESULT_DEFER_SESSION) {
|
||||
/* Session defer. */
|
||||
this->active_object.reset();
|
||||
this->set_deferred(true);
|
||||
rc = retval;
|
||||
} else if (retval == 0xF601) {
|
||||
/* Session close. */
|
||||
this->active_object.reset();
|
||||
rc = retval;
|
||||
} else {
|
||||
if (R_SUCCEEDED(retval)) {
|
||||
this->postprocess(r, cmd_id);
|
||||
}
|
||||
this->active_object.reset();
|
||||
rc = svcReplyAndReceive(&handle_index, &this->server_handle, 0, this->server_handle, 0);
|
||||
if (rc == 0xEA01) {
|
||||
rc = 0x0;
|
||||
}
|
||||
this->cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
Result dispatch_control_command(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id) {
|
||||
Result rc = 0xF601;
|
||||
|
||||
/* TODO: Implement. */
|
||||
switch ((IpcControlCommand)cmd_id) {
|
||||
case IpcCtrl_Cmd_ConvertCurrentObjectToDomain:
|
||||
rc = WrapIpcCommandImpl<&ISession::ConvertCurrentObjectToDomain>(this, r, out_c, (u8 *)this->pointer_buffer.data(), pointer_buffer.size());
|
||||
break;
|
||||
case IpcCtrl_Cmd_CopyFromCurrentDomain:
|
||||
rc = WrapIpcCommandImpl<&ISession::CopyFromCurrentDomain>(this, r, out_c, (u8 *)this->pointer_buffer.data(), pointer_buffer.size());
|
||||
break;
|
||||
case IpcCtrl_Cmd_CloneCurrentObject:
|
||||
rc = WrapIpcCommandImpl<&ISession::CloneCurrentObject>(this, r, out_c, (u8 *)this->pointer_buffer.data(), pointer_buffer.size());
|
||||
break;
|
||||
case IpcCtrl_Cmd_QueryPointerBufferSize:
|
||||
rc = WrapIpcCommandImpl<&ISession::QueryPointerBufferSize>(this, r, out_c, (u8 *)this->pointer_buffer.data(), pointer_buffer.size());
|
||||
break;
|
||||
case IpcCtrl_Cmd_CloneCurrentObjectEx:
|
||||
rc = WrapIpcCommandImpl<&ISession::CloneCurrentObjectEx>(this, r, out_c, (u8 *)this->pointer_buffer.data(), pointer_buffer.size());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Control commands. */
|
||||
std::tuple<Result> ConvertCurrentObjectToDomain() {
|
||||
/* TODO */
|
||||
return {0xF601};
|
||||
}
|
||||
std::tuple<Result> CopyFromCurrentDomain() {
|
||||
/* TODO */
|
||||
return {0xF601};
|
||||
}
|
||||
std::tuple<Result> CloneCurrentObject() {
|
||||
/* TODO */
|
||||
return {0xF601};
|
||||
}
|
||||
std::tuple<Result, u32> QueryPointerBufferSize() {
|
||||
return {0x0, (u32)this->pointer_buffer.size()};
|
||||
}
|
||||
std::tuple<Result> CloneCurrentObjectEx() {
|
||||
/* TODO */
|
||||
return {0xF601};
|
||||
}
|
||||
};
|
|
@ -18,47 +18,61 @@
|
|||
|
||||
#include <switch.h>
|
||||
|
||||
#include "waitablemanagerbase.hpp"
|
||||
|
||||
class WaitableManager;
|
||||
#include "waitable_manager_base.hpp"
|
||||
#include "hossynch.hpp"
|
||||
|
||||
class IWaitable {
|
||||
private:
|
||||
u64 wait_priority = 0;
|
||||
bool is_deferred = false;
|
||||
WaitableManagerBase *manager;
|
||||
WaitableManagerBase *manager = nullptr;
|
||||
protected:
|
||||
HosMutex sig_lock;
|
||||
bool is_signaled = false;
|
||||
public:
|
||||
virtual ~IWaitable() { }
|
||||
virtual ~IWaitable() = default;
|
||||
|
||||
virtual void handle_deferred() = 0;
|
||||
virtual Handle get_handle() = 0;
|
||||
virtual Result handle_signaled(u64 timeout) = 0;
|
||||
|
||||
WaitableManager *get_manager() {
|
||||
return (WaitableManager *)this->manager;
|
||||
virtual void HandleDeferred() {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
void set_manager(WaitableManagerBase *m) {
|
||||
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 update_priority() {
|
||||
void UpdatePriority() {
|
||||
if (manager) {
|
||||
this->wait_priority = this->manager->get_priority();
|
||||
this->wait_priority = this->manager->GetNextPriority();
|
||||
}
|
||||
}
|
||||
|
||||
bool get_deferred() {
|
||||
bool IsDeferred() {
|
||||
return this->is_deferred;
|
||||
}
|
||||
|
||||
void set_deferred(bool d) {
|
||||
void SetDeferred(bool d) {
|
||||
this->is_deferred = d;
|
||||
}
|
||||
|
||||
static bool compare(IWaitable *a, IWaitable *b) {
|
||||
return (a->wait_priority < b->wait_priority) && !a->is_deferred;
|
||||
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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#include "waitablemanager.hpp"
|
26
stratosphere/libstratosphere/include/stratosphere/mitm.hpp
Normal file
26
stratosphere/libstratosphere/include/stratosphere/mitm.hpp
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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"
|
|
@ -16,16 +16,20 @@
|
|||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include "iserver.hpp"
|
||||
#include <atomic>
|
||||
|
||||
template <typename T>
|
||||
class ExistingPortServer : public IServer<T> {
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
class IMitmServiceObject : public IServiceObject {
|
||||
protected:
|
||||
std::shared_ptr<Service> forward_service;
|
||||
u64 process_id = 0;
|
||||
u64 title_id = 0;
|
||||
public:
|
||||
ExistingPortServer(Handle port_h, unsigned int max_s, bool s_d = false) : IServer<T>(NULL, max_s, s_d) {
|
||||
this->port_handle = port_h;
|
||||
}
|
||||
IMitmServiceObject(std::shared_ptr<Service> s) : forward_service(s) {}
|
||||
|
||||
ISession<T> *get_new_session(Handle session_h) override {
|
||||
return new ServiceSession<T>(this, session_h, 0);
|
||||
}
|
||||
};
|
||||
static bool ShouldMitm(u64 pid, u64 tid);
|
||||
|
||||
protected:
|
||||
virtual ~IMitmServiceObject() = default;
|
||||
};
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* 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::ShouldMitm>(),
|
||||
MakeServiceCommandMeta<MQS_Cmd_AssociatePidTid, &MitmQueryService::AssociatePidToTid>(),
|
||||
};
|
||||
};
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
});
|
||||
|
||||
if (R_FAILED(smMitMGetService(forward_service.get(), mitm_name))) {
|
||||
/* TODO: Panic. */
|
||||
}
|
||||
|
||||
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->AddWaitable(srv);
|
||||
manager->AddSession(query_h, std::move(ServiceObjectHolder(std::move(std::make_shared<MitmQueryService<T>>()))));
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,313 @@
|
|||
/*
|
||||
* 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"
|
||||
|
||||
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;
|
||||
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 = ServiceObjectHolder(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 = ServiceObjectHolder(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) {
|
||||
return ForwardRequest(ctx);
|
||||
}
|
||||
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) {
|
||||
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>(),
|
||||
};
|
||||
};
|
||||
};
|
|
@ -0,0 +1,24 @@
|
|||
/**
|
||||
* @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,58 +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 <vector>
|
||||
|
||||
#include "waitablemanager.hpp"
|
||||
#include "systemevent.hpp"
|
||||
|
||||
class MultiThreadedWaitableManager : public WaitableManager {
|
||||
protected:
|
||||
u32 num_threads;
|
||||
Thread *threads;
|
||||
HosMutex get_waitable_lock;
|
||||
SystemEvent *new_waitable_event;
|
||||
public:
|
||||
MultiThreadedWaitableManager(u32 n, u64 t, u32 ss = 0x8000) : WaitableManager(t), num_threads(n-1) {
|
||||
u32 prio;
|
||||
u32 cpuid = svcGetCurrentProcessorNumber();
|
||||
Result rc;
|
||||
threads = new Thread[num_threads];
|
||||
if (R_FAILED((rc = svcGetThreadPriority(&prio, CUR_THREAD_HANDLE)))) {
|
||||
fatalSimple(rc);
|
||||
}
|
||||
for (unsigned int i = 0; i < num_threads; i++) {
|
||||
threads[i] = {0};
|
||||
threadCreate(&threads[i], &MultiThreadedWaitableManager::thread_func, this, ss, prio, cpuid);
|
||||
}
|
||||
new_waitable_event = new SystemEvent(this, &MultiThreadedWaitableManager::add_waitable_callback);
|
||||
this->waitables.push_back(new_waitable_event);
|
||||
}
|
||||
~MultiThreadedWaitableManager() override {
|
||||
/* TODO: Exit the threads? */
|
||||
}
|
||||
|
||||
IWaitable *get_waitable();
|
||||
|
||||
void add_waitable(IWaitable *waitable) override;
|
||||
void process() override;
|
||||
void process_until_timeout() override;
|
||||
|
||||
static Result add_waitable_callback(void *this_ptr, Handle *handles, size_t num_handles, u64 timeout);
|
||||
static void thread_func(void *this_ptr);
|
||||
};
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* 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() + [&]()
|
|
@ -16,48 +16,36 @@
|
|||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <algorithm>
|
||||
#include <type_traits>
|
||||
|
||||
#include "iserviceobject.hpp"
|
||||
#include "iwaitable.hpp"
|
||||
#include "isession.hpp"
|
||||
#include "ipc.hpp"
|
||||
|
||||
template <typename T>
|
||||
class ISession;
|
||||
|
||||
template <typename T>
|
||||
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;
|
||||
bool supports_domains;
|
||||
|
||||
public:
|
||||
IServer(const char *service_name, unsigned int max_s, bool s_d = false) : max_sessions(max_s), supports_domains(s_d) {
|
||||
|
||||
}
|
||||
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());
|
||||
}
|
||||
|
||||
virtual ISession<T> *get_new_session(Handle session_h) = 0;
|
||||
|
||||
/* IWaitable */
|
||||
virtual Handle get_handle() {
|
||||
virtual Handle GetHandle() override {
|
||||
return this->port_handle;
|
||||
}
|
||||
|
||||
|
||||
virtual void handle_deferred() {
|
||||
/* TODO: Panic, because we can never defer a server. */
|
||||
}
|
||||
|
||||
virtual Result handle_signaled(u64 timeout) {
|
||||
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);
|
||||
|
@ -65,7 +53,35 @@ class IServer : public IWaitable {
|
|||
return rc;
|
||||
}
|
||||
|
||||
this->get_manager()->add_waitable(this->get_new_session(session_h));
|
||||
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 */
|
||||
}
|
||||
}
|
||||
};
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* 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"
|
|
@ -16,21 +16,16 @@
|
|||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <type_traits>
|
||||
|
||||
#include "ipc_templating.hpp"
|
||||
#include "iserviceobject.hpp"
|
||||
#include "iwaitable.hpp"
|
||||
#include "iserver.hpp"
|
||||
#include "isession.hpp"
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
class ServiceSession final : public ISession<T> {
|
||||
static_assert(std::is_base_of<IServiceObject, T>::value, "Service Objects must derive from IServiceObject");
|
||||
|
||||
public:
|
||||
ServiceSession<T>(IServer<T> *s, Handle s_h, Handle c_h, size_t pbs = 0x400) : ISession<T>(s, s_h, c_h, pbs) {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
};
|
||||
Result smManagerAmsInitialize(void);
|
||||
void smManagerAmsExit(void);
|
||||
|
||||
Result smManagerAmsEndInitialDefers(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
|
||||
#include "iwaitable.hpp"
|
||||
#include "ievent.hpp"
|
||||
|
||||
#define SYSTEMEVENT_INDEX_WAITHANDLE 0
|
||||
#define SYSTEMEVENT_INDEX_SGNLHANDLE 1
|
||||
|
||||
class SystemEvent final : public IEvent {
|
||||
public:
|
||||
SystemEvent(void *a, EventCallback callback) : IEvent(0, a, callback) {
|
||||
Handle wait_h;
|
||||
Handle sig_h;
|
||||
if (R_FAILED(svcCreateEvent(&sig_h, &wait_h))) {
|
||||
/* TODO: Panic. */
|
||||
}
|
||||
|
||||
this->handles.push_back(wait_h);
|
||||
this->handles.push_back(sig_h);
|
||||
}
|
||||
|
||||
Result signal_event() override {
|
||||
return svcSignalEvent(this->handles[SYSTEMEVENT_INDEX_SGNLHANDLE]);
|
||||
}
|
||||
};
|
|
@ -0,0 +1,330 @@
|
|||
/*
|
||||
* 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() {
|
||||
/* TODO: Wait for libnx to add a way for me to call getThreadVars() */
|
||||
return *((Handle *)((uintptr_t)armGetTls() + 0x1E4));
|
||||
}
|
||||
|
||||
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;
|
||||
u32 num_threads;
|
||||
Thread *threads;
|
||||
HosMutex process_lock;
|
||||
HosMutex signal_lock;
|
||||
HosMutex add_lock;
|
||||
IEvent *new_waitable_event = nullptr;
|
||||
|
||||
IWaitable *next_signaled = nullptr;
|
||||
Handle cur_thread_handle = INVALID_HANDLE;
|
||||
public:
|
||||
WaitableManager(u32 n, u32 ss = 0x8000) : num_threads(n-1) {
|
||||
u32 prio;
|
||||
u32 cpuid = svcGetCurrentProcessorNumber();
|
||||
Result rc;
|
||||
threads = new Thread[num_threads];
|
||||
if (num_threads) {
|
||||
if (R_FAILED((rc = svcGetThreadPriority(&prio, CUR_THREAD_HANDLE)))) {
|
||||
fatalSimple(rc);
|
||||
}
|
||||
for (unsigned int i = 0; i < num_threads; i++) {
|
||||
threads[i] = {0};
|
||||
threadCreate(&threads[i], &WaitableManager::ProcessLoop, this, ss, prio, cpuid);
|
||||
}
|
||||
}
|
||||
new_waitable_event = CreateSystemEvent([](u64 timeout) { return 0; }, true);
|
||||
}
|
||||
|
||||
~WaitableManager() override {
|
||||
/* This should call the destructor for every waitable. */
|
||||
std::for_each(waitables.begin(), waitables.end(), std::default_delete<IWaitable>{});
|
||||
|
||||
/* TODO: Exit 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->new_waitable_event->Signal();
|
||||
}
|
||||
|
||||
virtual void NotifySignaled(IWaitable *w) override {
|
||||
std::scoped_lock lk{this->signal_lock};
|
||||
if (this->next_signaled == nullptr && w != this->new_waitable_event) {
|
||||
this->next_signaled = w;
|
||||
}
|
||||
svcCancelSynchronization(this->cur_thread_handle);
|
||||
}
|
||||
|
||||
virtual void Process() override {
|
||||
/* Add initial set of waitables. */
|
||||
AddWaitablesInternal();
|
||||
|
||||
Result rc;
|
||||
for (unsigned int i = 0; i < num_threads; i++) {
|
||||
if (R_FAILED((rc = threadStart(&threads[i])))) {
|
||||
fatalSimple(rc);
|
||||
}
|
||||
}
|
||||
|
||||
ProcessLoop(this);
|
||||
}
|
||||
private:
|
||||
static void ProcessLoop(void *t) {
|
||||
WaitableManager *this_ptr = (WaitableManager *)t;
|
||||
while (true) {
|
||||
IWaitable *w = this_ptr->GetWaitable();
|
||||
if (w) {
|
||||
Result rc = w->HandleSignaled(0);
|
||||
if (rc == 0xF601) {
|
||||
/* Close! */
|
||||
delete w;
|
||||
} else {
|
||||
this_ptr->AddWaitable(w);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
IWaitable *GetWaitable() {
|
||||
std::scoped_lock lk{this->process_lock};
|
||||
this->next_signaled = nullptr;
|
||||
IWaitable *result = nullptr;
|
||||
|
||||
if (this->new_waitable_event->IsSignaled()) {
|
||||
AddWaitablesInternal();
|
||||
}
|
||||
|
||||
this->cur_thread_handle = GetCurrentThreadHandle();
|
||||
ON_SCOPE_EXIT {
|
||||
this->cur_thread_handle = INVALID_HANDLE;
|
||||
};
|
||||
|
||||
/* 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;
|
||||
|
||||
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() + 1);
|
||||
handles[0] = this->new_waitable_event->GetHandle();
|
||||
unsigned int num_handles = 1;
|
||||
for (unsigned int i = 0; i < this->waitables.size(); i++) {
|
||||
Handle h = this->waitables[i]->GetHandle();
|
||||
if (h != INVALID_HANDLE) {
|
||||
handles[num_handles++] = h;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Do deferred callback for each waitable. This has to happen before we wait on anything else. */
|
||||
for (auto & waitable : this->waitables) {
|
||||
if (waitable->IsDeferred()) {
|
||||
waitable->HandleDeferred();
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait forever. */
|
||||
rc = svcWaitSynchronization(&handle_index, handles.data(), num_handles, U64_MAX);
|
||||
|
||||
IWaitable *w = this->waitables[handle_index - 1];
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
if (handle_index == 0) {
|
||||
AddWaitablesInternal();
|
||||
continue;
|
||||
}
|
||||
std::for_each(waitables.begin(), waitables.begin() + handle_index - 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. */
|
||||
std::scoped_lock lk{this->signal_lock};
|
||||
if (this->next_signaled != nullptr) result = this->next_signaled;
|
||||
} else if (rc != 0xF601 && rc != 0xE401) {
|
||||
std::abort();
|
||||
} else {
|
||||
if (handle_index == 0) {
|
||||
std::abort();
|
||||
}
|
||||
this->waitables.erase(this->waitables.begin() + handle_index - 1);
|
||||
std::for_each(waitables.begin(), waitables.begin() + handle_index - 1, std::mem_fn(&IWaitable::UpdatePriority));
|
||||
delete w;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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};
|
||||
this->waitables.insert(this->waitables.end(), this->to_add_waitables.begin(), this->to_add_waitables.end());
|
||||
this->to_add_waitables.clear();
|
||||
this->new_waitable_event->Clear();
|
||||
}
|
||||
}
|
||||
/* 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;
|
||||
}
|
||||
};
|
||||
|
|
@ -15,9 +15,9 @@
|
|||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
|
||||
class IWaitable;
|
||||
|
||||
class WaitableManagerBase {
|
||||
std::atomic<u64> cur_priority = 0;
|
||||
|
@ -25,7 +25,13 @@ class WaitableManagerBase {
|
|||
WaitableManagerBase() = default;
|
||||
virtual ~WaitableManagerBase() = default;
|
||||
|
||||
u64 get_priority() {
|
||||
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 Process() = 0;
|
||||
};
|
||||
|
|
@ -1,48 +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 <vector>
|
||||
|
||||
#include "waitablemanagerbase.hpp"
|
||||
#include "iwaitable.hpp"
|
||||
#include "hossynch.hpp"
|
||||
|
||||
class IWaitable;
|
||||
|
||||
class WaitableManager : public WaitableManagerBase {
|
||||
protected:
|
||||
std::vector<IWaitable *> to_add_waitables;
|
||||
std::vector<IWaitable *> waitables;
|
||||
u64 timeout = 0;
|
||||
HosMutex lock;
|
||||
std::atomic_bool has_new_items = false;
|
||||
private:
|
||||
void process_internal(bool break_on_timeout);
|
||||
public:
|
||||
WaitableManager(u64 t) : timeout(t) { }
|
||||
~WaitableManager() override {
|
||||
/* This should call the destructor for every waitable. */
|
||||
std::for_each(waitables.begin(), waitables.end(), std::default_delete<IWaitable>{});
|
||||
}
|
||||
|
||||
virtual void add_waitable(IWaitable *waitable);
|
||||
virtual void process();
|
||||
virtual void process_until_timeout();
|
||||
};
|
42
stratosphere/libstratosphere/source/mitm_query_service.cpp
Normal file
42
stratosphere/libstratosphere/source/mitm_query_service.cpp
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* 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,119 +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 <algorithm>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
|
||||
#include <stratosphere/multithreadedwaitablemanager.hpp>
|
||||
|
||||
void MultiThreadedWaitableManager::process() {
|
||||
Result rc;
|
||||
for (unsigned int i = 0; i < num_threads; i++) {
|
||||
if (R_FAILED((rc = threadStart(&threads[i])))) {
|
||||
fatalSimple(rc);
|
||||
}
|
||||
}
|
||||
MultiThreadedWaitableManager::thread_func(this);
|
||||
}
|
||||
|
||||
void MultiThreadedWaitableManager::process_until_timeout() {
|
||||
/* TODO: Panic. */
|
||||
}
|
||||
|
||||
void MultiThreadedWaitableManager::add_waitable(IWaitable *waitable) {
|
||||
std::scoped_lock lk{this->lock};
|
||||
this->to_add_waitables.push_back(waitable);
|
||||
waitable->set_manager(this);
|
||||
this->new_waitable_event->signal_event();
|
||||
}
|
||||
|
||||
|
||||
IWaitable *MultiThreadedWaitableManager::get_waitable() {
|
||||
std::vector<Handle> handles;
|
||||
|
||||
int handle_index = 0;
|
||||
Result rc;
|
||||
std::scoped_lock lk{this->get_waitable_lock};
|
||||
while (1) {
|
||||
/* Sort waitables by priority. */
|
||||
std::sort(this->waitables.begin(), this->waitables.end(), IWaitable::compare);
|
||||
|
||||
/* Copy out handles. */
|
||||
handles.resize(this->waitables.size());
|
||||
std::transform(this->waitables.begin(), this->waitables.end(), handles.begin(), [](IWaitable *w) { return w->get_handle(); });
|
||||
|
||||
rc = svcWaitSynchronization(&handle_index, handles.data(), this->waitables.size(), this->timeout);
|
||||
IWaitable *w = this->waitables[handle_index];
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
std::for_each(waitables.begin(), waitables.begin() + handle_index, std::mem_fn(&IWaitable::update_priority));
|
||||
this->waitables.erase(this->waitables.begin() + handle_index);
|
||||
} else if (rc == 0xEA01) {
|
||||
/* Timeout. */
|
||||
std::for_each(waitables.begin(), waitables.end(), std::mem_fn(&IWaitable::update_priority));
|
||||
} else if (rc != 0xF601 && rc != 0xE401) {
|
||||
/* TODO: Panic. When can this happen? */
|
||||
} else {
|
||||
std::for_each(waitables.begin(), waitables.begin() + handle_index, std::mem_fn(&IWaitable::update_priority));
|
||||
this->waitables.erase(this->waitables.begin() + handle_index);
|
||||
delete w;
|
||||
}
|
||||
|
||||
/* Do deferred callback for each waitable. */
|
||||
for (auto & waitable : this->waitables) {
|
||||
if (waitable->get_deferred()) {
|
||||
waitable->handle_deferred();
|
||||
}
|
||||
}
|
||||
|
||||
/* Return waitable. */
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
if (w == this->new_waitable_event) {
|
||||
w->handle_signaled(0);
|
||||
this->waitables.push_back(w);
|
||||
} else {
|
||||
return w;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Result MultiThreadedWaitableManager::add_waitable_callback(void *arg, Handle *handles, size_t num_handles, u64 timeout) {
|
||||
MultiThreadedWaitableManager *this_ptr = (MultiThreadedWaitableManager *)arg;
|
||||
svcClearEvent(handles[0]);
|
||||
std::scoped_lock lk{this_ptr->lock};
|
||||
this_ptr->waitables.insert(this_ptr->waitables.end(), this_ptr->to_add_waitables.begin(), this_ptr->to_add_waitables.end());
|
||||
this_ptr->to_add_waitables.clear();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void MultiThreadedWaitableManager::thread_func(void *t) {
|
||||
MultiThreadedWaitableManager *this_ptr = (MultiThreadedWaitableManager *)t;
|
||||
while (1) {
|
||||
IWaitable *w = this_ptr->get_waitable();
|
||||
if (w) {
|
||||
Result rc = w->handle_signaled(0);
|
||||
if (rc == 0xF601) {
|
||||
/* Close! */
|
||||
delete w;
|
||||
} else {
|
||||
this_ptr->add_waitable(w);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
190
stratosphere/libstratosphere/source/sm_mitm.c
Normal file
190
stratosphere/libstratosphere/source/sm_mitm.c
Normal file
|
@ -0,0 +1,190 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
69
stratosphere/libstratosphere/source/smm_ams.c
Normal file
69
stratosphere/libstratosphere/source/smm_ams.c
Normal file
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
}
|
|
@ -1,105 +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 <algorithm>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
|
||||
#include <stratosphere/waitablemanager.hpp>
|
||||
|
||||
void WaitableManager::add_waitable(IWaitable *waitable) {
|
||||
std::scoped_lock lk{this->lock};
|
||||
this->to_add_waitables.push_back(waitable);
|
||||
waitable->set_manager(this);
|
||||
this->has_new_items = true;
|
||||
}
|
||||
|
||||
void WaitableManager::process_internal(bool break_on_timeout) {
|
||||
std::vector<Handle> handles;
|
||||
|
||||
int handle_index = 0;
|
||||
Result rc;
|
||||
|
||||
while (1) {
|
||||
/* Add new items, if relevant. */
|
||||
if (this->has_new_items) {
|
||||
std::scoped_lock lk{this->lock};
|
||||
this->waitables.insert(this->waitables.end(), this->to_add_waitables.begin(), this->to_add_waitables.end());
|
||||
this->to_add_waitables.clear();
|
||||
this->has_new_items = false;
|
||||
}
|
||||
|
||||
/* Sort waitables by priority. */
|
||||
std::sort(this->waitables.begin(), this->waitables.end(), IWaitable::compare);
|
||||
|
||||
/* Copy out handles. */
|
||||
handles.resize(this->waitables.size());
|
||||
std::transform(this->waitables.begin(), this->waitables.end(), handles.begin(), [](IWaitable *w) { return w->get_handle(); });
|
||||
|
||||
|
||||
rc = svcWaitSynchronization(&handle_index, handles.data(), this->waitables.size(), this->timeout);
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
/* Handle a signaled waitable. */
|
||||
/* TODO: What timeout should be passed here? */
|
||||
|
||||
rc = this->waitables[handle_index]->handle_signaled(0);
|
||||
|
||||
std::for_each(waitables.begin(), waitables.begin() + handle_index, std::mem_fn(&IWaitable::update_priority));
|
||||
} else if (rc == 0xEA01) {
|
||||
/* Timeout. */
|
||||
std::for_each(waitables.begin(), waitables.end(), std::mem_fn(&IWaitable::update_priority));
|
||||
if (break_on_timeout) {
|
||||
return;
|
||||
}
|
||||
} else if (rc != 0xF601) {
|
||||
/* TODO: Panic. When can this happen? */
|
||||
}
|
||||
|
||||
if (rc == 0xF601) {
|
||||
/* handles[handle_index] was closed! */
|
||||
|
||||
/* Close the handle. */
|
||||
svcCloseHandle(handles[handle_index]);
|
||||
|
||||
IWaitable *to_delete = this->waitables[handle_index];
|
||||
|
||||
/* If relevant, remove from waitables. */
|
||||
this->waitables.erase(this->waitables.begin() + handle_index);
|
||||
|
||||
/* Delete it. */
|
||||
delete to_delete;
|
||||
|
||||
std::for_each(waitables.begin(), waitables.begin() + handle_index, std::mem_fn(&IWaitable::update_priority));
|
||||
}
|
||||
|
||||
/* Do deferred callback for each waitable. */
|
||||
for (auto & waitable : this->waitables) {
|
||||
if (waitable->get_deferred()) {
|
||||
waitable->handle_deferred();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WaitableManager::process() {
|
||||
WaitableManager::process_internal(false);
|
||||
}
|
||||
|
||||
void WaitableManager::process_until_timeout() {
|
||||
WaitableManager::process_internal(true);
|
||||
}
|
Loading…
Reference in a new issue