diff --git a/stratosphere/libstratosphere/include/stratosphere.hpp b/stratosphere/libstratosphere/include/stratosphere.hpp
index 041d72741..503f6f7f1 100644
--- a/stratosphere/libstratosphere/include/stratosphere.hpp
+++ b/stratosphere/libstratosphere/include/stratosphere.hpp
@@ -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"
\ No newline at end of file
+#include "stratosphere/ipc.hpp"
+
+#include "stratosphere/mitm.hpp"
+
+#include "stratosphere/services.hpp"
\ No newline at end of file
diff --git a/stratosphere/libstratosphere/include/stratosphere/domainowner.hpp b/stratosphere/libstratosphere/include/stratosphere/domainowner.hpp
deleted file mode 100644
index 74500eb63..000000000
--- a/stratosphere/libstratosphere/include/stratosphere/domainowner.hpp
+++ /dev/null
@@ -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 .
- */
-
-#pragma once
-#include
-#include
-#include
-#include
-
-#include "iserviceobject.hpp"
-
-#define DOMAIN_ID_MAX 0x1000
-
-class IServiceObject;
-
-class DomainOwner {
- private:
- std::array, DOMAIN_ID_MAX> domain_objects;
- public:
- /* Shared ptrs should auto delete here. */
- virtual ~DomainOwner() = default;
-
- std::shared_ptr get_domain_object(unsigned int i) {
- if (i < DOMAIN_ID_MAX) {
- return domain_objects[i];
- }
- return nullptr;
- }
-
- Result reserve_object(std::shared_ptr 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 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 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 object) {
- auto object_it = std::find(domain_objects.begin(), domain_objects.end(), object);
- if (object_it != domain_objects.end()) {
- object_it->reset();
- }
- }
-};
diff --git a/stratosphere/libstratosphere/include/stratosphere/event.hpp b/stratosphere/libstratosphere/include/stratosphere/event.hpp
new file mode 100644
index 000000000..ba726d557
--- /dev/null
+++ b/stratosphere/libstratosphere/include/stratosphere/event.hpp
@@ -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 .
+ */
+
+#pragma once
+#include
+#include
+#include
+
+#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 lock(this->sig_lock);
+ this->is_signaled = false;
+ if (this->r_h != INVALID_HANDLE) {
+ svcResetSignal(this->r_h);
+ }
+ }
+
+ void Signal() {
+ std::scoped_lock 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 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
+static IEvent *CreateHosEvent(F f, bool autoclear = false) {
+ return new HosEvent(INVALID_HANDLE, INVALID_HANDLE, std::move(f), autoclear);
+}
+
+template
+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(r_h, w_h, std::move(f), autoclear);
+}
+
+template
+static IEvent *CreateWriteOnlySystemEvent() {
+ return CreateSystemEvent([](u64 timeout) { std::abort(); return 0; }, a);
+}
diff --git a/stratosphere/libstratosphere/include/stratosphere/firmware_version.hpp b/stratosphere/libstratosphere/include/stratosphere/firmware_version.hpp
new file mode 100644
index 000000000..312c68c67
--- /dev/null
+++ b/stratosphere/libstratosphere/include/stratosphere/firmware_version.hpp
@@ -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 .
+ */
+
+#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;
+}
diff --git a/stratosphere/libstratosphere/include/stratosphere/hossynch.hpp b/stratosphere/libstratosphere/include/stratosphere/hossynch.hpp
index b923a4ec1..73ca16d76 100644
--- a/stratosphere/libstratosphere/include/stratosphere/hossynch.hpp
+++ b/stratosphere/libstratosphere/include/stratosphere/hossynch.hpp
@@ -16,6 +16,7 @@
#pragma once
#include
+#include
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 {
diff --git a/stratosphere/libstratosphere/include/stratosphere/ievent.hpp b/stratosphere/libstratosphere/include/stratosphere/ievent.hpp
deleted file mode 100644
index d1c93ef63..000000000
--- a/stratosphere/libstratosphere/include/stratosphere/ievent.hpp
+++ /dev/null
@@ -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 .
- */
-
-#pragma once
-#include
-#include
-#include
-
-#include "iwaitable.hpp"
-
-typedef Result (*EventCallback)(void *arg, Handle *handles, size_t num_handles, u64 timeout);
-
-class IEvent : public IWaitable {
- protected:
- std::vector 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;
- }
-};
diff --git a/stratosphere/libstratosphere/include/stratosphere/ipc.hpp b/stratosphere/libstratosphere/include/stratosphere/ipc.hpp
new file mode 100644
index 000000000..e035fda52
--- /dev/null
+++ b/stratosphere/libstratosphere/include/stratosphere/ipc.hpp
@@ -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 .
+ */
+
+#pragma once
+
+#include "ipc/ipc_service_object.hpp"
+#include "ipc/ipc_serialization.hpp"
+
+#include "ipc/ipc_service_session.hpp"
\ No newline at end of file
diff --git a/stratosphere/libstratosphere/include/stratosphere/ipc/ipc_buffers.hpp b/stratosphere/libstratosphere/include/stratosphere/ipc/ipc_buffers.hpp
new file mode 100644
index 000000000..6bf2f71c0
--- /dev/null
+++ b/stratosphere/libstratosphere/include/stratosphere/ipc/ipc_buffers.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 .
+ */
+
+#pragma once
+#include
+#include
+
+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
+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
+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
+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
+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
+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)) { }
+};
diff --git a/stratosphere/libstratosphere/include/stratosphere/ipc/ipc_domain_object.hpp b/stratosphere/libstratosphere/include/stratosphere/ipc/ipc_domain_object.hpp
new file mode 100644
index 000000000..1394fc095
--- /dev/null
+++ b/stratosphere/libstratosphere/include/stratosphere/ipc/ipc_domain_object.hpp
@@ -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 .
+ */
+
+#pragma once
+#include
+#include
+#include
+#include
+
+#include "ipc_service_object.hpp"
+
+class IDomainObject;
+
+class DomainManager {
+ public:
+ virtual std::shared_ptr 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();
+}
+
+static constexpr bool IsDomainObject(ServiceObjectHolder *holder) {
+ return holder->GetServiceId() == ServiceObjectId();
+}
+
+/* Out for service impl. */
+template
+class Out> : public OutSessionTag {
+ static_assert(std::is_base_of_v, "OutSessions must be shared_ptr!");
+
+ template
+ friend class Out;
+
+ private:
+ std::shared_ptr *srv;
+ IDomainObject *domain = nullptr;
+ u32 *object_id = nullptr;
+ public:
+ Out>(std::shared_ptr *s, IDomainObject *dm, u32 *o) : srv(reinterpret_cast *>(s)), domain(dm), object_id(o) { }
+
+ ServiceObjectHolder GetHolder() {
+ std::shared_ptr 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 &&s) {
+ *this->srv = std::move(s);
+ }
+};
diff --git a/stratosphere/libstratosphere/include/stratosphere/ipc/ipc_out.hpp b/stratosphere/libstratosphere/include/stratosphere/ipc/ipc_out.hpp
new file mode 100644
index 000000000..fbd186d28
--- /dev/null
+++ b/stratosphere/libstratosphere/include/stratosphere/ipc/ipc_out.hpp
@@ -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 .
+ */
+
+#pragma once
+#include
+#include
+
+/* Declare false allowed struct. */
+template
+struct AllowedOut : std::false_type {};
+
+struct OutDataTag{};
+struct OutHandleTag{};
+struct OutSessionTag{};
+
+/* Define out struct, so that we can get errors on enable_if */
+template
+class Out {
+ static_assert(std::is_pod::value && !std::is_pod::value, "Invalid IPC Out Type!");
+};
+
+template
+class Out::value || AllowedOut::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
+class Out {
+ static_assert(std::is_pod::value && !std::is_pod::value, "Invalid IPC Out Type (Raw Pointer)!");
+};
+
+template
+struct OutHelper;
+
+template
+struct OutHelper> {
+ using type = T;
+};
\ No newline at end of file
diff --git a/stratosphere/libstratosphere/include/stratosphere/managedportserver.hpp b/stratosphere/libstratosphere/include/stratosphere/ipc/ipc_response_context.hpp
similarity index 52%
rename from stratosphere/libstratosphere/include/stratosphere/managedportserver.hpp
rename to stratosphere/libstratosphere/include/stratosphere/ipc/ipc_response_context.hpp
index f61545f74..4e2ed8345 100644
--- a/stratosphere/libstratosphere/include/stratosphere/managedportserver.hpp
+++ b/stratosphere/libstratosphere/include/stratosphere/ipc/ipc_response_context.hpp
@@ -16,18 +16,29 @@
#pragma once
#include
-#include "iserver.hpp"
-template
-class ManagedPortServer : public IServer {
- public:
- ManagedPortServer(const char *service_name, unsigned int max_s, bool s_d = false) : IServer(service_name, max_s, s_d) {
- if (R_FAILED(svcManageNamedPort(&this->port_handle, service_name, this->max_sessions))) {
- /* TODO: panic */
- }
- }
-
- ISession *get_new_session(Handle session_h) override {
- return new ServiceSession(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 *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;
};
\ No newline at end of file
diff --git a/stratosphere/libstratosphere/include/stratosphere/ipc/ipc_serialization.hpp b/stratosphere/libstratosphere/include/stratosphere/ipc/ipc_serialization.hpp
new file mode 100644
index 000000000..93c894af3
--- /dev/null
+++ b/stratosphere/libstratosphere/include/stratosphere/ipc/ipc_serialization.hpp
@@ -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 .
+ */
+
+#pragma once
+#include
+#include
+#include
+#include
+#include "../../boost/callable_traits.hpp"
+#include
+#include
+
+#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
+struct PopFront;
+
+template
+struct PopFront> {
+ using type = std::tuple;
+};
+
+template struct WhichType;
+
+template
+struct TypeList{};
+
+template
+constexpr auto Concatenate(TypeList, TypeList) {
+ return TypeList{};
+}
+
+template typename Condition, typename R>
+constexpr auto FilterTypes(R result, TypeList<>) {
+ return result;
+}
+
+template typename Condition, typename R, typename T, typename... Ts>
+constexpr auto FilterTypes(R result, TypeList) {
+ if constexpr (Condition{})
+ return FilterTypes(Concatenate(result, TypeList{}), TypeList{});
+ else
+ return FilterTypes(result, TypeList{});
+}
+
+template struct TypeListToTuple;
+
+template
+struct TypeListToTuple> {
+ using type = std::tuple;
+};
+
+template typename Condition, typename... Types>
+using FilteredTypes = typename TypeListToTuple(TypeList<>{}, TypeList{}))>>::type;
+
+enum class ArgType {
+ InData,
+ OutData,
+ InHandle,
+ OutHandle,
+ InSession,
+ OutSession,
+ PidDesc,
+ InBuffer,
+ OutBuffer,
+ InPointer,
+ OutPointerClientSize,
+ OutPointerServerSize,
+};
+
+template
+constexpr ArgType GetArgType() {
+ if constexpr (std::is_base_of_v) {
+ return ArgType::OutData;
+ } else if constexpr (std::is_base_of_v) {
+ return ArgType::OutSession;
+ } else if constexpr (std::is_base_of_v) {
+ return ArgType::OutHandle;
+ } else if constexpr (std::is_base_of_v) {
+ return ArgType::InBuffer;
+ } else if constexpr (std::is_base_of_v) {
+ return ArgType::OutBuffer;
+ } else if constexpr (std::is_base_of_v) {
+ return ArgType::InPointer;
+ } else if constexpr (std::is_base_of_v) {
+ return ArgType::OutPointerClientSize;
+ } else if constexpr (std::is_base_of_v) {
+ return ArgType::OutPointerServerSize;
+ } else if constexpr (std::is_base_of_v) {
+ return ArgType::PidDesc;
+ } else if constexpr (std::is_base_of_v) {
+ return ArgType::InHandle;
+ } else if constexpr (std::is_trivial_v && !std::is_pointer_v) {
+ return ArgType::InData;
+ } else {
+ static_assert(std::is_pod_v && !std::is_pod_v, "Unhandled InSession!");
+ return ArgType::InSession;
+ }
+}
+
+template
+struct ArgTypeFilter {
+ template
+ using type = std::conditional_t() == ArgT, std::true_type, std::false_type>;
+};
+
+template
+struct IsArgTypeBuffer {
+ static constexpr bool value = ArgT == ArgType::InBuffer || ArgT == ArgType::OutBuffer || ArgT == ArgType::InPointer || ArgT == ArgType::OutPointerClientSize || ArgT == ArgType::OutPointerServerSize;
+};
+
+struct ArgTypeBufferFilter {
+ template
+ using type = std::conditional_t()>::value, std::true_type, std::false_type>;
+};
+
+template
+struct IsArgTypeInData {
+ static constexpr bool value = ArgT == ArgType::InData || ArgT == ArgType::PidDesc;
+};
+
+struct ArgTypeInDataFilter {
+ template
+ using type = std::conditional_t()>::value, std::true_type, std::false_type>;
+};
+
+template
+struct RawDataHelper {
+ static_assert(GetArgType() == ArgType::InData || GetArgType() == ArgType::PidDesc);
+ static constexpr size_t align = (GetArgType() == ArgType::InData) ? __alignof__(T) : __alignof__(u64);
+ static constexpr size_t size = (GetArgType() == ArgType::InData) ? sizeof(T) : sizeof(u64);
+};
+
+template
+struct RawDataHelper> {
+ static_assert(GetArgType() == ArgType::InData);
+ static constexpr size_t align = __alignof(T);
+ static constexpr size_t size = sizeof(T);
+};
+
+template
+struct RawSizeElementAdder {
+ static constexpr size_t GetUpdateElementSize(size_t &size) {
+ constexpr size_t t_align = RawDataHelper::align;
+ constexpr size_t t_size = RawDataHelper::size;
+ if (size % t_align == 0) {
+ size += t_align - (size % t_align);
+ }
+ size += t_size;
+ return size;
+ }
+};
+
+template
+struct GetRawDataSize;
+
+template
+struct GetRawDataSize> {
+ static constexpr size_t Size() {
+ if constexpr (sizeof...(Ts) == 0) {
+ return 0;
+ } else {
+ size_t s = 0;
+ size_t ends[] = { RawSizeElementAdder::GetUpdateElementSize(s)... };
+ return (ends[sizeof...(Ts)-1] + 3) & ~3;
+ }
+ }
+};
+
+template
+struct CommandMetaInfo;
+
+template
+struct CommandMetaInfo, _ReturnType> {
+ using Args = std::tuple<_Args...>;
+ using ReturnType = _ReturnType;
+
+ static constexpr bool ReturnsResult = std::is_same_v;
+ static constexpr bool ReturnsVoid = std::is_same_v;
+
+ using InDatas = FilteredTypes;
+ using OutDatas = FilteredTypes::type, _Args...>;
+ using InHandles = FilteredTypes::type, _Args...>;
+ using OutHandles = FilteredTypes::type, _Args...>;
+ using InSessions = FilteredTypes::type, _Args...>;
+ using OutSessions = FilteredTypes::type, _Args...>;
+ using PidDescs = FilteredTypes::type, _Args...>;
+
+ using InBuffers = FilteredTypes::type, _Args...>;
+ using OutBuffers = FilteredTypes::type, _Args...>;
+ using InPointers = FilteredTypes::type, _Args...>;
+ using ClientSizeOutPointers = FilteredTypes::type, _Args...>;
+ using ServerSizeOutPointers = FilteredTypes::type, _Args...>;
+ using Buffers = FilteredTypes;
+
+ static constexpr size_t NumInDatas = std::tuple_size_v;
+ static constexpr size_t NumOutDatas = std::tuple_size_v;
+ static constexpr size_t NumInHandles = std::tuple_size_v;
+ static constexpr size_t NumOutHandles = std::tuple_size_v;
+ static constexpr size_t NumInSessions = std::tuple_size_v;
+ static constexpr size_t NumOutSessions = std::tuple_size_v;
+ static constexpr size_t NumPidDescs = std::tuple_size_v;
+
+ static constexpr size_t NumInBuffers = std::tuple_size_v;
+ static constexpr size_t NumOutBuffers = std::tuple_size_v;
+ static constexpr size_t NumInPointers = std::tuple_size_v;
+ static constexpr size_t NumClientSizeOutPointers = std::tuple_size_v;
+ static constexpr size_t NumServerSizeOutPointers = std::tuple_size_v;
+ static constexpr size_t NumBuffers = std::tuple_size_v;
+
+ 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::Size();
+ static constexpr size_t InRawArgSizeWithOutPointers = ((InRawArgSize + NumClientSizeOutPointers * sizeof(u16)) + 3) & ~3;
+
+ static constexpr size_t OutRawArgSize = GetRawDataSize::Size();
+};
+
+
+/* ================================================================================= */
+/* Actual wrapping implementation goes here. */
+
+/* Validator. */
+struct Validator {
+
+ template
+ 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();
+ 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) {
+ return !ctx->request.WasHandleCopied[h_index++];
+ } else if constexpr (std::is_same_v) {
+ 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
+ struct ValidateCommandTuple;
+
+ template
+ struct ValidateCommandTuple> {
+ 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(ctx, a_index, b_index, x_index, h_index, cur_c_size_offset, total_c_size) && ...);
+ }
+ };
+
+ template
+ 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::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
+ 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();
+ 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::align;
+ constexpr size_t t_size = RawDataHelper::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::align;
+ constexpr size_t t_size = RawDataHelper::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) {
+ return *((u8 *)ptr) & 1;
+ } else {
+ return *((T *)ptr);
+ }
+ } else if constexpr (argT == ArgType::OutData) {
+ constexpr size_t t_align = RawDataHelper::align;
+ constexpr size_t t_size = RawDataHelper::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::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(), &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
+ struct DecodeTuple;
+
+ template
+ struct DecodeTuple> {
+ static constexpr std::tuple 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 {
+ DecodeCommandArgument(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
+ 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::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
+static constexpr void EncodeArgument(IpcResponseContext *ctx, size_t&out_obj_index, T& arg) {
+ constexpr ArgType argT = GetArgType();
+ if constexpr (argT == ArgType::OutHandle) {
+ if constexpr (std::is_same_v::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();
+ 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
+struct Encoder;
+
+template
+struct Encoder> {
+
+ 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(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
+constexpr Result WrapIpcCommandImpl(IpcResponseContext *ctx) {
+ using InArgs = typename PopFront>::type;
+ using OutArgs = typename boost::callable_traits::return_type_t;
+ using ClassType = typename boost::callable_traits::class_of_t;
+
+ using CommandMetaData = CommandMetaInfo;
+
+ 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(ctx);
+
+ if (R_FAILED(rc)) {
+ return 0xAAEE;
+ }
+
+ ClassType *this_ptr = nullptr;
+ if (IsDomainObject(ctx->obj_holder)) {
+ this_ptr = ctx->obj_holder->GetServiceObject()->GetObject(ctx->request.InThisObjectId)->GetServiceObject();
+ } else {
+ this_ptr = ctx->obj_holder->GetServiceObject();
+ }
+ if (this_ptr == nullptr) {
+ return 0xBBEE;
+ }
+
+ std::shared_ptr 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()->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()->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(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::EncodeSuccess, std::tuple_cat(std::make_tuple(ctx), args));
+ } else {
+ std::apply(Encoder::EncodeFailure, std::tuple_cat(std::make_tuple(ctx), std::make_tuple(rc)));
+ }
+ } else {
+ std::apply(Encoder::EncodeFailure, std::tuple_cat(std::make_tuple(ctx), std::make_tuple(rc)));
+ }
+
+ return rc;
+}
+
+
+template
+inline static constexpr ServiceCommandMeta MakeServiceCommandMeta() {
+ return {
+ .fw_low = l,
+ .fw_high = h,
+ .cmd_id = c,
+ .handler = WrapIpcCommandImpl,
+ };
+};
+
+
+#pragma GCC diagnostic pop
\ No newline at end of file
diff --git a/stratosphere/libstratosphere/include/stratosphere/ipc/ipc_service_object.hpp b/stratosphere/libstratosphere/include/stratosphere/ipc/ipc_service_object.hpp
new file mode 100644
index 000000000..979cd22a1
--- /dev/null
+++ b/stratosphere/libstratosphere/include/stratosphere/ipc/ipc_service_object.hpp
@@ -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 .
+ */
+
+#pragma once
+#include
+#include
+#include
+
+#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
+static constexpr size_t DispatchTableEntryCount() {
+ static_assert(std::is_base_of::value, "DispatchTable owners must derive from IServiceObject");
+ return sizeof(T::SERVICE_DISPATCH_TABLE_NAME)/sizeof(ServiceCommandMeta);
+}
+template
+static constexpr const ServiceCommandMeta* DispatchTable() {
+ static_assert(std::is_base_of::value, "DispatchTable owners must derive from IServiceObject");
+ return reinterpret_cast(&T::SERVICE_DISPATCH_TABLE_NAME);
+}
+
+template
+static constexpr uintptr_t ServiceObjectId() {
+ static_assert(std::is_base_of::value, "Service Objects must derive from IServiceObject");
+ return reinterpret_cast(&T::SERVICE_DISPATCH_TABLE_NAME);
+}
+
+class ServiceObjectHolder {
+ private:
+ std::shared_ptr 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
+ explicit ServiceObjectHolder(std::shared_ptr&& s) : srv(std::move(s)), dispatch_table(DispatchTable()), entry_count(DispatchTableEntryCount()) { }
+
+ template
+ ServiceImpl *GetServiceObject() {
+ if (GetServiceId() == ServiceObjectId()) {
+ return static_cast(this->srv.get());
+ }
+ return nullptr;
+ }
+
+ template
+ ServiceImpl *GetServiceObjectUnsafe() {
+ return static_cast