kern: SvcConnectToNamedPort

This commit is contained in:
Michael Scire 2020-07-09 14:49:51 -07:00
parent a2eb93fde8
commit 7400a8ff68
15 changed files with 376 additions and 15 deletions

View File

@ -177,10 +177,12 @@ namespace ams::kern {
} }
}; };
template<typename T> template<typename T> requires std::derived_from<T, KAutoObject>
class KScopedAutoObject { class KScopedAutoObject {
static_assert(std::is_base_of<KAutoObject, T>::value);
NON_COPYABLE(KScopedAutoObject); NON_COPYABLE(KScopedAutoObject);
private:
template<typename U>
friend class KScopedAutoObject;
private: private:
T *obj; T *obj;
private: private:
@ -202,12 +204,32 @@ namespace ams::kern {
this->obj = nullptr; this->obj = nullptr;
} }
constexpr ALWAYS_INLINE KScopedAutoObject(KScopedAutoObject &&rhs) { template<typename U>
this->obj = rhs.obj; constexpr ALWAYS_INLINE KScopedAutoObject(KScopedAutoObject<U> &&rhs) {
rhs.obj = nullptr; if constexpr (std::same_as<T, U>) {
this->obj = rhs.obj;
rhs.obj = nullptr;
} else {
T *derived = rhs.obj->template DynamicCast<T *>();
if (derived == nullptr) {
rhs.obj->Close();
}
this->obj = derived;
rhs.obj = nullptr;
}
} }
constexpr ALWAYS_INLINE KScopedAutoObject &operator=(KScopedAutoObject &&rhs) { template<typename U>
constexpr ALWAYS_INLINE KScopedAutoObject &operator=(KScopedAutoObject<U> &&rhs) {
if constexpr (!std::same_as<T, U>) {
T *derived = rhs.obj->template DynamicCast<T *>();
if (derived == nullptr) {
rhs.obj->Close();
}
rhs.obj = nullptr;
}
rhs.Swap(*this); rhs.Swap(*this);
return *this; return *this;
} }

View File

@ -20,6 +20,10 @@
namespace ams::kern { namespace ams::kern {
class KPort; class KPort;
class KSession;
class KClientSession;
class KLightSession;
class KLightClientSession;
class KClientPort final : public KSynchronizationObject { class KClientPort final : public KSynchronizationObject {
MESOSPHERE_AUTOOBJECT_TRAITS(KClientPort, KSynchronizationObject); MESOSPHERE_AUTOOBJECT_TRAITS(KClientPort, KSynchronizationObject);
@ -33,6 +37,8 @@ namespace ams::kern {
virtual ~KClientPort() { /* ... */ } virtual ~KClientPort() { /* ... */ }
void Initialize(KPort *parent, s32 max_sessions); void Initialize(KPort *parent, s32 max_sessions);
void OnSessionFinalized();
void OnServerClosed();
constexpr const KPort *GetParent() const { return this->parent; } constexpr const KPort *GetParent() const { return this->parent; }
@ -43,6 +49,7 @@ namespace ams::kern {
virtual bool IsSignaled() const override; virtual bool IsSignaled() const override;
/* TODO: More of KClientPort. */ /* TODO: More of KClientPort. */
Result CreateSession(KClientSession **out);
}; };
} }

View File

@ -39,6 +39,7 @@ namespace ams::kern {
constexpr const KSession *GetParent() const { return this->parent; } constexpr const KSession *GetParent() const { return this->parent; }
/* TODO: More of KClientSession. */ /* TODO: More of KClientSession. */
void OnServerClosed();
}; };
} }

View File

@ -49,6 +49,11 @@ namespace ams::kern {
return Delete(obj.GetPointerUnsafe(), name); return Delete(obj.GetPointerUnsafe(), name);
} }
template<typename Derived> requires std::derived_from<Derived, KAutoObject>
static KScopedAutoObject<Derived> Find(const char *name) {
return Find(name);
}
private: private:
static KScopedAutoObject<KAutoObject> FindImpl(const char *name); static KScopedAutoObject<KAutoObject> FindImpl(const char *name);

View File

