From 41e2fab54c861e85eb5fbcbddf91491ed87fa0c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Thu, 11 Feb 2021 09:47:32 +0100 Subject: [PATCH 1/6] IOS/ES: Log content ID and index when opening contents for debugging The content ID/index is what actually matters.. --- Source/Core/Core/IOS/ES/TitleContents.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/Core/Core/IOS/ES/TitleContents.cpp b/Source/Core/Core/IOS/ES/TitleContents.cpp index 0cab7b7c91..b099937367 100644 --- a/Source/Core/Core/IOS/ES/TitleContents.cpp +++ b/Source/Core/Core/IOS/ES/TitleContents.cpp @@ -39,7 +39,9 @@ s32 ESDevice::OpenContent(const ES::TMDReader& tmd, u16 content_index, u32 uid) entry.m_content = content; entry.m_title_id = title_id; entry.m_uid = uid; - INFO_LOG_FMT(IOS_ES, "OpenContent: title ID {:016x}, UID {:#x}, CFD {}", title_id, uid, i); + INFO_LOG_FMT(IOS_ES, + "OpenContent: title ID {:016x}, UID {:#x}, content {:08x} (index {}) -> CFD {}", + title_id, uid, content.id, content_index, i); return static_cast(i); } From 1073463d350693bda54c48eab021d692a9e3274c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Thu, 11 Feb 2021 10:21:43 +0100 Subject: [PATCH 2/6] IOS/ES: Log content reads for debugging We log FS reads already, might as well log ES content reads. --- Source/Core/Core/IOS/ES/TitleContents.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Core/Core/IOS/ES/TitleContents.cpp b/Source/Core/Core/IOS/ES/TitleContents.cpp index b099937367..30dd6c08c5 100644 --- a/Source/Core/Core/IOS/ES/TitleContents.cpp +++ b/Source/Core/Core/IOS/ES/TitleContents.cpp @@ -110,6 +110,8 @@ IPCReply ESDevice::ReadContent(u32 uid, const IOCtlVRequest& request) const u32 size = request.io_vectors[0].size; const u32 addr = request.io_vectors[0].address; + INFO_LOG_FMT(IOS_ES, "ReadContent(uid={:#x}, cfd={}, size={}, addr={:08x})", uid, cfd, size, + addr); return IPCReply(ReadContent(cfd, Memory::GetPointer(addr), size, uid)); } From f214df5d2cd6461322640deb853ac18e713ad10b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Sun, 14 Feb 2021 00:08:07 +0100 Subject: [PATCH 3/6] IOS/FS: Allow IPC interface to be used internally from IOS HLE This makes it more convenient to emulate timings for IPC commands that perform internal IOS <-> IOS IPC, for example ES relying on FS for filesystem access. --- Source/Core/Core/IOS/FS/FileSystemProxy.cpp | 182 +++++++++++++------- Source/Core/Core/IOS/FS/FileSystemProxy.h | 26 ++- Source/Core/Core/IOS/IOS.h | 32 ++++ Source/Core/Core/State.cpp | 2 +- 4 files changed, 172 insertions(+), 70 deletions(-) diff --git a/Source/Core/Core/IOS/FS/FileSystemProxy.cpp b/Source/Core/Core/IOS/FS/FileSystemProxy.cpp index bb4e76a341..dd7ae5ceb8 100644 --- a/Source/Core/Core/IOS/FS/FileSystemProxy.cpp +++ b/Source/Core/Core/IOS/FS/FileSystemProxy.cpp @@ -22,10 +22,16 @@ namespace IOS::HLE { using namespace IOS::HLE::FS; -static IPCReply GetFSReply(s32 return_value, u64 extra_tb_ticks = 0) +static constexpr u64 GetIPCOverheadTicks() { // According to hardware tests, FS takes at least 2700 TB ticks to reply to commands. - return IPCReply{return_value, (2700 + extra_tb_ticks) * SystemTimers::TIMER_RATIO}; + return 2700; +} + +static IPCReply GetFSReply(s32 return_value, u64 extra_tb_ticks = 0) +{ + return IPCReply{return_value, + (GetIPCOverheadTicks() + extra_tb_ticks) * SystemTimers::TIMER_RATIO}; } /// Duration of a superblock write (in timebase ticks). @@ -95,10 +101,11 @@ FSDevice::FSDevice(Kernel& ios, const std::string& device_name) : Device(ios, de void FSDevice::DoState(PointerWrap& p) { - p.Do(m_fd_map); - p.Do(m_cache_fd); - p.Do(m_cache_chain_index); p.Do(m_dirty_cache); + p.Do(m_cache_chain_index); + p.Do(m_cache_fd); + p.Do(m_next_fd); + p.Do(m_fd_map); } template @@ -153,59 +160,78 @@ static IPCReply GetReplyForSuperblockOperation(int ios_version, ResultCode resul std::optional FSDevice::Open(const OpenRequest& request) { + return MakeIPCReply([&](Ticks t) { + return Open(request.uid, request.gid, request.path, static_cast(request.flags & 3), + request.fd, t); + }); +} + +s64 FSDevice::Open(FS::Uid uid, FS::Gid gid, const std::string& path, FS::Mode mode, + std::optional ipc_fd, Ticks ticks) +{ + ticks.AddTimeBaseTicks(GetIPCOverheadTicks()); + if (m_fd_map.size() >= 16) - return GetFSReply(ConvertResult(ResultCode::NoFreeHandle)); + return ConvertResult(ResultCode::NoFreeHandle); - if (request.path.size() >= 64) - return GetFSReply(ConvertResult(ResultCode::Invalid)); + if (path.size() >= 64) + return ConvertResult(ResultCode::Invalid); - if (request.path == "/dev/fs") + const u64 fd = ipc_fd.has_value() ? u64(*ipc_fd) : m_next_fd++; + + if (path == "/dev/fs") { - m_fd_map[request.fd] = {request.gid, request.uid, INVALID_FD}; - return GetFSReply(IPC_SUCCESS); + m_fd_map[fd] = {gid, uid, INVALID_FD}; + return fd; } - const u64 ticks = EstimateFileLookupTicks(request.path, FileLookupMode::Normal); + ticks.AddTimeBaseTicks(EstimateFileLookupTicks(path, FileLookupMode::Normal)); - auto backend_fd = m_ios.GetFS()->OpenFile(request.uid, request.gid, request.path, - static_cast(request.flags & 3)); - LogResult(backend_fd, "OpenFile({})", request.path); + auto backend_fd = m_ios.GetFS()->OpenFile(uid, gid, path, mode); + LogResult(backend_fd, "OpenFile({})", path); if (!backend_fd) - return GetFSReply(ConvertResult(backend_fd.Error()), ticks); + return ConvertResult(backend_fd.Error()); - m_fd_map[request.fd] = {request.gid, request.uid, backend_fd->Release()}; - std::strncpy(m_fd_map[request.fd].name.data(), request.path.c_str(), 64); - return GetFSReply(IPC_SUCCESS, ticks); + auto& handle = m_fd_map[fd] = {gid, uid, backend_fd->Release()}; + std::strncpy(handle.name.data(), path.c_str(), handle.name.size()); + return fd; } std::optional FSDevice::Close(u32 fd) { - u64 ticks = 0; - if (m_fd_map[fd].fs_fd != INVALID_FD) + return MakeIPCReply([&](Ticks t) { return Close(static_cast(fd), t); }); +} + +s32 FSDevice::Close(u64 fd, Ticks ticks) +{ + ticks.AddTimeBaseTicks(GetIPCOverheadTicks()); + + const auto& handle = m_fd_map[fd]; + if (handle.fs_fd != INVALID_FD) { if (fd == m_cache_fd) { - ticks += SimulateFlushFileCache(); - m_cache_fd = INVALID_FD; + ticks.AddTimeBaseTicks(SimulateFlushFileCache()); + m_cache_fd.reset(); } - if (m_fd_map[fd].superblock_flush_needed) - ticks += GetSuperblockWriteTbTicks(m_ios.GetVersion()); + if (handle.superblock_flush_needed) + ticks.AddTimeBaseTicks(GetSuperblockWriteTbTicks(m_ios.GetVersion())); - const ResultCode result = m_ios.GetFS()->Close(m_fd_map[fd].fs_fd); - LogResult(result, "Close({})", m_fd_map[fd].name.data()); + const ResultCode result = m_ios.GetFS()->Close(handle.fs_fd); + LogResult(result, "Close({})", handle.name.data()); m_fd_map.erase(fd); if (result != ResultCode::Success) - return GetFSReply(ConvertResult(result)); + return ConvertResult(result); } else { m_fd_map.erase(fd); } - return GetFSReply(IPC_SUCCESS, ticks); + return IPC_SUCCESS; } -u64 FSDevice::SimulatePopulateFileCache(u32 fd, u32 offset, u32 file_size) +u64 FSDevice::SimulatePopulateFileCache(u64 fd, u32 offset, u32 file_size) { if (HasCacheForFile(fd, offset)) return 0; @@ -221,22 +247,23 @@ u64 FSDevice::SimulatePopulateFileCache(u32 fd, u32 offset, u32 file_size) u64 FSDevice::SimulateFlushFileCache() { - if (m_cache_fd == INVALID_FD || !m_dirty_cache) + if (!m_cache_fd.has_value() || !m_dirty_cache) return 0; m_dirty_cache = false; - m_fd_map[m_cache_fd].superblock_flush_needed = true; + m_fd_map[*m_cache_fd].superblock_flush_needed = true; return GetClusterWriteTbTicks(m_ios.GetVersion()); } // Simulate parts of the FS read/write logic to estimate ticks for file operations correctly. -u64 FSDevice::EstimateTicksForReadWrite(const Handle& handle, const ReadWriteRequest& request) +u64 FSDevice::EstimateTicksForReadWrite(const Handle& handle, u64 fd, IPCCommandType command, + u32 size) { u64 ticks = 0; - const bool is_write = request.command == IPC_CMD_WRITE; + const bool is_write = command == IPC_CMD_WRITE; const Result status = m_ios.GetFS()->GetFileStatus(handle.fs_fd); u32 offset = status->offset; - u32 count = request.size; + u32 count = size; if (!is_write && count + offset > status->size) count = status->size - offset; @@ -244,17 +271,17 @@ u64 FSDevice::EstimateTicksForReadWrite(const Handle& handle, const ReadWriteReq { u32 copy_length; // Fast path (if not cached): FS copies an entire cluster directly from/to the request. - if (!HasCacheForFile(request.fd, offset) && count >= CLUSTER_DATA_SIZE && + if (!HasCacheForFile(fd, offset) && count >= CLUSTER_DATA_SIZE && offset % CLUSTER_DATA_SIZE == 0) { ticks += (is_write ? GetClusterWriteTbTicks : GetClusterReadTbTicks)(m_ios.GetVersion()); copy_length = CLUSTER_DATA_SIZE; if (is_write) - m_fd_map[request.fd].superblock_flush_needed = true; + m_fd_map[fd].superblock_flush_needed = true; } else { - ticks += SimulatePopulateFileCache(request.fd, offset, status->size); + ticks += SimulatePopulateFileCache(fd, offset, status->size); const u32 start = offset - m_cache_chain_index * CLUSTER_DATA_SIZE; copy_length = std::min(CLUSTER_DATA_SIZE - start, count); @@ -276,7 +303,7 @@ u64 FSDevice::EstimateTicksForReadWrite(const Handle& handle, const ReadWriteReq return ticks; } -bool FSDevice::HasCacheForFile(u32 fd, u32 offset) const +bool FSDevice::HasCacheForFile(u64 fd, u32 offset) const { const u16 chain_index = static_cast(offset / CLUSTER_DATA_SIZE); return m_cache_fd == fd && m_cache_chain_index == chain_index; @@ -284,52 +311,81 @@ bool FSDevice::HasCacheForFile(u32 fd, u32 offset) const std::optional FSDevice::Read(const ReadWriteRequest& request) { - const Handle& handle = m_fd_map[request.fd]; + return MakeIPCReply([&](Ticks t) { + return Read(request.fd, Memory::GetPointer(request.buffer), request.size, request.buffer, t); + }); +} + +s32 FSDevice::Read(u64 fd, u8* data, u32 size, std::optional ipc_buffer_addr, Ticks ticks) +{ + ticks.AddTimeBaseTicks(GetIPCOverheadTicks()); + + const Handle& handle = m_fd_map[fd]; if (handle.fs_fd == INVALID_FD) - return GetFSReply(ConvertResult(ResultCode::Invalid)); + return ConvertResult(ResultCode::Invalid); // Simulate the FS read logic to estimate ticks. Note: this must be done before reading. - const u64 ticks = EstimateTicksForReadWrite(handle, request); + ticks.AddTimeBaseTicks(EstimateTicksForReadWrite(handle, fd, IPC_CMD_READ, size)); + + const Result result = m_ios.GetFS()->ReadBytesFromFile(handle.fs_fd, data, size); + if (ipc_buffer_addr) + LogResult(result, "Read({}, 0x{:08x}, {})", handle.name.data(), *ipc_buffer_addr, size); - const Result result = m_ios.GetFS()->ReadBytesFromFile( - handle.fs_fd, Memory::GetPointer(request.buffer), request.size); - LogResult(result, "Read({}, 0x{:08x}, {})", handle.name.data(), request.buffer, request.size); if (!result) - return GetFSReply(ConvertResult(result.Error())); + return ConvertResult(result.Error()); - return GetFSReply(*result, ticks); + return *result; } std::optional FSDevice::Write(const ReadWriteRequest& request) { - const Handle& handle = m_fd_map[request.fd]; + return MakeIPCReply([&](Ticks t) { + return Write(request.fd, Memory::GetPointer(request.buffer), request.size, request.buffer, t); + }); +} + +s32 FSDevice::Write(u64 fd, const u8* data, u32 size, std::optional ipc_buffer_addr, + Ticks ticks) +{ + ticks.AddTimeBaseTicks(GetIPCOverheadTicks()); + + const Handle& handle = m_fd_map[fd]; if (handle.fs_fd == INVALID_FD) - return GetFSReply(ConvertResult(ResultCode::Invalid)); + return ConvertResult(ResultCode::Invalid); // Simulate the FS write logic to estimate ticks. Must be done before writing. - const u64 ticks = EstimateTicksForReadWrite(handle, request); + ticks.AddTimeBaseTicks(EstimateTicksForReadWrite(handle, fd, IPC_CMD_WRITE, size)); + + const Result result = m_ios.GetFS()->WriteBytesToFile(handle.fs_fd, data, size); + if (ipc_buffer_addr) + LogResult(result, "Write({}, 0x{:08x}, {})", handle.name.data(), *ipc_buffer_addr, size); - const Result result = m_ios.GetFS()->WriteBytesToFile( - handle.fs_fd, Memory::GetPointer(request.buffer), request.size); - LogResult(result, "Write({}, 0x{:08x}, {})", handle.name.data(), request.buffer, request.size); if (!result) - return GetFSReply(ConvertResult(result.Error())); + return ConvertResult(result.Error()); - return GetFSReply(*result, ticks); + return *result; } std::optional FSDevice::Seek(const SeekRequest& request) { - const Handle& handle = m_fd_map[request.fd]; - if (handle.fs_fd == INVALID_FD) - return GetFSReply(ConvertResult(ResultCode::Invalid)); + return MakeIPCReply([&](Ticks t) { + return Seek(request.fd, request.offset, HLE::FS::SeekMode(request.mode), t); + }); +} - const Result result = - m_ios.GetFS()->SeekFile(handle.fs_fd, request.offset, FS::SeekMode(request.mode)); - LogResult(result, "Seek({}, 0x{:08x}, {})", handle.name.data(), request.offset, request.mode); +s32 FSDevice::Seek(u64 fd, u32 offset, FS::SeekMode mode, Ticks ticks) +{ + ticks.AddTimeBaseTicks(GetIPCOverheadTicks()); + + const Handle& handle = m_fd_map[fd]; + if (handle.fs_fd == INVALID_FD) + return ConvertResult(ResultCode::Invalid); + + const Result result = m_ios.GetFS()->SeekFile(handle.fs_fd, offset, mode); + LogResult(result, "Seek({}, 0x{:08x}, {})", handle.name.data(), offset, mode); if (!result) - return GetFSReply(ConvertResult(result.Error())); - return GetFSReply(*result); + return ConvertResult(result.Error()); + return *result; } #pragma pack(push, 1) diff --git a/Source/Core/Core/IOS/FS/FileSystemProxy.h b/Source/Core/Core/IOS/FS/FileSystemProxy.h index e2967eb5dc..a2a1c20428 100644 --- a/Source/Core/Core/IOS/FS/FileSystemProxy.h +++ b/Source/Core/Core/IOS/FS/FileSystemProxy.h @@ -6,7 +6,9 @@ #include #include +#include #include +#include #include "Common/CommonTypes.h" #include "Core/IOS/Device.h" @@ -24,6 +26,16 @@ class FSDevice : public Device public: FSDevice(Kernel& ios, const std::string& device_name); + // These are the equivalent of the IPC command handlers so IPC overhead is included + // in timing calculations. + s64 Open(FS::Uid uid, FS::Gid gid, const std::string& path, FS::Mode mode, + std::optional ipc_fd = {}, Ticks ticks = {}); + s32 Close(u64 fd, Ticks ticks = {}); + s32 Read(u64 fd, u8* data, u32 size, std::optional ipc_buffer_addr = {}, Ticks ticks = {}); + s32 Write(u64 fd, const u8* data, u32 size, std::optional ipc_buffer_addr = {}, + Ticks ticks = {}); + s32 Seek(u64 fd, u32 offset, FS::SeekMode mode, Ticks ticks = {}); + void DoState(PointerWrap& p) override; std::optional Open(const OpenRequest& request) override; @@ -76,14 +88,16 @@ private: IPCReply GetUsage(const Handle& handle, const IOCtlVRequest& request); IPCReply Shutdown(const Handle& handle, const IOCtlRequest& request); - u64 EstimateTicksForReadWrite(const Handle& handle, const ReadWriteRequest& request); - u64 SimulatePopulateFileCache(u32 fd, u32 offset, u32 file_size); + u64 EstimateTicksForReadWrite(const Handle& handle, u64 fd, IPCCommandType command, u32 size); + u64 SimulatePopulateFileCache(u64 fd, u32 offset, u32 file_size); u64 SimulateFlushFileCache(); - bool HasCacheForFile(u32 fd, u32 offset) const; + bool HasCacheForFile(u64 fd, u32 offset) const; - std::map m_fd_map; - u32 m_cache_fd = INVALID_FD; - u16 m_cache_chain_index = 0; bool m_dirty_cache = false; + u16 m_cache_chain_index = 0; + std::optional m_cache_fd; + // The first 0x18 IDs are reserved for the PPC. + u64 m_next_fd = 0x18; + std::map m_fd_map; }; } // namespace IOS::HLE diff --git a/Source/Core/Core/IOS/IOS.h b/Source/Core/Core/IOS/IOS.h index ba823894b9..15bcc1cccf 100644 --- a/Source/Core/Core/IOS/IOS.h +++ b/Source/Core/Core/IOS/IOS.h @@ -45,6 +45,38 @@ struct IPCReply u64 reply_delay_ticks; }; +// Used to make it more convenient for functions to return timing information +// without having to explicitly keep track of ticks in callers. +class Ticks +{ +public: + Ticks(u64* ticks = nullptr) : m_ticks(ticks) {} + + void Add(u64 ticks) + { + if (m_ticks != nullptr) + *m_ticks += ticks; + } + + void AddTimeBaseTicks(u64 tb_ticks) { Add(tb_ticks * SystemTimers::TIMER_RATIO); } + +private: + u64* m_ticks = nullptr; +}; + +template +IPCReply MakeIPCReply(u64 ticks, const ResultProducer& fn) +{ + const s32 result_value = fn(Ticks{&ticks}); + return IPCReply{result_value, ticks}; +} + +template +IPCReply MakeIPCReply(const ResultProducer& fn) +{ + return MakeIPCReply(0, fn); +} + enum IPCCommandType : u32 { IPC_CMD_OPEN = 1, diff --git a/Source/Core/Core/State.cpp b/Source/Core/Core/State.cpp index cca921c421..3d20c0fb05 100644 --- a/Source/Core/Core/State.cpp +++ b/Source/Core/Core/State.cpp @@ -74,7 +74,7 @@ static Common::Event g_compressAndDumpStateSyncEvent; static std::thread g_save_thread; // Don't forget to increase this after doing changes on the savestate system -constexpr u32 STATE_VERSION = 128; // Last changed in PR 9366 +constexpr u32 STATE_VERSION = 129; // Last changed in PR 9511 // Maps savestate versions to Dolphin versions. // Versions after 42 don't need to be added to this list, From 5eca82a6f2049bc10843e9bfaeb5e8963fc071de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Tue, 16 Feb 2021 12:30:29 +0100 Subject: [PATCH 4/6] IOS/ES: Allow various utility functions to return timing info --- Source/Core/Core/IOS/ES/ES.cpp | 4 +- Source/Core/Core/IOS/ES/ES.h | 10 ++--- Source/Core/Core/IOS/ES/Formats.cpp | 25 +++++++----- Source/Core/Core/IOS/ES/Formats.h | 12 +++++- Source/Core/Core/IOS/ES/NandUtils.cpp | 45 ++++++++++----------- Source/Core/Core/IOS/ES/TitleContents.cpp | 2 +- Source/Core/Core/IOS/ES/TitleManagement.cpp | 6 +-- Source/Core/Core/IOS/FS/FileSystemProxy.cpp | 33 ++++++++++----- Source/Core/Core/IOS/FS/FileSystemProxy.h | 9 +++++ Source/Core/Core/IOS/IOS.cpp | 5 +++ Source/Core/Core/IOS/IOS.h | 2 + 11 files changed, 98 insertions(+), 55 deletions(-) diff --git a/Source/Core/Core/IOS/ES/ES.cpp b/Source/Core/Core/IOS/ES/ES.cpp index 8be562c84b..5ce32591c8 100644 --- a/Source/Core/Core/IOS/ES/ES.cpp +++ b/Source/Core/Core/IOS/ES/ES.cpp @@ -161,7 +161,7 @@ IPCReply ESDevice::GetTitleId(const IOCtlVRequest& request) static bool UpdateUIDAndGID(Kernel& kernel, const ES::TMDReader& tmd) { - ES::UIDSys uid_sys{kernel.GetFS()}; + ES::UIDSys uid_sys{kernel.GetFSDevice()}; const u64 title_id = tmd.GetTitleId(); const u32 uid = uid_sys.GetOrInsertUIDForTitle(title_id); if (uid == 0) @@ -177,7 +177,7 @@ static bool UpdateUIDAndGID(Kernel& kernel, const ES::TMDReader& tmd) static ReturnCode CheckIsAllowedToSetUID(Kernel& kernel, const u32 caller_uid, const ES::TMDReader& active_tmd) { - ES::UIDSys uid_map{kernel.GetFS()}; + ES::UIDSys uid_map{kernel.GetFSDevice()}; const u32 system_menu_uid = uid_map.GetOrInsertUIDForTitle(Titles::SYSTEM_MENU); if (!system_menu_uid) return ES_SHORT_READ; diff --git a/Source/Core/Core/IOS/ES/ES.h b/Source/Core/Core/IOS/ES/ES.h index c4636cab17..4e2d0fa8d9 100644 --- a/Source/Core/Core/IOS/ES/ES.h +++ b/Source/Core/Core/IOS/ES/ES.h @@ -87,8 +87,8 @@ public: No = false, }; - ES::TMDReader FindImportTMD(u64 title_id) const; - ES::TMDReader FindInstalledTMD(u64 title_id) const; + ES::TMDReader FindImportTMD(u64 title_id, Ticks ticks = {}) const; + ES::TMDReader FindInstalledTMD(u64 title_id, Ticks ticks = {}) const; ES::TicketReader FindSignedTicket(u64 title_id) const; // Get installed titles (in /title) without checking for TMDs at all. @@ -364,9 +364,9 @@ private: void FinishStaleImport(u64 title_id); void FinishAllStaleImports(); - std::string GetContentPath(u64 title_id, const ES::Content& content, - const ES::SharedContentMap& map) const; - std::string GetContentPath(u64 title_id, const ES::Content& content) const; + std::string GetContentPath(u64 title_id, const ES::Content& content, Ticks ticks = {}) const; + + static constexpr u64 IPC_OVERHEAD_TICKS = 2700_tbticks; struct OpenedContent { diff --git a/Source/Core/Core/IOS/ES/Formats.cpp b/Source/Core/Core/IOS/ES/Formats.cpp index fcda458126..441747a7d9 100644 --- a/Source/Core/Core/IOS/ES/Formats.cpp +++ b/Source/Core/Core/IOS/ES/Formats.cpp @@ -27,6 +27,7 @@ #include "Core/CommonTitles.h" #include "Core/IOS/Device.h" #include "Core/IOS/FS/FileSystem.h" +#include "Core/IOS/FS/FileSystemProxy.h" #include "Core/IOS/IOS.h" #include "Core/IOS/IOSC.h" #include "Core/IOS/Uids.h" @@ -521,17 +522,21 @@ struct SharedContentMap::Entry }; constexpr char CONTENT_MAP_PATH[] = "/shared1/content.map"; -SharedContentMap::SharedContentMap(std::shared_ptr fs) : m_fs{fs} +SharedContentMap::SharedContentMap(std::shared_ptr fs) + : m_fs_device{fs}, m_fs{fs->GetFS()} { static_assert(sizeof(Entry) == 28, "SharedContentMap::Entry has the wrong size"); Entry entry; - const auto file = fs->OpenFile(PID_KERNEL, PID_KERNEL, CONTENT_MAP_PATH, HLE::FS::Mode::Read); - while (file && file->Read(&entry, 1)) + s64 fd = fs->Open(PID_KERNEL, PID_KERNEL, CONTENT_MAP_PATH, HLE::FS::Mode::Read, {}, &m_ticks); + if (fd < 0) + return; + while (fs->Read(fd, &entry, 1, &m_ticks) == sizeof(entry)) { m_entries.push_back(entry); m_last_id++; } + fs->Close(fd, &m_ticks); } SharedContentMap::~SharedContentMap() = default; @@ -600,32 +605,34 @@ bool SharedContentMap::WriteEntries() const HLE::FS::ResultCode::Success; } -static std::pair ReadUidSysEntry(const HLE::FS::FileHandle& file) +static std::pair ReadUidSysEntry(HLE::FSDevice& fs, u64 fd, u64* ticks) { u64 title_id = 0; - if (!file.Read(&title_id, 1)) + if (fs.Read(fd, &title_id, 1, ticks) != sizeof(title_id)) return {}; u32 uid = 0; - if (!file.Read(&uid, 1)) + if (fs.Read(fd, &uid, 1, ticks) != sizeof(uid)) return {}; return {Common::swap32(uid), Common::swap64(title_id)}; } constexpr char UID_MAP_PATH[] = "/sys/uid.sys"; -UIDSys::UIDSys(std::shared_ptr fs) : m_fs{fs} +UIDSys::UIDSys(std::shared_ptr fs) : m_fs_device{fs}, m_fs{fs->GetFS()} { - if (const auto file = fs->OpenFile(PID_KERNEL, PID_KERNEL, UID_MAP_PATH, HLE::FS::Mode::Read)) + s64 fd = fs->Open(PID_KERNEL, PID_KERNEL, UID_MAP_PATH, HLE::FS::Mode::Read, {}, &m_ticks); + if (fd >= 0) { while (true) { - std::pair entry = ReadUidSysEntry(*file); + std::pair entry = ReadUidSysEntry(*fs, fd, &m_ticks); if (!entry.first && !entry.second) break; m_entries.insert(std::move(entry)); } + fs->Close(fd, &m_ticks); } if (m_entries.empty()) diff --git a/Source/Core/Core/IOS/ES/Formats.h b/Source/Core/Core/IOS/ES/Formats.h index 2ab6cfe6b4..1b1fe6996e 100644 --- a/Source/Core/Core/IOS/ES/Formats.h +++ b/Source/Core/Core/IOS/ES/Formats.h @@ -260,7 +260,7 @@ public: class SharedContentMap final { public: - explicit SharedContentMap(std::shared_ptr fs); + explicit SharedContentMap(std::shared_ptr fs); ~SharedContentMap(); std::optional GetFilenameFromSHA1(const std::array& sha1) const; @@ -268,27 +268,35 @@ public: bool DeleteSharedContent(const std::array& sha1); std::vector> GetHashes() const; + u64 GetTicks() const { return m_ticks; } + private: bool WriteEntries() const; struct Entry; u32 m_last_id = 0; std::vector m_entries; + std::shared_ptr m_fs_device; std::shared_ptr m_fs; + u64 m_ticks = 0; }; class UIDSys final { public: - explicit UIDSys(std::shared_ptr fs); + explicit UIDSys(std::shared_ptr fs); u32 GetUIDFromTitle(u64 title_id) const; u32 GetOrInsertUIDForTitle(u64 title_id); u32 GetNextUID() const; + u64 GetTicks() const { return m_ticks; } + private: + std::shared_ptr m_fs_device; std::shared_ptr m_fs; std::map m_entries; + u64 m_ticks = 0; }; class CertReader final : public SignedBlobReader diff --git a/Source/Core/Core/IOS/ES/NandUtils.cpp b/Source/Core/Core/IOS/ES/NandUtils.cpp index 44fb46f471..88cc040552 100644 --- a/Source/Core/Core/IOS/ES/NandUtils.cpp +++ b/Source/Core/Core/IOS/ES/NandUtils.cpp @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include @@ -17,35 +16,38 @@ #include "Common/CommonTypes.h" #include "Common/Logging/Log.h" #include "Common/NandPaths.h" +#include "Common/ScopeGuard.h" #include "Common/StringUtil.h" #include "Core/IOS/ES/ES.h" #include "Core/IOS/ES/Formats.h" +#include "Core/IOS/FS/FileSystemProxy.h" #include "Core/IOS/Uids.h" namespace IOS::HLE { -static ES::TMDReader FindTMD(FS::FileSystem* fs, u64 title_id, const std::string& tmd_path) +static ES::TMDReader FindTMD(FSDevice& fs, const std::string& tmd_path, Ticks ticks) { - const auto file = fs->OpenFile(PID_KERNEL, PID_KERNEL, tmd_path, FS::Mode::Read); - if (!file) + const s64 fd = fs.Open(PID_KERNEL, PID_KERNEL, tmd_path, FS::Mode::Read, {}, ticks); + if (fd < 0) return {}; + Common::ScopeGuard guard{[&] { fs.Close(fd, ticks); }}; - std::vector tmd_bytes(file->GetStatus()->size); - if (!file->Read(tmd_bytes.data(), tmd_bytes.size())) + std::vector tmd_bytes(fs.GetFileStatus(fd, ticks)->size); + if (!fs.Read(fd, tmd_bytes.data(), tmd_bytes.size(), ticks)) return {}; return ES::TMDReader{std::move(tmd_bytes)}; } -ES::TMDReader ESDevice::FindImportTMD(u64 title_id) const +ES::TMDReader ESDevice::FindImportTMD(u64 title_id, Ticks ticks) const { - return FindTMD(m_ios.GetFS().get(), title_id, - Common::GetImportTitlePath(title_id) + "/content/title.tmd"); + return FindTMD(*m_ios.GetFSDevice(), Common::GetImportTitlePath(title_id) + "/content/title.tmd", + ticks); } -ES::TMDReader ESDevice::FindInstalledTMD(u64 title_id) const +ES::TMDReader ESDevice::FindInstalledTMD(u64 title_id, Ticks ticks) const { - return FindTMD(m_ios.GetFS().get(), title_id, Common::GetTMDFileName(title_id)); + return FindTMD(*m_ios.GetFSDevice(), Common::GetTMDFileName(title_id), ticks); } ES::TicketReader ESDevice::FindSignedTicket(u64 title_id) const @@ -171,16 +173,15 @@ ESDevice::GetStoredContentsFromTMD(const ES::TMDReader& tmd, if (!tmd.IsValid()) return {}; - const ES::SharedContentMap map{m_ios.GetFS()}; const std::vector contents = tmd.GetContents(); std::vector stored_contents; std::copy_if(contents.begin(), contents.end(), std::back_inserter(stored_contents), - [this, &tmd, &map, check_content_hashes](const ES::Content& content) { + [this, &tmd, check_content_hashes](const ES::Content& content) { const auto fs = m_ios.GetFS(); - const std::string path = GetContentPath(tmd.GetTitleId(), content, map); + const std::string path = GetContentPath(tmd.GetTitleId(), content); if (path.empty()) return false; @@ -217,7 +218,7 @@ u32 ESDevice::GetSharedContentsCount() const std::vector> ESDevice::GetSharedContents() const { - const ES::SharedContentMap map{m_ios.GetFS()}; + const ES::SharedContentMap map{m_ios.GetFSDevice()}; return map.GetHashes(); } @@ -267,7 +268,7 @@ bool ESDevice::CreateTitleDirectories(u64 title_id, u16 group_id) const return false; } - ES::UIDSys uid_sys{fs}; + ES::UIDSys uid_sys{m_ios.GetFSDevice()}; const u32 uid = uid_sys.GetOrInsertUIDForTitle(title_id); if (fs->SetMetadata(0, data_dir, uid, group_id, 0, data_dir_modes) != FS::ResultCode::Success) { @@ -382,16 +383,14 @@ void ESDevice::FinishAllStaleImports() } std::string ESDevice::GetContentPath(const u64 title_id, const ES::Content& content, - const ES::SharedContentMap& content_map) const + Ticks ticks) const { if (content.IsShared()) + { + ES::SharedContentMap content_map{m_ios.GetFSDevice()}; + ticks.Add(content_map.GetTicks()); return content_map.GetFilenameFromSHA1(content.sha1).value_or(""); + } return fmt::format("{}/{:08x}.app", Common::GetTitleContentPath(title_id), content.id); } - -std::string ESDevice::GetContentPath(const u64 title_id, const ES::Content& content) const -{ - ES::SharedContentMap map{m_ios.GetFS()}; - return GetContentPath(title_id, content, map); -} } // namespace IOS::HLE diff --git a/Source/Core/Core/IOS/ES/TitleContents.cpp b/Source/Core/Core/IOS/ES/TitleContents.cpp index 30dd6c08c5..547df70eaf 100644 --- a/Source/Core/Core/IOS/ES/TitleContents.cpp +++ b/Source/Core/Core/IOS/ES/TitleContents.cpp @@ -78,7 +78,7 @@ IPCReply ESDevice::OpenActiveTitleContent(u32 caller_uid, const IOCtlVRequest& r if (!m_title_context.active) return IPCReply(ES_EINVAL); - ES::UIDSys uid_map{m_ios.GetFS()}; + ES::UIDSys uid_map{m_ios.GetFSDevice()}; const u32 uid = uid_map.GetOrInsertUIDForTitle(m_title_context.tmd.GetTitleId()); if (caller_uid != 0 && caller_uid != uid) return IPCReply(ES_EACCES); diff --git a/Source/Core/Core/IOS/ES/TitleManagement.cpp b/Source/Core/Core/IOS/ES/TitleManagement.cpp index 09b543f1db..df9c6ec9c2 100644 --- a/Source/Core/Core/IOS/ES/TitleManagement.cpp +++ b/Source/Core/Core/IOS/ES/TitleManagement.cpp @@ -394,7 +394,7 @@ ReturnCode ESDevice::ImportContentEnd(Context& context, u32 content_fd) std::string content_path; if (content_info.IsShared()) { - ES::SharedContentMap shared_content{fs}; + ES::SharedContentMap shared_content{m_ios.GetFSDevice()}; content_path = shared_content.AddSharedContent(content_info.sha1); } else @@ -441,7 +441,7 @@ static bool HasAllRequiredContents(Kernel& ios, const ES::TMDReader& tmd) { const u64 title_id = tmd.GetTitleId(); const std::vector contents = tmd.GetContents(); - const ES::SharedContentMap shared_content_map{ios.GetFS()}; + const ES::SharedContentMap shared_content_map{ios.GetFSDevice()}; return std::all_of(contents.cbegin(), contents.cend(), [&](const ES::Content& content) { if (content.IsOptional()) return true; @@ -818,7 +818,7 @@ IPCReply ESDevice::ExportTitleDone(Context& context, const IOCtlVRequest& reques ReturnCode ESDevice::DeleteSharedContent(const std::array& sha1) const { - ES::SharedContentMap map{m_ios.GetFS()}; + ES::SharedContentMap map{m_ios.GetFSDevice()}; const auto content_path = map.GetFilenameFromSHA1(sha1); if (!content_path) return ES_EINVAL; diff --git a/Source/Core/Core/IOS/FS/FileSystemProxy.cpp b/Source/Core/Core/IOS/FS/FileSystemProxy.cpp index dd7ae5ceb8..7bd33e44dd 100644 --- a/Source/Core/Core/IOS/FS/FileSystemProxy.cpp +++ b/Source/Core/Core/IOS/FS/FileSystemProxy.cpp @@ -661,19 +661,32 @@ IPCReply FSDevice::SetFileVersionControl(const Handle& handle, const IOCtlReques IPCReply FSDevice::GetFileStats(const Handle& handle, const IOCtlRequest& request) { - if (request.buffer_out_size < 8 || handle.fs_fd == INVALID_FD) + if (request.buffer_out_size < 8) return GetFSReply(ConvertResult(ResultCode::Invalid)); - const Result status = m_ios.GetFS()->GetFileStatus(handle.fs_fd); - LogResult(status, "GetFileStatus({})", handle.name.data()); - if (!status) - return IPCReply(ConvertResult(status.Error())); + return MakeIPCReply([&](Ticks ticks) { + const Result status = GetFileStatus(request.fd, ticks); + if (!status) + return ConvertResult(status.Error()); - ISFSFileStats out; - out.size = status->size; - out.seek_position = status->offset; - Memory::CopyToEmu(request.buffer_out, &out, sizeof(out)); - return IPCReply(IPC_SUCCESS); + ISFSFileStats out; + out.size = status->size; + out.seek_position = status->offset; + Memory::CopyToEmu(request.buffer_out, &out, sizeof(out)); + return IPC_SUCCESS; + }); +} + +FS::Result FSDevice::GetFileStatus(u64 fd, Ticks ticks) +{ + ticks.AddTimeBaseTicks(GetIPCOverheadTicks()); + const auto& handle = m_fd_map[fd]; + if (handle.fs_fd == INVALID_FD) + return ResultCode::Invalid; + + auto status = m_ios.GetFS()->GetFileStatus(handle.fs_fd); + LogResult(status, "GetFileStatus({})", handle.name.data()); + return status; } IPCReply FSDevice::GetUsage(const Handle& handle, const IOCtlVRequest& request) diff --git a/Source/Core/Core/IOS/FS/FileSystemProxy.h b/Source/Core/Core/IOS/FS/FileSystemProxy.h index a2a1c20428..101dbdffc2 100644 --- a/Source/Core/Core/IOS/FS/FileSystemProxy.h +++ b/Source/Core/Core/IOS/FS/FileSystemProxy.h @@ -35,6 +35,15 @@ public: s32 Write(u64 fd, const u8* data, u32 size, std::optional ipc_buffer_addr = {}, Ticks ticks = {}); s32 Seek(u64 fd, u32 offset, FS::SeekMode mode, Ticks ticks = {}); + FS::Result GetFileStatus(u64 fd, Ticks ticks = {}); + + template + s32 Read(u64 fd, T* data, size_t count, Ticks ticks = {}) + { + return Read(fd, reinterpret_cast(data), static_cast(sizeof(T) * count), {}, ticks); + } + + std::shared_ptr GetFS() const { return m_ios.GetFS(); } void DoState(PointerWrap& p) override; diff --git a/Source/Core/Core/IOS/IOS.cpp b/Source/Core/Core/IOS/IOS.cpp index db0c3593f8..50b586e343 100644 --- a/Source/Core/Core/IOS/IOS.cpp +++ b/Source/Core/Core/IOS/IOS.cpp @@ -285,6 +285,11 @@ std::shared_ptr Kernel::GetFS() return m_fs; } +std::shared_ptr Kernel::GetFSDevice() +{ + return std::static_pointer_cast(m_device_map.at("/dev/fs")); +} + std::shared_ptr Kernel::GetES() { return std::static_pointer_cast(m_device_map.at("/dev/es")); diff --git a/Source/Core/Core/IOS/IOS.h b/Source/Core/Core/IOS/IOS.h index 15bcc1cccf..11220f1194 100644 --- a/Source/Core/Core/IOS/IOS.h +++ b/Source/Core/Core/IOS/IOS.h @@ -30,6 +30,7 @@ class FileSystem; class Device; class ESDevice; +class FSDevice; struct Request; struct OpenRequest; @@ -116,6 +117,7 @@ public: // These are *always* part of the IOS kernel and always available. // They are also the only available resource managers even before loading any module. std::shared_ptr GetFS(); + std::shared_ptr GetFSDevice(); std::shared_ptr GetES(); void SDIO_EventNotify(); From f750208aa31a860414fc877346ed17001c0df512 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Sun, 14 Feb 2021 00:58:27 +0100 Subject: [PATCH 5/6] IOS/ES: Emulate FS timings for content wrapper IPC commands Filesystem accesses aren't magically faster when they are done by ES, so this commit changes our content wrapper IPC commands to take FS access times and read operations into account. This should make content read timings a lot more accurate and closer to console. Note that the accuracy of the timings are limited to the accuracy of the emulated FS timings, and currently performance differences between IOS9-IOS28 and newer IOS versions are not emulated. Part 1 of fixing https://bugs.dolphin-emu.org/issues/11346 (part 2 will involve emulating those differences) --- Source/Core/Core/IOS/ES/ES.h | 10 +- Source/Core/Core/IOS/ES/TitleContents.cpp | 122 +++++++++++--------- Source/Core/Core/IOS/ES/TitleManagement.cpp | 2 +- 3 files changed, 71 insertions(+), 63 deletions(-) diff --git a/Source/Core/Core/IOS/ES/ES.h b/Source/Core/Core/IOS/ES/ES.h index 4e2d0fa8d9..10af9fbd02 100644 --- a/Source/Core/Core/IOS/ES/ES.h +++ b/Source/Core/Core/IOS/ES/ES.h @@ -105,10 +105,10 @@ public: std::vector> GetSharedContents() const; // Title contents - s32 OpenContent(const ES::TMDReader& tmd, u16 content_index, u32 uid); - ReturnCode CloseContent(u32 cfd, u32 uid); - s32 ReadContent(u32 cfd, u8* buffer, u32 size, u32 uid); - s32 SeekContent(u32 cfd, u32 offset, SeekMode mode, u32 uid); + s32 OpenContent(const ES::TMDReader& tmd, u16 content_index, u32 uid, Ticks ticks = {}); + s32 CloseContent(u32 cfd, u32 uid, Ticks ticks = {}); + s32 ReadContent(u32 cfd, u8* buffer, u32 size, u32 uid, Ticks ticks = {}); + s32 SeekContent(u32 cfd, u32 offset, SeekMode mode, u32 uid, Ticks ticks = {}); // Title management enum class TicketImportType @@ -371,7 +371,7 @@ private: struct OpenedContent { bool m_opened = false; - FS::Fd m_fd; + u64 m_fd; u64 m_title_id = 0; ES::Content m_content; u32 m_uid = 0; diff --git a/Source/Core/Core/IOS/ES/TitleContents.cpp b/Source/Core/Core/IOS/ES/TitleContents.cpp index 547df70eaf..9a68475743 100644 --- a/Source/Core/Core/IOS/ES/TitleContents.cpp +++ b/Source/Core/Core/IOS/ES/TitleContents.cpp @@ -4,18 +4,17 @@ #include "Core/IOS/ES/ES.h" -#include #include #include "Common/Logging/Log.h" -#include "Common/MsgHandler.h" #include "Core/HW/Memmap.h" #include "Core/IOS/ES/Formats.h" +#include "Core/IOS/FS/FileSystemProxy.h" #include "Core/IOS/Uids.h" namespace IOS::HLE { -s32 ESDevice::OpenContent(const ES::TMDReader& tmd, u16 content_index, u32 uid) +s32 ESDevice::OpenContent(const ES::TMDReader& tmd, u16 content_index, u32 uid, Ticks ticks) { const u64 title_id = tmd.GetTitleId(); @@ -29,13 +28,13 @@ s32 ESDevice::OpenContent(const ES::TMDReader& tmd, u16 content_index, u32 uid) if (entry.m_opened) continue; - auto file = m_ios.GetFS()->OpenFile(PID_KERNEL, PID_KERNEL, GetContentPath(title_id, content), - FS::Mode::Read); - if (!file) - return FS::ConvertResult(file.Error()); + const std::string path = GetContentPath(title_id, content, ticks); + s64 fd = m_ios.GetFSDevice()->Open(PID_KERNEL, PID_KERNEL, path, FS::Mode::Read, {}, ticks); + if (fd < 0) + return fd; entry.m_opened = true; - entry.m_fd = file->Release(); + entry.m_fd = fd; entry.m_content = content; entry.m_title_id = title_id; entry.m_uid = uid; @@ -50,43 +49,48 @@ s32 ESDevice::OpenContent(const ES::TMDReader& tmd, u16 content_index, u32 uid) IPCReply ESDevice::OpenContent(u32 uid, const IOCtlVRequest& request) { - if (!request.HasNumberOfValidVectors(3, 0) || request.in_vectors[0].size != sizeof(u64) || - request.in_vectors[1].size != sizeof(ES::TicketView) || - request.in_vectors[2].size != sizeof(u32)) - { - return IPCReply(ES_EINVAL); - } + return MakeIPCReply(IPC_OVERHEAD_TICKS, [&](Ticks ticks) -> s32 { + if (!request.HasNumberOfValidVectors(3, 0) || request.in_vectors[0].size != sizeof(u64) || + request.in_vectors[1].size != sizeof(ES::TicketView) || + request.in_vectors[2].size != sizeof(u32)) + { + return ES_EINVAL; + } - const u64 title_id = Memory::Read_U64(request.in_vectors[0].address); - const u32 content_index = Memory::Read_U32(request.in_vectors[2].address); - // TODO: check the ticket view, check permissions. + const u64 title_id = Memory::Read_U64(request.in_vectors[0].address); + const u32 content_index = Memory::Read_U32(request.in_vectors[2].address); + // TODO: check the ticket view, check permissions. - const auto tmd = FindInstalledTMD(title_id); - if (!tmd.IsValid()) - return IPCReply(FS_ENOENT); + const auto tmd = FindInstalledTMD(title_id, ticks); + if (!tmd.IsValid()) + return FS_ENOENT; - return IPCReply(OpenContent(tmd, content_index, uid)); + return OpenContent(tmd, content_index, uid, ticks); + }); } IPCReply ESDevice::OpenActiveTitleContent(u32 caller_uid, const IOCtlVRequest& request) { - if (!request.HasNumberOfValidVectors(1, 0) || request.in_vectors[0].size != sizeof(u32)) - return IPCReply(ES_EINVAL); + return MakeIPCReply(IPC_OVERHEAD_TICKS, [&](Ticks ticks) -> s32 { + if (!request.HasNumberOfValidVectors(1, 0) || request.in_vectors[0].size != sizeof(u32)) + return ES_EINVAL; - const u32 content_index = Memory::Read_U32(request.in_vectors[0].address); + const u32 content_index = Memory::Read_U32(request.in_vectors[0].address); - if (!m_title_context.active) - return IPCReply(ES_EINVAL); + if (!m_title_context.active) + return ES_EINVAL; - ES::UIDSys uid_map{m_ios.GetFSDevice()}; - const u32 uid = uid_map.GetOrInsertUIDForTitle(m_title_context.tmd.GetTitleId()); - if (caller_uid != 0 && caller_uid != uid) - return IPCReply(ES_EACCES); + ES::UIDSys uid_map{m_ios.GetFSDevice()}; + const u32 uid = uid_map.GetOrInsertUIDForTitle(m_title_context.tmd.GetTitleId()); + ticks.Add(uid_map.GetTicks()); + if (caller_uid != 0 && caller_uid != uid) + return ES_EACCES; - return IPCReply(OpenContent(m_title_context.tmd, content_index, caller_uid)); + return OpenContent(m_title_context.tmd, content_index, caller_uid, ticks); + }); } -s32 ESDevice::ReadContent(u32 cfd, u8* buffer, u32 size, u32 uid) +s32 ESDevice::ReadContent(u32 cfd, u8* buffer, u32 size, u32 uid, Ticks ticks) { if (cfd >= m_content_table.size()) return ES_EINVAL; @@ -97,25 +101,26 @@ s32 ESDevice::ReadContent(u32 cfd, u8* buffer, u32 size, u32 uid) if (!entry.m_opened) return IPC_EINVAL; - const auto result = m_ios.GetFS()->ReadBytesFromFile(entry.m_fd, buffer, size); - return result.Succeeded() ? *result : FS::ConvertResult(result.Error()); + return m_ios.GetFSDevice()->Read(entry.m_fd, buffer, size, {}, ticks); } IPCReply ESDevice::ReadContent(u32 uid, const IOCtlVRequest& request) { - if (!request.HasNumberOfValidVectors(1, 1) || request.in_vectors[0].size != sizeof(u32)) - return IPCReply(ES_EINVAL); + return MakeIPCReply(IPC_OVERHEAD_TICKS, [&](Ticks ticks) -> s32 { + if (!request.HasNumberOfValidVectors(1, 1) || request.in_vectors[0].size != sizeof(u32)) + return ES_EINVAL; - const u32 cfd = Memory::Read_U32(request.in_vectors[0].address); - const u32 size = request.io_vectors[0].size; - const u32 addr = request.io_vectors[0].address; + const u32 cfd = Memory::Read_U32(request.in_vectors[0].address); + const u32 size = request.io_vectors[0].size; + const u32 addr = request.io_vectors[0].address; - INFO_LOG_FMT(IOS_ES, "ReadContent(uid={:#x}, cfd={}, size={}, addr={:08x})", uid, cfd, size, - addr); - return IPCReply(ReadContent(cfd, Memory::GetPointer(addr), size, uid)); + INFO_LOG_FMT(IOS_ES, "ReadContent(uid={:#x}, cfd={}, size={}, addr={:08x})", uid, cfd, size, + addr); + return ReadContent(cfd, Memory::GetPointer(addr), size, uid, ticks); + }); } -ReturnCode ESDevice::CloseContent(u32 cfd, u32 uid) +s32 ESDevice::CloseContent(u32 cfd, u32 uid, Ticks ticks) { if (cfd >= m_content_table.size()) return ES_EINVAL; @@ -126,7 +131,7 @@ ReturnCode ESDevice::CloseContent(u32 cfd, u32 uid) if (!entry.m_opened) return IPC_EINVAL; - m_ios.GetFS()->Close(entry.m_fd); + m_ios.GetFSDevice()->Close(entry.m_fd, ticks); entry = {}; INFO_LOG_FMT(IOS_ES, "CloseContent: CFD {}", cfd); return IPC_SUCCESS; @@ -134,14 +139,16 @@ ReturnCode ESDevice::CloseContent(u32 cfd, u32 uid) IPCReply ESDevice::CloseContent(u32 uid, const IOCtlVRequest& request) { - if (!request.HasNumberOfValidVectors(1, 0) || request.in_vectors[0].size != sizeof(u32)) - return IPCReply(ES_EINVAL); + return MakeIPCReply(IPC_OVERHEAD_TICKS, [&](Ticks ticks) -> s32 { + if (!request.HasNumberOfValidVectors(1, 0) || request.in_vectors[0].size != sizeof(u32)) + return ES_EINVAL; - const u32 cfd = Memory::Read_U32(request.in_vectors[0].address); - return IPCReply(CloseContent(cfd, uid)); + const u32 cfd = Memory::Read_U32(request.in_vectors[0].address); + return CloseContent(cfd, uid, ticks); + }); } -s32 ESDevice::SeekContent(u32 cfd, u32 offset, SeekMode mode, u32 uid) +s32 ESDevice::SeekContent(u32 cfd, u32 offset, SeekMode mode, u32 uid, Ticks ticks) { if (cfd >= m_content_table.size()) return ES_EINVAL; @@ -152,19 +159,20 @@ s32 ESDevice::SeekContent(u32 cfd, u32 offset, SeekMode mode, u32 uid) if (!entry.m_opened) return IPC_EINVAL; - const auto result = m_ios.GetFS()->SeekFile(entry.m_fd, offset, static_cast(mode)); - return result.Succeeded() ? *result : FS::ConvertResult(result.Error()); + return m_ios.GetFSDevice()->Seek(entry.m_fd, offset, static_cast(mode), ticks); } IPCReply ESDevice::SeekContent(u32 uid, const IOCtlVRequest& request) { - if (!request.HasNumberOfValidVectors(3, 0)) - return IPCReply(ES_EINVAL); + return MakeIPCReply(IPC_OVERHEAD_TICKS, [&](Ticks ticks) -> s32 { + if (!request.HasNumberOfValidVectors(3, 0)) + return ES_EINVAL; - const u32 cfd = Memory::Read_U32(request.in_vectors[0].address); - const u32 offset = Memory::Read_U32(request.in_vectors[1].address); - const SeekMode mode = static_cast(Memory::Read_U32(request.in_vectors[2].address)); + const u32 cfd = Memory::Read_U32(request.in_vectors[0].address); + const u32 offset = Memory::Read_U32(request.in_vectors[1].address); + const auto mode = static_cast(Memory::Read_U32(request.in_vectors[2].address)); - return IPCReply(SeekContent(cfd, offset, mode, uid)); + return SeekContent(cfd, offset, mode, uid, ticks); + }); } } // namespace IOS::HLE diff --git a/Source/Core/Core/IOS/ES/TitleManagement.cpp b/Source/Core/Core/IOS/ES/TitleManagement.cpp index df9c6ec9c2..cf63efd8f6 100644 --- a/Source/Core/Core/IOS/ES/TitleManagement.cpp +++ b/Source/Core/Core/IOS/ES/TitleManagement.cpp @@ -793,7 +793,7 @@ ReturnCode ESDevice::ExportContentEnd(Context& context, u32 content_fd) { if (!context.title_import_export.valid || !context.title_import_export.content.valid) return ES_EINVAL; - return CloseContent(content_fd, 0); + return static_cast(CloseContent(content_fd, 0)); } IPCReply ESDevice::ExportContentEnd(Context& context, const IOCtlVRequest& request) From e3bf5fca93cc4a9f2d793e286453df40b040dc81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Thu, 18 Feb 2021 21:06:41 +0100 Subject: [PATCH 6/6] IOS: Deduplicate IPC_OVERHEAD_TICKS timing constant --- Source/Core/Core/IOS/DI/DI.cpp | 2 +- Source/Core/Core/IOS/ES/ES.h | 2 -- Source/Core/Core/IOS/FS/FileSystemProxy.cpp | 21 +++++++-------------- Source/Core/Core/IOS/IOS.h | 2 ++ 4 files changed, 10 insertions(+), 17 deletions(-) diff --git a/Source/Core/Core/IOS/DI/DI.cpp b/Source/Core/Core/IOS/DI/DI.cpp index 6810943699..4c15a71007 100644 --- a/Source/Core/Core/IOS/DI/DI.cpp +++ b/Source/Core/Core/IOS/DI/DI.cpp @@ -109,7 +109,7 @@ void DIDevice::ProcessQueuedIOCtl() auto finished = StartIOCtl(request); if (finished) { - CoreTiming::ScheduleEvent(2700 * SystemTimers::TIMER_RATIO, s_finish_executing_di_command, + CoreTiming::ScheduleEvent(IPC_OVERHEAD_TICKS, s_finish_executing_di_command, static_cast(finished.value())); return; } diff --git a/Source/Core/Core/IOS/ES/ES.h b/Source/Core/Core/IOS/ES/ES.h index 10af9fbd02..989474749d 100644 --- a/Source/Core/Core/IOS/ES/ES.h +++ b/Source/Core/Core/IOS/ES/ES.h @@ -366,8 +366,6 @@ private: std::string GetContentPath(u64 title_id, const ES::Content& content, Ticks ticks = {}) const; - static constexpr u64 IPC_OVERHEAD_TICKS = 2700_tbticks; - struct OpenedContent { bool m_opened = false; diff --git a/Source/Core/Core/IOS/FS/FileSystemProxy.cpp b/Source/Core/Core/IOS/FS/FileSystemProxy.cpp index 7bd33e44dd..47197164d2 100644 --- a/Source/Core/Core/IOS/FS/FileSystemProxy.cpp +++ b/Source/Core/Core/IOS/FS/FileSystemProxy.cpp @@ -22,16 +22,9 @@ namespace IOS::HLE { using namespace IOS::HLE::FS; -static constexpr u64 GetIPCOverheadTicks() -{ - // According to hardware tests, FS takes at least 2700 TB ticks to reply to commands. - return 2700; -} - static IPCReply GetFSReply(s32 return_value, u64 extra_tb_ticks = 0) { - return IPCReply{return_value, - (GetIPCOverheadTicks() + extra_tb_ticks) * SystemTimers::TIMER_RATIO}; + return IPCReply{return_value, IPC_OVERHEAD_TICKS + extra_tb_ticks * SystemTimers::TIMER_RATIO}; } /// Duration of a superblock write (in timebase ticks). @@ -169,7 +162,7 @@ std::optional FSDevice::Open(const OpenRequest& request) s64 FSDevice::Open(FS::Uid uid, FS::Gid gid, const std::string& path, FS::Mode mode, std::optional ipc_fd, Ticks ticks) { - ticks.AddTimeBaseTicks(GetIPCOverheadTicks()); + ticks.AddTimeBaseTicks(IPC_OVERHEAD_TICKS); if (m_fd_map.size() >= 16) return ConvertResult(ResultCode::NoFreeHandle); @@ -204,7 +197,7 @@ std::optional FSDevice::Close(u32 fd) s32 FSDevice::Close(u64 fd, Ticks ticks) { - ticks.AddTimeBaseTicks(GetIPCOverheadTicks()); + ticks.AddTimeBaseTicks(IPC_OVERHEAD_TICKS); const auto& handle = m_fd_map[fd]; if (handle.fs_fd != INVALID_FD) @@ -318,7 +311,7 @@ std::optional FSDevice::Read(const ReadWriteRequest& request) s32 FSDevice::Read(u64 fd, u8* data, u32 size, std::optional ipc_buffer_addr, Ticks ticks) { - ticks.AddTimeBaseTicks(GetIPCOverheadTicks()); + ticks.AddTimeBaseTicks(IPC_OVERHEAD_TICKS); const Handle& handle = m_fd_map[fd]; if (handle.fs_fd == INVALID_FD) @@ -347,7 +340,7 @@ std::optional FSDevice::Write(const ReadWriteRequest& request) s32 FSDevice::Write(u64 fd, const u8* data, u32 size, std::optional ipc_buffer_addr, Ticks ticks) { - ticks.AddTimeBaseTicks(GetIPCOverheadTicks()); + ticks.AddTimeBaseTicks(IPC_OVERHEAD_TICKS); const Handle& handle = m_fd_map[fd]; if (handle.fs_fd == INVALID_FD) @@ -375,7 +368,7 @@ std::optional FSDevice::Seek(const SeekRequest& request) s32 FSDevice::Seek(u64 fd, u32 offset, FS::SeekMode mode, Ticks ticks) { - ticks.AddTimeBaseTicks(GetIPCOverheadTicks()); + ticks.AddTimeBaseTicks(IPC_OVERHEAD_TICKS); const Handle& handle = m_fd_map[fd]; if (handle.fs_fd == INVALID_FD) @@ -679,7 +672,7 @@ IPCReply FSDevice::GetFileStats(const Handle& handle, const IOCtlRequest& reques FS::Result FSDevice::GetFileStatus(u64 fd, Ticks ticks) { - ticks.AddTimeBaseTicks(GetIPCOverheadTicks()); + ticks.AddTimeBaseTicks(IPC_OVERHEAD_TICKS); const auto& handle = m_fd_map[fd]; if (handle.fs_fd == INVALID_FD) return ResultCode::Invalid; diff --git a/Source/Core/Core/IOS/IOS.h b/Source/Core/Core/IOS/IOS.h index 11220f1194..fe2336cd47 100644 --- a/Source/Core/Core/IOS/IOS.h +++ b/Source/Core/Core/IOS/IOS.h @@ -46,6 +46,8 @@ struct IPCReply u64 reply_delay_ticks; }; +constexpr u64 IPC_OVERHEAD_TICKS = 2700_tbticks; + // Used to make it more convenient for functions to return timing information // without having to explicitly keep track of ticks in callers. class Ticks