/* * 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 . */ #include namespace ams::kern::svc { /* ============================= Common ============================= */ namespace { template Result CreateSession(ams::svc::Handle *out_server, ams::svc::Handle *out_client, uintptr_t name) { /* Get the current process and handle table. */ auto &process = GetCurrentProcess(); auto &handle_table = process.GetHandleTable(); /* Declare the session we're going to allocate. */ T *session; /* Reserve a new session from the process resource limit. */ KScopedResourceReservation session_reservation(std::addressof(process), ams::svc::LimitableResource_SessionCountMax); if (session_reservation.Succeeded()) { /* Allocate a session normally. */ session = T::Create(); } else { /* We couldn't reserve a session. Check that we support dynamically expanding the resource limit. */ R_UNLESS(process.GetResourceLimit() == std::addressof(Kernel::GetSystemResourceLimit()), svc::ResultLimitReached()); R_UNLESS(KTargetSystem::IsDynamicResourceLimitsEnabled(), svc::ResultLimitReached()); /* Try to allocate a session from unused slab memory. */ session = T::CreateFromUnusedSlabMemory(); R_UNLESS(session != nullptr, svc::ResultLimitReached()); ON_RESULT_FAILURE { session->Close(); }; /* If we're creating a KSession, we want to add two KSessionRequests to the heap, to prevent request exhaustion. */ /* NOTE: Nintendo checks if session->DynamicCast() != nullptr, but there's no reason to not do this statically. */ if constexpr (std::same_as) { for (size_t i = 0; i < 2; ++i) { KSessionRequest *request = KSessionRequest::CreateFromUnusedSlabMemory(); R_UNLESS(request != nullptr, svc::ResultLimitReached()); request->Close(); } } /* We successfully allocated a session, so add the object we allocated to the resource limit. */ Kernel::GetSystemResourceLimit().Add(ams::svc::LimitableResource_SessionCountMax, 1); } /* Check that we successfully created a session. */ R_UNLESS(session != nullptr, svc::ResultOutOfResource()); /* Initialize the session. */ session->Initialize(nullptr, name); /* Commit the session reservation. */ session_reservation.Commit(); /* Ensure that we clean up the session (and its only references are handle table) on function end. */ ON_SCOPE_EXIT { session->GetClientSession().Close(); session->GetServerSession().Close(); }; /* Register the session. */ T::Register(session); /* Add the server session to the handle table. */ R_TRY(handle_table.Add(out_server, std::addressof(session->GetServerSession()))); /* Ensure that we maintaing a clean handle state on exit. */ ON_RESULT_FAILURE { handle_table.Remove(*out_server); }; /* Add the client session to the handle table. */ R_RETURN(handle_table.Add(out_client, std::addressof(session->GetClientSession()))); } Result CreateSession(ams::svc::Handle *out_server, ams::svc::Handle *out_client, bool is_light, uintptr_t name) { if (is_light) { R_RETURN(CreateSession(out_server, out_client, name)); } else { R_RETURN(CreateSession(out_server, out_client, name)); } } Result AcceptSession(ams::svc::Handle *out, ams::svc::Handle port_handle) { /* Get the current handle table. */ auto &handle_table = GetCurrentProcess().GetHandleTable(); /* Get the server port. */ KScopedAutoObject port = handle_table.GetObject(port_handle); R_UNLESS(port.IsNotNull(), svc::ResultInvalidHandle()); /* Reserve an entry for the new session. */ R_TRY(handle_table.Reserve(out)); ON_RESULT_FAILURE { handle_table.Unreserve(*out); }; /* Accept the session. */ KAutoObject *session; if (port->IsLight()) { session = port->AcceptLightSession(); } else { session = port->AcceptSession(); } /* Ensure we accepted successfully. */ R_UNLESS(session != nullptr, svc::ResultNotFound()); /* Register the session. */ handle_table.Register(*out, session); session->Close(); R_SUCCEED(); } } /* ============================= 64 ABI ============================= */ Result CreateSession64(ams::svc::Handle *out_server_session_handle, ams::svc::Handle *out_client_session_handle, bool is_light, ams::svc::Address name) { R_RETURN(CreateSession(out_server_session_handle, out_client_session_handle, is_light, name)); } Result AcceptSession64(ams::svc::Handle *out_handle, ams::svc::Handle port) { R_RETURN(AcceptSession(out_handle, port)); } /* ============================= 64From32 ABI ============================= */ Result CreateSession64From32(ams::svc::Handle *out_server_session_handle, ams::svc::Handle *out_client_session_handle, bool is_light, ams::svc::Address name) { R_RETURN(CreateSession(out_server_session_handle, out_client_session_handle, is_light, name)); } Result AcceptSession64From32(ams::svc::Handle *out_handle, ams::svc::Handle port) { R_RETURN(AcceptSession(out_handle, port)); } }