1 // Copyright 2019 yuzu emulator team
2 // Licensed under GPLv2 or any later version
3 // Refer to the license.txt file included.
4
5 #include "common/logging/log.h"
6 #include "core/core.h"
7 #include "core/core_timing.h"
8 #include "core/core_timing_util.h"
9 #include "core/hardware_properties.h"
10 #include "core/hle/ipc_helpers.h"
11 #include "core/hle/kernel/client_port.h"
12 #include "core/hle/kernel/client_session.h"
13 #include "core/hle/kernel/kernel.h"
14 #include "core/hle/kernel/scheduler.h"
15 #include "core/hle/service/time/interface.h"
16 #include "core/hle/service/time/time.h"
17 #include "core/hle/service/time/time_sharedmemory.h"
18 #include "core/hle/service/time/time_zone_service.h"
19
20 namespace Service::Time {
21
22 class ISystemClock final : public ServiceFramework<ISystemClock> {
23 public:
ISystemClock(Clock::SystemClockCore & clock_core_,Core::System & system_)24 explicit ISystemClock(Clock::SystemClockCore& clock_core_, Core::System& system_)
25 : ServiceFramework{system_, "ISystemClock"}, clock_core{clock_core_} {
26 // clang-format off
27 static const FunctionInfo functions[] = {
28 {0, &ISystemClock::GetCurrentTime, "GetCurrentTime"},
29 {1, nullptr, "SetCurrentTime"},
30 {2, &ISystemClock::GetSystemClockContext, "GetSystemClockContext"},
31 {3, nullptr, "SetSystemClockContext"},
32 {4, nullptr, "GetOperationEventReadableHandle"},
33 };
34 // clang-format on
35
36 RegisterHandlers(functions);
37 }
38
39 private:
GetCurrentTime(Kernel::HLERequestContext & ctx)40 void GetCurrentTime(Kernel::HLERequestContext& ctx) {
41 LOG_DEBUG(Service_Time, "called");
42
43 if (!clock_core.IsInitialized()) {
44 IPC::ResponseBuilder rb{ctx, 2};
45 rb.Push(ERROR_UNINITIALIZED_CLOCK);
46 return;
47 }
48
49 s64 posix_time{};
50 if (const ResultCode result{clock_core.GetCurrentTime(system, posix_time)};
51 result.IsError()) {
52 IPC::ResponseBuilder rb{ctx, 2};
53 rb.Push(result);
54 return;
55 }
56
57 IPC::ResponseBuilder rb{ctx, 4};
58 rb.Push(RESULT_SUCCESS);
59 rb.Push<s64>(posix_time);
60 }
61
GetSystemClockContext(Kernel::HLERequestContext & ctx)62 void GetSystemClockContext(Kernel::HLERequestContext& ctx) {
63 LOG_DEBUG(Service_Time, "called");
64
65 if (!clock_core.IsInitialized()) {
66 IPC::ResponseBuilder rb{ctx, 2};
67 rb.Push(ERROR_UNINITIALIZED_CLOCK);
68 return;
69 }
70
71 Clock::SystemClockContext system_clock_context{};
72 if (const ResultCode result{clock_core.GetClockContext(system, system_clock_context)};
73 result.IsError()) {
74 IPC::ResponseBuilder rb{ctx, 2};
75 rb.Push(result);
76 return;
77 }
78
79 IPC::ResponseBuilder rb{ctx, sizeof(Clock::SystemClockContext) / 4 + 2};
80 rb.Push(RESULT_SUCCESS);
81 rb.PushRaw(system_clock_context);
82 }
83
84 Clock::SystemClockCore& clock_core;
85 };
86
87 class ISteadyClock final : public ServiceFramework<ISteadyClock> {
88 public:
ISteadyClock(Clock::SteadyClockCore & clock_core_,Core::System & system_)89 explicit ISteadyClock(Clock::SteadyClockCore& clock_core_, Core::System& system_)
90 : ServiceFramework{system_, "ISteadyClock"}, clock_core{clock_core_} {
91 static const FunctionInfo functions[] = {
92 {0, &ISteadyClock::GetCurrentTimePoint, "GetCurrentTimePoint"},
93 {2, nullptr, "GetTestOffset"},
94 {3, nullptr, "SetTestOffset"},
95 {100, nullptr, "GetRtcValue"},
96 {101, nullptr, "IsRtcResetDetected"},
97 {102, nullptr, "GetSetupResultValue"},
98 {200, nullptr, "GetInternalOffset"},
99 {201, nullptr, "SetInternalOffset"},
100 };
101 RegisterHandlers(functions);
102 }
103
104 private:
GetCurrentTimePoint(Kernel::HLERequestContext & ctx)105 void GetCurrentTimePoint(Kernel::HLERequestContext& ctx) {
106 LOG_DEBUG(Service_Time, "called");
107
108 if (!clock_core.IsInitialized()) {
109 IPC::ResponseBuilder rb{ctx, 2};
110 rb.Push(ERROR_UNINITIALIZED_CLOCK);
111 return;
112 }
113
114 const Clock::SteadyClockTimePoint time_point{clock_core.GetCurrentTimePoint(system)};
115 IPC::ResponseBuilder rb{ctx, (sizeof(Clock::SteadyClockTimePoint) / 4) + 2};
116 rb.Push(RESULT_SUCCESS);
117 rb.PushRaw(time_point);
118 }
119
120 Clock::SteadyClockCore& clock_core;
121 };
122
GetClockSnapshotFromSystemClockContextInternal(Kernel::Thread * thread,Clock::SystemClockContext user_context,Clock::SystemClockContext network_context,u8 type,Clock::ClockSnapshot & clock_snapshot)123 ResultCode Module::Interface::GetClockSnapshotFromSystemClockContextInternal(
124 Kernel::Thread* thread, Clock::SystemClockContext user_context,
125 Clock::SystemClockContext network_context, u8 type, Clock::ClockSnapshot& clock_snapshot) {
126
127 auto& time_manager{system.GetTimeManager()};
128
129 clock_snapshot.is_automatic_correction_enabled =
130 time_manager.GetStandardUserSystemClockCore().IsAutomaticCorrectionEnabled();
131 clock_snapshot.user_context = user_context;
132 clock_snapshot.network_context = network_context;
133
134 if (const ResultCode result{
135 time_manager.GetTimeZoneContentManager().GetTimeZoneManager().GetDeviceLocationName(
136 clock_snapshot.location_name)};
137 result != RESULT_SUCCESS) {
138 return result;
139 }
140
141 const auto current_time_point{
142 time_manager.GetStandardSteadyClockCore().GetCurrentTimePoint(system)};
143 if (const ResultCode result{Clock::ClockSnapshot::GetCurrentTime(
144 clock_snapshot.user_time, current_time_point, clock_snapshot.user_context)};
145 result != RESULT_SUCCESS) {
146 return result;
147 }
148
149 TimeZone::CalendarInfo userCalendarInfo{};
150 if (const ResultCode result{
151 time_manager.GetTimeZoneContentManager().GetTimeZoneManager().ToCalendarTimeWithMyRules(
152 clock_snapshot.user_time, userCalendarInfo)};
153 result != RESULT_SUCCESS) {
154 return result;
155 }
156
157 clock_snapshot.user_calendar_time = userCalendarInfo.time;
158 clock_snapshot.user_calendar_additional_time = userCalendarInfo.additiona_info;
159
160 if (Clock::ClockSnapshot::GetCurrentTime(clock_snapshot.network_time, current_time_point,
161 clock_snapshot.network_context) != RESULT_SUCCESS) {
162 clock_snapshot.network_time = 0;
163 }
164
165 TimeZone::CalendarInfo networkCalendarInfo{};
166 if (const ResultCode result{
167 time_manager.GetTimeZoneContentManager().GetTimeZoneManager().ToCalendarTimeWithMyRules(
168 clock_snapshot.network_time, networkCalendarInfo)};
169 result != RESULT_SUCCESS) {
170 return result;
171 }
172
173 clock_snapshot.network_calendar_time = networkCalendarInfo.time;
174 clock_snapshot.network_calendar_additional_time = networkCalendarInfo.additiona_info;
175 clock_snapshot.type = type;
176
177 return RESULT_SUCCESS;
178 }
179
GetStandardUserSystemClock(Kernel::HLERequestContext & ctx)180 void Module::Interface::GetStandardUserSystemClock(Kernel::HLERequestContext& ctx) {
181 LOG_DEBUG(Service_Time, "called");
182 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
183 rb.Push(RESULT_SUCCESS);
184 rb.PushIpcInterface<ISystemClock>(system.GetTimeManager().GetStandardUserSystemClockCore(),
185 system);
186 }
187
GetStandardNetworkSystemClock(Kernel::HLERequestContext & ctx)188 void Module::Interface::GetStandardNetworkSystemClock(Kernel::HLERequestContext& ctx) {
189 LOG_DEBUG(Service_Time, "called");
190 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
191 rb.Push(RESULT_SUCCESS);
192 rb.PushIpcInterface<ISystemClock>(system.GetTimeManager().GetStandardNetworkSystemClockCore(),
193 system);
194 }
195
GetStandardSteadyClock(Kernel::HLERequestContext & ctx)196 void Module::Interface::GetStandardSteadyClock(Kernel::HLERequestContext& ctx) {
197 LOG_DEBUG(Service_Time, "called");
198 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
199 rb.Push(RESULT_SUCCESS);
200 rb.PushIpcInterface<ISteadyClock>(system.GetTimeManager().GetStandardSteadyClockCore(), system);
201 }
202
GetTimeZoneService(Kernel::HLERequestContext & ctx)203 void Module::Interface::GetTimeZoneService(Kernel::HLERequestContext& ctx) {
204 LOG_DEBUG(Service_Time, "called");
205 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
206 rb.Push(RESULT_SUCCESS);
207 rb.PushIpcInterface<ITimeZoneService>(system,
208 system.GetTimeManager().GetTimeZoneContentManager());
209 }
210
GetStandardLocalSystemClock(Kernel::HLERequestContext & ctx)211 void Module::Interface::GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx) {
212 LOG_DEBUG(Service_Time, "called");
213 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
214 rb.Push(RESULT_SUCCESS);
215 rb.PushIpcInterface<ISystemClock>(system.GetTimeManager().GetStandardLocalSystemClockCore(),
216 system);
217 }
218
IsStandardNetworkSystemClockAccuracySufficient(Kernel::HLERequestContext & ctx)219 void Module::Interface::IsStandardNetworkSystemClockAccuracySufficient(
220 Kernel::HLERequestContext& ctx) {
221 LOG_DEBUG(Service_Time, "called");
222 auto& clock_core{system.GetTimeManager().GetStandardNetworkSystemClockCore()};
223 IPC::ResponseBuilder rb{ctx, 3};
224 rb.Push(RESULT_SUCCESS);
225 rb.Push<u32>(clock_core.IsStandardNetworkSystemClockAccuracySufficient(system));
226 }
227
CalculateMonotonicSystemClockBaseTimePoint(Kernel::HLERequestContext & ctx)228 void Module::Interface::CalculateMonotonicSystemClockBaseTimePoint(Kernel::HLERequestContext& ctx) {
229 LOG_DEBUG(Service_Time, "called");
230
231 auto& steady_clock_core{system.GetTimeManager().GetStandardSteadyClockCore()};
232 if (!steady_clock_core.IsInitialized()) {
233 IPC::ResponseBuilder rb{ctx, 2};
234 rb.Push(ERROR_UNINITIALIZED_CLOCK);
235 return;
236 }
237
238 IPC::RequestParser rp{ctx};
239 const auto context{rp.PopRaw<Clock::SystemClockContext>()};
240 const auto current_time_point{steady_clock_core.GetCurrentTimePoint(system)};
241
242 if (current_time_point.clock_source_id == context.steady_time_point.clock_source_id) {
243 const auto ticks{Clock::TimeSpanType::FromTicks(system.CoreTiming().GetClockTicks(),
244 Core::Hardware::CNTFREQ)};
245 const s64 base_time_point{context.offset + current_time_point.time_point -
246 ticks.ToSeconds()};
247 IPC::ResponseBuilder rb{ctx, (sizeof(s64) / 4) + 2};
248 rb.Push(RESULT_SUCCESS);
249 rb.PushRaw(base_time_point);
250 return;
251 }
252
253 IPC::ResponseBuilder rb{ctx, 2};
254 rb.Push(ERROR_TIME_MISMATCH);
255 }
256
GetClockSnapshot(Kernel::HLERequestContext & ctx)257 void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
258 LOG_DEBUG(Service_Time, "called");
259 IPC::RequestParser rp{ctx};
260 const auto type{rp.PopRaw<u8>()};
261
262 Clock::SystemClockContext user_context{};
263 if (const ResultCode result{
264 system.GetTimeManager().GetStandardUserSystemClockCore().GetClockContext(system,
265 user_context)};
266 result.IsError()) {
267 IPC::ResponseBuilder rb{ctx, 2};
268 rb.Push(result);
269 return;
270 }
271 Clock::SystemClockContext network_context{};
272 if (const ResultCode result{
273 system.GetTimeManager().GetStandardNetworkSystemClockCore().GetClockContext(
274 system, network_context)};
275 result.IsError()) {
276 IPC::ResponseBuilder rb{ctx, 2};
277 rb.Push(result);
278 return;
279 }
280
281 Clock::ClockSnapshot clock_snapshot{};
282 if (const ResultCode result{GetClockSnapshotFromSystemClockContextInternal(
283 &ctx.GetThread(), user_context, network_context, type, clock_snapshot)};
284 result.IsError()) {
285 IPC::ResponseBuilder rb{ctx, 2};
286 rb.Push(result);
287 return;
288 }
289
290 IPC::ResponseBuilder rb{ctx, 2};
291 rb.Push(RESULT_SUCCESS);
292 ctx.WriteBuffer(clock_snapshot);
293 }
294
GetClockSnapshotFromSystemClockContext(Kernel::HLERequestContext & ctx)295 void Module::Interface::GetClockSnapshotFromSystemClockContext(Kernel::HLERequestContext& ctx) {
296 LOG_DEBUG(Service_Time, "called");
297 IPC::RequestParser rp{ctx};
298 const auto type{rp.PopRaw<u8>()};
299 rp.AlignWithPadding();
300
301 const Clock::SystemClockContext user_context{rp.PopRaw<Clock::SystemClockContext>()};
302 const Clock::SystemClockContext network_context{rp.PopRaw<Clock::SystemClockContext>()};
303
304 Clock::ClockSnapshot clock_snapshot{};
305 if (const ResultCode result{GetClockSnapshotFromSystemClockContextInternal(
306 &ctx.GetThread(), user_context, network_context, type, clock_snapshot)};
307 result != RESULT_SUCCESS) {
308 IPC::ResponseBuilder rb{ctx, 2};
309 rb.Push(result);
310 return;
311 }
312
313 IPC::ResponseBuilder rb{ctx, 2};
314 rb.Push(RESULT_SUCCESS);
315 ctx.WriteBuffer(clock_snapshot);
316 }
317
CalculateStandardUserSystemClockDifferenceByUser(Kernel::HLERequestContext & ctx)318 void Module::Interface::CalculateStandardUserSystemClockDifferenceByUser(
319 Kernel::HLERequestContext& ctx) {
320 LOG_DEBUG(Service_Time, "called");
321
322 IPC::RequestParser rp{ctx};
323 const auto snapshot_a = rp.PopRaw<Clock::ClockSnapshot>();
324 const auto snapshot_b = rp.PopRaw<Clock::ClockSnapshot>();
325
326 auto time_span_type{Clock::TimeSpanType::FromSeconds(snapshot_b.user_context.offset -
327 snapshot_a.user_context.offset)};
328
329 if ((snapshot_b.user_context.steady_time_point.clock_source_id !=
330 snapshot_a.user_context.steady_time_point.clock_source_id) ||
331 (snapshot_b.is_automatic_correction_enabled &&
332 snapshot_a.is_automatic_correction_enabled)) {
333 time_span_type.nanoseconds = 0;
334 }
335
336 IPC::ResponseBuilder rb{ctx, (sizeof(s64) / 4) + 2};
337 rb.Push(RESULT_SUCCESS);
338 rb.PushRaw(time_span_type.nanoseconds);
339 }
340
CalculateSpanBetween(Kernel::HLERequestContext & ctx)341 void Module::Interface::CalculateSpanBetween(Kernel::HLERequestContext& ctx) {
342 LOG_DEBUG(Service_Time, "called");
343
344 IPC::RequestParser rp{ctx};
345 const auto snapshot_a = rp.PopRaw<Clock::ClockSnapshot>();
346 const auto snapshot_b = rp.PopRaw<Clock::ClockSnapshot>();
347
348 Clock::TimeSpanType time_span_type{};
349 s64 span{};
350 if (const ResultCode result{snapshot_a.steady_clock_time_point.GetSpanBetween(
351 snapshot_b.steady_clock_time_point, span)};
352 result != RESULT_SUCCESS) {
353 if (snapshot_a.network_time && snapshot_b.network_time) {
354 time_span_type =
355 Clock::TimeSpanType::FromSeconds(snapshot_b.network_time - snapshot_a.network_time);
356 } else {
357 IPC::ResponseBuilder rb{ctx, 2};
358 rb.Push(ERROR_TIME_NOT_FOUND);
359 return;
360 }
361 } else {
362 time_span_type = Clock::TimeSpanType::FromSeconds(span);
363 }
364
365 IPC::ResponseBuilder rb{ctx, (sizeof(s64) / 4) + 2};
366 rb.Push(RESULT_SUCCESS);
367 rb.PushRaw(time_span_type.nanoseconds);
368 }
369
GetSharedMemoryNativeHandle(Kernel::HLERequestContext & ctx)370 void Module::Interface::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) {
371 LOG_DEBUG(Service_Time, "called");
372 IPC::ResponseBuilder rb{ctx, 2, 1};
373 rb.Push(RESULT_SUCCESS);
374 rb.PushCopyObjects(SharedFrom(&system.Kernel().GetTimeSharedMem()));
375 }
376
Interface(std::shared_ptr<Module> module_,Core::System & system_,const char * name)377 Module::Interface::Interface(std::shared_ptr<Module> module_, Core::System& system_,
378 const char* name)
379 : ServiceFramework{system_, name}, module{std::move(module_)} {}
380
381 Module::Interface::~Interface() = default;
382
InstallInterfaces(Core::System & system)383 void InstallInterfaces(Core::System& system) {
384 auto module{std::make_shared<Module>()};
385 std::make_shared<Time>(module, system, "time:a")->InstallAsService(system.ServiceManager());
386 std::make_shared<Time>(module, system, "time:s")->InstallAsService(system.ServiceManager());
387 std::make_shared<Time>(module, system, "time:u")->InstallAsService(system.ServiceManager());
388 }
389
390 } // namespace Service::Time
391