From f1cf6dcf9712fac90001be8cc8d1e3c4e66fc01d Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sun, 22 Apr 2018 00:11:57 -0600 Subject: [PATCH] Implement all of sm. --- stratosphere/sm/source/sm_manager_service.cpp | 7 +- stratosphere/sm/source/sm_registration.cpp | 262 ++++++++++++++++++ stratosphere/sm/source/sm_registration.hpp | 40 +++ stratosphere/sm/source/sm_user_service.cpp | 14 +- 4 files changed, 313 insertions(+), 10 deletions(-) create mode 100644 stratosphere/sm/source/sm_registration.cpp create mode 100644 stratosphere/sm/source/sm_registration.hpp diff --git a/stratosphere/sm/source/sm_manager_service.cpp b/stratosphere/sm/source/sm_manager_service.cpp index 87eeb6b47..f63e7ca28 100644 --- a/stratosphere/sm/source/sm_manager_service.cpp +++ b/stratosphere/sm/source/sm_manager_service.cpp @@ -1,5 +1,6 @@ #include #include "sm_manager_service.hpp" +#include "sm_registration.hpp" Result ManagerService::dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size) { Result rc = 0xF601; @@ -19,11 +20,9 @@ Result ManagerService::dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_ std::tuple ManagerService::register_process(u64 pid, InBuffer acid_sac, InBuffer aci0_sac) { - /* TODO */ - return std::make_tuple(0xF601); + return std::make_tuple(Registration::RegisterProcess(pid, acid_sac.buffer, acid_sac.num_elements, aci0_sac.buffer, aci0_sac.num_elements)); } std::tuple ManagerService::unregister_process(u64 pid) { - /* TODO */ - return std::make_tuple(0xF601); + return std::make_tuple(Registration::UnregisterProcess(pid)); } \ No newline at end of file diff --git a/stratosphere/sm/source/sm_registration.cpp b/stratosphere/sm/source/sm_registration.cpp new file mode 100644 index 000000000..b52157002 --- /dev/null +++ b/stratosphere/sm/source/sm_registration.cpp @@ -0,0 +1,262 @@ +#include +#include +#include "sm_registration.hpp" + +static Registration::Process g_process_list[REGISTRATION_LIST_MAX_PROCESS] = {0}; +static Registration::Service g_service_list[REGISTRATION_LIST_MAX_SERVICE] = {0}; + +/* Utilities. */ +Registration::Process *Registration::GetProcessForPid(u64 pid) { + for (unsigned int i = 0; i < REGISTRATION_LIST_MAX_PROCESS; i++) { + if (g_process_list[i].pid == pid) { + return &g_process_list[i]; + } + } + return NULL; +} + +Registration::Process *Registration::GetFreeProcess() { + return GetProcessForPid(0); +} + +Registration::Service *Registration::GetService(u64 service) { + for (unsigned int i = 0; i < REGISTRATION_LIST_MAX_SERVICE; i++) { + if (g_service_list[i].service_name == service) { + return &g_service_list[i]; + } + } + return NULL; +} + +Registration::Service *Registration::GetFreeService() { + return GetService(0); +} + +bool Registration::IsValidForSac(u8 *sac, size_t sac_size, u64 service, bool is_host) { + u8 cur_ctrl; + u64 cur_service; + bool cur_is_host; + size_t remaining = sac_size; + while (remaining) { + cur_ctrl = *sac++; + remaining--; + size_t cur_size = (cur_ctrl & 7) + 1; + if (cur_size > remaining) { + break; + } + cur_is_host = (cur_ctrl & 0x80) != 0; + cur_service = 0; + for (unsigned int i = 0; i < cur_size; i++) { + cur_service |= ((u64)sac[i]) << (8 * i); + } + /* Check if the last byte is a wildcard ('*') */ + if (sac[cur_size - 1] == '*') { + /* Mask cur_service, service with 0xFF.. up until the wildcard. */ + cur_service &= U64_MAX >> (8 * (8 - cur_size - 1)); + service &= U64_MAX >> (8 * (8 - cur_size - 1)); + } + if (cur_service == service && (!is_host || cur_is_host)) { + return true; + } + sac += cur_size; + remaining -= cur_size; + } + return false; +} + + +bool Registration::ValidateSacAgainstRestriction(u8 *r_sac, size_t r_sac_size, u8 *sac, size_t sac_size) { + u8 cur_ctrl; + u64 cur_service; + bool cur_is_host; + size_t remaining = sac_size; + while (remaining) { + cur_ctrl = *sac++; + remaining--; + size_t cur_size = (cur_ctrl & 7) + 1; + if (cur_size > remaining) { + break; + } + cur_is_host = (cur_ctrl & 0x80) != 0; + cur_service = 0; + for (unsigned int i = 0; i < cur_size; i++) { + cur_service |= ((u64)sac[i]) << (8 * i); + } + if (!IsValidForSac(r_sac, r_sac_size, cur_service, cur_is_host)) { + return false; + } + sac += cur_size; + remaining -= cur_size; + } + return true; +} + +/* Process management. */ +Result Registration::RegisterProcess(u64 pid, u8 *acid_sac, size_t acid_sac_size, u8 *aci0_sac, size_t aci0_sac_size) { + Registration::Process *proc = GetFreeProcess(); + if (aci0_sac_size > REGISTRATION_MAX_SAC_SIZE) { + return 0x1215; + } + + if (proc == NULL) { + return 0x215; + } + + if (aci0_sac_size && !ValidateSacAgainstRestriction(acid_sac, acid_sac_size, aci0_sac, aci0_sac_size)) { + return 0x1015; + } + + proc->pid = pid; + proc->sac_size = aci0_sac_size; + std::copy(aci0_sac, aci0_sac + aci0_sac_size, proc->sac); + return 0; +} + +Result Registration::UnregisterProcess(u64 pid) { + Registration::Process *proc = GetProcessForPid(pid); + if (proc == NULL) { + return 0x415; + } + + proc->pid = 0; + return 0; +} + +/* Service management. */ +bool Registration::HasService(u64 service) { + for (unsigned int i = 0; i < REGISTRATION_LIST_MAX_SERVICE; i++) { + if (g_service_list[i].service_name == service) { + return true; + } + } + return false; +} + +Result Registration::GetServiceForPid(u64 pid, u64 service, Handle *out) { + if (!service) { + return 0xC15; + } + + u64 service_name_len = 0; + while ((service >> (8 * service_name_len)) & 0xFF) { + service_name_len++; + } + + /* If the service has bytes after a null terminator, that's no good. */ + if ((service >> (8 * service_name_len))) { + return 0xC15; + } + + if (pid >= REGISTRATION_PID_BUILTIN_MAX) { + Registration::Process *proc = GetProcessForPid(pid); + if (proc == NULL) { + return 0x415; + } + + if (!IsValidForSac(proc->sac, proc->sac_size, service, false)) { + return 0x1015; + } + } + + Registration::Service *target_service = GetService(service); + if (target_service == NULL) { + /* TODO: This defers the request, somehow? Needs to be RE'd in more detail. */ + return 0x6580A; + } + + *out = 0; + Result rc = svcConnectToPort(out, target_service->port_h); + if (R_FAILED(rc)) { + if ((rc & 0x3FFFFF) == 0xE01) { + return 0x615; + } + } + return rc; +} + +Result Registration::RegisterServiceForPid(u64 pid, u64 service, u64 max_sessions, bool is_light, Handle *out) { + if (!service) { + return 0xC15; + } + + u64 service_name_len = 0; + while ((service >> (8 * service_name_len)) & 0xFF) { + service_name_len++; + } + + /* If the service has bytes after a null terminator, that's no good. */ + if ((service >> (8 * service_name_len))) { + return 0xC15; + } + + if (pid >= REGISTRATION_PID_BUILTIN_MAX) { + Registration::Process *proc = GetProcessForPid(pid); + if (proc == NULL) { + return 0x415; + } + + if (!IsValidForSac(proc->sac, proc->sac_size, service, true)) { + return 0x1015; + } + } + + if (HasService(service)) { + return 0x815; + } + + Registration::Service *free_service = GetFreeService(); + if (free_service == NULL) { + return 0xA15; + } + + *out = 0; + *free_service = (const Registration::Service){0}; + Result rc = svcCreatePort(out, &free_service->port_h, max_sessions, is_light, (char *)&free_service->service_name); + + if (R_SUCCEEDED(rc)) { + free_service->service_name = service; + free_service->owner_pid = pid; + } + + return rc; +} + +Result Registration::UnregisterServiceForPid(u64 pid, u64 service) { + if (!service) { + return 0xC15; + } + + u64 service_name_len = 0; + while ((service >> (8 * service_name_len)) & 0xFF) { + service_name_len++; + } + + /* If the service has bytes after a null terminator, that's no good. */ + if ((service >> (8 * service_name_len))) { + return 0xC15; + } + + if (pid >= REGISTRATION_PID_BUILTIN_MAX) { + Registration::Process *proc = GetProcessForPid(pid); + if (proc == NULL) { + return 0x415; + } + + if (!IsValidForSac(proc->sac, proc->sac_size, service, true)) { + return 0x1015; + } + } + + Registration::Service *target_service = GetService(service); + if (target_service == NULL) { + return 0xE15; + } + + if (target_service->owner_pid != pid) { + return 0x1015; + } + + svcCloseHandle(target_service->port_h); + *target_service = (const Registration::Service){0}; + return 0; +} diff --git a/stratosphere/sm/source/sm_registration.hpp b/stratosphere/sm/source/sm_registration.hpp new file mode 100644 index 000000000..8b15a97eb --- /dev/null +++ b/stratosphere/sm/source/sm_registration.hpp @@ -0,0 +1,40 @@ +#pragma once +#include + +#define REGISTRATION_LIST_MAX_PROCESS (0x40) +#define REGISTRATION_LIST_MAX_SERVICE (0x100) +#define REGISTRATION_MAX_SAC_SIZE (0x200) +#define REGISTRATION_PID_BUILTIN_MAX 0x7 + +class Registration { + public: + struct Process { + u64 pid; + u64 sac_size; + u8 sac[REGISTRATION_MAX_SAC_SIZE]; + }; + + struct Service { + u64 service_name; + u64 owner_pid; + Handle port_h; + }; + + /* Utilities. */ + static Registration::Process *GetProcessForPid(u64 pid); + static Registration::Process *GetFreeProcess(); + static Registration::Service *GetService(u64 service); + static Registration::Service *GetFreeService(); + static bool IsValidForSac(u8 *sac, size_t sac_size, u64 service, bool is_host); + static bool ValidateSacAgainstRestriction(u8 *r_sac, size_t r_sac_size, u8 *sac, size_t sac_size); + + /* Process management. */ + static Result RegisterProcess(u64 pid, u8 *acid_sac, size_t acid_sac_size, u8 *aci0_sac, size_t aci0_sac_size); + static Result UnregisterProcess(u64 pid); + + /* Service management. */ + static bool HasService(u64 service); + static Result GetServiceForPid(u64 pid, u64 service, Handle *out); + static Result RegisterServiceForPid(u64 pid, u64 service, u64 max_sessions, bool is_light, Handle *out); + static Result UnregisterServiceForPid(u64 pid, u64 service); +}; diff --git a/stratosphere/sm/source/sm_user_service.cpp b/stratosphere/sm/source/sm_user_service.cpp index aed18d948..44726aa1c 100644 --- a/stratosphere/sm/source/sm_user_service.cpp +++ b/stratosphere/sm/source/sm_user_service.cpp @@ -1,5 +1,6 @@ #include #include "sm_user_service.hpp" +#include "sm_registration.hpp" Result UserService::dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size) { Result rc = 0xF601; @@ -30,16 +31,17 @@ std::tuple UserService::initialize(PidDescriptor pid) { } std::tuple UserService::get_service(u64 service) { - /* TODO */ - return std::make_tuple(0xF601, MovedHandle{0}); + Handle session_h = 0; + Result rc = Registration::GetServiceForPid(this->pid, service, &session_h); + return std::make_tuple(rc, MovedHandle{session_h}); } std::tuple UserService::register_service(u64 service, u8 is_light, u32 max_sessions) { - /* TODO */ - return std::make_tuple(0xF601, MovedHandle{0}); + Handle service_h = 0; + Result rc = Registration::RegisterServiceForPid(this->pid, service, max_sessions, is_light != 0, &service_h); + return std::make_tuple(rc, MovedHandle{service_h}); } std::tuple UserService::unregister_service(u64 service) { - /* TODO */ - return std::make_tuple(0xF601); + return std::make_tuple(Registration::UnregisterServiceForPid(this->pid, service)); }