mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-06-02 03:48:45 +02:00
fe5061a8e0
This addresses all CR comments including more codebase-wide changes arising from certain review comments like proper usage of its/it's and consistent contraction of it is into it's. An overhaul was made to the presentation and formatting of `KThread.h` and `LoadBalance` works has been superseded by `GetOptimalCoreForThread` which can be used alongside `InsertThread` or `MigrateToCore`. It makes the API far more atomic and neater. This was a major point of contention for the design prior, it's simplified some code and potentially improved performance.
303 lines
12 KiB
C++
303 lines
12 KiB
C++
// SPDX-License-Identifier: MPL-2.0
|
|
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
|
|
|
#pragma once
|
|
|
|
#include <common.h>
|
|
|
|
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<u32>(address & 0x7FFFFFFF80000000);
|
|
address32_35 = static_cast<u16>(address & 0x78000000);
|
|
address36_38 = static_cast<u16>(address & 0x7000000);
|
|
counter0_5 = static_cast<u16>(address & 0x7E00);
|
|
counter9_11 = static_cast<u16>(address & 0x38);
|
|
}
|
|
|
|
inline u8 *Pointer() {
|
|
return reinterpret_cast<u8 *>(static_cast<u64>(address0_31) | static_cast<u64>(address32_35) << 32 | static_cast<u64>(address36_38) << 36);
|
|
}
|
|
|
|
inline u16 Counter() {
|
|
return static_cast<u16>(counter0_5) | static_cast<u16>(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<u32>(address & 0x7FFFFFFF80000000);
|
|
address32_35 = static_cast<u8>(address & 0x78000000);
|
|
address36_38 = static_cast<u8>(address & 0x7000000);
|
|
size0_31 = static_cast<u32>(size & 0x7FFFFFFF80000000);
|
|
size32_35 = static_cast<u8>(size & 0x78000000);
|
|
}
|
|
|
|
inline u8 *Pointer() {
|
|
return reinterpret_cast<u8 *>(static_cast<u64>(address0_31) | static_cast<u64>(address32_35) << 32 | static_cast<u64>(address36_38) << 36);
|
|
}
|
|
|
|
inline u64 Size() {
|
|
return static_cast<u64>(size0_31) | static_cast<u64>(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<u8 *>(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<KHandle> 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<KHandle> moveHandles; //!< The handles that should be moved from the server to the client process rather than copied
|
|
std::vector<KHandle> domainObjects;
|
|
std::vector<span<u8>> inputBuf;
|
|
std::vector<span<u8>> outputBuf;
|
|
|
|
IpcRequest(bool isDomain, const DeviceState &state);
|
|
|
|
/**
|
|
* @brief Returns a reference to an item from the top of the payload
|
|
*/
|
|
template<typename ValueType>
|
|
inline ValueType &Pop() {
|
|
ValueType &value{*reinterpret_cast<ValueType *>(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<const char *>(payloadOffset), size) : std::string_view(reinterpret_cast<const char *>(payloadOffset))};
|
|
payloadOffset += view.length();
|
|
return view;
|
|
}
|
|
|
|
/**
|
|
* @brief Skips an object to pop off the top
|
|
*/
|
|
template<typename ValueType>
|
|
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<u8> payload; //!< The contents to be pushed to the data payload
|
|
|
|
public:
|
|
Result errorCode{}; //!< The error code to respond with, it's 0 (Success) by default
|
|
std::vector<KHandle> copyHandles;
|
|
std::vector<KHandle> moveHandles;
|
|
std::vector<KHandle> 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<typename ValueType>
|
|
inline void Push(const ValueType &value) {
|
|
auto size{payload.size()};
|
|
payload.resize(size + sizeof(ValueType));
|
|
std::memcpy(payload.data() + size, reinterpret_cast<const u8 *>(&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);
|
|
};
|
|
}
|
|
}
|