From 20e5abb30807d4e0e34c79c049252f0872e47ca7 Mon Sep 17 00:00:00 2001
From: Yuri Kunde Schlesner <yuriks@yuriks.net>
Date: Thu, 8 Jun 2017 23:52:30 -0700
Subject: [PATCH] ServiceFramework: Use separate copy of command buffer

Copy the IPC command buffer to/from the request context before/after the
handler is invoked. This is part of a move away from using global data
for handling IPC requests.
---
 src/core/hle/ipc.h               |  3 +++
 src/core/hle/kernel/hle_ipc.h    |  9 ++++++---
 src/core/hle/service/service.cpp | 26 ++++++++++++++++++++------
 3 files changed, 29 insertions(+), 9 deletions(-)

diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h
index 303ca090d..f7f96125a 100644
--- a/src/core/hle/ipc.h
+++ b/src/core/hle/ipc.h
@@ -44,6 +44,9 @@ inline u32* GetStaticBuffers(const int offset = 0) {
 
 namespace IPC {
 
+/// Size of the command buffer area, in 32-bit words.
+constexpr size_t COMMAND_BUFFER_LENGTH = 0x100 / sizeof(u32);
+
 // These errors are commonly returned by invalid IPC translations, so alias them here for
 // convenience.
 // TODO(yuriks): These will probably go away once translation is implemented inside the kernel.
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index c30184eab..aa0046001 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -4,8 +4,11 @@
 
 #pragma once
 
+#include <array>
 #include <memory>
 #include <vector>
+#include "common/common_types.h"
+#include "core/hle/ipc.h"
 #include "core/hle/kernel/kernel.h"
 #include "core/hle/kernel/server_session.h"
 
@@ -65,8 +68,8 @@ public:
     ~HLERequestContext();
 
     /// Returns a pointer to the IPC command buffer for this request.
-    u32* CommandBuffer() const {
-        return cmd_buf;
+    u32* CommandBuffer() {
+        return cmd_buf.data();
     }
 
     /**
@@ -80,7 +83,7 @@ public:
 private:
     friend class Service::ServiceFrameworkBase;
 
-    u32* cmd_buf = nullptr;
+    std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;
     SharedPtr<ServerSession> session;
 };
 
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index d34968428..35582b0ff 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -2,9 +2,12 @@
 // Licensed under GPLv2 or any later version
 // Refer to the license.txt file included.
 
+#include <algorithm>
 #include <fmt/format.h>
+#include "common/assert.h"
 #include "common/logging/log.h"
 #include "common/string_util.h"
+#include "core/hle/ipc.h"
 #include "core/hle/kernel/client_port.h"
 #include "core/hle/kernel/server_port.h"
 #include "core/hle/kernel/server_session.h"
@@ -160,12 +163,6 @@ void ServiceFrameworkBase::ReportUnimplementedFunction(u32* cmd_buf, const Funct
 void ServiceFrameworkBase::HandleSyncRequest(SharedPtr<ServerSession> server_session) {
     u32* cmd_buf = Kernel::GetCommandBuffer();
 
-    // TODO(yuriks): The kernel should be the one handling this as part of translation after
-    // everything else is migrated
-    Kernel::HLERequestContext context;
-    context.cmd_buf = cmd_buf;
-    context.session = std::move(server_session);
-
     u32 header_code = cmd_buf[0];
     auto itr = handlers.find(header_code);
     const FunctionInfoBase* info = itr == handlers.end() ? nullptr : &itr->second;
@@ -173,9 +170,26 @@ void ServiceFrameworkBase::HandleSyncRequest(SharedPtr<ServerSession> server_ses
         return ReportUnimplementedFunction(cmd_buf, info);
     }
 
+    // TODO(yuriks): The kernel should be the one handling this as part of translation after
+    // everything else is migrated
+    IPC::Header request_header{cmd_buf[0]};
+    size_t request_size =
+        1 + request_header.normal_params_size + request_header.translate_params_size;
+    ASSERT(request_size <= IPC::COMMAND_BUFFER_LENGTH); // TODO(yuriks): Return error
+
+    Kernel::HLERequestContext context;
+    std::copy_n(cmd_buf, request_size, context.cmd_buf.begin());
+    context.session = std::move(server_session);
+
     LOG_TRACE(Service, "%s",
               MakeFunctionString(info->name, GetServiceName().c_str(), cmd_buf).c_str());
     handler_invoker(this, info->handler_callback, context);
+
+    IPC::Header response_header{context.cmd_buf[0]};
+    size_t response_size =
+        1 + response_header.normal_params_size + response_header.translate_params_size;
+    ASSERT(response_size <= IPC::COMMAND_BUFFER_LENGTH);
+    std::copy_n(context.cmd_buf.begin(), response_size, cmd_buf);
 }
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////