diff options
Diffstat (limited to 'src/core')
171 files changed, 9390 insertions, 4698 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 16ddb5e90..45a0d8746 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -37,6 +37,8 @@ add_library(core STATIC debugger/gdbstub_arch.h debugger/gdbstub.cpp debugger/gdbstub.h + device_memory_manager.h + device_memory_manager.inc device_memory.cpp device_memory.h file_sys/fssystem/fs_i_storage.h @@ -470,6 +472,8 @@ add_library(core STATIC hle/service/caps/caps_types.h hle/service/caps/caps_u.cpp hle/service/caps/caps_u.h + hle/service/cmif_serialization.h + hle/service/cmif_types.h hle/service/erpt/erpt.cpp hle/service/erpt/erpt.h hle/service/es/es.cpp @@ -513,6 +517,24 @@ add_library(core STATIC hle/service/glue/glue_manager.h hle/service/glue/notif.cpp hle/service/glue/notif.h + hle/service/glue/time/alarm_worker.cpp + hle/service/glue/time/alarm_worker.h + hle/service/glue/time/file_timestamp_worker.cpp + hle/service/glue/time/file_timestamp_worker.h + hle/service/glue/time/manager.cpp + hle/service/glue/time/manager.h + hle/service/glue/time/pm_state_change_handler.cpp + hle/service/glue/time/pm_state_change_handler.h + hle/service/glue/time/standard_steady_clock_resource.cpp + hle/service/glue/time/standard_steady_clock_resource.h + hle/service/glue/time/static.cpp + hle/service/glue/time/static.h + hle/service/glue/time/time_zone.cpp + hle/service/glue/time/time_zone.h + hle/service/glue/time/time_zone_binary.cpp + hle/service/glue/time/time_zone_binary.h + hle/service/glue/time/worker.cpp + hle/service/glue/time/worker.h hle/service/grc/grc.cpp hle/service/grc/grc.h hle/service/hid/hid.cpp @@ -609,6 +631,8 @@ add_library(core STATIC hle/service/ns/pdm_qry.h hle/service/nvdrv/core/container.cpp hle/service/nvdrv/core/container.h + hle/service/nvdrv/core/heap_mapper.cpp + hle/service/nvdrv/core/heap_mapper.h hle/service/nvdrv/core/nvmap.cpp hle/service/nvdrv/core/nvmap.h hle/service/nvdrv/core/syncpoint_manager.cpp @@ -689,6 +713,46 @@ add_library(core STATIC hle/service/prepo/prepo.h hle/service/psc/psc.cpp hle/service/psc/psc.h + hle/service/psc/time/alarms.cpp + hle/service/psc/time/alarms.h + hle/service/psc/time/clocks/context_writers.cpp + hle/service/psc/time/clocks/context_writers.h + hle/service/psc/time/clocks/ephemeral_network_system_clock_core.h + hle/service/psc/time/clocks/standard_local_system_clock_core.cpp + hle/service/psc/time/clocks/standard_local_system_clock_core.h + hle/service/psc/time/clocks/standard_network_system_clock_core.cpp + hle/service/psc/time/clocks/standard_network_system_clock_core.h + hle/service/psc/time/clocks/standard_steady_clock_core.cpp + hle/service/psc/time/clocks/standard_steady_clock_core.h + hle/service/psc/time/clocks/standard_user_system_clock_core.cpp + hle/service/psc/time/clocks/standard_user_system_clock_core.h + hle/service/psc/time/clocks/steady_clock_core.h + hle/service/psc/time/clocks/system_clock_core.cpp + hle/service/psc/time/clocks/system_clock_core.h + hle/service/psc/time/clocks/tick_based_steady_clock_core.cpp + hle/service/psc/time/clocks/tick_based_steady_clock_core.h + hle/service/psc/time/common.cpp + hle/service/psc/time/common.h + hle/service/psc/time/errors.h + hle/service/psc/time/shared_memory.cpp + hle/service/psc/time/shared_memory.h + hle/service/psc/time/static.cpp + hle/service/psc/time/static.h + hle/service/psc/time/manager.h + hle/service/psc/time/power_state_service.cpp + hle/service/psc/time/power_state_service.h + hle/service/psc/time/service_manager.cpp + hle/service/psc/time/service_manager.h + hle/service/psc/time/steady_clock.cpp + hle/service/psc/time/steady_clock.h + hle/service/psc/time/system_clock.cpp + hle/service/psc/time/system_clock.h + hle/service/psc/time/time_zone.cpp + hle/service/psc/time/time_zone.h + hle/service/psc/time/time_zone_service.cpp + hle/service/psc/time/time_zone_service.h + hle/service/psc/time/power_state_request_manager.cpp + hle/service/psc/time/power_state_request_manager.h hle/service/ptm/psm.cpp hle/service/ptm/psm.h hle/service/ptm/ptm.cpp @@ -756,40 +820,6 @@ add_library(core STATIC hle/service/ssl/ssl.cpp hle/service/ssl/ssl.h hle/service/ssl/ssl_backend.h - hle/service/time/clock_types.h - hle/service/time/ephemeral_network_system_clock_context_writer.h - hle/service/time/ephemeral_network_system_clock_core.h - hle/service/time/errors.h - hle/service/time/local_system_clock_context_writer.h - hle/service/time/network_system_clock_context_writer.h - hle/service/time/standard_local_system_clock_core.h - hle/service/time/standard_network_system_clock_core.h - hle/service/time/standard_steady_clock_core.cpp - hle/service/time/standard_steady_clock_core.h - hle/service/time/standard_user_system_clock_core.cpp - hle/service/time/standard_user_system_clock_core.h - hle/service/time/steady_clock_core.h - hle/service/time/system_clock_context_update_callback.cpp - hle/service/time/system_clock_context_update_callback.h - hle/service/time/system_clock_core.cpp - hle/service/time/system_clock_core.h - hle/service/time/tick_based_steady_clock_core.cpp - hle/service/time/tick_based_steady_clock_core.h - hle/service/time/time.cpp - hle/service/time/time.h - hle/service/time/time_interface.cpp - hle/service/time/time_interface.h - hle/service/time/time_manager.cpp - hle/service/time/time_manager.h - hle/service/time/time_sharedmemory.cpp - hle/service/time/time_sharedmemory.h - hle/service/time/time_zone_content_manager.cpp - hle/service/time/time_zone_content_manager.h - hle/service/time/time_zone_manager.cpp - hle/service/time/time_zone_manager.h - hle/service/time/time_zone_service.cpp - hle/service/time/time_zone_service.h - hle/service/time/time_zone_types.h hle/service/usb/usb.cpp hle/service/usb/usb.h hle/service/vi/display/vi_display.cpp @@ -870,7 +900,7 @@ endif() create_target_directory_groups(core) -target_link_libraries(core PUBLIC common PRIVATE audio_core hid_core network video_core nx_tzdb) +target_link_libraries(core PUBLIC common PRIVATE audio_core hid_core network video_core nx_tzdb tz) target_link_libraries(core PUBLIC Boost::headers PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls RenderDoc::API) if (MINGW) target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY}) diff --git a/src/core/core.cpp b/src/core/core.cpp index 461eea9c8..dd9de948c 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -28,6 +28,7 @@ #include "core/file_sys/savedata_factory.h" #include "core/file_sys/vfs_concat.h" #include "core/file_sys/vfs_real.h" +#include "core/gpu_dirty_memory_manager.h" #include "core/hle/kernel/k_memory_manager.h" #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_resource_limit.h" @@ -39,9 +40,14 @@ #include "core/hle/service/apm/apm_controller.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/glue/glue_manager.h" +#include "core/hle/service/glue/time/static.h" +#include "core/hle/service/psc/time/static.h" +#include "core/hle/service/psc/time/steady_clock.h" +#include "core/hle/service/psc/time/system_clock.h" +#include "core/hle/service/psc/time/time_zone_service.h" #include "core/hle/service/service.h" +#include "core/hle/service/set/system_settings_server.h" #include "core/hle/service/sm/sm.h" -#include "core/hle/service/time/time_manager.h" #include "core/internal_network/network.h" #include "core/loader/loader.h" #include "core/memory.h" @@ -129,8 +135,8 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, struct System::Impl { explicit Impl(System& system) - : kernel{system}, fs_controller{system}, hid_core{}, room_network{}, cpu_manager{system}, - reporter{system}, applet_manager{system}, profile_manager{}, time_manager{system} {} + : kernel{system}, fs_controller{system}, hid_core{}, room_network{}, + cpu_manager{system}, reporter{system}, applet_manager{system}, profile_manager{} {} void Initialize(System& system) { device_memory = std::make_unique<Core::DeviceMemory>(); @@ -142,8 +148,6 @@ struct System::Impl { core_timing.SetMulticore(is_multicore); core_timing.Initialize([&system]() { system.RegisterHostThread(); }); - RefreshTime(); - // Create a default fs if one doesn't already exist. if (virtual_filesystem == nullptr) { virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>(); @@ -181,14 +185,57 @@ struct System::Impl { Initialize(system); } - void RefreshTime() { + void RefreshTime(System& system) { + if (!system.IsPoweredOn()) { + return; + } + + auto settings_service = + system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", + true); + auto static_service_a = + system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:a", true); + + auto static_service_s = + system.ServiceManager().GetService<Service::PSC::Time::StaticService>("time:s", true); + + std::shared_ptr<Service::PSC::Time::SystemClock> user_clock; + static_service_a->GetStandardUserSystemClock(user_clock); + + std::shared_ptr<Service::PSC::Time::SystemClock> local_clock; + static_service_a->GetStandardLocalSystemClock(local_clock); + + std::shared_ptr<Service::PSC::Time::SystemClock> network_clock; + static_service_s->GetStandardNetworkSystemClock(network_clock); + + std::shared_ptr<Service::Glue::Time::TimeZoneService> timezone_service; + static_service_a->GetTimeZoneService(timezone_service); + + Service::PSC::Time::LocationName name{}; + auto new_name = Settings::GetTimeZoneString(Settings::values.time_zone_index.GetValue()); + std::memcpy(name.name.data(), new_name.data(), std::min(name.name.size(), new_name.size())); + + timezone_service->SetDeviceLocation(name); + + u64 time_offset = 0; + if (Settings::values.custom_rtc_enabled) { + time_offset = Settings::values.custom_rtc_offset.GetValue(); + } + const auto posix_time = std::chrono::system_clock::now().time_since_epoch(); - const auto current_time = - std::chrono::duration_cast<std::chrono::seconds>(posix_time).count(); - Settings::values.custom_rtc_differential = - (Settings::values.custom_rtc_enabled ? Settings::values.custom_rtc.GetValue() - : current_time) - - current_time; + const u64 current_time = + +std::chrono::duration_cast<std::chrono::seconds>(posix_time).count(); + const u64 new_time = current_time + time_offset; + + Service::PSC::Time::SystemClockContext context{}; + settings_service->SetUserSystemClockContext(context); + user_clock->SetCurrentTime(new_time); + + local_clock->SetCurrentTime(new_time); + + network_clock->GetSystemClockContext(context); + settings_service->SetNetworkSystemClockContext(context); + network_clock->SetCurrentTime(new_time); } void Run() { @@ -264,9 +311,6 @@ struct System::Impl { service_manager = std::make_shared<Service::SM::ServiceManager>(kernel); services = std::make_unique<Service::Services>(service_manager, system); - // Initialize time manager, which must happen after kernel is created - time_manager.Initialize(); - is_powered_on = true; exit_locked = false; exit_requested = false; @@ -416,7 +460,6 @@ struct System::Impl { fs_controller.Reset(); cheat_engine.reset(); telemetry_session.reset(); - time_manager.Shutdown(); core_timing.ClearPendingEvents(); app_loader.reset(); audio_core.reset(); @@ -532,7 +575,6 @@ struct System::Impl { /// Service State Service::Glue::ARPManager arp_manager; Service::Account::ProfileManager profile_manager; - Service::Time::TimeManager time_manager; /// Service manager std::shared_ptr<Service::SM::ServiceManager> service_manager; @@ -565,6 +607,9 @@ struct System::Impl { std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{}; std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_cpu{}; + std::array<Core::GPUDirtyMemoryManager, Core::Hardware::NUM_CPU_CORES> + gpu_dirty_memory_managers; + std::deque<std::vector<u8>> user_channel; }; @@ -651,8 +696,14 @@ size_t System::GetCurrentHostThreadID() const { return impl->kernel.GetCurrentHostThreadID(); } -void System::GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback) { - return this->ApplicationProcess()->GatherGPUDirtyMemory(callback); +std::span<GPUDirtyMemoryManager> System::GetGPUDirtyMemoryManager() { + return impl->gpu_dirty_memory_managers; +} + +void System::GatherGPUDirtyMemory(std::function<void(PAddr, size_t)>& callback) { + for (auto& manager : impl->gpu_dirty_memory_managers) { + manager.Gather(callback); + } } PerfStatsResults System::GetAndResetPerfStats() { @@ -901,14 +952,6 @@ const Service::Account::ProfileManager& System::GetProfileManager() const { return impl->profile_manager; } -Service::Time::TimeManager& System::GetTimeManager() { - return impl->time_manager; -} - -const Service::Time::TimeManager& System::GetTimeManager() const { - return impl->time_manager; -} - void System::SetExitLocked(bool locked) { impl->exit_locked = locked; } @@ -1020,13 +1063,9 @@ void System::Exit() { } void System::ApplySettings() { - impl->RefreshTime(); + impl->RefreshTime(*this); if (IsPoweredOn()) { - if (Settings::values.custom_rtc_enabled) { - const s64 posix_time{Settings::values.custom_rtc.GetValue()}; - GetTimeManager().UpdateLocalSystemClockTime(posix_time); - } Renderer().RefreshBaseSettings(); } } diff --git a/src/core/core.h b/src/core/core.h index ba5add0dc..183410602 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -8,6 +8,7 @@ #include <functional> #include <memory> #include <mutex> +#include <span> #include <string> #include <vector> @@ -72,10 +73,6 @@ namespace SM { class ServiceManager; } // namespace SM -namespace Time { -class TimeManager; -} // namespace Time - } // namespace Service namespace Tegra { @@ -116,6 +113,7 @@ class CpuManager; class Debugger; class DeviceMemory; class ExclusiveMonitor; +class GPUDirtyMemoryManager; class PerfStats; class Reporter; class SpeedLimiter; @@ -224,7 +222,9 @@ public: /// Prepare the core emulation for a reschedule void PrepareReschedule(u32 core_index); - void GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback); + std::span<GPUDirtyMemoryManager> GetGPUDirtyMemoryManager(); + + void GatherGPUDirtyMemory(std::function<void(PAddr, size_t)>& callback); [[nodiscard]] size_t GetCurrentHostThreadID() const; @@ -377,9 +377,6 @@ public: [[nodiscard]] Service::Account::ProfileManager& GetProfileManager(); [[nodiscard]] const Service::Account::ProfileManager& GetProfileManager() const; - [[nodiscard]] Service::Time::TimeManager& GetTimeManager(); - [[nodiscard]] const Service::Time::TimeManager& GetTimeManager() const; - [[nodiscard]] Core::Debugger& GetDebugger(); [[nodiscard]] const Core::Debugger& GetDebugger() const; diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index fc536413b..1abfa920c 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -157,7 +157,7 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, } } - for (auto h : to_remove) { + for (auto& h : to_remove) { event_queue.erase(h); } diff --git a/src/core/device_memory.h b/src/core/device_memory.h index 13388b73e..11bf0e326 100644 --- a/src/core/device_memory.h +++ b/src/core/device_memory.h @@ -32,6 +32,12 @@ public: } template <typename T> + PAddr GetRawPhysicalAddr(const T* ptr) const { + return static_cast<PAddr>(reinterpret_cast<uintptr_t>(ptr) - + reinterpret_cast<uintptr_t>(buffer.BackingBasePointer())); + } + + template <typename T> T* GetPointer(Common::PhysicalAddress addr) { return reinterpret_cast<T*>(buffer.BackingBasePointer() + (GetInteger(addr) - DramMemoryMap::Base)); @@ -43,6 +49,16 @@ public: (GetInteger(addr) - DramMemoryMap::Base)); } + template <typename T> + T* GetPointerFromRaw(PAddr addr) { + return reinterpret_cast<T*>(buffer.BackingBasePointer() + addr); + } + + template <typename T> + const T* GetPointerFromRaw(PAddr addr) const { + return reinterpret_cast<T*>(buffer.BackingBasePointer() + addr); + } + Common::HostMemory buffer; }; diff --git a/src/core/device_memory_manager.h b/src/core/device_memory_manager.h new file mode 100644 index 000000000..ffeed46cc --- /dev/null +++ b/src/core/device_memory_manager.h @@ -0,0 +1,211 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> +#include <atomic> +#include <deque> +#include <memory> +#include <mutex> + +#include "common/common_types.h" +#include "common/scratch_buffer.h" +#include "common/virtual_buffer.h" + +namespace Core { + +constexpr size_t DEVICE_PAGEBITS = 12ULL; +constexpr size_t DEVICE_PAGESIZE = 1ULL << DEVICE_PAGEBITS; +constexpr size_t DEVICE_PAGEMASK = DEVICE_PAGESIZE - 1ULL; + +class DeviceMemory; + +namespace Memory { +class Memory; +} + +template <typename DTraits> +struct DeviceMemoryManagerAllocator; + +struct Asid { + size_t id; +}; + +template <typename Traits> +class DeviceMemoryManager { + using DeviceInterface = typename Traits::DeviceInterface; + using DeviceMethods = typename Traits::DeviceMethods; + +public: + DeviceMemoryManager(const DeviceMemory& device_memory); + ~DeviceMemoryManager(); + + void BindInterface(DeviceInterface* device_inter); + + DAddr Allocate(size_t size); + void AllocateFixed(DAddr start, size_t size); + void Free(DAddr start, size_t size); + + void Map(DAddr address, VAddr virtual_address, size_t size, Asid asid, bool track = false); + + void Unmap(DAddr address, size_t size); + + void TrackContinuityImpl(DAddr address, VAddr virtual_address, size_t size, Asid asid); + void TrackContinuity(DAddr address, VAddr virtual_address, size_t size, Asid asid) { + std::scoped_lock lk(mapping_guard); + TrackContinuityImpl(address, virtual_address, size, asid); + } + + // Write / Read + template <typename T> + T* GetPointer(DAddr address); + + template <typename T> + const T* GetPointer(DAddr address) const; + + template <typename Func> + void ApplyOpOnPAddr(PAddr address, Common::ScratchBuffer<u32>& buffer, Func&& operation) { + DAddr subbits = static_cast<DAddr>(address & page_mask); + const u32 base = compressed_device_addr[(address >> page_bits)]; + if ((base >> MULTI_FLAG_BITS) == 0) [[likely]] { + const DAddr d_address = (static_cast<DAddr>(base) << page_bits) + subbits; + operation(d_address); + return; + } + InnerGatherDeviceAddresses(buffer, address); + for (u32 value : buffer) { + operation((static_cast<DAddr>(value) << page_bits) + subbits); + } + } + + template <typename Func> + void ApplyOpOnPointer(const u8* p, Common::ScratchBuffer<u32>& buffer, Func&& operation) { + PAddr address = GetRawPhysicalAddr<u8>(p); + ApplyOpOnPAddr(address, buffer, operation); + } + + PAddr GetPhysicalRawAddressFromDAddr(DAddr address) const { + PAddr subbits = static_cast<PAddr>(address & page_mask); + auto paddr = compressed_physical_ptr[(address >> page_bits)]; + if (paddr == 0) { + return 0; + } + return (static_cast<PAddr>(paddr - 1) << page_bits) + subbits; + } + + template <typename T> + void Write(DAddr address, T value); + + template <typename T> + T Read(DAddr address) const; + + u8* GetSpan(const DAddr src_addr, const std::size_t size); + const u8* GetSpan(const DAddr src_addr, const std::size_t size) const; + + void ReadBlock(DAddr address, void* dest_pointer, size_t size); + void ReadBlockUnsafe(DAddr address, void* dest_pointer, size_t size); + void WriteBlock(DAddr address, const void* src_pointer, size_t size); + void WriteBlockUnsafe(DAddr address, const void* src_pointer, size_t size); + + Asid RegisterProcess(Memory::Memory* memory); + void UnregisterProcess(Asid id); + + void UpdatePagesCachedCount(DAddr addr, size_t size, s32 delta); + + static constexpr size_t AS_BITS = Traits::device_virtual_bits; + +private: + static constexpr size_t device_virtual_bits = Traits::device_virtual_bits; + static constexpr size_t device_as_size = 1ULL << device_virtual_bits; + static constexpr size_t physical_min_bits = 32; + static constexpr size_t physical_max_bits = 33; + static constexpr size_t page_bits = 12; + static constexpr size_t page_size = 1ULL << page_bits; + static constexpr size_t page_mask = page_size - 1ULL; + static constexpr u32 physical_address_base = 1U << page_bits; + static constexpr u32 MULTI_FLAG_BITS = 31; + static constexpr u32 MULTI_FLAG = 1U << MULTI_FLAG_BITS; + static constexpr u32 MULTI_MASK = ~MULTI_FLAG; + + template <typename T> + T* GetPointerFromRaw(PAddr addr) { + return reinterpret_cast<T*>(physical_base + addr); + } + + template <typename T> + const T* GetPointerFromRaw(PAddr addr) const { + return reinterpret_cast<T*>(physical_base + addr); + } + + template <typename T> + PAddr GetRawPhysicalAddr(const T* ptr) const { + return static_cast<PAddr>(reinterpret_cast<uintptr_t>(ptr) - physical_base); + } + + void WalkBlock(const DAddr addr, const std::size_t size, auto on_unmapped, auto on_memory, + auto increment); + + void InnerGatherDeviceAddresses(Common::ScratchBuffer<u32>& buffer, PAddr address); + + std::unique_ptr<DeviceMemoryManagerAllocator<Traits>> impl; + + const uintptr_t physical_base; + DeviceInterface* device_inter; + Common::VirtualBuffer<u32> compressed_physical_ptr; + Common::VirtualBuffer<u32> compressed_device_addr; + Common::VirtualBuffer<u32> continuity_tracker; + + // Process memory interfaces + + std::deque<size_t> id_pool; + std::deque<Memory::Memory*> registered_processes; + + // Memory protection management + + static constexpr size_t guest_max_as_bits = 39; + static constexpr size_t guest_as_size = 1ULL << guest_max_as_bits; + static constexpr size_t guest_mask = guest_as_size - 1ULL; + static constexpr size_t asid_start_bit = guest_max_as_bits; + + std::pair<Asid, VAddr> ExtractCPUBacking(size_t page_index) { + auto content = cpu_backing_address[page_index]; + const VAddr address = content & guest_mask; + const Asid asid{static_cast<size_t>(content >> asid_start_bit)}; + return std::make_pair(asid, address); + } + + void InsertCPUBacking(size_t page_index, VAddr address, Asid asid) { + cpu_backing_address[page_index] = address | (asid.id << asid_start_bit); + } + + Common::VirtualBuffer<VAddr> cpu_backing_address; + static constexpr size_t subentries = 8 / sizeof(u8); + static constexpr size_t subentries_mask = subentries - 1; + class CounterEntry final { + public: + CounterEntry() = default; + + std::atomic_uint8_t& Count(std::size_t page) { + return values[page & subentries_mask]; + } + + const std::atomic_uint8_t& Count(std::size_t page) const { + return values[page & subentries_mask]; + } + + private: + std::array<std::atomic_uint8_t, subentries> values{}; + }; + static_assert(sizeof(CounterEntry) == subentries * sizeof(u8), + "CounterEntry should be 8 bytes!"); + + static constexpr size_t num_counter_entries = + (1ULL << (device_virtual_bits - page_bits)) / subentries; + using CachedPages = std::array<CounterEntry, num_counter_entries>; + std::unique_ptr<CachedPages> cached_pages; + std::mutex counter_guard; + std::mutex mapping_guard; +}; + +} // namespace Core diff --git a/src/core/device_memory_manager.inc b/src/core/device_memory_manager.inc new file mode 100644 index 000000000..eab8a2731 --- /dev/null +++ b/src/core/device_memory_manager.inc @@ -0,0 +1,581 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <atomic> +#include <limits> +#include <memory> +#include <type_traits> + +#include "common/address_space.h" +#include "common/address_space.inc" +#include "common/alignment.h" +#include "common/assert.h" +#include "common/div_ceil.h" +#include "common/scope_exit.h" +#include "common/settings.h" +#include "core/device_memory.h" +#include "core/device_memory_manager.h" +#include "core/memory.h" + +namespace Core { + +namespace { + +class MultiAddressContainer { +public: + MultiAddressContainer() = default; + ~MultiAddressContainer() = default; + + void GatherValues(u32 start_entry, Common::ScratchBuffer<u32>& buffer) { + buffer.resize(8); + buffer.resize(0); + size_t index = 0; + const auto add_value = [&](u32 value) { + buffer.resize(index + 1); + buffer[index++] = value; + }; + + u32 iter_entry = start_entry; + Entry* current = &storage[iter_entry - 1]; + add_value(current->value); + while (current->next_entry != 0) { + iter_entry = current->next_entry; + current = &storage[iter_entry - 1]; + add_value(current->value); + } + } + + u32 Register(u32 value) { + return RegisterImplementation(value); + } + + void Register(u32 value, u32 start_entry) { + auto entry_id = RegisterImplementation(value); + u32 iter_entry = start_entry; + Entry* current = &storage[iter_entry - 1]; + while (current->next_entry != 0) { + iter_entry = current->next_entry; + current = &storage[iter_entry - 1]; + } + current->next_entry = entry_id; + } + + std::pair<bool, u32> Unregister(u32 value, u32 start_entry) { + u32 iter_entry = start_entry; + Entry* previous{}; + Entry* current = &storage[iter_entry - 1]; + Entry* next{}; + bool more_than_one_remaining = false; + u32 result_start{start_entry}; + size_t count = 0; + while (current->value != value) { + count++; + previous = current; + iter_entry = current->next_entry; + current = &storage[iter_entry - 1]; + } + // Find next + u32 next_entry = current->next_entry; + if (next_entry != 0) { + next = &storage[next_entry - 1]; + more_than_one_remaining = next->next_entry != 0 || previous != nullptr; + } + if (previous) { + previous->next_entry = next_entry; + } else { + result_start = next_entry; + } + free_entries.emplace_back(iter_entry); + return std::make_pair(more_than_one_remaining || count > 1, result_start); + } + + u32 ReleaseEntry(u32 start_entry) { + Entry* current = &storage[start_entry - 1]; + free_entries.emplace_back(start_entry); + return current->value; + } + +private: + u32 RegisterImplementation(u32 value) { + auto entry_id = GetNewEntry(); + auto& entry = storage[entry_id - 1]; + entry.next_entry = 0; + entry.value = value; + return entry_id; + } + u32 GetNewEntry() { + if (!free_entries.empty()) { + u32 result = free_entries.front(); + free_entries.pop_front(); + return result; + } + storage.emplace_back(); + u32 new_entry = static_cast<u32>(storage.size()); + return new_entry; + } + + struct Entry { + u32 next_entry{}; + u32 value{}; + }; + + std::deque<Entry> storage; + std::deque<u32> free_entries; +}; + +struct EmptyAllocator { + EmptyAllocator([[maybe_unused]] DAddr address) {} +}; + +} // namespace + +template <typename DTraits> +struct DeviceMemoryManagerAllocator { + static constexpr size_t device_virtual_bits = DTraits::device_virtual_bits; + static constexpr DAddr first_address = 1ULL << Memory::YUZU_PAGEBITS; + static constexpr DAddr max_device_area = 1ULL << device_virtual_bits; + + DeviceMemoryManagerAllocator() : main_allocator(first_address) {} + + Common::FlatAllocator<DAddr, 0, device_virtual_bits> main_allocator; + MultiAddressContainer multi_dev_address; + + /// Returns true when vaddr -> vaddr+size is fully contained in the buffer + template <bool pin_area> + [[nodiscard]] bool IsInBounds(VAddr addr, u64 size) const noexcept { + return addr >= 0 && addr + size <= max_device_area; + } + + DAddr Allocate(size_t size) { + return main_allocator.Allocate(size); + } + + void AllocateFixed(DAddr b_address, size_t b_size) { + main_allocator.AllocateFixed(b_address, b_size); + } + + void Free(DAddr b_address, size_t b_size) { + main_allocator.Free(b_address, b_size); + } +}; + +template <typename Traits> +DeviceMemoryManager<Traits>::DeviceMemoryManager(const DeviceMemory& device_memory_) + : physical_base{reinterpret_cast<const uintptr_t>(device_memory_.buffer.BackingBasePointer())}, + device_inter{nullptr}, compressed_physical_ptr(device_as_size >> Memory::YUZU_PAGEBITS), + compressed_device_addr(1ULL << ((Settings::values.memory_layout_mode.GetValue() == + Settings::MemoryLayout::Memory_4Gb + ? physical_min_bits + : physical_max_bits) - + Memory::YUZU_PAGEBITS)), + continuity_tracker(device_as_size >> Memory::YUZU_PAGEBITS), + cpu_backing_address(device_as_size >> Memory::YUZU_PAGEBITS) { + impl = std::make_unique<DeviceMemoryManagerAllocator<Traits>>(); + cached_pages = std::make_unique<CachedPages>(); + + const size_t total_virtual = device_as_size >> Memory::YUZU_PAGEBITS; + for (size_t i = 0; i < total_virtual; i++) { + compressed_physical_ptr[i] = 0; + continuity_tracker[i] = 1; + cpu_backing_address[i] = 0; + } + const size_t total_phys = 1ULL << ((Settings::values.memory_layout_mode.GetValue() == + Settings::MemoryLayout::Memory_4Gb + ? physical_min_bits + : physical_max_bits) - + Memory::YUZU_PAGEBITS); + for (size_t i = 0; i < total_phys; i++) { + compressed_device_addr[i] = 0; + } +} + +template <typename Traits> +DeviceMemoryManager<Traits>::~DeviceMemoryManager() = default; + +template <typename Traits> +void DeviceMemoryManager<Traits>::BindInterface(DeviceInterface* device_inter_) { + device_inter = device_inter_; +} + +template <typename Traits> +DAddr DeviceMemoryManager<Traits>::Allocate(size_t size) { + return impl->Allocate(size); +} + +template <typename Traits> +void DeviceMemoryManager<Traits>::AllocateFixed(DAddr start, size_t size) { + return impl->AllocateFixed(start, size); +} + +template <typename Traits> +void DeviceMemoryManager<Traits>::Free(DAddr start, size_t size) { + impl->Free(start, size); +} + +template <typename Traits> +void DeviceMemoryManager<Traits>::Map(DAddr address, VAddr virtual_address, size_t size, + Asid asid, bool track) { + Core::Memory::Memory* process_memory = registered_processes[asid.id]; + size_t start_page_d = address >> Memory::YUZU_PAGEBITS; + size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS; + std::scoped_lock lk(mapping_guard); + for (size_t i = 0; i < num_pages; i++) { + const VAddr new_vaddress = virtual_address + i * Memory::YUZU_PAGESIZE; + auto* ptr = process_memory->GetPointerSilent(Common::ProcessAddress(new_vaddress)); + if (ptr == nullptr) [[unlikely]] { + compressed_physical_ptr[start_page_d + i] = 0; + continue; + } + auto phys_addr = static_cast<u32>(GetRawPhysicalAddr(ptr) >> Memory::YUZU_PAGEBITS) + 1U; + compressed_physical_ptr[start_page_d + i] = phys_addr; + InsertCPUBacking(start_page_d + i, new_vaddress, asid); + const u32 base_dev = compressed_device_addr[phys_addr - 1U]; + const u32 new_dev = static_cast<u32>(start_page_d + i); + if (base_dev == 0) [[likely]] { + compressed_device_addr[phys_addr - 1U] = new_dev; + continue; + } + u32 start_id = base_dev & MULTI_MASK; + if ((base_dev >> MULTI_FLAG_BITS) == 0) { + start_id = impl->multi_dev_address.Register(base_dev); + compressed_device_addr[phys_addr - 1U] = MULTI_FLAG | start_id; + } + impl->multi_dev_address.Register(new_dev, start_id); + } + if (track) { + TrackContinuityImpl(address, virtual_address, size, asid); + } +} + +template <typename Traits> +void DeviceMemoryManager<Traits>::Unmap(DAddr address, size_t size) { + size_t start_page_d = address >> Memory::YUZU_PAGEBITS; + size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS; + device_inter->InvalidateRegion(address, size); + std::scoped_lock lk(mapping_guard); + for (size_t i = 0; i < num_pages; i++) { + auto phys_addr = compressed_physical_ptr[start_page_d + i]; + compressed_physical_ptr[start_page_d + i] = 0; + cpu_backing_address[start_page_d + i] = 0; + if (phys_addr != 0) [[likely]] { + const u32 base_dev = compressed_device_addr[phys_addr - 1U]; + if ((base_dev >> MULTI_FLAG_BITS) == 0) [[likely]] { + compressed_device_addr[phys_addr - 1] = 0; + continue; + } + const auto [more_entries, new_start] = impl->multi_dev_address.Unregister( + static_cast<u32>(start_page_d + i), base_dev & MULTI_MASK); + if (!more_entries) { + compressed_device_addr[phys_addr - 1] = + impl->multi_dev_address.ReleaseEntry(new_start); + continue; + } + compressed_device_addr[phys_addr - 1] = new_start | MULTI_FLAG; + } + } +} +template <typename Traits> +void DeviceMemoryManager<Traits>::TrackContinuityImpl(DAddr address, VAddr virtual_address, + size_t size, Asid asid) { + Core::Memory::Memory* process_memory = registered_processes[asid.id]; + size_t start_page_d = address >> Memory::YUZU_PAGEBITS; + size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS; + uintptr_t last_ptr = 0; + size_t page_count = 1; + for (size_t i = num_pages; i > 0; i--) { + size_t index = i - 1; + const VAddr new_vaddress = virtual_address + index * Memory::YUZU_PAGESIZE; + const uintptr_t new_ptr = reinterpret_cast<uintptr_t>( + process_memory->GetPointerSilent(Common::ProcessAddress(new_vaddress))); + if (new_ptr + page_size == last_ptr) { + page_count++; + } else { + page_count = 1; + } + last_ptr = new_ptr; + continuity_tracker[start_page_d + index] = static_cast<u32>(page_count); + } +} +template <typename Traits> +u8* DeviceMemoryManager<Traits>::GetSpan(const DAddr src_addr, const std::size_t size) { + size_t page_index = src_addr >> page_bits; + size_t subbits = src_addr & page_mask; + if ((static_cast<size_t>(continuity_tracker[page_index]) << page_bits) >= size + subbits) { + return GetPointer<u8>(src_addr); + } + return nullptr; +} + +template <typename Traits> +const u8* DeviceMemoryManager<Traits>::GetSpan(const DAddr src_addr, const std::size_t size) const { + size_t page_index = src_addr >> page_bits; + size_t subbits = src_addr & page_mask; + if ((static_cast<size_t>(continuity_tracker[page_index]) << page_bits) >= size + subbits) { + return GetPointer<u8>(src_addr); + } + return nullptr; +} + +template <typename Traits> +void DeviceMemoryManager<Traits>::InnerGatherDeviceAddresses(Common::ScratchBuffer<u32>& buffer, + PAddr address) { + size_t phys_addr = address >> page_bits; + std::scoped_lock lk(mapping_guard); + u32 backing = compressed_device_addr[phys_addr]; + if ((backing >> MULTI_FLAG_BITS) != 0) { + impl->multi_dev_address.GatherValues(backing & MULTI_MASK, buffer); + return; + } + buffer.resize(1); + buffer[0] = backing; +} + +template <typename Traits> +template <typename T> +T* DeviceMemoryManager<Traits>::GetPointer(DAddr address) { + const size_t index = address >> Memory::YUZU_PAGEBITS; + const size_t offset = address & Memory::YUZU_PAGEMASK; + auto phys_addr = compressed_physical_ptr[index]; + if (phys_addr == 0) [[unlikely]] { + return nullptr; + } + return GetPointerFromRaw<T>((static_cast<PAddr>(phys_addr - 1) << Memory::YUZU_PAGEBITS) + + offset); +} + +template <typename Traits> +template <typename T> +const T* DeviceMemoryManager<Traits>::GetPointer(DAddr address) const { + const size_t index = address >> Memory::YUZU_PAGEBITS; + const size_t offset = address & Memory::YUZU_PAGEMASK; + auto phys_addr = compressed_physical_ptr[index]; + if (phys_addr == 0) [[unlikely]] { + return nullptr; + } + return GetPointerFromRaw<T>((static_cast<PAddr>(phys_addr - 1) << Memory::YUZU_PAGEBITS) + + offset); +} + +template <typename Traits> +template <typename T> +void DeviceMemoryManager<Traits>::Write(DAddr address, T value) { + T* ptr = GetPointer<T>(address); + if (!ptr) [[unlikely]] { + return; + } + std::memcpy(ptr, &value, sizeof(T)); +} + +template <typename Traits> +template <typename T> +T DeviceMemoryManager<Traits>::Read(DAddr address) const { + const T* ptr = GetPointer<T>(address); + T result{}; + if (!ptr) [[unlikely]] { + return result; + } + std::memcpy(&result, ptr, sizeof(T)); + return result; +} + +template <typename Traits> +void DeviceMemoryManager<Traits>::WalkBlock(DAddr addr, std::size_t size, auto on_unmapped, + auto on_memory, auto increment) { + std::size_t remaining_size = size; + std::size_t page_index = addr >> Memory::YUZU_PAGEBITS; + std::size_t page_offset = addr & Memory::YUZU_PAGEMASK; + + while (remaining_size) { + const size_t next_pages = static_cast<std::size_t>(continuity_tracker[page_index]); + const std::size_t copy_amount = + std::min((next_pages << Memory::YUZU_PAGEBITS) - page_offset, remaining_size); + const auto current_vaddr = + static_cast<u64>((page_index << Memory::YUZU_PAGEBITS) + page_offset); + SCOPE_EXIT({ + page_index += next_pages; + page_offset = 0; + increment(copy_amount); + remaining_size -= copy_amount; + }); + + auto phys_addr = compressed_physical_ptr[page_index]; + if (phys_addr == 0) { + on_unmapped(copy_amount, current_vaddr); + continue; + } + auto* mem_ptr = GetPointerFromRaw<u8>( + (static_cast<PAddr>(phys_addr - 1) << Memory::YUZU_PAGEBITS) + page_offset); + on_memory(copy_amount, mem_ptr); + } +} + +template <typename Traits> +void DeviceMemoryManager<Traits>::ReadBlock(DAddr address, void* dest_pointer, size_t size) { + device_inter->FlushRegion(address, size); + WalkBlock( + address, size, + [&](size_t copy_amount, DAddr current_vaddr) { + LOG_ERROR( + HW_Memory, + "Unmapped Device ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", + current_vaddr, address, size); + std::memset(dest_pointer, 0, copy_amount); + }, + [&](size_t copy_amount, const u8* const src_ptr) { + std::memcpy(dest_pointer, src_ptr, copy_amount); + }, + [&](const std::size_t copy_amount) { + dest_pointer = static_cast<u8*>(dest_pointer) + copy_amount; + }); +} + +template <typename Traits> +void DeviceMemoryManager<Traits>::WriteBlock(DAddr address, const void* src_pointer, size_t size) { + WalkBlock( + address, size, + [&](size_t copy_amount, DAddr current_vaddr) { + LOG_ERROR( + HW_Memory, + "Unmapped Device WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", + current_vaddr, address, size); + }, + [&](size_t copy_amount, u8* const dst_ptr) { + std::memcpy(dst_ptr, src_pointer, copy_amount); + }, + [&](const std::size_t copy_amount) { + src_pointer = static_cast<const u8*>(src_pointer) + copy_amount; + }); + device_inter->InvalidateRegion(address, size); +} + +template <typename Traits> +void DeviceMemoryManager<Traits>::ReadBlockUnsafe(DAddr address, void* dest_pointer, size_t size) { + WalkBlock( + address, size, + [&](size_t copy_amount, DAddr current_vaddr) { + LOG_ERROR( + HW_Memory, + "Unmapped Device ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", + current_vaddr, address, size); + std::memset(dest_pointer, 0, copy_amount); + }, + [&](size_t copy_amount, const u8* const src_ptr) { + std::memcpy(dest_pointer, src_ptr, copy_amount); + }, + [&](const std::size_t copy_amount) { + dest_pointer = static_cast<u8*>(dest_pointer) + copy_amount; + }); +} + +template <typename Traits> +void DeviceMemoryManager<Traits>::WriteBlockUnsafe(DAddr address, const void* src_pointer, + size_t size) { + WalkBlock( + address, size, + [&](size_t copy_amount, DAddr current_vaddr) { + LOG_ERROR( + HW_Memory, + "Unmapped Device WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", + current_vaddr, address, size); + }, + [&](size_t copy_amount, u8* const dst_ptr) { + std::memcpy(dst_ptr, src_pointer, copy_amount); + }, + [&](const std::size_t copy_amount) { + src_pointer = static_cast<const u8*>(src_pointer) + copy_amount; + }); +} + +template <typename Traits> +Asid DeviceMemoryManager<Traits>::RegisterProcess(Memory::Memory* memory_device_inter) { + size_t new_id{}; + if (!id_pool.empty()) { + new_id = id_pool.front(); + id_pool.pop_front(); + registered_processes[new_id] = memory_device_inter; + } else { + registered_processes.emplace_back(memory_device_inter); + new_id = registered_processes.size() - 1U; + } + return Asid{new_id}; +} + +template <typename Traits> +void DeviceMemoryManager<Traits>::UnregisterProcess(Asid asid) { + registered_processes[asid.id] = nullptr; + id_pool.push_front(asid.id); +} + +template <typename Traits> +void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size, s32 delta) { + std::unique_lock<std::mutex> lk(counter_guard, std::defer_lock); + const auto Lock = [&] { + if (!lk) { + lk.lock(); + } + }; + u64 uncache_begin = 0; + u64 cache_begin = 0; + u64 uncache_bytes = 0; + u64 cache_bytes = 0; + const auto MarkRegionCaching = &DeviceMemoryManager<Traits>::DeviceMethods::MarkRegionCaching; + + std::atomic_thread_fence(std::memory_order_acquire); + const size_t page_end = Common::DivCeil(addr + size, Memory::YUZU_PAGESIZE); + size_t page = addr >> Memory::YUZU_PAGEBITS; + auto [asid, base_vaddress] = ExtractCPUBacking(page); + size_t vpage = base_vaddress >> Memory::YUZU_PAGEBITS; + auto* memory_device_inter = registered_processes[asid.id]; + for (; page != page_end; ++page) { + std::atomic_uint8_t& count = cached_pages->at(page >> 3).Count(page); + + if (delta > 0) { + ASSERT_MSG(count.load(std::memory_order::relaxed) < std::numeric_limits<u8>::max(), + "Count may overflow!"); + } else if (delta < 0) { + ASSERT_MSG(count.load(std::memory_order::relaxed) > 0, "Count may underflow!"); + } else { + ASSERT_MSG(false, "Delta must be non-zero!"); + } + + // Adds or subtracts 1, as count is a unsigned 8-bit value + count.fetch_add(static_cast<u8>(delta), std::memory_order_release); + + // Assume delta is either -1 or 1 + if (count.load(std::memory_order::relaxed) == 0) { + if (uncache_bytes == 0) { + uncache_begin = vpage; + } + uncache_bytes += Memory::YUZU_PAGESIZE; + } else if (uncache_bytes > 0) { + Lock(); + MarkRegionCaching(memory_device_inter, uncache_begin << Memory::YUZU_PAGEBITS, + uncache_bytes, false); + uncache_bytes = 0; + } + if (count.load(std::memory_order::relaxed) == 1 && delta > 0) { + if (cache_bytes == 0) { + cache_begin = vpage; + } + cache_bytes += Memory::YUZU_PAGESIZE; + } else if (cache_bytes > 0) { + Lock(); + MarkRegionCaching(memory_device_inter, cache_begin << Memory::YUZU_PAGEBITS, cache_bytes, + true); + cache_bytes = 0; + } + vpage++; + } + if (uncache_bytes > 0) { + Lock(); + MarkRegionCaching(memory_device_inter, uncache_begin << Memory::YUZU_PAGEBITS, uncache_bytes, + false); + } + if (cache_bytes > 0) { + Lock(); + MarkRegionCaching(memory_device_inter, cache_begin << Memory::YUZU_PAGEBITS, cache_bytes, + true); + } +} + +} // namespace Core diff --git a/src/core/file_sys/system_archive/system_archive.cpp b/src/core/file_sys/system_archive/system_archive.cpp index 6abac793b..b53eef877 100644 --- a/src/core/file_sys/system_archive/system_archive.cpp +++ b/src/core/file_sys/system_archive/system_archive.cpp @@ -67,25 +67,29 @@ constexpr std::array<SystemArchiveDescriptor, SYSTEM_ARCHIVE_COUNT> SYSTEM_ARCHI }}; VirtualFile SynthesizeSystemArchive(const u64 title_id) { - if (title_id < SYSTEM_ARCHIVES.front().title_id || title_id > SYSTEM_ARCHIVES.back().title_id) + if (title_id < SYSTEM_ARCHIVES.front().title_id || title_id > SYSTEM_ARCHIVES.back().title_id) { return nullptr; + } const auto& desc = SYSTEM_ARCHIVES[title_id - SYSTEM_ARCHIVE_BASE_TITLE_ID]; LOG_INFO(Service_FS, "Synthesizing system archive '{}' (0x{:016X}).", desc.name, desc.title_id); - if (desc.supplier == nullptr) + if (desc.supplier == nullptr) { return nullptr; + } const auto dir = desc.supplier(); - if (dir == nullptr) + if (dir == nullptr) { return nullptr; + } const auto romfs = CreateRomFS(dir); - if (romfs == nullptr) + if (romfs == nullptr) { return nullptr; + } LOG_INFO(Service_FS, " - System archive generation successful!"); return romfs; diff --git a/src/core/file_sys/system_archive/time_zone_binary.cpp b/src/core/file_sys/system_archive/time_zone_binary.cpp index 7c17bbefa..d4d2eae76 100644 --- a/src/core/file_sys/system_archive/time_zone_binary.cpp +++ b/src/core/file_sys/system_archive/time_zone_binary.cpp @@ -6,7 +6,6 @@ #include "common/swap.h" #include "core/file_sys/system_archive/time_zone_binary.h" #include "core/file_sys/vfs_vector.h" -#include "core/hle/service/time/time_zone_types.h" #include "nx_tzdb.h" diff --git a/src/core/gpu_dirty_memory_manager.h b/src/core/gpu_dirty_memory_manager.h index 9687531e8..cc8fc176f 100644 --- a/src/core/gpu_dirty_memory_manager.h +++ b/src/core/gpu_dirty_memory_manager.h @@ -10,7 +10,7 @@ #include <utility> #include <vector> -#include "core/memory.h" +#include "core/device_memory_manager.h" namespace Core { @@ -23,7 +23,7 @@ public: ~GPUDirtyMemoryManager() = default; - void Collect(VAddr address, size_t size) { + void Collect(PAddr address, size_t size) { TransformAddress t = BuildTransform(address, size); TransformAddress tmp, original; do { @@ -47,7 +47,7 @@ public: std::memory_order_relaxed)); } - void Gather(std::function<void(VAddr, size_t)>& callback) { + void Gather(std::function<void(PAddr, size_t)>& callback) { { std::scoped_lock lk(guard); TransformAddress t = current.exchange(default_transform, std::memory_order_relaxed); @@ -65,7 +65,7 @@ public: mask = mask >> empty_bits; const size_t continuous_bits = std::countr_one(mask); - callback((static_cast<VAddr>(transform.address) << page_bits) + offset, + callback((static_cast<PAddr>(transform.address) << page_bits) + offset, continuous_bits << align_bits); mask = continuous_bits < align_size ? (mask >> continuous_bits) : 0; offset += continuous_bits << align_bits; @@ -80,7 +80,7 @@ private: u32 mask; }; - constexpr static size_t page_bits = Memory::YUZU_PAGEBITS - 1; + constexpr static size_t page_bits = DEVICE_PAGEBITS - 1; constexpr static size_t page_size = 1ULL << page_bits; constexpr static size_t page_mask = page_size - 1; @@ -89,7 +89,7 @@ private: constexpr static size_t align_mask = align_size - 1; constexpr static TransformAddress default_transform = {.address = ~0U, .mask = 0U}; - bool IsValid(VAddr address) { + bool IsValid(PAddr address) { return address < (1ULL << 39); } @@ -103,7 +103,7 @@ private: return mask; } - TransformAddress BuildTransform(VAddr address, size_t size) { + TransformAddress BuildTransform(PAddr address, size_t size) { const size_t minor_address = address & page_mask; const size_t minor_bit = minor_address >> align_bits; const size_t top_bit = (minor_address + size + align_mask) >> align_bits; diff --git a/src/core/guest_memory.h b/src/core/guest_memory.h new file mode 100644 index 000000000..7ee18c126 --- /dev/null +++ b/src/core/guest_memory.h @@ -0,0 +1,214 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <iterator> +#include <memory> +#include <optional> +#include <span> +#include <vector> + +#include "common/assert.h" +#include "common/scratch_buffer.h" + +namespace Core::Memory { + +enum GuestMemoryFlags : u32 { + Read = 1 << 0, + Write = 1 << 1, + Safe = 1 << 2, + Cached = 1 << 3, + + SafeRead = Read | Safe, + SafeWrite = Write | Safe, + SafeReadWrite = SafeRead | SafeWrite, + SafeReadCachedWrite = SafeReadWrite | Cached, + + UnsafeRead = Read, + UnsafeWrite = Write, + UnsafeReadWrite = UnsafeRead | UnsafeWrite, + UnsafeReadCachedWrite = UnsafeReadWrite | Cached, +}; + +namespace { +template <typename M, typename T, GuestMemoryFlags FLAGS> +class GuestMemory { + using iterator = T*; + using const_iterator = const T*; + using value_type = T; + using element_type = T; + using iterator_category = std::contiguous_iterator_tag; + +public: + GuestMemory() = delete; + explicit GuestMemory(M& memory, u64 addr, std::size_t size, + Common::ScratchBuffer<T>* backup = nullptr) + : m_memory{memory}, m_addr{addr}, m_size{size} { + static_assert(FLAGS & GuestMemoryFlags::Read || FLAGS & GuestMemoryFlags::Write); + if constexpr (FLAGS & GuestMemoryFlags::Read) { + Read(addr, size, backup); + } + } + + ~GuestMemory() = default; + + T* data() noexcept { + return m_data_span.data(); + } + + const T* data() const noexcept { + return m_data_span.data(); + } + + size_t size() const noexcept { + return m_size; + } + + size_t size_bytes() const noexcept { + return this->size() * sizeof(T); + } + + [[nodiscard]] T* begin() noexcept { + return this->data(); + } + + [[nodiscard]] const T* begin() const noexcept { + return this->data(); + } + + [[nodiscard]] T* end() noexcept { + return this->data() + this->size(); + } + + [[nodiscard]] const T* end() const noexcept { + return this->data() + this->size(); + } + + T& operator[](size_t index) noexcept { + return m_data_span[index]; + } + + const T& operator[](size_t index) const noexcept { + return m_data_span[index]; + } + + void SetAddressAndSize(u64 addr, std::size_t size) noexcept { + m_addr = addr; + m_size = size; + m_addr_changed = true; + } + + std::span<T> Read(u64 addr, std::size_t size, + Common::ScratchBuffer<T>* backup = nullptr) noexcept { + m_addr = addr; + m_size = size; + if (m_size == 0) { + m_is_data_copy = true; + return {}; + } + + if (this->TrySetSpan()) { + if constexpr (FLAGS & GuestMemoryFlags::Safe) { + m_memory.FlushRegion(m_addr, this->size_bytes()); + } + } else { + if (backup) { + backup->resize_destructive(this->size()); + m_data_span = *backup; + } else { + m_data_copy.resize(this->size()); + m_data_span = std::span(m_data_copy); + } + m_is_data_copy = true; + m_span_valid = true; + if constexpr (FLAGS & GuestMemoryFlags::Safe) { + m_memory.ReadBlock(m_addr, this->data(), this->size_bytes()); + } else { + m_memory.ReadBlockUnsafe(m_addr, this->data(), this->size_bytes()); + } + } + return m_data_span; + } + + void Write(std::span<T> write_data) noexcept { + if constexpr (FLAGS & GuestMemoryFlags::Cached) { + m_memory.WriteBlockCached(m_addr, write_data.data(), this->size_bytes()); + } else if constexpr (FLAGS & GuestMemoryFlags::Safe) { + m_memory.WriteBlock(m_addr, write_data.data(), this->size_bytes()); + } else { + m_memory.WriteBlockUnsafe(m_addr, write_data.data(), this->size_bytes()); + } + } + + bool TrySetSpan() noexcept { + if (u8* ptr = m_memory.GetSpan(m_addr, this->size_bytes()); ptr) { + m_data_span = {reinterpret_cast<T*>(ptr), this->size()}; + m_span_valid = true; + return true; + } + return false; + } + +protected: + bool IsDataCopy() const noexcept { + return m_is_data_copy; + } + + bool AddressChanged() const noexcept { + return m_addr_changed; + } + + M& m_memory; + u64 m_addr{}; + size_t m_size{}; + std::span<T> m_data_span{}; + std::vector<T> m_data_copy{}; + bool m_span_valid{false}; + bool m_is_data_copy{false}; + bool m_addr_changed{false}; +}; + +template <typename M, typename T, GuestMemoryFlags FLAGS> +class GuestMemoryScoped : public GuestMemory<M, T, FLAGS> { +public: + GuestMemoryScoped() = delete; + explicit GuestMemoryScoped(M& memory, u64 addr, std::size_t size, + Common::ScratchBuffer<T>* backup = nullptr) + : GuestMemory<M, T, FLAGS>(memory, addr, size, backup) { + if constexpr (!(FLAGS & GuestMemoryFlags::Read)) { + if (!this->TrySetSpan()) { + if (backup) { + this->m_data_span = *backup; + this->m_span_valid = true; + this->m_is_data_copy = true; + } + } + } + } + + ~GuestMemoryScoped() { + if constexpr (FLAGS & GuestMemoryFlags::Write) { + if (this->size() == 0) [[unlikely]] { + return; + } + + if (this->AddressChanged() || this->IsDataCopy()) { + ASSERT(this->m_span_valid); + if constexpr (FLAGS & GuestMemoryFlags::Cached) { + this->m_memory.WriteBlockCached(this->m_addr, this->data(), this->size_bytes()); + } else if constexpr (FLAGS & GuestMemoryFlags::Safe) { + this->m_memory.WriteBlock(this->m_addr, this->data(), this->size_bytes()); + } else { + this->m_memory.WriteBlockUnsafe(this->m_addr, this->data(), this->size_bytes()); + } + } else if constexpr ((FLAGS & GuestMemoryFlags::Safe) || + (FLAGS & GuestMemoryFlags::Cached)) { + this->m_memory.InvalidateRegion(this->m_addr, this->size_bytes()); + } + } + } +}; +} // namespace + +} // namespace Core::Memory diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index 53735a225..0b08e877e 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp @@ -5,6 +5,7 @@ #include "common/scope_exit.h" #include "common/settings.h" #include "core/core.h" +#include "core/gpu_dirty_memory_manager.h" #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_scoped_resource_reservation.h" #include "core/hle/kernel/k_shared_memory.h" @@ -320,7 +321,7 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params, const KPa // Ensure our memory is initialized. m_memory.SetCurrentPageTable(*this); - m_memory.SetGPUDirtyManagers(m_dirty_memory_managers); + m_memory.SetGPUDirtyManagers(m_kernel.System().GetGPUDirtyMemoryManager()); // Ensure we can insert the code region. R_UNLESS(m_page_table.CanContain(params.code_address, params.code_num_pages * PageSize, @@ -417,7 +418,7 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params, // Ensure our memory is initialized. m_memory.SetCurrentPageTable(*this); - m_memory.SetGPUDirtyManagers(m_dirty_memory_managers); + m_memory.SetGPUDirtyManagers(m_kernel.System().GetGPUDirtyMemoryManager()); // Ensure we can insert the code region. R_UNLESS(m_page_table.CanContain(params.code_address, code_size, KMemoryState::Code), @@ -1141,8 +1142,7 @@ void KProcess::Switch(KProcess* cur_process, KProcess* next_process) {} KProcess::KProcess(KernelCore& kernel) : KAutoObjectWithSlabHeapAndContainer(kernel), m_page_table{kernel}, m_state_lock{kernel}, m_list_lock{kernel}, m_cond_var{kernel.System()}, m_address_arbiter{kernel.System()}, - m_handle_table{kernel}, m_dirty_memory_managers{}, - m_exclusive_monitor{}, m_memory{kernel.System()} {} + m_handle_table{kernel}, m_exclusive_monitor{}, m_memory{kernel.System()} {} KProcess::~KProcess() = default; Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size, @@ -1324,10 +1324,4 @@ bool KProcess::RemoveWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointT return true; } -void KProcess::GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback) { - for (auto& manager : m_dirty_memory_managers) { - manager.Gather(callback); - } -} - } // namespace Kernel diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h index 53c0e3316..ab1358a12 100644 --- a/src/core/hle/kernel/k_process.h +++ b/src/core/hle/kernel/k_process.h @@ -7,7 +7,6 @@ #include "core/arm/arm_interface.h" #include "core/file_sys/program_metadata.h" -#include "core/gpu_dirty_memory_manager.h" #include "core/hle/kernel/code_set.h" #include "core/hle/kernel/k_address_arbiter.h" #include "core/hle/kernel/k_capabilities.h" @@ -128,7 +127,6 @@ private: #ifdef HAS_NCE std::unordered_map<u64, u64> m_post_handlers{}; #endif - std::array<Core::GPUDirtyMemoryManager, Core::Hardware::NUM_CPU_CORES> m_dirty_memory_managers; std::unique_ptr<Core::ExclusiveMonitor> m_exclusive_monitor; Core::Memory::Memory m_memory; @@ -511,8 +509,6 @@ public: return m_memory; } - void GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback); - Core::ExclusiveMonitor& GetExclusiveMonitor() const { return *m_exclusive_monitor; } diff --git a/src/core/hle/service/caps/caps_manager.cpp b/src/core/hle/service/caps/caps_manager.cpp index 261fc204c..e3b8ecf3e 100644 --- a/src/core/hle/service/caps/caps_manager.cpp +++ b/src/core/hle/service/caps/caps_manager.cpp @@ -10,8 +10,10 @@ #include "core/core.h" #include "core/hle/service/caps/caps_manager.h" #include "core/hle/service/caps/caps_result.h" -#include "core/hle/service/time/time_manager.h" -#include "core/hle/service/time/time_zone_content_manager.h" +#include "core/hle/service/glue/time/static.h" +#include "core/hle/service/psc/time/system_clock.h" +#include "core/hle/service/service.h" +#include "core/hle/service/sm/sm.h" namespace Service::Capture { @@ -239,10 +241,15 @@ Result AlbumManager::SaveScreenShot(ApplicationAlbumEntry& out_entry, const ApplicationData& app_data, std::span<const u8> image_data, u64 aruid) { const u64 title_id = system.GetApplicationProcessProgramID(); - const auto& user_clock = system.GetTimeManager().GetStandardUserSystemClockCore(); + + auto static_service = + system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", true); + + std::shared_ptr<Service::PSC::Time::SystemClock> user_clock{}; + static_service->GetStandardUserSystemClock(user_clock); s64 posix_time{}; - Result result = user_clock.GetCurrentTime(system, posix_time); + auto result = user_clock->GetCurrentTime(posix_time); if (result.IsError()) { return result; @@ -257,10 +264,14 @@ Result AlbumManager::SaveEditedScreenShot(ApplicationAlbumEntry& out_entry, const ScreenShotAttribute& attribute, const AlbumFileId& file_id, std::span<const u8> image_data) { - const auto& user_clock = system.GetTimeManager().GetStandardUserSystemClockCore(); + auto static_service = + system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", true); + + std::shared_ptr<Service::PSC::Time::SystemClock> user_clock{}; + static_service->GetStandardUserSystemClock(user_clock); s64 posix_time{}; - Result result = user_clock.GetCurrentTime(system, posix_time); + auto result = user_clock->GetCurrentTime(posix_time); if (result.IsError()) { return result; @@ -455,19 +466,23 @@ Result AlbumManager::SaveImage(ApplicationAlbumEntry& out_entry, std::span<const } AlbumFileDateTime AlbumManager::ConvertToAlbumDateTime(u64 posix_time) const { - Time::TimeZone::CalendarInfo calendar_date{}; - const auto& time_zone_manager = - system.GetTimeManager().GetTimeZoneContentManager().GetTimeZoneManager(); + auto static_service = + system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", true); + + std::shared_ptr<Service::Glue::Time::TimeZoneService> timezone_service{}; + static_service->GetTimeZoneService(timezone_service); - time_zone_manager.ToCalendarTimeWithMyRules(posix_time, calendar_date); + Service::PSC::Time::CalendarTime calendar_time{}; + Service::PSC::Time::CalendarAdditionalInfo additional_info{}; + timezone_service->ToCalendarTimeWithMyRule(calendar_time, additional_info, posix_time); return { - .year = calendar_date.time.year, - .month = calendar_date.time.month, - .day = calendar_date.time.day, - .hour = calendar_date.time.hour, - .minute = calendar_date.time.minute, - .second = calendar_date.time.second, + .year = calendar_time.year, + .month = calendar_time.month, + .day = calendar_time.day, + .hour = calendar_time.hour, + .minute = calendar_time.minute, + .second = calendar_time.second, .unique_id = 0, }; } diff --git a/src/core/hle/service/cmif_serialization.h b/src/core/hle/service/cmif_serialization.h new file mode 100644 index 000000000..8e8cf2507 --- /dev/null +++ b/src/core/hle/service/cmif_serialization.h @@ -0,0 +1,337 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/div_ceil.h" + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/service.h" + +namespace Service { + +// clang-format off +struct RequestLayout { + u32 copy_handle_count; + u32 move_handle_count; + u32 cmif_raw_data_size; + u32 domain_interface_count; +}; + +template <ArgumentType Type1, ArgumentType Type2, typename MethodArguments, size_t PrevAlign = 1, size_t DataOffset = 0, size_t ArgIndex = 0> +constexpr u32 GetArgumentRawDataSize() { + if constexpr (ArgIndex >= std::tuple_size_v<MethodArguments>) { + return static_cast<u32>(DataOffset); + } else { + using ArgType = std::tuple_element_t<ArgIndex, MethodArguments>; + + if constexpr (ArgumentTraits<ArgType>::Type == Type1 || ArgumentTraits<ArgType>::Type == Type2) { + constexpr size_t ArgAlign = alignof(ArgType); + constexpr size_t ArgSize = sizeof(ArgType); + + static_assert(PrevAlign <= ArgAlign, "Input argument is not ordered by alignment"); + + constexpr size_t ArgOffset = Common::AlignUp(DataOffset, ArgAlign); + constexpr size_t ArgEnd = ArgOffset + ArgSize; + + return GetArgumentRawDataSize<Type1, Type2, MethodArguments, ArgAlign, ArgEnd, ArgIndex + 1>(); + } else { + return GetArgumentRawDataSize<Type1, Type2, MethodArguments, PrevAlign, DataOffset, ArgIndex + 1>(); + } + } +} + +template <ArgumentType DataType, typename MethodArguments, size_t ArgCount = 0, size_t ArgIndex = 0> +constexpr u32 GetArgumentTypeCount() { + if constexpr (ArgIndex >= std::tuple_size_v<MethodArguments>) { + return static_cast<u32>(ArgCount); + } else { + using ArgType = std::tuple_element_t<ArgIndex, MethodArguments>; + + if constexpr (ArgumentTraits<ArgType>::Type == DataType) { + return GetArgumentTypeCount<DataType, MethodArguments, ArgCount + 1, ArgIndex + 1>(); + } else { + return GetArgumentTypeCount<DataType, MethodArguments, ArgCount, ArgIndex + 1>(); + } + } +} + +template <typename MethodArguments> +constexpr RequestLayout GetNonDomainReplyInLayout() { + return RequestLayout{ + .copy_handle_count = GetArgumentTypeCount<ArgumentType::InCopyHandle, MethodArguments>(), + .move_handle_count = 0, + .cmif_raw_data_size = GetArgumentRawDataSize<ArgumentType::InData, ArgumentType::InProcessId, MethodArguments>(), + .domain_interface_count = 0, + }; +} + +template <typename MethodArguments> +constexpr RequestLayout GetDomainReplyInLayout() { + return RequestLayout{ + .copy_handle_count = GetArgumentTypeCount<ArgumentType::InCopyHandle, MethodArguments>(), + .move_handle_count = 0, + .cmif_raw_data_size = GetArgumentRawDataSize<ArgumentType::InData, ArgumentType::InProcessId, MethodArguments>(), + .domain_interface_count = GetArgumentTypeCount<ArgumentType::InInterface, MethodArguments>(), + }; +} + +template <typename MethodArguments> +constexpr RequestLayout GetNonDomainReplyOutLayout() { + return RequestLayout{ + .copy_handle_count = GetArgumentTypeCount<ArgumentType::OutCopyHandle, MethodArguments>(), + .move_handle_count = GetArgumentTypeCount<ArgumentType::OutMoveHandle, MethodArguments>() + GetArgumentTypeCount<ArgumentType::OutInterface, MethodArguments>(), + .cmif_raw_data_size = GetArgumentRawDataSize<ArgumentType::OutData, ArgumentType::OutData, MethodArguments>(), + .domain_interface_count = 0, + }; +} + +template <typename MethodArguments> +constexpr RequestLayout GetDomainReplyOutLayout() { + return RequestLayout{ + .copy_handle_count = GetArgumentTypeCount<ArgumentType::OutCopyHandle, MethodArguments>(), + .move_handle_count = GetArgumentTypeCount<ArgumentType::OutMoveHandle, MethodArguments>(), + .cmif_raw_data_size = GetArgumentRawDataSize<ArgumentType::OutData, ArgumentType::OutData, MethodArguments>(), + .domain_interface_count = GetArgumentTypeCount<ArgumentType::OutInterface, MethodArguments>(), + }; +} + +template <bool Domain, typename MethodArguments> +constexpr RequestLayout GetReplyInLayout() { + return Domain ? GetDomainReplyInLayout<MethodArguments>() : GetNonDomainReplyInLayout<MethodArguments>(); +} + +template <bool Domain, typename MethodArguments> +constexpr RequestLayout GetReplyOutLayout() { + return Domain ? GetDomainReplyOutLayout<MethodArguments>() : GetNonDomainReplyOutLayout<MethodArguments>(); +} + +using OutTemporaryBuffers = std::array<Common::ScratchBuffer<u8>, 3>; + +template <bool Domain, typename MethodArguments, typename CallArguments, size_t PrevAlign = 1, size_t DataOffset = 0, size_t HandleIndex = 0, size_t InBufferIndex = 0, size_t OutBufferIndex = 0, bool RawDataFinished = false, size_t ArgIndex = 0> +void ReadInArgument(CallArguments& args, const u8* raw_data, HLERequestContext& ctx, OutTemporaryBuffers& temp) { + if constexpr (ArgIndex >= std::tuple_size_v<CallArguments>) { + return; + } else { + using ArgType = std::tuple_element_t<ArgIndex, MethodArguments>; + + if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InData || ArgumentTraits<ArgType>::Type == ArgumentType::InProcessId) { + constexpr size_t ArgAlign = alignof(ArgType); + constexpr size_t ArgSize = sizeof(ArgType); + + static_assert(PrevAlign <= ArgAlign, "Input argument is not ordered by alignment"); + static_assert(!RawDataFinished, "All input interface arguments must appear after raw data"); + + constexpr size_t ArgOffset = Common::AlignUp(DataOffset, ArgAlign); + constexpr size_t ArgEnd = ArgOffset + ArgSize; + + if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InProcessId) { + // TODO: abort parsing if PID is not provided? + // TODO: validate against raw data value? + std::get<ArgIndex>(args).pid = ctx.GetPID(); + } else { + std::memcpy(&std::get<ArgIndex>(args), raw_data + ArgOffset, ArgSize); + } + + return ReadInArgument<Domain, MethodArguments, CallArguments, ArgAlign, ArgEnd, HandleIndex, InBufferIndex, OutBufferIndex, false, ArgIndex + 1>(args, raw_data, ctx, temp); + } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InInterface) { + constexpr size_t ArgAlign = alignof(u32); + constexpr size_t ArgSize = sizeof(u32); + constexpr size_t ArgOffset = Common::AlignUp(DataOffset, ArgAlign); + constexpr size_t ArgEnd = ArgOffset + ArgSize; + + static_assert(Domain); + ASSERT(ctx.GetDomainMessageHeader().input_object_count > 0); + + u32 value{}; + std::memcpy(&value, raw_data + ArgOffset, ArgSize); + std::get<ArgIndex>(args) = ctx.GetDomainHandler<ArgType::Type>(value - 1); + + return ReadInArgument<Domain, MethodArguments, CallArguments, ArgAlign, ArgEnd, HandleIndex, InBufferIndex, OutBufferIndex, true, ArgIndex + 1>(args, raw_data, ctx, temp); + } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InCopyHandle) { + std::get<ArgIndex>(args) = std::move(ctx.GetObjectFromHandle<typename ArgType::Type>(ctx.GetCopyHandle(HandleIndex))); + + return ReadInArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex + 1, InBufferIndex, OutBufferIndex, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp); + } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InLargeData) { + constexpr size_t BufferSize = sizeof(ArgType); + + // Clear the existing data. + std::memset(&std::get<ArgIndex>(args), 0, BufferSize); + + std::span<const u8> buffer{}; + + ASSERT(ctx.CanReadBuffer(InBufferIndex)); + if constexpr (ArgType::Attr & BufferAttr_HipcAutoSelect) { + buffer = ctx.ReadBuffer(InBufferIndex); + } else if constexpr (ArgType::Attr & BufferAttr_HipcMapAlias) { + buffer = ctx.ReadBufferA(InBufferIndex); + } else /* if (ArgType::Attr & BufferAttr_HipcPointer) */ { + buffer = ctx.ReadBufferX(InBufferIndex); + } + + std::memcpy(&std::get<ArgIndex>(args), buffer.data(), std::min(BufferSize, buffer.size())); + + return ReadInArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex + 1, OutBufferIndex, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp); + } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InBuffer) { + using ElementType = typename ArgType::Type; + + std::span<const u8> buffer{}; + + if (ctx.CanReadBuffer(InBufferIndex)) { + if constexpr (ArgType::Attr & BufferAttr_HipcAutoSelect) { + buffer = ctx.ReadBuffer(InBufferIndex); + } else if constexpr (ArgType::Attr & BufferAttr_HipcMapAlias) { + buffer = ctx.ReadBufferA(InBufferIndex); + } else /* if (ArgType::Attr & BufferAttr_HipcPointer) */ { + buffer = ctx.ReadBufferX(InBufferIndex); + } + } + + ElementType* ptr = (ElementType*) buffer.data(); + size_t size = buffer.size() / sizeof(ElementType); + + std::get<ArgIndex>(args) = std::span(ptr, size); + + return ReadInArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex + 1, OutBufferIndex, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp); + } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutLargeData) { + constexpr size_t BufferSize = sizeof(ArgType); + + // Clear the existing data. + std::memset(&std::get<ArgIndex>(args), 0, BufferSize); + + return ReadInArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex, OutBufferIndex + 1, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp); + } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutBuffer) { + using ElementType = typename ArgType::Type; + + // Set up scratch buffer. + auto& buffer = temp[OutBufferIndex]; + if (ctx.CanWriteBuffer(OutBufferIndex)) { + buffer.resize_destructive(ctx.GetWriteBufferSize(OutBufferIndex)); + } else { + buffer.resize_destructive(0); + } + + ElementType* ptr = (ElementType*) buffer.data(); + size_t size = buffer.size() / sizeof(ElementType); + + std::get<ArgIndex>(args) = std::span(ptr, size); + + return ReadInArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex, OutBufferIndex + 1, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp); + } else { + return ReadInArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex, OutBufferIndex, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp); + } + } +} + +template <bool Domain, typename MethodArguments, typename CallArguments, size_t PrevAlign = 1, size_t DataOffset = 0, size_t OutBufferIndex = 0, bool RawDataFinished = false, size_t ArgIndex = 0> +void WriteOutArgument(CallArguments& args, u8* raw_data, HLERequestContext& ctx, OutTemporaryBuffers& temp) { + if constexpr (ArgIndex >= std::tuple_size_v<CallArguments>) { + return; + } else { + using ArgType = std::tuple_element_t<ArgIndex, MethodArguments>; + + if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutData) { + constexpr size_t ArgAlign = alignof(ArgType); + constexpr size_t ArgSize = sizeof(ArgType); + + static_assert(PrevAlign <= ArgAlign, "Output argument is not ordered by alignment"); + static_assert(!RawDataFinished, "All output interface arguments must appear after raw data"); + + constexpr size_t ArgOffset = Common::AlignUp(DataOffset, ArgAlign); + constexpr size_t ArgEnd = ArgOffset + ArgSize; + + std::memcpy(raw_data + ArgOffset, &std::get<ArgIndex>(args), ArgSize); + + return WriteOutArgument<Domain, MethodArguments, CallArguments, ArgAlign, ArgEnd, OutBufferIndex, false, ArgIndex + 1>(args, raw_data, ctx, temp); + } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutInterface) { + if constexpr (Domain) { + ctx.AddDomainObject(std::get<ArgIndex>(args)); + } else { + ctx.AddMoveInterface(std::get<ArgIndex>(args)); + } + + return WriteOutArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex, true, ArgIndex + 1>(args, raw_data, ctx, temp); + } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutCopyHandle) { + ctx.AddCopyObject(std::get<ArgIndex>(args).GetPointerUnsafe()); + + return WriteOutArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp); + } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutMoveHandle) { + ctx.AddMoveObject(std::get<ArgIndex>(args).GetPointerUnsafe()); + + return WriteOutArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp); + } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutLargeData) { + constexpr size_t BufferSize = sizeof(ArgType); + + ASSERT(ctx.CanWriteBuffer(OutBufferIndex)); + if constexpr (ArgType::Attr & BufferAttr_HipcAutoSelect) { + ctx.WriteBuffer(std::get<ArgIndex>(args), OutBufferIndex); + } else if constexpr (ArgType::Attr & BufferAttr_HipcMapAlias) { + ctx.WriteBufferB(&std::get<ArgIndex>(args), BufferSize, OutBufferIndex); + } else /* if (ArgType::Attr & BufferAttr_HipcPointer) */ { + ctx.WriteBufferC(&std::get<ArgIndex>(args), BufferSize, OutBufferIndex); + } + + return WriteOutArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex + 1, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp); + } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutBuffer) { + auto& buffer = temp[OutBufferIndex]; + const size_t size = buffer.size(); + + if (ctx.CanWriteBuffer(OutBufferIndex)) { + if constexpr (ArgType::Attr & BufferAttr_HipcAutoSelect) { + ctx.WriteBuffer(buffer.data(), size, OutBufferIndex); + } else if constexpr (ArgType::Attr & BufferAttr_HipcMapAlias) { + ctx.WriteBufferB(buffer.data(), size, OutBufferIndex); + } else /* if (ArgType::Attr & BufferAttr_HipcPointer) */ { + ctx.WriteBufferC(buffer.data(), size, OutBufferIndex); + } + } + + return WriteOutArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex + 1, RawDataFinished, ArgIndex + 1>( args, raw_data, ctx, temp); + } else { + return WriteOutArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp); + } + } +} + +template <bool Domain, typename T, typename... A> +void CmifReplyWrapImpl(HLERequestContext& ctx, T& t, Result (T::*f)(A...)) { + // Verify domain state. + if constexpr (Domain) { + ASSERT_MSG(ctx.GetManager()->IsDomain(), "Domain reply used on non-domain session"); + } else { + ASSERT_MSG(!ctx.GetManager()->IsDomain(), "Non-domain reply used on domain session"); + } + + using MethodArguments = std::tuple<std::remove_reference_t<A>...>; + + OutTemporaryBuffers buffers{}; + auto call_arguments = std::tuple<typename RemoveOut<A>::Type...>(); + + // Read inputs. + const size_t offset_plus_command_id = ctx.GetDataPayloadOffset() + 2; + ReadInArgument<Domain, MethodArguments>(call_arguments, reinterpret_cast<u8*>(ctx.CommandBuffer() + offset_plus_command_id), ctx, buffers); + + // Call. + const auto Callable = [&]<typename... CallArgs>(CallArgs&... args) { + return (t.*f)(args...); + }; + const Result res = std::apply(Callable, call_arguments); + + // Write result. + constexpr RequestLayout layout = GetReplyOutLayout<Domain, MethodArguments>(); + IPC::ResponseBuilder rb{ctx, 2 + Common::DivCeil(layout.cmif_raw_data_size, sizeof(u32)), layout.copy_handle_count, layout.move_handle_count + layout.domain_interface_count}; + rb.Push(res); + + // Write out arguments. + WriteOutArgument<Domain, MethodArguments>(call_arguments, reinterpret_cast<u8*>(ctx.CommandBuffer() + rb.GetCurrentOffset()), ctx, buffers); +} +// clang-format on + +template <typename Self> +template <bool Domain, auto F> +inline void ServiceFramework<Self>::CmifReplyWrap(HLERequestContext& ctx) { + return CmifReplyWrapImpl<Domain>(ctx, *static_cast<Self*>(this), F); +} + +} // namespace Service diff --git a/src/core/hle/service/cmif_types.h b/src/core/hle/service/cmif_types.h new file mode 100644 index 000000000..b80028c19 --- /dev/null +++ b/src/core/hle/service/cmif_types.h @@ -0,0 +1,234 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <memory> + +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "core/hle/service/hle_ipc.h" + +namespace Service { + +// clang-format off +template <typename T> +class Out { +public: + /* implicit */ Out(T& t) : raw(&t) {} + ~Out() = default; + + T* Get() const { + return raw; + } + + T& operator*() { + return *raw; + } + +private: + T* raw; +}; + +template <typename T> +using SharedPointer = std::shared_ptr<T>; + +struct ClientProcessId { + explicit operator bool() const { + return pid != 0; + } + + const u64& operator*() const { + return pid; + } + + u64 pid; +}; + +using ClientAppletResourceUserId = ClientProcessId; + +template <typename T> +class InCopyHandle : public Kernel::KScopedAutoObject<T> { +public: + using Type = T; + + template <typename... Args> + /* implicit */ InCopyHandle(Args&&... args) : Kernel::KScopedAutoObject<T>(std::forward<Args...>(args)...) {} + ~InCopyHandle() = default; + + InCopyHandle& operator=(InCopyHandle&& rhs) { + Kernel::KScopedAutoObject<T>::operator=(std::move(rhs)); + return *this; + } +}; + +template <typename T> +class OutCopyHandle : public Kernel::KScopedAutoObject<T> { +public: + using Type = T; + + template <typename... Args> + /* implicit */ OutCopyHandle(Args&&... args) : Kernel::KScopedAutoObject<T>(std::forward<Args...>(args)...) {} + ~OutCopyHandle() = default; + + OutCopyHandle& operator=(OutCopyHandle&& rhs) { + Kernel::KScopedAutoObject<T>::operator=(std::move(rhs)); + return *this; + } +}; + +template <typename T> +class OutMoveHandle : public Kernel::KScopedAutoObject<T> { +public: + using Type = T; + + template <typename... Args> + /* implicit */ OutMoveHandle(Args&&... args) : Kernel::KScopedAutoObject<T>(std::forward<Args...>(args)...) {} + ~OutMoveHandle() = default; + + OutMoveHandle& operator=(OutMoveHandle&& rhs) { + Kernel::KScopedAutoObject<T>::operator=(std::move(rhs)); + return *this; + } +}; + +enum BufferAttr : int { + BufferAttr_In = (1U << 0), + BufferAttr_Out = (1U << 1), + BufferAttr_HipcMapAlias = (1U << 2), + BufferAttr_HipcPointer = (1U << 3), + BufferAttr_FixedSize = (1U << 4), + BufferAttr_HipcAutoSelect = (1U << 5), + BufferAttr_HipcMapTransferAllowsNonSecure = (1U << 6), + BufferAttr_HipcMapTransferAllowsNonDevice = (1U << 7), +}; + +template <typename T, int A> +struct Buffer : public std::span<T> { + static_assert(std::is_trivial_v<T>, "Buffer type must be trivial"); + static_assert((A & BufferAttr_FixedSize) == 0, "Buffer attr must not contain FixedSize"); + static_assert(((A & BufferAttr_In) == 0) ^ ((A & BufferAttr_Out) == 0), "Buffer attr must be In or Out"); + static constexpr BufferAttr Attr = static_cast<BufferAttr>(A); + using Type = T; + + Buffer& operator=(const std::span<T>& rhs) { + std::span<T>::operator=(rhs); + return *this; + } + + T& operator*() const { + return *this->data(); + } + + explicit operator bool() const { + return this->size() > 0; + } +}; + +template <BufferAttr A> +using InBuffer = Buffer<const u8, BufferAttr_In | A>; + +template <typename T, BufferAttr A> +using InArray = Buffer<T, BufferAttr_In | A>; + +template <BufferAttr A> +using OutBuffer = Buffer<u8, BufferAttr_Out | A>; + +template <typename T, BufferAttr A> +using OutArray = Buffer<T, BufferAttr_Out | A>; + +template <typename T, int A> +struct LargeData : public T { + static_assert(std::is_trivial_v<T>, "LargeData type must be trivial"); + static_assert((A & BufferAttr_FixedSize) != 0, "LargeData attr must contain FixedSize"); + static_assert(((A & BufferAttr_In) == 0) ^ ((A & BufferAttr_Out) == 0), "LargeData attr must be In or Out"); + static constexpr BufferAttr Attr = static_cast<BufferAttr>(A); + using Type = T; +}; + +template <typename T, BufferAttr A> +using InLargeData = LargeData<T, BufferAttr_FixedSize | BufferAttr_In | A>; + +template <typename T, BufferAttr A> +using OutLargeData = LargeData<T, BufferAttr_FixedSize | BufferAttr_Out | A>; + +template <typename T> +struct RemoveOut { + using Type = std::remove_reference_t<T>; +}; + +template <typename T> +struct RemoveOut<Out<T>> { + using Type = T; +}; + +enum class ArgumentType { + InProcessId, + InData, + InInterface, + InCopyHandle, + OutData, + OutInterface, + OutCopyHandle, + OutMoveHandle, + InBuffer, + InLargeData, + OutBuffer, + OutLargeData, +}; + +template <typename T> +struct ArgumentTraits; + +template <> +struct ArgumentTraits<ClientProcessId> { + static constexpr ArgumentType Type = ArgumentType::InProcessId; +}; + +template <typename T> +struct ArgumentTraits<SharedPointer<T>> { + static constexpr ArgumentType Type = ArgumentType::InInterface; +}; + +template <typename T> +struct ArgumentTraits<InCopyHandle<T>> { + static constexpr ArgumentType Type = ArgumentType::InCopyHandle; +}; + +template <typename T> +struct ArgumentTraits<Out<SharedPointer<T>>> { + static constexpr ArgumentType Type = ArgumentType::OutInterface; +}; + +template <typename T> +struct ArgumentTraits<Out<T>> { + static constexpr ArgumentType Type = ArgumentType::OutData; +}; + +template <typename T> +struct ArgumentTraits<OutCopyHandle<T>> { + static constexpr ArgumentType Type = ArgumentType::OutCopyHandle; +}; + +template <typename T> +struct ArgumentTraits<OutMoveHandle<T>> { + static constexpr ArgumentType Type = ArgumentType::OutMoveHandle; +}; + +template <typename T, int A> +struct ArgumentTraits<Buffer<T, A>> { + static constexpr ArgumentType Type = (A & BufferAttr_In) == 0 ? ArgumentType::OutBuffer : ArgumentType::InBuffer; +}; + +template <typename T, int A> +struct ArgumentTraits<LargeData<T, A>> { + static constexpr ArgumentType Type = (A & BufferAttr_In) == 0 ? ArgumentType::OutLargeData : ArgumentType::InLargeData; +}; + +template <typename T> +struct ArgumentTraits { + static constexpr ArgumentType Type = ArgumentType::InData; +}; +// clang-format on + +} // namespace Service diff --git a/src/core/hle/service/glue/glue.cpp b/src/core/hle/service/glue/glue.cpp index 993c3d21d..10376bfac 100644 --- a/src/core/hle/service/glue/glue.cpp +++ b/src/core/hle/service/glue/glue.cpp @@ -8,6 +8,9 @@ #include "core/hle/service/glue/ectx.h" #include "core/hle/service/glue/glue.h" #include "core/hle/service/glue/notif.h" +#include "core/hle/service/glue/time/manager.h" +#include "core/hle/service/glue/time/static.h" +#include "core/hle/service/psc/time/common.h" #include "core/hle/service/server_manager.h" namespace Service::Glue { @@ -31,6 +34,22 @@ void LoopProcess(Core::System& system) { // Notification Services for application server_manager->RegisterNamedService("notif:a", std::make_shared<NOTIF_A>(system)); + // Time + auto time = std::make_shared<Time::TimeManager>(system); + + server_manager->RegisterNamedService( + "time:u", + std::make_shared<Time::StaticService>( + system, Service::PSC::Time::StaticServiceSetupInfo{0, 0, 0, 0, 0, 0}, time, "time:u")); + server_manager->RegisterNamedService( + "time:a", + std::make_shared<Time::StaticService>( + system, Service::PSC::Time::StaticServiceSetupInfo{1, 1, 0, 1, 0, 0}, time, "time:a")); + server_manager->RegisterNamedService( + "time:r", + std::make_shared<Time::StaticService>( + system, Service::PSC::Time::StaticServiceSetupInfo{0, 0, 0, 0, 1, 0}, time, "time:r")); + ServerManager::RunServer(std::move(server_manager)); } diff --git a/src/core/hle/service/glue/time/alarm_worker.cpp b/src/core/hle/service/glue/time/alarm_worker.cpp new file mode 100644 index 000000000..f549ed00a --- /dev/null +++ b/src/core/hle/service/glue/time/alarm_worker.cpp @@ -0,0 +1,82 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/core_timing.h" +#include "core/hle/kernel/svc.h" +#include "core/hle/service/glue/time/alarm_worker.h" +#include "core/hle/service/psc/time/service_manager.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::Glue::Time { + +AlarmWorker::AlarmWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource) + : m_system{system}, m_ctx{system, "Glue:AlarmWorker"}, m_steady_clock_resource{ + steady_clock_resource} {} + +AlarmWorker::~AlarmWorker() { + m_system.CoreTiming().UnscheduleEvent(m_timer_timing_event); + + m_ctx.CloseEvent(m_timer_event); +} + +void AlarmWorker::Initialize(std::shared_ptr<Service::PSC::Time::ServiceManager> time_m) { + m_time_m = std::move(time_m); + + m_timer_event = m_ctx.CreateEvent("Glue:AlarmWorker:TimerEvent"); + m_timer_timing_event = Core::Timing::CreateEvent( + "Glue:AlarmWorker::AlarmTimer", + [this](s64 time, + std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { + m_timer_event->Signal(); + return std::nullopt; + }); + + AttachToClosestAlarmEvent(); +} + +bool AlarmWorker::GetClosestAlarmInfo(Service::PSC::Time::AlarmInfo& out_alarm_info, + s64& out_time) { + bool is_valid{}; + Service::PSC::Time::AlarmInfo alarm_info{}; + s64 closest_time{}; + + auto res = m_time_m->GetClosestAlarmInfo(is_valid, alarm_info, closest_time); + ASSERT(res == ResultSuccess); + + if (is_valid) { + out_alarm_info = alarm_info; + out_time = closest_time; + } + + return is_valid; +} + +void AlarmWorker::OnPowerStateChanged() { + Service::PSC::Time::AlarmInfo closest_alarm_info{}; + s64 closest_time{}; + if (!GetClosestAlarmInfo(closest_alarm_info, closest_time)) { + m_system.CoreTiming().UnscheduleEvent(m_timer_timing_event); + m_timer_event->Clear(); + return; + } + + if (closest_alarm_info.alert_time <= closest_time) { + m_time_m->CheckAndSignalAlarms(); + } else { + auto next_time{closest_alarm_info.alert_time - closest_time}; + + m_system.CoreTiming().UnscheduleEvent(m_timer_timing_event); + m_timer_event->Clear(); + + m_system.CoreTiming().ScheduleEvent(std::chrono::nanoseconds(next_time), + m_timer_timing_event); + } +} + +Result AlarmWorker::AttachToClosestAlarmEvent() { + m_time_m->GetClosestAlarmUpdatedEvent(&m_event); + R_SUCCEED(); +} + +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/glue/time/alarm_worker.h b/src/core/hle/service/glue/time/alarm_worker.h new file mode 100644 index 000000000..f269cffdb --- /dev/null +++ b/src/core/hle/service/glue/time/alarm_worker.h @@ -0,0 +1,53 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/psc/time/common.h" + +namespace Core { +class System; +} + +namespace Service::PSC::Time { +class ServiceManager; +} + +namespace Service::Glue::Time { +class StandardSteadyClockResource; + +class AlarmWorker { +public: + explicit AlarmWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource); + ~AlarmWorker(); + + void Initialize(std::shared_ptr<Service::PSC::Time::ServiceManager> time_m); + + Kernel::KEvent& GetEvent() { + return *m_event; + } + + Kernel::KEvent& GetTimerEvent() { + return *m_timer_event; + } + + void OnPowerStateChanged(); + +private: + bool GetClosestAlarmInfo(Service::PSC::Time::AlarmInfo& out_alarm_info, s64& out_time); + Result AttachToClosestAlarmEvent(); + + Core::System& m_system; + KernelHelpers::ServiceContext m_ctx; + std::shared_ptr<Service::PSC::Time::ServiceManager> m_time_m; + + Kernel::KEvent* m_event{}; + Kernel::KEvent* m_timer_event{}; + std::shared_ptr<Core::Timing::EventType> m_timer_timing_event; + StandardSteadyClockResource& m_steady_clock_resource; +}; + +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/glue/time/file_timestamp_worker.cpp b/src/core/hle/service/glue/time/file_timestamp_worker.cpp new file mode 100644 index 000000000..5a6309549 --- /dev/null +++ b/src/core/hle/service/glue/time/file_timestamp_worker.cpp @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/glue/time/file_timestamp_worker.h" +#include "core/hle/service/psc/time/common.h" +#include "core/hle/service/psc/time/system_clock.h" +#include "core/hle/service/psc/time/time_zone_service.h" + +namespace Service::Glue::Time { + +void FileTimestampWorker::SetFilesystemPosixTime() { + s64 time{}; + Service::PSC::Time::CalendarTime calendar_time{}; + Service::PSC::Time::CalendarAdditionalInfo additional_info{}; + + if (m_initialized && m_system_clock->GetCurrentTime(time) == ResultSuccess && + m_time_zone->ToCalendarTimeWithMyRule(calendar_time, additional_info, time) == + ResultSuccess) { + // TODO IFileSystemProxy::SetCurrentPosixTime + } +} + +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/glue/time/file_timestamp_worker.h b/src/core/hle/service/glue/time/file_timestamp_worker.h new file mode 100644 index 000000000..5f8b9b049 --- /dev/null +++ b/src/core/hle/service/glue/time/file_timestamp_worker.h @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <memory> + +#include "common/common_types.h" + +namespace Service::PSC::Time { +class SystemClock; +class TimeZoneService; +} // namespace Service::PSC::Time + +namespace Service::Glue::Time { + +class FileTimestampWorker { +public: + FileTimestampWorker() = default; + + void SetFilesystemPosixTime(); + + std::shared_ptr<Service::PSC::Time::SystemClock> m_system_clock{}; + std::shared_ptr<Service::PSC::Time::TimeZoneService> m_time_zone{}; + bool m_initialized{}; +}; + +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/glue/time/manager.cpp b/src/core/hle/service/glue/time/manager.cpp new file mode 100644 index 000000000..6423e5089 --- /dev/null +++ b/src/core/hle/service/glue/time/manager.cpp @@ -0,0 +1,277 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <chrono> + +#include "core/core.h" +#include "core/core_timing.h" + +#include "common/settings.h" +#include "common/time_zone.h" +#include "core/file_sys/vfs.h" +#include "core/hle/kernel/svc.h" +#include "core/hle/service/glue/time/manager.h" +#include "core/hle/service/glue/time/time_zone_binary.h" +#include "core/hle/service/psc/time/service_manager.h" +#include "core/hle/service/psc/time/static.h" +#include "core/hle/service/psc/time/system_clock.h" +#include "core/hle/service/psc/time/time_zone_service.h" +#include "core/hle/service/set/system_settings_server.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::Glue::Time { +namespace { + +template <typename T> +T GetSettingsItemValue(std::shared_ptr<Service::Set::ISystemSettingsServer>& set_sys, + const char* category, const char* name) { + std::vector<u8> interval_buf; + auto res = set_sys->GetSettingsItemValue(interval_buf, category, name); + ASSERT(res == ResultSuccess); + + T v{}; + std::memcpy(&v, interval_buf.data(), sizeof(T)); + return v; +} + +s64 CalendarTimeToEpoch(Service::PSC::Time::CalendarTime calendar) { + constexpr auto is_leap = [](s32 year) -> bool { + return (((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0)); + }; + constexpr std::array<s32, 12> MonthStartDayOfYear{ + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, + }; + + s16 month_s16{calendar.month}; + s8 month{static_cast<s8>(((month_s16 * 43) & ~std::numeric_limits<s16>::max()) + + ((month_s16 * 43) >> 9))}; + s8 month_index{static_cast<s8>(calendar.month - 12 * month)}; + if (month_index == 0) { + month_index = 12; + } + s32 year{(month + calendar.year) - !month_index}; + s32 v8{year >= 0 ? year : year + 3}; + + s64 days_since_epoch = calendar.day + MonthStartDayOfYear[month_index - 1]; + days_since_epoch += (year * 365) + (v8 / 4) - (year / 100) + (year / 400) - 365; + + if (month_index <= 2 && is_leap(year)) { + days_since_epoch--; + } + auto epoch_s{((24ll * days_since_epoch + calendar.hour) * 60ll + calendar.minute) * 60ll + + calendar.second}; + return epoch_s - 62135683200ll; +} + +s64 GetEpochTimeFromInitialYear(std::shared_ptr<Service::Set::ISystemSettingsServer>& set_sys) { + Service::PSC::Time::CalendarTime calendar{ + .year = GetSettingsItemValue<s16>(set_sys, "time", "standard_user_clock_initial_year"), + .month = 1, + .day = 1, + .hour = 0, + .minute = 0, + .second = 0, + }; + return CalendarTimeToEpoch(calendar); +} + +Service::PSC::Time::LocationName GetTimeZoneString(Service::PSC::Time::LocationName& in_name) { + auto configured_zone = Settings::GetTimeZoneString(Settings::values.time_zone_index.GetValue()); + + Service::PSC::Time::LocationName configured_name{}; + std::memcpy(configured_name.name.data(), configured_zone.data(), + std::min(configured_name.name.size(), configured_zone.size())); + + if (!IsTimeZoneBinaryValid(configured_name)) { + configured_zone = Common::TimeZone::FindSystemTimeZone(); + configured_name = {}; + std::memcpy(configured_name.name.data(), configured_zone.data(), + std::min(configured_name.name.size(), configured_zone.size())); + } + + ASSERT_MSG(IsTimeZoneBinaryValid(configured_name), "Invalid time zone {}!", + configured_name.name.data()); + + return configured_name; +} + +} // namespace + +TimeManager::TimeManager(Core::System& system) + : m_steady_clock_resource{system}, m_worker{system, m_steady_clock_resource, + m_file_timestamp_worker} { + m_time_m = + system.ServiceManager().GetService<Service::PSC::Time::ServiceManager>("time:m", true); + + auto res = m_time_m->GetStaticServiceAsServiceManager(m_time_sm); + ASSERT(res == ResultSuccess); + + m_set_sys = + system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true); + + res = MountTimeZoneBinary(system); + ASSERT(res == ResultSuccess); + + m_worker.Initialize(m_time_sm, m_set_sys); + + res = m_time_sm->GetStandardUserSystemClock(m_file_timestamp_worker.m_system_clock); + ASSERT(res == ResultSuccess); + + res = m_time_sm->GetTimeZoneService(m_file_timestamp_worker.m_time_zone); + ASSERT(res == ResultSuccess); + + res = SetupStandardSteadyClockCore(); + ASSERT(res == ResultSuccess); + + Service::PSC::Time::SystemClockContext user_clock_context{}; + res = m_set_sys->GetUserSystemClockContext(user_clock_context); + ASSERT(res == ResultSuccess); + + // TODO the local clock should initialise with this epoch time, and be updated somewhere else on + // first boot to update it, but I haven't been able to find that point (likely via ntc's auto + // correct as it's defaulted to be enabled). So to get a time that isn't stuck in the past for + // first boot, grab the current real seconds. + auto epoch_time{GetEpochTimeFromInitialYear(m_set_sys)}; + if (user_clock_context == Service::PSC::Time::SystemClockContext{}) { + m_steady_clock_resource.GetRtcTimeInSeconds(epoch_time); + } + + res = m_time_m->SetupStandardLocalSystemClockCore(user_clock_context, epoch_time); + ASSERT(res == ResultSuccess); + + Service::PSC::Time::SystemClockContext network_clock_context{}; + res = m_set_sys->GetNetworkSystemClockContext(network_clock_context); + ASSERT(res == ResultSuccess); + + auto network_accuracy_m{GetSettingsItemValue<s32>( + m_set_sys, "time", "standard_network_clock_sufficient_accuracy_minutes")}; + auto one_minute_ns{ + std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::minutes(1)).count()}; + s64 network_accuracy_ns{network_accuracy_m * one_minute_ns}; + + res = m_time_m->SetupStandardNetworkSystemClockCore(network_clock_context, network_accuracy_ns); + ASSERT(res == ResultSuccess); + + bool is_automatic_correction_enabled{}; + res = m_set_sys->IsUserSystemClockAutomaticCorrectionEnabled(is_automatic_correction_enabled); + ASSERT(res == ResultSuccess); + + Service::PSC::Time::SteadyClockTimePoint automatic_correction_time_point{}; + res = m_set_sys->GetUserSystemClockAutomaticCorrectionUpdatedTime( + automatic_correction_time_point); + ASSERT(res == ResultSuccess); + + res = m_time_m->SetupStandardUserSystemClockCore(automatic_correction_time_point, + is_automatic_correction_enabled); + ASSERT(res == ResultSuccess); + + res = m_time_m->SetupEphemeralNetworkSystemClockCore(); + ASSERT(res == ResultSuccess); + + res = SetupTimeZoneServiceCore(); + ASSERT(res == ResultSuccess); + + s64 rtc_time_s{}; + res = m_steady_clock_resource.GetRtcTimeInSeconds(rtc_time_s); + ASSERT(res == ResultSuccess); + + // TODO system report "launch" + // "rtc_reset" = m_steady_clock_resource.m_rtc_reset + // "rtc_value" = rtc_time_s + + m_worker.StartThread(); + + m_file_timestamp_worker.m_initialized = true; + + s64 system_clock_time{}; + if (m_file_timestamp_worker.m_system_clock->GetCurrentTime(system_clock_time) == + ResultSuccess) { + Service::PSC::Time::CalendarTime calendar_time{}; + Service::PSC::Time::CalendarAdditionalInfo calendar_additional{}; + if (m_file_timestamp_worker.m_time_zone->ToCalendarTimeWithMyRule( + calendar_time, calendar_additional, system_clock_time) == ResultSuccess) { + // TODO IFileSystemProxy::SetCurrentPosixTime(system_clock_time, + // calendar_additional.ut_offset) + } + } +} + +Result TimeManager::SetupStandardSteadyClockCore() { + Common::UUID external_clock_source_id{}; + auto res = m_set_sys->GetExternalSteadyClockSourceId(external_clock_source_id); + ASSERT(res == ResultSuccess); + + s64 external_steady_clock_internal_offset_s{}; + res = m_set_sys->GetExternalSteadyClockInternalOffset(external_steady_clock_internal_offset_s); + ASSERT(res == ResultSuccess); + + auto one_second_ns{ + std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()}; + s64 external_steady_clock_internal_offset_ns{external_steady_clock_internal_offset_s * + one_second_ns}; + + s32 standard_steady_clock_test_offset_m{ + GetSettingsItemValue<s32>(m_set_sys, "time", "standard_steady_clock_test_offset_minutes")}; + auto one_minute_ns{ + std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::minutes(1)).count()}; + s64 standard_steady_clock_test_offset_ns{standard_steady_clock_test_offset_m * one_minute_ns}; + + auto reset_detected = m_steady_clock_resource.GetResetDetected(); + if (reset_detected) { + external_clock_source_id = {}; + } + + Common::UUID clock_source_id{}; + m_steady_clock_resource.Initialize(&clock_source_id, &external_clock_source_id); + + if (clock_source_id != external_clock_source_id) { + m_set_sys->SetExternalSteadyClockSourceId(clock_source_id); + } + + res = m_time_m->SetupStandardSteadyClockCore(clock_source_id, m_steady_clock_resource.GetTime(), + external_steady_clock_internal_offset_ns, + standard_steady_clock_test_offset_ns, + reset_detected); + ASSERT(res == ResultSuccess); + R_SUCCEED(); +} + +Result TimeManager::SetupTimeZoneServiceCore() { + Service::PSC::Time::LocationName name{}; + auto res = m_set_sys->GetDeviceTimeZoneLocationName(name); + ASSERT(res == ResultSuccess); + + auto configured_zone = GetTimeZoneString(name); + + if (configured_zone.name != name.name) { + m_set_sys->SetDeviceTimeZoneLocationName(configured_zone); + name = configured_zone; + + std::shared_ptr<Service::PSC::Time::SystemClock> local_clock; + m_time_sm->GetStandardLocalSystemClock(local_clock); + Service::PSC::Time::SystemClockContext context{}; + local_clock->GetSystemClockContext(context); + m_set_sys->SetDeviceTimeZoneLocationUpdatedTime(context.steady_time_point); + } + + Service::PSC::Time::SteadyClockTimePoint time_point{}; + res = m_set_sys->GetDeviceTimeZoneLocationUpdatedTime(time_point); + ASSERT(res == ResultSuccess); + + auto location_count = GetTimeZoneCount(); + Service::PSC::Time::RuleVersion rule_version{}; + GetTimeZoneVersion(rule_version); + + std::span<const u8> rule_buffer{}; + size_t rule_size{}; + res = GetTimeZoneRule(rule_buffer, rule_size, name); + ASSERT(res == ResultSuccess); + + res = m_time_m->SetupTimeZoneServiceCore(name, time_point, rule_version, location_count, + rule_buffer); + ASSERT(res == ResultSuccess); + + R_SUCCEED(); +} + +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/glue/time/manager.h b/src/core/hle/service/glue/time/manager.h new file mode 100644 index 000000000..a46ec6364 --- /dev/null +++ b/src/core/hle/service/glue/time/manager.h @@ -0,0 +1,42 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <functional> +#include <string> + +#include "common/common_types.h" +#include "core/file_sys/vfs_types.h" +#include "core/hle/service/glue/time/file_timestamp_worker.h" +#include "core/hle/service/glue/time/standard_steady_clock_resource.h" +#include "core/hle/service/glue/time/worker.h" +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Service::PSC::Time { +class ServiceManager; +class StaticService; +} // namespace Service::PSC::Time + +namespace Service::Glue::Time { +class TimeManager { +public: + explicit TimeManager(Core::System& system); + + std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys; + + std::shared_ptr<Service::PSC::Time::ServiceManager> m_time_m{}; + std::shared_ptr<Service::PSC::Time::StaticService> m_time_sm{}; + StandardSteadyClockResource m_steady_clock_resource; + FileTimestampWorker m_file_timestamp_worker; + TimeWorker m_worker; + +private: + Result SetupStandardSteadyClockCore(); + Result SetupTimeZoneServiceCore(); +}; +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/glue/time/pm_state_change_handler.cpp b/src/core/hle/service/glue/time/pm_state_change_handler.cpp new file mode 100644 index 000000000..7470fb225 --- /dev/null +++ b/src/core/hle/service/glue/time/pm_state_change_handler.cpp @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/glue/time/pm_state_change_handler.h" + +namespace Service::Glue::Time { + +PmStateChangeHandler::PmStateChangeHandler(AlarmWorker& alarm_worker) + : m_alarm_worker{alarm_worker} { + // TODO Initialize IPmModule, dependent on Rtc and Fs +} + +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/glue/time/pm_state_change_handler.h b/src/core/hle/service/glue/time/pm_state_change_handler.h new file mode 100644 index 000000000..27d9f7872 --- /dev/null +++ b/src/core/hle/service/glue/time/pm_state_change_handler.h @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_types.h" + +namespace Service::Glue::Time { +class AlarmWorker; + +class PmStateChangeHandler { +public: + explicit PmStateChangeHandler(AlarmWorker& alarm_worker); + + AlarmWorker& m_alarm_worker; + s32 m_priority{}; +}; +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/glue/time/standard_steady_clock_resource.cpp b/src/core/hle/service/glue/time/standard_steady_clock_resource.cpp new file mode 100644 index 000000000..5ebaa33e0 --- /dev/null +++ b/src/core/hle/service/glue/time/standard_steady_clock_resource.cpp @@ -0,0 +1,123 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <chrono> + +#include "common/settings.h" +#include "core/core.h" +#include "core/core_timing.h" +#include "core/hle/kernel/svc.h" +#include "core/hle/service/glue/time/standard_steady_clock_resource.h" +#include "core/hle/service/psc/time/errors.h" + +namespace Service::Glue::Time { +namespace { +[[maybe_unused]] constexpr u32 Max77620PmicSession = 0x3A000001; +[[maybe_unused]] constexpr u32 Max77620RtcSession = 0x3B000001; + +Result GetTimeInSeconds(Core::System& system, s64& out_time_s) { + out_time_s = std::chrono::duration_cast<std::chrono::seconds>( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + + if (Settings::values.custom_rtc_enabled) { + out_time_s += Settings::values.custom_rtc_offset.GetValue(); + } + R_SUCCEED(); +} +} // namespace + +StandardSteadyClockResource::StandardSteadyClockResource(Core::System& system) : m_system{system} {} + +void StandardSteadyClockResource::Initialize(Common::UUID* out_source_id, + Common::UUID* external_source_id) { + constexpr size_t NUM_TRIES{20}; + + size_t i{0}; + Result res{ResultSuccess}; + for (; i < NUM_TRIES; i++) { + res = SetCurrentTime(); + if (res == ResultSuccess) { + break; + } + Kernel::Svc::SleepThread(m_system, std::chrono::duration_cast<std::chrono::nanoseconds>( + std::chrono::milliseconds(1)) + .count()); + } + + if (i < NUM_TRIES) { + m_set_time_result = ResultSuccess; + if (*external_source_id != Service::PSC::Time::ClockSourceId{}) { + m_clock_source_id = *external_source_id; + } else { + m_clock_source_id = Common::UUID::MakeRandom(); + } + } else { + m_set_time_result = res; + auto ticks{m_system.CoreTiming().GetClockTicks()}; + m_time = -Service::PSC::Time::ConvertToTimeSpan(ticks).count(); + m_clock_source_id = Common::UUID::MakeRandom(); + } + + if (out_source_id) { + *out_source_id = m_clock_source_id; + } +} + +bool StandardSteadyClockResource::GetResetDetected() { + // TODO: + // call Rtc::GetRtcResetDetected(Max77620RtcSession) + // if detected: + // SetSys::SetExternalSteadyClockSourceId(invalid_id) + // Rtc::ClearRtcResetDetected(Max77620RtcSession) + // set m_rtc_reset to result + // Instead, only set reset to true if we're booting for the first time. + m_rtc_reset = false; + return m_rtc_reset; +} + +Result StandardSteadyClockResource::SetCurrentTime() { + auto start_tick{m_system.CoreTiming().GetClockTicks()}; + + s64 rtc_time_s{}; + // TODO R_TRY(Rtc::GetTimeInSeconds(rtc_time_s, Max77620RtcSession)) + R_TRY(GetTimeInSeconds(m_system, rtc_time_s)); + + auto end_tick{m_system.CoreTiming().GetClockTicks()}; + auto diff{Service::PSC::Time::ConvertToTimeSpan(end_tick - start_tick)}; + // Why is this here? + R_UNLESS(diff < std::chrono::milliseconds(101), Service::PSC::Time::ResultRtcTimeout); + + auto one_second_ns{ + std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()}; + s64 boot_time{rtc_time_s * one_second_ns - + Service::PSC::Time::ConvertToTimeSpan(end_tick).count()}; + + std::scoped_lock l{m_mutex}; + m_time = boot_time; + R_SUCCEED(); +} + +Result StandardSteadyClockResource::GetRtcTimeInSeconds(s64& out_time) { + // TODO + // R_TRY(Rtc::GetTimeInSeconds(time_s, Max77620RtcSession) + R_RETURN(GetTimeInSeconds(m_system, out_time)); +} + +void StandardSteadyClockResource::UpdateTime() { + constexpr size_t NUM_TRIES{3}; + + size_t i{0}; + Result res{ResultSuccess}; + for (; i < NUM_TRIES; i++) { + res = SetCurrentTime(); + if (res == ResultSuccess) { + break; + } + Kernel::Svc::SleepThread(m_system, std::chrono::duration_cast<std::chrono::nanoseconds>( + std::chrono::milliseconds(1)) + .count()); + } +} + +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/glue/time/standard_steady_clock_resource.h b/src/core/hle/service/glue/time/standard_steady_clock_resource.h new file mode 100644 index 000000000..978d6b63b --- /dev/null +++ b/src/core/hle/service/glue/time/standard_steady_clock_resource.h @@ -0,0 +1,41 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <mutex> + +#include "common/common_types.h" +#include "core/hle/result.h" +#include "core/hle/service/psc/time/common.h" + +namespace Core { +class System; +} + +namespace Service::Glue::Time { +class StandardSteadyClockResource { +public: + StandardSteadyClockResource(Core::System& system); + + void Initialize(Common::UUID* out_source_id, Common::UUID* external_source_id); + + s64 GetTime() const { + return m_time; + } + + bool GetResetDetected(); + Result SetCurrentTime(); + Result GetRtcTimeInSeconds(s64& out_time); + void UpdateTime(); + +private: + Core::System& m_system; + + std::mutex m_mutex; + Service::PSC::Time::ClockSourceId m_clock_source_id{}; + s64 m_time{}; + Result m_set_time_result; + bool m_rtc_reset; +}; +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/glue/time/static.cpp b/src/core/hle/service/glue/time/static.cpp new file mode 100644 index 000000000..63b7d91da --- /dev/null +++ b/src/core/hle/service/glue/time/static.cpp @@ -0,0 +1,448 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <chrono> + +#include "core/core.h" +#include "core/hle/kernel/k_shared_memory.h" +#include "core/hle/kernel/svc.h" +#include "core/hle/service/glue/time/file_timestamp_worker.h" +#include "core/hle/service/glue/time/static.h" +#include "core/hle/service/psc/time/errors.h" +#include "core/hle/service/psc/time/service_manager.h" +#include "core/hle/service/psc/time/static.h" +#include "core/hle/service/psc/time/steady_clock.h" +#include "core/hle/service/psc/time/system_clock.h" +#include "core/hle/service/psc/time/time_zone_service.h" +#include "core/hle/service/set/system_settings_server.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::Glue::Time { +namespace { +template <typename T> +T GetSettingsItemValue(std::shared_ptr<Service::Set::ISystemSettingsServer>& set_sys, + const char* category, const char* name) { + std::vector<u8> interval_buf; + auto res = set_sys->GetSettingsItemValue(interval_buf, category, name); + ASSERT(res == ResultSuccess); + + T v{}; + std::memcpy(&v, interval_buf.data(), sizeof(T)); + return v; +} +} // namespace + +StaticService::StaticService(Core::System& system_, + Service::PSC::Time::StaticServiceSetupInfo setup_info, + std::shared_ptr<TimeManager> time, const char* name) + : ServiceFramework{system_, name}, m_system{system_}, m_time_m{time->m_time_m}, + m_setup_info{setup_info}, m_time_sm{time->m_time_sm}, + m_file_timestamp_worker{time->m_file_timestamp_worker}, m_standard_steady_clock_resource{ + time->m_steady_clock_resource} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &StaticService::Handle_GetStandardUserSystemClock, "GetStandardUserSystemClock"}, + {1, &StaticService::Handle_GetStandardNetworkSystemClock, "GetStandardNetworkSystemClock"}, + {2, &StaticService::Handle_GetStandardSteadyClock, "GetStandardSteadyClock"}, + {3, &StaticService::Handle_GetTimeZoneService, "GetTimeZoneService"}, + {4, &StaticService::Handle_GetStandardLocalSystemClock, "GetStandardLocalSystemClock"}, + {5, &StaticService::Handle_GetEphemeralNetworkSystemClock, "GetEphemeralNetworkSystemClock"}, + {20, &StaticService::Handle_GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"}, + {50, &StaticService::Handle_SetStandardSteadyClockInternalOffset, "SetStandardSteadyClockInternalOffset"}, + {51, &StaticService::Handle_GetStandardSteadyClockRtcValue, "GetStandardSteadyClockRtcValue"}, + {100, &StaticService::Handle_IsStandardUserSystemClockAutomaticCorrectionEnabled, "IsStandardUserSystemClockAutomaticCorrectionEnabled"}, + {101, &StaticService::Handle_SetStandardUserSystemClockAutomaticCorrectionEnabled, "SetStandardUserSystemClockAutomaticCorrectionEnabled"}, + {102, &StaticService::Handle_GetStandardUserSystemClockInitialYear, "GetStandardUserSystemClockInitialYear"}, + {200, &StaticService::Handle_IsStandardNetworkSystemClockAccuracySufficient, "IsStandardNetworkSystemClockAccuracySufficient"}, + {201, &StaticService::Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedTime, "GetStandardUserSystemClockAutomaticCorrectionUpdatedTime"}, + {300, &StaticService::Handle_CalculateMonotonicSystemClockBaseTimePoint, "CalculateMonotonicSystemClockBaseTimePoint"}, + {400, &StaticService::Handle_GetClockSnapshot, "GetClockSnapshot"}, + {401, &StaticService::Handle_GetClockSnapshotFromSystemClockContext, "GetClockSnapshotFromSystemClockContext"}, + {500, &StaticService::Handle_CalculateStandardUserSystemClockDifferenceByUser, "CalculateStandardUserSystemClockDifferenceByUser"}, + {501, &StaticService::Handle_CalculateSpanBetween, "CalculateSpanBetween"}, + }; + // clang-format on + + RegisterHandlers(functions); + + m_set_sys = + m_system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true); + + if (m_setup_info.can_write_local_clock && m_setup_info.can_write_user_clock && + !m_setup_info.can_write_network_clock && m_setup_info.can_write_timezone_device_location && + !m_setup_info.can_write_steady_clock && !m_setup_info.can_write_uninitialized_clock) { + m_time_m->GetStaticServiceAsAdmin(m_wrapped_service); + } else if (!m_setup_info.can_write_local_clock && !m_setup_info.can_write_user_clock && + !m_setup_info.can_write_network_clock && + !m_setup_info.can_write_timezone_device_location && + !m_setup_info.can_write_steady_clock && + !m_setup_info.can_write_uninitialized_clock) { + m_time_m->GetStaticServiceAsUser(m_wrapped_service); + } else if (!m_setup_info.can_write_local_clock && !m_setup_info.can_write_user_clock && + !m_setup_info.can_write_network_clock && + !m_setup_info.can_write_timezone_device_location && + m_setup_info.can_write_steady_clock && !m_setup_info.can_write_uninitialized_clock) { + m_time_m->GetStaticServiceAsRepair(m_wrapped_service); + } else { + UNREACHABLE(); + } + + auto res = m_wrapped_service->GetTimeZoneService(m_time_zone); + ASSERT(res == ResultSuccess); +} + +void StaticService::Handle_GetStandardUserSystemClock(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr<Service::PSC::Time::SystemClock> service{}; + auto res = GetStandardUserSystemClock(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface<Service::PSC::Time::SystemClock>(std::move(service)); +} + +void StaticService::Handle_GetStandardNetworkSystemClock(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr<Service::PSC::Time::SystemClock> service{}; + auto res = GetStandardNetworkSystemClock(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface<Service::PSC::Time::SystemClock>(std::move(service)); +} + +void StaticService::Handle_GetStandardSteadyClock(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr<Service::PSC::Time::SteadyClock> service{}; + auto res = GetStandardSteadyClock(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface(std::move(service)); +} + +void StaticService::Handle_GetTimeZoneService(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr<TimeZoneService> service{}; + auto res = GetTimeZoneService(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface(std::move(service)); +} + +void StaticService::Handle_GetStandardLocalSystemClock(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr<Service::PSC::Time::SystemClock> service{}; + auto res = GetStandardLocalSystemClock(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface<Service::PSC::Time::SystemClock>(std::move(service)); +} + +void StaticService::Handle_GetEphemeralNetworkSystemClock(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr<Service::PSC::Time::SystemClock> service{}; + auto res = GetEphemeralNetworkSystemClock(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface<Service::PSC::Time::SystemClock>(std::move(service)); +} + +void StaticService::Handle_GetSharedMemoryNativeHandle(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + Kernel::KSharedMemory* shared_memory{}; + auto res = GetSharedMemoryNativeHandle(&shared_memory); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(res); + rb.PushCopyObjects(shared_memory); +} + +void StaticService::Handle_SetStandardSteadyClockInternalOffset(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto offset_ns{rp.Pop<s64>()}; + + auto res = SetStandardSteadyClockInternalOffset(offset_ns); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void StaticService::Handle_GetStandardSteadyClockRtcValue(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + s64 rtc_value{}; + auto res = GetStandardSteadyClockRtcValue(rtc_value); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(res); + rb.Push(rtc_value); +} + +void StaticService::Handle_IsStandardUserSystemClockAutomaticCorrectionEnabled( + HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + bool is_enabled{}; + auto res = IsStandardUserSystemClockAutomaticCorrectionEnabled(is_enabled); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.Push<bool>(is_enabled); +} + +void StaticService::Handle_SetStandardUserSystemClockAutomaticCorrectionEnabled( + HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto automatic_correction{rp.Pop<bool>()}; + + auto res = SetStandardUserSystemClockAutomaticCorrectionEnabled(automatic_correction); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void StaticService::Handle_GetStandardUserSystemClockInitialYear(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + s32 initial_year{}; + auto res = GetStandardUserSystemClockInitialYear(initial_year); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.Push(initial_year); +} + +void StaticService::Handle_IsStandardNetworkSystemClockAccuracySufficient(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + bool is_sufficient{}; + auto res = IsStandardNetworkSystemClockAccuracySufficient(is_sufficient); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.Push<bool>(is_sufficient); +} + +void StaticService::Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedTime( + HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + Service::PSC::Time::SteadyClockTimePoint time_point{}; + auto res = GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(time_point); + + IPC::ResponseBuilder rb{ctx, + 2 + sizeof(Service::PSC::Time::SteadyClockTimePoint) / sizeof(u32)}; + rb.Push(res); + rb.PushRaw<Service::PSC::Time::SteadyClockTimePoint>(time_point); +} + +void StaticService::Handle_CalculateMonotonicSystemClockBaseTimePoint(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto context{rp.PopRaw<Service::PSC::Time::SystemClockContext>()}; + + s64 time{}; + auto res = CalculateMonotonicSystemClockBaseTimePoint(time, context); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(res); + rb.Push<s64>(time); +} + +void StaticService::Handle_GetClockSnapshot(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto type{rp.PopEnum<Service::PSC::Time::TimeType>()}; + + Service::PSC::Time::ClockSnapshot snapshot{}; + auto res = GetClockSnapshot(snapshot, type); + + ctx.WriteBuffer(snapshot); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void StaticService::Handle_GetClockSnapshotFromSystemClockContext(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto clock_type{rp.PopEnum<Service::PSC::Time::TimeType>()}; + [[maybe_unused]] auto alignment{rp.Pop<u32>()}; + auto user_context{rp.PopRaw<Service::PSC::Time::SystemClockContext>()}; + auto network_context{rp.PopRaw<Service::PSC::Time::SystemClockContext>()}; + + Service::PSC::Time::ClockSnapshot snapshot{}; + auto res = + GetClockSnapshotFromSystemClockContext(snapshot, user_context, network_context, clock_type); + + ctx.WriteBuffer(snapshot); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void StaticService::Handle_CalculateStandardUserSystemClockDifferenceByUser( + HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + Service::PSC::Time::ClockSnapshot a{}; + Service::PSC::Time::ClockSnapshot b{}; + + auto a_buffer{ctx.ReadBuffer(0)}; + auto b_buffer{ctx.ReadBuffer(1)}; + + std::memcpy(&a, a_buffer.data(), sizeof(Service::PSC::Time::ClockSnapshot)); + std::memcpy(&b, b_buffer.data(), sizeof(Service::PSC::Time::ClockSnapshot)); + + s64 difference{}; + auto res = CalculateStandardUserSystemClockDifferenceByUser(difference, a, b); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(res); + rb.Push(difference); +} + +void StaticService::Handle_CalculateSpanBetween(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + Service::PSC::Time::ClockSnapshot a{}; + Service::PSC::Time::ClockSnapshot b{}; + + auto a_buffer{ctx.ReadBuffer(0)}; + auto b_buffer{ctx.ReadBuffer(1)}; + + std::memcpy(&a, a_buffer.data(), sizeof(Service::PSC::Time::ClockSnapshot)); + std::memcpy(&b, b_buffer.data(), sizeof(Service::PSC::Time::ClockSnapshot)); + + s64 time{}; + auto res = CalculateSpanBetween(time, a, b); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(res); + rb.Push(time); +} + +// =============================== Implementations =========================== + +Result StaticService::GetStandardUserSystemClock( + std::shared_ptr<Service::PSC::Time::SystemClock>& out_service) { + R_RETURN(m_wrapped_service->GetStandardUserSystemClock(out_service)); +} + +Result StaticService::GetStandardNetworkSystemClock( + std::shared_ptr<Service::PSC::Time::SystemClock>& out_service) { + R_RETURN(m_wrapped_service->GetStandardNetworkSystemClock(out_service)); +} + +Result StaticService::GetStandardSteadyClock( + std::shared_ptr<Service::PSC::Time::SteadyClock>& out_service) { + R_RETURN(m_wrapped_service->GetStandardSteadyClock(out_service)); +} + +Result StaticService::GetTimeZoneService(std::shared_ptr<TimeZoneService>& out_service) { + out_service = std::make_shared<TimeZoneService>(m_system, m_file_timestamp_worker, + m_setup_info.can_write_timezone_device_location, + m_time_zone); + R_SUCCEED(); +} + +Result StaticService::GetStandardLocalSystemClock( + std::shared_ptr<Service::PSC::Time::SystemClock>& out_service) { + R_RETURN(m_wrapped_service->GetStandardLocalSystemClock(out_service)); +} + +Result StaticService::GetEphemeralNetworkSystemClock( + std::shared_ptr<Service::PSC::Time::SystemClock>& out_service) { + R_RETURN(m_wrapped_service->GetEphemeralNetworkSystemClock(out_service)); +} + +Result StaticService::GetSharedMemoryNativeHandle(Kernel::KSharedMemory** out_shared_memory) { + R_RETURN(m_wrapped_service->GetSharedMemoryNativeHandle(out_shared_memory)); +} + +Result StaticService::SetStandardSteadyClockInternalOffset(s64 offset_ns) { + R_UNLESS(m_setup_info.can_write_steady_clock, Service::PSC::Time::ResultPermissionDenied); + + R_RETURN(m_set_sys->SetExternalSteadyClockInternalOffset( + offset_ns / + std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count())); +} + +Result StaticService::GetStandardSteadyClockRtcValue(s64& out_rtc_value) { + R_RETURN(m_standard_steady_clock_resource.GetRtcTimeInSeconds(out_rtc_value)); +} + +Result StaticService::IsStandardUserSystemClockAutomaticCorrectionEnabled( + bool& out_automatic_correction) { + R_RETURN(m_wrapped_service->IsStandardUserSystemClockAutomaticCorrectionEnabled( + out_automatic_correction)); +} + +Result StaticService::SetStandardUserSystemClockAutomaticCorrectionEnabled( + bool automatic_correction) { + R_RETURN(m_wrapped_service->SetStandardUserSystemClockAutomaticCorrectionEnabled( + automatic_correction)); +} + +Result StaticService::GetStandardUserSystemClockInitialYear(s32& out_year) { + out_year = GetSettingsItemValue<s32>(m_set_sys, "time", "standard_user_clock_initial_year"); + R_SUCCEED(); +} + +Result StaticService::IsStandardNetworkSystemClockAccuracySufficient(bool& out_is_sufficient) { + R_RETURN(m_wrapped_service->IsStandardNetworkSystemClockAccuracySufficient(out_is_sufficient)); +} + +Result StaticService::GetStandardUserSystemClockAutomaticCorrectionUpdatedTime( + Service::PSC::Time::SteadyClockTimePoint& out_time_point) { + R_RETURN(m_wrapped_service->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime( + out_time_point)); +} + +Result StaticService::CalculateMonotonicSystemClockBaseTimePoint( + s64& out_time, Service::PSC::Time::SystemClockContext& context) { + R_RETURN(m_wrapped_service->CalculateMonotonicSystemClockBaseTimePoint(out_time, context)); +} + +Result StaticService::GetClockSnapshot(Service::PSC::Time::ClockSnapshot& out_snapshot, + Service::PSC::Time::TimeType type) { + R_RETURN(m_wrapped_service->GetClockSnapshot(out_snapshot, type)); +} + +Result StaticService::GetClockSnapshotFromSystemClockContext( + Service::PSC::Time::ClockSnapshot& out_snapshot, + Service::PSC::Time::SystemClockContext& user_context, + Service::PSC::Time::SystemClockContext& network_context, Service::PSC::Time::TimeType type) { + R_RETURN(m_wrapped_service->GetClockSnapshotFromSystemClockContext(out_snapshot, user_context, + network_context, type)); +} + +Result StaticService::CalculateStandardUserSystemClockDifferenceByUser( + s64& out_time, Service::PSC::Time::ClockSnapshot& a, Service::PSC::Time::ClockSnapshot& b) { + R_RETURN(m_wrapped_service->CalculateStandardUserSystemClockDifferenceByUser(out_time, a, b)); +} + +Result StaticService::CalculateSpanBetween(s64& out_time, Service::PSC::Time::ClockSnapshot& a, + Service::PSC::Time::ClockSnapshot& b) { + R_RETURN(m_wrapped_service->CalculateSpanBetween(out_time, a, b)); +} + +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/glue/time/static.h b/src/core/hle/service/glue/time/static.h new file mode 100644 index 000000000..75fe4e2cd --- /dev/null +++ b/src/core/hle/service/glue/time/static.h @@ -0,0 +1,110 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hle/service/glue/time/manager.h" +#include "core/hle/service/glue/time/time_zone.h" +#include "core/hle/service/psc/time/common.h" + +namespace Core { +class System; +} + +namespace Service::Set { +class ISystemSettingsServer; +} + +namespace Service::PSC::Time { +class StaticService; +class SystemClock; +class SteadyClock; +class TimeZoneService; +class ServiceManager; +} // namespace Service::PSC::Time + +namespace Service::Glue::Time { +class FileTimestampWorker; +class StandardSteadyClockResource; + +class StaticService final : public ServiceFramework<StaticService> { +public: + explicit StaticService(Core::System& system, + Service::PSC::Time::StaticServiceSetupInfo setup_info, + std::shared_ptr<TimeManager> time, const char* name); + + ~StaticService() override = default; + + Result GetStandardUserSystemClock( + std::shared_ptr<Service::PSC::Time::SystemClock>& out_service); + Result GetStandardNetworkSystemClock( + std::shared_ptr<Service::PSC::Time::SystemClock>& out_service); + Result GetStandardSteadyClock(std::shared_ptr<Service::PSC::Time::SteadyClock>& out_service); + Result GetTimeZoneService(std::shared_ptr<TimeZoneService>& out_service); + Result GetStandardLocalSystemClock( + std::shared_ptr<Service::PSC::Time::SystemClock>& out_service); + Result GetEphemeralNetworkSystemClock( + std::shared_ptr<Service::PSC::Time::SystemClock>& out_service); + Result GetSharedMemoryNativeHandle(Kernel::KSharedMemory** out_shared_memory); + Result SetStandardSteadyClockInternalOffset(s64 offset); + Result GetStandardSteadyClockRtcValue(s64& out_rtc_value); + Result IsStandardUserSystemClockAutomaticCorrectionEnabled(bool& out_automatic_correction); + Result SetStandardUserSystemClockAutomaticCorrectionEnabled(bool automatic_correction); + Result GetStandardUserSystemClockInitialYear(s32& out_year); + Result IsStandardNetworkSystemClockAccuracySufficient(bool& out_is_sufficient); + Result GetStandardUserSystemClockAutomaticCorrectionUpdatedTime( + Service::PSC::Time::SteadyClockTimePoint& out_time_point); + Result CalculateMonotonicSystemClockBaseTimePoint( + s64& out_time, Service::PSC::Time::SystemClockContext& context); + Result GetClockSnapshot(Service::PSC::Time::ClockSnapshot& out_snapshot, + Service::PSC::Time::TimeType type); + Result GetClockSnapshotFromSystemClockContext( + Service::PSC::Time::ClockSnapshot& out_snapshot, + Service::PSC::Time::SystemClockContext& user_context, + Service::PSC::Time::SystemClockContext& network_context, Service::PSC::Time::TimeType type); + Result CalculateStandardUserSystemClockDifferenceByUser(s64& out_time, + Service::PSC::Time::ClockSnapshot& a, + Service::PSC::Time::ClockSnapshot& b); + Result CalculateSpanBetween(s64& out_time, Service::PSC::Time::ClockSnapshot& a, + Service::PSC::Time::ClockSnapshot& b); + +private: + Result GetClockSnapshotImpl(Service::PSC::Time::ClockSnapshot& out_snapshot, + Service::PSC::Time::SystemClockContext& user_context, + Service::PSC::Time::SystemClockContext& network_context, + Service::PSC::Time::TimeType type); + + void Handle_GetStandardUserSystemClock(HLERequestContext& ctx); + void Handle_GetStandardNetworkSystemClock(HLERequestContext& ctx); + void Handle_GetStandardSteadyClock(HLERequestContext& ctx); + void Handle_GetTimeZoneService(HLERequestContext& ctx); + void Handle_GetStandardLocalSystemClock(HLERequestContext& ctx); + void Handle_GetEphemeralNetworkSystemClock(HLERequestContext& ctx); + void Handle_GetSharedMemoryNativeHandle(HLERequestContext& ctx); + void Handle_SetStandardSteadyClockInternalOffset(HLERequestContext& ctx); + void Handle_GetStandardSteadyClockRtcValue(HLERequestContext& ctx); + void Handle_IsStandardUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx); + void Handle_SetStandardUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx); + void Handle_GetStandardUserSystemClockInitialYear(HLERequestContext& ctx); + void Handle_IsStandardNetworkSystemClockAccuracySufficient(HLERequestContext& ctx); + void Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(HLERequestContext& ctx); + void Handle_CalculateMonotonicSystemClockBaseTimePoint(HLERequestContext& ctx); + void Handle_GetClockSnapshot(HLERequestContext& ctx); + void Handle_GetClockSnapshotFromSystemClockContext(HLERequestContext& ctx); + void Handle_CalculateStandardUserSystemClockDifferenceByUser(HLERequestContext& ctx); + void Handle_CalculateSpanBetween(HLERequestContext& ctx); + + Core::System& m_system; + + std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys; + std::shared_ptr<Service::PSC::Time::ServiceManager> m_time_m; + std::shared_ptr<Service::PSC::Time::StaticService> m_wrapped_service; + + Service::PSC::Time::StaticServiceSetupInfo m_setup_info; + std::shared_ptr<Service::PSC::Time::StaticService> m_time_sm; + std::shared_ptr<Service::PSC::Time::TimeZoneService> m_time_zone; + FileTimestampWorker& m_file_timestamp_worker; + StandardSteadyClockResource& m_standard_steady_clock_resource; +}; +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/glue/time/time_zone.cpp b/src/core/hle/service/glue/time/time_zone.cpp new file mode 100644 index 000000000..503c327dd --- /dev/null +++ b/src/core/hle/service/glue/time/time_zone.cpp @@ -0,0 +1,377 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <chrono> + +#include "core/core.h" +#include "core/hle/kernel/svc.h" +#include "core/hle/service/glue/time/file_timestamp_worker.h" +#include "core/hle/service/glue/time/time_zone.h" +#include "core/hle/service/glue/time/time_zone_binary.h" +#include "core/hle/service/psc/time/time_zone_service.h" +#include "core/hle/service/set/system_settings_server.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::Glue::Time { +namespace { +static std::mutex g_list_mutex; +static Common::IntrusiveListBaseTraits<Service::PSC::Time::OperationEvent>::ListType g_list_nodes{}; +} // namespace + +TimeZoneService::TimeZoneService( + Core::System& system_, FileTimestampWorker& file_timestamp_worker, + bool can_write_timezone_device_location, + std::shared_ptr<Service::PSC::Time::TimeZoneService> time_zone_service) + : ServiceFramework{system_, "ITimeZoneService"}, m_system{system}, + m_can_write_timezone_device_location{can_write_timezone_device_location}, + m_file_timestamp_worker{file_timestamp_worker}, + m_wrapped_service{std::move(time_zone_service)}, m_operation_event{m_system} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &TimeZoneService::Handle_GetDeviceLocationName, "GetDeviceLocationName"}, + {1, &TimeZoneService::Handle_SetDeviceLocationName, "SetDeviceLocationName"}, + {2, &TimeZoneService::Handle_GetTotalLocationNameCount, "GetTotalLocationNameCount"}, + {3, &TimeZoneService::Handle_LoadLocationNameList, "LoadLocationNameList"}, + {4, &TimeZoneService::Handle_LoadTimeZoneRule, "LoadTimeZoneRule"}, + {5, &TimeZoneService::Handle_GetTimeZoneRuleVersion, "GetTimeZoneRuleVersion"}, + {6, &TimeZoneService::Handle_GetDeviceLocationNameAndUpdatedTime, "GetDeviceLocationNameAndUpdatedTime"}, + {7, &TimeZoneService::Handle_SetDeviceLocationNameWithTimeZoneRule, "SetDeviceLocationNameWithTimeZoneRule"}, + {8, &TimeZoneService::Handle_ParseTimeZoneBinary, "ParseTimeZoneBinary"}, + {20, &TimeZoneService::Handle_GetDeviceLocationNameOperationEventReadableHandle, "GetDeviceLocationNameOperationEventReadableHandle"}, + {100, &TimeZoneService::Handle_ToCalendarTime, "ToCalendarTime"}, + {101, &TimeZoneService::Handle_ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"}, + {201, &TimeZoneService::Handle_ToPosixTime, "ToPosixTime"}, + {202, &TimeZoneService::Handle_ToPosixTimeWithMyRule, "ToPosixTimeWithMyRule"}, + }; + // clang-format on + RegisterHandlers(functions); + + g_list_nodes.clear(); + m_set_sys = + m_system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true); +} + +TimeZoneService::~TimeZoneService() = default; + +void TimeZoneService::Handle_GetDeviceLocationName(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + Service::PSC::Time::LocationName name{}; + auto res = GetDeviceLocationName(name); + + IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::LocationName) / sizeof(u32)}; + rb.Push(res); + rb.PushRaw<Service::PSC::Time::LocationName>(name); +} + +void TimeZoneService::Handle_SetDeviceLocationName(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto name{rp.PopRaw<Service::PSC::Time::LocationName>()}; + + auto res = SetDeviceLocation(name); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void TimeZoneService::Handle_GetTotalLocationNameCount(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + u32 count{}; + auto res = GetTotalLocationNameCount(count); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.Push(count); +} + +void TimeZoneService::Handle_LoadLocationNameList(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto index{rp.Pop<u32>()}; + + auto max_names{ctx.GetWriteBufferSize() / sizeof(Service::PSC::Time::LocationName)}; + + std::vector<Service::PSC::Time::LocationName> names{}; + u32 count{}; + auto res = LoadLocationNameList(count, names, max_names, index); + + ctx.WriteBuffer(names); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.Push(count); +} + +void TimeZoneService::Handle_LoadTimeZoneRule(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto name{rp.PopRaw<Service::PSC::Time::LocationName>()}; + + Tz::Rule rule{}; + auto res = LoadTimeZoneRule(rule, name); + + ctx.WriteBuffer(rule); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void TimeZoneService::Handle_GetTimeZoneRuleVersion(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + Service::PSC::Time::RuleVersion rule_version{}; + auto res = GetTimeZoneRuleVersion(rule_version); + + IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::RuleVersion) / sizeof(u32)}; + rb.Push(res); + rb.PushRaw<Service::PSC::Time::RuleVersion>(rule_version); +} + +void TimeZoneService::Handle_GetDeviceLocationNameAndUpdatedTime(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + Service::PSC::Time::LocationName name{}; + Service::PSC::Time::SteadyClockTimePoint time_point{}; + auto res = GetDeviceLocationNameAndUpdatedTime(time_point, name); + + IPC::ResponseBuilder rb{ctx, + 2 + (sizeof(Service::PSC::Time::LocationName) / sizeof(u32)) + + (sizeof(Service::PSC::Time::SteadyClockTimePoint) / sizeof(u32))}; + rb.Push(res); + rb.PushRaw<Service::PSC::Time::LocationName>(name); + rb.PushRaw<Service::PSC::Time::SteadyClockTimePoint>(time_point); +} + +void TimeZoneService::Handle_SetDeviceLocationNameWithTimeZoneRule(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + auto res = SetDeviceLocationNameWithTimeZoneRule(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void TimeZoneService::Handle_ParseTimeZoneBinary(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(Service::PSC::Time::ResultNotImplemented); +} + +void TimeZoneService::Handle_GetDeviceLocationNameOperationEventReadableHandle( + HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + Kernel::KEvent* event{}; + auto res = GetDeviceLocationNameOperationEventReadableHandle(&event); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(res); + rb.PushCopyObjects(event->GetReadableEvent()); +} + +void TimeZoneService::Handle_ToCalendarTime(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto time{rp.Pop<s64>()}; + + auto rule_buffer{ctx.ReadBuffer()}; + Tz::Rule rule{}; + std::memcpy(&rule, rule_buffer.data(), sizeof(Tz::Rule)); + + Service::PSC::Time::CalendarTime calendar_time{}; + Service::PSC::Time::CalendarAdditionalInfo additional_info{}; + auto res = ToCalendarTime(calendar_time, additional_info, time, rule); + + IPC::ResponseBuilder rb{ctx, + 2 + (sizeof(Service::PSC::Time::CalendarTime) / sizeof(u32)) + + (sizeof(Service::PSC::Time::CalendarAdditionalInfo) / sizeof(u32))}; + rb.Push(res); + rb.PushRaw<Service::PSC::Time::CalendarTime>(calendar_time); + rb.PushRaw<Service::PSC::Time::CalendarAdditionalInfo>(additional_info); +} + +void TimeZoneService::Handle_ToCalendarTimeWithMyRule(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + auto time{rp.Pop<s64>()}; + + LOG_DEBUG(Service_Time, "called. time={}", time); + + Service::PSC::Time::CalendarTime calendar_time{}; + Service::PSC::Time::CalendarAdditionalInfo additional_info{}; + auto res = ToCalendarTimeWithMyRule(calendar_time, additional_info, time); + + IPC::ResponseBuilder rb{ctx, + 2 + (sizeof(Service::PSC::Time::CalendarTime) / sizeof(u32)) + + (sizeof(Service::PSC::Time::CalendarAdditionalInfo) / sizeof(u32))}; + rb.Push(res); + rb.PushRaw<Service::PSC::Time::CalendarTime>(calendar_time); + rb.PushRaw<Service::PSC::Time::CalendarAdditionalInfo>(additional_info); +} + +void TimeZoneService::Handle_ToPosixTime(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + auto calendar{rp.PopRaw<Service::PSC::Time::CalendarTime>()}; + + LOG_DEBUG(Service_Time, "called. calendar year {} month {} day {} hour {} minute {} second {}", + calendar.year, calendar.month, calendar.day, calendar.hour, calendar.minute, + calendar.second); + + auto binary{ctx.ReadBuffer()}; + + Tz::Rule rule{}; + std::memcpy(&rule, binary.data(), sizeof(Tz::Rule)); + + u32 count{}; + std::array<s64, 2> times{}; + u32 times_count{static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(s64))}; + + auto res = ToPosixTime(count, times, times_count, calendar, rule); + + ctx.WriteBuffer(times); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.Push(count); +} + +void TimeZoneService::Handle_ToPosixTimeWithMyRule(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto calendar{rp.PopRaw<Service::PSC::Time::CalendarTime>()}; + + u32 count{}; + std::array<s64, 2> times{}; + u32 times_count{static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(s64))}; + + auto res = ToPosixTimeWithMyRule(count, times, times_count, calendar); + + ctx.WriteBuffer(times); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.Push(count); +} + +// =============================== Implementations =========================== + +Result TimeZoneService::GetDeviceLocationName(Service::PSC::Time::LocationName& out_location_name) { + R_RETURN(m_wrapped_service->GetDeviceLocationName(out_location_name)); +} + +Result TimeZoneService::SetDeviceLocation(Service::PSC::Time::LocationName& location_name) { + R_UNLESS(m_can_write_timezone_device_location, Service::PSC::Time::ResultPermissionDenied); + R_UNLESS(IsTimeZoneBinaryValid(location_name), Service::PSC::Time::ResultTimeZoneNotFound); + + std::scoped_lock l{m_mutex}; + + std::span<const u8> binary{}; + size_t binary_size{}; + R_TRY(GetTimeZoneRule(binary, binary_size, location_name)) + + R_TRY(m_wrapped_service->SetDeviceLocationNameWithTimeZoneRule(location_name, binary)); + + m_file_timestamp_worker.SetFilesystemPosixTime(); + + Service::PSC::Time::SteadyClockTimePoint time_point{}; + Service::PSC::Time::LocationName name{}; + R_TRY(m_wrapped_service->GetDeviceLocationNameAndUpdatedTime(time_point, name)); + + m_set_sys->SetDeviceTimeZoneLocationName(name); + m_set_sys->SetDeviceTimeZoneLocationUpdatedTime(time_point); + + std::scoped_lock m{g_list_mutex}; + for (auto& operation_event : g_list_nodes) { + operation_event.m_event->Signal(); + } + R_SUCCEED(); +} + +Result TimeZoneService::GetTotalLocationNameCount(u32& out_count) { + R_RETURN(m_wrapped_service->GetTotalLocationNameCount(out_count)); +} + +Result TimeZoneService::LoadLocationNameList( + u32& out_count, std::vector<Service::PSC::Time::LocationName>& out_names, size_t max_names, + u32 index) { + std::scoped_lock l{m_mutex}; + R_RETURN(GetTimeZoneLocationList(out_count, out_names, max_names, index)); +} + +Result TimeZoneService::LoadTimeZoneRule(Tz::Rule& out_rule, + Service::PSC::Time::LocationName& name) { + std::scoped_lock l{m_mutex}; + std::span<const u8> binary{}; + size_t binary_size{}; + R_TRY(GetTimeZoneRule(binary, binary_size, name)) + R_RETURN(m_wrapped_service->ParseTimeZoneBinary(out_rule, binary)); +} + +Result TimeZoneService::GetTimeZoneRuleVersion(Service::PSC::Time::RuleVersion& out_rule_version) { + R_RETURN(m_wrapped_service->GetTimeZoneRuleVersion(out_rule_version)); +} + +Result TimeZoneService::GetDeviceLocationNameAndUpdatedTime( + Service::PSC::Time::SteadyClockTimePoint& out_time_point, + Service::PSC::Time::LocationName& location_name) { + R_RETURN(m_wrapped_service->GetDeviceLocationNameAndUpdatedTime(out_time_point, location_name)); +} + +Result TimeZoneService::SetDeviceLocationNameWithTimeZoneRule() { + R_UNLESS(m_can_write_timezone_device_location, Service::PSC::Time::ResultPermissionDenied); + R_RETURN(Service::PSC::Time::ResultNotImplemented); +} + +Result TimeZoneService::GetDeviceLocationNameOperationEventReadableHandle( + Kernel::KEvent** out_event) { + if (!operation_event_initialized) { + operation_event_initialized = false; + + m_operation_event.m_ctx.CloseEvent(m_operation_event.m_event); + m_operation_event.m_event = + m_operation_event.m_ctx.CreateEvent("Psc:TimeZoneService:OperationEvent"); + operation_event_initialized = true; + std::scoped_lock l{m_mutex}; + g_list_nodes.push_back(m_operation_event); + } + + *out_event = m_operation_event.m_event; + R_SUCCEED(); +} + +Result TimeZoneService::ToCalendarTime( + Service::PSC::Time::CalendarTime& out_calendar_time, + Service::PSC::Time::CalendarAdditionalInfo& out_additional_info, s64 time, Tz::Rule& rule) { + R_RETURN(m_wrapped_service->ToCalendarTime(out_calendar_time, out_additional_info, time, rule)); +} + +Result TimeZoneService::ToCalendarTimeWithMyRule( + Service::PSC::Time::CalendarTime& out_calendar_time, + Service::PSC::Time::CalendarAdditionalInfo& out_additional_info, s64 time) { + R_RETURN( + m_wrapped_service->ToCalendarTimeWithMyRule(out_calendar_time, out_additional_info, time)); +} + +Result TimeZoneService::ToPosixTime(u32& out_count, std::span<s64, 2> out_times, + u32 out_times_count, + Service::PSC::Time::CalendarTime& calendar_time, + Tz::Rule& rule) { + R_RETURN( + m_wrapped_service->ToPosixTime(out_count, out_times, out_times_count, calendar_time, rule)); +} + +Result TimeZoneService::ToPosixTimeWithMyRule(u32& out_count, std::span<s64, 2> out_times, + u32 out_times_count, + Service::PSC::Time::CalendarTime& calendar_time) { + R_RETURN(m_wrapped_service->ToPosixTimeWithMyRule(out_count, out_times, out_times_count, + calendar_time)); +} + +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/glue/time/time_zone.h b/src/core/hle/service/glue/time/time_zone.h new file mode 100644 index 000000000..3c8ae4bf8 --- /dev/null +++ b/src/core/hle/service/glue/time/time_zone.h @@ -0,0 +1,95 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <memory> +#include <mutex> +#include <span> +#include <vector> + +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/psc/time/common.h" +#include "core/hle/service/server_manager.h" +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Tz { +struct Rule; +} + +namespace Service::Set { +class ISystemSettingsServer; +} + +namespace Service::PSC::Time { +class TimeZoneService; +} + +namespace Service::Glue::Time { +class FileTimestampWorker; + +class TimeZoneService final : public ServiceFramework<TimeZoneService> { +public: + explicit TimeZoneService( + Core::System& system, FileTimestampWorker& file_timestamp_worker, + bool can_write_timezone_device_location, + std::shared_ptr<Service::PSC::Time::TimeZoneService> time_zone_service); + + ~TimeZoneService() override; + + Result GetDeviceLocationName(Service::PSC::Time::LocationName& out_location_name); + Result SetDeviceLocation(Service::PSC::Time::LocationName& location_name); + Result GetTotalLocationNameCount(u32& out_count); + Result LoadLocationNameList(u32& out_count, + std::vector<Service::PSC::Time::LocationName>& out_names, + size_t max_names, u32 index); + Result LoadTimeZoneRule(Tz::Rule& out_rule, Service::PSC::Time::LocationName& name); + Result GetTimeZoneRuleVersion(Service::PSC::Time::RuleVersion& out_rule_version); + Result GetDeviceLocationNameAndUpdatedTime( + Service::PSC::Time::SteadyClockTimePoint& out_time_point, + Service::PSC::Time::LocationName& location_name); + Result SetDeviceLocationNameWithTimeZoneRule(); + Result GetDeviceLocationNameOperationEventReadableHandle(Kernel::KEvent** out_event); + Result ToCalendarTime(Service::PSC::Time::CalendarTime& out_calendar_time, + Service::PSC::Time::CalendarAdditionalInfo& out_additional_info, s64 time, + Tz::Rule& rule); + Result ToCalendarTimeWithMyRule(Service::PSC::Time::CalendarTime& out_calendar_time, + Service::PSC::Time::CalendarAdditionalInfo& out_additional_info, + s64 time); + Result ToPosixTime(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count, + Service::PSC::Time::CalendarTime& calendar_time, Tz::Rule& rule); + Result ToPosixTimeWithMyRule(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count, + Service::PSC::Time::CalendarTime& calendar_time); + +private: + void Handle_GetDeviceLocationName(HLERequestContext& ctx); + void Handle_SetDeviceLocationName(HLERequestContext& ctx); + void Handle_GetTotalLocationNameCount(HLERequestContext& ctx); + void Handle_LoadLocationNameList(HLERequestContext& ctx); + void Handle_LoadTimeZoneRule(HLERequestContext& ctx); + void Handle_GetTimeZoneRuleVersion(HLERequestContext& ctx); + void Handle_GetDeviceLocationNameAndUpdatedTime(HLERequestContext& ctx); + void Handle_SetDeviceLocationNameWithTimeZoneRule(HLERequestContext& ctx); + void Handle_ParseTimeZoneBinary(HLERequestContext& ctx); + void Handle_GetDeviceLocationNameOperationEventReadableHandle(HLERequestContext& ctx); + void Handle_ToCalendarTime(HLERequestContext& ctx); + void Handle_ToCalendarTimeWithMyRule(HLERequestContext& ctx); + void Handle_ToPosixTime(HLERequestContext& ctx); + void Handle_ToPosixTimeWithMyRule(HLERequestContext& ctx); + + Core::System& m_system; + std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys; + + bool m_can_write_timezone_device_location; + FileTimestampWorker& m_file_timestamp_worker; + std::shared_ptr<Service::PSC::Time::TimeZoneService> m_wrapped_service; + std::mutex m_mutex; + bool operation_event_initialized{}; + Service::PSC::Time::OperationEvent m_operation_event; +}; + +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/glue/time/time_zone_binary.cpp b/src/core/hle/service/glue/time/time_zone_binary.cpp new file mode 100644 index 000000000..67969aa3f --- /dev/null +++ b/src/core/hle/service/glue/time/time_zone_binary.cpp @@ -0,0 +1,221 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/file_sys/content_archive.h" +#include "core/file_sys/nca_metadata.h" +#include "core/file_sys/registered_cache.h" +#include "core/file_sys/romfs.h" +#include "core/file_sys/system_archive/system_archive.h" +#include "core/file_sys/vfs.h" +#include "core/hle/service/filesystem/filesystem.h" +#include "core/hle/service/glue/time/time_zone_binary.h" + +namespace Service::Glue::Time { +namespace { +constexpr u64 TimeZoneBinaryId = 0x10000000000080E; + +static FileSys::VirtualDir g_time_zone_binary_romfs{}; +static Result g_time_zone_binary_mount_result{ResultUnknown}; +static std::vector<u8> g_time_zone_scratch_space(0x2800, 0); + +Result TimeZoneReadBinary(size_t& out_read_size, std::span<u8> out_buffer, size_t out_buffer_size, + std::string_view path) { + R_UNLESS(g_time_zone_binary_mount_result == ResultSuccess, g_time_zone_binary_mount_result); + + auto vfs_file{g_time_zone_binary_romfs->GetFileRelative(path)}; + R_UNLESS(vfs_file, ResultUnknown); + + auto file_size{vfs_file->GetSize()}; + R_UNLESS(file_size > 0, ResultUnknown); + + R_UNLESS(file_size <= out_buffer_size, Service::PSC::Time::ResultFailed); + + out_read_size = vfs_file->Read(out_buffer.data(), file_size); + R_UNLESS(out_read_size > 0, ResultUnknown); + + R_SUCCEED(); +} +} // namespace + +void ResetTimeZoneBinary() { + g_time_zone_binary_romfs = {}; + g_time_zone_binary_mount_result = ResultUnknown; + g_time_zone_scratch_space.clear(); + g_time_zone_scratch_space.resize(0x2800, 0); +} + +Result MountTimeZoneBinary(Core::System& system) { + ResetTimeZoneBinary(); + + auto& fsc{system.GetFileSystemController()}; + std::unique_ptr<FileSys::NCA> nca{}; + + auto* bis_system = fsc.GetSystemNANDContents(); + + R_UNLESS(bis_system, ResultUnknown); + + nca = bis_system->GetEntry(TimeZoneBinaryId, FileSys::ContentRecordType::Data); + + if (nca) { + g_time_zone_binary_romfs = FileSys::ExtractRomFS(nca->GetRomFS()); + } + + if (g_time_zone_binary_romfs) { + // Validate that the romfs is readable, using invalid firmware keys can cause this to get + // set but the files to be garbage. In that case, we want to hit the next path and + // synthesise them instead. + Service::PSC::Time::LocationName name{"Etc/GMT"}; + if (!IsTimeZoneBinaryValid(name)) { + ResetTimeZoneBinary(); + } + } + + if (!g_time_zone_binary_romfs) { + g_time_zone_binary_romfs = FileSys::ExtractRomFS( + FileSys::SystemArchive::SynthesizeSystemArchive(TimeZoneBinaryId)); + } + + R_UNLESS(g_time_zone_binary_romfs, ResultUnknown); + + g_time_zone_binary_mount_result = ResultSuccess; + R_SUCCEED(); +} + +void GetTimeZoneBinaryListPath(std::string& out_path) { + if (g_time_zone_binary_mount_result != ResultSuccess) { + return; + } + // out_path = fmt::format("{}:/binaryList.txt", "TimeZoneBinary"); + out_path = "/binaryList.txt"; +} + +void GetTimeZoneBinaryVersionPath(std::string& out_path) { + if (g_time_zone_binary_mount_result != ResultSuccess) { + return; + } + // out_path = fmt::format("{}:/version.txt", "TimeZoneBinary"); + out_path = "/version.txt"; +} + +void GetTimeZoneZonePath(std::string& out_path, Service::PSC::Time::LocationName& name) { + if (g_time_zone_binary_mount_result != ResultSuccess) { + return; + } + // out_path = fmt::format("{}:/zoneinfo/{}", "TimeZoneBinary", name); + out_path = fmt::format("/zoneinfo/{}", name.name.data()); +} + +bool IsTimeZoneBinaryValid(Service::PSC::Time::LocationName& name) { + std::string path{}; + GetTimeZoneZonePath(path, name); + + auto vfs_file{g_time_zone_binary_romfs->GetFileRelative(path)}; + if (!vfs_file) { + LOG_INFO(Service_Time, "Could not find timezone file {}", path); + return false; + } + return vfs_file->GetSize() != 0; +} + +u32 GetTimeZoneCount() { + std::string path{}; + GetTimeZoneBinaryListPath(path); + + size_t bytes_read{}; + if (TimeZoneReadBinary(bytes_read, g_time_zone_scratch_space, 0x2800, path) != ResultSuccess) { + return 0; + } + if (bytes_read == 0) { + return 0; + } + + auto chars = std::span(reinterpret_cast<char*>(g_time_zone_scratch_space.data()), bytes_read); + u32 count{}; + for (auto chr : chars) { + if (chr == '\n') { + count++; + } + } + return count; +} + +Result GetTimeZoneVersion(Service::PSC::Time::RuleVersion& out_rule_version) { + std::string path{}; + GetTimeZoneBinaryVersionPath(path); + + auto rule_version_buffer{std::span(reinterpret_cast<u8*>(&out_rule_version), + sizeof(Service::PSC::Time::RuleVersion))}; + size_t bytes_read{}; + R_TRY(TimeZoneReadBinary(bytes_read, rule_version_buffer, rule_version_buffer.size_bytes(), + path)); + + rule_version_buffer[bytes_read] = 0; + R_SUCCEED(); +} + +Result GetTimeZoneRule(std::span<const u8>& out_rule, size_t& out_rule_size, + Service::PSC::Time::LocationName& name) { + std::string path{}; + GetTimeZoneZonePath(path, name); + + size_t bytes_read{}; + R_TRY(TimeZoneReadBinary(bytes_read, g_time_zone_scratch_space, + g_time_zone_scratch_space.size(), path)); + + out_rule = std::span(g_time_zone_scratch_space.data(), bytes_read); + out_rule_size = bytes_read; + R_SUCCEED(); +} + +Result GetTimeZoneLocationList(u32& out_count, + std::vector<Service::PSC::Time::LocationName>& out_names, + size_t max_names, u32 index) { + std::string path{}; + GetTimeZoneBinaryListPath(path); + + size_t bytes_read{}; + R_TRY(TimeZoneReadBinary(bytes_read, g_time_zone_scratch_space, + g_time_zone_scratch_space.size(), path)); + + out_count = 0; + R_SUCCEED_IF(bytes_read == 0); + + Service::PSC::Time::LocationName current_name{}; + size_t current_name_len{}; + std::span<const u8> chars{g_time_zone_scratch_space}; + u32 name_count{}; + + for (auto chr : chars) { + if (chr == '\r') { + continue; + } + + if (chr == '\n') { + if (name_count >= index) { + out_names.push_back(current_name); + out_count++; + if (out_count >= max_names) { + break; + } + } + name_count++; + current_name_len = 0; + current_name = {}; + continue; + } + + if (chr == '\0') { + break; + } + + R_UNLESS(current_name_len <= current_name.name.size() - 2, + Service::PSC::Time::ResultFailed); + + current_name.name[current_name_len++] = chr; + } + + R_SUCCEED(); +} + +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/glue/time/time_zone_binary.h b/src/core/hle/service/glue/time/time_zone_binary.h new file mode 100644 index 000000000..2cad6b458 --- /dev/null +++ b/src/core/hle/service/glue/time/time_zone_binary.h @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <span> +#include <string> +#include <string_view> + +#include "core/hle/service/psc/time/common.h" + +namespace Core { +class System; +} + +namespace Service::Glue::Time { + +void ResetTimeZoneBinary(); +Result MountTimeZoneBinary(Core::System& system); +void GetTimeZoneBinaryListPath(std::string& out_path); +void GetTimeZoneBinaryVersionPath(std::string& out_path); +void GetTimeZoneZonePath(std::string& out_path, Service::PSC::Time::LocationName& name); +bool IsTimeZoneBinaryValid(Service::PSC::Time::LocationName& name); +u32 GetTimeZoneCount(); +Result GetTimeZoneVersion(Service::PSC::Time::RuleVersion& out_rule_version); +Result GetTimeZoneRule(std::span<const u8>& out_rule, size_t& out_rule_size, + Service::PSC::Time::LocationName& name); +Result GetTimeZoneLocationList(u32& out_count, + std::vector<Service::PSC::Time::LocationName>& out_names, + size_t max_names, u32 index); + +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/glue/time/worker.cpp b/src/core/hle/service/glue/time/worker.cpp new file mode 100644 index 000000000..ea0e49b90 --- /dev/null +++ b/src/core/hle/service/glue/time/worker.cpp @@ -0,0 +1,338 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/scope_exit.h" +#include "core/core.h" +#include "core/core_timing.h" +#include "core/hle/service/glue/time/file_timestamp_worker.h" +#include "core/hle/service/glue/time/standard_steady_clock_resource.h" +#include "core/hle/service/glue/time/worker.h" +#include "core/hle/service/psc/time/common.h" +#include "core/hle/service/psc/time/service_manager.h" +#include "core/hle/service/psc/time/static.h" +#include "core/hle/service/psc/time/system_clock.h" +#include "core/hle/service/set/system_settings_server.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::Glue::Time { +namespace { + +bool g_ig_report_network_clock_context_set{}; +Service::PSC::Time::SystemClockContext g_report_network_clock_context{}; +bool g_ig_report_ephemeral_clock_context_set{}; +Service::PSC::Time::SystemClockContext g_report_ephemeral_clock_context{}; + +template <typename T> +T GetSettingsItemValue(std::shared_ptr<Service::Set::ISystemSettingsServer>& set_sys, + const char* category, const char* name) { + std::vector<u8> interval_buf; + auto res = set_sys->GetSettingsItemValue(interval_buf, category, name); + ASSERT(res == ResultSuccess); + + T v{}; + std::memcpy(&v, interval_buf.data(), sizeof(T)); + return v; +} + +} // namespace + +TimeWorker::TimeWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource, + FileTimestampWorker& file_timestamp_worker) + : m_system{system}, m_ctx{m_system, "Glue:58"}, m_event{m_ctx.CreateEvent("Glue:58:Event")}, + m_steady_clock_resource{steady_clock_resource}, + m_file_timestamp_worker{file_timestamp_worker}, m_timer_steady_clock{m_ctx.CreateEvent( + "Glue:58:SteadyClockTimerEvent")}, + m_timer_file_system{m_ctx.CreateEvent("Glue:58:FileTimeTimerEvent")}, + m_alarm_worker{m_system, m_steady_clock_resource}, m_pm_state_change_handler{m_alarm_worker} { + g_ig_report_network_clock_context_set = false; + g_report_network_clock_context = {}; + g_ig_report_ephemeral_clock_context_set = false; + g_report_ephemeral_clock_context = {}; + + m_timer_steady_clock_timing_event = Core::Timing::CreateEvent( + "Time::SteadyClockEvent", + [this](s64 time, + std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { + m_timer_steady_clock->Signal(); + return std::nullopt; + }); + + m_timer_file_system_timing_event = Core::Timing::CreateEvent( + "Time::SteadyClockEvent", + [this](s64 time, + std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { + m_timer_file_system->Signal(); + return std::nullopt; + }); +} + +TimeWorker::~TimeWorker() { + m_local_clock_event->Signal(); + m_network_clock_event->Signal(); + m_ephemeral_clock_event->Signal(); + std::this_thread::sleep_for(std::chrono::milliseconds(16)); + + m_thread.request_stop(); + m_event->Signal(); + m_thread.join(); + + m_ctx.CloseEvent(m_event); + m_system.CoreTiming().UnscheduleEvent(m_timer_steady_clock_timing_event); + m_ctx.CloseEvent(m_timer_steady_clock); + m_system.CoreTiming().UnscheduleEvent(m_timer_file_system_timing_event); + m_ctx.CloseEvent(m_timer_file_system); +} + +void TimeWorker::Initialize(std::shared_ptr<Service::PSC::Time::StaticService> time_sm, + std::shared_ptr<Service::Set::ISystemSettingsServer> set_sys) { + m_set_sys = std::move(set_sys); + m_time_m = + m_system.ServiceManager().GetService<Service::PSC::Time::ServiceManager>("time:m", true); + m_time_sm = std::move(time_sm); + + m_alarm_worker.Initialize(m_time_m); + + auto steady_clock_interval_m = GetSettingsItemValue<s32>( + m_set_sys, "time", "standard_steady_clock_rtc_update_interval_minutes"); + + auto one_minute_ns{ + std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::minutes(1)).count()}; + s64 steady_clock_interval_ns{steady_clock_interval_m * one_minute_ns}; + + m_system.CoreTiming().ScheduleLoopingEvent(std::chrono::nanoseconds(0), + std::chrono::nanoseconds(steady_clock_interval_ns), + m_timer_steady_clock_timing_event); + + auto fs_notify_time_s = + GetSettingsItemValue<s32>(m_set_sys, "time", "notify_time_to_fs_interval_seconds"); + auto one_second_ns{ + std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()}; + s64 fs_notify_time_ns{fs_notify_time_s * one_second_ns}; + + m_system.CoreTiming().ScheduleLoopingEvent(std::chrono::nanoseconds(0), + std::chrono::nanoseconds(fs_notify_time_ns), + m_timer_file_system_timing_event); + + auto res = m_time_sm->GetStandardLocalSystemClock(m_local_clock); + ASSERT(res == ResultSuccess); + res = m_time_m->GetStandardLocalClockOperationEvent(&m_local_clock_event); + ASSERT(res == ResultSuccess); + + res = m_time_sm->GetStandardNetworkSystemClock(m_network_clock); + ASSERT(res == ResultSuccess); + res = m_time_m->GetStandardNetworkClockOperationEventForServiceManager(&m_network_clock_event); + ASSERT(res == ResultSuccess); + + res = m_time_sm->GetEphemeralNetworkSystemClock(m_ephemeral_clock); + ASSERT(res == ResultSuccess); + res = + m_time_m->GetEphemeralNetworkClockOperationEventForServiceManager(&m_ephemeral_clock_event); + ASSERT(res == ResultSuccess); + + res = m_time_m->GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent( + &m_standard_user_auto_correct_clock_event); + ASSERT(res == ResultSuccess); +} + +void TimeWorker::StartThread() { + m_thread = std::jthread(std::bind_front(&TimeWorker::ThreadFunc, this)); +} + +void TimeWorker::ThreadFunc(std::stop_token stop_token) { + Common::SetCurrentThreadName("TimeWorker"); + Common::SetCurrentThreadPriority(Common::ThreadPriority::Low); + + enum class EventType { + Exit = 0, + IpmModuleService_GetEvent = 1, + PowerStateChange = 2, + SignalAlarms = 3, + UpdateLocalSystemClock = 4, + UpdateNetworkSystemClock = 5, + UpdateEphemeralSystemClock = 6, + UpdateSteadyClock = 7, + UpdateFileTimestamp = 8, + AutoCorrect = 9, + Max = 10, + }; + + s32 num_objs{}; + std::array<Kernel::KSynchronizationObject*, static_cast<u32>(EventType::Max)> wait_objs{}; + std::array<EventType, static_cast<u32>(EventType::Max)> wait_indices{}; + + const auto AddWaiter{ + [&](Kernel::KSynchronizationObject* synchronization_object, EventType type) { + // Open a new reference to the object. + synchronization_object->Open(); + + // Insert into the list. + wait_indices[num_objs] = type; + wait_objs[num_objs++] = synchronization_object; + }}; + + while (!stop_token.stop_requested()) { + SCOPE_EXIT({ + for (s32 i = 0; i < num_objs; i++) { + wait_objs[i]->Close(); + } + }); + + num_objs = {}; + wait_objs = {}; + if (m_pm_state_change_handler.m_priority != 0) { + AddWaiter(&m_event->GetReadableEvent(), EventType::Exit); + // TODO + // AddWaiter(gIPmModuleService::GetEvent(), 1); + AddWaiter(&m_alarm_worker.GetEvent().GetReadableEvent(), EventType::PowerStateChange); + } else { + AddWaiter(&m_event->GetReadableEvent(), EventType::Exit); + // TODO + // AddWaiter(gIPmModuleService::GetEvent(), 1); + AddWaiter(&m_alarm_worker.GetEvent().GetReadableEvent(), EventType::PowerStateChange); + AddWaiter(&m_alarm_worker.GetTimerEvent().GetReadableEvent(), EventType::SignalAlarms); + AddWaiter(&m_local_clock_event->GetReadableEvent(), EventType::UpdateLocalSystemClock); + AddWaiter(&m_network_clock_event->GetReadableEvent(), + EventType::UpdateNetworkSystemClock); + AddWaiter(&m_ephemeral_clock_event->GetReadableEvent(), + EventType::UpdateEphemeralSystemClock); + AddWaiter(&m_timer_steady_clock->GetReadableEvent(), EventType::UpdateSteadyClock); + AddWaiter(&m_timer_file_system->GetReadableEvent(), EventType::UpdateFileTimestamp); + AddWaiter(&m_standard_user_auto_correct_clock_event->GetReadableEvent(), + EventType::AutoCorrect); + } + + s32 out_index{-1}; + Kernel::KSynchronizationObject::Wait(m_system.Kernel(), &out_index, wait_objs.data(), + num_objs, -1); + ASSERT(out_index >= 0 && out_index < num_objs); + + if (stop_token.stop_requested()) { + return; + } + + switch (wait_indices[out_index]) { + case EventType::Exit: + return; + + case EventType::IpmModuleService_GetEvent: + // TODO + // IPmModuleService::GetEvent() + // clear the event + // Handle power state change event + break; + + case EventType::PowerStateChange: + m_alarm_worker.GetEvent().Clear(); + if (m_pm_state_change_handler.m_priority <= 1) { + m_alarm_worker.OnPowerStateChanged(); + } + break; + + case EventType::SignalAlarms: + m_alarm_worker.GetTimerEvent().Clear(); + m_time_m->CheckAndSignalAlarms(); + break; + + case EventType::UpdateLocalSystemClock: { + m_local_clock_event->Clear(); + + Service::PSC::Time::SystemClockContext context{}; + auto res = m_local_clock->GetSystemClockContext(context); + ASSERT(res == ResultSuccess); + + m_set_sys->SetUserSystemClockContext(context); + + m_file_timestamp_worker.SetFilesystemPosixTime(); + } break; + + case EventType::UpdateNetworkSystemClock: { + m_network_clock_event->Clear(); + Service::PSC::Time::SystemClockContext context{}; + auto res = m_network_clock->GetSystemClockContext(context); + ASSERT(res == ResultSuccess); + m_set_sys->SetNetworkSystemClockContext(context); + + s64 time{}; + if (m_network_clock->GetCurrentTime(time) != ResultSuccess) { + break; + } + + [[maybe_unused]] auto offset_before{ + g_ig_report_network_clock_context_set ? g_report_network_clock_context.offset : 0}; + // TODO system report "standard_netclock_operation" + // "clock_time" = time + // "context_offset_before" = offset_before + // "context_offset_after" = context.offset + g_report_network_clock_context = context; + if (!g_ig_report_network_clock_context_set) { + g_ig_report_network_clock_context_set = true; + } + + m_file_timestamp_worker.SetFilesystemPosixTime(); + } break; + + case EventType::UpdateEphemeralSystemClock: { + m_ephemeral_clock_event->Clear(); + + Service::PSC::Time::SystemClockContext context{}; + auto res = m_ephemeral_clock->GetSystemClockContext(context); + if (res != ResultSuccess) { + break; + } + + s64 time{}; + res = m_ephemeral_clock->GetCurrentTime(time); + if (res != ResultSuccess) { + break; + } + + [[maybe_unused]] auto offset_before{g_ig_report_ephemeral_clock_context_set + ? g_report_ephemeral_clock_context.offset + : 0}; + // TODO system report "ephemeral_netclock_operation" + // "clock_time" = time + // "context_offset_before" = offset_before + // "context_offset_after" = context.offset + g_report_ephemeral_clock_context = context; + if (!g_ig_report_ephemeral_clock_context_set) { + g_ig_report_ephemeral_clock_context_set = true; + } + } break; + + case EventType::UpdateSteadyClock: + m_timer_steady_clock->Clear(); + + m_steady_clock_resource.UpdateTime(); + m_time_m->SetStandardSteadyClockBaseTime(m_steady_clock_resource.GetTime()); + break; + + case EventType::UpdateFileTimestamp: + m_timer_file_system->Clear(); + + m_file_timestamp_worker.SetFilesystemPosixTime(); + break; + + case EventType::AutoCorrect: { + m_standard_user_auto_correct_clock_event->Clear(); + + bool automatic_correction{}; + auto res = m_time_sm->IsStandardUserSystemClockAutomaticCorrectionEnabled( + automatic_correction); + ASSERT(res == ResultSuccess); + + Service::PSC::Time::SteadyClockTimePoint time_point{}; + res = m_time_sm->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(time_point); + ASSERT(res == ResultSuccess); + + m_set_sys->SetUserSystemClockAutomaticCorrectionEnabled(automatic_correction); + m_set_sys->SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point); + } break; + + default: + UNREACHABLE(); + break; + } + } +} + +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/glue/time/worker.h b/src/core/hle/service/glue/time/worker.h new file mode 100644 index 000000000..adbbe6b6d --- /dev/null +++ b/src/core/hle/service/glue/time/worker.h @@ -0,0 +1,64 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/service/glue/time/alarm_worker.h" +#include "core/hle/service/glue/time/pm_state_change_handler.h" +#include "core/hle/service/kernel_helpers.h" + +namespace Service::Set { +class ISystemSettingsServer; +} + +namespace Service::PSC::Time { +class StaticService; +class SystemClock; +} // namespace Service::PSC::Time + +namespace Service::Glue::Time { +class FileTimestampWorker; +class StandardSteadyClockResource; + +class TimeWorker { +public: + explicit TimeWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource, + FileTimestampWorker& file_timestamp_worker); + ~TimeWorker(); + + void Initialize(std::shared_ptr<Service::PSC::Time::StaticService> time_sm, + std::shared_ptr<Service::Set::ISystemSettingsServer> set_sys); + + void StartThread(); + +private: + void ThreadFunc(std::stop_token stop_token); + + Core::System& m_system; + KernelHelpers::ServiceContext m_ctx; + std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys; + + std::jthread m_thread; + Kernel::KEvent* m_event{}; + std::shared_ptr<Service::PSC::Time::ServiceManager> m_time_m; + std::shared_ptr<Service::PSC::Time::StaticService> m_time_sm; + std::shared_ptr<Service::PSC::Time::SystemClock> m_network_clock; + std::shared_ptr<Service::PSC::Time::SystemClock> m_local_clock; + std::shared_ptr<Service::PSC::Time::SystemClock> m_ephemeral_clock; + StandardSteadyClockResource& m_steady_clock_resource; + FileTimestampWorker& m_file_timestamp_worker; + Kernel::KEvent* m_local_clock_event{}; + Kernel::KEvent* m_network_clock_event{}; + Kernel::KEvent* m_ephemeral_clock_event{}; + Kernel::KEvent* m_standard_user_auto_correct_clock_event{}; + Kernel::KEvent* m_timer_steady_clock{}; + std::shared_ptr<Core::Timing::EventType> m_timer_steady_clock_timing_event; + Kernel::KEvent* m_timer_file_system{}; + std::shared_ptr<Core::Timing::EventType> m_timer_file_system_timing_event; + AlarmWorker m_alarm_worker; + PmStateChangeHandler m_pm_state_change_handler; +}; + +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/hle_ipc.cpp b/src/core/hle/service/hle_ipc.cpp index 3f38ceb03..50e1ed756 100644 --- a/src/core/hle/service/hle_ipc.cpp +++ b/src/core/hle/service/hle_ipc.cpp @@ -12,6 +12,7 @@ #include "common/common_types.h" #include "common/logging/log.h" #include "common/scratch_buffer.h" +#include "core/guest_memory.h" #include "core/hle/kernel/k_auto_object.h" #include "core/hle/kernel/k_handle_table.h" #include "core/hle/kernel/k_process.h" @@ -23,19 +24,6 @@ #include "core/hle/service/ipc_helpers.h" #include "core/memory.h" -namespace { -static thread_local std::array read_buffer_data_a{ - Common::ScratchBuffer<u8>(), - Common::ScratchBuffer<u8>(), - Common::ScratchBuffer<u8>(), -}; -static thread_local std::array read_buffer_data_x{ - Common::ScratchBuffer<u8>(), - Common::ScratchBuffer<u8>(), - Common::ScratchBuffer<u8>(), -}; -} // Anonymous namespace - namespace Service { SessionRequestHandler::SessionRequestHandler(Kernel::KernelCore& kernel_, const char* service_name_) @@ -343,48 +331,27 @@ std::vector<u8> HLERequestContext::ReadBufferCopy(std::size_t buffer_index) cons } std::span<const u8> HLERequestContext::ReadBufferA(std::size_t buffer_index) const { - static thread_local std::array read_buffer_a{ - Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0), - Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0), - Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0), - }; + Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::UnsafeRead> gm(memory, 0, 0); ASSERT_OR_EXECUTE_MSG( BufferDescriptorA().size() > buffer_index, { return {}; }, "BufferDescriptorA invalid buffer_index {}", buffer_index); - auto& read_buffer = read_buffer_a[buffer_index]; - return read_buffer.Read(BufferDescriptorA()[buffer_index].Address(), - BufferDescriptorA()[buffer_index].Size(), - &read_buffer_data_a[buffer_index]); + return gm.Read(BufferDescriptorA()[buffer_index].Address(), + BufferDescriptorA()[buffer_index].Size(), &read_buffer_data_a[buffer_index]); } std::span<const u8> HLERequestContext::ReadBufferX(std::size_t buffer_index) const { - static thread_local std::array read_buffer_x{ - Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0), - Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0), - Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0), - }; + Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::UnsafeRead> gm(memory, 0, 0); ASSERT_OR_EXECUTE_MSG( BufferDescriptorX().size() > buffer_index, { return {}; }, "BufferDescriptorX invalid buffer_index {}", buffer_index); - auto& read_buffer = read_buffer_x[buffer_index]; - return read_buffer.Read(BufferDescriptorX()[buffer_index].Address(), - BufferDescriptorX()[buffer_index].Size(), - &read_buffer_data_x[buffer_index]); + return gm.Read(BufferDescriptorX()[buffer_index].Address(), + BufferDescriptorX()[buffer_index].Size(), &read_buffer_data_x[buffer_index]); } std::span<const u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const { - static thread_local std::array read_buffer_a{ - Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0), - Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0), - Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0), - }; - static thread_local std::array read_buffer_x{ - Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0), - Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0), - Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0), - }; + Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::UnsafeRead> gm(memory, 0, 0); const bool is_buffer_a{BufferDescriptorA().size() > buffer_index && BufferDescriptorA()[buffer_index].Size()}; @@ -401,18 +368,14 @@ std::span<const u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) cons ASSERT_OR_EXECUTE_MSG( BufferDescriptorA().size() > buffer_index, { return {}; }, "BufferDescriptorA invalid buffer_index {}", buffer_index); - auto& read_buffer = read_buffer_a[buffer_index]; - return read_buffer.Read(BufferDescriptorA()[buffer_index].Address(), - BufferDescriptorA()[buffer_index].Size(), - &read_buffer_data_a[buffer_index]); + return gm.Read(BufferDescriptorA()[buffer_index].Address(), + BufferDescriptorA()[buffer_index].Size(), &read_buffer_data_a[buffer_index]); } else { ASSERT_OR_EXECUTE_MSG( BufferDescriptorX().size() > buffer_index, { return {}; }, "BufferDescriptorX invalid buffer_index {}", buffer_index); - auto& read_buffer = read_buffer_x[buffer_index]; - return read_buffer.Read(BufferDescriptorX()[buffer_index].Address(), - BufferDescriptorX()[buffer_index].Size(), - &read_buffer_data_x[buffer_index]); + return gm.Read(BufferDescriptorX()[buffer_index].Address(), + BufferDescriptorX()[buffer_index].Size(), &read_buffer_data_x[buffer_index]); } } @@ -538,6 +501,22 @@ bool HLERequestContext::CanWriteBuffer(std::size_t buffer_index) const { } } +void HLERequestContext::AddMoveInterface(SessionRequestHandlerPtr s) { + ASSERT(Kernel::GetCurrentProcess(kernel).GetResourceLimit()->Reserve( + Kernel::LimitableResource::SessionCountMax, 1)); + + auto* session = Kernel::KSession::Create(kernel); + session->Initialize(nullptr, 0); + Kernel::KSession::Register(kernel, session); + + auto& server = manager.lock()->GetServerManager(); + auto next_manager = std::make_shared<Service::SessionRequestManager>(kernel, server); + next_manager->SetSessionHandler(std::move(s)); + server.RegisterSession(&session->GetServerSession(), next_manager); + + AddMoveObject(&session->GetClientSession()); +} + std::string HLERequestContext::Description() const { if (!command_header) { return "No command header available"; diff --git a/src/core/hle/service/hle_ipc.h b/src/core/hle/service/hle_ipc.h index 440737db5..c2e0e5e8c 100644 --- a/src/core/hle/service/hle_ipc.h +++ b/src/core/hle/service/hle_ipc.h @@ -41,6 +41,8 @@ class KernelCore; class KHandleTable; class KProcess; class KServerSession; +template <typename T> +class KScopedAutoObject; class KThread; } // namespace Kernel @@ -337,6 +339,8 @@ public: outgoing_move_objects.emplace_back(object); } + void AddMoveInterface(SessionRequestHandlerPtr s); + void AddCopyObject(Kernel::KAutoObject* object) { outgoing_copy_objects.emplace_back(object); } @@ -424,6 +428,9 @@ private: Kernel::KernelCore& kernel; Core::Memory::Memory& memory; + + mutable std::array<Common::ScratchBuffer<u8>, 3> read_buffer_data_a{}; + mutable std::array<Common::ScratchBuffer<u8>, 3> read_buffer_data_x{}; }; } // namespace Service diff --git a/src/core/hle/service/jit/jit.cpp b/src/core/hle/service/jit/jit.cpp index 77aa6d7d1..d8fefff89 100644 --- a/src/core/hle/service/jit/jit.cpp +++ b/src/core/hle/service/jit/jit.cpp @@ -6,12 +6,12 @@ #include "core/core.h" #include "core/hle/kernel/k_transfer_memory.h" #include "core/hle/result.h" +#include "core/hle/service/cmif_serialization.h" #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/jit/jit.h" #include "core/hle/service/jit/jit_code_memory.h" #include "core/hle/service/jit/jit_context.h" #include "core/hle/service/server_manager.h" -#include "core/hle/service/service.h" #include "core/memory.h" namespace Service::JIT { @@ -21,6 +21,9 @@ struct CodeRange { u64 size; }; +using Struct32 = std::array<u64, 4>; +static_assert(sizeof(Struct32) == 32, "Struct32 has wrong size"); + class IJitEnvironment final : public ServiceFramework<IJitEnvironment> { public: explicit IJitEnvironment(Core::System& system_, @@ -29,12 +32,13 @@ public: : ServiceFramework{system_, "IJitEnvironment"}, process{std::move(process_)}, user_rx{std::move(user_rx_)}, user_ro{std::move(user_ro_)}, context{system_.ApplicationMemory()} { + // clang-format off static const FunctionInfo functions[] = { - {0, &IJitEnvironment::GenerateCode, "GenerateCode"}, - {1, &IJitEnvironment::Control, "Control"}, - {1000, &IJitEnvironment::LoadPlugin, "LoadPlugin"}, - {1001, &IJitEnvironment::GetCodeAddress, "GetCodeAddress"}, + {0, C<&IJitEnvironment::GenerateCode>, "GenerateCode"}, + {1, C<&IJitEnvironment::Control>, "Control"}, + {1000, C<&IJitEnvironment::LoadPlugin>, "LoadPlugin"}, + {1001, C<&IJitEnvironment::GetCodeAddress>, "GetCodeAddress"}, }; // clang-format on @@ -50,28 +54,10 @@ public: configuration.sys_ro_memory = configuration.user_ro_memory; } - void GenerateCode(HLERequestContext& ctx) { - LOG_DEBUG(Service_JIT, "called"); - - struct InputParameters { - u32 data_size; - u64 command; - std::array<CodeRange, 2> ranges; - Struct32 data; - }; - - struct OutputParameters { - s32 return_value; - std::array<CodeRange, 2> ranges; - }; - - IPC::RequestParser rp{ctx}; - const auto parameters{rp.PopRaw<InputParameters>()}; - - // Optional input/output buffers - const auto input_buffer{ctx.CanReadBuffer() ? ctx.ReadBuffer() : std::span<const u8>()}; - std::vector<u8> output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0); - + Result GenerateCode(Out<s32> out_return_value, Out<CodeRange> out_range0, + Out<CodeRange> out_range1, OutBuffer<BufferAttr_HipcMapAlias> out_buffer, + u32 data_size, u64 command, CodeRange range0, CodeRange range1, + Struct32 data, InBuffer<BufferAttr_HipcMapAlias> buffer) { // Function call prototype: // void GenerateCode(s32* ret, CodeRange* c0_out, CodeRange* c1_out, JITConfiguration* cfg, // u64 cmd, u8* input_buf, size_t input_size, CodeRange* c0_in, @@ -83,66 +69,36 @@ public: // other arguments are used to transfer state between the game and the plugin. const VAddr ret_ptr{context.AddHeap(0u)}; - const VAddr c0_in_ptr{context.AddHeap(parameters.ranges[0])}; - const VAddr c1_in_ptr{context.AddHeap(parameters.ranges[1])}; - const VAddr c0_out_ptr{context.AddHeap(ClearSize(parameters.ranges[0]))}; - const VAddr c1_out_ptr{context.AddHeap(ClearSize(parameters.ranges[1]))}; - - const VAddr input_ptr{context.AddHeap(input_buffer.data(), input_buffer.size())}; - const VAddr output_ptr{context.AddHeap(output_buffer.data(), output_buffer.size())}; - const VAddr data_ptr{context.AddHeap(parameters.data)}; + const VAddr c0_in_ptr{context.AddHeap(range0)}; + const VAddr c1_in_ptr{context.AddHeap(range1)}; + const VAddr c0_out_ptr{context.AddHeap(ClearSize(range0))}; + const VAddr c1_out_ptr{context.AddHeap(ClearSize(range1))}; + + const VAddr input_ptr{context.AddHeap(buffer.data(), buffer.size())}; + const VAddr output_ptr{context.AddHeap(out_buffer.data(), out_buffer.size())}; + const VAddr data_ptr{context.AddHeap(data)}; const VAddr configuration_ptr{context.AddHeap(configuration)}; // The callback does not directly return a value, it only writes to the output pointer context.CallFunction(callbacks.GenerateCode, ret_ptr, c0_out_ptr, c1_out_ptr, - configuration_ptr, parameters.command, input_ptr, input_buffer.size(), - c0_in_ptr, c1_in_ptr, data_ptr, parameters.data_size, output_ptr, - output_buffer.size()); - - const s32 return_value{context.GetHeap<s32>(ret_ptr)}; - - if (return_value == 0) { - // The callback has written to the output executable code range, - // requiring an instruction cache invalidation - Core::InvalidateInstructionCacheRange(process.GetPointerUnsafe(), - configuration.user_rx_memory.offset, - configuration.user_rx_memory.size); - - // Write back to the IPC output buffer, if provided - if (ctx.CanWriteBuffer()) { - context.GetHeap(output_ptr, output_buffer.data(), output_buffer.size()); - ctx.WriteBuffer(output_buffer.data(), output_buffer.size()); - } - - const OutputParameters out{ - .return_value = return_value, - .ranges = - { - context.GetHeap<CodeRange>(c0_out_ptr), - context.GetHeap<CodeRange>(c1_out_ptr), - }, - }; - - IPC::ResponseBuilder rb{ctx, 8}; - rb.Push(ResultSuccess); - rb.PushRaw(out); - } else { - LOG_WARNING(Service_JIT, "plugin GenerateCode callback failed"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - } - }; + configuration_ptr, command, input_ptr, buffer.size(), c0_in_ptr, + c1_in_ptr, data_ptr, data_size, output_ptr, out_buffer.size()); - void Control(HLERequestContext& ctx) { - LOG_DEBUG(Service_JIT, "called"); + *out_return_value = context.GetHeap<s32>(ret_ptr); + *out_range0 = context.GetHeap<CodeRange>(c0_out_ptr); + *out_range1 = context.GetHeap<CodeRange>(c1_out_ptr); + context.GetHeap(output_ptr, out_buffer.data(), out_buffer.size()); - IPC::RequestParser rp{ctx}; - const auto command{rp.PopRaw<u64>()}; + if (*out_return_value != 0) { + LOG_WARNING(Service_JIT, "plugin GenerateCode callback failed"); + R_THROW(ResultUnknown); + } - // Optional input/output buffers - const auto input_buffer{ctx.CanReadBuffer() ? ctx.ReadBuffer() : std::span<const u8>()}; - std::vector<u8> output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0); + R_SUCCEED(); + } + Result Control(Out<s32> out_return_value, InBuffer<BufferAttr_HipcMapAlias> in_data, + OutBuffer<BufferAttr_HipcMapAlias> out_data, u64 command) { // Function call prototype: // u64 Control(s32* ret, JITConfiguration* cfg, u64 cmd, u8* input_buf, size_t input_size, // u8* output_buf, size_t output_size); @@ -152,53 +108,30 @@ public: const VAddr ret_ptr{context.AddHeap(0u)}; const VAddr configuration_ptr{context.AddHeap(configuration)}; - const VAddr input_ptr{context.AddHeap(input_buffer.data(), input_buffer.size())}; - const VAddr output_ptr{context.AddHeap(output_buffer.data(), output_buffer.size())}; + const VAddr input_ptr{context.AddHeap(in_data.data(), in_data.size())}; + const VAddr output_ptr{context.AddHeap(out_data.data(), out_data.size())}; const u64 wrapper_value{context.CallFunction(callbacks.Control, ret_ptr, configuration_ptr, - command, input_ptr, input_buffer.size(), - output_ptr, output_buffer.size())}; - - const s32 return_value{context.GetHeap<s32>(ret_ptr)}; - - if (wrapper_value == 0 && return_value == 0) { - // Write back to the IPC output buffer, if provided - if (ctx.CanWriteBuffer()) { - context.GetHeap(output_ptr, output_buffer.data(), output_buffer.size()); - ctx.WriteBuffer(output_buffer.data(), output_buffer.size()); - } - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(return_value); - } else { - LOG_WARNING(Service_JIT, "plugin Control callback failed"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - } - } - - void LoadPlugin(HLERequestContext& ctx) { - LOG_DEBUG(Service_JIT, "called"); + command, input_ptr, in_data.size(), output_ptr, + out_data.size())}; - IPC::RequestParser rp{ctx}; - const auto tmem_size{rp.PopRaw<u64>()}; - const auto tmem_handle{ctx.GetCopyHandle(0)}; - const auto nro_plugin{ctx.ReadBuffer(1)}; + *out_return_value = context.GetHeap<s32>(ret_ptr); + context.GetHeap(output_ptr, out_data.data(), out_data.size()); - if (tmem_size == 0) { - LOG_ERROR(Service_JIT, "attempted to load plugin with empty transfer memory"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; + if (wrapper_value == 0 && *out_return_value == 0) { + R_SUCCEED(); } - auto tmem{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(tmem_handle)}; + LOG_WARNING(Service_JIT, "plugin Control callback failed"); + R_THROW(ResultUnknown); + } + + Result LoadPlugin(u64 tmem_size, InCopyHandle<Kernel::KTransferMemory>& tmem, + InBuffer<BufferAttr_HipcMapAlias> nrr, + InBuffer<BufferAttr_HipcMapAlias> nro) { if (tmem.IsNull()) { - LOG_ERROR(Service_JIT, "attempted to load plugin with invalid transfer memory handle"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; + LOG_ERROR(Service_JIT, "Invalid transfer memory handle!"); + R_THROW(ResultUnknown); } // Set up the configuration with the required TransferMemory address @@ -206,7 +139,7 @@ public: configuration.transfer_memory.size = tmem_size; // Gather up all the callbacks from the loaded plugin - auto symbols{Core::Symbols::GetSymbols(nro_plugin, true)}; + auto symbols{Core::Symbols::GetSymbols(nro, true)}; const auto GetSymbol{[&](const std::string& name) { return symbols[name].first; }}; callbacks.rtld_fini = GetSymbol("_fini"); @@ -223,16 +156,12 @@ public: if (callbacks.GetVersion == 0 || callbacks.Configure == 0 || callbacks.GenerateCode == 0 || callbacks.OnPrepared == 0) { LOG_ERROR(Service_JIT, "plugin does not implement all necessary functionality"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; + R_THROW(ResultUnknown); } - if (!context.LoadNRO(nro_plugin)) { + if (!context.LoadNRO(nro)) { LOG_ERROR(Service_JIT, "failed to load plugin"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; + R_THROW(ResultUnknown); } context.MapProcessMemory(configuration.sys_ro_memory.offset, @@ -252,9 +181,7 @@ public: const auto version{context.CallFunction(callbacks.GetVersion)}; if (version != 1) { LOG_ERROR(Service_JIT, "unknown plugin version {}", version); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; + R_THROW(ResultUnknown); } // Function prototype: @@ -280,22 +207,19 @@ public: const auto configuration_ptr{context.AddHeap(configuration)}; context.CallFunction(callbacks.OnPrepared, configuration_ptr); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + R_SUCCEED(); } - void GetCodeAddress(HLERequestContext& ctx) { + Result GetCodeAddress(Out<u64> rx_offset, Out<u64> ro_offset) { LOG_DEBUG(Service_JIT, "called"); - IPC::ResponseBuilder rb{ctx, 6}; - rb.Push(ResultSuccess); - rb.Push(configuration.user_rx_memory.offset); - rb.Push(configuration.user_ro_memory.offset); + *rx_offset = configuration.user_rx_memory.offset; + *ro_offset = configuration.user_ro_memory.offset; + + R_SUCCEED(); } private: - using Struct32 = std::array<u8, 32>; - struct GuestCallbacks { VAddr rtld_fini; VAddr rtld_init; @@ -335,7 +259,7 @@ public: explicit JITU(Core::System& system_) : ServiceFramework{system_, "jit:u"} { // clang-format off static const FunctionInfo functions[] = { - {0, &JITU::CreateJitEnvironment, "CreateJitEnvironment"}, + {0, C<&JITU::CreateJitEnvironment>, "CreateJitEnvironment"}, }; // clang-format on @@ -343,76 +267,33 @@ public: } private: - void CreateJitEnvironment(HLERequestContext& ctx) { - LOG_DEBUG(Service_JIT, "called"); - - struct Parameters { - u64 rx_size; - u64 ro_size; - }; - - IPC::RequestParser rp{ctx}; - const auto parameters{rp.PopRaw<Parameters>()}; - const auto process_handle{ctx.GetCopyHandle(0)}; - const auto rx_mem_handle{ctx.GetCopyHandle(1)}; - const auto ro_mem_handle{ctx.GetCopyHandle(2)}; - - if (parameters.rx_size == 0 || parameters.ro_size == 0) { - LOG_ERROR(Service_JIT, "attempted to init with empty code regions"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; - } - - auto process{ctx.GetObjectFromHandle<Kernel::KProcess>(process_handle)}; + Result CreateJitEnvironment(Out<SharedPointer<IJitEnvironment>> out_jit_environment, + u64 rx_size, u64 ro_size, InCopyHandle<Kernel::KProcess>& process, + InCopyHandle<Kernel::KCodeMemory>& rx_mem, + InCopyHandle<Kernel::KCodeMemory>& ro_mem) { if (process.IsNull()) { - LOG_ERROR(Service_JIT, "process is null for handle=0x{:08X}", process_handle); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; + LOG_ERROR(Service_JIT, "process is null"); + R_THROW(ResultUnknown); } - - auto rx_mem{ctx.GetObjectFromHandle<Kernel::KCodeMemory>(rx_mem_handle)}; if (rx_mem.IsNull()) { - LOG_ERROR(Service_JIT, "rx_mem is null for handle=0x{:08X}", rx_mem_handle); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; + LOG_ERROR(Service_JIT, "rx_mem is null"); + R_THROW(ResultUnknown); } - - auto ro_mem{ctx.GetObjectFromHandle<Kernel::KCodeMemory>(ro_mem_handle)}; - if (ro_mem.IsNull()) { - LOG_ERROR(Service_JIT, "ro_mem is null for handle=0x{:08X}", ro_mem_handle); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; + if (rx_mem.IsNull()) { + LOG_ERROR(Service_JIT, "ro_mem is null"); + R_THROW(ResultUnknown); } CodeMemory rx, ro; - Result res; - - res = rx.Initialize(*process, *rx_mem, parameters.rx_size, - Kernel::Svc::MemoryPermission::ReadExecute, generate_random); - if (R_FAILED(res)) { - LOG_ERROR(Service_JIT, "rx_mem could not be mapped for handle=0x{:08X}", rx_mem_handle); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(res); - return; - } - res = ro.Initialize(*process, *ro_mem, parameters.ro_size, - Kernel::Svc::MemoryPermission::Read, generate_random); - if (R_FAILED(res)) { - LOG_ERROR(Service_JIT, "ro_mem could not be mapped for handle=0x{:08X}", ro_mem_handle); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(res); - return; - } + R_TRY(rx.Initialize(*process, *rx_mem, rx_size, Kernel::Svc::MemoryPermission::ReadExecute, + generate_random)); + R_TRY(ro.Initialize(*process, *ro_mem, ro_size, Kernel::Svc::MemoryPermission::Read, + generate_random)); - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IJitEnvironment>(system, std::move(process), std::move(rx), - std::move(ro)); + *out_jit_environment = std::make_shared<IJitEnvironment>(system, std::move(process), + std::move(rx), std::move(ro)); + R_SUCCEED(); } private: diff --git a/src/core/hle/service/kernel_helpers.cpp b/src/core/hle/service/kernel_helpers.cpp index f51e63564..f080f7ffa 100644 --- a/src/core/hle/service/kernel_helpers.cpp +++ b/src/core/hle/service/kernel_helpers.cpp @@ -65,6 +65,9 @@ Kernel::KEvent* ServiceContext::CreateEvent(std::string&& name) { } void ServiceContext::CloseEvent(Kernel::KEvent* event) { + if (!event) { + return; + } event->GetReadableEvent().Close(); event->Close(); } diff --git a/src/core/hle/service/nfc/common/device.cpp b/src/core/hle/service/nfc/common/device.cpp index cc7776efc..1e2d2d212 100644 --- a/src/core/hle/service/nfc/common/device.cpp +++ b/src/core/hle/service/nfc/common/device.cpp @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "core/hle/service/glue/time/static.h" +#include "core/hle/service/psc/time/steady_clock.h" #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4701) // Potentially uninitialized local variable 'result' used @@ -29,7 +31,8 @@ #include "core/hle/service/nfc/common/device.h" #include "core/hle/service/nfc/mifare_result.h" #include "core/hle/service/nfc/nfc_result.h" -#include "core/hle/service/time/time_manager.h" +#include "core/hle/service/service.h" +#include "core/hle/service/sm/sm.h" #include "hid_core/frontend/emulated_controller.h" #include "hid_core/hid_core.h" #include "hid_core/hid_types.h" @@ -393,8 +396,7 @@ Result NfcDevice::WriteMifare(std::span<const MifareWriteBlockParameter> paramet return result; } -Result NfcDevice::SendCommandByPassThrough(const Time::Clock::TimeSpanType& timeout, - std::span<const u8> command_data, +Result NfcDevice::SendCommandByPassThrough(const s64& timeout, std::span<const u8> command_data, std::span<u8> out_data) { // Not implemented return ResultSuccess; @@ -1399,27 +1401,41 @@ void NfcDevice::SetAmiiboName(NFP::AmiiboSettings& settings, } NFP::AmiiboDate NfcDevice::GetAmiiboDate(s64 posix_time) const { - const auto& time_zone_manager = - system.GetTimeManager().GetTimeZoneContentManager().GetTimeZoneManager(); - Time::TimeZone::CalendarInfo calendar_info{}; + auto static_service = + system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", true); + + std::shared_ptr<Service::Glue::Time::TimeZoneService> timezone_service{}; + static_service->GetTimeZoneService(timezone_service); + + Service::PSC::Time::CalendarTime calendar_time{}; + Service::PSC::Time::CalendarAdditionalInfo additional_info{}; + NFP::AmiiboDate amiibo_date{}; amiibo_date.SetYear(2000); amiibo_date.SetMonth(1); amiibo_date.SetDay(1); - if (time_zone_manager.ToCalendarTime({}, posix_time, calendar_info) == ResultSuccess) { - amiibo_date.SetYear(calendar_info.time.year); - amiibo_date.SetMonth(calendar_info.time.month); - amiibo_date.SetDay(calendar_info.time.day); + if (timezone_service->ToCalendarTimeWithMyRule(calendar_time, additional_info, posix_time) == + ResultSuccess) { + amiibo_date.SetYear(calendar_time.year); + amiibo_date.SetMonth(calendar_time.month); + amiibo_date.SetDay(calendar_time.day); } return amiibo_date; } -u64 NfcDevice::GetCurrentPosixTime() const { - auto& standard_steady_clock{system.GetTimeManager().GetStandardSteadyClockCore()}; - return standard_steady_clock.GetCurrentTimePoint(system).time_point; +s64 NfcDevice::GetCurrentPosixTime() const { + auto static_service = + system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", true); + + std::shared_ptr<Service::PSC::Time::SteadyClock> steady_clock{}; + static_service->GetStandardSteadyClock(steady_clock); + + Service::PSC::Time::SteadyClockTimePoint time_point{}; + R_ASSERT(steady_clock->GetCurrentTimePoint(time_point)); + return time_point.time_point; } u64 NfcDevice::RemoveVersionByte(u64 application_id) const { diff --git a/src/core/hle/service/nfc/common/device.h b/src/core/hle/service/nfc/common/device.h index 15f9b25da..d59202d18 100644 --- a/src/core/hle/service/nfc/common/device.h +++ b/src/core/hle/service/nfc/common/device.h @@ -11,7 +11,6 @@ #include "core/hle/service/nfc/nfc_types.h" #include "core/hle/service/nfp/nfp_types.h" #include "core/hle/service/service.h" -#include "core/hle/service/time/clock_types.h" namespace Kernel { class KEvent; @@ -49,8 +48,8 @@ public: Result WriteMifare(std::span<const MifareWriteBlockParameter> parameters); - Result SendCommandByPassThrough(const Time::Clock::TimeSpanType& timeout, - std::span<const u8> command_data, std::span<u8> out_data); + Result SendCommandByPassThrough(const s64& timeout, std::span<const u8> command_data, + std::span<u8> out_data); Result Mount(NFP::ModelType model_type, NFP::MountTarget mount_target); Result Unmount(); @@ -108,7 +107,7 @@ private: NFP::AmiiboName GetAmiiboName(const NFP::AmiiboSettings& settings) const; void SetAmiiboName(NFP::AmiiboSettings& settings, const NFP::AmiiboName& amiibo_name) const; NFP::AmiiboDate GetAmiiboDate(s64 posix_time) const; - u64 GetCurrentPosixTime() const; + s64 GetCurrentPosixTime() const; u64 RemoveVersionByte(u64 application_id) const; void UpdateSettingsCrc(); void UpdateRegisterInfoCrc(); diff --git a/src/core/hle/service/nfc/common/device_manager.cpp b/src/core/hle/service/nfc/common/device_manager.cpp index 44f651b87..b60699c45 100644 --- a/src/core/hle/service/nfc/common/device_manager.cpp +++ b/src/core/hle/service/nfc/common/device_manager.cpp @@ -6,12 +6,14 @@ #include "common/logging/log.h" #include "core/core.h" #include "core/hle/kernel/k_event.h" +#include "core/hle/service/glue/time/static.h" #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/nfc/common/device.h" #include "core/hle/service/nfc/common/device_manager.h" #include "core/hle/service/nfc/nfc_result.h" -#include "core/hle/service/time/clock_types.h" -#include "core/hle/service/time/time_manager.h" +#include "core/hle/service/psc/time/steady_clock.h" +#include "core/hle/service/service.h" +#include "core/hle/service/sm/sm.h" #include "hid_core/hid_types.h" #include "hid_core/hid_util.h" @@ -82,11 +84,19 @@ Result DeviceManager::ListDevices(std::vector<u64>& nfp_devices, std::size_t max continue; } if (skip_fatal_errors) { - constexpr u64 MinimumRecoveryTime = 60; - auto& standard_steady_clock{system.GetTimeManager().GetStandardSteadyClockCore()}; - const u64 elapsed_time = standard_steady_clock.GetCurrentTimePoint(system).time_point - - time_since_last_error; + constexpr s64 MinimumRecoveryTime = 60; + auto static_service = + system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", + true); + + std::shared_ptr<Service::PSC::Time::SteadyClock> steady_clock{}; + static_service->GetStandardSteadyClock(steady_clock); + + Service::PSC::Time::SteadyClockTimePoint time_point{}; + R_ASSERT(steady_clock->GetCurrentTimePoint(time_point)); + + const s64 elapsed_time = time_point.time_point - time_since_last_error; if (time_since_last_error != 0 && elapsed_time < MinimumRecoveryTime) { continue; } @@ -250,8 +260,7 @@ Result DeviceManager::WriteMifare(u64 device_handle, return result; } -Result DeviceManager::SendCommandByPassThrough(u64 device_handle, - const Time::Clock::TimeSpanType& timeout, +Result DeviceManager::SendCommandByPassThrough(u64 device_handle, const s64& timeout, std::span<const u8> command_data, std::span<u8> out_data) { std::scoped_lock lock{mutex}; @@ -741,8 +750,16 @@ Result DeviceManager::VerifyDeviceResult(std::shared_ptr<NfcDevice> device, if (operation_result == ResultUnknown112 || operation_result == ResultUnknown114 || operation_result == ResultUnknown115) { - auto& standard_steady_clock{system.GetTimeManager().GetStandardSteadyClockCore()}; - time_since_last_error = standard_steady_clock.GetCurrentTimePoint(system).time_point; + auto static_service = + system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", true); + + std::shared_ptr<Service::PSC::Time::SteadyClock> steady_clock{}; + static_service->GetStandardSteadyClock(steady_clock); + + Service::PSC::Time::SteadyClockTimePoint time_point{}; + R_ASSERT(steady_clock->GetCurrentTimePoint(time_point)); + + time_since_last_error = time_point.time_point; } return operation_result; diff --git a/src/core/hle/service/nfc/common/device_manager.h b/src/core/hle/service/nfc/common/device_manager.h index f02bdccf5..c56a2fbda 100644 --- a/src/core/hle/service/nfc/common/device_manager.h +++ b/src/core/hle/service/nfc/common/device_manager.h @@ -13,7 +13,6 @@ #include "core/hle/service/nfc/nfc_types.h" #include "core/hle/service/nfp/nfp_types.h" #include "core/hle/service/service.h" -#include "core/hle/service/time/clock_types.h" #include "hid_core/hid_types.h" namespace Service::NFC { @@ -42,7 +41,7 @@ public: std::span<MifareReadBlockData> read_data); Result WriteMifare(u64 device_handle, std::span<const MifareWriteBlockParameter> write_parameters); - Result SendCommandByPassThrough(u64 device_handle, const Time::Clock::TimeSpanType& timeout, + Result SendCommandByPassThrough(u64 device_handle, const s64& timeout, std::span<const u8> command_data, std::span<u8> out_data); // Nfp device manager @@ -92,7 +91,7 @@ private: const std::optional<std::shared_ptr<NfcDevice>> GetNfcDevice(u64 handle) const; bool is_initialized = false; - u64 time_since_last_error = 0; + s64 time_since_last_error = 0; mutable std::mutex mutex; std::array<std::shared_ptr<NfcDevice>, 10> devices{}; diff --git a/src/core/hle/service/nfc/nfc_interface.cpp b/src/core/hle/service/nfc/nfc_interface.cpp index a71cf74b8..207ac4efe 100644 --- a/src/core/hle/service/nfc/nfc_interface.cpp +++ b/src/core/hle/service/nfc/nfc_interface.cpp @@ -13,7 +13,6 @@ #include "core/hle/service/nfc/nfc_result.h" #include "core/hle/service/nfc/nfc_types.h" #include "core/hle/service/nfp/nfp_result.h" -#include "core/hle/service/time/clock_types.h" #include "hid_core/hid_types.h" namespace Service::NFC { @@ -261,10 +260,10 @@ void NfcInterface::WriteMifare(HLERequestContext& ctx) { void NfcInterface::SendCommandByPassThrough(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto device_handle{rp.Pop<u64>()}; - const auto timeout{rp.PopRaw<Time::Clock::TimeSpanType>()}; + const auto timeout{rp.PopRaw<s64>()}; const auto command_data{ctx.ReadBuffer()}; LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, timeout={}, data_size={}", - device_handle, timeout.ToSeconds(), command_data.size()); + device_handle, timeout, command_data.size()); std::vector<u8> out_data(1); auto result = diff --git a/src/core/hle/service/ns/language.cpp b/src/core/hle/service/ns/language.cpp index b1a7686ff..d187be935 100644 --- a/src/core/hle/service/ns/language.cpp +++ b/src/core/hle/service/ns/language.cpp @@ -415,4 +415,4 @@ std::optional<Set::LanguageCode> ConvertToLanguageCode(const ApplicationLanguage return std::nullopt; } } -} // namespace Service::NS
\ No newline at end of file +} // namespace Service::NS diff --git a/src/core/hle/service/ns/language.h b/src/core/hle/service/ns/language.h index ab6b71029..dad9934f2 100644 --- a/src/core/hle/service/ns/language.h +++ b/src/core/hle/service/ns/language.h @@ -5,10 +5,7 @@ #include <optional> #include "common/common_types.h" - -namespace Service::Set { -enum class LanguageCode : u64; -} +#include "core/hle/service/set/system_settings_server.h" namespace Service::NS { /// This is nn::ns::detail::ApplicationLanguage diff --git a/src/core/hle/service/nvdrv/core/container.cpp b/src/core/hle/service/nvdrv/core/container.cpp index 37ca24f5d..21ef57d27 100644 --- a/src/core/hle/service/nvdrv/core/container.cpp +++ b/src/core/hle/service/nvdrv/core/container.cpp @@ -2,27 +2,135 @@ // SPDX-FileCopyrightText: 2022 Skyline Team and Contributors // SPDX-License-Identifier: GPL-3.0-or-later +#include <atomic> +#include <deque> +#include <mutex> + +#include "core/hle/kernel/k_process.h" #include "core/hle/service/nvdrv/core/container.h" +#include "core/hle/service/nvdrv/core/heap_mapper.h" #include "core/hle/service/nvdrv/core/nvmap.h" #include "core/hle/service/nvdrv/core/syncpoint_manager.h" +#include "core/memory.h" #include "video_core/host1x/host1x.h" namespace Service::Nvidia::NvCore { +Session::Session(SessionId id_, Kernel::KProcess* process_, Core::Asid asid_) + : id{id_}, process{process_}, asid{asid_}, has_preallocated_area{}, mapper{}, is_active{} {} + +Session::~Session() = default; + struct ContainerImpl { - explicit ContainerImpl(Tegra::Host1x::Host1x& host1x_) - : file{host1x_}, manager{host1x_}, device_file_data{} {} + explicit ContainerImpl(Container& core, Tegra::Host1x::Host1x& host1x_) + : host1x{host1x_}, file{core, host1x_}, manager{host1x_}, device_file_data{} {} + Tegra::Host1x::Host1x& host1x; NvMap file; SyncpointManager manager; Container::Host1xDeviceFileData device_file_data; + std::deque<Session> sessions; + size_t new_ids{}; + std::deque<size_t> id_pool; + std::mutex session_guard; }; Container::Container(Tegra::Host1x::Host1x& host1x_) { - impl = std::make_unique<ContainerImpl>(host1x_); + impl = std::make_unique<ContainerImpl>(*this, host1x_); } Container::~Container() = default; +SessionId Container::OpenSession(Kernel::KProcess* process) { + using namespace Common::Literals; + + std::scoped_lock lk(impl->session_guard); + for (auto& session : impl->sessions) { + if (!session.is_active) { + continue; + } + if (session.process == process) { + return session.id; + } + } + size_t new_id{}; + auto* memory_interface = &process->GetMemory(); + auto& smmu = impl->host1x.MemoryManager(); + auto asid = smmu.RegisterProcess(memory_interface); + if (!impl->id_pool.empty()) { + new_id = impl->id_pool.front(); + impl->id_pool.pop_front(); + impl->sessions[new_id] = Session{SessionId{new_id}, process, asid}; + } else { + new_id = impl->new_ids++; + impl->sessions.emplace_back(SessionId{new_id}, process, asid); + } + auto& session = impl->sessions[new_id]; + session.is_active = true; + // Optimization + if (process->IsApplication()) { + auto& page_table = process->GetPageTable().GetBasePageTable(); + auto heap_start = page_table.GetHeapRegionStart(); + + Kernel::KProcessAddress cur_addr = heap_start; + size_t region_size = 0; + VAddr region_start = 0; + while (true) { + Kernel::KMemoryInfo mem_info{}; + Kernel::Svc::PageInfo page_info{}; + R_ASSERT(page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info), + cur_addr)); + auto svc_mem_info = mem_info.GetSvcMemoryInfo(); + + // Check if this memory block is heap. + if (svc_mem_info.state == Kernel::Svc::MemoryState::Normal) { + if (svc_mem_info.size > region_size) { + region_size = svc_mem_info.size; + region_start = svc_mem_info.base_address; + } + } + + // Check if we're done. + const uintptr_t next_address = svc_mem_info.base_address + svc_mem_info.size; + if (next_address <= GetInteger(cur_addr)) { + break; + } + + cur_addr = next_address; + } + session.has_preallocated_area = false; + auto start_region = region_size >= 32_MiB ? smmu.Allocate(region_size) : 0; + if (start_region != 0) { + session.mapper = std::make_unique<HeapMapper>(region_start, start_region, region_size, + asid, impl->host1x); + smmu.TrackContinuity(start_region, region_start, region_size, asid); + session.has_preallocated_area = true; + LOG_DEBUG(Debug, "Preallocation created!"); + } + } + return SessionId{new_id}; +} + +void Container::CloseSession(SessionId session_id) { + std::scoped_lock lk(impl->session_guard); + auto& session = impl->sessions[session_id.id]; + auto& smmu = impl->host1x.MemoryManager(); + if (session.has_preallocated_area) { + const DAddr region_start = session.mapper->GetRegionStart(); + const size_t region_size = session.mapper->GetRegionSize(); + session.mapper.reset(); + smmu.Free(region_start, region_size); + session.has_preallocated_area = false; + } + session.is_active = false; + smmu.UnregisterProcess(impl->sessions[session_id.id].asid); + impl->id_pool.emplace_front(session_id.id); +} + +Session* Container::GetSession(SessionId session_id) { + std::atomic_thread_fence(std::memory_order_acquire); + return &impl->sessions[session_id.id]; +} + NvMap& Container::GetNvMapFile() { return impl->file; } diff --git a/src/core/hle/service/nvdrv/core/container.h b/src/core/hle/service/nvdrv/core/container.h index b4b63ac90..b4d3938a8 100644 --- a/src/core/hle/service/nvdrv/core/container.h +++ b/src/core/hle/service/nvdrv/core/container.h @@ -8,24 +8,56 @@ #include <memory> #include <unordered_map> +#include "core/device_memory_manager.h" #include "core/hle/service/nvdrv/nvdata.h" +namespace Kernel { +class KProcess; +} + namespace Tegra::Host1x { class Host1x; } // namespace Tegra::Host1x namespace Service::Nvidia::NvCore { +class HeapMapper; class NvMap; class SyncpointManager; struct ContainerImpl; +struct SessionId { + size_t id; +}; + +struct Session { + Session(SessionId id_, Kernel::KProcess* process_, Core::Asid asid_); + ~Session(); + + Session(const Session&) = delete; + Session& operator=(const Session&) = delete; + Session(Session&&) = default; + Session& operator=(Session&&) = default; + + SessionId id; + Kernel::KProcess* process; + Core::Asid asid; + bool has_preallocated_area{}; + std::unique_ptr<HeapMapper> mapper{}; + bool is_active{}; +}; + class Container { public: explicit Container(Tegra::Host1x::Host1x& host1x); ~Container(); + SessionId OpenSession(Kernel::KProcess* process); + void CloseSession(SessionId id); + + Session* GetSession(SessionId id); + NvMap& GetNvMapFile(); const NvMap& GetNvMapFile() const; diff --git a/src/core/hle/service/nvdrv/core/heap_mapper.cpp b/src/core/hle/service/nvdrv/core/heap_mapper.cpp new file mode 100644 index 000000000..096dc5deb --- /dev/null +++ b/src/core/hle/service/nvdrv/core/heap_mapper.cpp @@ -0,0 +1,175 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include <mutex> + +#include <boost/container/small_vector.hpp> +#define BOOST_NO_MT +#include <boost/pool/detail/mutex.hpp> +#undef BOOST_NO_MT +#include <boost/icl/interval.hpp> +#include <boost/icl/interval_base_set.hpp> +#include <boost/icl/interval_set.hpp> +#include <boost/icl/split_interval_map.hpp> +#include <boost/pool/pool.hpp> +#include <boost/pool/pool_alloc.hpp> +#include <boost/pool/poolfwd.hpp> + +#include "core/hle/service/nvdrv/core/heap_mapper.h" +#include "video_core/host1x/host1x.h" + +namespace boost { +template <typename T> +class fast_pool_allocator<T, default_user_allocator_new_delete, details::pool::null_mutex, 4096, 0>; +} + +namespace Service::Nvidia::NvCore { + +using IntervalCompare = std::less<DAddr>; +using IntervalInstance = boost::icl::interval_type_default<DAddr, std::less>; +using IntervalAllocator = boost::fast_pool_allocator<DAddr>; +using IntervalSet = boost::icl::interval_set<DAddr>; +using IntervalType = typename IntervalSet::interval_type; + +template <typename Type> +struct counter_add_functor : public boost::icl::identity_based_inplace_combine<Type> { + // types + typedef counter_add_functor<Type> type; + typedef boost::icl::identity_based_inplace_combine<Type> base_type; + + // public member functions + void operator()(Type& current, const Type& added) const { + current += added; + if (current < base_type::identity_element()) { + current = base_type::identity_element(); + } + } + + // public static functions + static void version(Type&){}; +}; + +using OverlapCombine = counter_add_functor<int>; +using OverlapSection = boost::icl::inter_section<int>; +using OverlapCounter = boost::icl::split_interval_map<DAddr, int>; + +struct HeapMapper::HeapMapperInternal { + HeapMapperInternal(Tegra::Host1x::Host1x& host1x) : device_memory{host1x.MemoryManager()} {} + ~HeapMapperInternal() = default; + + template <typename Func> + void ForEachInOverlapCounter(OverlapCounter& current_range, VAddr cpu_addr, u64 size, + Func&& func) { + const DAddr start_address = cpu_addr; + const DAddr end_address = start_address + size; + const IntervalType search_interval{start_address, end_address}; + auto it = current_range.lower_bound(search_interval); + if (it == current_range.end()) { + return; + } + auto end_it = current_range.upper_bound(search_interval); + for (; it != end_it; it++) { + auto& inter = it->first; + DAddr inter_addr_end = inter.upper(); + DAddr inter_addr = inter.lower(); + if (inter_addr_end > end_address) { + inter_addr_end = end_address; + } + if (inter_addr < start_address) { + inter_addr = start_address; + } + func(inter_addr, inter_addr_end, it->second); + } + } + + void RemoveEachInOverlapCounter(OverlapCounter& current_range, + const IntervalType search_interval, int subtract_value) { + bool any_removals = false; + current_range.add(std::make_pair(search_interval, subtract_value)); + do { + any_removals = false; + auto it = current_range.lower_bound(search_interval); + if (it == current_range.end()) { + return; + } + auto end_it = current_range.upper_bound(search_interval); + for (; it != end_it; it++) { + if (it->second <= 0) { + any_removals = true; + current_range.erase(it); + break; + } + } + } while (any_removals); + } + + IntervalSet base_set; + OverlapCounter mapping_overlaps; + Tegra::MaxwellDeviceMemoryManager& device_memory; + std::mutex guard; +}; + +HeapMapper::HeapMapper(VAddr start_vaddress, DAddr start_daddress, size_t size, Core::Asid asid, + Tegra::Host1x::Host1x& host1x) + : m_vaddress{start_vaddress}, m_daddress{start_daddress}, m_size{size}, m_asid{asid} { + m_internal = std::make_unique<HeapMapperInternal>(host1x); +} + +HeapMapper::~HeapMapper() { + m_internal->device_memory.Unmap(m_daddress, m_size); +} + +DAddr HeapMapper::Map(VAddr start, size_t size) { + std::scoped_lock lk(m_internal->guard); + m_internal->base_set.clear(); + const IntervalType interval{start, start + size}; + m_internal->base_set.insert(interval); + m_internal->ForEachInOverlapCounter(m_internal->mapping_overlaps, start, size, + [this](VAddr start_addr, VAddr end_addr, int) { + const IntervalType other{start_addr, end_addr}; + m_internal->base_set.subtract(other); + }); + if (!m_internal->base_set.empty()) { + auto it = m_internal->base_set.begin(); + auto end_it = m_internal->base_set.end(); + for (; it != end_it; it++) { + const VAddr inter_addr_end = it->upper(); + const VAddr inter_addr = it->lower(); + const size_t offset = inter_addr - m_vaddress; + const size_t sub_size = inter_addr_end - inter_addr; + m_internal->device_memory.Map(m_daddress + offset, m_vaddress + offset, sub_size, + m_asid); + } + } + m_internal->mapping_overlaps += std::make_pair(interval, 1); + m_internal->base_set.clear(); + return m_daddress + (start - m_vaddress); +} + +void HeapMapper::Unmap(VAddr start, size_t size) { + std::scoped_lock lk(m_internal->guard); + m_internal->base_set.clear(); + m_internal->ForEachInOverlapCounter(m_internal->mapping_overlaps, start, size, + [this](VAddr start_addr, VAddr end_addr, int value) { + if (value <= 1) { + const IntervalType other{start_addr, end_addr}; + m_internal->base_set.insert(other); + } + }); + if (!m_internal->base_set.empty()) { + auto it = m_internal->base_set.begin(); + auto end_it = m_internal->base_set.end(); + for (; it != end_it; it++) { + const VAddr inter_addr_end = it->upper(); + const VAddr inter_addr = it->lower(); + const size_t offset = inter_addr - m_vaddress; + const size_t sub_size = inter_addr_end - inter_addr; + m_internal->device_memory.Unmap(m_daddress + offset, sub_size); + } + } + const IntervalType to_remove{start, start + size}; + m_internal->RemoveEachInOverlapCounter(m_internal->mapping_overlaps, to_remove, -1); + m_internal->base_set.clear(); +} + +} // namespace Service::Nvidia::NvCore diff --git a/src/core/hle/service/nvdrv/core/heap_mapper.h b/src/core/hle/service/nvdrv/core/heap_mapper.h new file mode 100644 index 000000000..491a12e4f --- /dev/null +++ b/src/core/hle/service/nvdrv/core/heap_mapper.h @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include <memory> + +#include "common/common_types.h" +#include "core/device_memory_manager.h" + +namespace Tegra::Host1x { +class Host1x; +} // namespace Tegra::Host1x + +namespace Service::Nvidia::NvCore { + +class HeapMapper { +public: + HeapMapper(VAddr start_vaddress, DAddr start_daddress, size_t size, Core::Asid asid, + Tegra::Host1x::Host1x& host1x); + ~HeapMapper(); + + bool IsInBounds(VAddr start, size_t size) const { + VAddr end = start + size; + return start >= m_vaddress && end <= (m_vaddress + m_size); + } + + DAddr Map(VAddr start, size_t size); + + void Unmap(VAddr start, size_t size); + + DAddr GetRegionStart() const { + return m_daddress; + } + + size_t GetRegionSize() const { + return m_size; + } + +private: + struct HeapMapperInternal; + VAddr m_vaddress; + DAddr m_daddress; + size_t m_size; + Core::Asid m_asid; + std::unique_ptr<HeapMapperInternal> m_internal; +}; + +} // namespace Service::Nvidia::NvCore diff --git a/src/core/hle/service/nvdrv/core/nvmap.cpp b/src/core/hle/service/nvdrv/core/nvmap.cpp index 0ca05257e..1b59c6b15 100644 --- a/src/core/hle/service/nvdrv/core/nvmap.cpp +++ b/src/core/hle/service/nvdrv/core/nvmap.cpp @@ -2,14 +2,19 @@ // SPDX-FileCopyrightText: 2022 Skyline Team and Contributors // SPDX-License-Identifier: GPL-3.0-or-later +#include <functional> + #include "common/alignment.h" #include "common/assert.h" #include "common/logging/log.h" +#include "core/hle/service/nvdrv/core/container.h" +#include "core/hle/service/nvdrv/core/heap_mapper.h" #include "core/hle/service/nvdrv/core/nvmap.h" #include "core/memory.h" #include "video_core/host1x/host1x.h" using Core::Memory::YUZU_PAGESIZE; +constexpr size_t BIG_PAGE_SIZE = YUZU_PAGESIZE * 16; namespace Service::Nvidia::NvCore { NvMap::Handle::Handle(u64 size_, Id id_) @@ -17,9 +22,9 @@ NvMap::Handle::Handle(u64 size_, Id id_) flags.raw = 0; } -NvResult NvMap::Handle::Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress) { +NvResult NvMap::Handle::Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress, + NvCore::SessionId pSessionId) { std::scoped_lock lock(mutex); - // Handles cannot be allocated twice if (allocated) { return NvResult::AccessDenied; @@ -28,6 +33,7 @@ NvResult NvMap::Handle::Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress) flags = pFlags; kind = pKind; align = pAlign < YUZU_PAGESIZE ? YUZU_PAGESIZE : pAlign; + session_id = pSessionId; // This flag is only applicable for handles with an address passed if (pAddress) { @@ -63,7 +69,7 @@ NvResult NvMap::Handle::Duplicate(bool internal_session) { return NvResult::Success; } -NvMap::NvMap(Tegra::Host1x::Host1x& host1x_) : host1x{host1x_} {} +NvMap::NvMap(Container& core_, Tegra::Host1x::Host1x& host1x_) : host1x{host1x_}, core{core_} {} void NvMap::AddHandle(std::shared_ptr<Handle> handle_description) { std::scoped_lock lock(handles_lock); @@ -78,12 +84,30 @@ void NvMap::UnmapHandle(Handle& handle_description) { handle_description.unmap_queue_entry.reset(); } + // Free and unmap the handle from Host1x GMMU + if (handle_description.pin_virt_address) { + host1x.GMMU().Unmap(static_cast<GPUVAddr>(handle_description.pin_virt_address), + handle_description.aligned_size); + host1x.Allocator().Free(handle_description.pin_virt_address, + static_cast<u32>(handle_description.aligned_size)); + handle_description.pin_virt_address = 0; + } + // Free and unmap the handle from the SMMU - host1x.MemoryManager().Unmap(static_cast<GPUVAddr>(handle_description.pin_virt_address), - handle_description.aligned_size); - host1x.Allocator().Free(handle_description.pin_virt_address, - static_cast<u32>(handle_description.aligned_size)); - handle_description.pin_virt_address = 0; + const size_t map_size = handle_description.aligned_size; + if (!handle_description.in_heap) { + auto& smmu = host1x.MemoryManager(); + size_t aligned_up = Common::AlignUp(map_size, BIG_PAGE_SIZE); + smmu.Unmap(handle_description.d_address, map_size); + smmu.Free(handle_description.d_address, static_cast<size_t>(aligned_up)); + handle_description.d_address = 0; + return; + } + const VAddr vaddress = handle_description.address; + auto* session = core.GetSession(handle_description.session_id); + session->mapper->Unmap(vaddress, map_size); + handle_description.d_address = 0; + handle_description.in_heap = false; } bool NvMap::TryRemoveHandle(const Handle& handle_description) { @@ -124,22 +148,33 @@ std::shared_ptr<NvMap::Handle> NvMap::GetHandle(Handle::Id handle) { } } -VAddr NvMap::GetHandleAddress(Handle::Id handle) { +DAddr NvMap::GetHandleAddress(Handle::Id handle) { std::scoped_lock lock(handles_lock); try { - return handles.at(handle)->address; + return handles.at(handle)->d_address; } catch (std::out_of_range&) { return 0; } } -u32 NvMap::PinHandle(NvMap::Handle::Id handle) { +DAddr NvMap::PinHandle(NvMap::Handle::Id handle, bool low_area_pin) { auto handle_description{GetHandle(handle)}; if (!handle_description) [[unlikely]] { return 0; } std::scoped_lock lock(handle_description->mutex); + const auto map_low_area = [&] { + if (handle_description->pin_virt_address == 0) { + auto& gmmu_allocator = host1x.Allocator(); + auto& gmmu = host1x.GMMU(); + u32 address = + gmmu_allocator.Allocate(static_cast<u32>(handle_description->aligned_size)); + gmmu.Map(static_cast<GPUVAddr>(address), handle_description->d_address, + handle_description->aligned_size); + handle_description->pin_virt_address = address; + } + }; if (!handle_description->pins) { // If we're in the unmap queue we can just remove ourselves and return since we're already // mapped @@ -151,37 +186,58 @@ u32 NvMap::PinHandle(NvMap::Handle::Id handle) { unmap_queue.erase(*handle_description->unmap_queue_entry); handle_description->unmap_queue_entry.reset(); + if (low_area_pin) { + map_low_area(); + handle_description->pins++; + return static_cast<DAddr>(handle_description->pin_virt_address); + } + handle_description->pins++; - return handle_description->pin_virt_address; + return handle_description->d_address; } } + using namespace std::placeholders; // If not then allocate some space and map it - u32 address{}; - auto& smmu_allocator = host1x.Allocator(); - auto& smmu_memory_manager = host1x.MemoryManager(); - while ((address = smmu_allocator.Allocate( - static_cast<u32>(handle_description->aligned_size))) == 0) { - // Free handles until the allocation succeeds - std::scoped_lock queueLock(unmap_queue_lock); - if (auto freeHandleDesc{unmap_queue.front()}) { - // Handles in the unmap queue are guaranteed not to be pinned so don't bother - // checking if they are before unmapping - std::scoped_lock freeLock(freeHandleDesc->mutex); - if (handle_description->pin_virt_address) - UnmapHandle(*freeHandleDesc); - } else { - LOG_CRITICAL(Service_NVDRV, "Ran out of SMMU address space!"); + DAddr address{}; + auto& smmu = host1x.MemoryManager(); + auto* session = core.GetSession(handle_description->session_id); + const VAddr vaddress = handle_description->address; + const size_t map_size = handle_description->aligned_size; + if (session->has_preallocated_area && session->mapper->IsInBounds(vaddress, map_size)) { + handle_description->d_address = session->mapper->Map(vaddress, map_size); + handle_description->in_heap = true; + } else { + size_t aligned_up = Common::AlignUp(map_size, BIG_PAGE_SIZE); + while ((address = smmu.Allocate(aligned_up)) == 0) { + // Free handles until the allocation succeeds + std::scoped_lock queueLock(unmap_queue_lock); + if (auto freeHandleDesc{unmap_queue.front()}) { + // Handles in the unmap queue are guaranteed not to be pinned so don't bother + // checking if they are before unmapping + std::scoped_lock freeLock(freeHandleDesc->mutex); + if (handle_description->d_address) + UnmapHandle(*freeHandleDesc); + } else { + LOG_CRITICAL(Service_NVDRV, "Ran out of SMMU address space!"); + } } + + handle_description->d_address = address; + smmu.Map(address, vaddress, map_size, session->asid, true); + handle_description->in_heap = false; } + } - smmu_memory_manager.Map(static_cast<GPUVAddr>(address), handle_description->address, - handle_description->aligned_size); - handle_description->pin_virt_address = address; + if (low_area_pin) { + map_low_area(); } handle_description->pins++; - return handle_description->pin_virt_address; + if (low_area_pin) { + return static_cast<DAddr>(handle_description->pin_virt_address); + } + return handle_description->d_address; } void NvMap::UnpinHandle(Handle::Id handle) { @@ -232,7 +288,7 @@ std::optional<NvMap::FreeInfo> NvMap::FreeHandle(Handle::Id handle, bool interna LOG_WARNING(Service_NVDRV, "User duplicate count imbalance detected!"); } else if (handle_description->dupes == 0) { // Force unmap the handle - if (handle_description->pin_virt_address) { + if (handle_description->d_address) { std::scoped_lock queueLock(unmap_queue_lock); UnmapHandle(*handle_description); } diff --git a/src/core/hle/service/nvdrv/core/nvmap.h b/src/core/hle/service/nvdrv/core/nvmap.h index a8e573890..d7f695845 100644 --- a/src/core/hle/service/nvdrv/core/nvmap.h +++ b/src/core/hle/service/nvdrv/core/nvmap.h @@ -14,6 +14,7 @@ #include "common/bit_field.h" #include "common/common_types.h" +#include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/nvdata.h" namespace Tegra { @@ -25,6 +26,8 @@ class Host1x; } // namespace Tegra namespace Service::Nvidia::NvCore { + +class Container; /** * @brief The nvmap core class holds the global state for nvmap and provides methods to manage * handles @@ -48,7 +51,7 @@ public: using Id = u32; Id id; //!< A globally unique identifier for this handle - s32 pins{}; + s64 pins{}; u32 pin_virt_address{}; std::optional<typename std::list<std::shared_ptr<Handle>>::iterator> unmap_queue_entry{}; @@ -61,15 +64,18 @@ public: } flags{}; static_assert(sizeof(Flags) == sizeof(u32)); - u64 address{}; //!< The memory location in the guest's AS that this handle corresponds to, - //!< this can also be in the nvdrv tmem + VAddr address{}; //!< The memory location in the guest's AS that this handle corresponds to, + //!< this can also be in the nvdrv tmem bool is_shared_mem_mapped{}; //!< If this nvmap has been mapped with the MapSharedMem IPC //!< call u8 kind{}; //!< Used for memory compression bool allocated{}; //!< If the handle has been allocated with `Alloc` + bool in_heap{}; + NvCore::SessionId session_id{}; - u64 dma_map_addr{}; //! remove me after implementing pinning. + DAddr d_address{}; //!< The memory location in the device's AS that this handle corresponds + //!< to, this can also be in the nvdrv tmem Handle(u64 size, Id id); @@ -77,7 +83,8 @@ public: * @brief Sets up the handle with the given memory config, can allocate memory from the tmem * if a 0 address is passed */ - [[nodiscard]] NvResult Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress); + [[nodiscard]] NvResult Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress, + NvCore::SessionId pSessionId); /** * @brief Increases the dupe counter of the handle for the given session @@ -108,7 +115,7 @@ public: bool can_unlock; //!< If the address region is ready to be unlocked }; - explicit NvMap(Tegra::Host1x::Host1x& host1x); + explicit NvMap(Container& core, Tegra::Host1x::Host1x& host1x); /** * @brief Creates an unallocated handle of the given size @@ -117,7 +124,7 @@ public: std::shared_ptr<Handle> GetHandle(Handle::Id handle); - VAddr GetHandleAddress(Handle::Id handle); + DAddr GetHandleAddress(Handle::Id handle); /** * @brief Maps a handle into the SMMU address space @@ -125,7 +132,7 @@ public: * number of calls to `UnpinHandle` * @return The SMMU virtual address that the handle has been mapped to */ - u32 PinHandle(Handle::Id handle); + DAddr PinHandle(Handle::Id handle, bool low_area_pin); /** * @brief When this has been called an equal number of times to `PinHandle` for the supplied @@ -172,5 +179,7 @@ private: * @return If the handle was removed from the map */ bool TryRemoveHandle(const Handle& handle_description); + + Container& core; }; } // namespace Service::Nvidia::NvCore diff --git a/src/core/hle/service/nvdrv/devices/nvdevice.h b/src/core/hle/service/nvdrv/devices/nvdevice.h index a04538d5d..8adaddc60 100644 --- a/src/core/hle/service/nvdrv/devices/nvdevice.h +++ b/src/core/hle/service/nvdrv/devices/nvdevice.h @@ -7,6 +7,7 @@ #include <vector> #include "common/common_types.h" +#include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/nvdata.h" namespace Core { @@ -62,7 +63,7 @@ public: * Called once a device is opened * @param fd The device fd */ - virtual void OnOpen(DeviceFD fd) = 0; + virtual void OnOpen(NvCore::SessionId session_id, DeviceFD fd) = 0; /** * Called once a device is closed diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp index 05a43d8dc..c1ebbd62d 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp @@ -35,14 +35,14 @@ NvResult nvdisp_disp0::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> in return NvResult::NotImplemented; } -void nvdisp_disp0::OnOpen(DeviceFD fd) {} +void nvdisp_disp0::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {} void nvdisp_disp0::OnClose(DeviceFD fd) {} void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width, u32 height, u32 stride, android::BufferTransformFlags transform, const Common::Rectangle<int>& crop_rect, std::array<Service::Nvidia::NvFence, 4>& fences, u32 num_fences) { - const VAddr addr = nvmap.GetHandleAddress(buffer_handle); + const DAddr addr = nvmap.GetHandleAddress(buffer_handle); LOG_TRACE(Service, "Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}", addr, offset, width, height, stride, format); diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h index daee05fe8..5f13a50a2 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h @@ -32,7 +32,7 @@ public: NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, std::span<u8> inline_output) override; - void OnOpen(DeviceFD fd) override; + void OnOpen(NvCore::SessionId session_id, DeviceFD fd) override; void OnClose(DeviceFD fd) override; /// Performs a screen flip, drawing the buffer pointed to by the handle. diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index 6b3639008..e6646ba04 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp @@ -86,7 +86,7 @@ NvResult nvhost_as_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> i return NvResult::NotImplemented; } -void nvhost_as_gpu::OnOpen(DeviceFD fd) {} +void nvhost_as_gpu::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {} void nvhost_as_gpu::OnClose(DeviceFD fd) {} NvResult nvhost_as_gpu::AllocAsEx(IoctlAllocAsEx& params) { @@ -206,6 +206,8 @@ void nvhost_as_gpu::FreeMappingLocked(u64 offset) { static_cast<u32>(aligned_size >> page_size_bits)); } + nvmap.UnpinHandle(mapping->handle); + // Sparse mappings shouldn't be fully unmapped, just returned to their sparse state // Only FreeSpace can unmap them fully if (mapping->sparse_alloc) { @@ -293,12 +295,12 @@ NvResult nvhost_as_gpu::Remap(std::span<IoctlRemapEntry> entries) { return NvResult::BadValue; } - VAddr cpu_address{static_cast<VAddr>( - handle->address + - (static_cast<u64>(entry.handle_offset_big_pages) << vm.big_page_size_bits))}; + DAddr base = nvmap.PinHandle(entry.handle, false); + DAddr device_address{static_cast<DAddr>( + base + (static_cast<u64>(entry.handle_offset_big_pages) << vm.big_page_size_bits))}; - gmmu->Map(virtual_address, cpu_address, size, static_cast<Tegra::PTEKind>(entry.kind), - use_big_pages); + gmmu->Map(virtual_address, device_address, size, + static_cast<Tegra::PTEKind>(entry.kind), use_big_pages); } } @@ -331,9 +333,9 @@ NvResult nvhost_as_gpu::MapBufferEx(IoctlMapBufferEx& params) { } u64 gpu_address{static_cast<u64>(params.offset + params.buffer_offset)}; - VAddr cpu_address{mapping->ptr + params.buffer_offset}; + VAddr device_address{mapping->ptr + params.buffer_offset}; - gmmu->Map(gpu_address, cpu_address, params.mapping_size, + gmmu->Map(gpu_address, device_address, params.mapping_size, static_cast<Tegra::PTEKind>(params.kind), mapping->big_page); return NvResult::Success; @@ -349,7 +351,8 @@ NvResult nvhost_as_gpu::MapBufferEx(IoctlMapBufferEx& params) { return NvResult::BadValue; } - VAddr cpu_address{static_cast<VAddr>(handle->address + params.buffer_offset)}; + DAddr device_address{ + static_cast<DAddr>(nvmap.PinHandle(params.handle, false) + params.buffer_offset)}; u64 size{params.mapping_size ? params.mapping_size : handle->orig_size}; bool big_page{[&]() { @@ -373,15 +376,14 @@ NvResult nvhost_as_gpu::MapBufferEx(IoctlMapBufferEx& params) { } const bool use_big_pages = alloc->second.big_pages && big_page; - gmmu->Map(params.offset, cpu_address, size, static_cast<Tegra::PTEKind>(params.kind), + gmmu->Map(params.offset, device_address, size, static_cast<Tegra::PTEKind>(params.kind), use_big_pages); - auto mapping{std::make_shared<Mapping>(cpu_address, params.offset, size, true, - use_big_pages, alloc->second.sparse)}; + auto mapping{std::make_shared<Mapping>(params.handle, device_address, params.offset, size, + true, use_big_pages, alloc->second.sparse)}; alloc->second.mappings.push_back(mapping); mapping_map[params.offset] = mapping; } else { - auto& allocator{big_page ? *vm.big_page_allocator : *vm.small_page_allocator}; u32 page_size{big_page ? vm.big_page_size : VM::YUZU_PAGESIZE}; u32 page_size_bits{big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS}; @@ -394,11 +396,11 @@ NvResult nvhost_as_gpu::MapBufferEx(IoctlMapBufferEx& params) { return NvResult::InsufficientMemory; } - gmmu->Map(params.offset, cpu_address, Common::AlignUp(size, page_size), + gmmu->Map(params.offset, device_address, Common::AlignUp(size, page_size), static_cast<Tegra::PTEKind>(params.kind), big_page); - auto mapping{ - std::make_shared<Mapping>(cpu_address, params.offset, size, false, big_page, false)}; + auto mapping{std::make_shared<Mapping>(params.handle, device_address, params.offset, size, + false, big_page, false)}; mapping_map[params.offset] = mapping; } @@ -433,6 +435,8 @@ NvResult nvhost_as_gpu::UnmapBuffer(IoctlUnmapBuffer& params) { gmmu->Unmap(params.offset, mapping->size); } + nvmap.UnpinHandle(mapping->handle); + mapping_map.erase(params.offset); } catch (const std::out_of_range&) { LOG_WARNING(Service_NVDRV, "Couldn't find region to unmap at 0x{:X}", params.offset); diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h index 79a21683d..7d0a99988 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h @@ -55,7 +55,7 @@ public: NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, std::span<u8> inline_output) override; - void OnOpen(DeviceFD fd) override; + void OnOpen(NvCore::SessionId session_id, DeviceFD fd) override; void OnClose(DeviceFD fd) override; Kernel::KEvent* QueryEvent(u32 event_id) override; @@ -159,16 +159,18 @@ private: NvCore::NvMap& nvmap; struct Mapping { - VAddr ptr; + NvCore::NvMap::Handle::Id handle; + DAddr ptr; u64 offset; u64 size; bool fixed; bool big_page; // Only valid if fixed == false bool sparse_alloc; - Mapping(VAddr ptr_, u64 offset_, u64 size_, bool fixed_, bool big_page_, bool sparse_alloc_) - : ptr(ptr_), offset(offset_), size(size_), fixed(fixed_), big_page(big_page_), - sparse_alloc(sparse_alloc_) {} + Mapping(NvCore::NvMap::Handle::Id handle_, DAddr ptr_, u64 offset_, u64 size_, bool fixed_, + bool big_page_, bool sparse_alloc_) + : handle(handle_), ptr(ptr_), offset(offset_), size(size_), fixed(fixed_), + big_page(big_page_), sparse_alloc(sparse_alloc_) {} }; struct Allocation { @@ -212,9 +214,6 @@ private: bool initialised{}; } vm; std::shared_ptr<Tegra::MemoryManager> gmmu; - - // s32 channel{}; - // u32 big_page_size{VM::DEFAULT_BIG_PAGE_SIZE}; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index b8dd34e24..250d01de3 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp @@ -76,7 +76,7 @@ NvResult nvhost_ctrl::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> inp return NvResult::NotImplemented; } -void nvhost_ctrl::OnOpen(DeviceFD fd) {} +void nvhost_ctrl::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {} void nvhost_ctrl::OnClose(DeviceFD fd) {} diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h index 992124b60..403f1a746 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h @@ -32,7 +32,7 @@ public: NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, std::span<u8> inline_output) override; - void OnOpen(DeviceFD fd) override; + void OnOpen(NvCore::SessionId session_id, DeviceFD fd) override; void OnClose(DeviceFD fd) override; Kernel::KEvent* QueryEvent(u32 event_id) override; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp index 3e0c96456..ddd85678b 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp @@ -82,7 +82,7 @@ NvResult nvhost_ctrl_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> return NvResult::NotImplemented; } -void nvhost_ctrl_gpu::OnOpen(DeviceFD fd) {} +void nvhost_ctrl_gpu::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {} void nvhost_ctrl_gpu::OnClose(DeviceFD fd) {} NvResult nvhost_ctrl_gpu::GetCharacteristics1(IoctlCharacteristics& params) { diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h index d170299bd..d2ab05b21 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h @@ -28,7 +28,7 @@ public: NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, std::span<u8> inline_output) override; - void OnOpen(DeviceFD fd) override; + void OnOpen(NvCore::SessionId session_id, DeviceFD fd) override; void OnClose(DeviceFD fd) override; Kernel::KEvent* QueryEvent(u32 event_id) override; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index b0395c2f0..bf12d69a5 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp @@ -120,7 +120,7 @@ NvResult nvhost_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> inpu return NvResult::NotImplemented; } -void nvhost_gpu::OnOpen(DeviceFD fd) {} +void nvhost_gpu::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {} void nvhost_gpu::OnClose(DeviceFD fd) {} NvResult nvhost_gpu::SetNVMAPfd(IoctlSetNvmapFD& params) { diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h index 88fd228ff..e34a978db 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h @@ -47,7 +47,7 @@ public: NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, std::span<u8> inline_output) override; - void OnOpen(DeviceFD fd) override; + void OnOpen(NvCore::SessionId session_id, DeviceFD fd) override; void OnClose(DeviceFD fd) override; Kernel::KEvent* QueryEvent(u32 event_id) override; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp index f43914e1b..2c0ac2a46 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp @@ -35,7 +35,7 @@ NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> in case 0x7: return WrapFixed(this, &nvhost_nvdec::SetSubmitTimeout, input, output); case 0x9: - return WrapFixedVariable(this, &nvhost_nvdec::MapBuffer, input, output); + return WrapFixedVariable(this, &nvhost_nvdec::MapBuffer, input, output, fd); case 0xa: return WrapFixedVariable(this, &nvhost_nvdec::UnmapBuffer, input, output); default: @@ -68,9 +68,10 @@ NvResult nvhost_nvdec::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> in return NvResult::NotImplemented; } -void nvhost_nvdec::OnOpen(DeviceFD fd) { +void nvhost_nvdec::OnOpen(NvCore::SessionId session_id, DeviceFD fd) { LOG_INFO(Service_NVDRV, "NVDEC video stream started"); system.SetNVDECActive(true); + sessions[fd] = session_id; } void nvhost_nvdec::OnClose(DeviceFD fd) { @@ -81,6 +82,10 @@ void nvhost_nvdec::OnClose(DeviceFD fd) { system.GPU().ClearCdmaInstance(iter->second); } system.SetNVDECActive(false); + auto it = sessions.find(fd); + if (it != sessions.end()) { + sessions.erase(it); + } } } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h index ad2233c49..627686757 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h @@ -20,7 +20,7 @@ public: NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, std::span<u8> inline_output) override; - void OnOpen(DeviceFD fd) override; + void OnOpen(NvCore::SessionId session_id, DeviceFD fd) override; void OnClose(DeviceFD fd) override; }; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp index 74c701b95..a0a7bfa40 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp @@ -8,6 +8,7 @@ #include "common/common_types.h" #include "common/logging/log.h" #include "core/core.h" +#include "core/hle/kernel/k_process.h" #include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/core/nvmap.h" #include "core/hle/service/nvdrv/core/syncpoint_manager.h" @@ -95,6 +96,8 @@ NvResult nvhost_nvdec_common::Submit(IoctlSubmit& params, std::span<u8> data, De offset += SliceVectors(data, fence_thresholds, params.fence_count, offset); auto& gpu = system.GPU(); + auto* session = core.GetSession(sessions[fd]); + if (gpu.UseNvdec()) { for (std::size_t i = 0; i < syncpt_increments.size(); i++) { const SyncptIncr& syncpt_incr = syncpt_increments[i]; @@ -106,8 +109,8 @@ NvResult nvhost_nvdec_common::Submit(IoctlSubmit& params, std::span<u8> data, De const auto object = nvmap.GetHandle(cmd_buffer.memory_id); ASSERT_OR_EXECUTE(object, return NvResult::InvalidState;); Tegra::ChCommandHeaderList cmdlist(cmd_buffer.word_count); - system.ApplicationMemory().ReadBlock(object->address + cmd_buffer.offset, cmdlist.data(), - cmdlist.size() * sizeof(u32)); + session->process->GetMemory().ReadBlock(object->address + cmd_buffer.offset, cmdlist.data(), + cmdlist.size() * sizeof(u32)); gpu.PushCommandBuffer(core.Host1xDeviceFile().fd_to_id[fd], cmdlist); } // Some games expect command_buffers to be written back @@ -133,10 +136,12 @@ NvResult nvhost_nvdec_common::GetWaitbase(IoctlGetWaitbase& params) { return NvResult::Success; } -NvResult nvhost_nvdec_common::MapBuffer(IoctlMapBuffer& params, std::span<MapBufferEntry> entries) { +NvResult nvhost_nvdec_common::MapBuffer(IoctlMapBuffer& params, std::span<MapBufferEntry> entries, + DeviceFD fd) { const size_t num_entries = std::min(params.num_entries, static_cast<u32>(entries.size())); for (size_t i = 0; i < num_entries; i++) { - entries[i].map_address = nvmap.PinHandle(entries[i].map_handle); + DAddr pin_address = nvmap.PinHandle(entries[i].map_handle, true); + entries[i].map_address = static_cast<u32>(pin_address); } return NvResult::Success; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h index 7ce748e18..900db81d2 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h @@ -4,7 +4,9 @@ #pragma once #include <deque> +#include <unordered_map> #include <vector> + #include "common/common_types.h" #include "common/swap.h" #include "core/hle/service/nvdrv/core/syncpoint_manager.h" @@ -111,7 +113,7 @@ protected: NvResult Submit(IoctlSubmit& params, std::span<u8> input, DeviceFD fd); NvResult GetSyncpoint(IoctlGetSyncpoint& params); NvResult GetWaitbase(IoctlGetWaitbase& params); - NvResult MapBuffer(IoctlMapBuffer& params, std::span<MapBufferEntry> entries); + NvResult MapBuffer(IoctlMapBuffer& params, std::span<MapBufferEntry> entries, DeviceFD fd); NvResult UnmapBuffer(IoctlMapBuffer& params, std::span<MapBufferEntry> entries); NvResult SetSubmitTimeout(u32 timeout); @@ -125,6 +127,7 @@ protected: NvCore::NvMap& nvmap; NvCore::ChannelType channel_type; std::array<u32, MaxSyncPoints> device_syncpoints{}; + std::unordered_map<DeviceFD, NvCore::SessionId> sessions; }; }; // namespace Devices } // namespace Service::Nvidia diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp index 9e6b86458..f87d53f12 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp @@ -44,7 +44,7 @@ NvResult nvhost_nvjpg::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> in return NvResult::NotImplemented; } -void nvhost_nvjpg::OnOpen(DeviceFD fd) {} +void nvhost_nvjpg::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {} void nvhost_nvjpg::OnClose(DeviceFD fd) {} NvResult nvhost_nvjpg::SetNVMAPfd(IoctlSetNvmapFD& params) { diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h index 790c97f6a..def9c254d 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h @@ -22,7 +22,7 @@ public: NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, std::span<u8> inline_output) override; - void OnOpen(DeviceFD fd) override; + void OnOpen(NvCore::SessionId session_id, DeviceFD fd) override; void OnClose(DeviceFD fd) override; private: diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp index 87f8d7c22..bf090f5eb 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp @@ -33,7 +33,7 @@ NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inpu case 0x3: return WrapFixed(this, &nvhost_vic::GetWaitbase, input, output); case 0x9: - return WrapFixedVariable(this, &nvhost_vic::MapBuffer, input, output); + return WrapFixedVariable(this, &nvhost_vic::MapBuffer, input, output, fd); case 0xa: return WrapFixedVariable(this, &nvhost_vic::UnmapBuffer, input, output); default: @@ -68,7 +68,9 @@ NvResult nvhost_vic::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> inpu return NvResult::NotImplemented; } -void nvhost_vic::OnOpen(DeviceFD fd) {} +void nvhost_vic::OnOpen(NvCore::SessionId session_id, DeviceFD fd) { + sessions[fd] = session_id; +} void nvhost_vic::OnClose(DeviceFD fd) { auto& host1x_file = core.Host1xDeviceFile(); @@ -76,6 +78,7 @@ void nvhost_vic::OnClose(DeviceFD fd) { if (iter != host1x_file.fd_to_id.end()) { system.GPU().ClearCdmaInstance(iter->second); } + sessions.erase(fd); } } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h index cadbcb0a5..0cc04354a 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h @@ -19,7 +19,7 @@ public: NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, std::span<u8> inline_output) override; - void OnOpen(DeviceFD fd) override; + void OnOpen(NvCore::SessionId session_id, DeviceFD fd) override; void OnClose(DeviceFD fd) override; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp index 71b2e62ec..da61a3bfe 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.cpp +++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp @@ -36,9 +36,9 @@ NvResult nvmap::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, case 0x3: return WrapFixed(this, &nvmap::IocFromId, input, output); case 0x4: - return WrapFixed(this, &nvmap::IocAlloc, input, output); + return WrapFixed(this, &nvmap::IocAlloc, input, output, fd); case 0x5: - return WrapFixed(this, &nvmap::IocFree, input, output); + return WrapFixed(this, &nvmap::IocFree, input, output, fd); case 0x9: return WrapFixed(this, &nvmap::IocParam, input, output); case 0xe: @@ -67,8 +67,15 @@ NvResult nvmap::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, st return NvResult::NotImplemented; } -void nvmap::OnOpen(DeviceFD fd) {} -void nvmap::OnClose(DeviceFD fd) {} +void nvmap::OnOpen(NvCore::SessionId session_id, DeviceFD fd) { + sessions[fd] = session_id; +} +void nvmap::OnClose(DeviceFD fd) { + auto it = sessions.find(fd); + if (it != sessions.end()) { + sessions.erase(it); + } +} NvResult nvmap::IocCreate(IocCreateParams& params) { LOG_DEBUG(Service_NVDRV, "called, size=0x{:08X}", params.size); @@ -87,7 +94,7 @@ NvResult nvmap::IocCreate(IocCreateParams& params) { return NvResult::Success; } -NvResult nvmap::IocAlloc(IocAllocParams& params) { +NvResult nvmap::IocAlloc(IocAllocParams& params, DeviceFD fd) { LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.address); if (!params.handle) { @@ -116,15 +123,15 @@ NvResult nvmap::IocAlloc(IocAllocParams& params) { return NvResult::InsufficientMemory; } - const auto result = - handle_description->Alloc(params.flags, params.align, params.kind, params.address); + const auto result = handle_description->Alloc(params.flags, params.align, params.kind, + params.address, sessions[fd]); if (result != NvResult::Success) { LOG_CRITICAL(Service_NVDRV, "Object failed to allocate, handle={:08X}", params.handle); return result; } bool is_out_io{}; - ASSERT(system.ApplicationProcess() - ->GetPageTable() + auto process = container.GetSession(sessions[fd])->process; + ASSERT(process->GetPageTable() .LockForMapDeviceAddressSpace(&is_out_io, handle_description->address, handle_description->size, Kernel::KMemoryPermission::None, true, false) @@ -224,7 +231,7 @@ NvResult nvmap::IocParam(IocParamParams& params) { return NvResult::Success; } -NvResult nvmap::IocFree(IocFreeParams& params) { +NvResult nvmap::IocFree(IocFreeParams& params, DeviceFD fd) { LOG_DEBUG(Service_NVDRV, "called"); if (!params.handle) { @@ -233,9 +240,9 @@ NvResult nvmap::IocFree(IocFreeParams& params) { } if (auto freeInfo{file.FreeHandle(params.handle, false)}) { + auto process = container.GetSession(sessions[fd])->process; if (freeInfo->can_unlock) { - ASSERT(system.ApplicationProcess() - ->GetPageTable() + ASSERT(process->GetPageTable() .UnlockForDeviceAddressSpace(freeInfo->address, freeInfo->size) .IsSuccess()); } diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h index 049c11028..d07d85f88 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.h +++ b/src/core/hle/service/nvdrv/devices/nvmap.h @@ -33,7 +33,7 @@ public: NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output, std::span<u8> inline_output) override; - void OnOpen(DeviceFD fd) override; + void OnOpen(NvCore::SessionId session_id, DeviceFD fd) override; void OnClose(DeviceFD fd) override; enum class HandleParameterType : u32_le { @@ -100,11 +100,11 @@ public: static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size"); NvResult IocCreate(IocCreateParams& params); - NvResult IocAlloc(IocAllocParams& params); + NvResult IocAlloc(IocAllocParams& params, DeviceFD fd); NvResult IocGetId(IocGetIdParams& params); NvResult IocFromId(IocFromIdParams& params); NvResult IocParam(IocParamParams& params); - NvResult IocFree(IocFreeParams& params); + NvResult IocFree(IocFreeParams& params, DeviceFD fd); private: /// Id to use for the next handle that is created. @@ -115,6 +115,7 @@ private: NvCore::Container& container; NvCore::NvMap& file; + std::unordered_map<DeviceFD, NvCore::SessionId> sessions; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp index 9e46ee8dd..cb256e5b4 100644 --- a/src/core/hle/service/nvdrv/nvdrv.cpp +++ b/src/core/hle/service/nvdrv/nvdrv.cpp @@ -45,13 +45,22 @@ void EventInterface::FreeEvent(Kernel::KEvent* event) { void LoopProcess(Nvnflinger::Nvnflinger& nvnflinger, Core::System& system) { auto server_manager = std::make_unique<ServerManager>(system); auto module = std::make_shared<Module>(system); - server_manager->RegisterNamedService("nvdrv", std::make_shared<NVDRV>(system, module, "nvdrv")); - server_manager->RegisterNamedService("nvdrv:a", - std::make_shared<NVDRV>(system, module, "nvdrv:a")); - server_manager->RegisterNamedService("nvdrv:s", - std::make_shared<NVDRV>(system, module, "nvdrv:s")); - server_manager->RegisterNamedService("nvdrv:t", - std::make_shared<NVDRV>(system, module, "nvdrv:t")); + const auto NvdrvInterfaceFactoryForApplication = [&, module] { + return std::make_shared<NVDRV>(system, module, "nvdrv"); + }; + const auto NvdrvInterfaceFactoryForApplets = [&, module] { + return std::make_shared<NVDRV>(system, module, "nvdrv:a"); + }; + const auto NvdrvInterfaceFactoryForSysmodules = [&, module] { + return std::make_shared<NVDRV>(system, module, "nvdrv:s"); + }; + const auto NvdrvInterfaceFactoryForTesting = [&, module] { + return std::make_shared<NVDRV>(system, module, "nvdrv:t"); + }; + server_manager->RegisterNamedService("nvdrv", NvdrvInterfaceFactoryForApplication); + server_manager->RegisterNamedService("nvdrv:a", NvdrvInterfaceFactoryForApplets); + server_manager->RegisterNamedService("nvdrv:s", NvdrvInterfaceFactoryForSysmodules); + server_manager->RegisterNamedService("nvdrv:t", NvdrvInterfaceFactoryForTesting); server_manager->RegisterNamedService("nvmemp", std::make_shared<NVMEMP>(system)); nvnflinger.SetNVDrvInstance(module); ServerManager::RunServer(std::move(server_manager)); @@ -113,7 +122,7 @@ NvResult Module::VerifyFD(DeviceFD fd) const { return NvResult::Success; } -DeviceFD Module::Open(const std::string& device_name) { +DeviceFD Module::Open(const std::string& device_name, NvCore::SessionId session_id) { auto it = builders.find(device_name); if (it == builders.end()) { LOG_ERROR(Service_NVDRV, "Trying to open unknown device {}", device_name); @@ -124,7 +133,7 @@ DeviceFD Module::Open(const std::string& device_name) { auto& builder = it->second; auto device = builder(fd)->second; - device->OnOpen(fd); + device->OnOpen(session_id, fd); return fd; } diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h index d8622b3ca..c594f0e5e 100644 --- a/src/core/hle/service/nvdrv/nvdrv.h +++ b/src/core/hle/service/nvdrv/nvdrv.h @@ -77,7 +77,7 @@ public: NvResult VerifyFD(DeviceFD fd) const; /// Opens a device node and returns a file descriptor to it. - DeviceFD Open(const std::string& device_name); + DeviceFD Open(const std::string& device_name, NvCore::SessionId session_id); /// Sends an ioctl command to the specified file descriptor. NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output); @@ -93,6 +93,10 @@ public: NvResult QueryEvent(DeviceFD fd, u32 event_id, Kernel::KEvent*& event); + NvCore::Container& GetContainer() { + return container; + } + private: friend class EventInterface; friend class Service::Nvnflinger::Nvnflinger; diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.cpp b/src/core/hle/service/nvdrv/nvdrv_interface.cpp index c8a880e84..ffe72f281 100644 --- a/src/core/hle/service/nvdrv/nvdrv_interface.cpp +++ b/src/core/hle/service/nvdrv/nvdrv_interface.cpp @@ -3,8 +3,11 @@ // SPDX-License-Identifier: GPL-3.0-or-later #include "common/logging/log.h" +#include "common/scope_exit.h" +#include "common/string_util.h" #include "core/core.h" #include "core/hle/kernel/k_event.h" +#include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_readable_event.h" #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/nvdrv/nvdata.h" @@ -27,7 +30,7 @@ void NVDRV::Open(HLERequestContext& ctx) { } const auto& buffer = ctx.ReadBuffer(); - const std::string device_name(buffer.begin(), buffer.end()); + const std::string device_name(Common::StringFromBuffer(buffer)); if (device_name == "/dev/nvhost-prof-gpu") { rb.Push<DeviceFD>(0); @@ -37,7 +40,7 @@ void NVDRV::Open(HLERequestContext& ctx) { return; } - DeviceFD fd = nvdrv->Open(device_name); + DeviceFD fd = nvdrv->Open(device_name, session_id); rb.Push<DeviceFD>(fd); rb.PushEnum(fd != INVALID_NVDRV_FD ? NvResult::Success : NvResult::FileOperationFailed); @@ -150,12 +153,29 @@ void NVDRV::Close(HLERequestContext& ctx) { void NVDRV::Initialize(HLERequestContext& ctx) { LOG_WARNING(Service_NVDRV, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; + SCOPE_EXIT({ + rb.Push(ResultSuccess); + rb.PushEnum(NvResult::Success); + }); - is_initialized = true; + if (is_initialized) { + // No need to initialize again + return; + } - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(NvResult::Success); + IPC::RequestParser rp{ctx}; + const auto process_handle{ctx.GetCopyHandle(0)}; + // The transfer memory is lent to nvdrv as a work buffer since nvdrv is + // unable to allocate as much memory on its own. For HLE it's unnecessary to handle it + [[maybe_unused]] const auto transfer_memory_handle{ctx.GetCopyHandle(1)}; + [[maybe_unused]] const auto transfer_memory_size = rp.Pop<u32>(); + + auto& container = nvdrv->GetContainer(); + auto process = ctx.GetObjectFromHandle<Kernel::KProcess>(process_handle); + session_id = container.OpenSession(process.GetPointerUnsafe()); + + is_initialized = true; } void NVDRV::QueryEvent(HLERequestContext& ctx) { @@ -242,6 +262,9 @@ NVDRV::NVDRV(Core::System& system_, std::shared_ptr<Module> nvdrv_, const char* RegisterHandlers(functions); } -NVDRV::~NVDRV() = default; +NVDRV::~NVDRV() { + auto& container = nvdrv->GetContainer(); + container.CloseSession(session_id); +} } // namespace Service::Nvidia diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.h b/src/core/hle/service/nvdrv/nvdrv_interface.h index 6e98115dc..f2195ae1e 100644 --- a/src/core/hle/service/nvdrv/nvdrv_interface.h +++ b/src/core/hle/service/nvdrv/nvdrv_interface.h @@ -35,6 +35,7 @@ private: u64 pid{}; bool is_initialized{}; + NvCore::SessionId session_id{}; Common::ScratchBuffer<u8> output_buffer; Common::ScratchBuffer<u8> inline_output_buffer; }; diff --git a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp index 2fef6cc1a..86e272b41 100644 --- a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp +++ b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp @@ -87,19 +87,20 @@ Result CreateNvMapHandle(u32* out_nv_map_handle, Nvidia::Devices::nvmap& nvmap, R_SUCCEED(); } -Result FreeNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle) { +Result FreeNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle, Nvidia::DeviceFD nvmap_fd) { // Free the handle. Nvidia::Devices::nvmap::IocFreeParams free_params{ .handle = handle, }; - R_UNLESS(nvmap.IocFree(free_params) == Nvidia::NvResult::Success, VI::ResultOperationFailed); + R_UNLESS(nvmap.IocFree(free_params, nvmap_fd) == Nvidia::NvResult::Success, + VI::ResultOperationFailed); // We succeeded. R_SUCCEED(); } Result AllocNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle, Common::ProcessAddress buffer, - u32 size) { + u32 size, Nvidia::DeviceFD nvmap_fd) { // Assign the allocated memory to the handle. Nvidia::Devices::nvmap::IocAllocParams alloc_params{ .handle = handle, @@ -109,16 +110,16 @@ Result AllocNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle, Common::Proce .kind = 0, .address = GetInteger(buffer), }; - R_UNLESS(nvmap.IocAlloc(alloc_params) == Nvidia::NvResult::Success, VI::ResultOperationFailed); + R_UNLESS(nvmap.IocAlloc(alloc_params, nvmap_fd) == Nvidia::NvResult::Success, + VI::ResultOperationFailed); // We succeeded. R_SUCCEED(); } -Result AllocateHandleForBuffer(u32* out_handle, Nvidia::Module& nvdrv, +Result AllocateHandleForBuffer(u32* out_handle, Nvidia::Module& nvdrv, Nvidia::DeviceFD nvmap_fd, Common::ProcessAddress buffer, u32 size) { // Get the nvmap device. - auto nvmap_fd = nvdrv.Open("/dev/nvmap"); auto nvmap = nvdrv.GetDevice<Nvidia::Devices::nvmap>(nvmap_fd); ASSERT(nvmap != nullptr); @@ -127,11 +128,11 @@ Result AllocateHandleForBuffer(u32* out_handle, Nvidia::Module& nvdrv, // Ensure we maintain a clean state on failure. ON_RESULT_FAILURE { - ASSERT(R_SUCCEEDED(FreeNvMapHandle(*nvmap, *out_handle))); + ASSERT(R_SUCCEEDED(FreeNvMapHandle(*nvmap, *out_handle, nvmap_fd))); }; // Assign the allocated memory to the handle. - R_RETURN(AllocNvMapHandle(*nvmap, *out_handle, buffer, size)); + R_RETURN(AllocNvMapHandle(*nvmap, *out_handle, buffer, size, nvmap_fd)); } constexpr auto SharedBufferBlockLinearFormat = android::PixelFormat::Rgba8888; @@ -197,9 +198,13 @@ Result FbShareBufferManager::Initialize(u64* out_buffer_id, u64* out_layer_id, u std::addressof(m_buffer_page_group), m_system, SharedBufferSize)); + auto& container = m_nvdrv->GetContainer(); + m_session_id = container.OpenSession(m_system.ApplicationProcess()); + m_nvmap_fd = m_nvdrv->Open("/dev/nvmap", m_session_id); + // Create an nvmap handle for the buffer and assign the memory to it. - R_TRY(AllocateHandleForBuffer(std::addressof(m_buffer_nvmap_handle), *m_nvdrv, map_address, - SharedBufferSize)); + R_TRY(AllocateHandleForBuffer(std::addressof(m_buffer_nvmap_handle), *m_nvdrv, m_nvmap_fd, + map_address, SharedBufferSize)); // Record the display id. m_display_id = display_id; diff --git a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.h b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.h index c809c01b4..033bf4bbe 100644 --- a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.h +++ b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.h @@ -4,6 +4,8 @@ #pragma once #include "common/math_util.h" +#include "core/hle/service/nvdrv/core/container.h" +#include "core/hle/service/nvdrv/nvdata.h" #include "core/hle/service/nvnflinger/nvnflinger.h" #include "core/hle/service/nvnflinger/ui/fence.h" @@ -53,7 +55,8 @@ private: u64 m_layer_id = 0; u32 m_buffer_nvmap_handle = 0; SharedMemoryPoolLayout m_pool_layout = {}; - + Nvidia::DeviceFD m_nvmap_fd = {}; + Nvidia::NvCore::SessionId m_session_id = {}; std::unique_ptr<Kernel::KPageGroup> m_buffer_page_group; std::mutex m_guard; diff --git a/src/core/hle/service/nvnflinger/nvnflinger.cpp b/src/core/hle/service/nvnflinger/nvnflinger.cpp index af6591370..71d6fdb0c 100644 --- a/src/core/hle/service/nvnflinger/nvnflinger.cpp +++ b/src/core/hle/service/nvnflinger/nvnflinger.cpp @@ -124,7 +124,7 @@ void Nvnflinger::ShutdownLayers() { void Nvnflinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) { nvdrv = std::move(instance); - disp_fd = nvdrv->Open("/dev/nvdisp_disp0"); + disp_fd = nvdrv->Open("/dev/nvdisp_disp0", {}); } std::optional<u64> Nvnflinger::OpenDisplay(std::string_view name) { diff --git a/src/core/hle/service/nvnflinger/ui/graphic_buffer.cpp b/src/core/hle/service/nvnflinger/ui/graphic_buffer.cpp index ce70946ec..ede2a1193 100644 --- a/src/core/hle/service/nvnflinger/ui/graphic_buffer.cpp +++ b/src/core/hle/service/nvnflinger/ui/graphic_buffer.cpp @@ -22,11 +22,13 @@ GraphicBuffer::GraphicBuffer(Service::Nvidia::NvCore::NvMap& nvmap, : NvGraphicBuffer(GetBuffer(buffer)), m_nvmap(std::addressof(nvmap)) { if (this->BufferId() > 0) { m_nvmap->DuplicateHandle(this->BufferId(), true); + m_nvmap->PinHandle(this->BufferId(), false); } } GraphicBuffer::~GraphicBuffer() { if (m_nvmap != nullptr && this->BufferId() > 0) { + m_nvmap->UnpinHandle(this->BufferId()); m_nvmap->FreeHandle(this->BufferId(), true); } } diff --git a/src/core/hle/service/psc/psc.cpp b/src/core/hle/service/psc/psc.cpp index cd0cc9287..44310756b 100644 --- a/src/core/hle/service/psc/psc.cpp +++ b/src/core/hle/service/psc/psc.cpp @@ -4,9 +4,13 @@ #include <memory> #include "common/logging/log.h" +#include "core/core.h" #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/psc/psc.h" -#include "core/hle/service/server_manager.h" +#include "core/hle/service/psc/time/manager.h" +#include "core/hle/service/psc/time/power_state_service.h" +#include "core/hle/service/psc/time/service_manager.h" +#include "core/hle/service/psc/time/static.h" #include "core/hle/service/service.h" namespace Service::PSC { @@ -76,6 +80,17 @@ void LoopProcess(Core::System& system) { server_manager->RegisterNamedService("psc:c", std::make_shared<IPmControl>(system)); server_manager->RegisterNamedService("psc:m", std::make_shared<IPmService>(system)); + + auto time = std::make_shared<Time::TimeManager>(system); + + server_manager->RegisterNamedService( + "time:m", std::make_shared<Time::ServiceManager>(system, time, server_manager.get())); + server_manager->RegisterNamedService( + "time:su", std::make_shared<Time::StaticService>( + system, Time::StaticServiceSetupInfo{0, 0, 0, 0, 0, 1}, time, "time:su")); + server_manager->RegisterNamedService("time:al", + std::make_shared<Time::IAlarmService>(system, time)); + ServerManager::RunServer(std::move(server_manager)); } diff --git a/src/core/hle/service/psc/time/alarms.cpp b/src/core/hle/service/psc/time/alarms.cpp new file mode 100644 index 000000000..5e52c19f8 --- /dev/null +++ b/src/core/hle/service/psc/time/alarms.cpp @@ -0,0 +1,209 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/service/psc/time/alarms.h" +#include "core/hle/service/psc/time/manager.h" + +namespace Service::PSC::Time { +Alarm::Alarm(Core::System& system, KernelHelpers::ServiceContext& ctx, AlarmType type) + : m_ctx{ctx}, m_event{ctx.CreateEvent("Psc:Alarm:Event")} { + m_event->Clear(); + + switch (type) { + case WakeupAlarm: + m_priority = 1; + break; + case BackgroundTaskAlarm: + m_priority = 0; + break; + default: + UNREACHABLE(); + return; + } +} + +Alarm::~Alarm() { + m_ctx.CloseEvent(m_event); +} + +Alarms::Alarms(Core::System& system, StandardSteadyClockCore& steady_clock, + PowerStateRequestManager& power_state_request_manager) + : m_system{system}, m_ctx{system, "Psc:Alarms"}, m_steady_clock{steady_clock}, + m_power_state_request_manager{power_state_request_manager}, m_event{m_ctx.CreateEvent( + "Psc:Alarms:Event")} {} + +Alarms::~Alarms() { + m_ctx.CloseEvent(m_event); +} + +Result Alarms::Enable(Alarm& alarm, s64 time) { + R_UNLESS(m_steady_clock.IsInitialized(), ResultClockUninitialized); + + std::scoped_lock l{m_mutex}; + R_UNLESS(alarm.IsLinked(), ResultAlarmNotRegistered); + + auto time_ns{time + m_steady_clock.GetRawTime()}; + auto one_second_ns{ + std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()}; + time_ns = Common::AlignUp(time_ns, one_second_ns); + alarm.SetAlertTime(time_ns); + + Insert(alarm); + R_RETURN(UpdateClosestAndSignal()); +} + +void Alarms::Disable(Alarm& alarm) { + std::scoped_lock l{m_mutex}; + if (!alarm.IsLinked()) { + return; + } + + Erase(alarm); + UpdateClosestAndSignal(); +} + +void Alarms::CheckAndSignal() { + std::scoped_lock l{m_mutex}; + if (m_alarms.empty()) { + return; + } + + bool alarm_signalled{false}; + for (auto& alarm : m_alarms) { + if (m_steady_clock.GetRawTime() >= alarm.GetAlertTime()) { + alarm.Signal(); + alarm.Lock(); + Erase(alarm); + + m_power_state_request_manager.UpdatePendingPowerStateRequestPriority( + alarm.GetPriority()); + alarm_signalled = true; + } + } + + if (!alarm_signalled) { + return; + } + + m_power_state_request_manager.SignalPowerStateRequestAvailability(); + UpdateClosestAndSignal(); +} + +bool Alarms::GetClosestAlarm(Alarm** out_alarm) { + std::scoped_lock l{m_mutex}; + auto alarm = m_alarms.empty() ? nullptr : std::addressof(m_alarms.front()); + *out_alarm = alarm; + return alarm != nullptr; +} + +void Alarms::Insert(Alarm& alarm) { + // Alarms are sorted by alert time, then priority + auto it{m_alarms.begin()}; + while (it != m_alarms.end()) { + if (alarm.GetAlertTime() < it->GetAlertTime() || + (alarm.GetAlertTime() == it->GetAlertTime() && + alarm.GetPriority() < it->GetPriority())) { + m_alarms.insert(it, alarm); + return; + } + it++; + } + + m_alarms.push_back(alarm); +} + +void Alarms::Erase(Alarm& alarm) { + m_alarms.erase(m_alarms.iterator_to(alarm)); +} + +Result Alarms::UpdateClosestAndSignal() { + m_closest_alarm = m_alarms.empty() ? nullptr : std::addressof(m_alarms.front()); + R_SUCCEED_IF(m_closest_alarm == nullptr); + + m_event->Signal(); + + R_SUCCEED(); +} + +IAlarmService::IAlarmService(Core::System& system_, std::shared_ptr<TimeManager> manager) + : ServiceFramework{system_, "time:al"}, m_system{system}, m_alarms{manager->m_alarms} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &IAlarmService::CreateWakeupAlarm, "CreateWakeupAlarm"}, + {1, &IAlarmService::CreateBackgroundTaskAlarm, "CreateBackgroundTaskAlarm"}, + }; + // clang-format on + RegisterHandlers(functions); +} + +void IAlarmService::CreateWakeupAlarm(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<ISteadyClockAlarm>(system, m_alarms, AlarmType::WakeupAlarm); +} + +void IAlarmService::CreateBackgroundTaskAlarm(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<ISteadyClockAlarm>(system, m_alarms, AlarmType::BackgroundTaskAlarm); +} + +ISteadyClockAlarm::ISteadyClockAlarm(Core::System& system_, Alarms& alarms, AlarmType type) + : ServiceFramework{system_, "ISteadyClockAlarm"}, m_ctx{system, "Psc:ISteadyClockAlarm"}, + m_alarms{alarms}, m_alarm{system, m_ctx, type} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &ISteadyClockAlarm::GetAlarmEvent, "GetAlarmEvent"}, + {1, &ISteadyClockAlarm::Enable, "Enable"}, + {2, &ISteadyClockAlarm::Disable, "Disable"}, + {3, &ISteadyClockAlarm::IsEnabled, "IsEnabled"}, + {10, nullptr, "CreateWakeLock"}, + {11, nullptr, "DestroyWakeLock"}, + }; + // clang-format on + RegisterHandlers(functions); +} + +void ISteadyClockAlarm::GetAlarmEvent(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(m_alarm.GetEventHandle()); +} + +void ISteadyClockAlarm::Enable(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto time{rp.Pop<s64>()}; + + auto res = m_alarms.Enable(m_alarm, time); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void ISteadyClockAlarm::Disable(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + m_alarms.Disable(m_alarm); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISteadyClockAlarm::IsEnabled(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push<bool>(m_alarm.IsLinked()); +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/alarms.h b/src/core/hle/service/psc/time/alarms.h new file mode 100644 index 000000000..597770028 --- /dev/null +++ b/src/core/hle/service/psc/time/alarms.h @@ -0,0 +1,139 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <mutex> + +#include "core/hle/kernel/k_event.h" +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/psc/time/clocks/standard_steady_clock_core.h" +#include "core/hle/service/psc/time/common.h" +#include "core/hle/service/psc/time/power_state_request_manager.h" +#include "core/hle/service/server_manager.h" +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Service::PSC::Time { +class TimeManager; + +enum AlarmType : u32 { + WakeupAlarm = 0, + BackgroundTaskAlarm = 1, +}; + +struct Alarm : public Common::IntrusiveListBaseNode<Alarm> { + using AlarmList = Common::IntrusiveListBaseTraits<Alarm>::ListType; + + Alarm(Core::System& system, KernelHelpers::ServiceContext& ctx, AlarmType type); + ~Alarm(); + + Kernel::KReadableEvent& GetEventHandle() { + return m_event->GetReadableEvent(); + } + + s64 GetAlertTime() const { + return m_alert_time; + } + + void SetAlertTime(s64 time) { + m_alert_time = time; + } + + u32 GetPriority() const { + return m_priority; + } + + void Signal() { + m_event->Signal(); + } + + Result Lock() { + // TODO + // if (m_lock_service) { + // return m_lock_service->Lock(); + // } + R_SUCCEED(); + } + + KernelHelpers::ServiceContext& m_ctx; + + u32 m_priority; + Kernel::KEvent* m_event{}; + s64 m_alert_time{}; + // TODO + // nn::psc::sf::IPmStateLock* m_lock_service{}; +}; + +class Alarms { +public: + explicit Alarms(Core::System& system, StandardSteadyClockCore& steady_clock, + PowerStateRequestManager& power_state_request_manager); + ~Alarms(); + + Kernel::KEvent& GetEvent() { + return *m_event; + } + + s64 GetRawTime() { + return m_steady_clock.GetRawTime(); + } + + Result Enable(Alarm& alarm, s64 time); + void Disable(Alarm& alarm); + void CheckAndSignal(); + bool GetClosestAlarm(Alarm** out_alarm); + +private: + void Insert(Alarm& alarm); + void Erase(Alarm& alarm); + Result UpdateClosestAndSignal(); + + Core::System& m_system; + KernelHelpers::ServiceContext m_ctx; + + StandardSteadyClockCore& m_steady_clock; + PowerStateRequestManager& m_power_state_request_manager; + Alarm::AlarmList m_alarms; + Kernel::KEvent* m_event{}; + Alarm* m_closest_alarm{}; + std::mutex m_mutex; +}; + +class IAlarmService final : public ServiceFramework<IAlarmService> { +public: + explicit IAlarmService(Core::System& system, std::shared_ptr<TimeManager> manager); + + ~IAlarmService() override = default; + +private: + void CreateWakeupAlarm(HLERequestContext& ctx); + void CreateBackgroundTaskAlarm(HLERequestContext& ctx); + + Core::System& m_system; + Alarms& m_alarms; +}; + +class ISteadyClockAlarm final : public ServiceFramework<ISteadyClockAlarm> { +public: + explicit ISteadyClockAlarm(Core::System& system, Alarms& alarms, AlarmType type); + + ~ISteadyClockAlarm() override = default; + +private: + void GetAlarmEvent(HLERequestContext& ctx); + void Enable(HLERequestContext& ctx); + void Disable(HLERequestContext& ctx); + void IsEnabled(HLERequestContext& ctx); + + KernelHelpers::ServiceContext m_ctx; + + Alarms& m_alarms; + Alarm m_alarm; +}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/context_writers.cpp b/src/core/hle/service/psc/time/clocks/context_writers.cpp new file mode 100644 index 000000000..ac8700f76 --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/context_writers.cpp @@ -0,0 +1,83 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/service/psc/time/clocks/context_writers.h" + +namespace Service::PSC::Time { + +void ContextWriter::SignalAllNodes() { + std::scoped_lock l{m_mutex}; + for (auto& operation : m_operation_events) { + operation.m_event->Signal(); + } +} + +void ContextWriter::Link(OperationEvent& operation_event) { + std::scoped_lock l{m_mutex}; + m_operation_events.push_back(operation_event); +} + +LocalSystemClockContextWriter::LocalSystemClockContextWriter(Core::System& system, + SharedMemory& shared_memory) + : m_system{system}, m_shared_memory{shared_memory} {} + +Result LocalSystemClockContextWriter::Write(SystemClockContext& context) { + if (m_in_use) { + R_SUCCEED_IF(context == m_context); + m_context = context; + } else { + m_context = context; + m_in_use = true; + } + + m_shared_memory.SetLocalSystemContext(context); + + SignalAllNodes(); + + R_SUCCEED(); +} + +NetworkSystemClockContextWriter::NetworkSystemClockContextWriter(Core::System& system, + SharedMemory& shared_memory, + SystemClockCore& system_clock) + : m_system{system}, m_shared_memory{shared_memory}, m_system_clock{system_clock} {} + +Result NetworkSystemClockContextWriter::Write(SystemClockContext& context) { + s64 time{}; + [[maybe_unused]] auto res = m_system_clock.GetCurrentTime(&time); + + if (m_in_use) { + R_SUCCEED_IF(context == m_context); + m_context = context; + } else { + m_context = context; + m_in_use = true; + } + + m_shared_memory.SetNetworkSystemContext(context); + + SignalAllNodes(); + + R_SUCCEED(); +} + +EphemeralNetworkSystemClockContextWriter::EphemeralNetworkSystemClockContextWriter( + Core::System& system) + : m_system{system} {} + +Result EphemeralNetworkSystemClockContextWriter::Write(SystemClockContext& context) { + if (m_in_use) { + R_SUCCEED_IF(context == m_context); + m_context = context; + } else { + m_context = context; + m_in_use = true; + } + + SignalAllNodes(); + + R_SUCCEED(); +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/context_writers.h b/src/core/hle/service/psc/time/clocks/context_writers.h new file mode 100644 index 000000000..afd3725d4 --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/context_writers.h @@ -0,0 +1,79 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <list> + +#include "common/common_types.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/service/psc/time/clocks/system_clock_core.h" +#include "core/hle/service/psc/time/common.h" +#include "core/hle/service/psc/time/shared_memory.h" + +namespace Core { +class System; +} + +namespace Service::PSC::Time { + +class ContextWriter { +private: + using OperationEventList = Common::IntrusiveListBaseTraits<OperationEvent>::ListType; + +public: + virtual ~ContextWriter() = default; + + virtual Result Write(SystemClockContext& context) = 0; + void SignalAllNodes(); + void Link(OperationEvent& operation_event); + +private: + OperationEventList m_operation_events; + std::mutex m_mutex; +}; + +class LocalSystemClockContextWriter : public ContextWriter { +public: + explicit LocalSystemClockContextWriter(Core::System& system, SharedMemory& shared_memory); + + Result Write(SystemClockContext& context) override; + +private: + Core::System& m_system; + + SharedMemory& m_shared_memory; + bool m_in_use{}; + SystemClockContext m_context{}; +}; + +class NetworkSystemClockContextWriter : public ContextWriter { +public: + explicit NetworkSystemClockContextWriter(Core::System& system, SharedMemory& shared_memory, + SystemClockCore& system_clock); + + Result Write(SystemClockContext& context) override; + +private: + Core::System& m_system; + + SharedMemory& m_shared_memory; + bool m_in_use{}; + SystemClockContext m_context{}; + SystemClockCore& m_system_clock; +}; + +class EphemeralNetworkSystemClockContextWriter : public ContextWriter { +public: + EphemeralNetworkSystemClockContextWriter(Core::System& system); + + Result Write(SystemClockContext& context) override; + +private: + Core::System& m_system; + + bool m_in_use{}; + SystemClockContext m_context{}; +}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/ephemeral_network_system_clock_core.h b/src/core/hle/service/psc/time/clocks/ephemeral_network_system_clock_core.h new file mode 100644 index 000000000..0a68867d9 --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/ephemeral_network_system_clock_core.h @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/result.h" +#include "core/hle/service/psc/time/clocks/context_writers.h" +#include "core/hle/service/psc/time/clocks/steady_clock_core.h" +#include "core/hle/service/psc/time/clocks/system_clock_core.h" +#include "core/hle/service/psc/time/common.h" + +namespace Service::PSC::Time { + +class EphemeralNetworkSystemClockCore : public SystemClockCore { +public: + explicit EphemeralNetworkSystemClockCore(SteadyClockCore& steady_clock) + : SystemClockCore{steady_clock} {} + ~EphemeralNetworkSystemClockCore() override = default; +}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.cpp b/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.cpp new file mode 100644 index 000000000..36dca6689 --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.cpp @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/psc/time/clocks/standard_local_system_clock_core.h" + +namespace Service::PSC::Time { + +void StandardLocalSystemClockCore::Initialize(SystemClockContext& context, s64 time) { + SteadyClockTimePoint time_point{}; + if (GetCurrentTimePoint(time_point) == ResultSuccess && + context.steady_time_point.IdMatches(time_point)) { + SetContextAndWrite(context); + } else if (SetCurrentTime(time) != ResultSuccess) { + LOG_ERROR(Service_Time, "Failed to SetCurrentTime"); + } + + SetInitialized(); +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.h b/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.h new file mode 100644 index 000000000..176ba3e94 --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.h @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/result.h" +#include "core/hle/service/psc/time/clocks/context_writers.h" +#include "core/hle/service/psc/time/clocks/steady_clock_core.h" +#include "core/hle/service/psc/time/clocks/system_clock_core.h" +#include "core/hle/service/psc/time/common.h" + +namespace Service::PSC::Time { + +class StandardLocalSystemClockCore : public SystemClockCore { +public: + explicit StandardLocalSystemClockCore(SteadyClockCore& steady_clock) + : SystemClockCore{steady_clock} {} + ~StandardLocalSystemClockCore() override = default; + + void Initialize(SystemClockContext& context, s64 time); +}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.cpp b/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.cpp new file mode 100644 index 000000000..8d6cb7db1 --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.cpp @@ -0,0 +1,42 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/psc/time/clocks/standard_network_system_clock_core.h" + +namespace Service::PSC::Time { + +void StandardNetworkSystemClockCore::Initialize(SystemClockContext& context, s64 accuracy) { + if (SetContextAndWrite(context) != ResultSuccess) { + LOG_ERROR(Service_Time, "Failed to SetContext"); + } + m_sufficient_accuracy = accuracy; + SetInitialized(); +} + +bool StandardNetworkSystemClockCore::IsAccuracySufficient() { + if (!IsInitialized()) { + return false; + } + + SystemClockContext context{}; + SteadyClockTimePoint current_time_point{}; + if (GetCurrentTimePoint(current_time_point) != ResultSuccess || + GetContext(context) != ResultSuccess) { + return false; + } + + s64 seconds{}; + if (GetSpanBetweenTimePoints(&seconds, context.steady_time_point, current_time_point) != + ResultSuccess) { + return false; + } + + if (std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(seconds)) + .count() < m_sufficient_accuracy) { + return true; + } + + return false; +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.h b/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.h new file mode 100644 index 000000000..933d2c8e3 --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.h @@ -0,0 +1,30 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <chrono> + +#include "core/hle/result.h" +#include "core/hle/service/psc/time/clocks/context_writers.h" +#include "core/hle/service/psc/time/clocks/steady_clock_core.h" +#include "core/hle/service/psc/time/clocks/system_clock_core.h" +#include "core/hle/service/psc/time/common.h" + +namespace Service::PSC::Time { + +class StandardNetworkSystemClockCore : public SystemClockCore { +public: + explicit StandardNetworkSystemClockCore(SteadyClockCore& steady_clock) + : SystemClockCore{steady_clock} {} + ~StandardNetworkSystemClockCore() override = default; + + void Initialize(SystemClockContext& context, s64 accuracy); + bool IsAccuracySufficient(); + +private: + s64 m_sufficient_accuracy{ + std::chrono ::duration_cast<std::chrono::nanoseconds>(std::chrono::days(10)).count()}; +}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/standard_steady_clock_core.cpp b/src/core/hle/service/psc/time/clocks/standard_steady_clock_core.cpp new file mode 100644 index 000000000..7a72d7aa2 --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/standard_steady_clock_core.cpp @@ -0,0 +1,101 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <chrono> + +#include "core/core.h" +#include "core/core_timing.h" +#include "core/hle/service/psc/time/clocks/standard_steady_clock_core.h" + +namespace Service::PSC::Time { + +void StandardSteadyClockCore::Initialize(ClockSourceId clock_source_id, s64 rtc_offset, + s64 internal_offset, s64 test_offset, + bool is_rtc_reset_detected) { + m_clock_source_id = clock_source_id; + m_rtc_offset = rtc_offset; + m_internal_offset = internal_offset; + m_test_offset = test_offset; + if (is_rtc_reset_detected) { + SetResetDetected(); + } + SetInitialized(); +} + +void StandardSteadyClockCore::SetRtcOffset(s64 offset) { + m_rtc_offset = offset; +} + +void StandardSteadyClockCore::SetContinuousAdjustment(ClockSourceId clock_source_id, s64 time) { + auto ticks{m_system.CoreTiming().GetClockTicks()}; + + m_continuous_adjustment_time_point.rtc_offset = ConvertToTimeSpan(ticks).count(); + m_continuous_adjustment_time_point.diff_scale = 0; + m_continuous_adjustment_time_point.shift_amount = 0; + m_continuous_adjustment_time_point.lower = time; + m_continuous_adjustment_time_point.upper = time; + m_continuous_adjustment_time_point.clock_source_id = clock_source_id; +} + +void StandardSteadyClockCore::GetContinuousAdjustment( + ContinuousAdjustmentTimePoint& out_time_point) const { + out_time_point = m_continuous_adjustment_time_point; +} + +void StandardSteadyClockCore::UpdateContinuousAdjustmentTime(s64 in_time) { + auto ticks{m_system.CoreTiming().GetClockTicks()}; + auto uptime_ns{ConvertToTimeSpan(ticks).count()}; + auto adjusted_time{((uptime_ns - m_continuous_adjustment_time_point.rtc_offset) * + m_continuous_adjustment_time_point.diff_scale) >> + m_continuous_adjustment_time_point.shift_amount}; + auto expected_time{adjusted_time + m_continuous_adjustment_time_point.lower}; + + auto last_time_point{m_continuous_adjustment_time_point.upper}; + m_continuous_adjustment_time_point.upper = in_time; + auto t1{std::min<s64>(expected_time, last_time_point)}; + expected_time = std::max<s64>(expected_time, last_time_point); + expected_time = m_continuous_adjustment_time_point.diff_scale >= 0 ? t1 : expected_time; + + auto new_diff{in_time < expected_time ? -55 : 55}; + + m_continuous_adjustment_time_point.rtc_offset = uptime_ns; + m_continuous_adjustment_time_point.shift_amount = expected_time == in_time ? 0 : 14; + m_continuous_adjustment_time_point.diff_scale = expected_time == in_time ? 0 : new_diff; + m_continuous_adjustment_time_point.lower = expected_time; +} + +Result StandardSteadyClockCore::GetCurrentTimePointImpl(SteadyClockTimePoint& out_time_point) { + auto current_time_ns = GetCurrentRawTimePointImpl(); + auto current_time_s = + std::chrono::duration_cast<std::chrono::seconds>(std::chrono::nanoseconds(current_time_ns)); + out_time_point.time_point = current_time_s.count(); + out_time_point.clock_source_id = m_clock_source_id; + R_SUCCEED(); +} + +s64 StandardSteadyClockCore::GetCurrentRawTimePointImpl() { + std::scoped_lock l{m_mutex}; + auto ticks{static_cast<s64>(m_system.CoreTiming().GetClockTicks())}; + auto current_time_ns = m_rtc_offset + ConvertToTimeSpan(ticks).count(); + auto time_point = std::max<s64>(current_time_ns, m_cached_time_point); + m_cached_time_point = time_point; + return time_point; +} + +s64 StandardSteadyClockCore::GetTestOffsetImpl() const { + return m_test_offset; +} + +void StandardSteadyClockCore::SetTestOffsetImpl(s64 offset) { + m_test_offset = offset; +} + +s64 StandardSteadyClockCore::GetInternalOffsetImpl() const { + return m_internal_offset; +} + +void StandardSteadyClockCore::SetInternalOffsetImpl(s64 offset) { + m_internal_offset = offset; +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/standard_steady_clock_core.h b/src/core/hle/service/psc/time/clocks/standard_steady_clock_core.h new file mode 100644 index 000000000..bbf98fcd5 --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/standard_steady_clock_core.h @@ -0,0 +1,54 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <mutex> + +#include "core/hle/service/psc/time/clocks/steady_clock_core.h" + +namespace Core { +class System; +} + +namespace Service::PSC::Time { +class StandardSteadyClockCore : public SteadyClockCore { +public: + explicit StandardSteadyClockCore(Core::System& system) : m_system{system} {} + ~StandardSteadyClockCore() override = default; + + void Initialize(ClockSourceId clock_source_id, s64 rtc_offset, s64 internal_offset, + s64 test_offset, bool is_rtc_reset_detected); + + void SetRtcOffset(s64 offset); + void SetContinuousAdjustment(ClockSourceId clock_source_id, s64 time); + void GetContinuousAdjustment(ContinuousAdjustmentTimePoint& out_time_point) const; + void UpdateContinuousAdjustmentTime(s64 time); + + Result GetCurrentTimePointImpl(SteadyClockTimePoint& out_time_point) override; + s64 GetCurrentRawTimePointImpl() override; + s64 GetTestOffsetImpl() const override; + void SetTestOffsetImpl(s64 offset) override; + s64 GetInternalOffsetImpl() const override; + void SetInternalOffsetImpl(s64 offset) override; + + Result GetRtcValueImpl(s64& out_value) override { + R_RETURN(ResultNotImplemented); + } + + Result GetSetupResultValueImpl() override { + R_SUCCEED(); + } + +private: + Core::System& m_system; + + std::mutex m_mutex; + s64 m_test_offset{}; + s64 m_internal_offset{}; + ClockSourceId m_clock_source_id{}; + s64 m_rtc_offset{}; + s64 m_cached_time_point{}; + ContinuousAdjustmentTimePoint m_continuous_adjustment_time_point{}; +}; +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.cpp b/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.cpp new file mode 100644 index 000000000..9e9be05d6 --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.cpp @@ -0,0 +1,63 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/service/psc/time/clocks/standard_user_system_clock_core.h" + +namespace Service::PSC::Time { + +StandardUserSystemClockCore::StandardUserSystemClockCore( + Core::System& system, StandardLocalSystemClockCore& local_clock, + StandardNetworkSystemClockCore& network_clock) + : SystemClockCore{local_clock.GetSteadyClock()}, m_system{system}, + m_ctx{m_system, "Psc:StandardUserSystemClockCore"}, m_local_system_clock{local_clock}, + m_network_system_clock{network_clock}, m_event{m_ctx.CreateEvent( + "Psc:StandardUserSystemClockCore:Event")} {} + +StandardUserSystemClockCore::~StandardUserSystemClockCore() { + m_ctx.CloseEvent(m_event); +} + +Result StandardUserSystemClockCore::SetAutomaticCorrection(bool automatic_correction) { + R_SUCCEED_IF(m_automatic_correction == automatic_correction); + R_SUCCEED_IF(!m_network_system_clock.CheckClockSourceMatches()); + + SystemClockContext context{}; + R_TRY(m_network_system_clock.GetContext(context)); + R_TRY(m_local_system_clock.SetContextAndWrite(context)); + + m_automatic_correction = automatic_correction; + R_SUCCEED(); +} + +Result StandardUserSystemClockCore::GetContext(SystemClockContext& out_context) const { + if (!m_automatic_correction) { + R_RETURN(m_local_system_clock.GetContext(out_context)); + } + + if (!m_network_system_clock.CheckClockSourceMatches()) { + R_RETURN(m_local_system_clock.GetContext(out_context)); + } + + SystemClockContext context{}; + R_TRY(m_network_system_clock.GetContext(context)); + R_TRY(m_local_system_clock.SetContextAndWrite(context)); + + R_RETURN(m_local_system_clock.GetContext(out_context)); +} + +Result StandardUserSystemClockCore::SetContext(SystemClockContext& context) { + R_RETURN(ResultNotImplemented); +} + +Result StandardUserSystemClockCore::GetTimePoint(SteadyClockTimePoint& out_time_point) { + out_time_point = m_time_point; + R_SUCCEED(); +} + +void StandardUserSystemClockCore::SetTimePointAndSignal(SteadyClockTimePoint& time_point) { + m_time_point = time_point; + m_event->Signal(); +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.h b/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.h new file mode 100644 index 000000000..a7fe7648d --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.h @@ -0,0 +1,55 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/kernel/k_event.h" +#include "core/hle/result.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/psc/time/clocks/context_writers.h" +#include "core/hle/service/psc/time/clocks/standard_local_system_clock_core.h" +#include "core/hle/service/psc/time/clocks/standard_network_system_clock_core.h" +#include "core/hle/service/psc/time/clocks/steady_clock_core.h" +#include "core/hle/service/psc/time/clocks/system_clock_core.h" +#include "core/hle/service/psc/time/common.h" + +namespace Core { +class System; +} + +namespace Service::PSC::Time { + +class StandardUserSystemClockCore : public SystemClockCore { +public: + explicit StandardUserSystemClockCore(Core::System& system, + StandardLocalSystemClockCore& local_clock, + StandardNetworkSystemClockCore& network_clock); + ~StandardUserSystemClockCore() override; + + Kernel::KEvent& GetEvent() { + return *m_event; + } + + bool GetAutomaticCorrection() const { + return m_automatic_correction; + } + Result SetAutomaticCorrection(bool automatic_correction); + + Result GetContext(SystemClockContext& out_context) const override; + Result SetContext(SystemClockContext& context) override; + + Result GetTimePoint(SteadyClockTimePoint& out_time_point); + void SetTimePointAndSignal(SteadyClockTimePoint& time_point); + +private: + Core::System& m_system; + KernelHelpers::ServiceContext m_ctx; + + bool m_automatic_correction{}; + StandardLocalSystemClockCore& m_local_system_clock; + StandardNetworkSystemClockCore& m_network_system_clock; + SteadyClockTimePoint m_time_point{}; + Kernel::KEvent* m_event{}; +}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/steady_clock_core.h b/src/core/hle/service/psc/time/clocks/steady_clock_core.h new file mode 100644 index 000000000..f933cc15f --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/steady_clock_core.h @@ -0,0 +1,81 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <chrono> + +#include "core/hle/result.h" +#include "core/hle/service/psc/time/common.h" + +namespace Service::PSC::Time { + +class SteadyClockCore { +public: + SteadyClockCore() = default; + virtual ~SteadyClockCore() = default; + + void SetInitialized() { + m_initialized = true; + } + + bool IsInitialized() const { + return m_initialized; + } + + void SetResetDetected() { + m_reset_detected = true; + } + + bool IsResetDetected() const { + return m_reset_detected; + } + + Result GetCurrentTimePoint(SteadyClockTimePoint& out_time_point) { + R_TRY(GetCurrentTimePointImpl(out_time_point)); + + auto one_second_ns{ + std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()}; + out_time_point.time_point += GetTestOffsetImpl() / one_second_ns; + out_time_point.time_point += GetInternalOffsetImpl() / one_second_ns; + R_SUCCEED(); + } + + s64 GetTestOffset() const { + return GetTestOffsetImpl(); + } + + void SetTestOffset(s64 offset) { + SetTestOffsetImpl(offset); + } + + s64 GetInternalOffset() const { + return GetInternalOffsetImpl(); + } + + s64 GetRawTime() { + return GetCurrentRawTimePointImpl() + GetTestOffsetImpl() + GetInternalOffsetImpl(); + } + + Result GetRtcValue(s64& out_value) { + R_RETURN(GetRtcValueImpl(out_value)); + } + + Result GetSetupResultValue() { + R_RETURN(GetSetupResultValueImpl()); + } + +private: + virtual Result GetCurrentTimePointImpl(SteadyClockTimePoint& out_time_point) = 0; + virtual s64 GetCurrentRawTimePointImpl() = 0; + virtual s64 GetTestOffsetImpl() const = 0; + virtual void SetTestOffsetImpl(s64 offset) = 0; + virtual s64 GetInternalOffsetImpl() const = 0; + virtual void SetInternalOffsetImpl(s64 offset) = 0; + virtual Result GetRtcValueImpl(s64& out_value) = 0; + virtual Result GetSetupResultValueImpl() = 0; + + bool m_initialized{}; + bool m_reset_detected{}; +}; +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/system_clock_core.cpp b/src/core/hle/service/psc/time/clocks/system_clock_core.cpp new file mode 100644 index 000000000..c507ef517 --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/system_clock_core.cpp @@ -0,0 +1,75 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/psc/time/clocks/context_writers.h" +#include "core/hle/service/psc/time/clocks/system_clock_core.h" + +namespace Service::PSC::Time { + +bool SystemClockCore::CheckClockSourceMatches() { + SystemClockContext context{}; + if (GetContext(context) != ResultSuccess) { + return false; + } + + SteadyClockTimePoint time_point{}; + if (m_steady_clock.GetCurrentTimePoint(time_point) != ResultSuccess) { + return false; + } + + return context.steady_time_point.IdMatches(time_point); +} + +Result SystemClockCore::GetCurrentTime(s64* out_time) const { + R_UNLESS(out_time != nullptr, ResultInvalidArgument); + + SystemClockContext context{}; + SteadyClockTimePoint time_point{}; + + R_TRY(m_steady_clock.GetCurrentTimePoint(time_point)); + R_TRY(GetContext(context)); + + R_UNLESS(context.steady_time_point.IdMatches(time_point), ResultClockMismatch); + + *out_time = context.offset + time_point.time_point; + R_SUCCEED(); +} + +Result SystemClockCore::SetCurrentTime(s64 time) { + SteadyClockTimePoint time_point{}; + R_TRY(m_steady_clock.GetCurrentTimePoint(time_point)); + + SystemClockContext context{ + .offset = time - time_point.time_point, + .steady_time_point = time_point, + }; + R_RETURN(SetContextAndWrite(context)); +} + +Result SystemClockCore::GetContext(SystemClockContext& out_context) const { + out_context = m_context; + R_SUCCEED(); +} + +Result SystemClockCore::SetContext(SystemClockContext& context) { + m_context = context; + R_SUCCEED(); +} + +Result SystemClockCore::SetContextAndWrite(SystemClockContext& context) { + R_TRY(SetContext(context)); + + if (m_context_writer) { + R_RETURN(m_context_writer->Write(context)); + } + + R_SUCCEED(); +} + +void SystemClockCore::LinkOperationEvent(OperationEvent& operation_event) { + if (m_context_writer) { + m_context_writer->Link(operation_event); + } +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/system_clock_core.h b/src/core/hle/service/psc/time/clocks/system_clock_core.h new file mode 100644 index 000000000..73811712e --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/system_clock_core.h @@ -0,0 +1,55 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/result.h" +#include "core/hle/service/psc/time/clocks/steady_clock_core.h" +#include "core/hle/service/psc/time/common.h" + +namespace Service::PSC::Time { +class ContextWriter; + +class SystemClockCore { +public: + explicit SystemClockCore(SteadyClockCore& steady_clock) : m_steady_clock{steady_clock} {} + virtual ~SystemClockCore() = default; + + SteadyClockCore& GetSteadyClock() { + return m_steady_clock; + } + + bool IsInitialized() const { + return m_initialized; + } + + void SetInitialized() { + m_initialized = true; + } + + void SetContextWriter(ContextWriter& context_writer) { + m_context_writer = &context_writer; + } + + bool CheckClockSourceMatches(); + + Result GetCurrentTime(s64* out_time) const; + Result SetCurrentTime(s64 time); + + Result GetCurrentTimePoint(SteadyClockTimePoint& out_time_point) { + R_RETURN(m_steady_clock.GetCurrentTimePoint(out_time_point)); + } + + virtual Result GetContext(SystemClockContext& out_context) const; + virtual Result SetContext(SystemClockContext& context); + Result SetContextAndWrite(SystemClockContext& context); + + void LinkOperationEvent(OperationEvent& operation_event); + +private: + bool m_initialized{}; + ContextWriter* m_context_writer{}; + SteadyClockCore& m_steady_clock; + SystemClockContext m_context{}; +}; +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.cpp b/src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.cpp new file mode 100644 index 000000000..22da1fbcc --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.cpp @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <chrono> + +#include "core/core.h" +#include "core/core_timing.h" +#include "core/hle/service/psc/time/clocks/tick_based_steady_clock_core.h" + +namespace Service::PSC::Time { + +Result TickBasedSteadyClockCore::GetCurrentTimePointImpl(SteadyClockTimePoint& out_time_point) { + auto ticks{m_system.CoreTiming().GetClockTicks()}; + auto current_time_s = + std::chrono::duration_cast<std::chrono::seconds>(ConvertToTimeSpan(ticks)).count(); + out_time_point.time_point = current_time_s; + out_time_point.clock_source_id = m_clock_source_id; + R_SUCCEED(); +} + +s64 TickBasedSteadyClockCore::GetCurrentRawTimePointImpl() { + SteadyClockTimePoint time_point{}; + if (GetCurrentTimePointImpl(time_point) != ResultSuccess) { + LOG_ERROR(Service_Time, "Failed to GetCurrentTimePoint!"); + } + return std::chrono::duration_cast<std::chrono::nanoseconds>( + std::chrono::seconds(time_point.time_point)) + .count(); +} + +s64 TickBasedSteadyClockCore::GetTestOffsetImpl() const { + return 0; +} + +void TickBasedSteadyClockCore::SetTestOffsetImpl(s64 offset) {} + +s64 TickBasedSteadyClockCore::GetInternalOffsetImpl() const { + return 0; +} + +void TickBasedSteadyClockCore::SetInternalOffsetImpl(s64 offset) {} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.h b/src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.h new file mode 100644 index 000000000..a7bea86a2 --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.h @@ -0,0 +1,41 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <mutex> + +#include "common/uuid.h" +#include "core/hle/service/psc/time/clocks/steady_clock_core.h" + +namespace Core { +class System; +} + +namespace Service::PSC::Time { +class TickBasedSteadyClockCore : public SteadyClockCore { +public: + explicit TickBasedSteadyClockCore(Core::System& system) : m_system{system} {} + ~TickBasedSteadyClockCore() override = default; + + Result GetCurrentTimePointImpl(SteadyClockTimePoint& out_time_point) override; + s64 GetCurrentRawTimePointImpl() override; + s64 GetTestOffsetImpl() const override; + void SetTestOffsetImpl(s64 offset) override; + s64 GetInternalOffsetImpl() const override; + void SetInternalOffsetImpl(s64 offset) override; + + Result GetRtcValueImpl(s64& out_value) override { + R_RETURN(ResultNotImplemented); + } + + Result GetSetupResultValueImpl() override { + R_SUCCEED(); + } + +private: + Core::System& m_system; + + ClockSourceId m_clock_source_id{Common::UUID::MakeRandom()}; +}; +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/common.cpp b/src/core/hle/service/psc/time/common.cpp new file mode 100644 index 000000000..a6d9f02ca --- /dev/null +++ b/src/core/hle/service/psc/time/common.cpp @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/service/psc/time/common.h" + +namespace Service::PSC::Time { +OperationEvent::OperationEvent(Core::System& system) + : m_ctx{system, "Time:OperationEvent"}, m_event{ + m_ctx.CreateEvent("Time:OperationEvent:Event")} {} + +OperationEvent::~OperationEvent() { + m_ctx.CloseEvent(m_event); +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/common.h b/src/core/hle/service/psc/time/common.h new file mode 100644 index 000000000..d17b31143 --- /dev/null +++ b/src/core/hle/service/psc/time/common.h @@ -0,0 +1,168 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> +#include <chrono> + +#include "common/common_types.h" +#include "common/intrusive_list.h" +#include "common/uuid.h" +#include "common/wall_clock.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/psc/time/errors.h" + +namespace Core { +class System; +} + +namespace Service::PSC::Time { +using ClockSourceId = Common::UUID; + +struct SteadyClockTimePoint { + constexpr bool IdMatches(SteadyClockTimePoint& other) { + return clock_source_id == other.clock_source_id; + } + bool operator==(const SteadyClockTimePoint& other) const = default; + + s64 time_point; + ClockSourceId clock_source_id; +}; +static_assert(sizeof(SteadyClockTimePoint) == 0x18, "SteadyClockTimePoint has the wrong size!"); +static_assert(std::is_trivial_v<ClockSourceId>); + +struct SystemClockContext { + bool operator==(const SystemClockContext& other) const = default; + + s64 offset; + SteadyClockTimePoint steady_time_point; +}; +static_assert(sizeof(SystemClockContext) == 0x20, "SystemClockContext has the wrong size!"); +static_assert(std::is_trivial_v<SystemClockContext>); + +enum class TimeType : u8 { + UserSystemClock, + NetworkSystemClock, + LocalSystemClock, +}; + +struct CalendarTime { + s16 year; + s8 month; + s8 day; + s8 hour; + s8 minute; + s8 second; +}; +static_assert(sizeof(CalendarTime) == 0x8, "CalendarTime has the wrong size!"); + +struct CalendarAdditionalInfo { + s32 day_of_week; + s32 day_of_year; + std::array<char, 8> name; + s32 is_dst; + s32 ut_offset; +}; +static_assert(sizeof(CalendarAdditionalInfo) == 0x18, "CalendarAdditionalInfo has the wrong size!"); + +struct LocationName { + std::array<char, 36> name; +}; +static_assert(sizeof(LocationName) == 0x24, "LocationName has the wrong size!"); + +struct RuleVersion { + std::array<char, 16> version; +}; +static_assert(sizeof(RuleVersion) == 0x10, "RuleVersion has the wrong size!"); + +struct ClockSnapshot { + SystemClockContext user_context; + SystemClockContext network_context; + s64 user_time; + s64 network_time; + CalendarTime user_calendar_time; + CalendarTime network_calendar_time; + CalendarAdditionalInfo user_calendar_additional_time; + CalendarAdditionalInfo network_calendar_additional_time; + SteadyClockTimePoint steady_clock_time_point; + LocationName location_name; + bool is_automatic_correction_enabled; + TimeType type; + u16 unk_CE; +}; +static_assert(sizeof(ClockSnapshot) == 0xD0, "ClockSnapshot has the wrong size!"); +static_assert(std::is_trivial_v<ClockSnapshot>); + +struct ContinuousAdjustmentTimePoint { + s64 rtc_offset; + s64 diff_scale; + s64 shift_amount; + s64 lower; + s64 upper; + ClockSourceId clock_source_id; +}; +static_assert(sizeof(ContinuousAdjustmentTimePoint) == 0x38, + "ContinuousAdjustmentTimePoint has the wrong size!"); +static_assert(std::is_trivial_v<ContinuousAdjustmentTimePoint>); + +struct AlarmInfo { + s64 alert_time; + u32 priority; +}; +static_assert(sizeof(AlarmInfo) == 0x10, "AlarmInfo has the wrong size!"); + +struct StaticServiceSetupInfo { + bool can_write_local_clock; + bool can_write_user_clock; + bool can_write_network_clock; + bool can_write_timezone_device_location; + bool can_write_steady_clock; + bool can_write_uninitialized_clock; +}; +static_assert(sizeof(StaticServiceSetupInfo) == 0x6, "StaticServiceSetupInfo has the wrong size!"); + +struct OperationEvent : public Common::IntrusiveListBaseNode<OperationEvent> { + using OperationEventList = Common::IntrusiveListBaseTraits<OperationEvent>::ListType; + + OperationEvent(Core::System& system); + ~OperationEvent(); + + KernelHelpers::ServiceContext m_ctx; + Kernel::KEvent* m_event{}; +}; + +constexpr inline std::chrono::nanoseconds ConvertToTimeSpan(s64 ticks) { + constexpr auto one_second_ns{ + std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()}; + + constexpr s64 max{Common::WallClock::CNTFRQ * + (std::numeric_limits<s64>::max() / one_second_ns)}; + + if (ticks > max) { + return std::chrono::nanoseconds(std::numeric_limits<s64>::max()); + } else if (ticks < -max) { + return std::chrono::nanoseconds(std::numeric_limits<s64>::min()); + } + + auto a{ticks / Common::WallClock::CNTFRQ * one_second_ns}; + auto b{((ticks % Common::WallClock::CNTFRQ) * one_second_ns) / Common::WallClock::CNTFRQ}; + + return std::chrono::nanoseconds(a + b); +} + +constexpr inline Result GetSpanBetweenTimePoints(s64* out_seconds, SteadyClockTimePoint& a, + SteadyClockTimePoint& b) { + R_UNLESS(out_seconds, ResultInvalidArgument); + R_UNLESS(a.IdMatches(b), ResultInvalidArgument); + R_UNLESS(a.time_point >= 0 || b.time_point <= a.time_point + std::numeric_limits<s64>::max(), + ResultOverflow); + R_UNLESS(a.time_point < 0 || b.time_point >= a.time_point + std::numeric_limits<s64>::min(), + ResultOverflow); + + *out_seconds = b.time_point - a.time_point; + R_SUCCEED(); +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/errors.h b/src/core/hle/service/psc/time/errors.h new file mode 100644 index 000000000..6d833a006 --- /dev/null +++ b/src/core/hle/service/psc/time/errors.h @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/result.h" + +namespace Service::PSC::Time { + +constexpr Result ResultPermissionDenied{ErrorModule::Time, 1}; +constexpr Result ResultClockMismatch{ErrorModule::Time, 102}; +constexpr Result ResultClockUninitialized{ErrorModule::Time, 103}; +constexpr Result ResultTimeNotFound{ErrorModule::Time, 200}; +constexpr Result ResultOverflow{ErrorModule::Time, 201}; +constexpr Result ResultFailed{ErrorModule::Time, 801}; +constexpr Result ResultInvalidArgument{ErrorModule::Time, 901}; +constexpr Result ResultTimeZoneOutOfRange{ErrorModule::Time, 902}; +constexpr Result ResultTimeZoneParseFailed{ErrorModule::Time, 903}; +constexpr Result ResultRtcTimeout{ErrorModule::Time, 988}; +constexpr Result ResultTimeZoneNotFound{ErrorModule::Time, 989}; +constexpr Result ResultNotImplemented{ErrorModule::Time, 990}; +constexpr Result ResultAlarmNotRegistered{ErrorModule::Time, 1502}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/manager.h b/src/core/hle/service/psc/time/manager.h new file mode 100644 index 000000000..62ded1247 --- /dev/null +++ b/src/core/hle/service/psc/time/manager.h @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/psc/time/alarms.h" +#include "core/hle/service/psc/time/clocks/context_writers.h" +#include "core/hle/service/psc/time/clocks/ephemeral_network_system_clock_core.h" +#include "core/hle/service/psc/time/clocks/standard_local_system_clock_core.h" +#include "core/hle/service/psc/time/clocks/standard_network_system_clock_core.h" +#include "core/hle/service/psc/time/clocks/standard_steady_clock_core.h" +#include "core/hle/service/psc/time/clocks/standard_user_system_clock_core.h" +#include "core/hle/service/psc/time/clocks/tick_based_steady_clock_core.h" +#include "core/hle/service/psc/time/power_state_request_manager.h" +#include "core/hle/service/psc/time/shared_memory.h" +#include "core/hle/service/psc/time/time_zone.h" + +namespace Core { +class System; +} + +namespace Service::PSC::Time { +class TimeManager { +public: + explicit TimeManager(Core::System& system) + : m_system{system}, m_standard_steady_clock{system}, m_tick_based_steady_clock{m_system}, + m_standard_local_system_clock{m_standard_steady_clock}, + m_standard_network_system_clock{m_standard_steady_clock}, + m_standard_user_system_clock{m_system, m_standard_local_system_clock, + m_standard_network_system_clock}, + m_ephemeral_network_clock{m_tick_based_steady_clock}, m_shared_memory{m_system}, + m_power_state_request_manager{m_system}, m_alarms{m_system, m_standard_steady_clock, + m_power_state_request_manager}, + m_local_system_clock_context_writer{m_system, m_shared_memory}, + m_network_system_clock_context_writer{m_system, m_shared_memory, + m_standard_user_system_clock}, + m_ephemeral_network_clock_context_writer{m_system} {} + + Core::System& m_system; + + StandardSteadyClockCore m_standard_steady_clock; + TickBasedSteadyClockCore m_tick_based_steady_clock; + StandardLocalSystemClockCore m_standard_local_system_clock; + StandardNetworkSystemClockCore m_standard_network_system_clock; + StandardUserSystemClockCore m_standard_user_system_clock; + EphemeralNetworkSystemClockCore m_ephemeral_network_clock; + TimeZone m_time_zone; + SharedMemory m_shared_memory; + PowerStateRequestManager m_power_state_request_manager; + Alarms m_alarms; + LocalSystemClockContextWriter m_local_system_clock_context_writer; + NetworkSystemClockContextWriter m_network_system_clock_context_writer; + EphemeralNetworkSystemClockContextWriter m_ephemeral_network_clock_context_writer; +}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/power_state_request_manager.cpp b/src/core/hle/service/psc/time/power_state_request_manager.cpp new file mode 100644 index 000000000..17de0bf4d --- /dev/null +++ b/src/core/hle/service/psc/time/power_state_request_manager.cpp @@ -0,0 +1,50 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/service/psc/time/power_state_request_manager.h" + +namespace Service::PSC::Time { + +PowerStateRequestManager::PowerStateRequestManager(Core::System& system) + : m_system{system}, m_ctx{system, "Psc:PowerStateRequestManager"}, + m_event{m_ctx.CreateEvent("Psc:PowerStateRequestManager:Event")} {} + +PowerStateRequestManager::~PowerStateRequestManager() { + m_ctx.CloseEvent(m_event); +} + +void PowerStateRequestManager::UpdatePendingPowerStateRequestPriority(u32 priority) { + std::scoped_lock l{m_mutex}; + if (m_has_pending_request) { + m_pending_request_priority = std::max(m_pending_request_priority, priority); + } else { + m_pending_request_priority = priority; + m_has_pending_request = true; + } +} + +void PowerStateRequestManager::SignalPowerStateRequestAvailability() { + std::scoped_lock l{m_mutex}; + if (m_has_pending_request) { + if (!m_has_available_request) { + m_has_available_request = true; + } + m_has_pending_request = false; + m_available_request_priority = m_pending_request_priority; + m_event->Signal(); + } +} + +bool PowerStateRequestManager::GetAndClearPowerStateRequest(u32& out_priority) { + std::scoped_lock l{m_mutex}; + auto had_request{m_has_available_request}; + if (m_has_available_request) { + out_priority = m_available_request_priority; + m_has_available_request = false; + m_event->Clear(); + } + return had_request; +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/power_state_request_manager.h b/src/core/hle/service/psc/time/power_state_request_manager.h new file mode 100644 index 000000000..30a0c947d --- /dev/null +++ b/src/core/hle/service/psc/time/power_state_request_manager.h @@ -0,0 +1,42 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <mutex> + +#include "core/hle/kernel/k_event.h" +#include "core/hle/service/kernel_helpers.h" + +namespace Core { +class System; +} + +namespace Service::PSC::Time { + +class PowerStateRequestManager { +public: + explicit PowerStateRequestManager(Core::System& system); + ~PowerStateRequestManager(); + + Kernel::KReadableEvent& GetReadableEvent() { + return m_event->GetReadableEvent(); + } + + void UpdatePendingPowerStateRequestPriority(u32 priority); + void SignalPowerStateRequestAvailability(); + bool GetAndClearPowerStateRequest(u32& out_priority); + +private: + Core::System& m_system; + KernelHelpers::ServiceContext m_ctx; + + Kernel::KEvent* m_event{}; + bool m_has_pending_request{}; + u32 m_pending_request_priority{}; + bool m_has_available_request{}; + u32 m_available_request_priority{}; + std::mutex m_mutex; +}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/power_state_service.cpp b/src/core/hle/service/psc/time/power_state_service.cpp new file mode 100644 index 000000000..b0ae71bf9 --- /dev/null +++ b/src/core/hle/service/psc/time/power_state_service.cpp @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/psc/time/power_state_service.h" + +namespace Service::PSC::Time { + +IPowerStateRequestHandler::IPowerStateRequestHandler( + Core::System& system_, PowerStateRequestManager& power_state_request_manager) + : ServiceFramework{system_, "time:p"}, m_system{system}, m_power_state_request_manager{ + power_state_request_manager} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &IPowerStateRequestHandler::GetPowerStateRequestEventReadableHandle, "GetPowerStateRequestEventReadableHandle"}, + {1, &IPowerStateRequestHandler::GetAndClearPowerStateRequest, "GetAndClearPowerStateRequest"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +void IPowerStateRequestHandler::GetPowerStateRequestEventReadableHandle(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(m_power_state_request_manager.GetReadableEvent()); +} + +void IPowerStateRequestHandler::GetAndClearPowerStateRequest(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + u32 priority{}; + auto cleared = m_power_state_request_manager.GetAndClearPowerStateRequest(priority); + + if (cleared) { + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push(priority); + rb.Push(cleared); + return; + } + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(cleared); +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/power_state_service.h b/src/core/hle/service/psc/time/power_state_service.h new file mode 100644 index 000000000..3ebfddb79 --- /dev/null +++ b/src/core/hle/service/psc/time/power_state_service.h @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/psc/time/power_state_request_manager.h" +#include "core/hle/service/server_manager.h" +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Service::PSC::Time { + +class IPowerStateRequestHandler final : public ServiceFramework<IPowerStateRequestHandler> { +public: + explicit IPowerStateRequestHandler(Core::System& system, + PowerStateRequestManager& power_state_request_manager); + + ~IPowerStateRequestHandler() override = default; + +private: + void GetPowerStateRequestEventReadableHandle(HLERequestContext& ctx); + void GetAndClearPowerStateRequest(HLERequestContext& ctx); + + Core::System& m_system; + PowerStateRequestManager& m_power_state_request_manager; +}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/service_manager.cpp b/src/core/hle/service/psc/time/service_manager.cpp new file mode 100644 index 000000000..60820aa9b --- /dev/null +++ b/src/core/hle/service/psc/time/service_manager.cpp @@ -0,0 +1,494 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/core_timing.h" +#include "core/hle/service/psc/time/power_state_service.h" +#include "core/hle/service/psc/time/service_manager.h" +#include "core/hle/service/psc/time/static.h" + +namespace Service::PSC::Time { + +ServiceManager::ServiceManager(Core::System& system_, std::shared_ptr<TimeManager> time, + ServerManager* server_manager) + : ServiceFramework{system_, "time:m"}, m_system{system}, m_time{std::move(time)}, + m_server_manager{*server_manager}, + m_local_system_clock{m_time->m_standard_local_system_clock}, + m_user_system_clock{m_time->m_standard_user_system_clock}, + m_network_system_clock{m_time->m_standard_network_system_clock}, + m_steady_clock{m_time->m_standard_steady_clock}, m_time_zone{m_time->m_time_zone}, + m_ephemeral_network_clock{m_time->m_ephemeral_network_clock}, + m_shared_memory{m_time->m_shared_memory}, m_alarms{m_time->m_alarms}, + m_local_system_context_writer{m_time->m_local_system_clock_context_writer}, + m_network_system_context_writer{m_time->m_network_system_clock_context_writer}, + m_ephemeral_system_context_writer{m_time->m_ephemeral_network_clock_context_writer}, + m_local_operation{m_system}, m_network_operation{m_system}, m_ephemeral_operation{m_system} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &ServiceManager::Handle_GetStaticServiceAsUser, "GetStaticServiceAsUser"}, + {5, &ServiceManager::Handle_GetStaticServiceAsAdmin, "GetStaticServiceAsAdmin"}, + {6, &ServiceManager::Handle_GetStaticServiceAsRepair, "GetStaticServiceAsRepair"}, + {9, &ServiceManager::Handle_GetStaticServiceAsServiceManager, "GetStaticServiceAsServiceManager"}, + {10, &ServiceManager::Handle_SetupStandardSteadyClockCore, "SetupStandardSteadyClockCore"}, + {11, &ServiceManager::Handle_SetupStandardLocalSystemClockCore, "SetupStandardLocalSystemClockCore"}, + {12, &ServiceManager::Handle_SetupStandardNetworkSystemClockCore, "SetupStandardNetworkSystemClockCore"}, + {13, &ServiceManager::Handle_SetupStandardUserSystemClockCore, "SetupStandardUserSystemClockCore"}, + {14, &ServiceManager::Handle_SetupTimeZoneServiceCore, "SetupTimeZoneServiceCore"}, + {15, &ServiceManager::Handle_SetupEphemeralNetworkSystemClockCore, "SetupEphemeralNetworkSystemClockCore"}, + {50, &ServiceManager::Handle_GetStandardLocalClockOperationEvent, "GetStandardLocalClockOperationEvent"}, + {51, &ServiceManager::Handle_GetStandardNetworkClockOperationEventForServiceManager, "GetStandardNetworkClockOperationEventForServiceManager"}, + {52, &ServiceManager::Handle_GetEphemeralNetworkClockOperationEventForServiceManager, "GetEphemeralNetworkClockOperationEventForServiceManager"}, + {60, &ServiceManager::Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent, "GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent"}, + {100, &ServiceManager::Handle_SetStandardSteadyClockBaseTime, "SetStandardSteadyClockBaseTime"}, + {200, &ServiceManager::Handle_GetClosestAlarmUpdatedEvent, "GetClosestAlarmUpdatedEvent"}, + {201, &ServiceManager::Handle_CheckAndSignalAlarms, "CheckAndSignalAlarms"}, + {202, &ServiceManager::Handle_GetClosestAlarmInfo, "GetClosestAlarmInfo "}, + }; + // clang-format on + RegisterHandlers(functions); + + m_local_system_context_writer.Link(m_local_operation); + m_network_system_context_writer.Link(m_network_operation); + m_ephemeral_system_context_writer.Link(m_ephemeral_operation); +} + +void ServiceManager::SetupSAndP() { + if (!m_is_s_and_p_setup) { + m_is_s_and_p_setup = true; + m_server_manager.RegisterNamedService( + "time:s", std::make_shared<StaticService>( + m_system, StaticServiceSetupInfo{0, 0, 1, 0, 0, 0}, m_time, "time:s")); + m_server_manager.RegisterNamedService("time:p", + std::make_shared<IPowerStateRequestHandler>( + m_system, m_time->m_power_state_request_manager)); + } +} + +void ServiceManager::CheckAndSetupServicesSAndP() { + if (m_local_system_clock.IsInitialized() && m_user_system_clock.IsInitialized() && + m_network_system_clock.IsInitialized() && m_steady_clock.IsInitialized() && + m_time_zone.IsInitialized() && m_ephemeral_network_clock.IsInitialized()) { + SetupSAndP(); + } +} + +void ServiceManager::Handle_GetStaticServiceAsUser(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr<StaticService> service{}; + auto res = GetStaticServiceAsUser(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface<StaticService>(std::move(service)); +} + +void ServiceManager::Handle_GetStaticServiceAsAdmin(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr<StaticService> service{}; + auto res = GetStaticServiceAsAdmin(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface<StaticService>(std::move(service)); +} + +void ServiceManager::Handle_GetStaticServiceAsRepair(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr<StaticService> service{}; + auto res = GetStaticServiceAsRepair(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface<StaticService>(std::move(service)); +} + +void ServiceManager::Handle_GetStaticServiceAsServiceManager(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr<StaticService> service{}; + auto res = GetStaticServiceAsServiceManager(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface<StaticService>(std::move(service)); +} + +void ServiceManager::Handle_SetupStandardSteadyClockCore(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + struct Parameters { + bool reset_detected; + Common::UUID clock_source_id; + s64 rtc_offset; + s64 internal_offset; + s64 test_offset; + }; + static_assert(sizeof(Parameters) == 0x30); + + IPC::RequestParser rp{ctx}; + auto params{rp.PopRaw<Parameters>()}; + + auto res = SetupStandardSteadyClockCore(params.clock_source_id, params.rtc_offset, + params.internal_offset, params.test_offset, + params.reset_detected); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void ServiceManager::Handle_SetupStandardLocalSystemClockCore(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto context{rp.PopRaw<SystemClockContext>()}; + auto time{rp.Pop<s64>()}; + + auto res = SetupStandardLocalSystemClockCore(context, time); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void ServiceManager::Handle_SetupStandardNetworkSystemClockCore(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto context{rp.PopRaw<SystemClockContext>()}; + auto accuracy{rp.Pop<s64>()}; + + auto res = SetupStandardNetworkSystemClockCore(context, accuracy); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void ServiceManager::Handle_SetupStandardUserSystemClockCore(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + struct Parameters { + bool automatic_correction; + SteadyClockTimePoint time_point; + }; + static_assert(sizeof(Parameters) == 0x20); + + IPC::RequestParser rp{ctx}; + auto params{rp.PopRaw<Parameters>()}; + + auto res = SetupStandardUserSystemClockCore(params.time_point, params.automatic_correction); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void ServiceManager::Handle_SetupTimeZoneServiceCore(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + struct Parameters { + u32 location_count; + LocationName name; + SteadyClockTimePoint time_point; + RuleVersion rule_version; + }; + static_assert(sizeof(Parameters) == 0x50); + + IPC::RequestParser rp{ctx}; + auto params{rp.PopRaw<Parameters>()}; + + auto rule_buffer{ctx.ReadBuffer()}; + + auto res = SetupTimeZoneServiceCore(params.name, params.time_point, params.rule_version, + params.location_count, rule_buffer); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void ServiceManager::Handle_SetupEphemeralNetworkSystemClockCore(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + auto res = SetupEphemeralNetworkSystemClockCore(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void ServiceManager::Handle_GetStandardLocalClockOperationEvent(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + Kernel::KEvent* event{}; + auto res = GetStandardLocalClockOperationEvent(&event); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(res); + rb.PushCopyObjects(event->GetReadableEvent()); +} + +void ServiceManager::Handle_GetStandardNetworkClockOperationEventForServiceManager( + HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + Kernel::KEvent* event{}; + auto res = GetStandardNetworkClockOperationEventForServiceManager(&event); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(res); + rb.PushCopyObjects(event); +} + +void ServiceManager::Handle_GetEphemeralNetworkClockOperationEventForServiceManager( + HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + Kernel::KEvent* event{}; + auto res = GetEphemeralNetworkClockOperationEventForServiceManager(&event); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(res); + rb.PushCopyObjects(event); +} + +void ServiceManager::Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent( + HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + Kernel::KEvent* event{}; + auto res = GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent(&event); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(res); + rb.PushCopyObjects(event); +} + +void ServiceManager::Handle_SetStandardSteadyClockBaseTime(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto base_time{rp.Pop<s64>()}; + + auto res = SetStandardSteadyClockBaseTime(base_time); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void ServiceManager::Handle_GetClosestAlarmUpdatedEvent(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + Kernel::KEvent* event{}; + auto res = GetClosestAlarmUpdatedEvent(&event); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.PushCopyObjects(event->GetReadableEvent()); +} + +void ServiceManager::Handle_CheckAndSignalAlarms(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + auto res = CheckAndSignalAlarms(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void ServiceManager::Handle_GetClosestAlarmInfo(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + AlarmInfo alarm_info{}; + bool is_valid{}; + s64 time{}; + auto res = GetClosestAlarmInfo(is_valid, alarm_info, time); + + struct OutParameters { + bool is_valid; + AlarmInfo alarm_info; + s64 time; + }; + static_assert(sizeof(OutParameters) == 0x20); + + OutParameters out_params{ + .is_valid = is_valid, + .alarm_info = alarm_info, + .time = time, + }; + + IPC::ResponseBuilder rb{ctx, 2 + sizeof(OutParameters) / sizeof(u32)}; + rb.Push(res); + rb.PushRaw<OutParameters>(out_params); +} + +// =============================== Implementations =========================== + +Result ServiceManager::GetStaticService(std::shared_ptr<StaticService>& out_service, + StaticServiceSetupInfo setup_info, const char* name) { + out_service = std::make_shared<StaticService>(m_system, setup_info, m_time, name); + R_SUCCEED(); +} + +Result ServiceManager::GetStaticServiceAsUser(std::shared_ptr<StaticService>& out_service) { + R_RETURN(GetStaticService(out_service, StaticServiceSetupInfo{0, 0, 0, 0, 0, 0}, "time:u")); +} + +Result ServiceManager::GetStaticServiceAsAdmin(std::shared_ptr<StaticService>& out_service) { + R_RETURN(GetStaticService(out_service, StaticServiceSetupInfo{1, 1, 0, 1, 0, 0}, "time:a")); +} + +Result ServiceManager::GetStaticServiceAsRepair(std::shared_ptr<StaticService>& out_service) { + R_RETURN(GetStaticService(out_service, StaticServiceSetupInfo{0, 0, 0, 0, 1, 0}, "time:r")); +} + +Result ServiceManager::GetStaticServiceAsServiceManager( + std::shared_ptr<StaticService>& out_service) { + R_RETURN(GetStaticService(out_service, StaticServiceSetupInfo{1, 1, 1, 1, 1, 0}, "time:sm")); +} + +Result ServiceManager::SetupStandardSteadyClockCore(Common::UUID& clock_source_id, s64 rtc_offset, + s64 internal_offset, s64 test_offset, + bool is_rtc_reset_detected) { + m_steady_clock.Initialize(clock_source_id, rtc_offset, internal_offset, test_offset, + is_rtc_reset_detected); + auto time = m_steady_clock.GetRawTime(); + auto ticks = m_system.CoreTiming().GetClockTicks(); + auto boot_time = time - ConvertToTimeSpan(ticks).count(); + m_shared_memory.SetSteadyClockTimePoint(clock_source_id, boot_time); + m_steady_clock.SetContinuousAdjustment(clock_source_id, boot_time); + + ContinuousAdjustmentTimePoint time_point{}; + m_steady_clock.GetContinuousAdjustment(time_point); + m_shared_memory.SetContinuousAdjustment(time_point); + + CheckAndSetupServicesSAndP(); + R_SUCCEED(); +} + +Result ServiceManager::SetupStandardLocalSystemClockCore(SystemClockContext& context, s64 time) { + m_local_system_clock.SetContextWriter(m_local_system_context_writer); + m_local_system_clock.Initialize(context, time); + + CheckAndSetupServicesSAndP(); + R_SUCCEED(); +} + +Result ServiceManager::SetupStandardNetworkSystemClockCore(SystemClockContext& context, + s64 accuracy) { + // TODO this is a hack! The network clock should be updated independently, from the ntc service + // and maybe elsewhere. We do not do that, so fix the clock to the local clock on first boot + // to avoid it being stuck at 0. + if (context == Service::PSC::Time::SystemClockContext{}) { + m_local_system_clock.GetContext(context); + } + + m_network_system_clock.SetContextWriter(m_network_system_context_writer); + m_network_system_clock.Initialize(context, accuracy); + + CheckAndSetupServicesSAndP(); + R_SUCCEED(); +} + +Result ServiceManager::SetupStandardUserSystemClockCore(SteadyClockTimePoint& time_point, + bool automatic_correction) { + // TODO this is a hack! The user clock should be updated independently, from the ntc service + // and maybe elsewhere. We do not do that, so fix the clock to the local clock on first boot + // to avoid it being stuck at 0. + if (time_point == Service::PSC::Time::SteadyClockTimePoint{}) { + m_local_system_clock.GetCurrentTimePoint(time_point); + } + + m_user_system_clock.SetAutomaticCorrection(automatic_correction); + m_user_system_clock.SetTimePointAndSignal(time_point); + m_user_system_clock.SetInitialized(); + m_shared_memory.SetAutomaticCorrection(automatic_correction); + + CheckAndSetupServicesSAndP(); + R_SUCCEED(); +} + +Result ServiceManager::SetupTimeZoneServiceCore(LocationName& name, + SteadyClockTimePoint& time_point, + RuleVersion& rule_version, u32 location_count, + std::span<const u8> rule_buffer) { + if (m_time_zone.ParseBinary(name, rule_buffer) != ResultSuccess) { + LOG_ERROR(Service_Time, "Failed to parse time zone binary!"); + } + + m_time_zone.SetTimePoint(time_point); + m_time_zone.SetTotalLocationNameCount(location_count); + m_time_zone.SetRuleVersion(rule_version); + m_time_zone.SetInitialized(); + + CheckAndSetupServicesSAndP(); + R_SUCCEED(); +} + +Result ServiceManager::SetupEphemeralNetworkSystemClockCore() { + m_ephemeral_network_clock.SetContextWriter(m_ephemeral_system_context_writer); + m_ephemeral_network_clock.SetInitialized(); + + CheckAndSetupServicesSAndP(); + R_SUCCEED(); +} + +Result ServiceManager::GetStandardLocalClockOperationEvent(Kernel::KEvent** out_event) { + *out_event = m_local_operation.m_event; + R_SUCCEED(); +} + +Result ServiceManager::GetStandardNetworkClockOperationEventForServiceManager( + Kernel::KEvent** out_event) { + *out_event = m_network_operation.m_event; + R_SUCCEED(); +} + +Result ServiceManager::GetEphemeralNetworkClockOperationEventForServiceManager( + Kernel::KEvent** out_event) { + *out_event = m_ephemeral_operation.m_event; + R_SUCCEED(); +} + +Result ServiceManager::GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent( + Kernel::KEvent** out_event) { + *out_event = &m_user_system_clock.GetEvent(); + R_SUCCEED(); +} + +Result ServiceManager::SetStandardSteadyClockBaseTime(s64 base_time) { + m_steady_clock.SetRtcOffset(base_time); + auto time = m_steady_clock.GetRawTime(); + auto ticks = m_system.CoreTiming().GetClockTicks(); + auto diff = time - ConvertToTimeSpan(ticks).count(); + m_shared_memory.UpdateBaseTime(diff); + m_steady_clock.UpdateContinuousAdjustmentTime(diff); + + ContinuousAdjustmentTimePoint time_point{}; + m_steady_clock.GetContinuousAdjustment(time_point); + m_shared_memory.SetContinuousAdjustment(time_point); + R_SUCCEED(); +} + +Result ServiceManager::GetClosestAlarmUpdatedEvent(Kernel::KEvent** out_event) { + *out_event = &m_alarms.GetEvent(); + R_SUCCEED(); +} + +Result ServiceManager::CheckAndSignalAlarms() { + m_alarms.CheckAndSignal(); + R_SUCCEED(); +} + +Result ServiceManager::GetClosestAlarmInfo(bool& out_is_valid, AlarmInfo& out_info, s64& out_time) { + Alarm* alarm{nullptr}; + out_is_valid = m_alarms.GetClosestAlarm(&alarm); + if (out_is_valid) { + out_info = { + .alert_time = alarm->GetAlertTime(), + .priority = alarm->GetPriority(), + }; + out_time = m_alarms.GetRawTime(); + } + R_SUCCEED(); +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/service_manager.h b/src/core/hle/service/psc/time/service_manager.h new file mode 100644 index 000000000..1d9952317 --- /dev/null +++ b/src/core/hle/service/psc/time/service_manager.h @@ -0,0 +1,101 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <list> +#include <memory> + +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/psc/time/common.h" +#include "core/hle/service/psc/time/manager.h" +#include "core/hle/service/server_manager.h" +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Kernel { +class KReadableEvent; +} + +namespace Service::PSC::Time { +class StaticService; + +class ServiceManager final : public ServiceFramework<ServiceManager> { +public: + explicit ServiceManager(Core::System& system, std::shared_ptr<TimeManager> time, + ServerManager* server_manager); + ~ServiceManager() override = default; + + Result GetStaticServiceAsUser(std::shared_ptr<StaticService>& out_service); + Result GetStaticServiceAsAdmin(std::shared_ptr<StaticService>& out_service); + Result GetStaticServiceAsRepair(std::shared_ptr<StaticService>& out_service); + Result GetStaticServiceAsServiceManager(std::shared_ptr<StaticService>& out_service); + Result SetupStandardSteadyClockCore(Common::UUID& clock_source_id, s64 rtc_offset, + s64 internal_offset, s64 test_offset, + bool is_rtc_reset_detected); + Result SetupStandardLocalSystemClockCore(SystemClockContext& context, s64 time); + Result SetupStandardNetworkSystemClockCore(SystemClockContext& context, s64 accuracy); + Result SetupStandardUserSystemClockCore(SteadyClockTimePoint& time_point, + bool automatic_correction); + Result SetupTimeZoneServiceCore(LocationName& name, SteadyClockTimePoint& time_point, + RuleVersion& rule_version, u32 location_count, + std::span<const u8> rule_buffer); + Result SetupEphemeralNetworkSystemClockCore(); + Result GetStandardLocalClockOperationEvent(Kernel::KEvent** out_event); + Result GetStandardNetworkClockOperationEventForServiceManager(Kernel::KEvent** out_event); + Result GetEphemeralNetworkClockOperationEventForServiceManager(Kernel::KEvent** out_event); + Result GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent(Kernel::KEvent** out_event); + Result SetStandardSteadyClockBaseTime(s64 base_time); + Result GetClosestAlarmUpdatedEvent(Kernel::KEvent** out_event); + Result CheckAndSignalAlarms(); + Result GetClosestAlarmInfo(bool& out_is_valid, AlarmInfo& out_info, s64& out_time); + +private: + void CheckAndSetupServicesSAndP(); + void SetupSAndP(); + Result GetStaticService(std::shared_ptr<StaticService>& out_service, + StaticServiceSetupInfo setup_info, const char* name); + + void Handle_GetStaticServiceAsUser(HLERequestContext& ctx); + void Handle_GetStaticServiceAsAdmin(HLERequestContext& ctx); + void Handle_GetStaticServiceAsRepair(HLERequestContext& ctx); + void Handle_GetStaticServiceAsServiceManager(HLERequestContext& ctx); + void Handle_SetupStandardSteadyClockCore(HLERequestContext& ctx); + void Handle_SetupStandardLocalSystemClockCore(HLERequestContext& ctx); + void Handle_SetupStandardNetworkSystemClockCore(HLERequestContext& ctx); + void Handle_SetupStandardUserSystemClockCore(HLERequestContext& ctx); + void Handle_SetupTimeZoneServiceCore(HLERequestContext& ctx); + void Handle_SetupEphemeralNetworkSystemClockCore(HLERequestContext& ctx); + void Handle_GetStandardLocalClockOperationEvent(HLERequestContext& ctx); + void Handle_GetStandardNetworkClockOperationEventForServiceManager(HLERequestContext& ctx); + void Handle_GetEphemeralNetworkClockOperationEventForServiceManager(HLERequestContext& ctx); + void Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent(HLERequestContext& ctx); + void Handle_SetStandardSteadyClockBaseTime(HLERequestContext& ctx); + void Handle_GetClosestAlarmUpdatedEvent(HLERequestContext& ctx); + void Handle_CheckAndSignalAlarms(HLERequestContext& ctx); + void Handle_GetClosestAlarmInfo(HLERequestContext& ctx); + + Core::System& m_system; + std::shared_ptr<TimeManager> m_time; + ServerManager& m_server_manager; + bool m_is_s_and_p_setup{}; + StandardLocalSystemClockCore& m_local_system_clock; + StandardUserSystemClockCore& m_user_system_clock; + StandardNetworkSystemClockCore& m_network_system_clock; + StandardSteadyClockCore& m_steady_clock; + TimeZone& m_time_zone; + EphemeralNetworkSystemClockCore& m_ephemeral_network_clock; + SharedMemory& m_shared_memory; + Alarms& m_alarms; + LocalSystemClockContextWriter& m_local_system_context_writer; + NetworkSystemClockContextWriter& m_network_system_context_writer; + EphemeralNetworkSystemClockContextWriter& m_ephemeral_system_context_writer; + OperationEvent m_local_operation; + OperationEvent m_network_operation; + OperationEvent m_ephemeral_operation; +}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/shared_memory.cpp b/src/core/hle/service/psc/time/shared_memory.cpp new file mode 100644 index 000000000..defaceebe --- /dev/null +++ b/src/core/hle/service/psc/time/shared_memory.cpp @@ -0,0 +1,84 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/kernel/k_shared_memory.h" +#include "core/hle/service/psc/time/shared_memory.h" + +namespace Service::PSC::Time { +namespace { +template <typename T> +constexpr inline T ReadFromLockFreeAtomicType(const LockFreeAtomicType<T>* p) { + while (true) { + // Get the counter. + auto counter = p->m_counter; + + // Get the value. + auto value = p->m_value[counter % 2]; + + // Fence memory. + std::atomic_thread_fence(std::memory_order_acquire); + + // Check that the counter matches. + if (counter == p->m_counter) { + return value; + } + } +} + +template <typename T> +constexpr inline void WriteToLockFreeAtomicType(LockFreeAtomicType<T>* p, const T& value) { + // Get the current counter. + auto counter = p->m_counter; + + // Increment the counter. + ++counter; + + // Store the updated value. + p->m_value[counter % 2] = value; + + // Fence memory. + std::atomic_thread_fence(std::memory_order_release); + + // Set the updated counter. + p->m_counter = counter; +} +} // namespace + +SharedMemory::SharedMemory(Core::System& system) + : m_system{system}, m_k_shared_memory{m_system.Kernel().GetTimeSharedMem()}, + m_shared_memory_ptr{reinterpret_cast<SharedMemoryStruct*>(m_k_shared_memory.GetPointer())} { + std::memset(m_shared_memory_ptr, 0, sizeof(*m_shared_memory_ptr)); +} + +void SharedMemory::SetLocalSystemContext(SystemClockContext& context) { + WriteToLockFreeAtomicType(&m_shared_memory_ptr->local_system_clock_contexts, context); +} + +void SharedMemory::SetNetworkSystemContext(SystemClockContext& context) { + WriteToLockFreeAtomicType(&m_shared_memory_ptr->network_system_clock_contexts, context); +} + +void SharedMemory::SetSteadyClockTimePoint(ClockSourceId clock_source_id, s64 time_point) { + WriteToLockFreeAtomicType(&m_shared_memory_ptr->steady_time_points, + {time_point, clock_source_id}); +} + +void SharedMemory::SetContinuousAdjustment(ContinuousAdjustmentTimePoint& time_point) { + WriteToLockFreeAtomicType(&m_shared_memory_ptr->continuous_adjustment_time_points, time_point); +} + +void SharedMemory::SetAutomaticCorrection(bool automatic_correction) { + WriteToLockFreeAtomicType(&m_shared_memory_ptr->automatic_corrections, automatic_correction); +} + +void SharedMemory::UpdateBaseTime(s64 time) { + SteadyClockTimePoint time_point{ + ReadFromLockFreeAtomicType(&m_shared_memory_ptr->steady_time_points)}; + + time_point.time_point = time; + + WriteToLockFreeAtomicType(&m_shared_memory_ptr->steady_time_points, time_point); +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/shared_memory.h b/src/core/hle/service/psc/time/shared_memory.h new file mode 100644 index 000000000..f9bf97d5c --- /dev/null +++ b/src/core/hle/service/psc/time/shared_memory.h @@ -0,0 +1,70 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> + +#include "common/common_types.h" +#include "core/hle/service/psc/time/common.h" + +namespace Core { +class System; +} + +namespace Kernel { +class KSharedMemory; +} + +namespace Service::PSC::Time { + +template <typename T> +struct LockFreeAtomicType { + u32 m_counter; + std::array<T, 2> m_value; +}; + +struct SharedMemoryStruct { + LockFreeAtomicType<SteadyClockTimePoint> steady_time_points; + LockFreeAtomicType<SystemClockContext> local_system_clock_contexts; + LockFreeAtomicType<SystemClockContext> network_system_clock_contexts; + LockFreeAtomicType<bool> automatic_corrections; + LockFreeAtomicType<ContinuousAdjustmentTimePoint> continuous_adjustment_time_points; + std::array<char, 0xEB8> pad0148; +}; +static_assert(offsetof(SharedMemoryStruct, steady_time_points) == 0x0, + "steady_time_points are in the wrong place!"); +static_assert(offsetof(SharedMemoryStruct, local_system_clock_contexts) == 0x38, + "local_system_clock_contexts are in the wrong place!"); +static_assert(offsetof(SharedMemoryStruct, network_system_clock_contexts) == 0x80, + "network_system_clock_contexts are in the wrong place!"); +static_assert(offsetof(SharedMemoryStruct, automatic_corrections) == 0xC8, + "automatic_corrections are in the wrong place!"); +static_assert(offsetof(SharedMemoryStruct, continuous_adjustment_time_points) == 0xD0, + "continuous_adjustment_time_points are in the wrong place!"); +static_assert(sizeof(SharedMemoryStruct) == 0x1000, + "Time's SharedMemoryStruct has the wrong size!"); +static_assert(std::is_trivial_v<SharedMemoryStruct>); + +class SharedMemory { +public: + explicit SharedMemory(Core::System& system); + + Kernel::KSharedMemory& GetKSharedMemory() { + return m_k_shared_memory; + } + + void SetLocalSystemContext(SystemClockContext& context); + void SetNetworkSystemContext(SystemClockContext& context); + void SetSteadyClockTimePoint(ClockSourceId clock_source_id, s64 time_diff); + void SetContinuousAdjustment(ContinuousAdjustmentTimePoint& time_point); + void SetAutomaticCorrection(bool automatic_correction); + void UpdateBaseTime(s64 time); + +private: + Core::System& m_system; + Kernel::KSharedMemory& m_k_shared_memory; + SharedMemoryStruct* m_shared_memory_ptr; +}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/static.cpp b/src/core/hle/service/psc/time/static.cpp new file mode 100644 index 000000000..6f8cf3f88 --- /dev/null +++ b/src/core/hle/service/psc/time/static.cpp @@ -0,0 +1,500 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/core_timing.h" +#include "core/hle/kernel/k_shared_memory.h" +#include "core/hle/service/psc/time/clocks/ephemeral_network_system_clock_core.h" +#include "core/hle/service/psc/time/clocks/standard_local_system_clock_core.h" +#include "core/hle/service/psc/time/clocks/standard_network_system_clock_core.h" +#include "core/hle/service/psc/time/clocks/standard_user_system_clock_core.h" +#include "core/hle/service/psc/time/manager.h" +#include "core/hle/service/psc/time/shared_memory.h" +#include "core/hle/service/psc/time/static.h" +#include "core/hle/service/psc/time/steady_clock.h" +#include "core/hle/service/psc/time/system_clock.h" +#include "core/hle/service/psc/time/time_zone.h" +#include "core/hle/service/psc/time/time_zone_service.h" + +namespace Service::PSC::Time { +namespace { +constexpr Result GetTimeFromTimePointAndContext(s64* out_time, SteadyClockTimePoint& time_point, + SystemClockContext& context) { + R_UNLESS(out_time != nullptr, ResultInvalidArgument); + R_UNLESS(time_point.IdMatches(context.steady_time_point), ResultClockMismatch); + + *out_time = context.offset + time_point.time_point; + R_SUCCEED(); +} +} // namespace + +StaticService::StaticService(Core::System& system_, StaticServiceSetupInfo setup_info, + std::shared_ptr<TimeManager> time, const char* name) + : ServiceFramework{system_, name}, m_system{system}, m_setup_info{setup_info}, m_time{time}, + m_local_system_clock{m_time->m_standard_local_system_clock}, + m_user_system_clock{m_time->m_standard_user_system_clock}, + m_network_system_clock{m_time->m_standard_network_system_clock}, + m_time_zone{m_time->m_time_zone}, + m_ephemeral_network_clock{m_time->m_ephemeral_network_clock}, m_shared_memory{ + m_time->m_shared_memory} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &StaticService::Handle_GetStandardUserSystemClock, "GetStandardUserSystemClock"}, + {1, &StaticService::Handle_GetStandardNetworkSystemClock, "GetStandardNetworkSystemClock"}, + {2, &StaticService::Handle_GetStandardSteadyClock, "GetStandardSteadyClock"}, + {3, &StaticService::Handle_GetTimeZoneService, "GetTimeZoneService"}, + {4, &StaticService::Handle_GetStandardLocalSystemClock, "GetStandardLocalSystemClock"}, + {5, &StaticService::Handle_GetEphemeralNetworkSystemClock, "GetEphemeralNetworkSystemClock"}, + {20, &StaticService::Handle_GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"}, + {50, &StaticService::Handle_SetStandardSteadyClockInternalOffset, "SetStandardSteadyClockInternalOffset"}, + {51, &StaticService::Handle_GetStandardSteadyClockRtcValue, "GetStandardSteadyClockRtcValue"}, + {100, &StaticService::Handle_IsStandardUserSystemClockAutomaticCorrectionEnabled, "IsStandardUserSystemClockAutomaticCorrectionEnabled"}, + {101, &StaticService::Handle_SetStandardUserSystemClockAutomaticCorrectionEnabled, "SetStandardUserSystemClockAutomaticCorrectionEnabled"}, + {102, &StaticService::Handle_GetStandardUserSystemClockInitialYear, "GetStandardUserSystemClockInitialYear"}, + {200, &StaticService::Handle_IsStandardNetworkSystemClockAccuracySufficient, "IsStandardNetworkSystemClockAccuracySufficient"}, + {201, &StaticService::Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedTime, "GetStandardUserSystemClockAutomaticCorrectionUpdatedTime"}, + {300, &StaticService::Handle_CalculateMonotonicSystemClockBaseTimePoint, "CalculateMonotonicSystemClockBaseTimePoint"}, + {400, &StaticService::Handle_GetClockSnapshot, "GetClockSnapshot"}, + {401, &StaticService::Handle_GetClockSnapshotFromSystemClockContext, "GetClockSnapshotFromSystemClockContext"}, + {500, &StaticService::Handle_CalculateStandardUserSystemClockDifferenceByUser, "CalculateStandardUserSystemClockDifferenceByUser"}, + {501, &StaticService::Handle_CalculateSpanBetween, "CalculateSpanBetween"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +Result StaticService::GetClockSnapshotImpl(ClockSnapshot& out_snapshot, + SystemClockContext& user_context, + SystemClockContext& network_context, TimeType type) { + out_snapshot.user_context = user_context; + out_snapshot.network_context = network_context; + + R_TRY( + m_time->m_standard_steady_clock.GetCurrentTimePoint(out_snapshot.steady_clock_time_point)); + + out_snapshot.is_automatic_correction_enabled = m_user_system_clock.GetAutomaticCorrection(); + + R_TRY(m_time_zone.GetLocationName(out_snapshot.location_name)); + + R_TRY(GetTimeFromTimePointAndContext( + &out_snapshot.user_time, out_snapshot.steady_clock_time_point, out_snapshot.user_context)); + + R_TRY(m_time_zone.ToCalendarTimeWithMyRule(out_snapshot.user_calendar_time, + out_snapshot.user_calendar_additional_time, + out_snapshot.user_time)); + + if (GetTimeFromTimePointAndContext(&out_snapshot.network_time, + out_snapshot.steady_clock_time_point, + out_snapshot.network_context) != ResultSuccess) { + out_snapshot.network_time = 0; + } + + R_TRY(m_time_zone.ToCalendarTimeWithMyRule(out_snapshot.network_calendar_time, + out_snapshot.network_calendar_additional_time, + out_snapshot.network_time)); + out_snapshot.type = type; + out_snapshot.unk_CE = 0; + R_SUCCEED(); +} + +void StaticService::Handle_GetStandardUserSystemClock(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr<SystemClock> service{}; + auto res = GetStandardUserSystemClock(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface<SystemClock>(std::move(service)); +} + +void StaticService::Handle_GetStandardNetworkSystemClock(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr<SystemClock> service{}; + auto res = GetStandardNetworkSystemClock(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface<SystemClock>(std::move(service)); +} + +void StaticService::Handle_GetStandardSteadyClock(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr<SteadyClock> service{}; + auto res = GetStandardSteadyClock(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface(std::move(service)); +} + +void StaticService::Handle_GetTimeZoneService(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr<TimeZoneService> service{}; + auto res = GetTimeZoneService(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface(std::move(service)); +} + +void StaticService::Handle_GetStandardLocalSystemClock(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr<SystemClock> service{}; + auto res = GetStandardLocalSystemClock(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface<SystemClock>(std::move(service)); +} + +void StaticService::Handle_GetEphemeralNetworkSystemClock(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr<SystemClock> service{}; + auto res = GetEphemeralNetworkSystemClock(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface<SystemClock>(std::move(service)); +} + +void StaticService::Handle_GetSharedMemoryNativeHandle(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + Kernel::KSharedMemory* shared_memory{}; + auto res = GetSharedMemoryNativeHandle(&shared_memory); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(res); + rb.PushCopyObjects(shared_memory); +} + +void StaticService::Handle_SetStandardSteadyClockInternalOffset(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(m_setup_info.can_write_steady_clock ? ResultNotImplemented : ResultPermissionDenied); +} + +void StaticService::Handle_GetStandardSteadyClockRtcValue(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultNotImplemented); +} + +void StaticService::Handle_IsStandardUserSystemClockAutomaticCorrectionEnabled( + HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + bool is_enabled{}; + auto res = IsStandardUserSystemClockAutomaticCorrectionEnabled(is_enabled); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.Push<bool>(is_enabled); +} + +void StaticService::Handle_SetStandardUserSystemClockAutomaticCorrectionEnabled( + HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto automatic_correction{rp.Pop<bool>()}; + + auto res = SetStandardUserSystemClockAutomaticCorrectionEnabled(automatic_correction); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void StaticService::Handle_GetStandardUserSystemClockInitialYear(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultNotImplemented); +} + +void StaticService::Handle_IsStandardNetworkSystemClockAccuracySufficient(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + bool is_sufficient{}; + auto res = IsStandardNetworkSystemClockAccuracySufficient(is_sufficient); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.Push<bool>(is_sufficient); +} + +void StaticService::Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedTime( + HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + SteadyClockTimePoint time_point{}; + auto res = GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(time_point); + + IPC::ResponseBuilder rb{ctx, 2 + sizeof(SteadyClockTimePoint) / sizeof(u32)}; + rb.Push(res); + rb.PushRaw<SteadyClockTimePoint>(time_point); +} + +void StaticService::Handle_CalculateMonotonicSystemClockBaseTimePoint(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto context{rp.PopRaw<SystemClockContext>()}; + + s64 time{}; + auto res = CalculateMonotonicSystemClockBaseTimePoint(time, context); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(res); + rb.Push<s64>(time); +} + +void StaticService::Handle_GetClockSnapshot(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto type{rp.PopEnum<TimeType>()}; + + ClockSnapshot snapshot{}; + auto res = GetClockSnapshot(snapshot, type); + + ctx.WriteBuffer(snapshot); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void StaticService::Handle_GetClockSnapshotFromSystemClockContext(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto clock_type{rp.PopEnum<TimeType>()}; + [[maybe_unused]] auto alignment{rp.Pop<u32>()}; + auto user_context{rp.PopRaw<SystemClockContext>()}; + auto network_context{rp.PopRaw<SystemClockContext>()}; + + ClockSnapshot snapshot{}; + auto res = + GetClockSnapshotFromSystemClockContext(snapshot, user_context, network_context, clock_type); + + ctx.WriteBuffer(snapshot); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void StaticService::Handle_CalculateStandardUserSystemClockDifferenceByUser( + HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + ClockSnapshot a{}; + ClockSnapshot b{}; + + auto a_buffer{ctx.ReadBuffer(0)}; + auto b_buffer{ctx.ReadBuffer(1)}; + + std::memcpy(&a, a_buffer.data(), sizeof(ClockSnapshot)); + std::memcpy(&b, b_buffer.data(), sizeof(ClockSnapshot)); + + s64 difference{}; + auto res = CalculateStandardUserSystemClockDifferenceByUser(difference, a, b); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(res); + rb.Push(difference); +} + +void StaticService::Handle_CalculateSpanBetween(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + ClockSnapshot a{}; + ClockSnapshot b{}; + + auto a_buffer{ctx.ReadBuffer(0)}; + auto b_buffer{ctx.ReadBuffer(1)}; + + std::memcpy(&a, a_buffer.data(), sizeof(ClockSnapshot)); + std::memcpy(&b, b_buffer.data(), sizeof(ClockSnapshot)); + + s64 time{}; + auto res = CalculateSpanBetween(time, a, b); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(res); + rb.Push(time); +} + +// =============================== Implementations =========================== + +Result StaticService::GetStandardUserSystemClock(std::shared_ptr<SystemClock>& out_service) { + out_service = std::make_shared<SystemClock>(m_system, m_user_system_clock, + m_setup_info.can_write_user_clock, + m_setup_info.can_write_uninitialized_clock); + R_SUCCEED(); +} + +Result StaticService::GetStandardNetworkSystemClock(std::shared_ptr<SystemClock>& out_service) { + out_service = std::make_shared<SystemClock>(m_system, m_network_system_clock, + m_setup_info.can_write_network_clock, + m_setup_info.can_write_uninitialized_clock); + R_SUCCEED(); +} + +Result StaticService::GetStandardSteadyClock(std::shared_ptr<SteadyClock>& out_service) { + out_service = + std::make_shared<SteadyClock>(m_system, m_time, m_setup_info.can_write_steady_clock, + m_setup_info.can_write_uninitialized_clock); + R_SUCCEED(); +} + +Result StaticService::GetTimeZoneService(std::shared_ptr<TimeZoneService>& out_service) { + out_service = + std::make_shared<TimeZoneService>(m_system, m_time->m_standard_steady_clock, m_time_zone, + m_setup_info.can_write_timezone_device_location); + R_SUCCEED(); +} + +Result StaticService::GetStandardLocalSystemClock(std::shared_ptr<SystemClock>& out_service) { + out_service = std::make_shared<SystemClock>(m_system, m_local_system_clock, + m_setup_info.can_write_local_clock, + m_setup_info.can_write_uninitialized_clock); + R_SUCCEED(); +} + +Result StaticService::GetEphemeralNetworkSystemClock(std::shared_ptr<SystemClock>& out_service) { + out_service = std::make_shared<SystemClock>(m_system, m_ephemeral_network_clock, + m_setup_info.can_write_network_clock, + m_setup_info.can_write_uninitialized_clock); + R_SUCCEED(); +} + +Result StaticService::GetSharedMemoryNativeHandle(Kernel::KSharedMemory** out_shared_memory) { + *out_shared_memory = &m_shared_memory.GetKSharedMemory(); + R_SUCCEED(); +} + +Result StaticService::IsStandardUserSystemClockAutomaticCorrectionEnabled(bool& out_is_enabled) { + R_UNLESS(m_user_system_clock.IsInitialized(), ResultClockUninitialized); + + out_is_enabled = m_user_system_clock.GetAutomaticCorrection(); + R_SUCCEED(); +} + +Result StaticService::SetStandardUserSystemClockAutomaticCorrectionEnabled( + bool automatic_correction) { + R_UNLESS(m_user_system_clock.IsInitialized() && m_time->m_standard_steady_clock.IsInitialized(), + ResultClockUninitialized); + R_UNLESS(m_setup_info.can_write_user_clock, ResultPermissionDenied); + + R_TRY(m_user_system_clock.SetAutomaticCorrection(automatic_correction)); + + m_shared_memory.SetAutomaticCorrection(automatic_correction); + + SteadyClockTimePoint time_point{}; + R_TRY(m_time->m_standard_steady_clock.GetCurrentTimePoint(time_point)); + + m_user_system_clock.SetTimePointAndSignal(time_point); + m_user_system_clock.GetEvent().Signal(); + R_SUCCEED(); +} + +Result StaticService::IsStandardNetworkSystemClockAccuracySufficient(bool& out_is_sufficient) { + out_is_sufficient = m_network_system_clock.IsAccuracySufficient(); + R_SUCCEED(); +} + +Result StaticService::GetStandardUserSystemClockAutomaticCorrectionUpdatedTime( + SteadyClockTimePoint& out_time_point) { + R_UNLESS(m_user_system_clock.IsInitialized(), ResultClockUninitialized); + + m_user_system_clock.GetTimePoint(out_time_point); + + R_SUCCEED(); +} + +Result StaticService::CalculateMonotonicSystemClockBaseTimePoint(s64& out_time, + SystemClockContext& context) { + R_UNLESS(m_time->m_standard_steady_clock.IsInitialized(), ResultClockUninitialized); + + SteadyClockTimePoint time_point{}; + R_TRY(m_time->m_standard_steady_clock.GetCurrentTimePoint(time_point)); + + R_UNLESS(time_point.IdMatches(context.steady_time_point), ResultClockMismatch); + + auto one_second_ns{ + std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()}; + auto ticks{m_system.CoreTiming().GetClockTicks()}; + auto current_time{ConvertToTimeSpan(ticks).count()}; + out_time = ((context.offset + time_point.time_point) - (current_time / one_second_ns)); + R_SUCCEED(); +} + +Result StaticService::GetClockSnapshot(ClockSnapshot& out_snapshot, TimeType type) { + SystemClockContext user_context{}; + R_TRY(m_user_system_clock.GetContext(user_context)); + + SystemClockContext network_context{}; + R_TRY(m_network_system_clock.GetContext(network_context)); + + R_RETURN(GetClockSnapshotImpl(out_snapshot, user_context, network_context, type)); +} + +Result StaticService::GetClockSnapshotFromSystemClockContext(ClockSnapshot& out_snapshot, + SystemClockContext& user_context, + SystemClockContext& network_context, + TimeType type) { + R_RETURN(GetClockSnapshotImpl(out_snapshot, user_context, network_context, type)); +} + +Result StaticService::CalculateStandardUserSystemClockDifferenceByUser(s64& out_time, + ClockSnapshot& a, + ClockSnapshot& b) { + auto diff_s = + std::chrono::seconds(b.user_context.offset) - std::chrono::seconds(a.user_context.offset); + + if (a.user_context == b.user_context || + !a.user_context.steady_time_point.IdMatches(b.user_context.steady_time_point)) { + out_time = 0; + R_SUCCEED(); + } + + if (!a.is_automatic_correction_enabled || !b.is_automatic_correction_enabled) { + out_time = std::chrono::duration_cast<std::chrono::nanoseconds>(diff_s).count(); + R_SUCCEED(); + } + + if (a.network_context.steady_time_point.IdMatches(a.steady_clock_time_point) || + b.network_context.steady_time_point.IdMatches(b.steady_clock_time_point)) { + out_time = 0; + R_SUCCEED(); + } + + out_time = std::chrono::duration_cast<std::chrono::nanoseconds>(diff_s).count(); + R_SUCCEED(); +} + +Result StaticService::CalculateSpanBetween(s64& out_time, ClockSnapshot& a, ClockSnapshot& b) { + s64 time_s{}; + auto res = + GetSpanBetweenTimePoints(&time_s, a.steady_clock_time_point, b.steady_clock_time_point); + + if (res != ResultSuccess) { + R_UNLESS(a.network_time != 0 && b.network_time != 0, ResultTimeNotFound); + time_s = b.network_time - a.network_time; + } + + out_time = + std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(time_s)).count(); + R_SUCCEED(); +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/static.h b/src/core/hle/service/psc/time/static.h new file mode 100644 index 000000000..498cd5ab5 --- /dev/null +++ b/src/core/hle/service/psc/time/static.h @@ -0,0 +1,95 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/psc/time/common.h" +#include "core/hle/service/server_manager.h" +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Kernel { +class KSharedMemory; +} + +namespace Service::PSC::Time { +class TimeManager; +class StandardLocalSystemClockCore; +class StandardUserSystemClockCore; +class StandardNetworkSystemClockCore; +class TimeZone; +class SystemClock; +class SteadyClock; +class TimeZoneService; +class EphemeralNetworkSystemClockCore; +class SharedMemory; + +class StaticService final : public ServiceFramework<StaticService> { +public: + explicit StaticService(Core::System& system, StaticServiceSetupInfo setup_info, + std::shared_ptr<TimeManager> time, const char* name); + + ~StaticService() override = default; + + Result GetStandardUserSystemClock(std::shared_ptr<SystemClock>& out_service); + Result GetStandardNetworkSystemClock(std::shared_ptr<SystemClock>& out_service); + Result GetStandardSteadyClock(std::shared_ptr<SteadyClock>& out_service); + Result GetTimeZoneService(std::shared_ptr<TimeZoneService>& out_service); + Result GetStandardLocalSystemClock(std::shared_ptr<SystemClock>& out_service); + Result GetEphemeralNetworkSystemClock(std::shared_ptr<SystemClock>& out_service); + Result GetSharedMemoryNativeHandle(Kernel::KSharedMemory** out_shared_memory); + Result IsStandardUserSystemClockAutomaticCorrectionEnabled(bool& out_is_enabled); + Result SetStandardUserSystemClockAutomaticCorrectionEnabled(bool automatic_correction); + Result IsStandardNetworkSystemClockAccuracySufficient(bool& out_is_sufficient); + Result GetStandardUserSystemClockAutomaticCorrectionUpdatedTime( + SteadyClockTimePoint& out_time_point); + Result CalculateMonotonicSystemClockBaseTimePoint(s64& out_time, SystemClockContext& context); + Result GetClockSnapshot(ClockSnapshot& out_snapshot, TimeType type); + Result GetClockSnapshotFromSystemClockContext(ClockSnapshot& out_snapshot, + SystemClockContext& user_context, + SystemClockContext& network_context, + TimeType type); + Result CalculateStandardUserSystemClockDifferenceByUser(s64& out_time, ClockSnapshot& a, + ClockSnapshot& b); + Result CalculateSpanBetween(s64& out_time, ClockSnapshot& a, ClockSnapshot& b); + +private: + Result GetClockSnapshotImpl(ClockSnapshot& out_snapshot, SystemClockContext& user_context, + SystemClockContext& network_context, TimeType type); + + void Handle_GetStandardUserSystemClock(HLERequestContext& ctx); + void Handle_GetStandardNetworkSystemClock(HLERequestContext& ctx); + void Handle_GetStandardSteadyClock(HLERequestContext& ctx); + void Handle_GetTimeZoneService(HLERequestContext& ctx); + void Handle_GetStandardLocalSystemClock(HLERequestContext& ctx); + void Handle_GetEphemeralNetworkSystemClock(HLERequestContext& ctx); + void Handle_GetSharedMemoryNativeHandle(HLERequestContext& ctx); + void Handle_SetStandardSteadyClockInternalOffset(HLERequestContext& ctx); + void Handle_GetStandardSteadyClockRtcValue(HLERequestContext& ctx); + void Handle_IsStandardUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx); + void Handle_SetStandardUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx); + void Handle_GetStandardUserSystemClockInitialYear(HLERequestContext& ctx); + void Handle_IsStandardNetworkSystemClockAccuracySufficient(HLERequestContext& ctx); + void Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(HLERequestContext& ctx); + void Handle_CalculateMonotonicSystemClockBaseTimePoint(HLERequestContext& ctx); + void Handle_GetClockSnapshot(HLERequestContext& ctx); + void Handle_GetClockSnapshotFromSystemClockContext(HLERequestContext& ctx); + void Handle_CalculateStandardUserSystemClockDifferenceByUser(HLERequestContext& ctx); + void Handle_CalculateSpanBetween(HLERequestContext& ctx); + + Core::System& m_system; + StaticServiceSetupInfo m_setup_info; + std::shared_ptr<TimeManager> m_time; + StandardLocalSystemClockCore& m_local_system_clock; + StandardUserSystemClockCore& m_user_system_clock; + StandardNetworkSystemClockCore& m_network_system_clock; + TimeZone& m_time_zone; + EphemeralNetworkSystemClockCore& m_ephemeral_network_clock; + SharedMemory& m_shared_memory; +}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/steady_clock.cpp b/src/core/hle/service/psc/time/steady_clock.cpp new file mode 100644 index 000000000..1ed5c7679 --- /dev/null +++ b/src/core/hle/service/psc/time/steady_clock.cpp @@ -0,0 +1,164 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/service/psc/time/steady_clock.h" + +namespace Service::PSC::Time { + +SteadyClock::SteadyClock(Core::System& system_, std::shared_ptr<TimeManager> manager, + bool can_write_steady_clock, bool can_write_uninitialized_clock) + : ServiceFramework{system_, "ISteadyClock"}, m_system{system}, + m_clock_core{manager->m_standard_steady_clock}, + m_can_write_steady_clock{can_write_steady_clock}, m_can_write_uninitialized_clock{ + can_write_uninitialized_clock} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &SteadyClock::Handle_GetCurrentTimePoint, "GetCurrentTimePoint"}, + {2, &SteadyClock::Handle_GetTestOffset, "GetTestOffset"}, + {3, &SteadyClock::Handle_SetTestOffset, "SetTestOffset"}, + {100, &SteadyClock::Handle_GetRtcValue, "GetRtcValue"}, + {101, &SteadyClock::Handle_IsRtcResetDetected, "IsRtcResetDetected"}, + {102, &SteadyClock::Handle_GetSetupResultValue, "GetSetupResultValue"}, + {200, &SteadyClock::Handle_GetInternalOffset, "GetInternalOffset"}, + }; + // clang-format on + RegisterHandlers(functions); +} + +void SteadyClock::Handle_GetCurrentTimePoint(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + SteadyClockTimePoint time_point{}; + auto res = GetCurrentTimePoint(time_point); + + IPC::ResponseBuilder rb{ctx, 2 + sizeof(SteadyClockTimePoint) / sizeof(u32)}; + rb.Push(res); + rb.PushRaw<SteadyClockTimePoint>(time_point); +} + +void SteadyClock::Handle_GetTestOffset(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + s64 test_offset{}; + auto res = GetTestOffset(test_offset); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(res); + rb.Push(test_offset); +} + +void SteadyClock::Handle_SetTestOffset(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto test_offset{rp.Pop<s64>()}; + + auto res = SetTestOffset(test_offset); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void SteadyClock::Handle_GetRtcValue(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + s64 rtc_value{}; + auto res = GetRtcValue(rtc_value); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(res); + rb.Push(rtc_value); +} + +void SteadyClock::Handle_IsRtcResetDetected(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + bool reset_detected{false}; + auto res = IsRtcResetDetected(reset_detected); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.Push(reset_detected); +} + +void SteadyClock::Handle_GetSetupResultValue(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + Result result_value{ResultSuccess}; + auto res = GetSetupResultValue(result_value); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.Push(result_value); +} + +void SteadyClock::Handle_GetInternalOffset(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + s64 internal_offset{}; + auto res = GetInternalOffset(internal_offset); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(res); + rb.Push(internal_offset); +} + +// =============================== Implementations =========================== + +Result SteadyClock::GetCurrentTimePoint(SteadyClockTimePoint& out_time_point) { + R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), + ResultClockUninitialized); + + R_RETURN(m_clock_core.GetCurrentTimePoint(out_time_point)); +} + +Result SteadyClock::GetTestOffset(s64& out_test_offset) { + R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), + ResultClockUninitialized); + + out_test_offset = m_clock_core.GetTestOffset(); + R_SUCCEED(); +} + +Result SteadyClock::SetTestOffset(s64 test_offset) { + R_UNLESS(m_can_write_steady_clock, ResultPermissionDenied); + R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), + ResultClockUninitialized); + + m_clock_core.SetTestOffset(test_offset); + R_SUCCEED(); +} + +Result SteadyClock::GetRtcValue(s64& out_rtc_value) { + R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), + ResultClockUninitialized); + + R_RETURN(m_clock_core.GetRtcValue(out_rtc_value)); +} + +Result SteadyClock::IsRtcResetDetected(bool& out_is_detected) { + R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), + ResultClockUninitialized); + + out_is_detected = m_clock_core.IsResetDetected(); + R_SUCCEED(); +} + +Result SteadyClock::GetSetupResultValue(Result& out_result) { + R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), + ResultClockUninitialized); + + out_result = m_clock_core.GetSetupResultValue(); + R_SUCCEED(); +} + +Result SteadyClock::GetInternalOffset(s64& out_internal_offset) { + R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), + ResultClockUninitialized); + + out_internal_offset = m_clock_core.GetInternalOffset(); + R_SUCCEED(); +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/steady_clock.h b/src/core/hle/service/psc/time/steady_clock.h new file mode 100644 index 000000000..115e9b138 --- /dev/null +++ b/src/core/hle/service/psc/time/steady_clock.h @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/psc/time/common.h" +#include "core/hle/service/psc/time/manager.h" +#include "core/hle/service/server_manager.h" +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Service::PSC::Time { + +class SteadyClock final : public ServiceFramework<SteadyClock> { +public: + explicit SteadyClock(Core::System& system, std::shared_ptr<TimeManager> manager, + bool can_write_steady_clock, bool can_write_uninitialized_clock); + + ~SteadyClock() override = default; + + Result GetCurrentTimePoint(SteadyClockTimePoint& out_time_point); + Result GetTestOffset(s64& out_test_offset); + Result SetTestOffset(s64 test_offset); + Result GetRtcValue(s64& out_rtc_value); + Result IsRtcResetDetected(bool& out_is_detected); + Result GetSetupResultValue(Result& out_result); + Result GetInternalOffset(s64& out_internal_offset); + +private: + void Handle_GetCurrentTimePoint(HLERequestContext& ctx); + void Handle_GetTestOffset(HLERequestContext& ctx); + void Handle_SetTestOffset(HLERequestContext& ctx); + void Handle_GetRtcValue(HLERequestContext& ctx); + void Handle_IsRtcResetDetected(HLERequestContext& ctx); + void Handle_GetSetupResultValue(HLERequestContext& ctx); + void Handle_GetInternalOffset(HLERequestContext& ctx); + + Core::System& m_system; + + StandardSteadyClockCore& m_clock_core; + bool m_can_write_steady_clock; + bool m_can_write_uninitialized_clock; +}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/system_clock.cpp b/src/core/hle/service/psc/time/system_clock.cpp new file mode 100644 index 000000000..13d2f1d11 --- /dev/null +++ b/src/core/hle/service/psc/time/system_clock.cpp @@ -0,0 +1,127 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/service/psc/time/system_clock.h" + +namespace Service::PSC::Time { + +SystemClock::SystemClock(Core::System& system_, SystemClockCore& clock_core, bool can_write_clock, + bool can_write_uninitialized_clock) + : ServiceFramework{system_, "ISystemClock"}, m_system{system}, m_clock_core{clock_core}, + m_can_write_clock{can_write_clock}, m_can_write_uninitialized_clock{ + can_write_uninitialized_clock} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &SystemClock::Handle_GetCurrentTime, "GetCurrentTime"}, + {1, &SystemClock::Handle_SetCurrentTime, "SetCurrentTime"}, + {2, &SystemClock::Handle_GetSystemClockContext, "GetSystemClockContext"}, + {3, &SystemClock::Handle_SetSystemClockContext, "SetSystemClockContext"}, + {4, &SystemClock::Handle_GetOperationEventReadableHandle, "GetOperationEventReadableHandle"}, + }; + // clang-format on + RegisterHandlers(functions); +} + +void SystemClock::Handle_GetCurrentTime(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + s64 time{}; + auto res = GetCurrentTime(time); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(res); + rb.Push<s64>(time); +} + +void SystemClock::Handle_SetCurrentTime(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto time{rp.Pop<s64>()}; + + auto res = SetCurrentTime(time); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void SystemClock::Handle_GetSystemClockContext(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + SystemClockContext context{}; + auto res = GetSystemClockContext(context); + + IPC::ResponseBuilder rb{ctx, 2 + sizeof(SystemClockContext) / sizeof(u32)}; + rb.Push(res); + rb.PushRaw<SystemClockContext>(context); +} + +void SystemClock::Handle_SetSystemClockContext(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto context{rp.PopRaw<SystemClockContext>()}; + + auto res = SetSystemClockContext(context); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void SystemClock::Handle_GetOperationEventReadableHandle(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + Kernel::KEvent* event{}; + auto res = GetOperationEventReadableHandle(&event); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(res); + rb.PushCopyObjects(event->GetReadableEvent()); +} + +// =============================== Implementations =========================== + +Result SystemClock::GetCurrentTime(s64& out_time) { + R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), + ResultClockUninitialized); + + R_RETURN(m_clock_core.GetCurrentTime(&out_time)); +} + +Result SystemClock::SetCurrentTime(s64 time) { + R_UNLESS(m_can_write_clock, ResultPermissionDenied); + R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), + ResultClockUninitialized); + + R_RETURN(m_clock_core.SetCurrentTime(time)); +} + +Result SystemClock::GetSystemClockContext(SystemClockContext& out_context) { + R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), + ResultClockUninitialized); + + R_RETURN(m_clock_core.GetContext(out_context)); +} + +Result SystemClock::SetSystemClockContext(SystemClockContext& context) { + R_UNLESS(m_can_write_clock, ResultPermissionDenied); + R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), + ResultClockUninitialized); + + R_RETURN(m_clock_core.SetContextAndWrite(context)); +} + +Result SystemClock::GetOperationEventReadableHandle(Kernel::KEvent** out_event) { + if (!m_operation_event) { + m_operation_event = std::make_unique<OperationEvent>(m_system); + R_UNLESS(m_operation_event != nullptr, ResultFailed); + + m_clock_core.LinkOperationEvent(*m_operation_event); + } + + *out_event = m_operation_event->m_event; + R_SUCCEED(); +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/system_clock.h b/src/core/hle/service/psc/time/system_clock.h new file mode 100644 index 000000000..f30027e7b --- /dev/null +++ b/src/core/hle/service/psc/time/system_clock.h @@ -0,0 +1,46 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/psc/time/common.h" +#include "core/hle/service/psc/time/manager.h" +#include "core/hle/service/server_manager.h" +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Service::PSC::Time { + +class SystemClock final : public ServiceFramework<SystemClock> { +public: + explicit SystemClock(Core::System& system, SystemClockCore& system_clock_core, + bool can_write_clock, bool can_write_uninitialized_clock); + + ~SystemClock() override = default; + + Result GetCurrentTime(s64& out_time); + Result SetCurrentTime(s64 time); + Result GetSystemClockContext(SystemClockContext& out_context); + Result SetSystemClockContext(SystemClockContext& context); + Result GetOperationEventReadableHandle(Kernel::KEvent** out_event); + +private: + void Handle_GetCurrentTime(HLERequestContext& ctx); + void Handle_SetCurrentTime(HLERequestContext& ctx); + void Handle_GetSystemClockContext(HLERequestContext& ctx); + void Handle_SetSystemClockContext(HLERequestContext& ctx); + void Handle_GetOperationEventReadableHandle(HLERequestContext& ctx); + + Core::System& m_system; + + SystemClockCore& m_clock_core; + bool m_can_write_clock; + bool m_can_write_uninitialized_clock; + std::unique_ptr<OperationEvent> m_operation_event{}; +}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/time_zone.cpp b/src/core/hle/service/psc/time/time_zone.cpp new file mode 100644 index 000000000..cfee8f866 --- /dev/null +++ b/src/core/hle/service/psc/time/time_zone.cpp @@ -0,0 +1,280 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/psc/time/time_zone.h" + +namespace Service::PSC::Time { +namespace { +constexpr Result ValidateRule(Tz::Rule& rule) { + if (rule.typecnt > static_cast<s32>(Tz::TZ_MAX_TYPES) || + rule.timecnt > static_cast<s32>(Tz::TZ_MAX_TIMES) || + rule.charcnt > static_cast<s32>(Tz::TZ_MAX_CHARS)) { + R_RETURN(ResultTimeZoneOutOfRange); + } + + for (s32 i = 0; i < rule.timecnt; i++) { + if (rule.types[i] >= rule.typecnt) { + R_RETURN(ResultTimeZoneOutOfRange); + } + } + + for (s32 i = 0; i < rule.typecnt; i++) { + if (rule.ttis[i].tt_desigidx >= static_cast<s32>(rule.chars.size())) { + R_RETURN(ResultTimeZoneOutOfRange); + } + } + R_SUCCEED(); +} + +constexpr bool GetTimeZoneTime(s64& out_time, Tz::Rule& rule, s64 time, s32 index, + s32 index_offset) { + s32 found_idx{}; + s32 expected_index{index + index_offset}; + s64 time_to_find{time + rule.ttis[rule.types[index]].tt_utoff - + rule.ttis[rule.types[expected_index]].tt_utoff}; + + if (rule.timecnt > 1 && rule.ats[0] <= time_to_find) { + s32 low{1}; + s32 high{rule.timecnt}; + + while (low < high) { + auto mid{(low + high) / 2}; + if (rule.ats[mid] <= time_to_find) { + low = mid + 1; + } else if (rule.ats[mid] > time_to_find) { + high = mid; + } + } + found_idx = low - 1; + } + + if (found_idx == expected_index) { + out_time = time_to_find; + } + return found_idx == expected_index; +} +} // namespace + +void TimeZone::SetTimePoint(SteadyClockTimePoint& time_point) { + std::scoped_lock l{m_mutex}; + m_steady_clock_time_point = time_point; +} + +void TimeZone::SetTotalLocationNameCount(u32 count) { + std::scoped_lock l{m_mutex}; + m_total_location_name_count = count; +} + +void TimeZone::SetRuleVersion(RuleVersion& rule_version) { + std::scoped_lock l{m_mutex}; + m_rule_version = rule_version; +} + +Result TimeZone::GetLocationName(LocationName& out_name) { + std::scoped_lock l{m_mutex}; + R_UNLESS(m_initialized, ResultClockUninitialized); + out_name = m_location; + R_SUCCEED(); +} + +Result TimeZone::GetTotalLocationCount(u32& out_count) { + std::scoped_lock l{m_mutex}; + if (!m_initialized) { + return ResultClockUninitialized; + } + + out_count = m_total_location_name_count; + R_SUCCEED(); +} + +Result TimeZone::GetRuleVersion(RuleVersion& out_rule_version) { + std::scoped_lock l{m_mutex}; + if (!m_initialized) { + return ResultClockUninitialized; + } + out_rule_version = m_rule_version; + R_SUCCEED(); +} + +Result TimeZone::GetTimePoint(SteadyClockTimePoint& out_time_point) { + std::scoped_lock l{m_mutex}; + if (!m_initialized) { + return ResultClockUninitialized; + } + out_time_point = m_steady_clock_time_point; + R_SUCCEED(); +} + +Result TimeZone::ToCalendarTime(CalendarTime& out_calendar_time, + CalendarAdditionalInfo& out_additional_info, s64 time, + Tz::Rule& rule) { + std::scoped_lock l{m_mutex}; + R_RETURN(ToCalendarTimeImpl(out_calendar_time, out_additional_info, time, rule)); +} + +Result TimeZone::ToCalendarTimeWithMyRule(CalendarTime& calendar_time, + CalendarAdditionalInfo& calendar_additional, s64 time) { + // This is checked outside the mutex. Bug? + if (!m_initialized) { + return ResultClockUninitialized; + } + + std::scoped_lock l{m_mutex}; + R_RETURN(ToCalendarTimeImpl(calendar_time, calendar_additional, time, m_my_rule)); +} + +Result TimeZone::ParseBinary(LocationName& name, std::span<const u8> binary) { + std::scoped_lock l{m_mutex}; + + Tz::Rule tmp_rule{}; + R_TRY(ParseBinaryImpl(tmp_rule, binary)); + + m_my_rule = tmp_rule; + m_location = name; + + R_SUCCEED(); +} + +Result TimeZone::ParseBinaryInto(Tz::Rule& out_rule, std::span<const u8> binary) { + std::scoped_lock l{m_mutex}; + R_RETURN(ParseBinaryImpl(out_rule, binary)); +} + +Result TimeZone::ToPosixTime(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count, + CalendarTime& calendar, Tz::Rule& rule) { + std::scoped_lock l{m_mutex}; + + auto res = ToPosixTimeImpl(out_count, out_times, out_times_count, calendar, rule, -1); + + if (res != ResultSuccess) { + if (res == ResultTimeZoneNotFound) { + res = ResultSuccess; + out_count = 0; + } + } else if (out_count == 2 && out_times[0] > out_times[1]) { + std::swap(out_times[0], out_times[1]); + } + R_RETURN(res); +} + +Result TimeZone::ToPosixTimeWithMyRule(u32& out_count, std::span<s64, 2> out_times, + u32 out_times_count, CalendarTime& calendar) { + std::scoped_lock l{m_mutex}; + + auto res = ToPosixTimeImpl(out_count, out_times, out_times_count, calendar, m_my_rule, -1); + + if (res != ResultSuccess) { + if (res == ResultTimeZoneNotFound) { + res = ResultSuccess; + out_count = 0; + } + } else if (out_count == 2 && out_times[0] > out_times[1]) { + std::swap(out_times[0], out_times[1]); + } + R_RETURN(res); +} + +Result TimeZone::ParseBinaryImpl(Tz::Rule& out_rule, std::span<const u8> binary) { + if (Tz::ParseTimeZoneBinary(out_rule, binary)) { + R_RETURN(ResultTimeZoneParseFailed); + } + R_SUCCEED(); +} + +Result TimeZone::ToCalendarTimeImpl(CalendarTime& out_calendar_time, + CalendarAdditionalInfo& out_additional_info, s64 time, + Tz::Rule& rule) { + R_TRY(ValidateRule(rule)); + + Tz::CalendarTimeInternal calendar_internal{}; + time_t time_tmp{static_cast<time_t>(time)}; + if (Tz::localtime_rz(&calendar_internal, &rule, &time_tmp)) { + R_RETURN(ResultOverflow); + } + + out_calendar_time.year = static_cast<s16>(calendar_internal.tm_year + 1900); + out_calendar_time.month = static_cast<s8>(calendar_internal.tm_mon + 1); + out_calendar_time.day = static_cast<s8>(calendar_internal.tm_mday); + out_calendar_time.hour = static_cast<s8>(calendar_internal.tm_hour); + out_calendar_time.minute = static_cast<s8>(calendar_internal.tm_min); + out_calendar_time.second = static_cast<s8>(calendar_internal.tm_sec); + + out_additional_info.day_of_week = calendar_internal.tm_wday; + out_additional_info.day_of_year = calendar_internal.tm_yday; + + std::memcpy(out_additional_info.name.data(), calendar_internal.tm_zone.data(), + out_additional_info.name.size()); + out_additional_info.name[out_additional_info.name.size() - 1] = '\0'; + + out_additional_info.is_dst = calendar_internal.tm_isdst; + out_additional_info.ut_offset = calendar_internal.tm_utoff; + + R_SUCCEED(); +} + +Result TimeZone::ToPosixTimeImpl(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count, + CalendarTime& calendar, Tz::Rule& rule, s32 is_dst) { + R_TRY(ValidateRule(rule)); + + calendar.month -= 1; + calendar.year -= 1900; + + Tz::CalendarTimeInternal internal{ + .tm_sec = calendar.second, + .tm_min = calendar.minute, + .tm_hour = calendar.hour, + .tm_mday = calendar.day, + .tm_mon = calendar.month, + .tm_year = calendar.year, + .tm_wday = 0, + .tm_yday = 0, + .tm_isdst = is_dst, + .tm_zone = {}, + .tm_utoff = 0, + .time_index = 0, + }; + time_t time_tmp{}; + auto res = Tz::mktime_tzname(&time_tmp, &rule, &internal); + s64 time = static_cast<s64>(time_tmp); + + if (res == 1) { + R_RETURN(ResultOverflow); + } else if (res == 2) { + R_RETURN(ResultTimeZoneNotFound); + } + + if (internal.tm_sec != calendar.second || internal.tm_min != calendar.minute || + internal.tm_hour != calendar.hour || internal.tm_mday != calendar.day || + internal.tm_mon != calendar.month || internal.tm_year != calendar.year) { + R_RETURN(ResultTimeZoneNotFound); + } + + if (res != 0) { + ASSERT(false); + } + + out_times[0] = time; + if (out_times_count < 2) { + out_count = 1; + R_SUCCEED(); + } + + s64 time2{}; + if (internal.time_index > 0 && GetTimeZoneTime(time2, rule, time, internal.time_index, -1)) { + out_times[1] = time2; + out_count = 2; + R_SUCCEED(); + } + + if (((internal.time_index + 1) < rule.timecnt) && + GetTimeZoneTime(time2, rule, time, internal.time_index, 1)) { + out_times[1] = time2; + out_count = 2; + R_SUCCEED(); + } + + out_count = 1; + R_SUCCEED(); +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/time_zone.h b/src/core/hle/service/psc/time/time_zone.h new file mode 100644 index 000000000..ce2acca17 --- /dev/null +++ b/src/core/hle/service/psc/time/time_zone.h @@ -0,0 +1,62 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <mutex> +#include <span> + +#include <tz/tz.h> +#include "core/hle/service/psc/time/common.h" + +namespace Service::PSC::Time { + +class TimeZone { +public: + TimeZone() = default; + + bool IsInitialized() const { + return m_initialized; + } + + void SetInitialized() { + m_initialized = true; + } + + void SetTimePoint(SteadyClockTimePoint& time_point); + void SetTotalLocationNameCount(u32 count); + void SetRuleVersion(RuleVersion& rule_version); + Result GetLocationName(LocationName& out_name); + Result GetTotalLocationCount(u32& out_count); + Result GetRuleVersion(RuleVersion& out_rule_version); + Result GetTimePoint(SteadyClockTimePoint& out_time_point); + + Result ToCalendarTime(CalendarTime& out_calendar_time, + CalendarAdditionalInfo& out_additional_info, s64 time, Tz::Rule& rule); + Result ToCalendarTimeWithMyRule(CalendarTime& calendar_time, + CalendarAdditionalInfo& calendar_additional, s64 time); + Result ParseBinary(LocationName& name, std::span<const u8> binary); + Result ParseBinaryInto(Tz::Rule& out_rule, std::span<const u8> binary); + Result ToPosixTime(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count, + CalendarTime& calendar, Tz::Rule& rule); + Result ToPosixTimeWithMyRule(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count, + CalendarTime& calendar); + +private: + Result ParseBinaryImpl(Tz::Rule& out_rule, std::span<const u8> binary); + Result ToCalendarTimeImpl(CalendarTime& out_calendar_time, + CalendarAdditionalInfo& out_additional_info, s64 time, + Tz::Rule& rule); + Result ToPosixTimeImpl(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count, + CalendarTime& calendar, Tz::Rule& rule, s32 is_dst); + + bool m_initialized{}; + std::recursive_mutex m_mutex; + LocationName m_location{}; + Tz::Rule m_my_rule{}; + SteadyClockTimePoint m_steady_clock_time_point{}; + u32 m_total_location_name_count{}; + RuleVersion m_rule_version{}; +}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/time_zone_service.cpp b/src/core/hle/service/psc/time/time_zone_service.cpp new file mode 100644 index 000000000..e304c8387 --- /dev/null +++ b/src/core/hle/service/psc/time/time_zone_service.cpp @@ -0,0 +1,289 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <tz/tz.h> +#include "core/core.h" +#include "core/hle/service/psc/time/time_zone_service.h" + +namespace Service::PSC::Time { + +TimeZoneService::TimeZoneService(Core::System& system_, StandardSteadyClockCore& clock_core, + TimeZone& time_zone, bool can_write_timezone_device_location) + : ServiceFramework{system_, "ITimeZoneService"}, m_system{system}, m_clock_core{clock_core}, + m_time_zone{time_zone}, m_can_write_timezone_device_location{ + can_write_timezone_device_location} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &TimeZoneService::Handle_GetDeviceLocationName, "GetDeviceLocationName"}, + {1, &TimeZoneService::Handle_SetDeviceLocationName, "SetDeviceLocationName"}, + {2, &TimeZoneService::Handle_GetTotalLocationNameCount, "GetTotalLocationNameCount"}, + {3, &TimeZoneService::Handle_LoadLocationNameList, "LoadLocationNameList"}, + {4, &TimeZoneService::Handle_LoadTimeZoneRule, "LoadTimeZoneRule"}, + {5, &TimeZoneService::Handle_GetTimeZoneRuleVersion, "GetTimeZoneRuleVersion"}, + {6, &TimeZoneService::Handle_GetDeviceLocationNameAndUpdatedTime, "GetDeviceLocationNameAndUpdatedTime"}, + {7, &TimeZoneService::Handle_SetDeviceLocationNameWithTimeZoneRule, "SetDeviceLocationNameWithTimeZoneRule"}, + {8, &TimeZoneService::Handle_ParseTimeZoneBinary, "ParseTimeZoneBinary"}, + {20, &TimeZoneService::Handle_GetDeviceLocationNameOperationEventReadableHandle, "GetDeviceLocationNameOperationEventReadableHandle"}, + {100, &TimeZoneService::Handle_ToCalendarTime, "ToCalendarTime"}, + {101, &TimeZoneService::Handle_ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"}, + {201, &TimeZoneService::Handle_ToPosixTime, "ToPosixTime"}, + {202, &TimeZoneService::Handle_ToPosixTimeWithMyRule, "ToPosixTimeWithMyRule"}, + }; + // clang-format on + RegisterHandlers(functions); +} + +void TimeZoneService::Handle_GetDeviceLocationName(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + LocationName name{}; + auto res = GetDeviceLocationName(name); + + IPC::ResponseBuilder rb{ctx, 2 + sizeof(LocationName) / sizeof(u32)}; + rb.Push(res); + rb.PushRaw<LocationName>(name); +} + +void TimeZoneService::Handle_SetDeviceLocationName(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + [[maybe_unused]] auto name{rp.PopRaw<LocationName>()}; + + if (!m_can_write_timezone_device_location) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultPermissionDenied); + return; + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultNotImplemented); +} + +void TimeZoneService::Handle_GetTotalLocationNameCount(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + u32 count{}; + auto res = GetTotalLocationNameCount(count); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.Push(count); +} + +void TimeZoneService::Handle_LoadLocationNameList(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultNotImplemented); +} + +void TimeZoneService::Handle_LoadTimeZoneRule(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultNotImplemented); +} + +void TimeZoneService::Handle_GetTimeZoneRuleVersion(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + RuleVersion rule_version{}; + auto res = GetTimeZoneRuleVersion(rule_version); + + IPC::ResponseBuilder rb{ctx, 2 + sizeof(RuleVersion) / sizeof(u32)}; + rb.Push(res); + rb.PushRaw<RuleVersion>(rule_version); +} + +void TimeZoneService::Handle_GetDeviceLocationNameAndUpdatedTime(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + LocationName name{}; + SteadyClockTimePoint time_point{}; + auto res = GetDeviceLocationNameAndUpdatedTime(time_point, name); + + IPC::ResponseBuilder rb{ctx, 2 + (sizeof(LocationName) / sizeof(u32)) + + (sizeof(SteadyClockTimePoint) / sizeof(u32))}; + rb.Push(res); + rb.PushRaw<LocationName>(name); + rb.PushRaw<SteadyClockTimePoint>(time_point); +} + +void TimeZoneService::Handle_SetDeviceLocationNameWithTimeZoneRule(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto name{rp.PopRaw<LocationName>()}; + + auto binary{ctx.ReadBuffer()}; + auto res = SetDeviceLocationNameWithTimeZoneRule(name, binary); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void TimeZoneService::Handle_ParseTimeZoneBinary(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + auto binary{ctx.ReadBuffer()}; + + Tz::Rule rule{}; + auto res = ParseTimeZoneBinary(rule, binary); + + ctx.WriteBuffer(rule); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void TimeZoneService::Handle_GetDeviceLocationNameOperationEventReadableHandle( + HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultNotImplemented); +} + +void TimeZoneService::Handle_ToCalendarTime(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto time{rp.Pop<s64>()}; + + auto rule_buffer{ctx.ReadBuffer()}; + Tz::Rule rule{}; + std::memcpy(&rule, rule_buffer.data(), sizeof(Tz::Rule)); + + CalendarTime calendar_time{}; + CalendarAdditionalInfo additional_info{}; + auto res = ToCalendarTime(calendar_time, additional_info, time, rule); + + IPC::ResponseBuilder rb{ctx, 2 + (sizeof(CalendarTime) / sizeof(u32)) + + (sizeof(CalendarAdditionalInfo) / sizeof(u32))}; + rb.Push(res); + rb.PushRaw<CalendarTime>(calendar_time); + rb.PushRaw<CalendarAdditionalInfo>(additional_info); +} + +void TimeZoneService::Handle_ToCalendarTimeWithMyRule(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto time{rp.Pop<s64>()}; + + CalendarTime calendar_time{}; + CalendarAdditionalInfo additional_info{}; + auto res = ToCalendarTimeWithMyRule(calendar_time, additional_info, time); + + IPC::ResponseBuilder rb{ctx, 2 + (sizeof(CalendarTime) / sizeof(u32)) + + (sizeof(CalendarAdditionalInfo) / sizeof(u32))}; + rb.Push(res); + rb.PushRaw<CalendarTime>(calendar_time); + rb.PushRaw<CalendarAdditionalInfo>(additional_info); +} + +void TimeZoneService::Handle_ToPosixTime(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto calendar{rp.PopRaw<CalendarTime>()}; + + auto binary{ctx.ReadBuffer()}; + + Tz::Rule rule{}; + std::memcpy(&rule, binary.data(), sizeof(Tz::Rule)); + + u32 count{}; + std::array<s64, 2> times{}; + u32 times_count{static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(s64))}; + + auto res = ToPosixTime(count, times, times_count, calendar, rule); + + ctx.WriteBuffer(times); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.Push(count); +} + +void TimeZoneService::Handle_ToPosixTimeWithMyRule(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto calendar{rp.PopRaw<CalendarTime>()}; + + u32 count{}; + std::array<s64, 2> times{}; + u32 times_count{static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(s64))}; + + auto res = ToPosixTimeWithMyRule(count, times, times_count, calendar); + + ctx.WriteBuffer(times); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.Push(count); +} + +// =============================== Implementations =========================== + +Result TimeZoneService::GetDeviceLocationName(LocationName& out_location_name) { + R_RETURN(m_time_zone.GetLocationName(out_location_name)); +} + +Result TimeZoneService::GetTotalLocationNameCount(u32& out_count) { + R_RETURN(m_time_zone.GetTotalLocationCount(out_count)); +} + +Result TimeZoneService::GetTimeZoneRuleVersion(RuleVersion& out_rule_version) { + R_RETURN(m_time_zone.GetRuleVersion(out_rule_version)); +} + +Result TimeZoneService::GetDeviceLocationNameAndUpdatedTime(SteadyClockTimePoint& out_time_point, + LocationName& location_name) { + R_TRY(m_time_zone.GetLocationName(location_name)); + R_RETURN(m_time_zone.GetTimePoint(out_time_point)); +} + +Result TimeZoneService::SetDeviceLocationNameWithTimeZoneRule(LocationName& location_name, + std::span<const u8> binary) { + R_UNLESS(m_can_write_timezone_device_location, ResultPermissionDenied); + R_TRY(m_time_zone.ParseBinary(location_name, binary)); + + SteadyClockTimePoint time_point{}; + R_TRY(m_clock_core.GetCurrentTimePoint(time_point)); + + m_time_zone.SetTimePoint(time_point); + R_SUCCEED(); +} + +Result TimeZoneService::ParseTimeZoneBinary(Tz::Rule& out_rule, std::span<const u8> binary) { + R_RETURN(m_time_zone.ParseBinaryInto(out_rule, binary)); +} + +Result TimeZoneService::ToCalendarTime(CalendarTime& out_calendar_time, + CalendarAdditionalInfo& out_additional_info, s64 time, + Tz::Rule& rule) { + R_RETURN(m_time_zone.ToCalendarTime(out_calendar_time, out_additional_info, time, rule)); +} + +Result TimeZoneService::ToCalendarTimeWithMyRule(CalendarTime& out_calendar_time, + CalendarAdditionalInfo& out_additional_info, + s64 time) { + R_RETURN(m_time_zone.ToCalendarTimeWithMyRule(out_calendar_time, out_additional_info, time)); +} + +Result TimeZoneService::ToPosixTime(u32& out_count, std::span<s64, 2> out_times, + u32 out_times_count, CalendarTime& calendar_time, + Tz::Rule& rule) { + R_RETURN(m_time_zone.ToPosixTime(out_count, out_times, out_times_count, calendar_time, rule)); +} + +Result TimeZoneService::ToPosixTimeWithMyRule(u32& out_count, std::span<s64, 2> out_times, + u32 out_times_count, CalendarTime& calendar_time) { + R_RETURN( + m_time_zone.ToPosixTimeWithMyRule(out_count, out_times, out_times_count, calendar_time)); +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/time_zone_service.h b/src/core/hle/service/psc/time/time_zone_service.h new file mode 100644 index 000000000..074c1d4ae --- /dev/null +++ b/src/core/hle/service/psc/time/time_zone_service.h @@ -0,0 +1,69 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/psc/time/common.h" +#include "core/hle/service/psc/time/manager.h" +#include "core/hle/service/server_manager.h" +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Tz { +struct Rule; +} + +namespace Service::PSC::Time { + +class TimeZoneService final : public ServiceFramework<TimeZoneService> { +public: + explicit TimeZoneService(Core::System& system, StandardSteadyClockCore& clock_core, + TimeZone& time_zone, bool can_write_timezone_device_location); + + ~TimeZoneService() override = default; + + Result GetDeviceLocationName(LocationName& out_location_name); + Result GetTotalLocationNameCount(u32& out_count); + Result GetTimeZoneRuleVersion(RuleVersion& out_rule_version); + Result GetDeviceLocationNameAndUpdatedTime(SteadyClockTimePoint& out_time_point, + LocationName& location_name); + Result SetDeviceLocationNameWithTimeZoneRule(LocationName& location_name, + std::span<const u8> binary); + Result ParseTimeZoneBinary(Tz::Rule& out_rule, std::span<const u8> binary); + Result ToCalendarTime(CalendarTime& out_calendar_time, + CalendarAdditionalInfo& out_additional_info, s64 time, Tz::Rule& rule); + Result ToCalendarTimeWithMyRule(CalendarTime& out_calendar_time, + CalendarAdditionalInfo& out_additional_info, s64 time); + Result ToPosixTime(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count, + CalendarTime& calendar_time, Tz::Rule& rule); + Result ToPosixTimeWithMyRule(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count, + CalendarTime& calendar_time); + +private: + void Handle_GetDeviceLocationName(HLERequestContext& ctx); + void Handle_SetDeviceLocationName(HLERequestContext& ctx); + void Handle_GetTotalLocationNameCount(HLERequestContext& ctx); + void Handle_LoadLocationNameList(HLERequestContext& ctx); + void Handle_LoadTimeZoneRule(HLERequestContext& ctx); + void Handle_GetTimeZoneRuleVersion(HLERequestContext& ctx); + void Handle_GetDeviceLocationNameAndUpdatedTime(HLERequestContext& ctx); + void Handle_SetDeviceLocationNameWithTimeZoneRule(HLERequestContext& ctx); + void Handle_ParseTimeZoneBinary(HLERequestContext& ctx); + void Handle_GetDeviceLocationNameOperationEventReadableHandle(HLERequestContext& ctx); + void Handle_ToCalendarTime(HLERequestContext& ctx); + void Handle_ToCalendarTimeWithMyRule(HLERequestContext& ctx); + void Handle_ToPosixTime(HLERequestContext& ctx); + void Handle_ToPosixTimeWithMyRule(HLERequestContext& ctx); + + Core::System& m_system; + + StandardSteadyClockCore& m_clock_core; + TimeZone& m_time_zone; + bool m_can_write_timezone_device_location; +}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/ro/ro.cpp b/src/core/hle/service/ro/ro.cpp index f0658bb5d..ae62c430e 100644 --- a/src/core/hle/service/ro/ro.cpp +++ b/src/core/hle/service/ro/ro.cpp @@ -6,13 +6,13 @@ #include "common/scope_exit.h" #include "core/hle/kernel/k_process.h" +#include "core/hle/service/cmif_serialization.h" #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/ro/ro.h" #include "core/hle/service/ro/ro_nro_utils.h" #include "core/hle/service/ro/ro_results.h" #include "core/hle/service/ro/ro_types.h" #include "core/hle/service/server_manager.h" -#include "core/hle/service/service.h" namespace Service::RO { @@ -500,46 +500,65 @@ private: } }; -class RoInterface { +class RoInterface : public ServiceFramework<RoInterface> { public: - explicit RoInterface(std::shared_ptr<RoContext> ro, NrrKind nrr_kind) - : m_ro(ro), m_context_id(InvalidContextId), m_nrr_kind(nrr_kind) {} + explicit RoInterface(Core::System& system_, const char* name_, std::shared_ptr<RoContext> ro, + NrrKind nrr_kind) + : ServiceFramework{system_, name_}, m_ro(ro), m_context_id(InvalidContextId), + m_nrr_kind(nrr_kind) { + + // clang-format off + static const FunctionInfo functions[] = { + {0, C<&RoInterface::MapManualLoadModuleMemory>, "MapManualLoadModuleMemory"}, + {1, C<&RoInterface::UnmapManualLoadModuleMemory>, "UnmapManualLoadModuleMemory"}, + {2, C<&RoInterface::RegisterModuleInfo>, "RegisterModuleInfo"}, + {3, C<&RoInterface::UnregisterModuleInfo>, "UnregisterModuleInfo"}, + {4, C<&RoInterface::RegisterProcessHandle>, "RegisterProcessHandle"}, + {10, C<&RoInterface::RegisterProcessModuleInfo>, "RegisterProcessModuleInfo"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + ~RoInterface() { m_ro->UnregisterProcess(m_context_id); } - Result MapManualLoadModuleMemory(u64* out_load_address, u64 client_pid, u64 nro_address, - u64 nro_size, u64 bss_address, u64 bss_size) { - R_TRY(m_ro->ValidateProcess(m_context_id, client_pid)); - R_RETURN(m_ro->MapManualLoadModuleMemory(out_load_address, m_context_id, nro_address, + Result MapManualLoadModuleMemory(Out<u64> out_load_address, ClientProcessId client_pid, + u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) { + R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid)); + R_RETURN(m_ro->MapManualLoadModuleMemory(out_load_address.Get(), m_context_id, nro_address, nro_size, bss_address, bss_size)); } - Result UnmapManualLoadModuleMemory(u64 client_pid, u64 nro_address) { - R_TRY(m_ro->ValidateProcess(m_context_id, client_pid)); + Result UnmapManualLoadModuleMemory(ClientProcessId client_pid, u64 nro_address) { + R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid)); R_RETURN(m_ro->UnmapManualLoadModuleMemory(m_context_id, nro_address)); } - Result RegisterModuleInfo(u64 client_pid, u64 nrr_address, u64 nrr_size) { - R_TRY(m_ro->ValidateProcess(m_context_id, client_pid)); + Result RegisterModuleInfo(ClientProcessId client_pid, u64 nrr_address, u64 nrr_size) { + R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid)); R_RETURN( m_ro->RegisterModuleInfo(m_context_id, nrr_address, nrr_size, NrrKind::User, true)); } - Result UnregisterModuleInfo(u64 client_pid, u64 nrr_address) { - R_TRY(m_ro->ValidateProcess(m_context_id, client_pid)); + Result UnregisterModuleInfo(ClientProcessId client_pid, u64 nrr_address) { + R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid)); R_RETURN(m_ro->UnregisterModuleInfo(m_context_id, nrr_address)); } - Result RegisterProcessHandle(u64 client_pid, Kernel::KProcess* process) { + Result RegisterProcessHandle(ClientProcessId client_pid, + InCopyHandle<Kernel::KProcess>& process) { // Register the process. - R_RETURN(m_ro->RegisterProcess(std::addressof(m_context_id), process, client_pid)); + R_RETURN(m_ro->RegisterProcess(std::addressof(m_context_id), process.GetPointerUnsafe(), + *client_pid)); } - Result RegisterProcessModuleInfo(u64 client_pid, u64 nrr_address, u64 nrr_size, - Kernel::KProcess* process) { + Result RegisterProcessModuleInfo(ClientProcessId client_pid, u64 nrr_address, u64 nrr_size, + InCopyHandle<Kernel::KProcess>& process) { // Validate the process. - R_TRY(m_ro->ValidateProcess(m_context_id, client_pid)); + R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid)); // Register the module. R_RETURN(m_ro->RegisterModuleInfo(m_context_id, nrr_address, nrr_size, m_nrr_kind, @@ -552,137 +571,6 @@ private: NrrKind m_nrr_kind{}; }; -class IRoInterface : public ServiceFramework<IRoInterface> { -public: - explicit IRoInterface(Core::System& system_, const char* name_, std::shared_ptr<RoContext> ro, - NrrKind nrr_kind) - : ServiceFramework{system_, name_}, interface { - ro, nrr_kind - } { - // clang-format off - static const FunctionInfo functions[] = { - {0, &IRoInterface::MapManualLoadModuleMemory, "MapManualLoadModuleMemory"}, - {1, &IRoInterface::UnmapManualLoadModuleMemory, "UnmapManualLoadModuleMemory"}, - {2, &IRoInterface::RegisterModuleInfo, "RegisterModuleInfo"}, - {3, &IRoInterface::UnregisterModuleInfo, "UnregisterModuleInfo"}, - {4, &IRoInterface::RegisterProcessHandle, "RegisterProcessHandle"}, - {10, &IRoInterface::RegisterProcessModuleInfo, "RegisterProcessModuleInfo"}, - }; - // clang-format on - - RegisterHandlers(functions); - } - -private: - void MapManualLoadModuleMemory(HLERequestContext& ctx) { - LOG_DEBUG(Service_LDR, "(called)"); - - struct InputParameters { - u64 client_pid; - u64 nro_address; - u64 nro_size; - u64 bss_address; - u64 bss_size; - }; - - IPC::RequestParser rp{ctx}; - auto params = rp.PopRaw<InputParameters>(); - - u64 load_address = 0; - auto result = interface.MapManualLoadModuleMemory(&load_address, ctx.GetPID(), - params.nro_address, params.nro_size, - params.bss_address, params.bss_size); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(result); - rb.Push(load_address); - } - - void UnmapManualLoadModuleMemory(HLERequestContext& ctx) { - LOG_DEBUG(Service_LDR, "(called)"); - - struct InputParameters { - u64 client_pid; - u64 nro_address; - }; - - IPC::RequestParser rp{ctx}; - auto params = rp.PopRaw<InputParameters>(); - auto result = interface.UnmapManualLoadModuleMemory(ctx.GetPID(), params.nro_address); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - } - - void RegisterModuleInfo(HLERequestContext& ctx) { - LOG_DEBUG(Service_LDR, "(called)"); - - struct InputParameters { - u64 client_pid; - u64 nrr_address; - u64 nrr_size; - }; - - IPC::RequestParser rp{ctx}; - auto params = rp.PopRaw<InputParameters>(); - auto result = - interface.RegisterModuleInfo(ctx.GetPID(), params.nrr_address, params.nrr_size); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - } - - void UnregisterModuleInfo(HLERequestContext& ctx) { - LOG_DEBUG(Service_LDR, "(called)"); - - struct InputParameters { - u64 client_pid; - u64 nrr_address; - }; - - IPC::RequestParser rp{ctx}; - auto params = rp.PopRaw<InputParameters>(); - auto result = interface.UnregisterModuleInfo(ctx.GetPID(), params.nrr_address); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - } - - void RegisterProcessHandle(HLERequestContext& ctx) { - LOG_DEBUG(Service_LDR, "(called)"); - - auto process = ctx.GetObjectFromHandle<Kernel::KProcess>(ctx.GetCopyHandle(0)); - auto client_pid = ctx.GetPID(); - auto result = interface.RegisterProcessHandle(client_pid, process.GetPointerUnsafe()); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - } - - void RegisterProcessModuleInfo(HLERequestContext& ctx) { - LOG_DEBUG(Service_LDR, "(called)"); - - struct InputParameters { - u64 client_pid; - u64 nrr_address; - u64 nrr_size; - }; - - IPC::RequestParser rp{ctx}; - auto params = rp.PopRaw<InputParameters>(); - auto process = ctx.GetObjectFromHandle<Kernel::KProcess>(ctx.GetCopyHandle(0)); - - auto client_pid = ctx.GetPID(); - auto result = interface.RegisterProcessModuleInfo( - client_pid, params.nrr_address, params.nrr_size, process.GetPointerUnsafe()); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - } - - RoInterface interface; -}; - } // namespace void LoopProcess(Core::System& system) { @@ -691,11 +579,11 @@ void LoopProcess(Core::System& system) { auto ro = std::make_shared<RoContext>(); const auto RoInterfaceFactoryForUser = [&, ro] { - return std::make_shared<IRoInterface>(system, "ldr:ro", ro, NrrKind::User); + return std::make_shared<RoInterface>(system, "ldr:ro", ro, NrrKind::User); }; const auto RoInterfaceFactoryForJitPlugin = [&, ro] { - return std::make_shared<IRoInterface>(system, "ro:1", ro, NrrKind::JitPlugin); + return std::make_shared<RoInterface>(system, "ro:1", ro, NrrKind::JitPlugin); }; server_manager->RegisterNamedService("ldr:ro", std::move(RoInterfaceFactoryForUser)); diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 39124c5fd..06cbad268 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -66,7 +66,6 @@ #include "core/hle/service/sockets/sockets.h" #include "core/hle/service/spl/spl_module.h" #include "core/hle/service/ssl/ssl.h" -#include "core/hle/service/time/time.h" #include "core/hle/service/usb/usb.h" #include "core/hle/service/vi/vi.h" #include "core/reporter.h" @@ -246,6 +245,9 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system kernel.RunOnGuestCoreProcess("fatal", [&] { Fatal::LoopProcess(system); }); kernel.RunOnGuestCoreProcess("fgm", [&] { FGM::LoopProcess(system); }); kernel.RunOnGuestCoreProcess("friends", [&] { Friend::LoopProcess(system); }); + // glue depends on settings and psc, so they must come first + kernel.RunOnGuestCoreProcess("settings", [&] { Set::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("psc", [&] { PSC::LoopProcess(system); }); kernel.RunOnGuestCoreProcess("glue", [&] { Glue::LoopProcess(system); }); kernel.RunOnGuestCoreProcess("grc", [&] { GRC::LoopProcess(system); }); kernel.RunOnGuestCoreProcess("hid", [&] { HID::LoopProcess(system); }); @@ -269,13 +271,10 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system kernel.RunOnGuestCoreProcess("pcv", [&] { PCV::LoopProcess(system); }); kernel.RunOnGuestCoreProcess("prepo", [&] { PlayReport::LoopProcess(system); }); kernel.RunOnGuestCoreProcess("ProcessManager", [&] { PM::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("psc", [&] { PSC::LoopProcess(system); }); kernel.RunOnGuestCoreProcess("ptm", [&] { PTM::LoopProcess(system); }); kernel.RunOnGuestCoreProcess("ro", [&] { RO::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("settings", [&] { Set::LoopProcess(system); }); kernel.RunOnGuestCoreProcess("spl", [&] { SPL::LoopProcess(system); }); kernel.RunOnGuestCoreProcess("ssl", [&] { SSL::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("time", [&] { Time::LoopProcess(system); }); kernel.RunOnGuestCoreProcess("usb", [&] { USB::LoopProcess(system); }); // clang-format on } diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index d539ed0f4..22d1343d5 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -206,6 +206,22 @@ protected: RegisterHandlersBaseTipc(functions, n); } +protected: + template <bool Domain, auto F> + void CmifReplyWrap(HLERequestContext& ctx); + + /** + * Wraps the template pointer-to-member function for use in a domain session. + */ + template <auto F> + static constexpr HandlerFnP<Self> D = &Self::template CmifReplyWrap<true, F>; + + /** + * Wraps the template pointer-to-member function for use in a non-domain session. + */ + template <auto F> + static constexpr HandlerFnP<Self> C = &Self::template CmifReplyWrap<false, F>; + private: /** * This function is used to allow invocation of pointers to handlers stored in the base class diff --git a/src/core/hle/service/set/private_settings.h b/src/core/hle/service/set/private_settings.h new file mode 100644 index 000000000..b02291ce7 --- /dev/null +++ b/src/core/hle/service/set/private_settings.h @@ -0,0 +1,72 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> + +#include "common/bit_field.h" +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "common/uuid.h" +#include "core/hle/service/psc/time/common.h" + +namespace Service::Set { + +/// This is nn::settings::system::InitialLaunchFlag +struct InitialLaunchFlag { + union { + u32 raw{}; + + BitField<0, 1, u32> InitialLaunchCompletionFlag; + BitField<8, 1, u32> InitialLaunchUserAdditionFlag; + BitField<16, 1, u32> InitialLaunchTimestampFlag; + }; +}; +static_assert(sizeof(InitialLaunchFlag) == 4, "InitialLaunchFlag is an invalid size"); + +/// This is nn::settings::system::InitialLaunchSettings +struct InitialLaunchSettings { + InitialLaunchFlag flags; + INSERT_PADDING_BYTES(0x4); + Service::PSC::Time::SteadyClockTimePoint timestamp; +}; +static_assert(sizeof(InitialLaunchSettings) == 0x20, "InitialLaunchSettings is incorrect size"); + +#pragma pack(push, 4) +struct InitialLaunchSettingsPacked { + InitialLaunchFlag flags; + Service::PSC::Time::SteadyClockTimePoint timestamp; +}; +#pragma pack(pop) +static_assert(sizeof(InitialLaunchSettingsPacked) == 0x1C, + "InitialLaunchSettingsPacked is incorrect size"); + +struct PrivateSettings { + std::array<u8, 0x10> reserved_00; + + // nn::settings::system::InitialLaunchSettings + InitialLaunchSettings initial_launch_settings; + + std::array<u8, 0x20> reserved_30; + + Common::UUID external_clock_source_id; + s64 shutdown_rtc_value; + s64 external_steady_clock_internal_offset; + + std::array<u8, 0x60> reserved_70; + + // nn::settings::system::PlatformRegion + std::array<u8, 0x4> platform_region; + + std::array<u8, 0x4> reserved_D4; +}; +static_assert(offsetof(PrivateSettings, initial_launch_settings) == 0x10); +static_assert(offsetof(PrivateSettings, external_clock_source_id) == 0x50); +static_assert(offsetof(PrivateSettings, reserved_70) == 0x70); +static_assert(offsetof(PrivateSettings, platform_region) == 0xD0); +static_assert(sizeof(PrivateSettings) == 0xD8, "PrivateSettings has the wrong size!"); + +PrivateSettings DefaultPrivateSettings(); + +} // namespace Service::Set diff --git a/src/core/hle/service/set/setting_formats/private_settings.h b/src/core/hle/service/set/setting_formats/private_settings.h index 6c40f62e1..6579e95e0 100644 --- a/src/core/hle/service/set/setting_formats/private_settings.h +++ b/src/core/hle/service/set/setting_formats/private_settings.h @@ -8,7 +8,6 @@ #include "common/common_types.h" #include "common/uuid.h" #include "core/hle/service/set/settings_types.h" -#include "core/hle/service/time/clock_types.h" namespace Service::Set { diff --git a/src/core/hle/service/set/setting_formats/system_settings.cpp b/src/core/hle/service/set/setting_formats/system_settings.cpp index 66e57651e..ec00b90a6 100644 --- a/src/core/hle/service/set/setting_formats/system_settings.cpp +++ b/src/core/hle/service/set/setting_formats/system_settings.cpp @@ -45,7 +45,7 @@ SystemSettings DefaultSystemSettings() { }; settings.device_time_zone_location_name = {"UTC"}; - settings.user_system_clock_automatic_correction_enabled = false; + settings.user_system_clock_automatic_correction_enabled = true; settings.primary_album_storage = PrimaryAlbumStorage::SdCard; settings.battery_percentage_flag = true; diff --git a/src/core/hle/service/set/setting_formats/system_settings.h b/src/core/hle/service/set/setting_formats/system_settings.h index 14654f8b1..af5929fa9 100644 --- a/src/core/hle/service/set/setting_formats/system_settings.h +++ b/src/core/hle/service/set/setting_formats/system_settings.h @@ -12,7 +12,6 @@ #include "common/vector_math.h" #include "core/hle/service/set/setting_formats/private_settings.h" #include "core/hle/service/set/settings_types.h" -#include "core/hle/service/time/clock_types.h" namespace Service::Set { @@ -197,12 +196,14 @@ struct SystemSettings { std::array<u8, 0x2C> backlight_settings_mixed_up; INSERT_PADDING_BYTES(0x64); // Reserved - Service::Time::Clock::SystemClockContext user_system_clock_context; - Service::Time::Clock::SystemClockContext network_system_clock_context; + // nn::time::SystemClockContext + Service::PSC::Time::SystemClockContext user_system_clock_context; + Service::PSC::Time::SystemClockContext network_system_clock_context; bool user_system_clock_automatic_correction_enabled; INSERT_PADDING_BYTES(0x3); INSERT_PADDING_BYTES(0x4); // Reserved - Service::Time::Clock::SteadyClockTimePoint + // nn::time::SteadyClockTimePoint + Service::PSC::Time::SteadyClockTimePoint user_system_clock_automatic_correction_updated_time_point; INSERT_PADDING_BYTES(0x10); // Reserved @@ -280,9 +281,12 @@ struct SystemSettings { bool requires_run_repair_time_reviser; INSERT_PADDING_BYTES(0x6B); // Reserved - Service::Time::TimeZone::LocationName device_time_zone_location_name; + // nn::time::LocationName + Service::PSC::Time::LocationName device_time_zone_location_name; INSERT_PADDING_BYTES(0x4); // Reserved - Service::Time::Clock::SteadyClockTimePoint device_time_zone_location_updated_time; + // nn::time::SteadyClockTimePoint + Service::PSC::Time::SteadyClockTimePoint device_time_zone_location_updated_time; + INSERT_PADDING_BYTES(0xC0); // Reserved // nn::settings::system::PrimaryAlbumStorage diff --git a/src/core/hle/service/set/settings_types.h b/src/core/hle/service/set/settings_types.h index 4dee202d7..f6f227fde 100644 --- a/src/core/hle/service/set/settings_types.h +++ b/src/core/hle/service/set/settings_types.h @@ -9,7 +9,7 @@ #include "common/common_funcs.h" #include "common/common_types.h" #include "common/uuid.h" -#include "core/hle/service/time/clock_types.h" +#include "core/hle/service/psc/time/common.h" namespace Service::Set { @@ -365,7 +365,7 @@ struct EulaVersion { EulaVersionClockType clock_type; INSERT_PADDING_BYTES(0x4); s64 posix_time; - Time::Clock::SteadyClockTimePoint timestamp; + Service::PSC::Time::SteadyClockTimePoint timestamp; }; static_assert(sizeof(EulaVersion) == 0x30, "EulaVersion is incorrect size"); @@ -398,14 +398,14 @@ static_assert(sizeof(HomeMenuScheme) == 0x14, "HomeMenuScheme is incorrect size" struct InitialLaunchSettings { InitialLaunchFlag flags; INSERT_PADDING_BYTES(0x4); - Service::Time::Clock::SteadyClockTimePoint timestamp; + Service::PSC::Time::SteadyClockTimePoint timestamp; }; static_assert(sizeof(InitialLaunchSettings) == 0x20, "InitialLaunchSettings is incorrect size"); #pragma pack(push, 4) struct InitialLaunchSettingsPacked { InitialLaunchFlag flags; - Service::Time::Clock::SteadyClockTimePoint timestamp; + Service::PSC::Time::SteadyClockTimePoint timestamp; }; #pragma pack(pop) static_assert(sizeof(InitialLaunchSettingsPacked) == 0x1C, diff --git a/src/core/hle/service/set/system_settings_server.cpp b/src/core/hle/service/set/system_settings_server.cpp index 2e5785fed..f40a1c8f3 100644 --- a/src/core/hle/service/set/system_settings_server.cpp +++ b/src/core/hle/service/set/system_settings_server.cpp @@ -489,11 +489,10 @@ void ISystemSettingsServer::SetExternalSteadyClockSourceId(HLERequestContext& ct void ISystemSettingsServer::GetUserSystemClockContext(HLERequestContext& ctx) { LOG_INFO(Service_SET, "called"); - Service::Time::Clock::SystemClockContext context{}; + Service::PSC::Time::SystemClockContext context{}; auto res = GetUserSystemClockContext(context); - IPC::ResponseBuilder rb{ctx, - 2 + sizeof(Service::Time::Clock::SystemClockContext) / sizeof(u32)}; + IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::SystemClockContext) / sizeof(u32)}; rb.Push(res); rb.PushRaw(context); } @@ -502,7 +501,7 @@ void ISystemSettingsServer::SetUserSystemClockContext(HLERequestContext& ctx) { LOG_INFO(Service_SET, "called"); IPC::RequestParser rp{ctx}; - auto context{rp.PopRaw<Service::Time::Clock::SystemClockContext>()}; + auto context{rp.PopRaw<Service::PSC::Time::SystemClockContext>()}; auto res = SetUserSystemClockContext(context); @@ -710,12 +709,12 @@ void ISystemSettingsServer::GetSettingsItemValueSize(HLERequestContext& ctx) { // The category of the setting. This corresponds to the top-level keys of // system_settings.ini. const auto setting_category_buf{ctx.ReadBuffer(0)}; - const std::string setting_category{setting_category_buf.begin(), setting_category_buf.end()}; + const std::string setting_category{Common::StringFromBuffer(setting_category_buf)}; // The name of the setting. This corresponds to the second-level keys of // system_settings.ini. const auto setting_name_buf{ctx.ReadBuffer(1)}; - const std::string setting_name{setting_name_buf.begin(), setting_name_buf.end()}; + const std::string setting_name{Common::StringFromBuffer(setting_name_buf)}; auto settings{GetSettings()}; u64 response_size{0}; @@ -733,12 +732,12 @@ void ISystemSettingsServer::GetSettingsItemValue(HLERequestContext& ctx) { // The category of the setting. This corresponds to the top-level keys of // system_settings.ini. const auto setting_category_buf{ctx.ReadBuffer(0)}; - const std::string setting_category{setting_category_buf.begin(), setting_category_buf.end()}; + const std::string setting_category{Common::StringFromBuffer(setting_category_buf)}; // The name of the setting. This corresponds to the second-level keys of // system_settings.ini. const auto setting_name_buf{ctx.ReadBuffer(1)}; - const std::string setting_name{setting_name_buf.begin(), setting_name_buf.end()}; + const std::string setting_name{Common::StringFromBuffer(setting_name_buf)}; std::vector<u8> value; auto response = GetSettingsItemValue(value, setting_category, setting_name); @@ -809,19 +808,19 @@ void ISystemSettingsServer::GetQuestFlag(HLERequestContext& ctx) { void ISystemSettingsServer::GetDeviceTimeZoneLocationName(HLERequestContext& ctx) { LOG_INFO(Service_SET, "called"); - Service::Time::TimeZone::LocationName name{}; + Service::PSC::Time::LocationName name{}; auto res = GetDeviceTimeZoneLocationName(name); - IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::Time::TimeZone::LocationName) / sizeof(u32)}; + IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::LocationName) / sizeof(u32)}; rb.Push(res); - rb.PushRaw<Service::Time::TimeZone::LocationName>(name); + rb.PushRaw<Service::PSC::Time::LocationName>(name); } void ISystemSettingsServer::SetDeviceTimeZoneLocationName(HLERequestContext& ctx) { LOG_INFO(Service_SET, "called"); IPC::RequestParser rp{ctx}; - auto name{rp.PopRaw<Service::Time::TimeZone::LocationName>()}; + auto name{rp.PopRaw<Service::PSC::Time::LocationName>()}; auto res = SetDeviceTimeZoneLocationName(name); @@ -843,11 +842,10 @@ void ISystemSettingsServer::SetRegionCode(HLERequestContext& ctx) { void ISystemSettingsServer::GetNetworkSystemClockContext(HLERequestContext& ctx) { LOG_INFO(Service_SET, "called"); - Service::Time::Clock::SystemClockContext context{}; + Service::PSC::Time::SystemClockContext context{}; auto res = GetNetworkSystemClockContext(context); - IPC::ResponseBuilder rb{ctx, - 2 + sizeof(Service::Time::Clock::SystemClockContext) / sizeof(u32)}; + IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::SystemClockContext) / sizeof(u32)}; rb.Push(res); rb.PushRaw(context); } @@ -856,7 +854,7 @@ void ISystemSettingsServer::SetNetworkSystemClockContext(HLERequestContext& ctx) LOG_INFO(Service_SET, "called"); IPC::RequestParser rp{ctx}; - auto context{rp.PopRaw<Service::Time::Clock::SystemClockContext>()}; + auto context{rp.PopRaw<Service::PSC::Time::SystemClockContext>()}; auto res = SetNetworkSystemClockContext(context); @@ -1141,19 +1139,19 @@ void ISystemSettingsServer::GetKeyboardLayout(HLERequestContext& ctx) { void ISystemSettingsServer::GetDeviceTimeZoneLocationUpdatedTime(HLERequestContext& ctx) { LOG_INFO(Service_SET, "called"); - Service::Time::Clock::SteadyClockTimePoint time_point{}; + Service::PSC::Time::SteadyClockTimePoint time_point{}; auto res = GetDeviceTimeZoneLocationUpdatedTime(time_point); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(res); - rb.PushRaw<Service::Time::Clock::SteadyClockTimePoint>(time_point); + rb.PushRaw<Service::PSC::Time::SteadyClockTimePoint>(time_point); } void ISystemSettingsServer::SetDeviceTimeZoneLocationUpdatedTime(HLERequestContext& ctx) { LOG_INFO(Service_SET, "called"); IPC::RequestParser rp{ctx}; - auto time_point{rp.PopRaw<Service::Time::Clock::SteadyClockTimePoint>()}; + auto time_point{rp.PopRaw<Service::PSC::Time::SteadyClockTimePoint>()}; auto res = SetDeviceTimeZoneLocationUpdatedTime(time_point); @@ -1165,12 +1163,12 @@ void ISystemSettingsServer::GetUserSystemClockAutomaticCorrectionUpdatedTime( HLERequestContext& ctx) { LOG_INFO(Service_SET, "called"); - Service::Time::Clock::SteadyClockTimePoint time_point{}; + Service::PSC::Time::SteadyClockTimePoint time_point{}; auto res = GetUserSystemClockAutomaticCorrectionUpdatedTime(time_point); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(res); - rb.PushRaw<Service::Time::Clock::SteadyClockTimePoint>(time_point); + rb.PushRaw<Service::PSC::Time::SteadyClockTimePoint>(time_point); } void ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionUpdatedTime( @@ -1178,7 +1176,7 @@ void ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionUpdatedTime( LOG_INFO(Service_SET, "called"); IPC::RequestParser rp{ctx}; - auto time_point{rp.PopRaw<Service::Time::Clock::SteadyClockTimePoint>()}; + auto time_point{rp.PopRaw<Service::PSC::Time::SteadyClockTimePoint>()}; auto res = SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point); @@ -1257,25 +1255,25 @@ void ISystemSettingsServer::StoreSettings() { auto system_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000050"; if (!StoreSettingsFile(system_dir, m_system_settings)) { - LOG_ERROR(HW_GPU, "Failed to store System settings"); + LOG_ERROR(Service_SET, "Failed to store System settings"); } auto private_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000052"; if (!StoreSettingsFile(private_dir, m_private_settings)) { - LOG_ERROR(HW_GPU, "Failed to store Private settings"); + LOG_ERROR(Service_SET, "Failed to store Private settings"); } auto device_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000053"; if (!StoreSettingsFile(device_dir, m_device_settings)) { - LOG_ERROR(HW_GPU, "Failed to store Device settings"); + LOG_ERROR(Service_SET, "Failed to store Device settings"); } auto appln_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000054"; if (!StoreSettingsFile(appln_dir, m_appln_settings)) { - LOG_ERROR(HW_GPU, "Failed to store ApplLn settings"); + LOG_ERROR(Service_SET, "Failed to store ApplLn settings"); } } @@ -1318,39 +1316,39 @@ Result ISystemSettingsServer::SetExternalSteadyClockSourceId(Common::UUID id) { } Result ISystemSettingsServer::GetUserSystemClockContext( - Service::Time::Clock::SystemClockContext& out_context) { + Service::PSC::Time::SystemClockContext& out_context) { out_context = m_system_settings.user_system_clock_context; R_SUCCEED(); } Result ISystemSettingsServer::SetUserSystemClockContext( - Service::Time::Clock::SystemClockContext& context) { + Service::PSC::Time::SystemClockContext& context) { m_system_settings.user_system_clock_context = context; SetSaveNeeded(); R_SUCCEED(); } Result ISystemSettingsServer::GetDeviceTimeZoneLocationName( - Service::Time::TimeZone::LocationName& out_name) { + Service::PSC::Time::LocationName& out_name) { out_name = m_system_settings.device_time_zone_location_name; R_SUCCEED(); } Result ISystemSettingsServer::SetDeviceTimeZoneLocationName( - Service::Time::TimeZone::LocationName& name) { + Service::PSC::Time::LocationName& name) { m_system_settings.device_time_zone_location_name = name; SetSaveNeeded(); R_SUCCEED(); } Result ISystemSettingsServer::GetNetworkSystemClockContext( - Service::Time::Clock::SystemClockContext& out_context) { + Service::PSC::Time::SystemClockContext& out_context) { out_context = m_system_settings.network_system_clock_context; R_SUCCEED(); } Result ISystemSettingsServer::SetNetworkSystemClockContext( - Service::Time::Clock::SystemClockContext& context) { + Service::PSC::Time::SystemClockContext& context) { m_system_settings.network_system_clock_context = context; SetSaveNeeded(); R_SUCCEED(); @@ -1379,26 +1377,26 @@ Result ISystemSettingsServer::GetExternalSteadyClockInternalOffset(s64& out_offs } Result ISystemSettingsServer::GetDeviceTimeZoneLocationUpdatedTime( - Service::Time::Clock::SteadyClockTimePoint& out_time_point) { + Service::PSC::Time::SteadyClockTimePoint& out_time_point) { out_time_point = m_system_settings.device_time_zone_location_updated_time; R_SUCCEED(); } Result ISystemSettingsServer::SetDeviceTimeZoneLocationUpdatedTime( - Service::Time::Clock::SteadyClockTimePoint& time_point) { + Service::PSC::Time::SteadyClockTimePoint& time_point) { m_system_settings.device_time_zone_location_updated_time = time_point; SetSaveNeeded(); R_SUCCEED(); } Result ISystemSettingsServer::GetUserSystemClockAutomaticCorrectionUpdatedTime( - Service::Time::Clock::SteadyClockTimePoint& out_time_point) { + Service::PSC::Time::SteadyClockTimePoint& out_time_point) { out_time_point = m_system_settings.user_system_clock_automatic_correction_updated_time_point; R_SUCCEED(); } Result ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionUpdatedTime( - Service::Time::Clock::SteadyClockTimePoint out_time_point) { + Service::PSC::Time::SteadyClockTimePoint out_time_point) { m_system_settings.user_system_clock_automatic_correction_updated_time_point = out_time_point; SetSaveNeeded(); R_SUCCEED(); diff --git a/src/core/hle/service/set/system_settings_server.h b/src/core/hle/service/set/system_settings_server.h index 32716f567..a2258d16d 100644 --- a/src/core/hle/service/set/system_settings_server.h +++ b/src/core/hle/service/set/system_settings_server.h @@ -11,14 +11,13 @@ #include "common/polyfill_thread.h" #include "common/uuid.h" #include "core/hle/result.h" +#include "core/hle/service/psc/time/common.h" #include "core/hle/service/service.h" #include "core/hle/service/set/setting_formats/appln_settings.h" #include "core/hle/service/set/setting_formats/device_settings.h" #include "core/hle/service/set/setting_formats/private_settings.h" #include "core/hle/service/set/setting_formats/system_settings.h" #include "core/hle/service/set/settings_types.h" -#include "core/hle/service/time/clock_types.h" -#include "core/hle/service/time/time_zone_types.h" namespace Core { class System; @@ -51,24 +50,24 @@ public: Result GetExternalSteadyClockSourceId(Common::UUID& out_id); Result SetExternalSteadyClockSourceId(Common::UUID id); - Result GetUserSystemClockContext(Service::Time::Clock::SystemClockContext& out_context); - Result SetUserSystemClockContext(Service::Time::Clock::SystemClockContext& context); - Result GetDeviceTimeZoneLocationName(Service::Time::TimeZone::LocationName& out_name); - Result SetDeviceTimeZoneLocationName(Service::Time::TimeZone::LocationName& name); - Result GetNetworkSystemClockContext(Service::Time::Clock::SystemClockContext& out_context); - Result SetNetworkSystemClockContext(Service::Time::Clock::SystemClockContext& context); + Result GetUserSystemClockContext(Service::PSC::Time::SystemClockContext& out_context); + Result SetUserSystemClockContext(Service::PSC::Time::SystemClockContext& context); + Result GetDeviceTimeZoneLocationName(Service::PSC::Time::LocationName& out_name); + Result SetDeviceTimeZoneLocationName(Service::PSC::Time::LocationName& name); + Result GetNetworkSystemClockContext(Service::PSC::Time::SystemClockContext& out_context); + Result SetNetworkSystemClockContext(Service::PSC::Time::SystemClockContext& context); Result IsUserSystemClockAutomaticCorrectionEnabled(bool& out_enabled); Result SetUserSystemClockAutomaticCorrectionEnabled(bool enabled); Result SetExternalSteadyClockInternalOffset(s64 offset); Result GetExternalSteadyClockInternalOffset(s64& out_offset); Result GetDeviceTimeZoneLocationUpdatedTime( - Service::Time::Clock::SteadyClockTimePoint& out_time_point); + Service::PSC::Time::SteadyClockTimePoint& out_time_point); Result SetDeviceTimeZoneLocationUpdatedTime( - Service::Time::Clock::SteadyClockTimePoint& time_point); + Service::PSC::Time::SteadyClockTimePoint& time_point); Result GetUserSystemClockAutomaticCorrectionUpdatedTime( - Service::Time::Clock::SteadyClockTimePoint& out_time_point); + Service::PSC::Time::SteadyClockTimePoint& out_time_point); Result SetUserSystemClockAutomaticCorrectionUpdatedTime( - Service::Time::Clock::SteadyClockTimePoint time_point); + Service::PSC::Time::SteadyClockTimePoint time_point); private: void SetLanguageCode(HLERequestContext& ctx); @@ -147,8 +146,8 @@ private: PrivateSettings m_private_settings{}; DeviceSettings m_device_settings{}; ApplnSettings m_appln_settings{}; - std::jthread m_save_thread; std::mutex m_save_needed_mutex; + std::jthread m_save_thread; bool m_save_needed{false}; }; diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h index 4ae32a9c1..32c218638 100644 --- a/src/core/hle/service/sm/sm.h +++ b/src/core/hle/service/sm/sm.h @@ -3,6 +3,7 @@ #pragma once +#include <chrono> #include <memory> #include <mutex> #include <string> @@ -10,6 +11,7 @@ #include "common/concepts.h" #include "core/hle/kernel/k_port.h" +#include "core/hle/kernel/svc.h" #include "core/hle/result.h" #include "core/hle/service/service.h" @@ -62,12 +64,21 @@ public: Result GetServicePort(Kernel::KClientPort** out_client_port, const std::string& name); template <Common::DerivedFrom<SessionRequestHandler> T> - std::shared_ptr<T> GetService(const std::string& service_name) const { + std::shared_ptr<T> GetService(const std::string& service_name, bool block = false) const { auto service = registered_services.find(service_name); - if (service == registered_services.end()) { + if (service == registered_services.end() && !block) { LOG_DEBUG(Service, "Can't find service: {}", service_name); return nullptr; + } else if (block) { + using namespace std::literals::chrono_literals; + while (service == registered_services.end()) { + Kernel::Svc::SleepThread( + kernel.System(), + std::chrono::duration_cast<std::chrono::nanoseconds>(100ms).count()); + service = registered_services.find(service_name); + } } + return std::static_pointer_cast<T>(service->second()); } diff --git a/src/core/hle/service/time/clock_types.h b/src/core/hle/service/time/clock_types.h deleted file mode 100644 index 7149fffeb..000000000 --- a/src/core/hle/service/time/clock_types.h +++ /dev/null @@ -1,129 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <ratio> - -#include "common/common_funcs.h" -#include "common/common_types.h" -#include "common/uuid.h" -#include "core/hle/service/time/errors.h" -#include "core/hle/service/time/time_zone_types.h" - -// Defined by WinBase.h on Windows -#ifdef GetCurrentTime -#undef GetCurrentTime -#endif - -namespace Service::Time::Clock { - -enum class TimeType : u8 { - UserSystemClock, - NetworkSystemClock, - LocalSystemClock, -}; - -/// https://switchbrew.org/wiki/Glue_services#SteadyClockTimePoint -struct SteadyClockTimePoint { - s64 time_point; - Common::UUID clock_source_id; - - Result GetSpanBetween(SteadyClockTimePoint other, s64& span) const { - span = 0; - - if (clock_source_id != other.clock_source_id) { - return ERROR_TIME_MISMATCH; - } - - span = other.time_point - time_point; - - return ResultSuccess; - } - - static SteadyClockTimePoint GetRandom() { - return {0, Common::UUID::MakeRandom()}; - } -}; -static_assert(sizeof(SteadyClockTimePoint) == 0x18, "SteadyClockTimePoint is incorrect size"); -static_assert(std::is_trivially_copyable_v<SteadyClockTimePoint>, - "SteadyClockTimePoint must be trivially copyable"); - -struct SteadyClockContext { - u64 internal_offset; - Common::UUID steady_time_point; -}; -static_assert(sizeof(SteadyClockContext) == 0x18, "SteadyClockContext is incorrect size"); -static_assert(std::is_trivially_copyable_v<SteadyClockContext>, - "SteadyClockContext must be trivially copyable"); -using StandardSteadyClockTimePointType = SteadyClockContext; - -struct SystemClockContext { - s64 offset; - SteadyClockTimePoint steady_time_point; -}; -static_assert(sizeof(SystemClockContext) == 0x20, "SystemClockContext is incorrect size"); -static_assert(std::is_trivially_copyable_v<SystemClockContext>, - "SystemClockContext must be trivially copyable"); - -struct ContinuousAdjustmentTimePoint { - s64 measurement_offset; - s64 diff_scale; - u32 shift_amount; - s64 lower; - s64 upper; - Common::UUID clock_source_id; -}; -static_assert(sizeof(ContinuousAdjustmentTimePoint) == 0x38); -static_assert(std::is_trivially_copyable_v<ContinuousAdjustmentTimePoint>, - "ContinuousAdjustmentTimePoint must be trivially copyable"); - -/// https://switchbrew.org/wiki/Glue_services#TimeSpanType -struct TimeSpanType { - s64 nanoseconds{}; - - s64 ToSeconds() const { - return nanoseconds / std::nano::den; - } - - static TimeSpanType FromSeconds(s64 seconds) { - return {seconds * std::nano::den}; - } - - template <u64 Frequency> - static TimeSpanType FromTicks(u64 ticks) { - using TicksToNSRatio = std::ratio<std::nano::den, Frequency>; - return {static_cast<s64>(ticks * TicksToNSRatio::num / TicksToNSRatio::den)}; - } -}; -static_assert(sizeof(TimeSpanType) == 8, "TimeSpanType is incorrect size"); - -struct ClockSnapshot { - SystemClockContext user_context; - SystemClockContext network_context; - s64 user_time; - s64 network_time; - TimeZone::CalendarTime user_calendar_time; - TimeZone::CalendarTime network_calendar_time; - TimeZone::CalendarAdditionalInfo user_calendar_additional_time; - TimeZone::CalendarAdditionalInfo network_calendar_additional_time; - SteadyClockTimePoint steady_clock_time_point; - TimeZone::LocationName location_name; - u8 is_automatic_correction_enabled; - TimeType type; - INSERT_PADDING_BYTES_NOINIT(0x2); - - static Result GetCurrentTime(s64& current_time, - const SteadyClockTimePoint& steady_clock_time_point, - const SystemClockContext& context) { - if (steady_clock_time_point.clock_source_id != context.steady_time_point.clock_source_id) { - current_time = 0; - return ERROR_TIME_MISMATCH; - } - current_time = steady_clock_time_point.time_point + context.offset; - return ResultSuccess; - } -}; -static_assert(sizeof(ClockSnapshot) == 0xD0, "ClockSnapshot is incorrect size"); - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/ephemeral_network_system_clock_context_writer.h b/src/core/hle/service/time/ephemeral_network_system_clock_context_writer.h deleted file mode 100644 index 0f928a5a5..000000000 --- a/src/core/hle/service/time/ephemeral_network_system_clock_context_writer.h +++ /dev/null @@ -1,15 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/time/system_clock_context_update_callback.h" - -namespace Service::Time::Clock { - -class EphemeralNetworkSystemClockContextWriter final : public SystemClockContextUpdateCallback { -public: - EphemeralNetworkSystemClockContextWriter() : SystemClockContextUpdateCallback{} {} -}; - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/ephemeral_network_system_clock_core.h b/src/core/hle/service/time/ephemeral_network_system_clock_core.h deleted file mode 100644 index 0a5f5aafb..000000000 --- a/src/core/hle/service/time/ephemeral_network_system_clock_core.h +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/time/system_clock_core.h" - -namespace Service::Time::Clock { - -class EphemeralNetworkSystemClockCore final : public SystemClockCore { -public: - explicit EphemeralNetworkSystemClockCore(SteadyClockCore& steady_clock_core_) - : SystemClockCore{steady_clock_core_} {} -}; - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/errors.h b/src/core/hle/service/time/errors.h deleted file mode 100644 index 6655d30e1..000000000 --- a/src/core/hle/service/time/errors.h +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/result.h" - -namespace Service::Time { - -constexpr Result ERROR_PERMISSION_DENIED{ErrorModule::Time, 1}; -constexpr Result ERROR_TIME_MISMATCH{ErrorModule::Time, 102}; -constexpr Result ERROR_UNINITIALIZED_CLOCK{ErrorModule::Time, 103}; -constexpr Result ERROR_TIME_NOT_FOUND{ErrorModule::Time, 200}; -constexpr Result ERROR_OVERFLOW{ErrorModule::Time, 201}; -constexpr Result ERROR_LOCATION_NAME_TOO_LONG{ErrorModule::Time, 801}; -constexpr Result ERROR_OUT_OF_RANGE{ErrorModule::Time, 902}; -constexpr Result ERROR_TIME_ZONE_CONVERSION_FAILED{ErrorModule::Time, 903}; -constexpr Result ERROR_TIME_ZONE_NOT_FOUND{ErrorModule::Time, 989}; -constexpr Result ERROR_NOT_IMPLEMENTED{ErrorModule::Time, 990}; - -} // namespace Service::Time diff --git a/src/core/hle/service/time/local_system_clock_context_writer.h b/src/core/hle/service/time/local_system_clock_context_writer.h deleted file mode 100644 index 1639ef2b9..000000000 --- a/src/core/hle/service/time/local_system_clock_context_writer.h +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/time/system_clock_context_update_callback.h" -#include "core/hle/service/time/time_sharedmemory.h" - -namespace Service::Time::Clock { - -class LocalSystemClockContextWriter final : public SystemClockContextUpdateCallback { -public: - explicit LocalSystemClockContextWriter(SharedMemory& shared_memory_) - : SystemClockContextUpdateCallback{}, shared_memory{shared_memory_} {} - -protected: - Result Update() override { - shared_memory.UpdateLocalSystemClockContext(context); - return ResultSuccess; - } - -private: - SharedMemory& shared_memory; -}; - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/network_system_clock_context_writer.h b/src/core/hle/service/time/network_system_clock_context_writer.h deleted file mode 100644 index 655e4c06d..000000000 --- a/src/core/hle/service/time/network_system_clock_context_writer.h +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/time/errors.h" -#include "core/hle/service/time/system_clock_context_update_callback.h" -#include "core/hle/service/time/time_sharedmemory.h" - -namespace Service::Time::Clock { - -class NetworkSystemClockContextWriter final : public SystemClockContextUpdateCallback { -public: - explicit NetworkSystemClockContextWriter(SharedMemory& shared_memory_) - : SystemClockContextUpdateCallback{}, shared_memory{shared_memory_} {} - -protected: - Result Update() override { - shared_memory.UpdateNetworkSystemClockContext(context); - return ResultSuccess; - } - -private: - SharedMemory& shared_memory; -}; - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/standard_local_system_clock_core.h b/src/core/hle/service/time/standard_local_system_clock_core.h deleted file mode 100644 index ae2ff1bfd..000000000 --- a/src/core/hle/service/time/standard_local_system_clock_core.h +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/time/system_clock_core.h" - -namespace Service::Time::Clock { - -class StandardLocalSystemClockCore final : public SystemClockCore { -public: - explicit StandardLocalSystemClockCore(SteadyClockCore& steady_clock_core_) - : SystemClockCore{steady_clock_core_} {} -}; - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/standard_network_system_clock_core.h b/src/core/hle/service/time/standard_network_system_clock_core.h deleted file mode 100644 index c1ec5252b..000000000 --- a/src/core/hle/service/time/standard_network_system_clock_core.h +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/time/clock_types.h" -#include "core/hle/service/time/steady_clock_core.h" -#include "core/hle/service/time/system_clock_core.h" - -namespace Core { -class System; -} - -namespace Service::Time::Clock { - -class StandardNetworkSystemClockCore final : public SystemClockCore { -public: - explicit StandardNetworkSystemClockCore(SteadyClockCore& steady_clock_core_) - : SystemClockCore{steady_clock_core_} {} - - void SetStandardNetworkClockSufficientAccuracy(TimeSpanType value) { - standard_network_clock_sufficient_accuracy = value; - } - - bool IsStandardNetworkSystemClockAccuracySufficient(Core::System& system) const { - SystemClockContext clock_ctx{}; - if (GetClockContext(system, clock_ctx) != ResultSuccess) { - return {}; - } - - s64 span{}; - if (clock_ctx.steady_time_point.GetSpanBetween( - GetSteadyClockCore().GetCurrentTimePoint(system), span) != ResultSuccess) { - return {}; - } - - return TimeSpanType{span}.nanoseconds < - standard_network_clock_sufficient_accuracy.nanoseconds; - } - -private: - TimeSpanType standard_network_clock_sufficient_accuracy{}; -}; - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/standard_steady_clock_core.cpp b/src/core/hle/service/time/standard_steady_clock_core.cpp deleted file mode 100644 index 5627b7003..000000000 --- a/src/core/hle/service/time/standard_steady_clock_core.cpp +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/core.h" -#include "core/core_timing.h" -#include "core/hardware_properties.h" -#include "core/hle/service/time/standard_steady_clock_core.h" - -namespace Service::Time::Clock { - -TimeSpanType StandardSteadyClockCore::GetCurrentRawTimePoint(Core::System& system) { - const TimeSpanType ticks_time_span{ - TimeSpanType::FromTicks<Core::Hardware::CNTFREQ>(system.CoreTiming().GetClockTicks())}; - TimeSpanType raw_time_point{setup_value.nanoseconds + ticks_time_span.nanoseconds}; - - if (raw_time_point.nanoseconds < cached_raw_time_point.nanoseconds) { - raw_time_point.nanoseconds = cached_raw_time_point.nanoseconds; - } - - cached_raw_time_point = raw_time_point; - return raw_time_point; -} - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/standard_steady_clock_core.h b/src/core/hle/service/time/standard_steady_clock_core.h deleted file mode 100644 index 036463b87..000000000 --- a/src/core/hle/service/time/standard_steady_clock_core.h +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/time/clock_types.h" -#include "core/hle/service/time/steady_clock_core.h" - -namespace Core { -class System; -} - -namespace Service::Time::Clock { - -class StandardSteadyClockCore final : public SteadyClockCore { -public: - SteadyClockTimePoint GetTimePoint(Core::System& system) override { - return {GetCurrentRawTimePoint(system).ToSeconds(), GetClockSourceId()}; - } - - TimeSpanType GetInternalOffset() const override { - return internal_offset; - } - - void SetInternalOffset(TimeSpanType value) override { - internal_offset = value; - } - - TimeSpanType GetCurrentRawTimePoint(Core::System& system) override; - - void SetSetupValue(TimeSpanType value) { - setup_value = value; - } - -private: - TimeSpanType setup_value{}; - TimeSpanType internal_offset{}; - TimeSpanType cached_raw_time_point{}; -}; - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/standard_user_system_clock_core.cpp b/src/core/hle/service/time/standard_user_system_clock_core.cpp deleted file mode 100644 index b033757ed..000000000 --- a/src/core/hle/service/time/standard_user_system_clock_core.cpp +++ /dev/null @@ -1,81 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/assert.h" -#include "core/core.h" -#include "core/hle/kernel/k_event.h" -#include "core/hle/service/time/standard_local_system_clock_core.h" -#include "core/hle/service/time/standard_network_system_clock_core.h" -#include "core/hle/service/time/standard_user_system_clock_core.h" - -namespace Service::Time::Clock { - -StandardUserSystemClockCore::StandardUserSystemClockCore( - StandardLocalSystemClockCore& local_system_clock_core_, - StandardNetworkSystemClockCore& network_system_clock_core_, Core::System& system_) - : SystemClockCore(local_system_clock_core_.GetSteadyClockCore()), - local_system_clock_core{local_system_clock_core_}, - network_system_clock_core{network_system_clock_core_}, - auto_correction_time{SteadyClockTimePoint::GetRandom()}, service_context{ - system_, - "StandardUserSystemClockCore"} { - auto_correction_event = - service_context.CreateEvent("StandardUserSystemClockCore:AutoCorrectionEvent"); -} - -StandardUserSystemClockCore::~StandardUserSystemClockCore() { - service_context.CloseEvent(auto_correction_event); -} - -Result StandardUserSystemClockCore::SetAutomaticCorrectionEnabled(Core::System& system, - bool value) { - if (const Result result{ApplyAutomaticCorrection(system, value)}; result != ResultSuccess) { - return result; - } - - auto_correction_enabled = value; - - return ResultSuccess; -} - -Result StandardUserSystemClockCore::GetClockContext(Core::System& system, - SystemClockContext& ctx) const { - if (const Result result{ApplyAutomaticCorrection(system, false)}; result != ResultSuccess) { - return result; - } - - return local_system_clock_core.GetClockContext(system, ctx); -} - -Result StandardUserSystemClockCore::Flush(const SystemClockContext&) { - UNIMPLEMENTED(); - return ERROR_NOT_IMPLEMENTED; -} - -Result StandardUserSystemClockCore::SetClockContext(const SystemClockContext&) { - UNIMPLEMENTED(); - return ERROR_NOT_IMPLEMENTED; -} - -Result StandardUserSystemClockCore::ApplyAutomaticCorrection(Core::System& system, - bool value) const { - if (auto_correction_enabled == value) { - return ResultSuccess; - } - - if (!network_system_clock_core.IsClockSetup(system)) { - return ERROR_UNINITIALIZED_CLOCK; - } - - SystemClockContext ctx{}; - if (const Result result{network_system_clock_core.GetClockContext(system, ctx)}; - result != ResultSuccess) { - return result; - } - - local_system_clock_core.SetClockContext(ctx); - - return ResultSuccess; -} - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/standard_user_system_clock_core.h b/src/core/hle/service/time/standard_user_system_clock_core.h deleted file mode 100644 index ee6e29487..000000000 --- a/src/core/hle/service/time/standard_user_system_clock_core.h +++ /dev/null @@ -1,63 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/kernel_helpers.h" -#include "core/hle/service/time/clock_types.h" -#include "core/hle/service/time/system_clock_core.h" - -namespace Core { -class System; -} - -namespace Kernel { -class KEvent; -} - -namespace Service::Time::Clock { - -class StandardLocalSystemClockCore; -class StandardNetworkSystemClockCore; - -class StandardUserSystemClockCore final : public SystemClockCore { -public: - StandardUserSystemClockCore(StandardLocalSystemClockCore& local_system_clock_core_, - StandardNetworkSystemClockCore& network_system_clock_core_, - Core::System& system_); - - ~StandardUserSystemClockCore() override; - - Result SetAutomaticCorrectionEnabled(Core::System& system, bool value); - - Result GetClockContext(Core::System& system, SystemClockContext& ctx) const override; - - bool IsAutomaticCorrectionEnabled() const { - return auto_correction_enabled; - } - - void SetAutomaticCorrectionUpdatedTime(SteadyClockTimePoint steady_clock_time_point) { - auto_correction_time = steady_clock_time_point; - } - -protected: - Result Flush(const SystemClockContext&) override; - - Result SetClockContext(const SystemClockContext&) override; - - Result ApplyAutomaticCorrection(Core::System& system, bool value) const; - - const SteadyClockTimePoint& GetAutomaticCorrectionUpdatedTime() const { - return auto_correction_time; - } - -private: - StandardLocalSystemClockCore& local_system_clock_core; - StandardNetworkSystemClockCore& network_system_clock_core; - bool auto_correction_enabled{}; - SteadyClockTimePoint auto_correction_time; - KernelHelpers::ServiceContext service_context; - Kernel::KEvent* auto_correction_event; -}; - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/steady_clock_core.h b/src/core/hle/service/time/steady_clock_core.h deleted file mode 100644 index 2867c351c..000000000 --- a/src/core/hle/service/time/steady_clock_core.h +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "common/uuid.h" -#include "core/hle/service/time/clock_types.h" - -namespace Core { -class System; -} - -namespace Service::Time::Clock { - -class SteadyClockCore { -public: - SteadyClockCore() = default; - virtual ~SteadyClockCore() = default; - - const Common::UUID& GetClockSourceId() const { - return clock_source_id; - } - - void SetClockSourceId(const Common::UUID& value) { - clock_source_id = value; - } - - virtual TimeSpanType GetInternalOffset() const = 0; - - virtual void SetInternalOffset(TimeSpanType internal_offset) = 0; - - virtual SteadyClockTimePoint GetTimePoint(Core::System& system) = 0; - - virtual TimeSpanType GetCurrentRawTimePoint(Core::System& system) = 0; - - SteadyClockTimePoint GetCurrentTimePoint(Core::System& system) { - SteadyClockTimePoint result{GetTimePoint(system)}; - result.time_point += GetInternalOffset().ToSeconds(); - return result; - } - - bool IsInitialized() const { - return is_initialized; - } - - void MarkAsInitialized() { - is_initialized = true; - } - -private: - Common::UUID clock_source_id{Common::UUID::MakeRandom()}; - bool is_initialized{}; -}; - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/system_clock_context_update_callback.cpp b/src/core/hle/service/time/system_clock_context_update_callback.cpp deleted file mode 100644 index cafc04ee7..000000000 --- a/src/core/hle/service/time/system_clock_context_update_callback.cpp +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hle/kernel/k_event.h" -#include "core/hle/service/time/errors.h" -#include "core/hle/service/time/system_clock_context_update_callback.h" - -namespace Service::Time::Clock { - -SystemClockContextUpdateCallback::SystemClockContextUpdateCallback() = default; -SystemClockContextUpdateCallback::~SystemClockContextUpdateCallback() = default; - -bool SystemClockContextUpdateCallback::NeedUpdate(const SystemClockContext& value) const { - if (has_context) { - return context.offset != value.offset || - context.steady_time_point.clock_source_id != value.steady_time_point.clock_source_id; - } - - return true; -} - -void SystemClockContextUpdateCallback::RegisterOperationEvent( - std::shared_ptr<Kernel::KEvent>&& event) { - operation_event_list.emplace_back(std::move(event)); -} - -void SystemClockContextUpdateCallback::BroadcastOperationEvent() { - for (const auto& event : operation_event_list) { - event->Signal(); - } -} - -Result SystemClockContextUpdateCallback::Update(const SystemClockContext& value) { - Result result{ResultSuccess}; - - if (NeedUpdate(value)) { - context = value; - has_context = true; - - result = Update(); - - if (result == ResultSuccess) { - BroadcastOperationEvent(); - } - } - - return result; -} - -Result SystemClockContextUpdateCallback::Update() { - return ResultSuccess; -} - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/system_clock_context_update_callback.h b/src/core/hle/service/time/system_clock_context_update_callback.h deleted file mode 100644 index bf657acd9..000000000 --- a/src/core/hle/service/time/system_clock_context_update_callback.h +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <memory> -#include <vector> - -#include "core/hle/service/time/clock_types.h" - -namespace Kernel { -class KEvent; -} - -namespace Service::Time::Clock { - -// Parts of this implementation were based on Ryujinx (https://github.com/Ryujinx/Ryujinx/pull/783). -// This code was released under public domain. - -class SystemClockContextUpdateCallback { -public: - SystemClockContextUpdateCallback(); - virtual ~SystemClockContextUpdateCallback(); - - bool NeedUpdate(const SystemClockContext& value) const; - - void RegisterOperationEvent(std::shared_ptr<Kernel::KEvent>&& event); - - void BroadcastOperationEvent(); - - Result Update(const SystemClockContext& value); - -protected: - virtual Result Update(); - - SystemClockContext context{}; - -private: - bool has_context{}; - std::vector<std::shared_ptr<Kernel::KEvent>> operation_event_list; -}; - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/system_clock_core.cpp b/src/core/hle/service/time/system_clock_core.cpp deleted file mode 100644 index da078241f..000000000 --- a/src/core/hle/service/time/system_clock_core.cpp +++ /dev/null @@ -1,71 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hle/service/time/steady_clock_core.h" -#include "core/hle/service/time/system_clock_context_update_callback.h" -#include "core/hle/service/time/system_clock_core.h" - -namespace Service::Time::Clock { - -SystemClockCore::SystemClockCore(SteadyClockCore& steady_clock_core_) - : steady_clock_core{steady_clock_core_} { - context.steady_time_point.clock_source_id = steady_clock_core.GetClockSourceId(); -} - -SystemClockCore::~SystemClockCore() = default; - -Result SystemClockCore::GetCurrentTime(Core::System& system, s64& posix_time) const { - posix_time = 0; - - const SteadyClockTimePoint current_time_point{steady_clock_core.GetCurrentTimePoint(system)}; - - SystemClockContext clock_context{}; - if (const Result result{GetClockContext(system, clock_context)}; result != ResultSuccess) { - return result; - } - - if (current_time_point.clock_source_id != clock_context.steady_time_point.clock_source_id) { - return ERROR_TIME_MISMATCH; - } - - posix_time = clock_context.offset + current_time_point.time_point; - - return ResultSuccess; -} - -Result SystemClockCore::SetCurrentTime(Core::System& system, s64 posix_time) { - const SteadyClockTimePoint current_time_point{steady_clock_core.GetCurrentTimePoint(system)}; - const SystemClockContext clock_context{posix_time - current_time_point.time_point, - current_time_point}; - - if (const Result result{SetClockContext(clock_context)}; result != ResultSuccess) { - return result; - } - return Flush(clock_context); -} - -Result SystemClockCore::Flush(const SystemClockContext& clock_context) { - if (!system_clock_context_update_callback) { - return ResultSuccess; - } - return system_clock_context_update_callback->Update(clock_context); -} - -Result SystemClockCore::SetSystemClockContext(const SystemClockContext& clock_context) { - if (const Result result{SetClockContext(clock_context)}; result != ResultSuccess) { - return result; - } - return Flush(clock_context); -} - -bool SystemClockCore::IsClockSetup(Core::System& system) const { - SystemClockContext value{}; - if (GetClockContext(system, value) == ResultSuccess) { - const SteadyClockTimePoint steady_clock_time_point{ - steady_clock_core.GetCurrentTimePoint(system)}; - return steady_clock_time_point.clock_source_id == value.steady_time_point.clock_source_id; - } - return {}; -} - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/system_clock_core.h b/src/core/hle/service/time/system_clock_core.h deleted file mode 100644 index 8cb34126f..000000000 --- a/src/core/hle/service/time/system_clock_core.h +++ /dev/null @@ -1,72 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <memory> - -#include "common/common_types.h" -#include "core/hle/service/time/clock_types.h" - -namespace Core { -class System; -} - -namespace Service::Time::Clock { - -class SteadyClockCore; -class SystemClockContextUpdateCallback; - -// Parts of this implementation were based on Ryujinx (https://github.com/Ryujinx/Ryujinx/pull/783). -// This code was released under public domain. - -class SystemClockCore { -public: - explicit SystemClockCore(SteadyClockCore& steady_clock_core_); - virtual ~SystemClockCore(); - - SteadyClockCore& GetSteadyClockCore() const { - return steady_clock_core; - } - - Result GetCurrentTime(Core::System& system, s64& posix_time) const; - - Result SetCurrentTime(Core::System& system, s64 posix_time); - - virtual Result GetClockContext([[maybe_unused]] Core::System& system, - SystemClockContext& value) const { - value = context; - return ResultSuccess; - } - - virtual Result SetClockContext(const SystemClockContext& value) { - context = value; - return ResultSuccess; - } - - virtual Result Flush(const SystemClockContext& clock_context); - - void SetUpdateCallbackInstance(std::shared_ptr<SystemClockContextUpdateCallback> callback) { - system_clock_context_update_callback = std::move(callback); - } - - Result SetSystemClockContext(const SystemClockContext& context); - - bool IsInitialized() const { - return is_initialized; - } - - void MarkAsInitialized() { - is_initialized = true; - } - - bool IsClockSetup(Core::System& system) const; - -private: - SteadyClockCore& steady_clock_core; - SystemClockContext context{}; - bool is_initialized{}; - std::shared_ptr<SystemClockContextUpdateCallback> system_clock_context_update_callback; -}; - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/tick_based_steady_clock_core.cpp b/src/core/hle/service/time/tick_based_steady_clock_core.cpp deleted file mode 100644 index 0d9fb3143..000000000 --- a/src/core/hle/service/time/tick_based_steady_clock_core.cpp +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/core.h" -#include "core/core_timing.h" -#include "core/hardware_properties.h" -#include "core/hle/service/time/tick_based_steady_clock_core.h" - -namespace Service::Time::Clock { - -SteadyClockTimePoint TickBasedSteadyClockCore::GetTimePoint(Core::System& system) { - const TimeSpanType ticks_time_span{ - TimeSpanType::FromTicks<Core::Hardware::CNTFREQ>(system.CoreTiming().GetClockTicks())}; - - return {ticks_time_span.ToSeconds(), GetClockSourceId()}; -} - -TimeSpanType TickBasedSteadyClockCore::GetCurrentRawTimePoint(Core::System& system) { - return TimeSpanType::FromSeconds(GetTimePoint(system).time_point); -} - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/tick_based_steady_clock_core.h b/src/core/hle/service/time/tick_based_steady_clock_core.h deleted file mode 100644 index 491185dc3..000000000 --- a/src/core/hle/service/time/tick_based_steady_clock_core.h +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/time/clock_types.h" -#include "core/hle/service/time/steady_clock_core.h" - -namespace Core { -class System; -} - -namespace Service::Time::Clock { - -class TickBasedSteadyClockCore final : public SteadyClockCore { -public: - TimeSpanType GetInternalOffset() const override { - return {}; - } - - void SetInternalOffset(TimeSpanType internal_offset) override {} - - SteadyClockTimePoint GetTimePoint(Core::System& system) override; - - TimeSpanType GetCurrentRawTimePoint(Core::System& system) override; -}; - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp deleted file mode 100644 index 7197ca30f..000000000 --- a/src/core/hle/service/time/time.cpp +++ /dev/null @@ -1,412 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/logging/log.h" -#include "core/core.h" -#include "core/core_timing.h" -#include "core/hardware_properties.h" -#include "core/hle/kernel/kernel.h" -#include "core/hle/service/ipc_helpers.h" -#include "core/hle/service/server_manager.h" -#include "core/hle/service/time/time.h" -#include "core/hle/service/time/time_interface.h" -#include "core/hle/service/time/time_manager.h" -#include "core/hle/service/time/time_sharedmemory.h" -#include "core/hle/service/time/time_zone_service.h" - -namespace Service::Time { - -class ISystemClock final : public ServiceFramework<ISystemClock> { -public: - explicit ISystemClock(Clock::SystemClockCore& clock_core_, Core::System& system_) - : ServiceFramework{system_, "ISystemClock"}, clock_core{clock_core_} { - // clang-format off - static const FunctionInfo functions[] = { - {0, &ISystemClock::GetCurrentTime, "GetCurrentTime"}, - {1, nullptr, "SetCurrentTime"}, - {2, &ISystemClock::GetSystemClockContext, "GetSystemClockContext"}, - {3, nullptr, "SetSystemClockContext"}, - {4, nullptr, "GetOperationEventReadableHandle"}, - }; - // clang-format on - - RegisterHandlers(functions); - } - -private: - void GetCurrentTime(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - - if (!clock_core.IsInitialized()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERROR_UNINITIALIZED_CLOCK); - return; - } - - s64 posix_time{}; - if (const Result result{clock_core.GetCurrentTime(system, posix_time)}; result.IsError()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push<s64>(posix_time); - } - - void GetSystemClockContext(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - - if (!clock_core.IsInitialized()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERROR_UNINITIALIZED_CLOCK); - return; - } - - Clock::SystemClockContext system_clock_context{}; - if (const Result result{clock_core.GetClockContext(system, system_clock_context)}; - result.IsError()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - IPC::ResponseBuilder rb{ctx, sizeof(Clock::SystemClockContext) / 4 + 2}; - rb.Push(ResultSuccess); - rb.PushRaw(system_clock_context); - } - - Clock::SystemClockCore& clock_core; -}; - -class ISteadyClock final : public ServiceFramework<ISteadyClock> { -public: - explicit ISteadyClock(Clock::SteadyClockCore& clock_core_, Core::System& system_) - : ServiceFramework{system_, "ISteadyClock"}, clock_core{clock_core_} { - static const FunctionInfo functions[] = { - {0, &ISteadyClock::GetCurrentTimePoint, "GetCurrentTimePoint"}, - {2, nullptr, "GetTestOffset"}, - {3, nullptr, "SetTestOffset"}, - {100, nullptr, "GetRtcValue"}, - {101, nullptr, "IsRtcResetDetected"}, - {102, nullptr, "GetSetupResultValue"}, - {200, nullptr, "GetInternalOffset"}, - {201, nullptr, "SetInternalOffset"}, - }; - RegisterHandlers(functions); - } - -private: - void GetCurrentTimePoint(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - - if (!clock_core.IsInitialized()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERROR_UNINITIALIZED_CLOCK); - return; - } - - const Clock::SteadyClockTimePoint time_point{clock_core.GetCurrentTimePoint(system)}; - IPC::ResponseBuilder rb{ctx, (sizeof(Clock::SteadyClockTimePoint) / 4) + 2}; - rb.Push(ResultSuccess); - rb.PushRaw(time_point); - } - - Clock::SteadyClockCore& clock_core; -}; - -Result Module::Interface::GetClockSnapshotFromSystemClockContextInternal( - Kernel::KThread* thread, Clock::SystemClockContext user_context, - Clock::SystemClockContext network_context, Clock::TimeType type, - Clock::ClockSnapshot& clock_snapshot) { - - auto& time_manager{system.GetTimeManager()}; - - clock_snapshot.steady_clock_time_point = - time_manager.GetStandardSteadyClockCore().GetCurrentTimePoint(system); - clock_snapshot.is_automatic_correction_enabled = - time_manager.GetStandardUserSystemClockCore().IsAutomaticCorrectionEnabled(); - clock_snapshot.type = type; - - if (const Result result{ - time_manager.GetTimeZoneContentManager().GetTimeZoneManager().GetDeviceLocationName( - clock_snapshot.location_name)}; - result != ResultSuccess) { - return result; - } - - clock_snapshot.user_context = user_context; - - if (const Result result{Clock::ClockSnapshot::GetCurrentTime( - clock_snapshot.user_time, clock_snapshot.steady_clock_time_point, - clock_snapshot.user_context)}; - result != ResultSuccess) { - return result; - } - - TimeZone::CalendarInfo userCalendarInfo{}; - if (const Result result{ - time_manager.GetTimeZoneContentManager().GetTimeZoneManager().ToCalendarTimeWithMyRules( - clock_snapshot.user_time, userCalendarInfo)}; - result != ResultSuccess) { - return result; - } - - clock_snapshot.user_calendar_time = userCalendarInfo.time; - clock_snapshot.user_calendar_additional_time = userCalendarInfo.additional_info; - - clock_snapshot.network_context = network_context; - - if (Clock::ClockSnapshot::GetCurrentTime(clock_snapshot.network_time, - clock_snapshot.steady_clock_time_point, - clock_snapshot.network_context) != ResultSuccess) { - clock_snapshot.network_time = 0; - } - - TimeZone::CalendarInfo networkCalendarInfo{}; - if (const Result result{ - time_manager.GetTimeZoneContentManager().GetTimeZoneManager().ToCalendarTimeWithMyRules( - clock_snapshot.network_time, networkCalendarInfo)}; - result != ResultSuccess) { - return result; - } - - clock_snapshot.network_calendar_time = networkCalendarInfo.time; - clock_snapshot.network_calendar_additional_time = networkCalendarInfo.additional_info; - - return ResultSuccess; -} - -void Module::Interface::GetStandardUserSystemClock(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<ISystemClock>(system.GetTimeManager().GetStandardUserSystemClockCore(), - system); -} - -void Module::Interface::GetStandardNetworkSystemClock(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<ISystemClock>(system.GetTimeManager().GetStandardNetworkSystemClockCore(), - system); -} - -void Module::Interface::GetStandardSteadyClock(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<ISteadyClock>(system.GetTimeManager().GetStandardSteadyClockCore(), system); -} - -void Module::Interface::GetTimeZoneService(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<ITimeZoneService>(system, - system.GetTimeManager().GetTimeZoneContentManager()); -} - -void Module::Interface::GetStandardLocalSystemClock(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<ISystemClock>(system.GetTimeManager().GetStandardLocalSystemClockCore(), - system); -} - -void Module::Interface::IsStandardNetworkSystemClockAccuracySufficient(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - auto& clock_core{system.GetTimeManager().GetStandardNetworkSystemClockCore()}; - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<u32>(clock_core.IsStandardNetworkSystemClockAccuracySufficient(system)); -} - -void Module::Interface::CalculateMonotonicSystemClockBaseTimePoint(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - - auto& steady_clock_core{system.GetTimeManager().GetStandardSteadyClockCore()}; - if (!steady_clock_core.IsInitialized()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERROR_UNINITIALIZED_CLOCK); - return; - } - - IPC::RequestParser rp{ctx}; - const auto context{rp.PopRaw<Clock::SystemClockContext>()}; - const auto current_time_point{steady_clock_core.GetCurrentTimePoint(system)}; - - if (current_time_point.clock_source_id == context.steady_time_point.clock_source_id) { - const auto ticks{Clock::TimeSpanType::FromTicks<Core::Hardware::CNTFREQ>( - system.CoreTiming().GetClockTicks())}; - const s64 base_time_point{context.offset + current_time_point.time_point - - ticks.ToSeconds()}; - IPC::ResponseBuilder rb{ctx, (sizeof(s64) / 4) + 2}; - rb.Push(ResultSuccess); - rb.PushRaw(base_time_point); - return; - } - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERROR_TIME_MISMATCH); -} - -void Module::Interface::GetClockSnapshot(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto type{rp.PopEnum<Clock::TimeType>()}; - - LOG_DEBUG(Service_Time, "called, type={}", type); - - Clock::SystemClockContext user_context{}; - if (const Result result{ - system.GetTimeManager().GetStandardUserSystemClockCore().GetClockContext(system, - user_context)}; - result.IsError()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - Clock::SystemClockContext network_context{}; - if (const Result result{ - system.GetTimeManager().GetStandardNetworkSystemClockCore().GetClockContext( - system, network_context)}; - result.IsError()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - Clock::ClockSnapshot clock_snapshot{}; - if (const Result result{GetClockSnapshotFromSystemClockContextInternal( - &ctx.GetThread(), user_context, network_context, type, clock_snapshot)}; - result.IsError()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - ctx.WriteBuffer(clock_snapshot); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Module::Interface::GetClockSnapshotFromSystemClockContext(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto type{rp.PopEnum<Clock::TimeType>()}; - - rp.Skip(1, false); - - const Clock::SystemClockContext user_context{rp.PopRaw<Clock::SystemClockContext>()}; - const Clock::SystemClockContext network_context{rp.PopRaw<Clock::SystemClockContext>()}; - - LOG_DEBUG(Service_Time, "called, type={}", type); - - Clock::ClockSnapshot clock_snapshot{}; - if (const Result result{GetClockSnapshotFromSystemClockContextInternal( - &ctx.GetThread(), user_context, network_context, type, clock_snapshot)}; - result != ResultSuccess) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - ctx.WriteBuffer(clock_snapshot); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Module::Interface::CalculateStandardUserSystemClockDifferenceByUser(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - - Clock::ClockSnapshot snapshot_a; - Clock::ClockSnapshot snapshot_b; - - const auto snapshot_a_data = ctx.ReadBuffer(0); - const auto snapshot_b_data = ctx.ReadBuffer(1); - - std::memcpy(&snapshot_a, snapshot_a_data.data(), sizeof(Clock::ClockSnapshot)); - std::memcpy(&snapshot_b, snapshot_b_data.data(), sizeof(Clock::ClockSnapshot)); - - auto time_span_type{Clock::TimeSpanType::FromSeconds(snapshot_b.user_context.offset - - snapshot_a.user_context.offset)}; - - if ((snapshot_b.user_context.steady_time_point.clock_source_id != - snapshot_a.user_context.steady_time_point.clock_source_id) || - (snapshot_b.is_automatic_correction_enabled && - snapshot_a.is_automatic_correction_enabled)) { - time_span_type.nanoseconds = 0; - } - - IPC::ResponseBuilder rb{ctx, (sizeof(s64) / 4) + 2}; - rb.Push(ResultSuccess); - rb.PushRaw(time_span_type.nanoseconds); -} - -void Module::Interface::CalculateSpanBetween(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - - Clock::ClockSnapshot snapshot_a; - Clock::ClockSnapshot snapshot_b; - - const auto snapshot_a_data = ctx.ReadBuffer(0); - const auto snapshot_b_data = ctx.ReadBuffer(1); - - std::memcpy(&snapshot_a, snapshot_a_data.data(), sizeof(Clock::ClockSnapshot)); - std::memcpy(&snapshot_b, snapshot_b_data.data(), sizeof(Clock::ClockSnapshot)); - - Clock::TimeSpanType time_span_type{}; - s64 span{}; - - if (const Result result{snapshot_a.steady_clock_time_point.GetSpanBetween( - snapshot_b.steady_clock_time_point, span)}; - result != ResultSuccess) { - if (snapshot_a.network_time && snapshot_b.network_time) { - time_span_type = - Clock::TimeSpanType::FromSeconds(snapshot_b.network_time - snapshot_a.network_time); - } else { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERROR_TIME_NOT_FOUND); - return; - } - } else { - time_span_type = Clock::TimeSpanType::FromSeconds(span); - } - - IPC::ResponseBuilder rb{ctx, (sizeof(s64) / 4) + 2}; - rb.Push(ResultSuccess); - rb.PushRaw(time_span_type.nanoseconds); -} - -void Module::Interface::GetSharedMemoryNativeHandle(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(&system.Kernel().GetTimeSharedMem()); -} - -Module::Interface::Interface(std::shared_ptr<Module> module_, Core::System& system_, - const char* name) - : ServiceFramework{system_, name}, module{std::move(module_)} {} - -Module::Interface::~Interface() = default; - -void LoopProcess(Core::System& system) { - auto server_manager = std::make_unique<ServerManager>(system); - auto module{std::make_shared<Module>()}; - - server_manager->RegisterNamedService("time:a", - std::make_shared<Time>(module, system, "time:a")); - server_manager->RegisterNamedService("time:s", - std::make_shared<Time>(module, system, "time:s")); - server_manager->RegisterNamedService("time:u", - std::make_shared<Time>(module, system, "time:u")); - ServerManager::RunServer(std::move(server_manager)); -} - -} // namespace Service::Time diff --git a/src/core/hle/service/time/time.h b/src/core/hle/service/time/time.h deleted file mode 100644 index b2d754ef3..000000000 --- a/src/core/hle/service/time/time.h +++ /dev/null @@ -1,51 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/service.h" -#include "core/hle/service/time/clock_types.h" - -namespace Core { -class System; -} - -namespace Service::Time { - -class Module final { -public: - Module() = default; - - class Interface : public ServiceFramework<Interface> { - public: - explicit Interface(std::shared_ptr<Module> module_, Core::System& system_, - const char* name); - ~Interface() override; - - void GetStandardUserSystemClock(HLERequestContext& ctx); - void GetStandardNetworkSystemClock(HLERequestContext& ctx); - void GetStandardSteadyClock(HLERequestContext& ctx); - void GetTimeZoneService(HLERequestContext& ctx); - void GetStandardLocalSystemClock(HLERequestContext& ctx); - void IsStandardNetworkSystemClockAccuracySufficient(HLERequestContext& ctx); - void CalculateMonotonicSystemClockBaseTimePoint(HLERequestContext& ctx); - void GetClockSnapshot(HLERequestContext& ctx); - void GetClockSnapshotFromSystemClockContext(HLERequestContext& ctx); - void CalculateStandardUserSystemClockDifferenceByUser(HLERequestContext& ctx); - void CalculateSpanBetween(HLERequestContext& ctx); - void GetSharedMemoryNativeHandle(HLERequestContext& ctx); - - private: - Result GetClockSnapshotFromSystemClockContextInternal( - Kernel::KThread* thread, Clock::SystemClockContext user_context, - Clock::SystemClockContext network_context, Clock::TimeType type, - Clock::ClockSnapshot& cloc_snapshot); - - protected: - std::shared_ptr<Module> module; - }; -}; - -void LoopProcess(Core::System& system); - -} // namespace Service::Time diff --git a/src/core/hle/service/time/time_interface.cpp b/src/core/hle/service/time/time_interface.cpp deleted file mode 100644 index 0c53e98ee..000000000 --- a/src/core/hle/service/time/time_interface.cpp +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hle/service/time/time_interface.h" - -namespace Service::Time { - -Time::Time(std::shared_ptr<Module> module_, Core::System& system_, const char* name_) - : Interface{std::move(module_), system_, name_} { - // clang-format off - static const FunctionInfo functions[] = { - {0, &Time::GetStandardUserSystemClock, "GetStandardUserSystemClock"}, - {1, &Time::GetStandardNetworkSystemClock, "GetStandardNetworkSystemClock"}, - {2, &Time::GetStandardSteadyClock, "GetStandardSteadyClock"}, - {3, &Time::GetTimeZoneService, "GetTimeZoneService"}, - {4, &Time::GetStandardLocalSystemClock, "GetStandardLocalSystemClock"}, - {5, nullptr, "GetEphemeralNetworkSystemClock"}, - {20, &Time::GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"}, - {30, nullptr, "GetStandardNetworkClockOperationEventReadableHandle"}, - {31, nullptr, "GetEphemeralNetworkClockOperationEventReadableHandle"}, - {50, nullptr, "SetStandardSteadyClockInternalOffset"}, - {51, nullptr, "GetStandardSteadyClockRtcValue"}, - {100, nullptr, "IsStandardUserSystemClockAutomaticCorrectionEnabled"}, - {101, nullptr, "SetStandardUserSystemClockAutomaticCorrectionEnabled"}, - {102, nullptr, "GetStandardUserSystemClockInitialYear"}, - {200, &Time::IsStandardNetworkSystemClockAccuracySufficient, "IsStandardNetworkSystemClockAccuracySufficient"}, - {201, nullptr, "GetStandardUserSystemClockAutomaticCorrectionUpdatedTime"}, - {300, &Time::CalculateMonotonicSystemClockBaseTimePoint, "CalculateMonotonicSystemClockBaseTimePoint"}, - {400, &Time::GetClockSnapshot, "GetClockSnapshot"}, - {401, &Time::GetClockSnapshotFromSystemClockContext, "GetClockSnapshotFromSystemClockContext"}, - {500, &Time::CalculateStandardUserSystemClockDifferenceByUser, "CalculateStandardUserSystemClockDifferenceByUser"}, - {501, &Time::CalculateSpanBetween, "CalculateSpanBetween"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -Time::~Time() = default; - -} // namespace Service::Time diff --git a/src/core/hle/service/time/time_interface.h b/src/core/hle/service/time/time_interface.h deleted file mode 100644 index ceeb0e5ef..000000000 --- a/src/core/hle/service/time/time_interface.h +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/time/time.h" - -namespace Core { -class System; -} - -namespace Service::Time { - -class Time final : public Module::Interface { -public: - explicit Time(std::shared_ptr<Module> time, Core::System& system_, const char* name_); - ~Time() override; -}; - -} // namespace Service::Time diff --git a/src/core/hle/service/time/time_manager.cpp b/src/core/hle/service/time/time_manager.cpp deleted file mode 100644 index fa0fd0531..000000000 --- a/src/core/hle/service/time/time_manager.cpp +++ /dev/null @@ -1,293 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include <chrono> -#include <ctime> - -#include "common/settings.h" -#include "common/time_zone.h" -#include "core/hle/service/time/ephemeral_network_system_clock_context_writer.h" -#include "core/hle/service/time/ephemeral_network_system_clock_core.h" -#include "core/hle/service/time/local_system_clock_context_writer.h" -#include "core/hle/service/time/network_system_clock_context_writer.h" -#include "core/hle/service/time/tick_based_steady_clock_core.h" -#include "core/hle/service/time/time_manager.h" - -namespace Service::Time { -namespace { -constexpr Clock::TimeSpanType standard_network_clock_accuracy{0x0009356907420000ULL}; - -s64 GetSecondsSinceEpoch() { - const auto time_since_epoch = std::chrono::system_clock::now().time_since_epoch(); - return std::chrono::duration_cast<std::chrono::seconds>(time_since_epoch).count() + - Settings::values.custom_rtc_differential; -} -} // Anonymous namespace - -struct TimeManager::Impl final { - explicit Impl(Core::System& system) - : shared_memory{system}, standard_local_system_clock_core{standard_steady_clock_core}, - standard_network_system_clock_core{standard_steady_clock_core}, - standard_user_system_clock_core{standard_local_system_clock_core, - standard_network_system_clock_core, system}, - ephemeral_network_system_clock_core{tick_based_steady_clock_core}, - local_system_clock_context_writer{ - std::make_shared<Clock::LocalSystemClockContextWriter>(shared_memory)}, - network_system_clock_context_writer{ - std::make_shared<Clock::NetworkSystemClockContextWriter>(shared_memory)}, - ephemeral_network_system_clock_context_writer{ - std::make_shared<Clock::EphemeralNetworkSystemClockContextWriter>()}, - time_zone_content_manager{system} { - - const auto system_time{Clock::TimeSpanType::FromSeconds(GetSecondsSinceEpoch())}; - SetupStandardSteadyClock(system, Common::UUID::MakeRandom(), system_time, {}, {}); - SetupStandardLocalSystemClock(system, {}, system_time.ToSeconds()); - - Clock::SystemClockContext clock_context{}; - standard_local_system_clock_core.GetClockContext(system, clock_context); - - SetupStandardNetworkSystemClock(clock_context, standard_network_clock_accuracy); - SetupStandardUserSystemClock(system, {}, Clock::SteadyClockTimePoint::GetRandom()); - SetupEphemeralNetworkSystemClock(); - } - - ~Impl() = default; - - Clock::StandardSteadyClockCore& GetStandardSteadyClockCore() { - return standard_steady_clock_core; - } - - const Clock::StandardSteadyClockCore& GetStandardSteadyClockCore() const { - return standard_steady_clock_core; - } - - Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore() { - return standard_local_system_clock_core; - } - - const Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore() const { - return standard_local_system_clock_core; - } - - Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore() { - return standard_network_system_clock_core; - } - - const Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore() const { - return standard_network_system_clock_core; - } - - Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore() { - return standard_user_system_clock_core; - } - - const Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore() const { - return standard_user_system_clock_core; - } - - TimeZone::TimeZoneContentManager& GetTimeZoneContentManager() { - return time_zone_content_manager; - } - - const TimeZone::TimeZoneContentManager& GetTimeZoneContentManager() const { - return time_zone_content_manager; - } - - SharedMemory& GetSharedMemory() { - return shared_memory; - } - - const SharedMemory& GetSharedMemory() const { - return shared_memory; - } - - void SetupTimeZoneManager(std::string location_name, - Clock::SteadyClockTimePoint time_zone_updated_time_point, - std::vector<std::string> location_names, u128 time_zone_rule_version, - FileSys::VirtualFile& vfs_file) { - if (time_zone_content_manager.GetTimeZoneManager().SetDeviceLocationNameWithTimeZoneRule( - location_name, vfs_file) != ResultSuccess) { - ASSERT(false); - return; - } - - time_zone_content_manager.GetTimeZoneManager().SetUpdatedTime(time_zone_updated_time_point); - time_zone_content_manager.GetTimeZoneManager().SetTotalLocationNameCount( - location_names.size()); - time_zone_content_manager.GetTimeZoneManager().SetLocationNames(location_names); - time_zone_content_manager.GetTimeZoneManager().SetTimeZoneRuleVersion( - time_zone_rule_version); - time_zone_content_manager.GetTimeZoneManager().MarkAsInitialized(); - } - - void SetupStandardSteadyClock(Core::System& system_, Common::UUID clock_source_id, - Clock::TimeSpanType setup_value, - Clock::TimeSpanType internal_offset, bool is_rtc_reset_detected) { - standard_steady_clock_core.SetClockSourceId(clock_source_id); - standard_steady_clock_core.SetSetupValue(setup_value); - standard_steady_clock_core.SetInternalOffset(internal_offset); - standard_steady_clock_core.MarkAsInitialized(); - - const auto current_time_point{standard_steady_clock_core.GetCurrentRawTimePoint(system_)}; - shared_memory.SetupStandardSteadyClock(clock_source_id, current_time_point); - } - - void SetupStandardLocalSystemClock(Core::System& system_, - Clock::SystemClockContext clock_context, s64 posix_time) { - standard_local_system_clock_core.SetUpdateCallbackInstance( - local_system_clock_context_writer); - - const auto current_time_point{ - standard_local_system_clock_core.GetSteadyClockCore().GetCurrentTimePoint(system_)}; - if (current_time_point.clock_source_id == clock_context.steady_time_point.clock_source_id) { - standard_local_system_clock_core.SetSystemClockContext(clock_context); - } else { - if (standard_local_system_clock_core.SetCurrentTime(system_, posix_time) != - ResultSuccess) { - ASSERT(false); - return; - } - } - - standard_local_system_clock_core.MarkAsInitialized(); - } - - void SetupStandardNetworkSystemClock(Clock::SystemClockContext clock_context, - Clock::TimeSpanType sufficient_accuracy) { - standard_network_system_clock_core.SetUpdateCallbackInstance( - network_system_clock_context_writer); - - if (standard_network_system_clock_core.SetSystemClockContext(clock_context) != - ResultSuccess) { - ASSERT(false); - return; - } - - standard_network_system_clock_core.SetStandardNetworkClockSufficientAccuracy( - sufficient_accuracy); - standard_network_system_clock_core.MarkAsInitialized(); - } - - void SetupStandardUserSystemClock(Core::System& system_, bool is_automatic_correction_enabled, - Clock::SteadyClockTimePoint steady_clock_time_point) { - if (standard_user_system_clock_core.SetAutomaticCorrectionEnabled( - system_, is_automatic_correction_enabled) != ResultSuccess) { - ASSERT(false); - return; - } - - standard_user_system_clock_core.SetAutomaticCorrectionUpdatedTime(steady_clock_time_point); - standard_user_system_clock_core.MarkAsInitialized(); - shared_memory.SetAutomaticCorrectionEnabled(is_automatic_correction_enabled); - } - - void SetupEphemeralNetworkSystemClock() { - ephemeral_network_system_clock_core.SetUpdateCallbackInstance( - ephemeral_network_system_clock_context_writer); - ephemeral_network_system_clock_core.MarkAsInitialized(); - } - - void UpdateLocalSystemClockTime(Core::System& system_, s64 posix_time) { - const auto timespan{Clock::TimeSpanType::FromSeconds(posix_time)}; - if (GetStandardLocalSystemClockCore() - .SetCurrentTime(system_, timespan.ToSeconds()) - .IsError()) { - ASSERT(false); - return; - } - } - - SharedMemory shared_memory; - - Clock::StandardSteadyClockCore standard_steady_clock_core; - Clock::TickBasedSteadyClockCore tick_based_steady_clock_core; - Clock::StandardLocalSystemClockCore standard_local_system_clock_core; - Clock::StandardNetworkSystemClockCore standard_network_system_clock_core; - Clock::StandardUserSystemClockCore standard_user_system_clock_core; - Clock::EphemeralNetworkSystemClockCore ephemeral_network_system_clock_core; - - std::shared_ptr<Clock::LocalSystemClockContextWriter> local_system_clock_context_writer; - std::shared_ptr<Clock::NetworkSystemClockContextWriter> network_system_clock_context_writer; - std::shared_ptr<Clock::EphemeralNetworkSystemClockContextWriter> - ephemeral_network_system_clock_context_writer; - - TimeZone::TimeZoneContentManager time_zone_content_manager; -}; - -TimeManager::TimeManager(Core::System& system_) : system{system_} {} - -TimeManager::~TimeManager() = default; - -void TimeManager::Initialize() { - impl = std::make_unique<Impl>(system); - - // Time zones can only be initialized after impl is valid - impl->time_zone_content_manager.Initialize(*this); -} - -Clock::StandardSteadyClockCore& TimeManager::GetStandardSteadyClockCore() { - return impl->standard_steady_clock_core; -} - -const Clock::StandardSteadyClockCore& TimeManager::GetStandardSteadyClockCore() const { - return impl->standard_steady_clock_core; -} - -Clock::StandardLocalSystemClockCore& TimeManager::GetStandardLocalSystemClockCore() { - return impl->standard_local_system_clock_core; -} - -const Clock::StandardLocalSystemClockCore& TimeManager::GetStandardLocalSystemClockCore() const { - return impl->standard_local_system_clock_core; -} - -Clock::StandardNetworkSystemClockCore& TimeManager::GetStandardNetworkSystemClockCore() { - return impl->standard_network_system_clock_core; -} - -const Clock::StandardNetworkSystemClockCore& TimeManager::GetStandardNetworkSystemClockCore() - const { - return impl->standard_network_system_clock_core; -} - -Clock::StandardUserSystemClockCore& TimeManager::GetStandardUserSystemClockCore() { - return impl->standard_user_system_clock_core; -} - -const Clock::StandardUserSystemClockCore& TimeManager::GetStandardUserSystemClockCore() const { - return impl->standard_user_system_clock_core; -} - -TimeZone::TimeZoneContentManager& TimeManager::GetTimeZoneContentManager() { - return impl->time_zone_content_manager; -} - -const TimeZone::TimeZoneContentManager& TimeManager::GetTimeZoneContentManager() const { - return impl->time_zone_content_manager; -} - -SharedMemory& TimeManager::GetSharedMemory() { - return impl->shared_memory; -} - -const SharedMemory& TimeManager::GetSharedMemory() const { - return impl->shared_memory; -} - -void TimeManager::Shutdown() { - impl.reset(); -} - -void TimeManager::UpdateLocalSystemClockTime(s64 posix_time) { - impl->UpdateLocalSystemClockTime(system, posix_time); -} - -void TimeManager::SetupTimeZoneManager(std::string location_name, - Clock::SteadyClockTimePoint time_zone_updated_time_point, - std::vector<std::string> location_names, - u128 time_zone_rule_version, - FileSys::VirtualFile& vfs_file) { - impl->SetupTimeZoneManager(location_name, time_zone_updated_time_point, location_names, - time_zone_rule_version, vfs_file); -} -} // namespace Service::Time diff --git a/src/core/hle/service/time/time_manager.h b/src/core/hle/service/time/time_manager.h deleted file mode 100644 index 84572dbfa..000000000 --- a/src/core/hle/service/time/time_manager.h +++ /dev/null @@ -1,74 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "common/common_types.h" -#include "core/file_sys/vfs_types.h" -#include "core/hle/service/time/clock_types.h" -#include "core/hle/service/time/standard_local_system_clock_core.h" -#include "core/hle/service/time/standard_network_system_clock_core.h" -#include "core/hle/service/time/standard_steady_clock_core.h" -#include "core/hle/service/time/standard_user_system_clock_core.h" -#include "core/hle/service/time/time_sharedmemory.h" -#include "core/hle/service/time/time_zone_content_manager.h" - -namespace Service::Time { - -namespace Clock { -class EphemeralNetworkSystemClockContextWriter; -class LocalSystemClockContextWriter; -class NetworkSystemClockContextWriter; -} // namespace Clock - -// Parts of this implementation were based on Ryujinx (https://github.com/Ryujinx/Ryujinx/pull/783). -// This code was released under public domain. - -class TimeManager final { -public: - explicit TimeManager(Core::System& system_); - ~TimeManager(); - - void Initialize(); - - Clock::StandardSteadyClockCore& GetStandardSteadyClockCore(); - - const Clock::StandardSteadyClockCore& GetStandardSteadyClockCore() const; - - Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore(); - - const Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore() const; - - Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore(); - - const Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore() const; - - Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore(); - - const Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore() const; - - TimeZone::TimeZoneContentManager& GetTimeZoneContentManager(); - - const TimeZone::TimeZoneContentManager& GetTimeZoneContentManager() const; - - void UpdateLocalSystemClockTime(s64 posix_time); - - SharedMemory& GetSharedMemory(); - - const SharedMemory& GetSharedMemory() const; - - void Shutdown(); - - void SetupTimeZoneManager(std::string location_name, - Clock::SteadyClockTimePoint time_zone_updated_time_point, - std::vector<std::string> location_names, u128 time_zone_rule_version, - FileSys::VirtualFile& vfs_file); - -private: - Core::System& system; - - struct Impl; - std::unique_ptr<Impl> impl; -}; - -} // namespace Service::Time diff --git a/src/core/hle/service/time/time_sharedmemory.cpp b/src/core/hle/service/time/time_sharedmemory.cpp deleted file mode 100644 index a00676669..000000000 --- a/src/core/hle/service/time/time_sharedmemory.cpp +++ /dev/null @@ -1,69 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/core.h" -#include "core/core_timing.h" -#include "core/hardware_properties.h" -#include "core/hle/kernel/kernel.h" -#include "core/hle/service/time/clock_types.h" -#include "core/hle/service/time/steady_clock_core.h" -#include "core/hle/service/time/time_sharedmemory.h" - -namespace Service::Time { - -static constexpr std::size_t SHARED_MEMORY_SIZE{0x1000}; - -SharedMemory::SharedMemory(Core::System& system_) : system(system_) { - std::memset(system.Kernel().GetTimeSharedMem().GetPointer(), 0, SHARED_MEMORY_SIZE); -} - -SharedMemory::~SharedMemory() = default; - -void SharedMemory::SetupStandardSteadyClock(const Common::UUID& clock_source_id, - Clock::TimeSpanType current_time_point) { - const Clock::TimeSpanType ticks_time_span{ - Clock::TimeSpanType::FromTicks<Core::Hardware::CNTFREQ>( - system.CoreTiming().GetClockTicks())}; - const Clock::SteadyClockContext context{ - static_cast<u64>(current_time_point.nanoseconds - ticks_time_span.nanoseconds), - clock_source_id}; - StoreToLockFreeAtomicType(&GetFormat()->standard_steady_clock_timepoint, context); -} - -void SharedMemory::UpdateLocalSystemClockContext(const Clock::SystemClockContext& context) { - // lower and upper are related to the measurement point for the steady time point, - // and compare equal on boot - const s64 time_point_ns = context.steady_time_point.time_point * 1'000'000'000LL; - - // This adjusts for some sort of time skew - // Both 0 on boot - const s64 diff_scale = 0; - const u32 shift_amount = 0; - - const Clock::ContinuousAdjustmentTimePoint adjustment{ - .measurement_offset = system.CoreTiming().GetGlobalTimeNs().count(), - .diff_scale = diff_scale, - .shift_amount = shift_amount, - .lower = time_point_ns, - .upper = time_point_ns, - .clock_source_id = context.steady_time_point.clock_source_id, - }; - - StoreToLockFreeAtomicType(&GetFormat()->continuous_adjustment_timepoint, adjustment); - StoreToLockFreeAtomicType(&GetFormat()->standard_local_system_clock_context, context); -} - -void SharedMemory::UpdateNetworkSystemClockContext(const Clock::SystemClockContext& context) { - StoreToLockFreeAtomicType(&GetFormat()->standard_network_system_clock_context, context); -} - -void SharedMemory::SetAutomaticCorrectionEnabled(bool is_enabled) { - StoreToLockFreeAtomicType( - &GetFormat()->is_standard_user_system_clock_automatic_correction_enabled, is_enabled); -} - -SharedMemory::Format* SharedMemory::GetFormat() { - return reinterpret_cast<SharedMemory::Format*>(system.Kernel().GetTimeSharedMem().GetPointer()); -} - -} // namespace Service::Time diff --git a/src/core/hle/service/time/time_sharedmemory.h b/src/core/hle/service/time/time_sharedmemory.h deleted file mode 100644 index c89be9765..000000000 --- a/src/core/hle/service/time/time_sharedmemory.h +++ /dev/null @@ -1,89 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "common/common_types.h" -#include "common/uuid.h" -#include "core/hle/kernel/k_shared_memory.h" -#include "core/hle/service/time/clock_types.h" - -namespace Service::Time { - -// Note: this type is not safe for concurrent writes. -template <typename T> -struct LockFreeAtomicType { - u32 counter_; - std::array<T, 2> value_; -}; - -template <typename T> -static inline void StoreToLockFreeAtomicType(LockFreeAtomicType<T>* p, const T& value) { - // Get the current counter. - auto counter = p->counter_; - - // Increment the counter. - ++counter; - - // Store the updated value. - p->value_[counter % 2] = value; - - // Fence memory. - std::atomic_thread_fence(std::memory_order_release); - - // Set the updated counter. - p->counter_ = counter; -} - -template <typename T> -static inline T LoadFromLockFreeAtomicType(const LockFreeAtomicType<T>* p) { - while (true) { - // Get the counter. - auto counter = p->counter_; - - // Get the value. - auto value = p->value_[counter % 2]; - - // Fence memory. - std::atomic_thread_fence(std::memory_order_acquire); - - // Check that the counter matches. - if (counter == p->counter_) { - return value; - } - } -} - -class SharedMemory final { -public: - explicit SharedMemory(Core::System& system_); - ~SharedMemory(); - - // Shared memory format - struct Format { - LockFreeAtomicType<Clock::StandardSteadyClockTimePointType> standard_steady_clock_timepoint; - LockFreeAtomicType<Clock::SystemClockContext> standard_local_system_clock_context; - LockFreeAtomicType<Clock::SystemClockContext> standard_network_system_clock_context; - LockFreeAtomicType<bool> is_standard_user_system_clock_automatic_correction_enabled; - LockFreeAtomicType<Clock::ContinuousAdjustmentTimePoint> continuous_adjustment_timepoint; - }; - static_assert(offsetof(Format, standard_steady_clock_timepoint) == 0x0); - static_assert(offsetof(Format, standard_local_system_clock_context) == 0x38); - static_assert(offsetof(Format, standard_network_system_clock_context) == 0x80); - static_assert(offsetof(Format, is_standard_user_system_clock_automatic_correction_enabled) == - 0xc8); - static_assert(offsetof(Format, continuous_adjustment_timepoint) == 0xd0); - static_assert(sizeof(Format) == 0x148, "Format is an invalid size"); - - void SetupStandardSteadyClock(const Common::UUID& clock_source_id, - Clock::TimeSpanType current_time_point); - void UpdateLocalSystemClockContext(const Clock::SystemClockContext& context); - void UpdateNetworkSystemClockContext(const Clock::SystemClockContext& context); - void SetAutomaticCorrectionEnabled(bool is_enabled); - Format* GetFormat(); - -private: - Core::System& system; -}; - -} // namespace Service::Time diff --git a/src/core/hle/service/time/time_zone_content_manager.cpp b/src/core/hle/service/time/time_zone_content_manager.cpp deleted file mode 100644 index 1b96de37a..000000000 --- a/src/core/hle/service/time/time_zone_content_manager.cpp +++ /dev/null @@ -1,151 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include <chrono> -#include <sstream> -#include <utility> - -#include "common/logging/log.h" -#include "common/settings.h" -#include "common/time_zone.h" -#include "core/core.h" -#include "core/file_sys/content_archive.h" -#include "core/file_sys/nca_metadata.h" -#include "core/file_sys/registered_cache.h" -#include "core/file_sys/romfs.h" -#include "core/file_sys/system_archive/system_archive.h" -#include "core/file_sys/vfs.h" -#include "core/file_sys/vfs_types.h" -#include "core/hle/result.h" -#include "core/hle/service/filesystem/filesystem.h" -#include "core/hle/service/time/errors.h" -#include "core/hle/service/time/time_manager.h" -#include "core/hle/service/time/time_zone_content_manager.h" - -namespace Service::Time::TimeZone { - -constexpr u64 time_zone_binary_titleid{0x010000000000080E}; - -static FileSys::VirtualDir GetTimeZoneBinary(Core::System& system) { - const auto* nand{system.GetFileSystemController().GetSystemNANDContents()}; - const auto nca{nand->GetEntry(time_zone_binary_titleid, FileSys::ContentRecordType::Data)}; - - FileSys::VirtualFile romfs; - if (nca) { - romfs = nca->GetRomFS(); - } - - if (!romfs) { - romfs = FileSys::SystemArchive::SynthesizeSystemArchive(time_zone_binary_titleid); - } - - if (!romfs) { - LOG_ERROR(Service_Time, "Failed to find or synthesize {:016X!}", time_zone_binary_titleid); - return {}; - } - - return FileSys::ExtractRomFS(romfs); -} - -static std::vector<std::string> BuildLocationNameCache( - const FileSys::VirtualDir& time_zone_binary) { - if (!time_zone_binary) { - LOG_ERROR(Service_Time, "Failed to extract RomFS for {:016X}!", time_zone_binary_titleid); - return {}; - } - - const FileSys::VirtualFile binary_list{time_zone_binary->GetFile("binaryList.txt")}; - if (!binary_list) { - LOG_ERROR(Service_Time, "{:016X} has no file binaryList.txt!", time_zone_binary_titleid); - return {}; - } - - std::vector<char> raw_data(binary_list->GetSize() + 1); - binary_list->ReadBytes<char>(raw_data.data(), binary_list->GetSize()); - - std::stringstream data_stream{raw_data.data()}; - std::string name; - std::vector<std::string> location_name_cache; - while (std::getline(data_stream, name)) { - name.pop_back(); // Remove carriage return - location_name_cache.emplace_back(std::move(name)); - } - return location_name_cache; -} - -TimeZoneContentManager::TimeZoneContentManager(Core::System& system_) - : system{system_}, time_zone_binary{GetTimeZoneBinary(system)}, - location_name_cache{BuildLocationNameCache(time_zone_binary)} {} - -void TimeZoneContentManager::Initialize(TimeManager& time_manager) { - const auto timezone_setting = - Settings::GetTimeZoneString(Settings::values.time_zone_index.GetValue()); - - if (FileSys::VirtualFile vfs_file; - GetTimeZoneInfoFile(timezone_setting, vfs_file) == ResultSuccess) { - const auto time_point{ - time_manager.GetStandardSteadyClockCore().GetCurrentTimePoint(system)}; - time_manager.SetupTimeZoneManager(timezone_setting, time_point, location_name_cache, {}, - vfs_file); - } else { - time_zone_manager.MarkAsInitialized(); - } -} - -Result TimeZoneContentManager::LoadTimeZoneRule(TimeZoneRule& rules, - const std::string& location_name) const { - FileSys::VirtualFile vfs_file; - if (const Result result{GetTimeZoneInfoFile(location_name, vfs_file)}; - result != ResultSuccess) { - return result; - } - - return time_zone_manager.ParseTimeZoneRuleBinary(rules, vfs_file); -} - -bool TimeZoneContentManager::IsLocationNameValid(const std::string& location_name) const { - return std::find(location_name_cache.begin(), location_name_cache.end(), location_name) != - location_name_cache.end(); -} - -Result TimeZoneContentManager::GetTimeZoneInfoFile(const std::string& location_name, - FileSys::VirtualFile& vfs_file) const { - if (!IsLocationNameValid(location_name)) { - return ERROR_TIME_NOT_FOUND; - } - - if (!time_zone_binary) { - LOG_ERROR(Service_Time, "Failed to extract RomFS for {:016X}!", time_zone_binary_titleid); - return ERROR_TIME_NOT_FOUND; - } - - const FileSys::VirtualDir zoneinfo_dir{time_zone_binary->GetSubdirectory("zoneinfo")}; - if (!zoneinfo_dir) { - LOG_ERROR(Service_Time, "{:016X} has no directory zoneinfo!", time_zone_binary_titleid); - return ERROR_TIME_NOT_FOUND; - } - - vfs_file = zoneinfo_dir->GetFileRelative(location_name); - if (!vfs_file) { - LOG_WARNING(Service_Time, "{:016X} has no file \"{}\"! Using system timezone.", - time_zone_binary_titleid, location_name); - const std::string system_time_zone{Common::TimeZone::FindSystemTimeZone()}; - vfs_file = zoneinfo_dir->GetFile(system_time_zone); - } - - if (!vfs_file) { - LOG_WARNING(Service_Time, "{:016X} has no file \"{}\"! Using default timezone.", - time_zone_binary_titleid, location_name); - vfs_file = zoneinfo_dir->GetFile(Common::TimeZone::GetDefaultTimeZone()); - } - - if (!vfs_file) { - LOG_ERROR(Service_Time, "{:016X} has no file \"{}\"!", time_zone_binary_titleid, - location_name); - return ERROR_TIME_NOT_FOUND; - } - - return ResultSuccess; -} - -} // namespace Service::Time::TimeZone diff --git a/src/core/hle/service/time/time_zone_content_manager.h b/src/core/hle/service/time/time_zone_content_manager.h deleted file mode 100644 index a6f9698bc..000000000 --- a/src/core/hle/service/time/time_zone_content_manager.h +++ /dev/null @@ -1,49 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <string> -#include <vector> - -#include "core/file_sys/vfs_types.h" -#include "core/hle/service/time/time_zone_manager.h" - -namespace Core { -class System; -} - -namespace Service::Time { -class TimeManager; -} - -namespace Service::Time::TimeZone { - -class TimeZoneContentManager final { -public: - explicit TimeZoneContentManager(Core::System& system_); - - void Initialize(TimeManager& time_manager); - - TimeZoneManager& GetTimeZoneManager() { - return time_zone_manager; - } - - const TimeZoneManager& GetTimeZoneManager() const { - return time_zone_manager; - } - - Result LoadTimeZoneRule(TimeZoneRule& rules, const std::string& location_name) const; - -private: - bool IsLocationNameValid(const std::string& location_name) const; - Result GetTimeZoneInfoFile(const std::string& location_name, - FileSys::VirtualFile& vfs_file) const; - - Core::System& system; - TimeZoneManager time_zone_manager; - const FileSys::VirtualDir time_zone_binary; - const std::vector<std::string> location_name_cache; -}; - -} // namespace Service::Time::TimeZone diff --git a/src/core/hle/service/time/time_zone_manager.cpp b/src/core/hle/service/time/time_zone_manager.cpp deleted file mode 100644 index 205371a26..000000000 --- a/src/core/hle/service/time/time_zone_manager.cpp +++ /dev/null @@ -1,1182 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include <climits> -#include <limits> - -#include "common/assert.h" -#include "common/logging/log.h" -#include "core/file_sys/content_archive.h" -#include "core/file_sys/nca_metadata.h" -#include "core/file_sys/registered_cache.h" -#include "core/hle/service/time/time_zone_manager.h" -#include "core/hle/service/time/time_zone_types.h" - -namespace Service::Time::TimeZone { - -static constexpr s32 epoch_year{1970}; -static constexpr s32 year_base{1900}; -static constexpr s32 epoch_week_day{4}; -static constexpr s32 seconds_per_minute{60}; -static constexpr s32 minutes_per_hour{60}; -static constexpr s32 hours_per_day{24}; -static constexpr s32 days_per_week{7}; -static constexpr s32 days_per_normal_year{365}; -static constexpr s32 days_per_leap_year{366}; -static constexpr s32 months_per_year{12}; -static constexpr s32 seconds_per_hour{seconds_per_minute * minutes_per_hour}; -static constexpr s32 seconds_per_day{seconds_per_hour * hours_per_day}; -static constexpr s32 years_per_repeat{400}; -static constexpr s64 average_seconds_per_year{31556952}; -static constexpr s64 seconds_per_repeat{years_per_repeat * average_seconds_per_year}; - -struct Rule { - enum class Type : u32 { JulianDay, DayOfYear, MonthNthDayOfWeek }; - Type rule_type{}; - s32 day{}; - s32 week{}; - s32 month{}; - s32 transition_time{}; -}; - -struct CalendarTimeInternal { - s64 year{}; - s8 month{}; - s8 day{}; - s8 hour{}; - s8 minute{}; - s8 second{}; - int Compare(const CalendarTimeInternal& other) const { - if (year != other.year) { - if (year < other.year) { - return -1; - } - return 1; - } - if (month != other.month) { - return month - other.month; - } - if (day != other.day) { - return day - other.day; - } - if (hour != other.hour) { - return hour - other.hour; - } - if (minute != other.minute) { - return minute - other.minute; - } - if (second != other.second) { - return second - other.second; - } - return {}; - } -}; - -template <typename TResult, typename TOperand> -static bool SafeAdd(TResult& result, TOperand op) { - result = result + op; - return true; -} - -template <typename TResult, typename TUnit, typename TBase> -static bool SafeNormalize(TResult& result, TUnit& unit, TBase base) { - TUnit delta{}; - if (unit >= 0) { - delta = unit / base; - } else { - delta = -1 - (-1 - unit) / base; - } - unit -= delta * base; - return SafeAdd(result, delta); -} - -template <typename T> -static constexpr bool IsLeapYear(T year) { - return ((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0); -} - -template <typename T> -static constexpr T GetYearLengthInDays(T year) { - return IsLeapYear(year) ? days_per_leap_year : days_per_normal_year; -} - -static constexpr s64 GetLeapDaysFromYearPositive(s64 year) { - return year / 4 - year / 100 + year / years_per_repeat; -} - -static constexpr s64 GetLeapDaysFromYear(s64 year) { - if (year < 0) { - return -1 - GetLeapDaysFromYearPositive(-1 - year); - } else { - return GetLeapDaysFromYearPositive(year); - } -} - -static constexpr s8 GetMonthLength(bool is_leap_year, int month) { - constexpr std::array<s8, 12> month_lengths{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - constexpr std::array<s8, 12> month_lengths_leap{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - return is_leap_year ? month_lengths_leap[month] : month_lengths[month]; -} - -static constexpr bool IsDigit(char value) { - return value >= '0' && value <= '9'; -} - -static constexpr int GetQZName(const char* name, int offset, char delimiter) { - while (name[offset] != '\0' && name[offset] != delimiter) { - offset++; - } - return offset; -} - -static constexpr int GetTZName(const char* name, int offset) { - char c; - - while ((c = name[offset]) != '\0' && !IsDigit(c) && c != ',' && c != '-' && c != '+') { - ++offset; - } - return offset; -} - -static constexpr bool GetInteger(const char* name, int& offset, int& value, int min, int max) { - value = 0; - char temp{name[offset]}; - if (!IsDigit(temp)) { - return {}; - } - do { - value = value * 10 + (temp - '0'); - if (value > max) { - return {}; - } - offset++; - temp = name[offset]; - } while (IsDigit(temp)); - - return value >= min; -} - -static constexpr bool GetSeconds(const char* name, int& offset, int& seconds) { - seconds = 0; - int value{}; - if (!GetInteger(name, offset, value, 0, hours_per_day * days_per_week - 1)) { - return {}; - } - seconds = value * seconds_per_hour; - - if (name[offset] == ':') { - offset++; - if (!GetInteger(name, offset, value, 0, minutes_per_hour - 1)) { - return {}; - } - seconds += value * seconds_per_minute; - if (name[offset] == ':') { - offset++; - if (!GetInteger(name, offset, value, 0, seconds_per_minute)) { - return {}; - } - seconds += value; - } - } - return true; -} - -static constexpr bool GetOffset(const char* name, int& offset, int& value) { - bool is_negative{}; - if (name[offset] == '-') { - is_negative = true; - offset++; - } else if (name[offset] == '+') { - offset++; - } - if (!GetSeconds(name, offset, value)) { - return {}; - } - if (is_negative) { - value = -value; - } - return true; -} - -static constexpr bool GetRule(const char* name, int& position, Rule& rule) { - bool is_valid{}; - if (name[position] == 'J') { - position++; - rule.rule_type = Rule::Type::JulianDay; - is_valid = GetInteger(name, position, rule.day, 1, days_per_normal_year); - } else if (name[position] == 'M') { - position++; - rule.rule_type = Rule::Type::MonthNthDayOfWeek; - is_valid = GetInteger(name, position, rule.month, 1, months_per_year); - if (!is_valid) { - return {}; - } - if (name[position++] != '.') { - return {}; - } - is_valid = GetInteger(name, position, rule.week, 1, 5); - if (!is_valid) { - return {}; - } - if (name[position++] != '.') { - return {}; - } - is_valid = GetInteger(name, position, rule.day, 0, days_per_week - 1); - } else if (isdigit(name[position])) { - rule.rule_type = Rule::Type::DayOfYear; - is_valid = GetInteger(name, position, rule.day, 0, days_per_leap_year - 1); - } else { - return {}; - } - if (!is_valid) { - return {}; - } - if (name[position] == '/') { - position++; - return GetOffset(name, position, rule.transition_time); - } else { - rule.transition_time = 2 * seconds_per_hour; - } - return true; -} - -static constexpr int TransitionTime(int year, Rule rule, int offset) { - int value{}; - switch (rule.rule_type) { - case Rule::Type::JulianDay: - value = (rule.day - 1) * seconds_per_day; - if (IsLeapYear(year) && rule.day >= 60) { - value += seconds_per_day; - } - break; - case Rule::Type::DayOfYear: - value = rule.day * seconds_per_day; - break; - case Rule::Type::MonthNthDayOfWeek: { - // Use Zeller's Congruence (https://en.wikipedia.org/wiki/Zeller%27s_congruence) to - // calculate the day of the week for any Julian or Gregorian calendar date. - const int m1{(rule.month + 9) % 12 + 1}; - const int yy0{(rule.month <= 2) ? (year - 1) : year}; - const int yy1{yy0 / 100}; - const int yy2{yy0 % 100}; - int day_of_week{((26 * m1 - 2) / 10 + 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7}; - - if (day_of_week < 0) { - day_of_week += days_per_week; - } - int day{rule.day - day_of_week}; - if (day < 0) { - day += days_per_week; - } - for (int i{1}; i < rule.week; i++) { - if (day + days_per_week >= GetMonthLength(IsLeapYear(year), rule.month - 1)) { - break; - } - day += days_per_week; - } - - value = day * seconds_per_day; - for (int index{}; index < rule.month - 1; ++index) { - value += GetMonthLength(IsLeapYear(year), index) * seconds_per_day; - } - break; - } - default: - ASSERT(false); - break; - } - return value + rule.transition_time + offset; -} - -static bool ParsePosixName(const char* name, TimeZoneRule& rule) { - static constexpr char default_rule[]{",M4.1.0,M10.5.0"}; - const char* std_name{name}; - int std_len{}; - int offset{}; - int std_offset{}; - - if (name[offset] == '<') { - offset++; - std_name = name + offset; - const int std_name_offset{offset}; - offset = GetQZName(name, offset, '>'); - if (name[offset] != '>') { - return {}; - } - std_len = offset - std_name_offset; - offset++; - } else { - offset = GetTZName(name, offset); - std_len = offset; - } - if (std_len == 0) { - return {}; - } - if (!GetOffset(name, offset, std_offset)) { - return {}; - } - - int char_count{std_len + 1}; - int dest_len{}; - int dest_offset{}; - const char* dest_name{name + offset}; - if (rule.chars.size() < std::size_t(char_count)) { - return {}; - } - - if (name[offset] != '\0') { - if (name[offset] == '<') { - dest_name = name + (++offset); - const int dest_name_offset{offset}; - offset = GetQZName(name, offset, '>'); - if (name[offset] != '>') { - return {}; - } - dest_len = offset - dest_name_offset; - offset++; - } else { - dest_name = name + (offset); - offset = GetTZName(name, offset); - dest_len = offset; - } - if (dest_len == 0) { - return {}; - } - char_count += dest_len + 1; - if (rule.chars.size() < std::size_t(char_count)) { - return {}; - } - if (name[offset] != '\0' && name[offset] != ',' && name[offset] != ';') { - if (!GetOffset(name, offset, dest_offset)) { - return {}; - } - } else { - dest_offset = std_offset - seconds_per_hour; - } - if (name[offset] == '\0') { - name = default_rule; - offset = 0; - } - if (name[offset] == ',' || name[offset] == ';') { - offset++; - - Rule start{}; - if (!GetRule(name, offset, start)) { - return {}; - } - if (name[offset++] != ',') { - return {}; - } - - Rule end{}; - if (!GetRule(name, offset, end)) { - return {}; - } - if (name[offset] != '\0') { - return {}; - } - - rule.type_count = 2; - rule.ttis[0].gmt_offset = -dest_offset; - rule.ttis[0].is_dst = true; - rule.ttis[0].abbreviation_list_index = std_len + 1; - rule.ttis[1].gmt_offset = -std_offset; - rule.ttis[1].is_dst = false; - rule.ttis[1].abbreviation_list_index = 0; - rule.default_type = 0; - - s64 jan_first{}; - int time_count{}; - int jan_offset{}; - int year_beginning{epoch_year}; - do { - const int year_seconds{GetYearLengthInDays(year_beginning - 1) * seconds_per_day}; - year_beginning--; - if (!SafeAdd(jan_first, -year_seconds)) { - jan_offset = -year_seconds; - break; - } - } while (epoch_year - years_per_repeat / 2 < year_beginning); - - int year_limit{year_beginning + years_per_repeat + 1}; - int year{}; - for (year = year_beginning; year < year_limit; year++) { - int start_time{TransitionTime(year, start, std_offset)}; - int end_time{TransitionTime(year, end, dest_offset)}; - const int year_seconds{GetYearLengthInDays(year) * seconds_per_day}; - const bool is_reversed{end_time < start_time}; - if (is_reversed) { - int swap{start_time}; - start_time = end_time; - end_time = swap; - } - - if (is_reversed || - (start_time < end_time && - (end_time - start_time < (year_seconds + (std_offset - dest_offset))))) { - if (rule.ats.size() - 2 < std::size_t(time_count)) { - break; - } - - rule.ats[time_count] = jan_first; - if (SafeAdd(rule.ats[time_count], jan_offset + start_time)) { - rule.types[time_count++] = is_reversed ? 1 : 0; - } else if (jan_offset != 0) { - rule.default_type = is_reversed ? 1 : 0; - } - - rule.ats[time_count] = jan_first; - if (SafeAdd(rule.ats[time_count], jan_offset + end_time)) { - rule.types[time_count++] = is_reversed ? 0 : 1; - year_limit = year + years_per_repeat + 1; - } else if (jan_offset != 0) { - rule.default_type = is_reversed ? 0 : 1; - } - } - if (!SafeAdd(jan_first, jan_offset + year_seconds)) { - break; - } - jan_offset = 0; - } - rule.time_count = time_count; - if (time_count == 0) { - rule.type_count = 1; - } else if (years_per_repeat < year - year_beginning) { - rule.go_back = true; - rule.go_ahead = true; - } - } else { - if (name[offset] == '\0') { - return {}; - } - - s64 their_std_offset{}; - for (int index{}; index < rule.time_count; ++index) { - const s8 type{rule.types[index]}; - if (rule.ttis[type].is_standard_time_daylight) { - their_std_offset = -rule.ttis[type].gmt_offset; - } - } - - s64 their_offset{their_std_offset}; - for (int index{}; index < rule.time_count; ++index) { - const s8 type{rule.types[index]}; - rule.types[index] = rule.ttis[type].is_dst ? 1 : 0; - if (!rule.ttis[type].is_gmt) { - if (!rule.ttis[type].is_standard_time_daylight) { - rule.ats[index] += dest_offset - their_std_offset; - } else { - rule.ats[index] += std_offset - their_std_offset; - } - } - their_offset = -rule.ttis[type].gmt_offset; - if (!rule.ttis[type].is_dst) { - their_std_offset = their_offset; - } - } - - if (rule.time_count > 0) { - UNIMPLEMENTED(); - // TODO (lat9nq): Implement eggert/tz/localtime.c:tzparse:1329 - // Seems to be unused in yuzu for now: I never hit the UNIMPLEMENTED in testing - } - - rule.ttis[0].gmt_offset = -std_offset; - rule.ttis[0].is_dst = false; - rule.ttis[0].abbreviation_list_index = 0; - rule.ttis[1].gmt_offset = -dest_offset; - rule.ttis[1].is_dst = true; - rule.ttis[1].abbreviation_list_index = std_len + 1; - rule.type_count = 2; - rule.default_type = 0; - } - } else { - // Default is standard time - rule.type_count = 1; - rule.time_count = 0; - rule.default_type = 0; - rule.ttis[0].gmt_offset = -std_offset; - rule.ttis[0].is_dst = false; - rule.ttis[0].abbreviation_list_index = 0; - } - - rule.char_count = char_count; - for (int index{}; index < std_len; ++index) { - rule.chars[index] = std_name[index]; - } - - rule.chars[std_len++] = '\0'; - if (dest_len != 0) { - for (int index{}; index < dest_len; ++index) { - rule.chars[std_len + index] = dest_name[index]; - } - rule.chars[std_len + dest_len] = '\0'; - } - - return true; -} - -static bool ParseTimeZoneBinary(TimeZoneRule& time_zone_rule, FileSys::VirtualFile& vfs_file) { - TzifHeader header{}; - if (vfs_file->ReadObject<TzifHeader>(&header) != sizeof(TzifHeader)) { - return {}; - } - - constexpr s32 time_zone_max_leaps{50}; - constexpr s32 time_zone_max_chars{50}; - constexpr s32 time_zone_max_times{1000}; - if (!(0 <= header.leap_count && header.leap_count < time_zone_max_leaps && - 0 < header.type_count && header.type_count < s32(time_zone_rule.ttis.size()) && - 0 <= header.time_count && header.time_count < s32(time_zone_rule.ats.size()) && - 0 <= header.char_count && header.char_count < time_zone_max_chars && - (header.ttis_std_count == header.type_count || header.ttis_std_count == 0) && - (header.ttis_gmt_count == header.type_count || header.ttis_gmt_count == 0))) { - return {}; - } - time_zone_rule.time_count = header.time_count; - time_zone_rule.type_count = header.type_count; - time_zone_rule.char_count = header.char_count; - - int time_count{}; - u64 read_offset = sizeof(TzifHeader); - for (int index{}; index < time_zone_rule.time_count; ++index) { - s64_be at{}; - vfs_file->ReadObject<s64_be>(&at, read_offset); - time_zone_rule.types[index] = 1; - if (time_count != 0 && at <= time_zone_rule.ats[time_count - 1]) { - if (at < time_zone_rule.ats[time_count - 1]) { - return {}; - } - time_zone_rule.types[index - 1] = 0; - time_count--; - } - time_zone_rule.ats[time_count++] = at; - read_offset += sizeof(s64_be); - } - time_count = 0; - for (int index{}; index < time_zone_rule.time_count; ++index) { - const u8 type{*vfs_file->ReadByte(read_offset)}; - read_offset += sizeof(u8); - if (time_zone_rule.type_count <= type) { - return {}; - } - if (time_zone_rule.types[index] != 0) { - time_zone_rule.types[time_count++] = type; - } - } - time_zone_rule.time_count = time_count; - for (int index{}; index < time_zone_rule.type_count; ++index) { - TimeTypeInfo& ttis{time_zone_rule.ttis[index]}; - u32_be gmt_offset{}; - vfs_file->ReadObject<u32_be>(&gmt_offset, read_offset); - read_offset += sizeof(u32_be); - ttis.gmt_offset = gmt_offset; - - const u8 dst{*vfs_file->ReadByte(read_offset)}; - read_offset += sizeof(u8); - if (dst >= 2) { - return {}; - } - ttis.is_dst = dst != 0; - - const s32 abbreviation_list_index{*vfs_file->ReadByte(read_offset)}; - read_offset += sizeof(u8); - if (abbreviation_list_index >= time_zone_rule.char_count) { - return {}; - } - ttis.abbreviation_list_index = abbreviation_list_index; - } - - vfs_file->ReadArray(time_zone_rule.chars.data(), time_zone_rule.char_count, read_offset); - time_zone_rule.chars[time_zone_rule.char_count] = '\0'; - read_offset += time_zone_rule.char_count; - for (int index{}; index < time_zone_rule.type_count; ++index) { - if (header.ttis_std_count == 0) { - time_zone_rule.ttis[index].is_standard_time_daylight = false; - } else { - const u8 dst{*vfs_file->ReadByte(read_offset)}; - read_offset += sizeof(u8); - if (dst >= 2) { - return {}; - } - time_zone_rule.ttis[index].is_standard_time_daylight = dst != 0; - } - } - - for (int index{}; index < time_zone_rule.type_count; ++index) { - if (header.ttis_std_count == 0) { - time_zone_rule.ttis[index].is_gmt = false; - } else { - const u8 dst{*vfs_file->ReadByte(read_offset)}; - read_offset += sizeof(u8); - if (dst >= 2) { - return {}; - } - time_zone_rule.ttis[index].is_gmt = dst != 0; - } - } - - const u64 position{(read_offset - sizeof(TzifHeader))}; - const s64 bytes_read = s64(vfs_file->GetSize() - sizeof(TzifHeader) - position); - if (bytes_read < 0) { - return {}; - } - constexpr s32 time_zone_name_max{255}; - if (bytes_read > (time_zone_name_max + 1)) { - return {}; - } - - std::array<char, time_zone_name_max + 1> temp_name{}; - vfs_file->ReadArray(temp_name.data(), bytes_read, read_offset); - if (bytes_read > 2 && temp_name[0] == '\n' && temp_name[bytes_read - 1] == '\n' && - std::size_t(time_zone_rule.type_count) + 2 <= time_zone_rule.ttis.size()) { - temp_name[bytes_read - 1] = '\0'; - - std::array<char, time_zone_name_max> name{}; - std::memcpy(name.data(), temp_name.data() + 1, std::size_t(bytes_read - 1)); - - // Fill in computed transition times with temp rule - TimeZoneRule temp_rule; - if (ParsePosixName(name.data(), temp_rule)) { - int have_abbreviation = 0; - int char_count = time_zone_rule.char_count; - - for (int i = 0; i < temp_rule.type_count; i++) { - char* temp_abbreviation = - temp_rule.chars.data() + temp_rule.ttis[i].abbreviation_list_index; - int j; - for (j = 0; j < char_count; j++) { - if (std::strcmp(time_zone_rule.chars.data() + j, temp_abbreviation) == 0) { - temp_rule.ttis[i].abbreviation_list_index = j; - have_abbreviation++; - break; - } - } - if (j >= char_count) { - int temp_abbreviation_length = static_cast<int>(std::strlen(temp_abbreviation)); - if (j + temp_abbreviation_length < time_zone_max_chars) { - std::strcpy(time_zone_rule.chars.data() + j, temp_abbreviation); - char_count = j + temp_abbreviation_length + 1; - temp_rule.ttis[i].abbreviation_list_index = j; - have_abbreviation++; - } - } - } - - if (have_abbreviation == temp_rule.type_count) { - time_zone_rule.char_count = char_count; - - // Original comment: - /* Ignore any trailing, no-op transitions generated - by zic as they don't help here and can run afoul - of bugs in zic 2016j or earlier. */ - // This is possibly unnecessary for yuzu, since Nintendo doesn't run zic - while (1 < time_zone_rule.time_count && - (time_zone_rule.types[time_zone_rule.time_count - 1] == - time_zone_rule.types[time_zone_rule.time_count - 2])) { - time_zone_rule.time_count--; - } - - for (int i = 0; - i < temp_rule.time_count && time_zone_rule.time_count < time_zone_max_times; - i++) { - const s64 transition_time = temp_rule.ats[i]; - if (0 < time_zone_rule.time_count && - transition_time <= time_zone_rule.ats[time_zone_rule.time_count - 1]) { - continue; - } - - time_zone_rule.ats[time_zone_rule.time_count] = transition_time; - time_zone_rule.types[time_zone_rule.time_count] = - static_cast<s8>(time_zone_rule.type_count + temp_rule.types[i]); - time_zone_rule.time_count++; - } - for (int i = 0; i < temp_rule.type_count; i++) { - time_zone_rule.ttis[time_zone_rule.type_count++] = temp_rule.ttis[i]; - } - } - } - } - - const auto typesequiv = [](TimeZoneRule& rule, int a, int b) -> bool { - if (a < 0 || a >= rule.type_count || b < 0 || b >= rule.type_count) { - return {}; - } - - const struct TimeTypeInfo* ap = &rule.ttis[a]; - const struct TimeTypeInfo* bp = &rule.ttis[b]; - - return (ap->gmt_offset == bp->gmt_offset && ap->is_dst == bp->is_dst && - (std::strcmp(&rule.chars[ap->abbreviation_list_index], - &rule.chars[bp->abbreviation_list_index]) == 0)); - }; - - if (time_zone_rule.type_count == 0) { - return {}; - } - if (time_zone_rule.time_count > 1) { - if (time_zone_rule.ats[0] <= std::numeric_limits<s64>::max() - seconds_per_repeat) { - s64 repeatat = time_zone_rule.ats[0] + seconds_per_repeat; - int repeatattype = time_zone_rule.types[0]; - for (int i = 1; i < time_zone_rule.time_count; ++i) { - if (time_zone_rule.ats[i] == repeatat && - typesequiv(time_zone_rule, time_zone_rule.types[i], repeatattype)) { - time_zone_rule.go_back = true; - break; - } - } - } - if (std::numeric_limits<s64>::min() + seconds_per_repeat <= - time_zone_rule.ats[time_zone_rule.time_count - 1]) { - s64 repeatat = time_zone_rule.ats[time_zone_rule.time_count - 1] - seconds_per_repeat; - int repeatattype = time_zone_rule.types[time_zone_rule.time_count - 1]; - for (int i = time_zone_rule.time_count; i >= 0; --i) { - if (time_zone_rule.ats[i] == repeatat && - typesequiv(time_zone_rule, time_zone_rule.types[i], repeatattype)) { - time_zone_rule.go_ahead = true; - break; - } - } - } - } - - s32 default_type{}; - - for (default_type = 0; default_type < time_zone_rule.time_count; default_type++) { - if (time_zone_rule.types[default_type] == 0) { - break; - } - } - - default_type = default_type < time_zone_rule.time_count ? -1 : 0; - if (default_type < 0 && time_zone_rule.time_count > 0 && - time_zone_rule.ttis[time_zone_rule.types[0]].is_dst) { - default_type = time_zone_rule.types[0]; - while (--default_type >= 0) { - if (!time_zone_rule.ttis[default_type].is_dst) { - break; - } - } - } - if (default_type < 0) { - default_type = 0; - while (time_zone_rule.ttis[default_type].is_dst) { - if (++default_type >= time_zone_rule.type_count) { - default_type = 0; - break; - } - } - } - time_zone_rule.default_type = default_type; - return true; -} - -static Result CreateCalendarTime(s64 time, int gmt_offset, CalendarTimeInternal& calendar_time, - CalendarAdditionalInfo& calendar_additional_info) { - s64 year{epoch_year}; - s64 time_days{time / seconds_per_day}; - s64 remaining_seconds{time % seconds_per_day}; - while (time_days < 0 || time_days >= GetYearLengthInDays(year)) { - s64 delta = time_days / days_per_leap_year; - if (!delta) { - delta = time_days < 0 ? -1 : 1; - } - s64 new_year{year}; - if (!SafeAdd(new_year, delta)) { - return ERROR_OUT_OF_RANGE; - } - time_days -= (new_year - year) * days_per_normal_year; - time_days -= GetLeapDaysFromYear(new_year - 1) - GetLeapDaysFromYear(year - 1); - year = new_year; - } - - s64 day_of_year{time_days}; - remaining_seconds += gmt_offset; - while (remaining_seconds < 0) { - remaining_seconds += seconds_per_day; - day_of_year--; - } - - while (remaining_seconds >= seconds_per_day) { - remaining_seconds -= seconds_per_day; - day_of_year++; - } - - while (day_of_year < 0) { - if (!SafeAdd(year, -1)) { - return ERROR_OUT_OF_RANGE; - } - day_of_year += GetYearLengthInDays(year); - } - - while (day_of_year >= GetYearLengthInDays(year)) { - day_of_year -= GetYearLengthInDays(year); - if (!SafeAdd(year, 1)) { - return ERROR_OUT_OF_RANGE; - } - } - - calendar_time.year = year; - calendar_additional_info.day_of_year = static_cast<u32>(day_of_year); - s64 day_of_week{ - (epoch_week_day + - ((year - epoch_year) % days_per_week) * (days_per_normal_year % days_per_week) + - GetLeapDaysFromYear(year - 1) - GetLeapDaysFromYear(epoch_year - 1) + day_of_year) % - days_per_week}; - if (day_of_week < 0) { - day_of_week += days_per_week; - } - - calendar_additional_info.day_of_week = static_cast<u32>(day_of_week); - calendar_time.hour = static_cast<s8>((remaining_seconds / seconds_per_hour) % seconds_per_hour); - remaining_seconds %= seconds_per_hour; - calendar_time.minute = static_cast<s8>(remaining_seconds / seconds_per_minute); - calendar_time.second = static_cast<s8>(remaining_seconds % seconds_per_minute); - - for (calendar_time.month = 0; - day_of_year >= GetMonthLength(IsLeapYear(year), calendar_time.month); - ++calendar_time.month) { - day_of_year -= GetMonthLength(IsLeapYear(year), calendar_time.month); - } - - calendar_time.day = static_cast<s8>(day_of_year + 1); - calendar_additional_info.is_dst = false; - calendar_additional_info.gmt_offset = gmt_offset; - - return ResultSuccess; -} - -static Result ToCalendarTimeInternal(const TimeZoneRule& rules, s64 time, - CalendarTimeInternal& calendar_time, - CalendarAdditionalInfo& calendar_additional_info) { - ASSERT(rules.go_ahead ? rules.time_count > 0 : true); - if ((rules.go_back && time < rules.ats[0]) || - (rules.go_ahead && time > rules.ats[rules.time_count - 1])) { - s64 seconds{}; - if (time < rules.ats[0]) { - seconds = rules.ats[0] - time; - } else { - seconds = time - rules.ats[rules.time_count - 1]; - } - seconds--; - - const s64 years{(seconds / seconds_per_repeat + 1) * years_per_repeat}; - seconds = years * average_seconds_per_year; - - s64 new_time{time}; - if (time < rules.ats[0]) { - new_time += seconds; - } else { - new_time -= seconds; - } - if (new_time < rules.ats[0] && new_time > rules.ats[rules.time_count - 1]) { - return ERROR_TIME_NOT_FOUND; - } - if (const Result result{ - ToCalendarTimeInternal(rules, new_time, calendar_time, calendar_additional_info)}; - result != ResultSuccess) { - return result; - } - if (time < rules.ats[0]) { - calendar_time.year -= years; - } else { - calendar_time.year += years; - } - - return ResultSuccess; - } - - s32 tti_index{}; - if (rules.time_count == 0 || time < rules.ats[0]) { - tti_index = rules.default_type; - } else { - s32 low{1}; - s32 high{rules.time_count}; - while (low < high) { - s32 mid{(low + high) >> 1}; - if (time < rules.ats[mid]) { - high = mid; - } else { - low = mid + 1; - } - } - tti_index = rules.types[low - 1]; - } - - if (const Result result{CreateCalendarTime(time, rules.ttis[tti_index].gmt_offset, - calendar_time, calendar_additional_info)}; - result != ResultSuccess) { - return result; - } - - calendar_additional_info.is_dst = rules.ttis[tti_index].is_dst; - const char* time_zone{&rules.chars[rules.ttis[tti_index].abbreviation_list_index]}; - u32 index; - for (index = 0; time_zone[index] != '\0' && time_zone[index] != ',' && - index < calendar_additional_info.timezone_name.size() - 1; - ++index) { - calendar_additional_info.timezone_name[index] = time_zone[index]; - } - calendar_additional_info.timezone_name[index] = '\0'; - return ResultSuccess; -} - -static Result ToCalendarTimeImpl(const TimeZoneRule& rules, s64 time, CalendarInfo& calendar) { - CalendarTimeInternal calendar_time{}; - const Result result{ - ToCalendarTimeInternal(rules, time, calendar_time, calendar.additional_info)}; - calendar.time.year = static_cast<s16>(calendar_time.year); - - // Internal impl. uses 0-indexed month - calendar.time.month = static_cast<s8>(calendar_time.month + 1); - - calendar.time.day = calendar_time.day; - calendar.time.hour = calendar_time.hour; - calendar.time.minute = calendar_time.minute; - calendar.time.second = calendar_time.second; - return result; -} - -TimeZoneManager::TimeZoneManager() = default; -TimeZoneManager::~TimeZoneManager() = default; - -Result TimeZoneManager::ToCalendarTime(const TimeZoneRule& rules, s64 time, - CalendarInfo& calendar) const { - return ToCalendarTimeImpl(rules, time, calendar); -} - -Result TimeZoneManager::SetDeviceLocationNameWithTimeZoneRule(const std::string& location_name, - FileSys::VirtualFile& vfs_file) { - TimeZoneRule rule{}; - if (ParseTimeZoneBinary(rule, vfs_file)) { - device_location_name = location_name; - time_zone_rule = rule; - return ResultSuccess; - } - return ERROR_TIME_ZONE_CONVERSION_FAILED; -} - -Result TimeZoneManager::SetUpdatedTime(const Clock::SteadyClockTimePoint& value) { - time_zone_update_time_point = value; - return ResultSuccess; -} - -Result TimeZoneManager::ToCalendarTimeWithMyRules(s64 time, CalendarInfo& calendar) const { - if (is_initialized) { - return ToCalendarTime(time_zone_rule, time, calendar); - } else { - return ERROR_UNINITIALIZED_CLOCK; - } -} - -Result TimeZoneManager::ParseTimeZoneRuleBinary(TimeZoneRule& rules, - FileSys::VirtualFile& vfs_file) const { - if (!ParseTimeZoneBinary(rules, vfs_file)) { - return ERROR_TIME_ZONE_CONVERSION_FAILED; - } - return ResultSuccess; -} - -Result TimeZoneManager::ToPosixTime(const TimeZoneRule& rules, const CalendarTime& calendar_time, - s64& posix_time) const { - posix_time = 0; - - CalendarTimeInternal internal_time{ - .year = calendar_time.year, - // Internal impl. uses 0-indexed month - .month = static_cast<s8>(calendar_time.month - 1), - .day = calendar_time.day, - .hour = calendar_time.hour, - .minute = calendar_time.minute, - .second = calendar_time.second, - }; - - s32 hour{internal_time.hour}; - s32 minute{internal_time.minute}; - if (!SafeNormalize(hour, minute, minutes_per_hour)) { - return ERROR_OVERFLOW; - } - internal_time.minute = static_cast<s8>(minute); - - s32 day{internal_time.day}; - if (!SafeNormalize(day, hour, hours_per_day)) { - return ERROR_OVERFLOW; - } - internal_time.day = static_cast<s8>(day); - internal_time.hour = static_cast<s8>(hour); - - s64 year{internal_time.year}; - s64 month{internal_time.month}; - if (!SafeNormalize(year, month, months_per_year)) { - return ERROR_OVERFLOW; - } - internal_time.month = static_cast<s8>(month); - - if (!SafeAdd(year, year_base)) { - return ERROR_OVERFLOW; - } - - while (day <= 0) { - if (!SafeAdd(year, -1)) { - return ERROR_OVERFLOW; - } - s64 temp_year{year}; - if (1 < internal_time.month) { - ++temp_year; - } - day += static_cast<s32>(GetYearLengthInDays(temp_year)); - } - - while (day > days_per_leap_year) { - s64 temp_year{year}; - if (1 < internal_time.month) { - temp_year++; - } - day -= static_cast<s32>(GetYearLengthInDays(temp_year)); - if (!SafeAdd(year, 1)) { - return ERROR_OVERFLOW; - } - } - - while (true) { - const s32 month_length{GetMonthLength(IsLeapYear(year), internal_time.month)}; - if (day <= month_length) { - break; - } - day -= month_length; - internal_time.month++; - if (internal_time.month >= months_per_year) { - internal_time.month = 0; - if (!SafeAdd(year, 1)) { - return ERROR_OVERFLOW; - } - } - } - internal_time.day = static_cast<s8>(day); - - if (!SafeAdd(year, -year_base)) { - return ERROR_OVERFLOW; - } - internal_time.year = year; - - s32 saved_seconds{}; - if (internal_time.second >= 0 && internal_time.second < seconds_per_minute) { - saved_seconds = 0; - } else if (year + year_base < epoch_year) { - s32 second{internal_time.second}; - if (!SafeAdd(second, 1 - seconds_per_minute)) { - return ERROR_OVERFLOW; - } - saved_seconds = second; - internal_time.second = 1 - seconds_per_minute; - } else { - saved_seconds = internal_time.second; - internal_time.second = 0; - } - - s64 low{LLONG_MIN}; - s64 high{LLONG_MAX}; - while (true) { - s64 pivot{low / 2 + high / 2}; - if (pivot < low) { - pivot = low; - } else if (pivot > high) { - pivot = high; - } - s32 direction{}; - CalendarTimeInternal candidate_calendar_time{}; - CalendarAdditionalInfo unused{}; - if (ToCalendarTimeInternal(rules, pivot, candidate_calendar_time, unused) != - ResultSuccess) { - if (pivot > 0) { - direction = 1; - } else { - direction = -1; - } - } else { - direction = candidate_calendar_time.Compare(internal_time); - } - if (!direction) { - const s64 time_result{pivot + saved_seconds}; - if ((time_result < pivot) != (saved_seconds < 0)) { - return ERROR_OVERFLOW; - } - posix_time = time_result; - break; - } else { - if (pivot == low) { - if (pivot == LLONG_MAX) { - return ERROR_TIME_NOT_FOUND; - } - pivot++; - low++; - } else if (pivot == high) { - if (pivot == LLONG_MIN) { - return ERROR_TIME_NOT_FOUND; - } - pivot--; - high--; - } - if (low > high) { - return ERROR_TIME_NOT_FOUND; - } - if (direction > 0) { - high = pivot; - } else { - low = pivot; - } - } - } - return ResultSuccess; -} - -Result TimeZoneManager::ToPosixTimeWithMyRule(const CalendarTime& calendar_time, - s64& posix_time) const { - if (is_initialized) { - return ToPosixTime(time_zone_rule, calendar_time, posix_time); - } - posix_time = 0; - return ERROR_UNINITIALIZED_CLOCK; -} - -Result TimeZoneManager::GetDeviceLocationName(LocationName& value) const { - if (!is_initialized) { - return ERROR_UNINITIALIZED_CLOCK; - } - std::memcpy(value.data(), device_location_name.c_str(), device_location_name.size()); - return ResultSuccess; -} - -Result TimeZoneManager::GetTotalLocationNameCount(s32& count) const { - if (!is_initialized) { - return ERROR_UNINITIALIZED_CLOCK; - } - count = static_cast<u32>(total_location_name_count); - - return ResultSuccess; -} - -Result TimeZoneManager::GetTimeZoneRuleVersion(u128& version) const { - if (!is_initialized) { - return ERROR_UNINITIALIZED_CLOCK; - } - version = time_zone_rule_version; - - return ResultSuccess; -} - -Result TimeZoneManager::LoadLocationNameList(std::vector<LocationName>& values) const { - if (!is_initialized) { - return ERROR_UNINITIALIZED_CLOCK; - } - - for (const auto& name : total_location_names) { - LocationName entry{}; - std::memcpy(entry.data(), name.c_str(), name.size()); - values.push_back(entry); - } - - return ResultSuccess; -} - -} // namespace Service::Time::TimeZone diff --git a/src/core/hle/service/time/time_zone_manager.h b/src/core/hle/service/time/time_zone_manager.h deleted file mode 100644 index 8664f28d1..000000000 --- a/src/core/hle/service/time/time_zone_manager.h +++ /dev/null @@ -1,61 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <string> - -#include "common/common_types.h" -#include "core/file_sys/vfs_types.h" -#include "core/hle/service/time/clock_types.h" -#include "core/hle/service/time/time_zone_types.h" - -namespace Service::Time::TimeZone { - -class TimeZoneManager final { -public: - TimeZoneManager(); - ~TimeZoneManager(); - - void SetTotalLocationNameCount(std::size_t value) { - total_location_name_count = value; - } - - void SetLocationNames(std::vector<std::string> location_names) { - total_location_names = location_names; - } - - void SetTimeZoneRuleVersion(const u128& value) { - time_zone_rule_version = value; - } - - void MarkAsInitialized() { - is_initialized = true; - } - - Result SetDeviceLocationNameWithTimeZoneRule(const std::string& location_name, - FileSys::VirtualFile& vfs_file); - Result SetUpdatedTime(const Clock::SteadyClockTimePoint& value); - Result GetDeviceLocationName(TimeZone::LocationName& value) const; - Result GetTotalLocationNameCount(s32& count) const; - Result GetTimeZoneRuleVersion(u128& version) const; - Result LoadLocationNameList(std::vector<TimeZone::LocationName>& values) const; - Result ToCalendarTime(const TimeZoneRule& rules, s64 time, CalendarInfo& calendar) const; - Result ToCalendarTimeWithMyRules(s64 time, CalendarInfo& calendar) const; - Result ParseTimeZoneRuleBinary(TimeZoneRule& rules, FileSys::VirtualFile& vfs_file) const; - Result ToPosixTime(const TimeZoneRule& rules, const CalendarTime& calendar_time, - s64& posix_time) const; - Result ToPosixTimeWithMyRule(const CalendarTime& calendar_time, s64& posix_time) const; - -private: - bool is_initialized{}; - TimeZoneRule time_zone_rule{}; - std::string device_location_name{"GMT"}; - u128 time_zone_rule_version{}; - std::size_t total_location_name_count{}; - std::vector<std::string> total_location_names{}; - Clock::SteadyClockTimePoint time_zone_update_time_point{ - Clock::SteadyClockTimePoint::GetRandom()}; -}; - -} // namespace Service::Time::TimeZone diff --git a/src/core/hle/service/time/time_zone_service.cpp b/src/core/hle/service/time/time_zone_service.cpp deleted file mode 100644 index 8171c82a5..000000000 --- a/src/core/hle/service/time/time_zone_service.cpp +++ /dev/null @@ -1,217 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/logging/log.h" -#include "core/hle/service/ipc_helpers.h" -#include "core/hle/service/time/time_zone_content_manager.h" -#include "core/hle/service/time/time_zone_service.h" -#include "core/hle/service/time/time_zone_types.h" - -namespace Service::Time { - -ITimeZoneService::ITimeZoneService(Core::System& system_, - TimeZone::TimeZoneContentManager& time_zone_manager_) - : ServiceFramework{system_, "ITimeZoneService"}, time_zone_content_manager{time_zone_manager_} { - static const FunctionInfo functions[] = { - {0, &ITimeZoneService::GetDeviceLocationName, "GetDeviceLocationName"}, - {1, nullptr, "SetDeviceLocationName"}, - {2, &ITimeZoneService::GetTotalLocationNameCount, "GetTotalLocationNameCount"}, - {3, &ITimeZoneService::LoadLocationNameList, "LoadLocationNameList"}, - {4, &ITimeZoneService::LoadTimeZoneRule, "LoadTimeZoneRule"}, - {5, &ITimeZoneService::GetTimeZoneRuleVersion, "GetTimeZoneRuleVersion"}, - {6, nullptr, "GetDeviceLocationNameAndUpdatedTime"}, - {100, &ITimeZoneService::ToCalendarTime, "ToCalendarTime"}, - {101, &ITimeZoneService::ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"}, - {201, &ITimeZoneService::ToPosixTime, "ToPosixTime"}, - {202, &ITimeZoneService::ToPosixTimeWithMyRule, "ToPosixTimeWithMyRule"}, - }; - RegisterHandlers(functions); -} - -void ITimeZoneService::GetDeviceLocationName(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - - TimeZone::LocationName location_name{}; - if (const Result result{ - time_zone_content_manager.GetTimeZoneManager().GetDeviceLocationName(location_name)}; - result != ResultSuccess) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - IPC::ResponseBuilder rb{ctx, (sizeof(location_name) / 4) + 2}; - rb.Push(ResultSuccess); - rb.PushRaw(location_name); -} - -void ITimeZoneService::GetTotalLocationNameCount(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - - s32 count{}; - if (const Result result{ - time_zone_content_manager.GetTimeZoneManager().GetTotalLocationNameCount(count)}; - result != ResultSuccess) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(count); -} - -void ITimeZoneService::LoadLocationNameList(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - - std::vector<TimeZone::LocationName> location_names{}; - if (const Result result{ - time_zone_content_manager.GetTimeZoneManager().LoadLocationNameList(location_names)}; - result != ResultSuccess) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - ctx.WriteBuffer(location_names); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(static_cast<s32>(location_names.size())); -} -void ITimeZoneService::GetTimeZoneRuleVersion(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - - u128 rule_version{}; - if (const Result result{ - time_zone_content_manager.GetTimeZoneManager().GetTimeZoneRuleVersion(rule_version)}; - result != ResultSuccess) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - IPC::ResponseBuilder rb{ctx, 6}; - rb.Push(ResultSuccess); - rb.PushRaw(rule_version); -} - -void ITimeZoneService::LoadTimeZoneRule(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto raw_location_name{rp.PopRaw<std::array<u8, 0x24>>()}; - - std::string location_name; - for (const auto& byte : raw_location_name) { - // Strip extra bytes - if (byte == '\0') { - break; - } - location_name.push_back(byte); - } - - LOG_DEBUG(Service_Time, "called, location_name={}", location_name); - - TimeZone::TimeZoneRule time_zone_rule{}; - const Result result{time_zone_content_manager.LoadTimeZoneRule(time_zone_rule, location_name)}; - - std::vector<u8> time_zone_rule_outbuffer(sizeof(TimeZone::TimeZoneRule)); - std::memcpy(time_zone_rule_outbuffer.data(), &time_zone_rule, sizeof(TimeZone::TimeZoneRule)); - ctx.WriteBuffer(time_zone_rule_outbuffer); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void ITimeZoneService::ToCalendarTime(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto posix_time{rp.Pop<s64>()}; - - LOG_DEBUG(Service_Time, "called, posix_time=0x{:016X}", posix_time); - - TimeZone::TimeZoneRule time_zone_rule{}; - const auto buffer{ctx.ReadBuffer()}; - std::memcpy(&time_zone_rule, buffer.data(), buffer.size()); - - TimeZone::CalendarInfo calendar_info{}; - if (const Result result{time_zone_content_manager.GetTimeZoneManager().ToCalendarTime( - time_zone_rule, posix_time, calendar_info)}; - result != ResultSuccess) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - IPC::ResponseBuilder rb{ctx, 2 + (sizeof(TimeZone::CalendarInfo) / 4)}; - rb.Push(ResultSuccess); - rb.PushRaw(calendar_info); -} - -void ITimeZoneService::ToCalendarTimeWithMyRule(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto posix_time{rp.Pop<s64>()}; - - LOG_DEBUG(Service_Time, "called, posix_time=0x{:016X}", posix_time); - - TimeZone::CalendarInfo calendar_info{}; - if (const Result result{ - time_zone_content_manager.GetTimeZoneManager().ToCalendarTimeWithMyRules( - posix_time, calendar_info)}; - result != ResultSuccess) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - IPC::ResponseBuilder rb{ctx, 2 + (sizeof(TimeZone::CalendarInfo) / 4)}; - rb.Push(ResultSuccess); - rb.PushRaw(calendar_info); -} - -void ITimeZoneService::ToPosixTime(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - - IPC::RequestParser rp{ctx}; - const auto calendar_time{rp.PopRaw<TimeZone::CalendarTime>()}; - TimeZone::TimeZoneRule time_zone_rule{}; - std::memcpy(&time_zone_rule, ctx.ReadBuffer().data(), sizeof(TimeZone::TimeZoneRule)); - - s64 posix_time{}; - if (const Result result{time_zone_content_manager.GetTimeZoneManager().ToPosixTime( - time_zone_rule, calendar_time, posix_time)}; - result != ResultSuccess) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - ctx.WriteBuffer(posix_time); - - // TODO(bunnei): Handle multiple times - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushRaw<u32>(1); // Number of times we're returning -} - -void ITimeZoneService::ToPosixTimeWithMyRule(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - - IPC::RequestParser rp{ctx}; - const auto calendar_time{rp.PopRaw<TimeZone::CalendarTime>()}; - - s64 posix_time{}; - if (const Result result{time_zone_content_manager.GetTimeZoneManager().ToPosixTimeWithMyRule( - calendar_time, posix_time)}; - result != ResultSuccess) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - ctx.WriteBuffer(posix_time); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushRaw<u32>(1); // Number of times we're returning -} - -} // namespace Service::Time diff --git a/src/core/hle/service/time/time_zone_service.h b/src/core/hle/service/time/time_zone_service.h deleted file mode 100644 index 952fcb0e2..000000000 --- a/src/core/hle/service/time/time_zone_service.h +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/service.h" - -namespace Core { -class System; -} - -namespace Service::Time { - -namespace TimeZone { -class TimeZoneContentManager; -} - -class ITimeZoneService final : public ServiceFramework<ITimeZoneService> { -public: - explicit ITimeZoneService(Core::System& system_, - TimeZone::TimeZoneContentManager& time_zone_manager_); - -private: - void GetDeviceLocationName(HLERequestContext& ctx); - void GetTotalLocationNameCount(HLERequestContext& ctx); - void LoadLocationNameList(HLERequestContext& ctx); - void GetTimeZoneRuleVersion(HLERequestContext& ctx); - void LoadTimeZoneRule(HLERequestContext& ctx); - void ToCalendarTime(HLERequestContext& ctx); - void ToCalendarTimeWithMyRule(HLERequestContext& ctx); - void ToPosixTime(HLERequestContext& ctx); - void ToPosixTimeWithMyRule(HLERequestContext& ctx); - -private: - TimeZone::TimeZoneContentManager& time_zone_content_manager; -}; - -} // namespace Service::Time diff --git a/src/core/hle/service/time/time_zone_types.h b/src/core/hle/service/time/time_zone_types.h deleted file mode 100644 index eb4fb52d1..000000000 --- a/src/core/hle/service/time/time_zone_types.h +++ /dev/null @@ -1,86 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <array> - -#include "common/common_funcs.h" -#include "common/common_types.h" -#include "common/swap.h" - -namespace Service::Time::TimeZone { - -using LocationName = std::array<char, 0x24>; - -/// https://switchbrew.org/wiki/Glue_services#ttinfo -struct TimeTypeInfo { - s32 gmt_offset{}; - u8 is_dst{}; - INSERT_PADDING_BYTES(3); - s32 abbreviation_list_index{}; - u8 is_standard_time_daylight{}; - u8 is_gmt{}; - INSERT_PADDING_BYTES(2); -}; -static_assert(sizeof(TimeTypeInfo) == 0x10, "TimeTypeInfo is incorrect size"); - -/// https://switchbrew.org/wiki/Glue_services#TimeZoneRule -struct TimeZoneRule { - s32 time_count{}; - s32 type_count{}; - s32 char_count{}; - u8 go_back{}; - u8 go_ahead{}; - INSERT_PADDING_BYTES(2); - std::array<s64, 1000> ats{}; - std::array<s8, 1000> types{}; - std::array<TimeTypeInfo, 128> ttis{}; - std::array<char, 512> chars{}; - s32 default_type{}; - INSERT_PADDING_BYTES(0x12C4); -}; -static_assert(sizeof(TimeZoneRule) == 0x4000, "TimeZoneRule is incorrect size"); - -/// https://switchbrew.org/wiki/Glue_services#CalendarAdditionalInfo -struct CalendarAdditionalInfo { - u32 day_of_week; - u32 day_of_year; - std::array<char, 8> timezone_name; - u32 is_dst; - s32 gmt_offset; -}; -static_assert(sizeof(CalendarAdditionalInfo) == 0x18, "CalendarAdditionalInfo is incorrect size"); - -/// https://switchbrew.org/wiki/Glue_services#CalendarTime -struct CalendarTime { - s16 year; - s8 month; - s8 day; - s8 hour; - s8 minute; - s8 second; - INSERT_PADDING_BYTES_NOINIT(1); -}; -static_assert(sizeof(CalendarTime) == 0x8, "CalendarTime is incorrect size"); - -struct CalendarInfo { - CalendarTime time; - CalendarAdditionalInfo additional_info; -}; -static_assert(sizeof(CalendarInfo) == 0x20, "CalendarInfo is incorrect size"); - -struct TzifHeader { - u32_be magic{}; - u8 version{}; - INSERT_PADDING_BYTES(15); - s32_be ttis_gmt_count{}; - s32_be ttis_std_count{}; - s32_be leap_count{}; - s32_be time_count{}; - s32_be type_count{}; - s32_be char_count{}; -}; -static_assert(sizeof(TzifHeader) == 0x2C, "TzifHeader is incorrect size"); - -} // namespace Service::Time::TimeZone diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index bfcc27ddc..1f3d82c57 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -15,6 +15,7 @@ #include "common/logging/log.h" #include "common/math_util.h" #include "common/settings.h" +#include "common/string_util.h" #include "common/swap.h" #include "core/core_timing.h" #include "core/hle/kernel/k_readable_event.h" @@ -694,9 +695,7 @@ private: void OpenLayer(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto name_buf = rp.PopRaw<std::array<u8, 0x40>>(); - const auto end = std::find(name_buf.begin(), name_buf.end(), '\0'); - - const std::string display_name(name_buf.begin(), end); + const std::string display_name(Common::StringFromBuffer(name_buf)); const u64 layer_id = rp.Pop<u64>(); const u64 aruid = rp.Pop<u64>(); diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp index 28116ff3a..3016d5f25 100644 --- a/src/core/loader/nsp.cpp +++ b/src/core/loader/nsp.cpp @@ -10,6 +10,7 @@ #include "core/file_sys/nca_metadata.h" #include "core/file_sys/patch_manager.h" #include "core/file_sys/registered_cache.h" +#include "core/file_sys/romfs_factory.h" #include "core/file_sys/submission_package.h" #include "core/hle/kernel/k_process.h" #include "core/hle/service/filesystem/filesystem.h" @@ -109,6 +110,13 @@ AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::KProcess& process, Core::S return result; } + if (nsp->IsExtractedType()) { + system.GetFileSystemController().RegisterProcess( + process.GetProcessId(), {}, + std::make_shared<FileSys::RomFSFactory>(*this, system.GetContentProvider(), + system.GetFileSystemController())); + } + FileSys::VirtualFile update_raw; if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) { system.GetFileSystemController().SetPackedUpdate(process.GetProcessId(), diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 8176a41be..1c218566f 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -24,6 +24,8 @@ #include "core/hle/kernel/k_process.h" #include "core/memory.h" #include "video_core/gpu.h" +#include "video_core/host1x/gpu_device_memory_manager.h" +#include "video_core/host1x/host1x.h" #include "video_core/rasterizer_download_area.h" namespace Core::Memory { @@ -637,17 +639,6 @@ struct Memory::Impl { LOG_DEBUG(HW_Memory, "Mapping {:016X} onto {:016X}-{:016X}", GetInteger(target), base * YUZU_PAGESIZE, (base + size) * YUZU_PAGESIZE); - // During boot, current_page_table might not be set yet, in which case we need not flush - if (system.IsPoweredOn()) { - auto& gpu = system.GPU(); - for (u64 i = 0; i < size; i++) { - const auto page = base + i; - if (page_table.pointers[page].Type() == Common::PageType::RasterizerCachedMemory) { - gpu.FlushAndInvalidateRegion(page << YUZU_PAGEBITS, YUZU_PAGESIZE); - } - } - } - const auto end = base + size; ASSERT_MSG(end <= page_table.pointers.size(), "out of range mapping at {:016X}", base + page_table.pointers.size()); @@ -811,21 +802,33 @@ struct Memory::Impl { return true; } - void HandleRasterizerDownload(VAddr address, size_t size) { + void HandleRasterizerDownload(VAddr v_address, size_t size) { + const auto* p = GetPointerImpl( + v_address, []() {}, []() {}); + if (!gpu_device_memory) [[unlikely]] { + gpu_device_memory = &system.Host1x().MemoryManager(); + } const size_t core = system.GetCurrentHostThreadID(); auto& current_area = rasterizer_read_areas[core]; - const VAddr end_address = address + size; - if (current_area.start_address <= address && end_address <= current_area.end_address) - [[likely]] { - return; - } - current_area = system.GPU().OnCPURead(address, size); + gpu_device_memory->ApplyOpOnPointer(p, scratch_buffers[core], [&](DAddr address) { + const DAddr end_address = address + size; + if (current_area.start_address <= address && end_address <= current_area.end_address) + [[likely]] { + return; + } + current_area = system.GPU().OnCPURead(address, size); + }); } - void HandleRasterizerWrite(VAddr address, size_t size) { + void HandleRasterizerWrite(VAddr v_address, size_t size) { + const auto* p = GetPointerImpl( + v_address, []() {}, []() {}); constexpr size_t sys_core = Core::Hardware::NUM_CPU_CORES - 1; const size_t core = std::min(system.GetCurrentHostThreadID(), sys_core); // any other calls threads go to syscore. + if (!gpu_device_memory) [[unlikely]] { + gpu_device_memory = &system.Host1x().MemoryManager(); + } // Guard on sys_core; if (core == sys_core) [[unlikely]] { sys_core_guard.lock(); @@ -835,36 +838,53 @@ struct Memory::Impl { sys_core_guard.unlock(); } }); - auto& current_area = rasterizer_write_areas[core]; - VAddr subaddress = address >> YUZU_PAGEBITS; - bool do_collection = current_area.last_address == subaddress; - if (!do_collection) [[unlikely]] { - do_collection = system.GPU().OnCPUWrite(address, size); - if (!do_collection) { - return; + gpu_device_memory->ApplyOpOnPointer(p, scratch_buffers[core], [&](DAddr address) { + auto& current_area = rasterizer_write_areas[core]; + PAddr subaddress = address >> YUZU_PAGEBITS; + bool do_collection = current_area.last_address == subaddress; + if (!do_collection) [[unlikely]] { + do_collection = system.GPU().OnCPUWrite(address, size); + if (!do_collection) { + return; + } + current_area.last_address = subaddress; } - current_area.last_address = subaddress; - } - gpu_dirty_managers[core].Collect(address, size); + gpu_dirty_managers[core].Collect(address, size); + }); } struct GPUDirtyState { - VAddr last_address; + PAddr last_address; }; - void InvalidateRegion(Common::ProcessAddress dest_addr, size_t size) { - system.GPU().InvalidateRegion(GetInteger(dest_addr), size); - } - - void FlushRegion(Common::ProcessAddress dest_addr, size_t size) { - system.GPU().FlushRegion(GetInteger(dest_addr), size); + void InvalidateGPUMemory(u8* p, size_t size) { + constexpr size_t sys_core = Core::Hardware::NUM_CPU_CORES - 1; + const size_t core = std::min(system.GetCurrentHostThreadID(), + sys_core); // any other calls threads go to syscore. + if (!gpu_device_memory) [[unlikely]] { + gpu_device_memory = &system.Host1x().MemoryManager(); + } + // Guard on sys_core; + if (core == sys_core) [[unlikely]] { + sys_core_guard.lock(); + } + SCOPE_EXIT({ + if (core == sys_core) [[unlikely]] { + sys_core_guard.unlock(); + } + }); + auto& gpu = system.GPU(); + gpu_device_memory->ApplyOpOnPointer( + p, scratch_buffers[core], [&](DAddr address) { gpu.InvalidateRegion(address, size); }); } Core::System& system; + Tegra::MaxwellDeviceMemoryManager* gpu_device_memory{}; Common::PageTable* current_page_table = nullptr; std::array<VideoCore::RasterizerDownloadArea, Core::Hardware::NUM_CPU_CORES> rasterizer_read_areas{}; std::array<GPUDirtyState, Core::Hardware::NUM_CPU_CORES> rasterizer_write_areas{}; + std::array<Common::ScratchBuffer<u32>, Core::Hardware::NUM_CPU_CORES> scratch_buffers{}; std::span<Core::GPUDirtyMemoryManager> gpu_dirty_managers; std::mutex sys_core_guard; @@ -1059,14 +1079,6 @@ void Memory::MarkRegionDebug(Common::ProcessAddress vaddr, u64 size, bool debug) impl->MarkRegionDebug(GetInteger(vaddr), size, debug); } -void Memory::InvalidateRegion(Common::ProcessAddress dest_addr, size_t size) { - impl->InvalidateRegion(dest_addr, size); -} - -void Memory::FlushRegion(Common::ProcessAddress dest_addr, size_t size) { - impl->FlushRegion(dest_addr, size); -} - bool Memory::InvalidateNCE(Common::ProcessAddress vaddr, size_t size) { [[maybe_unused]] bool mapped = true; [[maybe_unused]] bool rasterizer = false; @@ -1078,10 +1090,10 @@ bool Memory::InvalidateNCE(Common::ProcessAddress vaddr, size_t size) { GetInteger(vaddr)); mapped = false; }, - [&] { - impl->system.GPU().InvalidateRegion(GetInteger(vaddr), size); - rasterizer = true; - }); + [&] { rasterizer = true; }); + if (rasterizer) { + impl->InvalidateGPUMemory(ptr, size); + } #ifdef __linux__ if (!rasterizer && mapped) { diff --git a/src/core/memory.h b/src/core/memory.h index dddfaf4a4..f7e6b297f 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -12,6 +12,7 @@ #include "common/scratch_buffer.h" #include "common/typed_address.h" +#include "core/guest_memory.h" #include "core/hle/result.h" namespace Common { @@ -486,10 +487,10 @@ public: void MarkRegionDebug(Common::ProcessAddress vaddr, u64 size, bool debug); void SetGPUDirtyManagers(std::span<Core::GPUDirtyMemoryManager> managers); - void InvalidateRegion(Common::ProcessAddress dest_addr, size_t size); + bool InvalidateNCE(Common::ProcessAddress vaddr, size_t size); + bool InvalidateSeparateHeap(void* fault_address); - void FlushRegion(Common::ProcessAddress dest_addr, size_t size); private: Core::System& system; @@ -498,209 +499,9 @@ private: std::unique_ptr<Impl> impl; }; -enum GuestMemoryFlags : u32 { - Read = 1 << 0, - Write = 1 << 1, - Safe = 1 << 2, - Cached = 1 << 3, - - SafeRead = Read | Safe, - SafeWrite = Write | Safe, - SafeReadWrite = SafeRead | SafeWrite, - SafeReadCachedWrite = SafeReadWrite | Cached, - - UnsafeRead = Read, - UnsafeWrite = Write, - UnsafeReadWrite = UnsafeRead | UnsafeWrite, - UnsafeReadCachedWrite = UnsafeReadWrite | Cached, -}; - -namespace { -template <typename M, typename T, GuestMemoryFlags FLAGS> -class GuestMemory { - using iterator = T*; - using const_iterator = const T*; - using value_type = T; - using element_type = T; - using iterator_category = std::contiguous_iterator_tag; - -public: - GuestMemory() = delete; - explicit GuestMemory(M& memory, u64 addr, std::size_t size, - Common::ScratchBuffer<T>* backup = nullptr) - : m_memory{memory}, m_addr{addr}, m_size{size} { - static_assert(FLAGS & GuestMemoryFlags::Read || FLAGS & GuestMemoryFlags::Write); - if constexpr (FLAGS & GuestMemoryFlags::Read) { - Read(addr, size, backup); - } - } - - ~GuestMemory() = default; - - T* data() noexcept { - return m_data_span.data(); - } - - const T* data() const noexcept { - return m_data_span.data(); - } - - size_t size() const noexcept { - return m_size; - } - - size_t size_bytes() const noexcept { - return this->size() * sizeof(T); - } - - [[nodiscard]] T* begin() noexcept { - return this->data(); - } - - [[nodiscard]] const T* begin() const noexcept { - return this->data(); - } - - [[nodiscard]] T* end() noexcept { - return this->data() + this->size(); - } - - [[nodiscard]] const T* end() const noexcept { - return this->data() + this->size(); - } - - T& operator[](size_t index) noexcept { - return m_data_span[index]; - } - - const T& operator[](size_t index) const noexcept { - return m_data_span[index]; - } - - void SetAddressAndSize(u64 addr, std::size_t size) noexcept { - m_addr = addr; - m_size = size; - m_addr_changed = true; - } - - std::span<T> Read(u64 addr, std::size_t size, - Common::ScratchBuffer<T>* backup = nullptr) noexcept { - m_addr = addr; - m_size = size; - if (m_size == 0) { - m_is_data_copy = true; - return {}; - } - - if (this->TrySetSpan()) { - if constexpr (FLAGS & GuestMemoryFlags::Safe) { - m_memory.FlushRegion(m_addr, this->size_bytes()); - } - } else { - if (backup) { - backup->resize_destructive(this->size()); - m_data_span = *backup; - } else { - m_data_copy.resize(this->size()); - m_data_span = std::span(m_data_copy); - } - m_is_data_copy = true; - m_span_valid = true; - if constexpr (FLAGS & GuestMemoryFlags::Safe) { - m_memory.ReadBlock(m_addr, this->data(), this->size_bytes()); - } else { - m_memory.ReadBlockUnsafe(m_addr, this->data(), this->size_bytes()); - } - } - return m_data_span; - } - - void Write(std::span<T> write_data) noexcept { - if constexpr (FLAGS & GuestMemoryFlags::Cached) { - m_memory.WriteBlockCached(m_addr, write_data.data(), this->size_bytes()); - } else if constexpr (FLAGS & GuestMemoryFlags::Safe) { - m_memory.WriteBlock(m_addr, write_data.data(), this->size_bytes()); - } else { - m_memory.WriteBlockUnsafe(m_addr, write_data.data(), this->size_bytes()); - } - } - - bool TrySetSpan() noexcept { - if (u8* ptr = m_memory.GetSpan(m_addr, this->size_bytes()); ptr) { - m_data_span = {reinterpret_cast<T*>(ptr), this->size()}; - m_span_valid = true; - return true; - } - return false; - } - -protected: - bool IsDataCopy() const noexcept { - return m_is_data_copy; - } - - bool AddressChanged() const noexcept { - return m_addr_changed; - } - - M& m_memory; - u64 m_addr{}; - size_t m_size{}; - std::span<T> m_data_span{}; - std::vector<T> m_data_copy{}; - bool m_span_valid{false}; - bool m_is_data_copy{false}; - bool m_addr_changed{false}; -}; - -template <typename M, typename T, GuestMemoryFlags FLAGS> -class GuestMemoryScoped : public GuestMemory<M, T, FLAGS> { -public: - GuestMemoryScoped() = delete; - explicit GuestMemoryScoped(M& memory, u64 addr, std::size_t size, - Common::ScratchBuffer<T>* backup = nullptr) - : GuestMemory<M, T, FLAGS>(memory, addr, size, backup) { - if constexpr (!(FLAGS & GuestMemoryFlags::Read)) { - if (!this->TrySetSpan()) { - if (backup) { - this->m_data_span = *backup; - this->m_span_valid = true; - this->m_is_data_copy = true; - } - } - } - } - - ~GuestMemoryScoped() { - if constexpr (FLAGS & GuestMemoryFlags::Write) { - if (this->size() == 0) [[unlikely]] { - return; - } - - if (this->AddressChanged() || this->IsDataCopy()) { - ASSERT(this->m_span_valid); - if constexpr (FLAGS & GuestMemoryFlags::Cached) { - this->m_memory.WriteBlockCached(this->m_addr, this->data(), this->size_bytes()); - } else if constexpr (FLAGS & GuestMemoryFlags::Safe) { - this->m_memory.WriteBlock(this->m_addr, this->data(), this->size_bytes()); - } else { - this->m_memory.WriteBlockUnsafe(this->m_addr, this->data(), this->size_bytes()); - } - } else if constexpr ((FLAGS & GuestMemoryFlags::Safe) || - (FLAGS & GuestMemoryFlags::Cached)) { - this->m_memory.InvalidateRegion(this->m_addr, this->size_bytes()); - } - } - } -}; -} // namespace - template <typename T, GuestMemoryFlags FLAGS> -using CpuGuestMemory = GuestMemory<Memory, T, FLAGS>; +using CpuGuestMemory = GuestMemory<Core::Memory::Memory, T, FLAGS>; template <typename T, GuestMemoryFlags FLAGS> -using CpuGuestMemoryScoped = GuestMemoryScoped<Memory, T, FLAGS>; -template <typename T, GuestMemoryFlags FLAGS> -using GpuGuestMemory = GuestMemory<Tegra::MemoryManager, T, FLAGS>; -template <typename T, GuestMemoryFlags FLAGS> -using GpuGuestMemoryScoped = GuestMemoryScoped<Tegra::MemoryManager, T, FLAGS>; +using CpuGuestMemoryScoped = GuestMemoryScoped<Core::Memory::Memory, T, FLAGS>; + } // namespace Core::Memory |