mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-11-09 22:56:35 +00:00
kern/ipc: finish Receive part of ReplyAndReceive
This commit is contained in:
parent
804aa0e55d
commit
9fa6d12586
3 changed files with 303 additions and 27 deletions
|
@ -53,6 +53,7 @@ namespace ams::kern {
|
|||
void OnClientClosed();
|
||||
private:
|
||||
bool IsSignaledImpl() const;
|
||||
void CleanupRequests();
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -62,17 +62,65 @@ namespace ams::kern {
|
|||
void Initialize() { /* ... */ }
|
||||
void Finalize();
|
||||
|
||||
size_t GetSendCount() const { return this->num_send; }
|
||||
size_t GetReceiveCount() const { return this->num_recv; }
|
||||
size_t GetExchangeCount() const { return this->num_exch; }
|
||||
constexpr size_t GetSendCount() const { return this->num_send; }
|
||||
constexpr size_t GetReceiveCount() const { return this->num_recv; }
|
||||
constexpr size_t GetExchangeCount() const { return this->num_exch; }
|
||||
|
||||
Result PushSend(KProcessAddress client, KProcessAddress server, size_t size, KMemoryState state);
|
||||
Result PushReceive(KProcessAddress client, KProcessAddress server, size_t size, KMemoryState state);
|
||||
Result PushExchange(KProcessAddress client, KProcessAddress server, size_t size, KMemoryState state);
|
||||
|
||||
/* TODO: More functionality. */
|
||||
constexpr KProcessAddress GetSendClientAddress(size_t i) const { return GetSendMapping(i).GetClientAddress(); }
|
||||
constexpr KProcessAddress GetSendServerAddress(size_t i) const { return GetSendMapping(i).GetServerAddress(); }
|
||||
constexpr size_t GetSendSize(size_t i) const { return GetSendMapping(i).GetSize(); }
|
||||
constexpr KMemoryState GetSendMemoryState(size_t i) const { return GetSendMapping(i).GetMemoryState(); }
|
||||
|
||||
constexpr KProcessAddress GetReceiveClientAddress(size_t i) const { return GetReceiveMapping(i).GetClientAddress(); }
|
||||
constexpr KProcessAddress GetReceiveServerAddress(size_t i) const { return GetReceiveMapping(i).GetServerAddress(); }
|
||||
constexpr size_t GetReceiveSize(size_t i) const { return GetReceiveMapping(i).GetSize(); }
|
||||
constexpr KMemoryState GetReceiveMemoryState(size_t i) const { return GetReceiveMapping(i).GetMemoryState(); }
|
||||
|
||||
constexpr KProcessAddress GetExchangeClientAddress(size_t i) const { return GetExchangeMapping(i).GetClientAddress(); }
|
||||
constexpr KProcessAddress GetExchangeServerAddress(size_t i) const { return GetExchangeMapping(i).GetServerAddress(); }
|
||||
constexpr size_t GetExchangeSize(size_t i) const { return GetExchangeMapping(i).GetSize(); }
|
||||
constexpr KMemoryState GetExchangeMemoryState(size_t i) const { return GetExchangeMapping(i).GetMemoryState(); }
|
||||
private:
|
||||
Result PushMap(KProcessAddress client, KProcessAddress server, size_t size, KMemoryState state, size_t index);
|
||||
|
||||
constexpr const Mapping &GetSendMapping(size_t i) const {
|
||||
MESOSPHERE_ASSERT(i < this->num_send);
|
||||
|
||||
const size_t index = i;
|
||||
if (index < NumStaticMappings) {
|
||||
return this->static_mappings[index];
|
||||
} else {
|
||||
return this->mappings[index - NumStaticMappings];
|
||||
}
|
||||
}
|
||||
|
||||
constexpr const Mapping &GetReceiveMapping(size_t i) const {
|
||||
MESOSPHERE_ASSERT(i < this->num_recv);
|
||||
|
||||
const size_t index = this->num_send + i;
|
||||
if (index < NumStaticMappings) {
|
||||
return this->static_mappings[index];
|
||||
} else {
|
||||
return this->mappings[index - NumStaticMappings];
|
||||
}
|
||||
}
|
||||
|
||||
constexpr const Mapping &GetExchangeMapping(size_t i) const {
|
||||
MESOSPHERE_ASSERT(i < this->num_exch);
|
||||
|
||||
const size_t index = this->num_send + this->num_recv + i;
|
||||
if (index < NumStaticMappings) {
|
||||
return this->static_mappings[index];
|
||||
} else {
|
||||
return this->mappings[index - NumStaticMappings];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
private:
|
||||
SessionMappings mappings;
|
||||
|
@ -128,23 +176,23 @@ namespace ams::kern {
|
|||
|
||||
static void PostDestroy(uintptr_t arg) { /* ... */ }
|
||||
|
||||
KThread *GetThread() const { return this->thread; }
|
||||
KWritableEvent *GetEvent() const { return this->event; }
|
||||
uintptr_t GetAddress() const { return this->address; }
|
||||
size_t GetSize() const { return this->size; }
|
||||
KProcess *GetServerProcess() const { return this->server; }
|
||||
constexpr KThread *GetThread() const { return this->thread; }
|
||||
constexpr KWritableEvent *GetEvent() const { return this->event; }
|
||||
constexpr uintptr_t GetAddress() const { return this->address; }
|
||||
constexpr size_t GetSize() const { return this->size; }
|
||||
constexpr KProcess *GetServerProcess() const { return this->server; }
|
||||
|
||||
void SetServerProcess(KProcess *process) {
|
||||
this->server = process;
|
||||
this->server->Open();
|
||||
}
|
||||
|
||||
void ClearThread() { this->thread = nullptr; }
|
||||
void ClearEvent() { this->event = nullptr; }
|
||||
constexpr void ClearThread() { this->thread = nullptr; }
|
||||
constexpr void ClearEvent() { this->event = nullptr; }
|
||||
|
||||
size_t GetSendCount() const { return this->mappings.GetSendCount(); }
|
||||
size_t GetReceiveCount() const { return this->mappings.GetReceiveCount(); }
|
||||
size_t GetExchangeCount() const { return this->mappings.GetExchangeCount(); }
|
||||
constexpr size_t GetSendCount() const { return this->mappings.GetSendCount(); }
|
||||
constexpr size_t GetReceiveCount() const { return this->mappings.GetReceiveCount(); }
|
||||
constexpr size_t GetExchangeCount() const { return this->mappings.GetExchangeCount(); }
|
||||
|
||||
Result PushSend(KProcessAddress client, KProcessAddress server, size_t size, KMemoryState state) {
|
||||
return this->mappings.PushSend(client, server, size, state);
|
||||
|
@ -158,7 +206,20 @@ namespace ams::kern {
|
|||
return this->mappings.PushExchange(client, server, size, state);
|
||||
}
|
||||
|
||||
/* TODO: More functionality. */
|
||||
constexpr KProcessAddress GetSendClientAddress(size_t i) const { return this->mappings.GetSendClientAddress(i); }
|
||||
constexpr KProcessAddress GetSendServerAddress(size_t i) const { return this->mappings.GetSendServerAddress(i); }
|
||||
constexpr size_t GetSendSize(size_t i) const { return this->mappings.GetSendSize(i); }
|
||||
constexpr KMemoryState GetSendMemoryState(size_t i) const { return this->mappings.GetSendMemoryState(i); }
|
||||
|
||||
constexpr KProcessAddress GetReceiveClientAddress(size_t i) const { return this->mappings.GetReceiveClientAddress(i); }
|
||||
constexpr KProcessAddress GetReceiveServerAddress(size_t i) const { return this->mappings.GetReceiveServerAddress(i); }
|
||||
constexpr size_t GetReceiveSize(size_t i) const { return this->mappings.GetReceiveSize(i); }
|
||||
constexpr KMemoryState GetReceiveMemoryState(size_t i) const { return this->mappings.GetReceiveMemoryState(i); }
|
||||
|
||||
constexpr KProcessAddress GetExchangeClientAddress(size_t i) const { return this->mappings.GetExchangeClientAddress(i); }
|
||||
constexpr KProcessAddress GetExchangeServerAddress(size_t i) const { return this->mappings.GetExchangeServerAddress(i); }
|
||||
constexpr size_t GetExchangeSize(size_t i) const { return this->mappings.GetExchangeSize(i); }
|
||||
constexpr KMemoryState GetExchangeMemoryState(size_t i) const { return this->mappings.GetExchangeMemoryState(i); }
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -251,6 +251,96 @@ namespace ams::kern {
|
|||
return ResultSuccess();
|
||||
}
|
||||
|
||||
|
||||
ALWAYS_INLINE void CleanupSpecialData(KProcess &dst_process, u32 *dst_msg_ptr, size_t dst_buffer_size) {
|
||||
/* Parse the message. */
|
||||
const ipc::MessageBuffer dst_msg(dst_msg_ptr, dst_buffer_size);
|
||||
const ipc::MessageBuffer::MessageHeader dst_header(dst_msg);
|
||||
const ipc::MessageBuffer::SpecialHeader dst_special_header(dst_msg, dst_header);
|
||||
|
||||
/* Check that the size is big enough. */
|
||||
if (ipc::MessageBuffer::GetMessageBufferSize(dst_header, dst_special_header) > dst_buffer_size) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set the special header. */
|
||||
int offset = dst_msg.Set(dst_special_header);
|
||||
|
||||
/* Clear the process id, if needed. */
|
||||
if (dst_special_header.GetHasProcessId()) {
|
||||
offset = dst_msg.SetProcessId(offset, 0);
|
||||
}
|
||||
|
||||
/* Clear handles, as relevant. */
|
||||
auto &dst_handle_table = dst_process.GetHandleTable();
|
||||
for (auto i = 0; i < (dst_special_header.GetCopyHandleCount() + dst_special_header.GetMoveHandleCount()); ++i) {
|
||||
const ams::svc::Handle handle = dst_msg.GetHandle(offset);
|
||||
|
||||
if (handle != ams::svc::InvalidHandle) {
|
||||
dst_handle_table.Remove(handle);
|
||||
}
|
||||
|
||||
offset = dst_msg.SetHandle(offset, ams::svc::InvalidHandle);
|
||||
}
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result CleanupServerMap(KSessionRequest *request, KProcess *server_process) {
|
||||
/* If there's no server process, there's nothing to clean up. */
|
||||
R_SUCCEED_IF(server_process == nullptr);
|
||||
|
||||
/* Get the page table. */
|
||||
auto &server_page_table = server_process->GetPageTable();
|
||||
|
||||
/* Cleanup Send mappings. */
|
||||
for (size_t i = 0; i < request->GetSendCount(); ++i) {
|
||||
R_TRY(server_page_table.CleanupForIpcServer(request->GetSendServerAddress(i), request->GetSendSize(i), request->GetSendMemoryState(i), server_process));
|
||||
}
|
||||
|
||||
/* Cleanup Receive mappings. */
|
||||
for (size_t i = 0; i < request->GetReceiveCount(); ++i) {
|
||||
R_TRY(server_page_table.CleanupForIpcServer(request->GetReceiveServerAddress(i), request->GetReceiveSize(i), request->GetReceiveMemoryState(i), server_process));
|
||||
}
|
||||
|
||||
/* Cleanup Exchange mappings. */
|
||||
for (size_t i = 0; i < request->GetExchangeCount(); ++i) {
|
||||
R_TRY(server_page_table.CleanupForIpcServer(request->GetExchangeServerAddress(i), request->GetExchangeSize(i), request->GetExchangeMemoryState(i), server_process));
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result CleanupClientMap(KSessionRequest *request, KProcessPageTable *client_page_table) {
|
||||
/* If there's no client page table, there's nothing to clean up. */
|
||||
R_SUCCEED_IF(client_page_table == nullptr);
|
||||
|
||||
/* Cleanup Send mappings. */
|
||||
for (size_t i = 0; i < request->GetSendCount(); ++i) {
|
||||
R_TRY(client_page_table->CleanupForIpcClient(request->GetSendServerAddress(i), request->GetSendSize(i), request->GetSendMemoryState(i)));
|
||||
}
|
||||
|
||||
/* Cleanup Receive mappings. */
|
||||
for (size_t i = 0; i < request->GetReceiveCount(); ++i) {
|
||||
R_TRY(client_page_table->CleanupForIpcClient(request->GetReceiveServerAddress(i), request->GetReceiveSize(i), request->GetReceiveMemoryState(i)));
|
||||
}
|
||||
|
||||
/* Cleanup Exchange mappings. */
|
||||
for (size_t i = 0; i < request->GetExchangeCount(); ++i) {
|
||||
R_TRY(client_page_table->CleanupForIpcClient(request->GetExchangeServerAddress(i), request->GetExchangeSize(i), request->GetExchangeMemoryState(i)));
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result CleanupMap(KSessionRequest *request, KProcess *server_process, KProcessPageTable *client_page_table) {
|
||||
/* Cleanup the server map. */
|
||||
R_TRY(CleanupServerMap(request, server_process));
|
||||
|
||||
/* Cleanup the client map. */
|
||||
R_TRY(CleanupClientMap(request, client_page_table));
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result ProcessReceiveMessageMapAliasDescriptors(int &offset, KProcessPageTable &dst_page_table, KProcessPageTable &src_page_table, const ipc::MessageBuffer &dst_msg, const ipc::MessageBuffer &src_msg, KSessionRequest *request, KMemoryPermission perm, bool send) {
|
||||
/* Get the offset at the start of processing. */
|
||||
const int cur_offset = offset;
|
||||
|
@ -380,8 +470,21 @@ namespace ams::kern {
|
|||
|
||||
/* Set up a guard to make sure that we end up in a clean state on error. */
|
||||
auto cleanup_guard = SCOPE_GUARD {
|
||||
/* TODO */
|
||||
MESOSPHERE_UNIMPLEMENTED();
|
||||
/* Cleanup mappings. */
|
||||
CleanupMap(request, std::addressof(dst_process), std::addressof(src_page_table));
|
||||
|
||||
/* Cleanup special data. */
|
||||
if (src_header.GetHasSpecialHeader()) {
|
||||
CleanupSpecialData(dst_process, dst_msg_ptr, dst_buffer_size);
|
||||
}
|
||||
|
||||
/* Cleanup the header if the receive list isn't broken. */
|
||||
if (!recv_list_broken) {
|
||||
dst_msg.Set(dst_header);
|
||||
if (dst_header.GetHasSpecialHeader()) {
|
||||
dst_msg.Set(dst_special_header);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/* Process any special data. */
|
||||
|
@ -464,12 +567,6 @@ namespace ams::kern {
|
|||
}
|
||||
}
|
||||
|
||||
/* TODO: Remove this when done, as these variables will be used by unimplemented stuff above. */
|
||||
static_cast<void>(dst_page_table);
|
||||
static_cast<void>(dst_user);
|
||||
static_cast<void>(src_user);
|
||||
static_cast<void>(pointer_key);
|
||||
|
||||
/* We succeeded! */
|
||||
cleanup_guard.Cancel();
|
||||
return ResultSuccess();
|
||||
|
@ -495,7 +592,7 @@ namespace ams::kern {
|
|||
|
||||
this->parent->OnServerClosed();
|
||||
|
||||
/* TODO: this->CleanupRequests(); */
|
||||
this->CleanupRequests();
|
||||
|
||||
this->parent->Close();
|
||||
}
|
||||
|
@ -543,8 +640,57 @@ namespace ams::kern {
|
|||
|
||||
/* Handle cleanup on receive failure. */
|
||||
if (R_FAILED(result)) {
|
||||
/* TODO */
|
||||
MESOSPHERE_UNIMPLEMENTED();
|
||||
/* Cache the result to return it to the client. */
|
||||
const Result result_for_client = result;
|
||||
|
||||
/* Clear the current request. */
|
||||
{
|
||||
KScopedSchedulerLock sl;
|
||||
MESOSPHERE_ASSERT(this->current_request == request);
|
||||
this->current_request = nullptr;
|
||||
if (!this->request_list.empty()) {
|
||||
this->NotifyAvailable();
|
||||
}
|
||||
}
|
||||
|
||||
/* Reply to the client. */
|
||||
{
|
||||
/* After we reply, close our reference to the request. */
|
||||
ON_SCOPE_EXIT { request->Close(); };
|
||||
|
||||
/* Get the event to check whether the request is async. */
|
||||
if (KWritableEvent *event = request->GetEvent(); event != nullptr) {
|
||||
/* The client sent an async request. */
|
||||
KProcess *client = client_thread->GetOwnerProcess();
|
||||
auto &client_pt = client->GetPageTable();
|
||||
|
||||
/* Send the async result. */
|
||||
if (R_FAILED(result_for_client)) {
|
||||
ReplyAsyncError(client, client_message, client_buffer_size, result_for_client);
|
||||
}
|
||||
|
||||
/* Unlock the client buffer. */
|
||||
/* NOTE: Nintendo does not check the result of this. */
|
||||
client_pt.UnlockForIpcUserBuffer(client_message, client_buffer_size);
|
||||
|
||||
/* Signal the event. */
|
||||
event->Signal();
|
||||
} else {
|
||||
/* Set the thread as runnable. */
|
||||
KScopedSchedulerLock sl;
|
||||
if (client_thread->GetState() == KThread::ThreadState_Waiting) {
|
||||
client_thread->SetSyncedObject(nullptr, result_for_client);
|
||||
client_thread->SetState(KThread::ThreadState_Runnable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the server result. */
|
||||
if (recv_list_broken) {
|
||||
return svc::ResultReceiveListBroken();
|
||||
} else {
|
||||
return svc::ResultNotFound();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -603,6 +749,74 @@ namespace ams::kern {
|
|||
return this->IsSignaledImpl();
|
||||
}
|
||||
|
||||
void KServerSession::CleanupRequests() {
|
||||
MESOSPHERE_ASSERT_THIS();
|
||||
|
||||
KScopedLightLock lk(this->lock);
|
||||
|
||||
/* Clean up any pending requests. */
|
||||
while (true) {
|
||||
/* Get the next request. */
|
||||
KSessionRequest *request = nullptr;
|
||||
{
|
||||
KScopedSchedulerLock sl;
|
||||
|
||||
if (this->current_request) {
|
||||
/* Choose the current request if we have one. */
|
||||
request = this->current_request;
|
||||
this->current_request = nullptr;
|
||||
} else if (!this->request_list.empty()) {
|
||||
/* Pop the request from the front of the list. */
|
||||
request = std::addressof(this->request_list.front());
|
||||
this->request_list.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
/* If there's no request, we're done. */
|
||||
if (request == nullptr) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Close a reference to the request once it's cleaned up. */
|
||||
ON_SCOPE_EXIT { request->Close(); };
|
||||
|
||||
/* Extract relevant information from the request. */
|
||||
const uintptr_t client_message = request->GetAddress();
|
||||
const size_t client_buffer_size = request->GetSize();
|
||||
KThread *client_thread = request->GetThread();
|
||||
KWritableEvent *event = request->GetEvent();
|
||||
|
||||
KProcess *server_process = request->GetServerProcess();
|
||||
KProcess *client_process = (client_thread != nullptr) ? client_thread->GetOwnerProcess() : nullptr;
|
||||
KProcessPageTable *client_page_table = (client_process != nullptr) ? std::addressof(client_process->GetPageTable()) : nullptr;
|
||||
|
||||
/* Cleanup the mappings. */
|
||||
Result result = CleanupMap(request, server_process, client_page_table);
|
||||
|
||||
/* If there's a client thread, update it. */
|
||||
if (client_thread != nullptr) {
|
||||
if (event != nullptr) {
|
||||
/* We need to reply async. */
|
||||
ReplyAsyncError(client_process, client_message, client_buffer_size, (R_SUCCEEDED(result) ? svc::ResultSessionClosed() : result));
|
||||
|
||||
/* Unlock the client buffer. */
|
||||
/* NOTE: Nintendo does not check the result of this. */
|
||||
client_page_table->UnlockForIpcUserBuffer(client_message, client_buffer_size);
|
||||
|
||||
/* Signal the event. */
|
||||
event->Signal();
|
||||
} else {
|
||||
/* Set the thread as runnable. */
|
||||
KScopedSchedulerLock sl;
|
||||
if (client_thread->GetState() == KThread::ThreadState_Waiting) {
|
||||
client_thread->SetSyncedObject(nullptr, (R_SUCCEEDED(result) ? svc::ResultSessionClosed() : result));
|
||||
client_thread->SetState(KThread::ThreadState_Runnable);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KServerSession::OnClientClosed() {
|
||||
MESOSPHERE_ASSERT_THIS();
|
||||
|
||||
|
@ -677,7 +891,7 @@ namespace ams::kern {
|
|||
MESOSPHERE_ASSERT(request->GetExchangeCount() == 0);
|
||||
|
||||
/* Get the process and page table. */
|
||||
KProcess *client_process = thread->GetOwner();
|
||||
KProcess *client_process = thread->GetOwnerProcess();
|
||||
auto &client_pt = client_process->GetPageTable();
|
||||
|
||||
/* Reply to the request. */
|
||||
|
|
Loading…
Reference in a new issue