// SPDX-License-Identifier: MPL-2.0 // Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) #pragma once #include namespace skyline { namespace constant { constexpr u8 IpcPaddingSum{0x10}; // The sum of the padding surrounding the data payload constexpr u16 TlsIpcSize{0x100}; // The size of the IPC command buffer in a TLS slot } namespace kernel::ipc { /** * @url https://switchbrew.org/wiki/IPC_Marshalling#Type */ enum class CommandType : u16 { Invalid = 0, LegacyRequest = 1, Close = 2, //!< Closes the IPC Session LegacyControl = 3, Request = 4, //!< A normal IPC transaction between the server and client process Control = 5, //!< A transaction between the client and IPC Manager RequestWithContext = 6, //!< Request with Token ControlWithContext = 7, //!< Control with Token }; /** * @url https://switchbrew.org/wiki/IPC_Marshalling#Buffer_descriptor_C_.22ReceiveList.22 * @note Any values beyond this are the amount of C-Buffers present (Which is calculated as value - 2) */ enum class BufferCFlag : u8 { None = 0, //!< No C-Buffers present InlineDescriptor = 1, //!< An inlined C-Buffer which is written after the raw data section SingleDescriptor = 2, //!< A single C-Buffer }; /** * @url https://switchbrew.org/wiki/IPC_Marshalling#IPC_Command_Structure */ struct CommandHeader { CommandType type : 16; u8 xNo : 4; u8 aNo : 4; u8 bNo : 4; u8 wNo : 4; u32 rawSize : 10; BufferCFlag cFlag : 4; u32 : 17; bool handleDesc : 1; }; static_assert(sizeof(CommandHeader) == 8); /** * @url https://switchbrew.org/wiki/IPC_Marshalling#Handle_descriptor */ struct HandleDescriptor { bool sendPid : 1; u32 copyCount : 4; u32 moveCount : 4; u32 : 23; }; static_assert(sizeof(HandleDescriptor) == 4); /** * @brief Commands which can be issued via a domain request */ enum class DomainCommand : u8 { SendMessage = 1, CloseVHandle = 2, }; /** * @url https://switchbrew.org/wiki/IPC_Marshalling#Domains */ struct DomainHeaderRequest { DomainCommand command; u8 inputCount; u16 payloadSz; u32 objectId; u32 : 32; u32 token; }; static_assert(sizeof(DomainHeaderRequest) == 16); /** * @url https://switchbrew.org/wiki/IPC_Marshalling#Domains */ struct DomainHeaderResponse { u32 outputCount; u32 : 32; u64 : 64; }; static_assert(sizeof(DomainHeaderResponse) == 16); /** * @url https://switchbrew.org/wiki/IPC_Marshalling#Data_payload */ struct PayloadHeader { u32 magic; u32 version; u32 value; u32 token; }; static_assert(sizeof(PayloadHeader) == 16); /** * @brief The IPC Control commands as encoded into the payload value * @url https://switchbrew.org/wiki/IPC_Marshalling#Control */ enum class ControlCommand : u32 { ConvertCurrentObjectToDomain = 0, //!< Converts a regular IPC session into a domain session CopyFromCurrentDomain = 1, CloneCurrentObject = 2, //!< Creates a duplicate of the current session QueryPointerBufferSize = 3, //!< The size of the X buffers written by the servers (and by extension C-buffers supplied by the client) CloneCurrentObjectEx = 4, //!< Same as CloneCurrentObject }; /** * @url https://switchbrew.org/wiki/IPC_Marshalling#Buffer_descriptor_X_.22Pointer.22 */ struct BufferDescriptorX { u16 counter0_5 : 6; //!< The first 5 bits of the counter u16 address36_38 : 3; //!< Bit 36-38 of the address u16 counter9_11 : 3; //!< Bit 9-11 of the counter u16 address32_35 : 4; //!< Bit 32-35 of the address u16 size : 16; //!< The 16 bit size of the buffer u32 address0_31 : 32; //!< The first 32-bits of the address BufferDescriptorX(u64 address, u16 counter, u16 size) : size(size) { address0_31 = static_cast(address & 0x7FFFFFFF80000000); address32_35 = static_cast(address & 0x78000000); address36_38 = static_cast(address & 0x7000000); counter0_5 = static_cast(address & 0x7E00); counter9_11 = static_cast(address & 0x38); } inline u8 *Pointer() { return reinterpret_cast(static_cast(address0_31) | static_cast(address32_35) << 32 | static_cast(address36_38) << 36); } inline u16 Counter() { return static_cast(counter0_5) | static_cast(counter9_11) << 9; } }; static_assert(sizeof(BufferDescriptorX) == 8); /** * @url https://switchbrew.org/wiki/IPC_Marshalling#Buffer_descriptor_A.2FB.2FW_.22Send.22.2F.22Receive.22.2F.22Exchange.22 */ struct BufferDescriptorABW { u32 size0_31 : 32; //!< The first 32 bits of the size u32 address0_31 : 32; //!< The first 32 bits of the address u8 flags : 2; //!< The buffer flags u8 address36_38 : 3; //!< Bit 36-38 of the address u32 : 19; u8 size32_35 : 4; //!< Bit 32-35 of the size u8 address32_35 : 4; //!< Bit 32-35 of the address BufferDescriptorABW(u64 address, u64 size) { address0_31 = static_cast(address & 0x7FFFFFFF80000000); address32_35 = static_cast(address & 0x78000000); address36_38 = static_cast(address & 0x7000000); size0_31 = static_cast(size & 0x7FFFFFFF80000000); size32_35 = static_cast(size & 0x78000000); } inline u8 *Pointer() { return reinterpret_cast(static_cast(address0_31) | static_cast(address32_35) << 32 | static_cast(address36_38) << 36); } inline u64 Size() { return static_cast(size0_31) | static_cast(size32_35) << 32; } }; static_assert(sizeof(BufferDescriptorABW) == 12); /** * @url https://switchbrew.org/wiki/IPC_Marshalling#Buffer_descriptor_C_.22ReceiveList.22 */ struct BufferDescriptorC { u64 address : 48; //!< The 48-bit address of the buffer u32 size : 16; //!< The 16-bit size of the buffer inline u8 *Pointer() { return reinterpret_cast(address); } BufferDescriptorC(u64 address, u16 size) : address(address), size(size) {} }; static_assert(sizeof(BufferDescriptorC) == 8); enum class IpcBufferType { X, //!< Type-X buffer (BufferDescriptorX) A, //!< Type-A buffer (BufferDescriptorABW) B, //!< Type-B buffer (BufferDescriptorABW) W, //!< Type-W buffer (BufferDescriptorABW) C, //!< Type-C buffer (BufferDescriptorC) }; /** * @brief A wrapper over an IPC Request which allows it to be parsed and used effectively * @url https://switchbrew.org/wiki/IPC_Marshalling */ class IpcRequest { private: u8 *payloadOffset; //!< The offset of the data read from the payload public: CommandHeader *header{}; HandleDescriptor *handleDesc{}; bool isDomain{}; //!< If this is a domain request DomainHeaderRequest *domain{}; PayloadHeader *payload{}; u8 *cmdArg{}; //!< A pointer to the data payload u64 cmdArgSz{}; //!< The size of the data payload std::vector copyHandles; //!< The handles that should be copied from the server to the client process (The difference is just to match application expectations, there is no real difference b/w copying and moving handles) std::vector moveHandles; //!< The handles that should be moved from the server to the client process rather than copied std::vector domainObjects; std::vector> inputBuf; std::vector> outputBuf; IpcRequest(bool isDomain, const DeviceState &state); /** * @brief Returns a reference to an item from the top of the payload */ template inline ValueType &Pop() { ValueType &value{*reinterpret_cast(payloadOffset)}; payloadOffset += sizeof(ValueType); return value; } /** * @brief Returns a std::string_view from the payload * @param size The length of the string (0 means the string is null terminated) */ inline std::string_view PopString(size_t size = 0) { auto view{size ? std::string_view(reinterpret_cast(payloadOffset), size) : std::string_view(reinterpret_cast(payloadOffset))}; payloadOffset += view.length(); return view; } /** * @brief Skips an object to pop off the top */ template inline void Skip() { payloadOffset += sizeof(ValueType); } }; /** * @brief A wrapper over an IPC Response which allows it to be defined and serialized efficiently * @url https://switchbrew.org/wiki/IPC_Marshalling */ class IpcResponse { private: const DeviceState &state; std::vector payload; //!< The contents to be pushed to the data payload public: Result errorCode{}; //!< The error code to respond with, it is 0 (Success) by default std::vector copyHandles; std::vector moveHandles; std::vector domainObjects; IpcResponse(const DeviceState &state); /** * @brief Writes an object to the payload * @tparam ValueType The type of the object to write * @param value A reference to the object to be written */ template inline void Push(const ValueType &value) { auto size{payload.size()}; payload.resize(size + sizeof(ValueType)); std::memcpy(payload.data() + size, reinterpret_cast(&value), sizeof(ValueType)); } /** * @brief Writes a string to the payload * @param string The string to write to the payload */ inline void Push(std::string_view string) { auto size{payload.size()}; payload.resize(size + string.size()); std::memcpy(payload.data() + size, string.data(), string.size()); } /** * @brief Writes this IpcResponse object's contents into TLS * @param isDomain Indicates if this is a domain response */ void WriteResponse(bool isDomain); }; } }