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.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..989474749d 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. @@ -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 @@ -364,14 +364,12 @@ 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; 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/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 0cab7b7c91..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,17 +28,19 @@ 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; - 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); } @@ -48,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.GetFS()}; - 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; @@ -95,23 +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; - 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; @@ -122,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; @@ -130,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; @@ -148,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 09b543f1db..cf63efd8f6 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; @@ -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) @@ -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 bb4e76a341..47197164d2 100644 --- a/Source/Core/Core/IOS/FS/FileSystemProxy.cpp +++ b/Source/Core/Core/IOS/FS/FileSystemProxy.cpp @@ -24,8 +24,7 @@ using namespace IOS::HLE::FS; static IPCReply GetFSReply(s32 return_value, u64 extra_tb_ticks = 0) { - // 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 IPCReply{return_value, IPC_OVERHEAD_TICKS + extra_tb_ticks * SystemTimers::TIMER_RATIO}; } /// Duration of a superblock write (in timebase ticks). @@ -95,10 +94,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 +153,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(IPC_OVERHEAD_TICKS); + 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(IPC_OVERHEAD_TICKS); + + 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 +240,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 +264,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 +296,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 +304,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(IPC_OVERHEAD_TICKS); + + 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(IPC_OVERHEAD_TICKS); + + 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(IPC_OVERHEAD_TICKS); + + 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) @@ -605,19 +654,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(IPC_OVERHEAD_TICKS); + 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 e2967eb5dc..101dbdffc2 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,25 @@ 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 = {}); + 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; std::optional Open(const OpenRequest& request) override; @@ -76,14 +97,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.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 ba823894b9..fe2336cd47 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; @@ -45,6 +46,40 @@ 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 +{ +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, @@ -84,6 +119,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(); 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,