/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#pragma once
#include "../sf_common.hpp"
#include "../sf_service_object.hpp"
#include "../sf_out.hpp"
#include "../sf_buffers.hpp"
#include "../sf_handles.hpp"
#include "../cmif/sf_cmif_pointer_and_size.hpp"
#include "../cmif/sf_cmif_service_dispatch.hpp"
#include "../cmif/sf_cmif_service_object_holder.hpp"
#include "../cmif/sf_cmif_domain_api.hpp"
#include "../hipc/sf_hipc_api.hpp"
#include "../hipc/sf_hipc_server_session_manager.hpp"
/* Serialization classes. */
namespace ams::sf {
namespace impl {
struct ProcessIdHolder {
os::ProcessId process_id;
constexpr explicit operator os::ProcessId() const { return this->process_id; }
constexpr os::ProcessId GetValue() const { return this->process_id; }
constexpr void SetValue(const os::ProcessId &p) { this->process_id = p; }
};
}
struct ClientProcessId : public impl::ProcessIdHolder {};
static_assert(std::is_trivial::value && sizeof(ClientProcessId) == sizeof(os::ProcessId), "ClientProcessId");
struct ClientAppletResourceUserId : public impl::ProcessIdHolder {};
static_assert(std::is_trivial::value && sizeof(ClientAppletResourceUserId ) == sizeof(os::ProcessId), "ClientAppletResourceUserId");
namespace impl {
constexpr inline Result MarshalProcessId(ClientProcessId &client, const os::ProcessId &client_process_id) {
client.SetValue(client_process_id);
return ResultSuccess();
}
constexpr inline Result MarshalProcessId(ClientAppletResourceUserId &client, const os::ProcessId &client_process_id) {
if (client.GetValue() != client_process_id && client.GetValue() != os::ProcessId{}) {
return sf::ResultPreconditionViolation();
}
return ResultSuccess();
}
}
namespace impl {
struct OutObjectTag{};
template
class InOutObjectHolder;
}
template requires std::derived_from
class Out> : public impl::OutObjectTag {
template
friend class Out;
public:
using Interface = ServiceImpl;
private:
impl::SharedPointerBase *m_out;
cmif::DomainObjectId *m_object_id;
private:
Out(impl::SharedPointerBase *p, cmif::DomainObjectId *o = nullptr) : m_out(p), m_object_id(o) { /* ... */ }
public:
Out(const Out &rhs) : m_out(rhs.m_out), m_object_id(rhs.m_object_id) { /* ... */ }
Out(SharedPointer *out, cmif::DomainObjectId *o = nullptr) : m_out(std::addressof(out->m_base)), m_object_id(o) { /* .... */ }
template requires std::derived_from
Out(Out> out) : m_out(out.m_out), m_object_id(out.m_object_id) { /* ... */ }
template requires std::derived_from
Out(SharedPointer *out, cmif::DomainObjectId *o = nullptr) : m_out(std::addressof(out->m_base)), m_object_id(o) { /* .... */ }
void SetValue(SharedPointer s, cmif::DomainObjectId new_object_id = cmif::InvalidDomainObjectId) {
*m_out = std::move(s.m_base);
if (new_object_id != cmif::InvalidDomainObjectId) {
AMS_ABORT_UNLESS(m_object_id != nullptr);
*m_object_id = new_object_id;
}
}
template requires std::derived_from
void SetValue(SharedPointer s, cmif::DomainObjectId new_object_id = cmif::InvalidDomainObjectId) {
*m_out = std::move(s.m_base);
if (new_object_id != cmif::InvalidDomainObjectId) {
AMS_ABORT_UNLESS(m_object_id != nullptr);
*m_object_id = new_object_id;
}
}
class DerefProxy {
template
friend class Out;
private:
Out &m_target;
private:
explicit DerefProxy(Out &t) : m_target(t) { /* ... */ }
public:
DerefProxy &operator=(SharedPointer p) {
m_target.SetValue(std::move(p));
return *this;
}
template requires std::derived_from
DerefProxy &operator=(SharedPointer p) {
m_target.SetValue(std::move(p));
return *this;
}
};
DerefProxy operator *() {
return DerefProxy(*this);
}
template
Out> DownCast() {
return Out>(m_out, m_object_id);
}
};
}
namespace ams::sf::impl {
/* Machinery for filtering type lists. */
template
struct TupleCat;
template
struct TupleCat, std::tuple> {
using type = std::tuple;
};
template typename Cond>
struct TupleFilter {
private:
template
struct ImplType;
template
struct ImplType> {
using type = Res;
};
template
struct ImplType> {
using type = typename std::conditional::value,
typename ImplType>::type, std::tuple>::type,
typename ImplType>::type
>::type;
};
public:
template
using FilteredType = typename ImplType, T>::type;
};
enum class ArgumentType {
InData,
OutData,
Buffer,
InHandle,
OutHandle,
InObject,
OutObject,
};
template
struct IsInObject : public std::false_type{};
template
struct IsInObject> : public std::true_type {
static_assert(std::is_base_of::value, "Invalid IsInObject>");
};
template
constexpr inline ArgumentType GetArgumentType = [] {
if constexpr (sf::IsBuffer) {
return ArgumentType::Buffer;
} else if constexpr (IsInObject::value) {
return ArgumentType::InObject;
} else if constexpr (std::is_base_of::value) {
return ArgumentType::OutObject;
} else if constexpr (std::is_base_of::value) {
return ArgumentType::InHandle;
} else if constexpr (std::is_base_of::value) {
return ArgumentType::OutHandle;
} else if constexpr (std::is_base_of::value) {
return ArgumentType::OutData;
} else if constexpr (std::is_trivial::value && !std::is_pointer::value) {
return ArgumentType::InData;
} else if constexpr (std::is_same::value) {
return ArgumentType::InData;
} else {
static_assert(!std::is_same::value, "Invalid ArgumentType");
}
}();
template
struct ArgumentTypeFilter : public std::bool_constant == ArgT>{};
template
struct TypeEqualityFilter : public std::bool_constant::value>{};
/* Argument type filters. */
template
using InDataFilter = ArgumentTypeFilter;
template
using OutDataFilter = ArgumentTypeFilter;
template
using BufferFilter = ArgumentTypeFilter;
template
using InHandleFilter = ArgumentTypeFilter;
template
using OutHandleFilter = ArgumentTypeFilter;
template
using InObjectFilter = ArgumentTypeFilter;
template
using OutObjectFilter = ArgumentTypeFilter;
template
struct ProcessIdHolderFilter : public std::bool_constant::value>{};
/* Handle kind filters. */
template
using InMoveHandleFilter = TypeEqualityFilter;
template
using InCopyHandleFilter = TypeEqualityFilter;
template
using OutMoveHandleFilter = TypeEqualityFilter>;
template
using OutCopyHandleFilter = TypeEqualityFilter>;
template
struct BufferAttributeArrayGetter;
template
struct BufferAttributeArrayGetter> {
static constexpr std::array value = { BufferAttributes..., };
};
template<>
struct BufferAttributeArrayGetter> {
static constexpr std::array value{};
};
template
struct BufferAttributeCounter {
template
static constexpr size_t GetCount(const std::array &attributes_array) {
size_t count = 0;
for (size_t i = 0; i < Size; i++) {
if (Predicate(attributes_array[i])) {
count++;
}
}
return count;
}
};
NX_CONSTEXPR size_t InHipcMapAliasBufferPredicate(const u32 attribute) {
if ((attribute & SfBufferAttr_In) && !(attribute & SfBufferAttr_Out)) {
return (attribute & SfBufferAttr_HipcMapAlias) || (attribute & SfBufferAttr_HipcAutoSelect);
} else {
return false;
}
}
NX_CONSTEXPR size_t OutHipcMapAliasBufferPredicate(const u32 attribute) {
if (!(attribute & SfBufferAttr_In) && (attribute & SfBufferAttr_Out)) {
return (attribute & SfBufferAttr_HipcMapAlias) || (attribute & SfBufferAttr_HipcAutoSelect);
} else {
return false;
}
}
NX_CONSTEXPR size_t InHipcPointerBufferPredicate(const u32 attribute) {
if ((attribute & SfBufferAttr_In) && !(attribute & SfBufferAttr_Out)) {
return (attribute & SfBufferAttr_HipcPointer) || (attribute & SfBufferAttr_HipcAutoSelect);
} else {
return false;
}
}
NX_CONSTEXPR size_t OutHipcPointerBufferPredicate(const u32 attribute) {
if (!(attribute & SfBufferAttr_In) && (attribute & SfBufferAttr_Out)) {
return (attribute & SfBufferAttr_HipcPointer) || (attribute & SfBufferAttr_HipcAutoSelect);
} else {
return false;
}
}
NX_CONSTEXPR size_t FixedSizeOutHipcPointerBufferPredicate(const u32 attribute) {
return OutHipcPointerBufferPredicate(attribute) && (attribute & SfBufferAttr_FixedSize);
}
template
struct RawDataOffsetCalculator;
template
struct RawDataOffsetCalculator> {
private:
template
struct LayoutHelper {
static_assert(GetArgumentType == ArgumentType::InData, "LayoutHelper InData");
static constexpr size_t Alignment = alignof(T);
static constexpr size_t Size = sizeof(T);
};
template
struct LayoutHelper> {
static_assert(GetArgumentType == ArgumentType::InData, "LayoutHelper OutData");
static constexpr size_t Alignment = alignof(T);
static constexpr size_t Size = sizeof(T);
};
static constexpr void StableSort(std::array &map, const std::array &values) {
/* Use insertion sort, which is stable and optimal for small numbers of parameters. */
for (size_t i = 1; i < sizeof...(Ts); i++) {
for (size_t j = i; j > 0 && values[map[j-1]] > values[map[j]]; j--) {
std::swap(map[j], map[j-1]);
}
}
}
public:
static constexpr std::array Offsets = [] {
std::array offsets{};
offsets[0] = 0;
if constexpr (sizeof...(Ts) > 0) {
/* Get size, alignment for each type. */
const std::array sizes = { LayoutHelper::Size... };
const std::array aligns = { LayoutHelper::Alignment... };
/* We want to sort...by alignment. */
std::array map = {};
for (size_t i = 0; i < sizeof...(Ts); i++) { map[i] = i; }
StableSort(map, aligns);
/* Iterate over sorted sizes. */
size_t cur_offset = 0;
for (size_t i : map) {
cur_offset = util::AlignUp(cur_offset, aligns[i]);
offsets[i] = cur_offset;
cur_offset += sizes[i];
}
offsets[sizeof...(Ts)] = cur_offset;
}
return offsets;
}();
};
struct ArgumentSerializationInfo {
/* Type used to select from below fields. */
ArgumentType arg_type;
/* Raw data indexing. */
size_t in_raw_data_index;
size_t out_raw_data_index;
/* Buffer indexing. */
size_t buffer_index;
size_t send_map_alias_index;
size_t recv_map_alias_index;
size_t send_pointer_index;
size_t recv_pointer_index;
size_t unfixed_recv_pointer_index;
size_t fixed_size;
/* Handle indexing. */
size_t in_move_handle_index;
size_t in_copy_handle_index;
size_t out_move_handle_index;
size_t out_copy_handle_index;
/* Object indexing. */
size_t in_object_index;
size_t out_object_index;
};
template
using DecayForCommandMetaArguments = typename std::conditional::type> && !std::is_base_of::type>::value, T, typename std::decay::type>::type;
template
struct CommandMetaInfo {
public:
using ArgsTypeForInvoke = std::tuple...>;
using ArgsType = std::tuple::type...>;
using InDatas = TupleFilter::FilteredType;
using OutDatas = TupleFilter::FilteredType;
using Buffers = TupleFilter::FilteredType;
using InHandles = TupleFilter::FilteredType;
using OutHandles = TupleFilter::FilteredType;
using InObjects = TupleFilter::FilteredType;
using OutObjects = TupleFilter::FilteredType;
/* Not kept separate from InDatas when processing, for reasons. */
using InProcessIdHolders = TupleFilter::FilteredType;
/* TODO: Support OutProcessIdHolders? */
static constexpr size_t NumInDatas = std::tuple_size::value;
static constexpr size_t NumOutDatas = std::tuple_size::value;
static constexpr size_t NumBuffers = std::tuple_size::value;
static constexpr size_t NumInHandles = std::tuple_size::value;
static constexpr size_t NumOutHandles = std::tuple_size::value;
static constexpr size_t NumInObjects = std::tuple_size::value;
static constexpr size_t NumOutObjects = std::tuple_size::value;
static constexpr size_t NumInProcessIdHolders = std::tuple_size::value;
static constexpr bool HasInProcessIdHolder = NumInProcessIdHolders >= 1;
template
static constexpr bool IsInProcessIdHolder = GetArgumentType == ArgumentType::InData && std::is_base_of::value;
template
static constexpr bool IsInProcessIdHolderIndex = I < std::tuple_size::value && IsInProcessIdHolder::type>;
static_assert(NumBuffers <= 8, "Methods must take in <= 8 Buffers");
static_assert(NumInHandles <= 8, "Methods must take in <= 8 Handles");
static_assert(NumOutHandles + NumOutObjects <= 8, "Methods must output <= 8 Handles");
/* Buffer marshalling. */
static constexpr std::array BufferAttributes = BufferAttributeArrayGetter::value;
static constexpr size_t NumInHipcMapAliasBuffers = BufferAttributeCounter::GetCount(BufferAttributes);
static constexpr size_t NumOutHipcMapAliasBuffers = BufferAttributeCounter::GetCount(BufferAttributes);
static constexpr size_t NumInHipcPointerBuffers = BufferAttributeCounter::GetCount(BufferAttributes);
static constexpr size_t NumOutHipcPointerBuffers = BufferAttributeCounter::GetCount(BufferAttributes);
static constexpr size_t NumFixedSizeOutHipcPointerBuffers = BufferAttributeCounter::GetCount(BufferAttributes);
static constexpr size_t NumUnfixedSizeOutHipcPointerBuffers = NumOutHipcPointerBuffers - NumFixedSizeOutHipcPointerBuffers;
/* In/Out data marshalling. */
static constexpr std::array InDataOffsets = RawDataOffsetCalculator::Offsets;
static constexpr size_t InDataSize = util::AlignUp(InDataOffsets[NumInDatas], alignof(u16));
static constexpr std::array OutDataOffsets = RawDataOffsetCalculator::Offsets;
static constexpr size_t OutDataSize = util::AlignUp(OutDataOffsets[NumOutDatas], alignof(u32));
static constexpr size_t OutDataAlign = [] {
if constexpr (std::tuple_size::value) {
return alignof(typename std::tuple_element<0, OutDatas>::type);
}
return size_t();
}();
/* Handle marshalling. */
static constexpr size_t NumInMoveHandles = std::tuple_size::FilteredType>::value;
static constexpr size_t NumInCopyHandles = std::tuple_size::FilteredType>::value;
static constexpr size_t NumOutMoveHandles = std::tuple_size::FilteredType>::value;
static constexpr size_t NumOutCopyHandles = std::tuple_size::FilteredType>::value;
static_assert(NumInMoveHandles + NumInCopyHandles == NumInHandles, "NumInMoveHandles + NumInCopyHandles == NumInHandles");
static_assert(NumOutMoveHandles + NumOutCopyHandles == NumOutHandles, "NumOutMoveHandles + NumOutCopyHandles == NumOutHandles");
/* Used by server message processor at runtime. */
static constexpr inline const cmif::ServerMessageRuntimeMetadata RuntimeMetadata = cmif::ServerMessageRuntimeMetadata{
.in_data_size = InDataSize,
.out_data_size = OutDataSize,
.in_headers_size = sizeof(CmifInHeader),
.out_headers_size = sizeof(CmifOutHeader),
.in_object_count = NumInObjects,
.out_object_count = NumOutObjects,
};
/* Construction of argument serialization structs. */
private:
template
struct ArgumentSerializationInfoConstructor;
template
struct ArgumentSerializationInfoConstructor> {
template
static constexpr ArgumentSerializationInfo ProcessUpdate(ArgumentSerializationInfo ¤t_info) {
/* Save a copy of the current state to return. */
ArgumentSerializationInfo returned_info = current_info;
/* Clear previous iteration's fixed size. */
returned_info.fixed_size = 0;
current_info.fixed_size = 0;
constexpr auto arg_type = GetArgumentType;
returned_info.arg_type = arg_type;
if constexpr (arg_type == ArgumentType::InData) {
/* New rawdata, so increment index. */
current_info.in_raw_data_index++;
} else if constexpr (arg_type == ArgumentType::OutData) {
/* New rawdata, so increment index. */
current_info.out_raw_data_index++;
} else if constexpr (arg_type == ArgumentType::InHandle) {
/* New InHandle, increment the appropriate index. */
if constexpr (std::is_same::value) {
current_info.in_move_handle_index++;
} else if constexpr (std::is_same::value) {
current_info.in_copy_handle_index++;
} else {
static_assert(!std::is_same::value, "Invalid InHandle kind");
}
} else if constexpr (arg_type == ArgumentType::OutHandle) {
/* New OutHandle, increment the appropriate index. */
if constexpr (std::is_same>::value) {
current_info.out_move_handle_index++;
} else if constexpr (std::is_same>::value) {
current_info.out_copy_handle_index++;
} else {
static_assert(!std::is_same::value, "Invalid OutHandle kind");
}
} else if constexpr (arg_type == ArgumentType::InObject) {
/* New InObject, increment the appropriate index. */
current_info.in_object_index++;
} else if constexpr (arg_type == ArgumentType::OutObject) {
/* New OutObject, increment the appropriate index. */
current_info.out_object_index++;
} else if constexpr (arg_type == ArgumentType::Buffer) {
/* New Buffer, increment the appropriate index. */
const auto attributes = BufferAttributes[current_info.buffer_index];
current_info.buffer_index++;
if (attributes & SfBufferAttr_HipcMapAlias) {
if (attributes & SfBufferAttr_In) {
current_info.send_map_alias_index++;
} else if (attributes & SfBufferAttr_Out) {
current_info.recv_map_alias_index++;
}
} else if (attributes & SfBufferAttr_HipcPointer) {
if (attributes & SfBufferAttr_In) {
current_info.send_pointer_index++;
} else if (attributes & SfBufferAttr_Out) {
current_info.recv_pointer_index++;
if (!(attributes & SfBufferAttr_FixedSize)) {
current_info.unfixed_recv_pointer_index++;
} else {
returned_info.fixed_size = LargeDataSize;
}
}
} else if (attributes & SfBufferAttr_HipcAutoSelect) {
if (attributes & SfBufferAttr_In) {
current_info.send_map_alias_index++;
current_info.send_pointer_index++;
} else if (attributes & SfBufferAttr_Out) {
current_info.recv_map_alias_index++;
current_info.recv_pointer_index++;
if (!(attributes & SfBufferAttr_FixedSize)) {
current_info.unfixed_recv_pointer_index++;
} else {
returned_info.fixed_size = LargeDataSize;
}
}
}
} else {
static_assert(!std::is_same::value, "Invalid ArgumentType");
}
return returned_info;
}
static constexpr std::array ArgumentSerializationInfos = [] {
ArgumentSerializationInfo current_info = {};
return std::array{ ProcessUpdate(current_info)... };
}();
};
public:
static constexpr std::array::value> ArgumentSerializationInfos = ArgumentSerializationInfoConstructor::ArgumentSerializationInfos;
};
template
class OutRawHolder {
public:
static constexpr size_t Size = _Size;
static constexpr size_t Align = _Align ? _Align : alignof(u8);
private:
alignas(Align) u8 data[Size];
public:
constexpr OutRawHolder() : data() { /* ... */ }
template
constexpr inline uintptr_t GetAddress() const {
static_assert(Offset <= Size, "Offset <= Size");
static_assert(TypeSize <= Size, "TypeSize <= Size");
static_assert(Offset + TypeSize <= Size, "Offset + TypeSize <= Size");
return reinterpret_cast(&data[Offset]);
}
constexpr inline void CopyTo(void *dst) const {
if constexpr (Size > 0) {
std::memcpy(dst, data, Size);
}
}
};
template
class OutHandleHolder {
public:
static constexpr size_t NumMove = _NumMove;
static constexpr size_t NumCopy = _NumCopy;
private:
MoveHandle move_handles[NumMove];
CopyHandle copy_handles[NumCopy];
bool copy_managed[NumCopy];
public:
constexpr OutHandleHolder() : move_handles(), copy_handles(), copy_managed() { /* ... */ }
template
constexpr inline MoveHandle *GetMoveHandlePointer() {
static_assert(Index < NumMove, "Index < NumMove");
return &move_handles[Index];
}
template
constexpr inline CopyHandle *GetCopyHandlePointer() {
static_assert(Index < NumCopy, "Index < NumCopy");
return ©_handles[Index];
}
template
constexpr inline bool *GetCopyHandleManagedPointer() {
static_assert(Index < NumCopy, "Index < NumCopy");
return ©_managed[Index];
}
constexpr inline void CopyTo(const cmif::ServiceDispatchContext &ctx, const HipcRequest &response, const size_t num_out_object_handles) {
ctx.handles_to_close->num_handles = 0;
#define _SF_OUT_HANDLE_HOLDER_WRITE_COPY_HANDLE(n) do { if constexpr (NumCopy > n) { const auto handle = copy_handles[n].GetValue(); response.copy_handles[n] = handle; if (copy_managed[n]) { ctx.handles_to_close->handles[ctx.handles_to_close->num_handles++] = handle; } } } while (0)
_SF_OUT_HANDLE_HOLDER_WRITE_COPY_HANDLE(0);
_SF_OUT_HANDLE_HOLDER_WRITE_COPY_HANDLE(1);
_SF_OUT_HANDLE_HOLDER_WRITE_COPY_HANDLE(2);
_SF_OUT_HANDLE_HOLDER_WRITE_COPY_HANDLE(3);
_SF_OUT_HANDLE_HOLDER_WRITE_COPY_HANDLE(4);
_SF_OUT_HANDLE_HOLDER_WRITE_COPY_HANDLE(5);
_SF_OUT_HANDLE_HOLDER_WRITE_COPY_HANDLE(6);
_SF_OUT_HANDLE_HOLDER_WRITE_COPY_HANDLE(7);
#undef _SF_OUT_HANDLE_HOLDER_WRITE_COPY_HANDLE
#define _SF_OUT_HANDLE_HOLDER_WRITE_MOVE_HANDLE(n) do { if constexpr (NumMove > n) { response.move_handles[n + num_out_object_handles] = move_handles[n].GetValue(); } } while (0)
_SF_OUT_HANDLE_HOLDER_WRITE_MOVE_HANDLE(0);
_SF_OUT_HANDLE_HOLDER_WRITE_MOVE_HANDLE(1);
_SF_OUT_HANDLE_HOLDER_WRITE_MOVE_HANDLE(2);
_SF_OUT_HANDLE_HOLDER_WRITE_MOVE_HANDLE(3);
_SF_OUT_HANDLE_HOLDER_WRITE_MOVE_HANDLE(4);
_SF_OUT_HANDLE_HOLDER_WRITE_MOVE_HANDLE(5);
_SF_OUT_HANDLE_HOLDER_WRITE_MOVE_HANDLE(6);
_SF_OUT_HANDLE_HOLDER_WRITE_MOVE_HANDLE(7);
#undef _SF_OUT_HANDLE_HOLDER_WRITE_MOVE_HANDLE
}
};
template
class InOutObjectHolder {
private:
std::array in_object_holders;
std::array out_object_holders;
std::array>, NumOutObjects> out_shared_pointers;
std::array out_object_ids;
public:
constexpr InOutObjectHolder() : in_object_holders(), out_object_holders() {
#define _SF_IN_OUT_HOLDER_INITIALIZE_OBJECT_ID(n) if constexpr (NumOutObjects > n) { this->out_object_ids[n] = cmif::InvalidDomainObjectId; }
_SF_IN_OUT_HOLDER_INITIALIZE_OBJECT_ID(0)
_SF_IN_OUT_HOLDER_INITIALIZE_OBJECT_ID(1)
_SF_IN_OUT_HOLDER_INITIALIZE_OBJECT_ID(2)
_SF_IN_OUT_HOLDER_INITIALIZE_OBJECT_ID(3)
_SF_IN_OUT_HOLDER_INITIALIZE_OBJECT_ID(4)
_SF_IN_OUT_HOLDER_INITIALIZE_OBJECT_ID(5)
_SF_IN_OUT_HOLDER_INITIALIZE_OBJECT_ID(6)
_SF_IN_OUT_HOLDER_INITIALIZE_OBJECT_ID(7)
#undef _SF_IN_OUT_HOLDER_INITIALIZE_OBJECT_ID
}
Result GetInObjects(const sf::cmif::ServerMessageProcessor *processor) {
if constexpr (NumInObjects > 0) {
R_TRY(processor->GetInObjects(this->in_object_holders.data()));
}
return ResultSuccess();
}
template
constexpr inline Result ValidateInObjects() const {
static_assert(std::tuple_size::value == NumInObjects);
#define _SF_IN_OUT_HOLDER_VALIDATE_IN_OBJECT(n) do { \
if constexpr (NumInObjects > n) { \
using SharedPointerType = typename std::tuple_element::type; \
using ServiceImplType = typename SharedPointerType::Interface; \
R_UNLESS((this->in_object_holders[n].template IsServiceObjectValid()), sf::cmif::ResultInvalidInObject()); \
} \
} while (0)
_SF_IN_OUT_HOLDER_VALIDATE_IN_OBJECT(0);
_SF_IN_OUT_HOLDER_VALIDATE_IN_OBJECT(1);
_SF_IN_OUT_HOLDER_VALIDATE_IN_OBJECT(2);
_SF_IN_OUT_HOLDER_VALIDATE_IN_OBJECT(3);
_SF_IN_OUT_HOLDER_VALIDATE_IN_OBJECT(4);
_SF_IN_OUT_HOLDER_VALIDATE_IN_OBJECT(5);
_SF_IN_OUT_HOLDER_VALIDATE_IN_OBJECT(6);
_SF_IN_OUT_HOLDER_VALIDATE_IN_OBJECT(7);
#undef _SF_IN_OUT_HOLDER_VALIDATE_IN_OBJECT
return ResultSuccess();
}
template
SharedPointer *GetOutObjectSharedPointer() {
static_assert(sizeof(SharedPointer) == sizeof(SharedPointer));
return static_cast *>(static_cast(GetPointer(out_shared_pointers[Index])));
}
template
Out> GetOutObject() {
auto sp = std::construct_at(GetOutObjectSharedPointer());
return Out>(sp, &this->out_object_ids[Index]);
}
template
void SetOutObject() {
this->out_object_holders[Index] = cmif::ServiceObjectHolder(std::move(*GetOutObjectSharedPointer()));
}
constexpr void SetOutObjects(const cmif::ServiceDispatchContext &ctx, const HipcRequest &response) {
if constexpr (NumOutObjects > 0) {
ctx.processor->SetOutObjects(ctx, response, this->out_object_holders.data(), this->out_object_ids.data());
}
}
};
class HipcCommandProcessorCommon : public sf::cmif::ServerMessageProcessor {
public:
virtual void SetImplementationProcessor(sf::cmif::ServerMessageProcessor *) override final { /* ... */ }
virtual void PrepareForErrorReply(const cmif::ServiceDispatchContext &ctx, cmif::PointerAndSize &out_raw_data, const cmif::ServerMessageRuntimeMetadata runtime_metadata) override final {
const size_t raw_size = runtime_metadata.GetOutHeadersSize();
const auto response = hipcMakeRequestInline(ctx.out_message_buffer.GetPointer(),
.type = CmifCommandType_Invalid, /* Really response */
.num_data_words = static_cast((util::AlignUp(raw_size, 0x4) + 0x10 /* padding */) / sizeof(u32)),
);
out_raw_data = cmif::PointerAndSize(util::AlignUp(reinterpret_cast(response.data_words), 0x10), raw_size);
}
virtual Result GetInObjects(cmif::ServiceObjectHolder *in_objects) const override final {
/* By default, InObjects aren't supported. */
return sf::ResultNotSupported();
}
};
template
struct HipcCommandProcessor : public HipcCommandProcessorCommon {
public:
virtual const cmif::ServerMessageRuntimeMetadata GetRuntimeMetadata() const override final {
return CommandMeta::RuntimeMetadata;
}
virtual Result PrepareForProcess(const cmif::ServiceDispatchContext &ctx, const cmif::ServerMessageRuntimeMetadata runtime_metadata) const override final {
const auto &meta = ctx.request.meta;
bool is_request_valid = true;
is_request_valid &= meta.send_pid == CommandMeta::HasInProcessIdHolder;
is_request_valid &= meta.num_send_statics == CommandMeta::NumInHipcPointerBuffers;
/* is_request_valid &= meta.num_recv_statics == CommandMeta::NumOutHipcPointerBuffers; */
is_request_valid &= meta.num_send_buffers == CommandMeta::NumInHipcMapAliasBuffers;
is_request_valid &= meta.num_recv_buffers == CommandMeta::NumOutHipcMapAliasBuffers;
is_request_valid &= meta.num_exch_buffers == 0; /* Exchange buffers aren't supported. */
is_request_valid &= meta.num_copy_handles == CommandMeta::NumInCopyHandles;
is_request_valid &= meta.num_move_handles == CommandMeta::NumInMoveHandles;
const size_t meta_raw_size = meta.num_data_words * sizeof(u32);
const size_t command_raw_size = util::AlignUp(runtime_metadata.GetUnfixedOutPointerSizeOffset() + (CommandMeta::NumUnfixedSizeOutHipcPointerBuffers * sizeof(u16)), alignof(u32));
is_request_valid &= meta_raw_size >= command_raw_size;
R_UNLESS(is_request_valid, sf::hipc::ResultInvalidCmifRequest());
return ResultSuccess();
}
virtual HipcRequest PrepareForReply(const cmif::ServiceDispatchContext &ctx, cmif::PointerAndSize &out_raw_data, const cmif::ServerMessageRuntimeMetadata runtime_metadata) override final {
const size_t raw_size = runtime_metadata.GetOutDataSize() + runtime_metadata.GetOutHeadersSize();
const auto response = hipcMakeRequestInline(ctx.out_message_buffer.GetPointer(),
.type = CmifCommandType_Invalid, /* Really response */
.num_send_statics = CommandMeta::NumOutHipcPointerBuffers,
.num_data_words = static_cast((util::AlignUp(raw_size, 0x4) + 0x10 /* padding */) / sizeof(u32)),
.num_copy_handles = CommandMeta::NumOutCopyHandles,
.num_move_handles = static_cast(CommandMeta::NumOutMoveHandles + runtime_metadata.GetOutObjectCount()),
);
out_raw_data = cmif::PointerAndSize(util::AlignUp(reinterpret_cast(response.data_words), 0x10), raw_size);
return response;
}
virtual void SetOutObjects(const cmif::ServiceDispatchContext &ctx, const HipcRequest &response, cmif::ServiceObjectHolder *out_objects, cmif::DomainObjectId *ids) override final {
#define _SF_IMPL_PROCESSOR_SET_OUT_OBJECT_IMPL(n) do { if constexpr (CommandMeta::NumOutObjects > n) { SetOutObjectImpl(response, ctx.manager, std::move(out_objects[n])); } } while (0)
_SF_IMPL_PROCESSOR_SET_OUT_OBJECT_IMPL(0);
_SF_IMPL_PROCESSOR_SET_OUT_OBJECT_IMPL(1);
_SF_IMPL_PROCESSOR_SET_OUT_OBJECT_IMPL(2);
_SF_IMPL_PROCESSOR_SET_OUT_OBJECT_IMPL(3);
_SF_IMPL_PROCESSOR_SET_OUT_OBJECT_IMPL(4);
_SF_IMPL_PROCESSOR_SET_OUT_OBJECT_IMPL(5);
_SF_IMPL_PROCESSOR_SET_OUT_OBJECT_IMPL(6);
_SF_IMPL_PROCESSOR_SET_OUT_OBJECT_IMPL(7);
#undef _SF_IMPL_PROCESSOR_SET_OUT_OBJECT_IMPL
}
/* Useful defines. */
using ArgsTypeForInvoke = typename CommandMeta::ArgsTypeForInvoke;
using ArgsType = typename CommandMeta::ArgsType;
using BufferArrayType = std::array;
using OutRawHolderType = OutRawHolder;
using OutHandleHolderType = OutHandleHolder;
using InOutObjectHolderType = InOutObjectHolder;
/* Buffer processing. */
private:
template
NX_CONSTEXPR void SetOutObjectImpl(const HipcRequest &response, hipc::ServerSessionManager *manager, cmif::ServiceObjectHolder &&object) {
/* If no object, write INVALID_HANDLE. This is what official software does. */
if (!object) {
response.move_handles[Index] = INVALID_HANDLE;
return;
}
Handle server_handle, client_handle;
R_ABORT_UNLESS(sf::hipc::CreateSession(&server_handle, &client_handle));
R_ABORT_UNLESS(manager->RegisterSession(server_handle, std::move(object)));
response.move_handles[Index] = client_handle;
}
template
NX_CONSTEXPR bool IsMapTransferModeValid(u32 mode) {
static_assert(!((Attributes & SfBufferAttr_HipcMapTransferAllowsNonSecure) && (Attributes & SfBufferAttr_HipcMapTransferAllowsNonDevice)), "Invalid Attributes");
if constexpr (Attributes & SfBufferAttr_HipcMapTransferAllowsNonSecure) {
return mode == HipcBufferMode_NonSecure;
} else if constexpr (Attributes & SfBufferAttr_HipcMapTransferAllowsNonDevice) {
return mode == HipcBufferMode_NonDevice;
} else {
return mode == HipcBufferMode_Normal;
}
}
template
static constexpr inline size_t GetIndexFromBufferIndex = [] {
for (size_t i = 0; i < CommandMeta::ArgumentSerializationInfos.size(); i++) {
const auto Info = CommandMeta::ArgumentSerializationInfos[i];
if (Info.arg_type == ArgumentType::Buffer && Info.buffer_index == BufferIndex) {
return i;
}
}
return std::numeric_limits::max();
}();
template>
NX_CONSTEXPR void ProcessBufferImpl(const cmif::ServiceDispatchContext &ctx, cmif::PointerAndSize &buffer, bool &is_buffer_map_alias, bool &map_alias_buffers_valid, size_t &pointer_buffer_head, size_t &pointer_buffer_tail, const cmif::ServerMessageRuntimeMetadata runtime_metadata) {
static_assert(Index != std::numeric_limits::max(), "Invalid Index From Buffer Index");
constexpr auto Info = CommandMeta::ArgumentSerializationInfos[Index];
constexpr auto Attributes = CommandMeta::BufferAttributes[BufferIndex];
static_assert(BufferIndex == Info.buffer_index && Info.arg_type == ArgumentType::Buffer, "BufferIndex == Info.buffer_index && Info.arg_type == ArgumentType::Buffer");
if constexpr (Attributes & SfBufferAttr_HipcMapAlias) {
is_buffer_map_alias = true;
if constexpr (Attributes & SfBufferAttr_In) {
const HipcBufferDescriptor *desc = &ctx.request.data.send_buffers[Info.send_map_alias_index];
buffer = cmif::PointerAndSize(hipcGetBufferAddress(desc), hipcGetBufferSize(desc));
if (!IsMapTransferModeValid(static_cast(desc->mode))) { map_alias_buffers_valid = false; }
} else if constexpr (Attributes & SfBufferAttr_Out) {
const HipcBufferDescriptor *desc = &ctx.request.data.recv_buffers[Info.recv_map_alias_index];
buffer = cmif::PointerAndSize(hipcGetBufferAddress(desc), hipcGetBufferSize(desc));
if (!IsMapTransferModeValid(static_cast(desc->mode))) { map_alias_buffers_valid = false; }
} else {
static_assert(Attributes != Attributes, "Invalid Buffer Attributes");
}
} else if constexpr (Attributes & SfBufferAttr_HipcPointer) {
is_buffer_map_alias = false;
if constexpr (Attributes & SfBufferAttr_In) {
const HipcStaticDescriptor *desc = &ctx.request.data.send_statics[Info.send_pointer_index];
buffer = cmif::PointerAndSize(hipcGetStaticAddress(desc), hipcGetStaticSize(desc));
const size_t size = buffer.GetSize();
if (size) {
pointer_buffer_tail = std::max(pointer_buffer_tail, buffer.GetAddress() + size);
}
} else if constexpr (Attributes & SfBufferAttr_Out) {
if constexpr (Attributes & SfBufferAttr_FixedSize) {
constexpr size_t size = Info.fixed_size;
static_assert(size > 0, "FixedSize object must have non-zero size!");
pointer_buffer_head = util::AlignDown(pointer_buffer_head - size, 0x10);
buffer = cmif::PointerAndSize(pointer_buffer_head, size);
} else {
const u16 *recv_pointer_sizes = reinterpret_cast(reinterpret_cast(ctx.request.data.data_words) + runtime_metadata.GetUnfixedOutPointerSizeOffset());
const size_t size = size_t(recv_pointer_sizes[Info.unfixed_recv_pointer_index]);
pointer_buffer_head = util::AlignDown(pointer_buffer_head - size, 0x10);
buffer = cmif::PointerAndSize(pointer_buffer_head, size);
}
} else {
static_assert(Attributes != Attributes, "Invalid Buffer Attributes");
}
} else if constexpr (Attributes & SfBufferAttr_HipcAutoSelect) {
if constexpr (Attributes & SfBufferAttr_In) {
const HipcBufferDescriptor *map_desc = &ctx.request.data.send_buffers[Info.send_map_alias_index];
const HipcStaticDescriptor *ptr_desc = &ctx.request.data.send_statics[Info.send_pointer_index];
is_buffer_map_alias = hipcGetBufferAddress(map_desc) != 0;
if (is_buffer_map_alias) {
buffer = cmif::PointerAndSize(hipcGetBufferAddress(map_desc), hipcGetBufferSize(map_desc));
if (!IsMapTransferModeValid(static_cast(map_desc->mode))) { map_alias_buffers_valid = false; }
} else {
buffer = cmif::PointerAndSize(hipcGetStaticAddress(ptr_desc), hipcGetStaticSize(ptr_desc));
const size_t size = buffer.GetSize();
if (size) {
pointer_buffer_tail = std::max(pointer_buffer_tail, buffer.GetAddress() + size);
}
}
} else if constexpr (Attributes & SfBufferAttr_Out) {
const HipcBufferDescriptor *map_desc = &ctx.request.data.recv_buffers[Info.recv_map_alias_index];
is_buffer_map_alias = hipcGetBufferAddress(map_desc) != 0;
if (is_buffer_map_alias) {
buffer = cmif::PointerAndSize(hipcGetBufferAddress(map_desc), hipcGetBufferSize(map_desc));
if (!IsMapTransferModeValid(static_cast(map_desc->mode))) { map_alias_buffers_valid = false; }
} else {
if constexpr (Attributes & SfBufferAttr_FixedSize) {
constexpr size_t size = Info.fixed_size;
static_assert(size > 0, "FixedSize object must have non-zero size!");
pointer_buffer_head = util::AlignDown(pointer_buffer_head - size, 0x10);
buffer = cmif::PointerAndSize(pointer_buffer_head, size);
} else {
const u16 *recv_pointer_sizes = reinterpret_cast(reinterpret_cast(ctx.request.data.data_words) + runtime_metadata.GetUnfixedOutPointerSizeOffset());
const size_t size = size_t(recv_pointer_sizes[Info.unfixed_recv_pointer_index]);
pointer_buffer_head = util::AlignDown(pointer_buffer_head - size, 0x10);
buffer = cmif::PointerAndSize(pointer_buffer_head, size);
}
}
} else {
static_assert(Attributes != Attributes, "Invalid Buffer Attributes");
}
} else {
static_assert(Attributes != Attributes, "Invalid Buffer Attributes");
}
}
template>
NX_CONSTEXPR void SetOutBufferImpl(const HipcRequest &response, const cmif::PointerAndSize &buffer, const bool is_buffer_map_alias) {
static_assert(Index != std::numeric_limits::max(), "Invalid Index From Buffer Index");
constexpr auto Info = CommandMeta::ArgumentSerializationInfos[Index];
constexpr auto Attributes = CommandMeta::BufferAttributes[BufferIndex];
static_assert(BufferIndex == Info.buffer_index && Info.arg_type == ArgumentType::Buffer, "BufferIndex == Info.buffer_index && Info.arg_type == ArgumentType::Buffer");
if constexpr (Attributes & SfBufferAttr_Out) {
if constexpr (Attributes & SfBufferAttr_HipcPointer) {
response.send_statics[Info.recv_pointer_index] = hipcMakeSendStatic(buffer.GetPointer(), buffer.GetSize(), Info.recv_pointer_index);
} else if constexpr (Attributes & SfBufferAttr_HipcAutoSelect) {
if (!is_buffer_map_alias) {
response.send_statics[Info.recv_pointer_index] = hipcMakeSendStatic(buffer.GetPointer(), buffer.GetSize(), Info.recv_pointer_index);
} else {
response.send_statics[Info.recv_pointer_index] = hipcMakeSendStatic(nullptr, 0, Info.recv_pointer_index);
}
}
}
}
public:
NX_CONSTEXPR Result ProcessBuffers(const cmif::ServiceDispatchContext &ctx, BufferArrayType &buffers, std::array &is_buffer_map_alias, const cmif::ServerMessageRuntimeMetadata runtime_metadata) {
bool map_alias_buffers_valid = true;
size_t pointer_buffer_tail = ctx.pointer_buffer.GetAddress();
size_t pointer_buffer_head = pointer_buffer_tail + ctx.pointer_buffer.GetSize();
#define _SF_IMPL_PROCESSOR_PROCESS_BUFFER_IMPL(n) do { if constexpr (CommandMeta::NumBuffers > n) { ProcessBufferImpl(ctx, buffers[n], is_buffer_map_alias[n], map_alias_buffers_valid, pointer_buffer_head, pointer_buffer_tail, runtime_metadata); } } while (0)
_SF_IMPL_PROCESSOR_PROCESS_BUFFER_IMPL(0);
_SF_IMPL_PROCESSOR_PROCESS_BUFFER_IMPL(1);
_SF_IMPL_PROCESSOR_PROCESS_BUFFER_IMPL(2);
_SF_IMPL_PROCESSOR_PROCESS_BUFFER_IMPL(3);
_SF_IMPL_PROCESSOR_PROCESS_BUFFER_IMPL(4);
_SF_IMPL_PROCESSOR_PROCESS_BUFFER_IMPL(5);
_SF_IMPL_PROCESSOR_PROCESS_BUFFER_IMPL(6);
_SF_IMPL_PROCESSOR_PROCESS_BUFFER_IMPL(7);
#undef _SF_IMPL_PROCESSOR_PROCESS_BUFFER_IMPL
R_UNLESS(map_alias_buffers_valid, sf::hipc::ResultInvalidCmifRequest());
if constexpr (CommandMeta::NumOutHipcPointerBuffers > 0) {
R_UNLESS(pointer_buffer_tail <= pointer_buffer_head, sf::hipc::ResultPointerBufferTooSmall());
}
return ResultSuccess();
}
NX_CONSTEXPR void SetOutBuffers(const HipcRequest &response, const BufferArrayType &buffers, const std::array &is_buffer_map_alias) {
#define _SF_IMPL_PROCESSOR_SET_OUT_BUFFER_IMPL(n) do { if constexpr (CommandMeta::NumBuffers > n) { SetOutBufferImpl