kern/ipc: finish Receive part of ReplyAndReceive

This commit is contained in:
Michael Scire 2020-07-10 10:20:33 -07:00
parent 804aa0e55d
commit 9fa6d12586
3 changed files with 303 additions and 27 deletions

View file

@ -53,6 +53,7 @@ namespace ams::kern {
void OnClientClosed();
private:
bool IsSignaledImpl() const;
void CleanupRequests();
};
}

View file

@ -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); }
};
}

View file

@ -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. */