1 // Copyright 2017 Citra Emulator Project
2 // Licensed under GPLv2 or any later version
3 // Refer to the license.txt file included.
4 
5 #include <algorithm>
6 #include <vector>
7 #include "common/assert.h"
8 #include "common/common_types.h"
9 #include "core/core.h"
10 #include "core/hle/kernel/event.h"
11 #include "core/hle/kernel/handle_table.h"
12 #include "core/hle/kernel/hle_ipc.h"
13 #include "core/hle/kernel/ipc_debugger/recorder.h"
14 #include "core/hle/kernel/kernel.h"
15 #include "core/hle/kernel/process.h"
16 
17 namespace Kernel {
18 
19 class HLERequestContext::ThreadCallback : public Kernel::WakeupCallback {
20 
21 public:
ThreadCallback(std::shared_ptr<HLERequestContext> context_,std::shared_ptr<HLERequestContext::WakeupCallback> callback_)22     ThreadCallback(std::shared_ptr<HLERequestContext> context_,
23                    std::shared_ptr<HLERequestContext::WakeupCallback> callback_)
24         : callback(std::move(callback_)), context(std::move(context_)) {}
WakeUp(ThreadWakeupReason reason,std::shared_ptr<Thread> thread,std::shared_ptr<WaitObject> object)25     void WakeUp(ThreadWakeupReason reason, std::shared_ptr<Thread> thread,
26                 std::shared_ptr<WaitObject> object) {
27         ASSERT(thread->status == ThreadStatus::WaitHleEvent);
28         if (callback) {
29             callback->WakeUp(thread, *context, reason);
30         }
31 
32         auto process = thread->owner_process.lock();
33         ASSERT(process);
34 
35         // We must copy the entire command buffer *plus* the entire static buffers area, since
36         // the translation might need to read from it in order to retrieve the StaticBuffer
37         // target addresses.
38         std::array<u32_le, IPC::COMMAND_BUFFER_LENGTH + 2 * IPC::MAX_STATIC_BUFFERS> cmd_buff;
39         Memory::MemorySystem& memory = context->kernel.memory;
40         memory.ReadBlock(*process, thread->GetCommandBufferAddress(), cmd_buff.data(),
41                          cmd_buff.size() * sizeof(u32));
42         context->WriteToOutgoingCommandBuffer(cmd_buff.data(), *process);
43         // Copy the translated command buffer back into the thread's command buffer area.
44         memory.WriteBlock(*process, thread->GetCommandBufferAddress(), cmd_buff.data(),
45                           cmd_buff.size() * sizeof(u32));
46     }
47 
48 private:
49     ThreadCallback() = default;
50     std::shared_ptr<HLERequestContext::WakeupCallback> callback{};
51     std::shared_ptr<HLERequestContext> context{};
52 
53     template <class Archive>
serialize(Archive & ar,const unsigned int)54     void serialize(Archive& ar, const unsigned int) {
55         ar& boost::serialization::base_object<Kernel::WakeupCallback>(*this);
56         ar& callback;
57         ar& context;
58     }
59     friend class boost::serialization::access;
60 };
61 
SessionInfo(std::shared_ptr<ServerSession> session,std::unique_ptr<SessionDataBase> data)62 SessionRequestHandler::SessionInfo::SessionInfo(std::shared_ptr<ServerSession> session,
63                                                 std::unique_ptr<SessionDataBase> data)
64     : session(std::move(session)), data(std::move(data)) {}
65 
ClientConnected(std::shared_ptr<ServerSession> server_session)66 void SessionRequestHandler::ClientConnected(std::shared_ptr<ServerSession> server_session) {
67     server_session->SetHleHandler(shared_from_this());
68     connected_sessions.emplace_back(std::move(server_session), MakeSessionData());
69 }
70 
ClientDisconnected(std::shared_ptr<ServerSession> server_session)71 void SessionRequestHandler::ClientDisconnected(std::shared_ptr<ServerSession> server_session) {
72     server_session->SetHleHandler(nullptr);
73     connected_sessions.erase(
74         std::remove_if(connected_sessions.begin(), connected_sessions.end(),
75                        [&](const SessionInfo& info) { return info.session == server_session; }),
76         connected_sessions.end());
77 }
78 
SleepClientThread(const std::string & reason,std::chrono::nanoseconds timeout,std::shared_ptr<WakeupCallback> callback)79 std::shared_ptr<Event> HLERequestContext::SleepClientThread(
80     const std::string& reason, std::chrono::nanoseconds timeout,
81     std::shared_ptr<WakeupCallback> callback) {
82     // Put the client thread to sleep until the wait event is signaled or the timeout expires.
83     thread->wakeup_callback = std::make_shared<ThreadCallback>(shared_from_this(), callback);
84 
85     auto event = kernel.CreateEvent(Kernel::ResetType::OneShot, "HLE Pause Event: " + reason);
86     thread->status = ThreadStatus::WaitHleEvent;
87     thread->wait_objects = {event};
88     event->AddWaitingThread(thread);
89 
90     if (timeout.count() > 0)
91         thread->WakeAfterDelay(timeout.count());
92 
93     return event;
94 }
95 
HLERequestContext()96 HLERequestContext::HLERequestContext() : kernel(Core::Global<KernelSystem>()) {}
97 
HLERequestContext(KernelSystem & kernel,std::shared_ptr<ServerSession> session,std::shared_ptr<Thread> thread)98 HLERequestContext::HLERequestContext(KernelSystem& kernel, std::shared_ptr<ServerSession> session,
99                                      std::shared_ptr<Thread> thread)
100     : kernel(kernel), session(std::move(session)), thread(thread) {
101     cmd_buf[0] = 0;
102 }
103 
104 HLERequestContext::~HLERequestContext() = default;
105 
GetIncomingHandle(u32 id_from_cmdbuf) const106 std::shared_ptr<Object> HLERequestContext::GetIncomingHandle(u32 id_from_cmdbuf) const {
107     ASSERT(id_from_cmdbuf < request_handles.size());
108     return request_handles[id_from_cmdbuf];
109 }
110 
AddOutgoingHandle(std::shared_ptr<Object> object)111 u32 HLERequestContext::AddOutgoingHandle(std::shared_ptr<Object> object) {
112     request_handles.push_back(std::move(object));
113     return static_cast<u32>(request_handles.size() - 1);
114 }
115 
ClearIncomingObjects()116 void HLERequestContext::ClearIncomingObjects() {
117     request_handles.clear();
118 }
119 
GetStaticBuffer(u8 buffer_id) const120 const std::vector<u8>& HLERequestContext::GetStaticBuffer(u8 buffer_id) const {
121     return static_buffers[buffer_id];
122 }
123 
AddStaticBuffer(u8 buffer_id,std::vector<u8> data)124 void HLERequestContext::AddStaticBuffer(u8 buffer_id, std::vector<u8> data) {
125     static_buffers[buffer_id] = std::move(data);
126 }
127 
PopulateFromIncomingCommandBuffer(const u32_le * src_cmdbuf,std::shared_ptr<Process> src_process_)128 ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(
129     const u32_le* src_cmdbuf, std::shared_ptr<Process> src_process_) {
130     auto& src_process = *src_process_;
131     IPC::Header header{src_cmdbuf[0]};
132 
133     std::size_t untranslated_size = 1u + header.normal_params_size;
134     std::size_t command_size = untranslated_size + header.translate_params_size;
135     ASSERT(command_size <= IPC::COMMAND_BUFFER_LENGTH); // TODO(yuriks): Return error
136 
137     std::copy_n(src_cmdbuf, untranslated_size, cmd_buf.begin());
138 
139     const bool should_record = kernel.GetIPCRecorder().IsEnabled();
140 
141     std::vector<u32> untranslated_cmdbuf;
142     if (should_record) {
143         untranslated_cmdbuf = std::vector<u32>{src_cmdbuf, src_cmdbuf + command_size};
144     }
145 
146     std::size_t i = untranslated_size;
147     while (i < command_size) {
148         u32 descriptor = cmd_buf[i] = src_cmdbuf[i];
149         i += 1;
150 
151         switch (IPC::GetDescriptorType(descriptor)) {
152         case IPC::DescriptorType::CopyHandle:
153         case IPC::DescriptorType::MoveHandle: {
154             u32 num_handles = IPC::HandleNumberFromDesc(descriptor);
155             ASSERT(i + num_handles <= command_size); // TODO(yuriks): Return error
156             for (u32 j = 0; j < num_handles; ++j) {
157                 Handle handle = src_cmdbuf[i];
158                 std::shared_ptr<Object> object = nullptr;
159                 if (handle != 0) {
160                     object = src_process.handle_table.GetGeneric(handle);
161                     ASSERT(object != nullptr); // TODO(yuriks): Return error
162                     if (descriptor == IPC::DescriptorType::MoveHandle) {
163                         src_process.handle_table.Close(handle);
164                     }
165                 }
166 
167                 cmd_buf[i++] = AddOutgoingHandle(std::move(object));
168             }
169             break;
170         }
171         case IPC::DescriptorType::CallingPid: {
172             cmd_buf[i++] = src_process.process_id;
173             break;
174         }
175         case IPC::DescriptorType::StaticBuffer: {
176             VAddr source_address = src_cmdbuf[i];
177             IPC::StaticBufferDescInfo buffer_info{descriptor};
178 
179             // Copy the input buffer into our own vector and store it.
180             std::vector<u8> data(buffer_info.size);
181             kernel.memory.ReadBlock(src_process, source_address, data.data(), data.size());
182 
183             AddStaticBuffer(buffer_info.buffer_id, std::move(data));
184             cmd_buf[i++] = source_address;
185             break;
186         }
187         case IPC::DescriptorType::MappedBuffer: {
188             u32 next_id = static_cast<u32>(request_mapped_buffers.size());
189             request_mapped_buffers.emplace_back(kernel.memory, src_process_, descriptor,
190                                                 src_cmdbuf[i], next_id);
191             cmd_buf[i++] = next_id;
192             break;
193         }
194         default:
195             UNIMPLEMENTED_MSG("Unsupported handle translation: {:#010X}", descriptor);
196         }
197     }
198 
199     if (should_record) {
200         std::vector<u32> translated_cmdbuf{cmd_buf.begin(), cmd_buf.begin() + command_size};
201         kernel.GetIPCRecorder().SetRequestInfo(thread, std::move(untranslated_cmdbuf),
202                                                std::move(translated_cmdbuf));
203     }
204 
205     return RESULT_SUCCESS;
206 }
207 
WriteToOutgoingCommandBuffer(u32_le * dst_cmdbuf,Process & dst_process) const208 ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf,
209                                                            Process& dst_process) const {
210     IPC::Header header{cmd_buf[0]};
211 
212     std::size_t untranslated_size = 1u + header.normal_params_size;
213     std::size_t command_size = untranslated_size + header.translate_params_size;
214     ASSERT(command_size <= IPC::COMMAND_BUFFER_LENGTH);
215 
216     std::copy_n(cmd_buf.begin(), untranslated_size, dst_cmdbuf);
217 
218     const bool should_record = kernel.GetIPCRecorder().IsEnabled();
219 
220     std::vector<u32> untranslated_cmdbuf;
221     if (should_record) {
222         untranslated_cmdbuf = std::vector<u32>{cmd_buf.begin(), cmd_buf.begin() + command_size};
223     }
224 
225     std::size_t i = untranslated_size;
226     while (i < command_size) {
227         u32 descriptor = dst_cmdbuf[i] = cmd_buf[i];
228         i += 1;
229 
230         switch (IPC::GetDescriptorType(descriptor)) {
231         case IPC::DescriptorType::CopyHandle:
232         case IPC::DescriptorType::MoveHandle: {
233             // HLE services don't use handles, so we treat both CopyHandle and MoveHandle equally
234             u32 num_handles = IPC::HandleNumberFromDesc(descriptor);
235             ASSERT(i + num_handles <= command_size);
236             for (u32 j = 0; j < num_handles; ++j) {
237                 std::shared_ptr<Object> object = GetIncomingHandle(cmd_buf[i]);
238                 Handle handle = 0;
239                 if (object != nullptr) {
240                     // TODO(yuriks): Figure out the proper error handling for if this fails
241                     handle = dst_process.handle_table.Create(object).Unwrap();
242                 }
243                 dst_cmdbuf[i++] = handle;
244             }
245             break;
246         }
247         case IPC::DescriptorType::StaticBuffer: {
248             IPC::StaticBufferDescInfo buffer_info{descriptor};
249 
250             const auto& data = GetStaticBuffer(buffer_info.buffer_id);
251 
252             // Grab the address that the target thread set up to receive the response static buffer
253             // and write our data there. The static buffers area is located right after the command
254             // buffer area.
255             std::size_t static_buffer_offset =
256                 IPC::COMMAND_BUFFER_LENGTH + 2 * buffer_info.buffer_id;
257             IPC::StaticBufferDescInfo target_descriptor{dst_cmdbuf[static_buffer_offset]};
258             VAddr target_address = dst_cmdbuf[static_buffer_offset + 1];
259 
260             ASSERT_MSG(target_descriptor.size >= data.size(), "Static buffer data is too big");
261 
262             kernel.memory.WriteBlock(dst_process, target_address, data.data(), data.size());
263 
264             dst_cmdbuf[i++] = target_address;
265             break;
266         }
267         case IPC::DescriptorType::MappedBuffer: {
268             VAddr addr = request_mapped_buffers[cmd_buf[i]].address;
269             dst_cmdbuf[i++] = addr;
270             break;
271         }
272         default:
273             UNIMPLEMENTED_MSG("Unsupported handle translation: {:#010X}", descriptor);
274         }
275     }
276 
277     if (should_record) {
278         std::vector<u32> translated_cmdbuf{dst_cmdbuf, dst_cmdbuf + command_size};
279         kernel.GetIPCRecorder().SetReplyInfo(thread, std::move(untranslated_cmdbuf),
280                                              std::move(translated_cmdbuf));
281     }
282 
283     return RESULT_SUCCESS;
284 }
285 
GetMappedBuffer(u32 id_from_cmdbuf)286 MappedBuffer& HLERequestContext::GetMappedBuffer(u32 id_from_cmdbuf) {
287     ASSERT_MSG(id_from_cmdbuf < request_mapped_buffers.size(), "Mapped Buffer ID out of range!");
288     return request_mapped_buffers[id_from_cmdbuf];
289 }
290 
ReportUnimplemented() const291 void HLERequestContext::ReportUnimplemented() const {
292     if (kernel.GetIPCRecorder().IsEnabled()) {
293         kernel.GetIPCRecorder().SetHLEUnimplemented(thread);
294     }
295 }
296 
MappedBuffer()297 MappedBuffer::MappedBuffer() : memory(&Core::Global<Core::System>().Memory()) {}
298 
MappedBuffer(Memory::MemorySystem & memory,std::shared_ptr<Process> process,u32 descriptor,VAddr address,u32 id)299 MappedBuffer::MappedBuffer(Memory::MemorySystem& memory, std::shared_ptr<Process> process,
300                            u32 descriptor, VAddr address, u32 id)
301     : memory(&memory), id(id), address(address), process(std::move(process)) {
302     IPC::MappedBufferDescInfo desc{descriptor};
303     size = desc.size;
304     perms = desc.perms;
305 }
306 
Read(void * dest_buffer,std::size_t offset,std::size_t size)307 void MappedBuffer::Read(void* dest_buffer, std::size_t offset, std::size_t size) {
308     ASSERT(perms & IPC::R);
309     ASSERT(offset + size <= this->size);
310     memory->ReadBlock(*process, address + static_cast<VAddr>(offset), dest_buffer, size);
311 }
312 
Write(const void * src_buffer,std::size_t offset,std::size_t size)313 void MappedBuffer::Write(const void* src_buffer, std::size_t offset, std::size_t size) {
314     ASSERT(perms & IPC::W);
315     ASSERT(offset + size <= this->size);
316     memory->WriteBlock(*process, address + static_cast<VAddr>(offset), src_buffer, size);
317 }
318 
319 } // namespace Kernel
320 
321 SERIALIZE_EXPORT_IMPL(Kernel::HLERequestContext::ThreadCallback)
322