mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-11-05 07:25:08 +01:00
Add support for the TIPC protocol introduced in HOS 12.0.0
TIPC is a much lighter layer ontop of the Horizon IPC system than CMIF and is used by SM in 12.0.0+. This implementation is slightly hacky since it doesn't really keep a seperation between the underlying kernel IPC stuff and userspace like CMIF/TIPC, this should be fixed eventually, probably together with an IPC dispatch rewrite to avoid the mess of frozen maps. Tested with Hentai Uni, which now crashes needing 'ldr:ro'.
This commit is contained in:
parent
a40d7c78ad
commit
51f4e7662e
@ -10,6 +10,7 @@ namespace skyline::kernel::ipc {
|
|||||||
u8 *pointer{tls};
|
u8 *pointer{tls};
|
||||||
|
|
||||||
header = reinterpret_cast<CommandHeader *>(pointer);
|
header = reinterpret_cast<CommandHeader *>(pointer);
|
||||||
|
isTipc = static_cast<u16>(header->type) > static_cast<u16>(CommandType::TipcCloseSession);
|
||||||
pointer += sizeof(CommandHeader);
|
pointer += sizeof(CommandHeader);
|
||||||
|
|
||||||
if (header->handleDesc) {
|
if (header->handleDesc) {
|
||||||
@ -64,6 +65,10 @@ namespace skyline::kernel::ipc {
|
|||||||
|
|
||||||
auto bufCPointer{pointer + header->rawSize * sizeof(u32)};
|
auto bufCPointer{pointer + header->rawSize * sizeof(u32)};
|
||||||
|
|
||||||
|
if (isTipc) {
|
||||||
|
cmdArg = pointer;
|
||||||
|
cmdArgSz = header->rawSize * sizeof(u32);
|
||||||
|
} else {
|
||||||
size_t offset{static_cast<size_t>(pointer - tls)}; // We calculate the relative offset as the absolute one might differ
|
size_t offset{static_cast<size_t>(pointer - tls)}; // We calculate the relative offset as the absolute one might differ
|
||||||
auto padding{util::AlignUp(offset, constant::IpcPaddingSum) - offset}; // Calculate the amount of padding at the front
|
auto padding{util::AlignUp(offset, constant::IpcPaddingSum) - offset}; // Calculate the amount of padding at the front
|
||||||
pointer += padding;
|
pointer += padding;
|
||||||
@ -90,13 +95,13 @@ namespace skyline::kernel::ipc {
|
|||||||
cmdArg = pointer;
|
cmdArg = pointer;
|
||||||
cmdArgSz = header->rawSize * sizeof(u32);
|
cmdArgSz = header->rawSize * sizeof(u32);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
payloadOffset = cmdArg;
|
payloadOffset = cmdArg;
|
||||||
|
|
||||||
if (payload->magic != util::MakeMagic<u32>("SFCI") && (header->type != CommandType::Control && header->type != CommandType::ControlWithContext && header->type != CommandType::Close) && (!domain || domain->command != DomainCommand::CloseVHandle)) // SFCI is the magic in received IPC messages
|
if (!isTipc && payload->magic != util::MakeMagic<u32>("SFCI") && (header->type != CommandType::Control && header->type != CommandType::ControlWithContext && header->type != CommandType::Close) && (!domain || domain->command != DomainCommand::CloseVHandle)) // SFCI is the magic in received IPC messages
|
||||||
Logger::Debug("Unexpected Magic in PayloadHeader: 0x{:X}", static_cast<u32>(payload->magic));
|
Logger::Debug("Unexpected Magic in PayloadHeader: 0x{:X}", static_cast<u32>(payload->magic));
|
||||||
|
|
||||||
|
|
||||||
if (header->cFlag == BufferCFlag::SingleDescriptor) {
|
if (header->cFlag == BufferCFlag::SingleDescriptor) {
|
||||||
auto bufC{reinterpret_cast<BufferDescriptorC *>(bufCPointer)};
|
auto bufC{reinterpret_cast<BufferDescriptorC *>(bufCPointer)};
|
||||||
if (bufC->address) {
|
if (bufC->address) {
|
||||||
@ -120,20 +125,25 @@ namespace skyline::kernel::ipc {
|
|||||||
Logger::Verbose("Handle Descriptor: Send PID: {}, Copy Count: {}, Move Count: {}", static_cast<bool>(handleDesc->sendPid), static_cast<u32>(handleDesc->copyCount), static_cast<u32>(handleDesc->moveCount));
|
Logger::Verbose("Handle Descriptor: Send PID: {}, Copy Count: {}, Move Count: {}", static_cast<bool>(handleDesc->sendPid), static_cast<u32>(handleDesc->copyCount), static_cast<u32>(handleDesc->moveCount));
|
||||||
if (isDomain)
|
if (isDomain)
|
||||||
Logger::Verbose("Domain Header: Command: {}, Input Object Count: {}, Object ID: 0x{:X}", domain->command, domain->inputCount, domain->objectId);
|
Logger::Verbose("Domain Header: Command: {}, Input Object Count: {}, Object ID: 0x{:X}", domain->command, domain->inputCount, domain->objectId);
|
||||||
|
|
||||||
|
if (isTipc)
|
||||||
|
Logger::Verbose("TIPC Command ID: 0x{:X}", static_cast<u16>(header->type));
|
||||||
|
else
|
||||||
Logger::Verbose("Command ID: 0x{:X}", static_cast<u32>(payload->value));
|
Logger::Verbose("Command ID: 0x{:X}", static_cast<u32>(payload->value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IpcResponse::IpcResponse(const DeviceState &state) : state(state) {}
|
IpcResponse::IpcResponse(const DeviceState &state) : state(state) {}
|
||||||
|
|
||||||
void IpcResponse::WriteResponse(bool isDomain) {
|
void IpcResponse::WriteResponse(bool isDomain, bool isTipc) {
|
||||||
auto tls{state.ctx->tpidrroEl0};
|
auto tls{state.ctx->tpidrroEl0};
|
||||||
u8 *pointer{tls};
|
u8 *pointer{tls};
|
||||||
|
|
||||||
memset(tls, 0, constant::TlsIpcSize);
|
memset(tls, 0, constant::TlsIpcSize);
|
||||||
|
|
||||||
auto header{reinterpret_cast<CommandHeader *>(pointer)};
|
auto header{reinterpret_cast<CommandHeader *>(pointer)};
|
||||||
header->rawSize = static_cast<u32>((sizeof(PayloadHeader) + payload.size() + (domainObjects.size() * sizeof(KHandle)) + constant::IpcPaddingSum + (isDomain ? sizeof(DomainHeaderRequest) : 0)) / sizeof(u32)); // Size is in 32-bit units because Nintendo
|
size_t sizeBytes{isTipc ? (payload.size() + sizeof(Result)) : (sizeof(PayloadHeader) + constant::IpcPaddingSum + payload.size() + (domainObjects.size() * sizeof(KHandle)) + (isDomain ? sizeof(DomainHeaderRequest) : 0))};
|
||||||
|
header->rawSize = static_cast<u32>(util::DivideCeil(sizeBytes, sizeof(u32))); // Size is in 32-bit units because Nintendo
|
||||||
header->handleDesc = (!copyHandles.empty() || !moveHandles.empty());
|
header->handleDesc = (!copyHandles.empty() || !moveHandles.empty());
|
||||||
pointer += sizeof(CommandHeader);
|
pointer += sizeof(CommandHeader);
|
||||||
|
|
||||||
@ -154,6 +164,11 @@ namespace skyline::kernel::ipc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isTipc) {
|
||||||
|
*reinterpret_cast<Result *>(pointer) = errorCode;
|
||||||
|
pointer += sizeof(Result);
|
||||||
|
std::memcpy(pointer, payload.data(), payload.size());
|
||||||
|
} else {
|
||||||
size_t offset{static_cast<size_t>(pointer - tls)}; // We calculate the relative offset as the absolute one might differ
|
size_t offset{static_cast<size_t>(pointer - tls)}; // We calculate the relative offset as the absolute one might differ
|
||||||
auto padding{util::AlignUp(offset, constant::IpcPaddingSum) - offset}; // Calculate the amount of padding at the front
|
auto padding{util::AlignUp(offset, constant::IpcPaddingSum) - offset}; // Calculate the amount of padding at the front
|
||||||
pointer += padding;
|
pointer += padding;
|
||||||
@ -180,7 +195,8 @@ namespace skyline::kernel::ipc {
|
|||||||
pointer += sizeof(KHandle);
|
pointer += sizeof(KHandle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Logger::Verbose("Output: Raw Size: {}, Result: 0x{:X}, Copy Handles: {}, Move Handles: {}", static_cast<u32>(header->rawSize), static_cast<u32>(payloadHeader->value), copyHandles.size(), moveHandles.size());
|
Logger::Verbose("Output: Raw Size: {}, Result: 0x{:X}, Copy Handles: {}, Move Handles: {}", static_cast<u32>(header->rawSize), static_cast<u32>(errorCode), copyHandles.size(), moveHandles.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ namespace skyline {
|
|||||||
Control = 5, //!< A transaction between the client and IPC Manager
|
Control = 5, //!< A transaction between the client and IPC Manager
|
||||||
RequestWithContext = 6, //!< Request with Token
|
RequestWithContext = 6, //!< Request with Token
|
||||||
ControlWithContext = 7, //!< Control with Token
|
ControlWithContext = 7, //!< Control with Token
|
||||||
|
TipcCloseSession = 0xF,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -196,6 +197,7 @@ namespace skyline {
|
|||||||
CommandHeader *header{};
|
CommandHeader *header{};
|
||||||
HandleDescriptor *handleDesc{};
|
HandleDescriptor *handleDesc{};
|
||||||
bool isDomain{}; //!< If this is a domain request
|
bool isDomain{}; //!< If this is a domain request
|
||||||
|
bool isTipc; //!< If this is request uses the TIPC protocol
|
||||||
DomainHeaderRequest *domain{};
|
DomainHeaderRequest *domain{};
|
||||||
PayloadHeader *payload{};
|
PayloadHeader *payload{};
|
||||||
u8 *cmdArg{}; //!< A pointer to the data payload
|
u8 *cmdArg{}; //!< A pointer to the data payload
|
||||||
@ -298,8 +300,9 @@ namespace skyline {
|
|||||||
/**
|
/**
|
||||||
* @brief Writes this IpcResponse object's contents into TLS
|
* @brief Writes this IpcResponse object's contents into TLS
|
||||||
* @param isDomain Indicates if this is a domain response
|
* @param isDomain Indicates if this is a domain response
|
||||||
|
* @param isTipc Indicates if this is a TIPC response
|
||||||
*/
|
*/
|
||||||
void WriteResponse(bool isDomain);
|
void WriteResponse(bool isDomain, bool isTipc = false);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,11 +21,13 @@ namespace skyline::service {
|
|||||||
|
|
||||||
Result service::BaseService::HandleRequest(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
Result service::BaseService::HandleRequest(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||||
ServiceFunctionDescriptor function;
|
ServiceFunctionDescriptor function;
|
||||||
|
u32 functionId{request.isTipc ? static_cast<u32>(request.header->type) : request.payload->value};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
function = GetServiceFunction(request.payload->value);
|
function = GetServiceFunction(functionId, request.isTipc);
|
||||||
Logger::DebugNoPrefix("Service: {}", function.name);
|
Logger::DebugNoPrefix("Service: {}", function.name);
|
||||||
} catch (const std::out_of_range &) {
|
} catch (const std::out_of_range &) {
|
||||||
Logger::Warn("Cannot find function in service '{0}': 0x{1:X} ({1})", GetName(), static_cast<u32>(request.payload->value));
|
Logger::Warn("Cannot find {0} function in service '{1}': 0x{2:X} ({2})", request.isTipc ? "TIPC" : "HIPC", GetName(), static_cast<u32>(functionId));
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
TRACE_EVENT("service", perfetto::StaticString{function.name});
|
TRACE_EVENT("service", perfetto::StaticString{function.name});
|
||||||
|
@ -5,8 +5,10 @@
|
|||||||
|
|
||||||
#include <kernel/ipc.h>
|
#include <kernel/ipc.h>
|
||||||
|
|
||||||
|
constexpr static skyline::u32 TipcFunctionIdFlag{1U << 31}; //!< Flag applied to the stored service function ID to differentiate between TIPC and HIPC functions
|
||||||
#define SERVICE_STRINGIFY(string) #string
|
#define SERVICE_STRINGIFY(string) #string
|
||||||
#define SFUNC(id, Class, Function) std::pair<u32, std::pair<Result(Class::*)(type::KSession &, ipc::IpcRequest &, ipc::IpcResponse &), const char*>>{id, {&Class::Function, SERVICE_STRINGIFY(Class::Function)}}
|
#define SFUNC(id, Class, Function) std::pair<u32, std::pair<Result(Class::*)(type::KSession &, ipc::IpcRequest &, ipc::IpcResponse &), const char*>>{id, {&Class::Function, SERVICE_STRINGIFY(Class::Function)}}
|
||||||
|
#define SFUNC_TIPC(id, Class, Function) std::pair<u32, std::pair<Result(Class::*)(type::KSession &, ipc::IpcRequest &, ipc::IpcResponse &), const char*>>{TipcFunctionIdFlag | id, {&Class::Function, SERVICE_STRINGIFY(Class::Function)}}
|
||||||
#define SFUNC_BASE(id, Class, BaseClass, Function) std::pair<u32, std::pair<Result(Class::*)(type::KSession &, ipc::IpcRequest &, ipc::IpcResponse &), const char*>>{id, {&Class::CallBaseFunction<BaseClass, decltype(&BaseClass::Function), &BaseClass::Function>, SERVICE_STRINGIFY(Class::Function)}}
|
#define SFUNC_BASE(id, Class, BaseClass, Function) std::pair<u32, std::pair<Result(Class::*)(type::KSession &, ipc::IpcRequest &, ipc::IpcResponse &), const char*>>{id, {&Class::CallBaseFunction<BaseClass, decltype(&BaseClass::Function), &BaseClass::Function>, SERVICE_STRINGIFY(Class::Function)}}
|
||||||
#define SERVICE_DECL_AUTO(name, value) decltype(value) name = value
|
#define SERVICE_DECL_AUTO(name, value) decltype(value) name = value
|
||||||
#define SERVICE_DECL(...) \
|
#define SERVICE_DECL(...) \
|
||||||
@ -17,8 +19,8 @@ Result CallBaseFunction(type::KSession &session, ipc::IpcRequest &request, ipc::
|
|||||||
} \
|
} \
|
||||||
SERVICE_DECL_AUTO(functions, frozen::make_unordered_map({__VA_ARGS__})); \
|
SERVICE_DECL_AUTO(functions, frozen::make_unordered_map({__VA_ARGS__})); \
|
||||||
protected: \
|
protected: \
|
||||||
ServiceFunctionDescriptor GetServiceFunction(u32 id) override { \
|
ServiceFunctionDescriptor GetServiceFunction(u32 id, bool isTipc) override { \
|
||||||
auto& function{functions.at(id)}; \
|
auto& function{functions.at((isTipc ? TipcFunctionIdFlag : 0U) | id)}; \
|
||||||
return ServiceFunctionDescriptor{ \
|
return ServiceFunctionDescriptor{ \
|
||||||
reinterpret_cast<DerivedService*>(this), \
|
reinterpret_cast<DerivedService*>(this), \
|
||||||
reinterpret_cast<decltype(ServiceFunctionDescriptor::function)>(function.first), \
|
reinterpret_cast<decltype(ServiceFunctionDescriptor::function)>(function.first), \
|
||||||
@ -71,7 +73,7 @@ namespace skyline::service {
|
|||||||
*/
|
*/
|
||||||
virtual ~BaseService() = default;
|
virtual ~BaseService() = default;
|
||||||
|
|
||||||
virtual ServiceFunctionDescriptor GetServiceFunction(u32 id) {
|
virtual ServiceFunctionDescriptor GetServiceFunction(u32 id, bool isTipc) {
|
||||||
throw std::out_of_range("GetServiceFunction not implemented");
|
throw std::out_of_range("GetServiceFunction not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -238,12 +238,19 @@ namespace skyline::service {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case ipc::CommandType::Close:
|
case ipc::CommandType::Close:
|
||||||
|
case ipc::CommandType::TipcCloseSession:
|
||||||
Logger::Debug("Closing Session");
|
Logger::Debug("Closing Session");
|
||||||
CloseSession(handle);
|
CloseSession(handle);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
// TIPC command ID is encoded in the request type
|
||||||
|
if (request.isTipc) {
|
||||||
|
response.errorCode = session->serviceObject->HandleRequest(*session, request, response);
|
||||||
|
response.WriteResponse(session->isDomain, true);
|
||||||
|
} else {
|
||||||
throw exception("Unimplemented IPC message type: {}", static_cast<u16>(request.header->type));
|
throw exception("Unimplemented IPC message type: {}", static_cast<u16>(request.header->type));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Logger::Warn("svcSendSyncRequest called on closed handle: 0x{:X}", handle);
|
Logger::Warn("svcSendSyncRequest called on closed handle: 0x{:X}", handle);
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,9 @@ namespace skyline::service::sm {
|
|||||||
|
|
||||||
SERVICE_DECL(
|
SERVICE_DECL(
|
||||||
SFUNC(0x0, IUserInterface, Initialize),
|
SFUNC(0x0, IUserInterface, Initialize),
|
||||||
SFUNC(0x1, IUserInterface, GetService)
|
SFUNC_TIPC(0x10, IUserInterface, Initialize),
|
||||||
|
SFUNC(0x1, IUserInterface, GetService),
|
||||||
|
SFUNC_TIPC(0x11, IUserInterface, GetService)
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user