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