@ -22,6 +22,9 @@
namespace ams::kern { namespace ams::kern {
class KServerSession;
class KLightServerSession;
class KPort final : public KAutoObjectWithSlabHeapAndContainer<KPort, KAutoObjectWithList> { class KPort final : public KAutoObjectWithSlabHeapAndContainer<KPort, KAutoObjectWithList> {
MESOSPHERE_AUTOOBJECT_TRAITS(KPort, KAutoObject); MESOSPHERE_AUTOOBJECT_TRAITS(KPort, KAutoObject);
private: private:
@ -50,7 +53,8 @@ namespace ams::kern {
uintptr_t GetName() const { return this->name; } uintptr_t GetName() const { return this->name; }
bool IsLight() const { return this->is_light; } bool IsLight() const { return this->is_light; }
/* TODO: More of KPort */ Result EnqueueSession(KServerSession *session);
Result EnqueueSession(KLightServerSession *session);
KClientPort &GetClientPort() { return this->client; } KClientPort &GetClientPort() { return this->client; }
KServerPort &GetServerPort() { return this->server; } KServerPort &GetServerPort() { return this->server; }

View File

@ -38,6 +38,8 @@ namespace ams::kern {
virtual ~KServerPort() { /* ... */ } virtual ~KServerPort() { /* ... */ }
void Initialize(KPort *parent); void Initialize(KPort *parent);
void EnqueueSession(KServerSession *session);
void EnqueueSession(KLightServerSession *session);
constexpr const KPort *GetParent() const { return this->parent; } constexpr const KPort *GetParent() const { return this->parent; }
@ -47,7 +49,7 @@ namespace ams::kern {
virtual void Destroy() override; virtual void Destroy() override;
virtual bool IsSignaled() const override; virtual bool IsSignaled() const override;
/* TODO: More of KClientPort. */ /* TODO: More of KServerPort. */
private: private:
void CleanupSessions(); void CleanupSessions();
/* TODO: This is a placeholder definition. */ /* TODO: This is a placeholder definition. */

View File

@ -36,13 +36,16 @@ namespace ams::kern {
constexpr KServerSession() : parent(), request_list(), current_request(), lock() { /* ... */ } constexpr KServerSession() : parent(), request_list(), current_request(), lock() { /* ... */ }
virtual ~KServerSession() { /* ... */ } virtual ~KServerSession() { /* ... */ }
void Initialize(KSession *parent); void Initialize(KSession *p) { this->parent = p; }
constexpr const KSession *GetParent() const { return this->parent; } constexpr const KSession *GetParent() const { return this->parent; }
virtual bool IsSignaled() const override { MESOSPHERE_UNIMPLEMENTED(); } virtual bool IsSignaled() const override { MESOSPHERE_UNIMPLEMENTED(); }
/* TODO: More of KServerSession. */ /* TODO: More of KServerSession. */
Result OnRequest(KSessionRequest *request);
void OnClientClosed();
}; };
} }

View File

@ -51,12 +51,21 @@ namespace ams::kern {
virtual ~KSession() { /* ... */ } virtual ~KSession() { /* ... */ }
void Initialize(KClientPort *client_port, uintptr_t name);
virtual void Finalize() override;
virtual bool IsInitialized() const override { return this->initialized; } virtual bool IsInitialized() const override { return this->initialized; }
virtual uintptr_t GetPostDestroyArgument() const override { return reinterpret_cast<uintptr_t>(this->process); } virtual uintptr_t GetPostDestroyArgument() const override { return reinterpret_cast<uintptr_t>(this->process); }
static void PostDestroy(uintptr_t arg); static void PostDestroy(uintptr_t arg);
/* TODO: This is a placeholder definition. */ void OnServerClosed();
void OnClientClosed();
bool IsServerClosed() const { return this->state != State::Normal; }
bool IsClientClosed() const { return this->state != State::Normal; }
Result OnRequest(KSessionRequest *request) { return this->server.OnRequest(request); }
KClientSession &GetClientSession() { return this->client; } KClientSession &GetClientSession() { return this->client; }
KServerSession &GetServerSession() { return this->server; } KServerSession &GetServerSession() { return this->server; }

View File

@ -25,6 +25,19 @@ namespace ams::kern {
this->max_sessions = max_sessions; this->max_sessions = max_sessions;
} }
void KClientPort::OnSessionFinalized() {
KScopedSchedulerLock sl;
const auto prev = this->num_sessions--;
if (prev == this->max_sessions) {
this->NotifyAvailable();
}
}
void KClientPort::OnServerClosed() {
MESOSPHERE_ASSERT_THIS();
}
bool KClientPort::IsLight() const { bool KClientPort::IsLight() const {
return this->GetParent()->IsLight(); return this->GetParent()->IsLight();
} }
@ -43,4 +56,71 @@ namespace ams::kern {
return this->num_sessions < this->max_sessions; return this->num_sessions < this->max_sessions;
} }
Result KClientPort::CreateSession(KClientSession **out) {
MESOSPHERE_ASSERT_THIS();
/* Reserve a new session from the resource limit. */
KScopedResourceReservation session_reservation(GetCurrentProcessPointer(), ams::svc::LimitableResource_SessionCountMax);
R_UNLESS(session_reservation.Succeeded(), svc::ResultLimitReached());
/* Update the session counts. */
{
/* Atomically increment the number of sessions. */
s32 new_sessions;
{
const auto max = this->max_sessions;
auto cur_sessions = this->num_sessions.load(std::memory_order_acquire);
do {
R_UNLESS(cur_sessions < max, svc::ResultOutOfSessions());
new_sessions = cur_sessions + 1;
} while (!this->num_sessions.compare_exchange_weak(cur_sessions, new_sessions, std::memory_order_relaxed));
}
/* Atomically update the peak session tracking. */
{
auto peak = this->peak_sessions.load(std::memory_order_acquire);
do {
if (peak >= new_sessions) {
break;
}
} while (!this->peak_sessions.compare_exchange_weak(peak, new_sessions, std::memory_order_relaxed));
}
}
/* Create a new session. */
KSession *session = KSession::Create();
if (session == nullptr) {
/* Decrement the session count. */
const auto prev = this->num_sessions--;
if (prev == this->max_sessions) {
this->NotifyAvailable();
}
return svc::ResultOutOfResource();
}
/* Initialize the session. */
session->Initialize(this, this->parent->GetName());
/* Commit the session reservation. */
session_reservation.Commit();
/* Register the session. */
KSession::Register(session);
auto session_guard = SCOPE_GUARD {
session->GetClientSession().Close();
session->GetServerSession().Close();
};
/* Enqueue the session with our parent. */
R_TRY(this->parent->EnqueueSession(std::addressof(session->GetServerSession())));
/* We succeeded, so set the output. */
session_guard.Cancel();
*out = std::addressof(session->GetClientSession());
return ResultSuccess();
}
} }

View File

@ -0,0 +1,24 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <mesosphere.hpp>
namespace ams::kern {
void KClientSession::OnServerClosed() {
MESOSPHERE_ASSERT_THIS();
}
}

View File

@ -34,11 +34,41 @@ namespace ams::kern {
} }
void KPort::OnClientClosed() { void KPort::OnClientClosed() {
MESOSPHERE_UNIMPLEMENTED(); MESOSPHERE_ASSERT_THIS();
KScopedSchedulerLock sl;
if (this->state == State::Normal) {
this->state = State::ClientClosed;
}
} }
void KPort::OnServerClosed() { void KPort::OnServerClosed() {
MESOSPHERE_UNIMPLEMENTED(); MESOSPHERE_ASSERT_THIS();
KScopedSchedulerLock sl;
if (this->state == State::Normal) {
this->state = State::ServerClosed;
}
}
Result KPort::EnqueueSession(KServerSession *session) {
KScopedSchedulerLock sl;
R_UNLESS(this->state == State::Normal, svc::ResultPortClosed());
this->server.EnqueueSession(session);
return ResultSuccess();
}
Result KPort::EnqueueSession(KLightServerSession *session) {
KScopedSchedulerLock sl;
R_UNLESS(this->state == State::Normal, svc::ResultPortClosed());
this->server.EnqueueSession(session);
return ResultSuccess();
} }
} }

