Atmosphere-libs/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_service_dispatch.hpp

178 lines
7.5 KiB
C++

/*
* Copyright (c) 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere/sf/sf_mitm_config.hpp>
#include <stratosphere/sf/sf_service_object.hpp>
#include <stratosphere/sf/cmif/sf_cmif_pointer_and_size.hpp>
#include <stratosphere/sf/cmif/sf_cmif_server_message_processor.hpp>
namespace ams::sf::hipc {
class ServerSessionManager;
class ServerSession;
}
namespace ams::sf::cmif {
class ServerMessageProcessor;
struct HandlesToClose {
os::NativeHandle handles[8];
size_t num_handles;
};
struct ServiceDispatchContext {
sf::IServiceObject *srv_obj;
hipc::ServerSessionManager *manager;
hipc::ServerSession *session;
ServerMessageProcessor *processor;
HandlesToClose *handles_to_close;
const PointerAndSize pointer_buffer;
const PointerAndSize in_message_buffer;
const PointerAndSize out_message_buffer;
const HipcParsedRequest request;
};
struct ServiceCommandMeta {
hos::Version hosver_low;
hos::Version hosver_high;
u32 cmd_id;
Result (*handler)(CmifOutHeader **out_header_ptr, ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data);
constexpr inline bool MatchesVersion(hos::Version hosver) const {
const bool min_valid = this->hosver_low == hos::Version_Min;
const bool max_valid = this->hosver_high == hos::Version_Max;
return (min_valid || this->hosver_low <= hosver) && (max_valid || hosver <= this->hosver_high);
}
constexpr inline bool Matches(u32 cmd_id, hos::Version hosver) const {
return this->cmd_id == cmd_id && this->MatchesVersion(hosver);
}
constexpr inline decltype(handler) GetHandler() const {
return this->handler;
}
constexpr inline bool operator>(const ServiceCommandMeta &rhs) const {
if (this->cmd_id > rhs.cmd_id) {
return true;
} else if (this->cmd_id == rhs.cmd_id && this->hosver_low > rhs.hosver_low) {
return true;
} else if (this->cmd_id == rhs.cmd_id && this->hosver_low == rhs.hosver_low && this->hosver_high == rhs.hosver_high){
return true;
} else {
return false;
}
}
};
static_assert(util::is_pod<ServiceCommandMeta>::value && sizeof(ServiceCommandMeta) == 0x18, "sizeof(ServiceCommandMeta)");
namespace impl {
class ServiceDispatchTableBase {
protected:
Result ProcessMessageImpl(ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data, const ServiceCommandMeta *entries, const size_t entry_count, u32 interface_id_for_debug) const;
Result ProcessMessageForMitmImpl(ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data, const ServiceCommandMeta *entries, const size_t entry_count, u32 interface_id_for_debug) const;
public:
/* CRTP. */
template<typename T>
Result ProcessMessage(ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data) const {
static_assert(std::is_base_of<ServiceDispatchTableBase, T>::value, "ServiceDispatchTableBase::Process<T>");
R_RETURN(static_cast<const T *>(this)->ProcessMessage(ctx, in_raw_data));
}
template<typename T>
Result ProcessMessageForMitm(ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data) const {
static_assert(std::is_base_of<ServiceDispatchTableBase, T>::value, "ServiceDispatchTableBase::ProcessForMitm<T>");
R_RETURN(static_cast<const T *>(this)->ProcessMessageForMitm(ctx, in_raw_data));
}
};
template<u32 InterfaceIdForDebug, size_t N>
class ServiceDispatchTableImpl : public ServiceDispatchTableBase {
public:
static constexpr size_t NumEntries = N;
private:
const std::array<ServiceCommandMeta, N> m_entries;
public:
explicit constexpr ServiceDispatchTableImpl(const std::array<ServiceCommandMeta, N> &e) : m_entries{e} { /* ... */ }
Result ProcessMessage(ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data) const {
R_RETURN(this->ProcessMessageImpl(ctx, in_raw_data, m_entries.data(), m_entries.size(), InterfaceIdForDebug));
}
Result ProcessMessageForMitm(ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data) const {
R_RETURN(this->ProcessMessageForMitmImpl(ctx, in_raw_data, m_entries.data(), m_entries.size(), InterfaceIdForDebug));
}
constexpr const std::array<ServiceCommandMeta, N> &GetEntries() const {
return m_entries;
}
};
}
template<u32 InterfaceIdForDebug, size_t N>
class ServiceDispatchTable : public impl::ServiceDispatchTableImpl<InterfaceIdForDebug, N> {
public:
explicit constexpr ServiceDispatchTable(const std::array<ServiceCommandMeta, N> &e) : impl::ServiceDispatchTableImpl<InterfaceIdForDebug, N>(e) { /* ... */ }
};
struct ServiceDispatchMeta {
const impl::ServiceDispatchTableBase *DispatchTable;
Result (impl::ServiceDispatchTableBase::*ProcessHandler)(ServiceDispatchContext &, const cmif::PointerAndSize &) const;
uintptr_t GetServiceId() const {
return reinterpret_cast<uintptr_t>(this->DispatchTable);
}
};
template<typename T> requires sf::IsServiceObject<T>
struct ServiceDispatchTraits {
using ProcessHandlerType = decltype(ServiceDispatchMeta::ProcessHandler);
static constexpr inline auto DispatchTable = T::template s_CmifServiceDispatchTable<T>;
using DispatchTableType = decltype(DispatchTable);
static constexpr ProcessHandlerType ProcessHandlerImpl = sf::IsMitmServiceObject<T> ? (&impl::ServiceDispatchTableBase::ProcessMessageForMitm<DispatchTableType>)
: (&impl::ServiceDispatchTableBase::ProcessMessage<DispatchTableType>);
static constexpr inline ServiceDispatchMeta Meta{std::addressof(DispatchTable), ProcessHandlerImpl};
};
template<>
struct ServiceDispatchTraits<sf::IServiceObject> {
static constexpr inline auto DispatchTable = ServiceDispatchTable<0, 0>(std::array<ServiceCommandMeta, 0>{});
};
#if AMS_SF_MITM_SUPPORTED
template<>
struct ServiceDispatchTraits<sf::IMitmServiceObject> {
static constexpr inline auto DispatchTable = ServiceDispatchTable<0, 0>(std::array<ServiceCommandMeta, 0>{});
};
#endif
template<typename T>
constexpr ALWAYS_INLINE const ServiceDispatchMeta *GetServiceDispatchMeta() {
return std::addressof(ServiceDispatchTraits<T>::Meta);
}
}