mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-12-29 15:15:30 +03: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};
|
||||
|
||||
header = reinterpret_cast<CommandHeader *>(pointer);
|
||||
isTipc = static_cast<u16>(header->type) > static_cast<u16>(CommandType::TipcCloseSession);
|
||||
pointer += sizeof(CommandHeader);
|
||||
|
||||
if (header->handleDesc) {
|
||||
@ -64,39 +65,43 @@ namespace skyline::kernel::ipc {
|
||||
|
||||
auto bufCPointer{pointer + header->rawSize * sizeof(u32)};
|
||||
|
||||
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
|
||||
pointer += padding;
|
||||
|
||||
if (isDomain && (header->type == CommandType::Request || header->type == CommandType::RequestWithContext)) {
|
||||
domain = reinterpret_cast<DomainHeaderRequest *>(pointer);
|
||||
pointer += sizeof(DomainHeaderRequest);
|
||||
|
||||
payload = reinterpret_cast<PayloadHeader *>(pointer);
|
||||
pointer += sizeof(PayloadHeader);
|
||||
|
||||
cmdArg = pointer;
|
||||
cmdArgSz = domain->payloadSz - sizeof(PayloadHeader);
|
||||
pointer += cmdArgSz;
|
||||
|
||||
for (u8 index{}; domain->inputCount > index; index++) {
|
||||
domainObjects.push_back(*reinterpret_cast<KHandle *>(pointer));
|
||||
pointer += sizeof(KHandle);
|
||||
}
|
||||
} else {
|
||||
payload = reinterpret_cast<PayloadHeader *>(pointer);
|
||||
pointer += sizeof(PayloadHeader);
|
||||
|
||||
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
|
||||
auto padding{util::AlignUp(offset, constant::IpcPaddingSum) - offset}; // Calculate the amount of padding at the front
|
||||
pointer += padding;
|
||||
|
||||
if (isDomain && (header->type == CommandType::Request || header->type == CommandType::RequestWithContext)) {
|
||||
domain = reinterpret_cast<DomainHeaderRequest *>(pointer);
|
||||
pointer += sizeof(DomainHeaderRequest);
|
||||
|
||||
payload = reinterpret_cast<PayloadHeader *>(pointer);
|
||||
pointer += sizeof(PayloadHeader);
|
||||
|
||||
cmdArg = pointer;
|
||||
cmdArgSz = domain->payloadSz - sizeof(PayloadHeader);
|
||||
pointer += cmdArgSz;
|
||||
|
||||
for (u8 index{}; domain->inputCount > index; index++) {
|
||||
domainObjects.push_back(*reinterpret_cast<KHandle *>(pointer));
|
||||
pointer += sizeof(KHandle);
|
||||
}
|
||||
} else {
|
||||
payload = reinterpret_cast<PayloadHeader *>(pointer);
|
||||
pointer += sizeof(PayloadHeader);
|
||||
|
||||
cmdArg = pointer;
|
||||
cmdArgSz = header->rawSize * sizeof(u32);
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
|
||||
if (header->cFlag == BufferCFlag::SingleDescriptor) {
|
||||
auto bufC{reinterpret_cast<BufferDescriptorC *>(bufCPointer)};
|
||||
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));
|
||||
if (isDomain)
|
||||
Logger::Verbose("Domain Header: Command: {}, Input Object Count: {}, Object ID: 0x{:X}", domain->command, domain->inputCount, domain->objectId);
|
||||
Logger::Verbose("Command ID: 0x{:X}", static_cast<u32>(payload->value));
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
IpcResponse::IpcResponse(const DeviceState &state) : state(state) {}
|
||||
|
||||
void IpcResponse::WriteResponse(bool isDomain) {
|
||||
void IpcResponse::WriteResponse(bool isDomain, bool isTipc) {
|
||||
auto tls{state.ctx->tpidrroEl0};
|
||||
u8 *pointer{tls};
|
||||
|
||||
memset(tls, 0, constant::TlsIpcSize);
|
||||
|
||||
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());
|
||||
pointer += sizeof(CommandHeader);
|
||||
|
||||
@ -154,33 +164,39 @@ namespace skyline::kernel::ipc {
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
pointer += padding;
|
||||
|
||||
if (isDomain) {
|
||||
auto domain{reinterpret_cast<DomainHeaderResponse *>(pointer)};
|
||||
domain->outputCount = static_cast<u32>(domainObjects.size());
|
||||
pointer += sizeof(DomainHeaderResponse);
|
||||
}
|
||||
|
||||
auto payloadHeader{reinterpret_cast<PayloadHeader *>(pointer)};
|
||||
payloadHeader->magic = util::MakeMagic<u32>("SFCO"); // SFCO is the magic in IPC responses
|
||||
payloadHeader->version = 1;
|
||||
payloadHeader->value = errorCode;
|
||||
pointer += sizeof(PayloadHeader);
|
||||
|
||||
if (!payload.empty())
|
||||
if (isTipc) {
|
||||
*reinterpret_cast<Result *>(pointer) = errorCode;
|
||||
pointer += sizeof(Result);
|
||||
std::memcpy(pointer, payload.data(), payload.size());
|
||||
pointer += payload.size();
|
||||
} else {
|
||||
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
|
||||
pointer += padding;
|
||||
|
||||
if (isDomain) {
|
||||
for (auto &domainObject : domainObjects) {
|
||||
*reinterpret_cast<KHandle *>(pointer) = domainObject;
|
||||
pointer += sizeof(KHandle);
|
||||
if (isDomain) {
|
||||
auto domain{reinterpret_cast<DomainHeaderResponse *>(pointer)};
|
||||
domain->outputCount = static_cast<u32>(domainObjects.size());
|
||||
pointer += sizeof(DomainHeaderResponse);
|
||||
}
|
||||
|
||||
auto payloadHeader{reinterpret_cast<PayloadHeader *>(pointer)};
|
||||
payloadHeader->magic = util::MakeMagic<u32>("SFCO"); // SFCO is the magic in IPC responses
|
||||
payloadHeader->version = 1;
|
||||
payloadHeader->value = errorCode;
|
||||
pointer += sizeof(PayloadHeader);
|
||||
|
||||
if (!payload.empty())
|
||||
std::memcpy(pointer, payload.data(), payload.size());
|
||||
pointer += payload.size();
|
||||
|
||||
if (isDomain) {
|
||||
for (auto &domainObject : domainObjects) {
|
||||
*reinterpret_cast<KHandle *>(pointer) = domainObject;
|
||||
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
|
||||
RequestWithContext = 6, //!< Request with Token
|
||||
ControlWithContext = 7, //!< Control with Token
|
||||
TipcCloseSession = 0xF,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -196,6 +197,7 @@ namespace skyline {
|
||||
CommandHeader *header{};
|
||||
HandleDescriptor *handleDesc{};
|
||||
bool isDomain{}; //!< If this is a domain request
|
||||
bool isTipc; //!< If this is request uses the TIPC protocol
|
||||
DomainHeaderRequest *domain{};
|
||||
PayloadHeader *payload{};
|
||||
u8 *cmdArg{}; //!< A pointer to the data payload
|
||||
@ -298,8 +300,9 @@ namespace skyline {
|
||||
/**
|
||||
* @brief Writes this IpcResponse object's contents into TLS
|
||||
* @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) {
|
||||
ServiceFunctionDescriptor function;
|
||||
u32 functionId{request.isTipc ? static_cast<u32>(request.header->type) : request.payload->value};
|
||||
|
||||
try {
|
||||
function = GetServiceFunction(request.payload->value);
|
||||
function = GetServiceFunction(functionId, request.isTipc);
|
||||
Logger::DebugNoPrefix("Service: {}", function.name);
|
||||
} 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 {};
|
||||
}
|
||||
TRACE_EVENT("service", perfetto::StaticString{function.name});
|
||||
|
@ -5,8 +5,10 @@
|
||||
|
||||
#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 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 SERVICE_DECL_AUTO(name, value) decltype(value) name = value
|
||||
#define SERVICE_DECL(...) \
|
||||
@ -15,10 +17,10 @@ template<typename BaseClass, typename BaseFunctionType, BaseFunctionType BaseFun
|
||||
Result CallBaseFunction(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { \
|
||||
return (static_cast<BaseClass *>(this)->*BaseFunction)(session, request, response); \
|
||||
} \
|
||||
SERVICE_DECL_AUTO(functions, frozen::make_unordered_map({__VA_ARGS__})); \
|
||||
SERVICE_DECL_AUTO(functions, frozen::make_unordered_map({__VA_ARGS__})); \
|
||||
protected: \
|
||||
ServiceFunctionDescriptor GetServiceFunction(u32 id) override { \
|
||||
auto& function{functions.at(id)}; \
|
||||
ServiceFunctionDescriptor GetServiceFunction(u32 id, bool isTipc) override { \
|
||||
auto& function{functions.at((isTipc ? TipcFunctionIdFlag : 0U) | id)}; \
|
||||
return ServiceFunctionDescriptor{ \
|
||||
reinterpret_cast<DerivedService*>(this), \
|
||||
reinterpret_cast<decltype(ServiceFunctionDescriptor::function)>(function.first), \
|
||||
@ -71,7 +73,7 @@ namespace skyline::service {
|
||||
*/
|
||||
virtual ~BaseService() = default;
|
||||
|
||||
virtual ServiceFunctionDescriptor GetServiceFunction(u32 id) {
|
||||
virtual ServiceFunctionDescriptor GetServiceFunction(u32 id, bool isTipc) {
|
||||
throw std::out_of_range("GetServiceFunction not implemented");
|
||||
}
|
||||
|
||||
|
@ -238,11 +238,18 @@ namespace skyline::service {
|
||||
break;
|
||||
|
||||
case ipc::CommandType::Close:
|
||||
case ipc::CommandType::TipcCloseSession:
|
||||
Logger::Debug("Closing Session");
|
||||
CloseSession(handle);
|
||||
break;
|
||||
default:
|
||||
throw exception("Unimplemented IPC message type: {}", static_cast<u16>(request.header->type));
|
||||
// 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));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Logger::Warn("svcSendSyncRequest called on closed handle: 0x{:X}", handle);
|
||||
|
@ -40,7 +40,9 @@ namespace skyline::service::sm {
|
||||
|
||||
SERVICE_DECL(
|
||||
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