View File

@ -89,7 +89,33 @@ namespace ams::kern {
if (this->IsLight()) { if (this->IsLight()) {
return !this->light_session_list.empty(); return !this->light_session_list.empty();
} else { } else {
return this->session_list.empty(); return !this->session_list.empty();
}
}
void KServerPort::EnqueueSession(KServerSession *session) {
MESOSPHERE_ASSERT_THIS();
MESOSPHERE_ASSERT(!this->IsLight());
KScopedSchedulerLock sl;
/* Add the session to our queue. */
this->session_list.push_back(*session);
if (this->session_list.size() == 1) {
this->NotifyAvailable();
}
}
void KServerPort::EnqueueSession(KLightServerSession *session) {
MESOSPHERE_ASSERT_THIS();
MESOSPHERE_ASSERT(this->IsLight());
KScopedSchedulerLock sl;
/* Add the session to our queue. */
this->light_session_list.push_back(*session);
if (this->light_session_list.size() == 1) {
this->NotifyAvailable();
} }
} }

View File

@ -0,0 +1,29 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <mesosphere.hpp>
namespace ams::kern {
Result KServerSession::OnRequest(KSessionRequest *request) {
MESOSPHERE_UNIMPLEMENTED();
}
void KServerSession::OnClientClosed() {
MESOSPHERE_ASSERT_THIS();
MESOSPHERE_UNIMPLEMENTED();
}
}

View File

