/* * 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 . */ #pragma once #include #include #include #include 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::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 Result ProcessMessage(ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data) const { static_assert(std::is_base_of::value, "ServiceDispatchTableBase::Process"); R_RETURN(static_cast(this)->ProcessMessage(ctx, in_raw_data)); } template Result ProcessMessageForMitm(ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data) const { static_assert(std::is_base_of::value, "ServiceDispatchTableBase::ProcessForMitm"); R_RETURN(static_cast(this)->ProcessMessageForMitm(ctx, in_raw_data)); } }; template class ServiceDispatchTableImpl : public ServiceDispatchTableBase { public: static constexpr size_t NumEntries = N; private: const std::array m_entries; public: explicit constexpr ServiceDispatchTableImpl(const std::array &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 &GetEntries() const { return m_entries; } }; } template class ServiceDispatchTable : public impl::ServiceDispatchTableImpl { public: explicit constexpr ServiceDispatchTable(const std::array &e) : impl::ServiceDispatchTableImpl(e) { /* ... */ } }; struct ServiceDispatchMeta { const impl::ServiceDispatchTableBase *DispatchTable; Result (impl::ServiceDispatchTableBase::*ProcessHandler)(ServiceDispatchContext &, const cmif::PointerAndSize &) const; uintptr_t GetServiceId() const { return reinterpret_cast(this->DispatchTable); } }; template requires sf::IsServiceObject struct ServiceDispatchTraits { using ProcessHandlerType = decltype(ServiceDispatchMeta::ProcessHandler); static constexpr inline auto DispatchTable = T::template s_CmifServiceDispatchTable; using DispatchTableType = decltype(DispatchTable); static constexpr ProcessHandlerType ProcessHandlerImpl = sf::IsMitmServiceObject ? (&impl::ServiceDispatchTableBase::ProcessMessageForMitm) : (&impl::ServiceDispatchTableBase::ProcessMessage); static constexpr inline ServiceDispatchMeta Meta{std::addressof(DispatchTable), ProcessHandlerImpl}; }; template<> struct ServiceDispatchTraits { static constexpr inline auto DispatchTable = ServiceDispatchTable<0, 0>(std::array{}); }; #if AMS_SF_MITM_SUPPORTED template<> struct ServiceDispatchTraits { static constexpr inline auto DispatchTable = ServiceDispatchTable<0, 0>(std::array{}); }; #endif template constexpr ALWAYS_INLINE const ServiceDispatchMeta *GetServiceDispatchMeta() { return std::addressof(ServiceDispatchTraits::Meta); } }