@ -0,0 +1,84 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <mesosphere.hpp>
namespace ams::kern {
void KSession::Initialize(KClientPort *client_port, uintptr_t name) {
MESOSPHERE_ASSERT_THIS();
/* Increment reference count. */
this->Open();
/* Create our sub sessions. */
KAutoObject::Create(std::addressof(this->server));
KAutoObject::Create(std::addressof(this->client));
/* Initialize our sub sessions. */
this->server.Initialize(this);
this->client.Initialize(this);
/* Set state and name. */
this->state = State::Normal;
this->name = name;
/* Set our owner process. */
this->process = GetCurrentProcessPointer();
this->process->Open();
/* Set our port. */
this->port = port;
if (this->port != nullptr) {
this->port->Open();
}
/* Mark initialized. */
this->initialized = true;
}
void KSession::Finalize() {
if (this->port != nullptr) {
this->port->OnSessionFinalized();
this->port->Close();
}
}
void KSession::OnServerClosed() {
MESOSPHERE_ASSERT_THIS();
if (this->state == State::Normal) {
this->state = State::ServerClosed;
this->client.OnServerClosed();
}
}
void KSession::OnClientClosed() {
MESOSPHERE_ASSERT_THIS();
if (this->state == State::Normal) {
this->state = State::ClientClosed;
this->server.OnClientClosed();
}
}
void KSession::PostDestroy(uintptr_t arg) {
/* Release the session count resource the owner process holds. */
KProcess *owner = reinterpret_cast<KProcess *>(arg);
owner->ReleaseResource(ams::svc::LimitableResource_SessionCountMax, 1);
owner->Close();
}
}

View File

@ -77,12 +77,47 @@ namespace ams::kern::svc {
return ResultSuccess(); return ResultSuccess();
} }
Result ConnectToNamedPort(ams::svc::Handle *out, KUserPointer<const char *> user_name) {
/* Copy the provided name from user memory to kernel memory. */
char name[KObjectName::NameLengthMax] = {};
R_TRY(user_name.CopyStringTo(name, sizeof(name)));
/* Validate that name is valid. */
R_UNLESS(name[sizeof(name) - 1] == '\x00', svc::ResultOutOfRange());
MESOSPHERE_LOG("%s: ConnectToNamedPort(%s) was called\n", GetCurrentProcess().GetName(), name);
/* Get the current handle table. */
auto &handle_table = GetCurrentProcess().GetHandleTable();
/* Find the client port. */
auto port = KObjectName::Find<KClientPort>(name);
R_UNLESS(port.IsNotNull(), svc::ResultNotFound());
/* Reserve a handle for the port. */
/* NOTE: Nintendo really does write directly to the output handle here. */
R_TRY(handle_table.Reserve(out));
auto handle_guard = SCOPE_GUARD { handle_table.Unreserve(*out); };
/* Create a session. */
KClientSession *session;
R_TRY(port->CreateSession(std::addressof(session)));
/* Register the session in the table, close the extra reference. */
handle_table.Register(*out, session);
session->Close();
/* We succeeded. */
handle_guard.Cancel();
return ResultSuccess();
}
} }
/* ============================= 64 ABI ============================= */ /* ============================= 64 ABI ============================= */
Result ConnectToNamedPort64(ams::svc::Handle *out_handle, KUserPointer<const char *> name) { Result ConnectToNamedPort64(ams::svc::Handle *out_handle, KUserPointer<const char *> name) {
MESOSPHERE_PANIC("Stubbed SvcConnectToNamedPort64 was called."); return ConnectToNamedPort(out_handle, name);
} }
Result CreatePort64(ams::svc::Handle *out_server_handle, ams::svc::Handle *out_client_handle, int32_t max_sessions, bool is_light, ams::svc::Address name) { Result CreatePort64(ams::svc::Handle *out_server_handle, ams::svc::Handle *out_client_handle, int32_t max_sessions, bool is_light, ams::svc::Address name) {
@ -100,7 +135,7 @@ namespace ams::kern::svc {
/* ============================= 64From32 ABI ============================= */ /* ============================= 64From32 ABI ============================= */
Result ConnectToNamedPort64From32(ams::svc::Handle *out_handle, KUserPointer<const char *> name) { Result ConnectToNamedPort64From32(ams::svc::Handle *out_handle, KUserPointer<const char *> name) {
MESOSPHERE_PANIC("Stubbed SvcConnectToNamedPort64From32 was called."); return ConnectToNamedPort(out_handle, name);
} }
Result CreatePort64From32(ams::svc::Handle *out_server_handle, ams::svc::Handle *out_client_handle, int32_t max_sessions, bool is_light, ams::svc::Address name) { Result CreatePort64From32(ams::svc::Handle *out_server_handle, ams::svc::Handle *out_client_handle, int32_t max_sessions, bool is_light, ams::svc::Address name) {