Merge pull request #9511 from leoetlino/es-content-timings
IOS: Emulate FS timings for ES content IPC commands
This commit is contained in:
commit
93f9d67d2f
|
@ -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<u64>(finished.value()));
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<std::array<u8, 20>> 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;
|
||||
|
|
|
@ -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<HLE::FS::FileSystem> fs) : m_fs{fs}
|
||||
SharedContentMap::SharedContentMap(std::shared_ptr<HLE::FSDevice> 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<u32, u64> ReadUidSysEntry(const HLE::FS::FileHandle& file)
|
||||
static std::pair<u32, u64> 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<HLE::FS::FileSystem> fs) : m_fs{fs}
|
||||
UIDSys::UIDSys(std::shared_ptr<HLE::FSDevice> 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<u32, u64> entry = ReadUidSysEntry(*file);
|
||||
std::pair<u32, u64> 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())
|
||||
|
|
|
@ -260,7 +260,7 @@ public:
|
|||
class SharedContentMap final
|
||||
{
|
||||
public:
|
||||
explicit SharedContentMap(std::shared_ptr<HLE::FS::FileSystem> fs);
|
||||
explicit SharedContentMap(std::shared_ptr<HLE::FSDevice> fs);
|
||||
~SharedContentMap();
|
||||
|
||||
std::optional<std::string> GetFilenameFromSHA1(const std::array<u8, 20>& sha1) const;
|
||||
|
@ -268,27 +268,35 @@ public:
|
|||
bool DeleteSharedContent(const std::array<u8, 20>& sha1);
|
||||
std::vector<std::array<u8, 20>> GetHashes() const;
|
||||
|
||||
u64 GetTicks() const { return m_ticks; }
|
||||
|
||||
private:
|
||||
bool WriteEntries() const;
|
||||
|
||||
struct Entry;
|
||||
u32 m_last_id = 0;
|
||||
std::vector<Entry> m_entries;
|
||||
std::shared_ptr<HLE::FSDevice> m_fs_device;
|
||||
std::shared_ptr<HLE::FS::FileSystem> m_fs;
|
||||
u64 m_ticks = 0;
|
||||
};
|
||||
|
||||
class UIDSys final
|
||||
{
|
||||
public:
|
||||
explicit UIDSys(std::shared_ptr<HLE::FS::FileSystem> fs);
|
||||
explicit UIDSys(std::shared_ptr<HLE::FSDevice> 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<HLE::FSDevice> m_fs_device;
|
||||
std::shared_ptr<HLE::FS::FileSystem> m_fs;
|
||||
std::map<u32, u64> m_entries;
|
||||
u64 m_ticks = 0;
|
||||
};
|
||||
|
||||
class CertReader final : public SignedBlobReader
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
#include <array>
|
||||
#include <cctype>
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
@ -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<u8> tmd_bytes(file->GetStatus()->size);
|
||||
if (!file->Read(tmd_bytes.data(), tmd_bytes.size()))
|
||||
std::vector<u8> 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<ES::Content> contents = tmd.GetContents();
|
||||
|
||||
std::vector<ES::Content> 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<std::array<u8, 20>> 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
|
||||
|
|
|
@ -4,18 +4,17 @@
|
|||
|
||||
#include "Core/IOS/ES/ES.h"
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#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<s32>(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<FS::SeekMode>(mode));
|
||||
return result.Succeeded() ? *result : FS::ConvertResult(result.Error());
|
||||
return m_ios.GetFSDevice()->Seek(entry.m_fd, offset, static_cast<FS::SeekMode>(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<SeekMode>(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<SeekMode>(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
|
||||
|
|
|
@ -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<ES::Content> 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<ReturnCode>(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<u8, 20>& 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;
|
||||
|
|
|
@ -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 <typename... Args>
|
||||
|
@ -153,59 +153,78 @@ static IPCReply GetReplyForSuperblockOperation(int ios_version, ResultCode resul
|
|||
|
||||
std::optional<IPCReply> FSDevice::Open(const OpenRequest& request)
|
||||
{
|
||||
return MakeIPCReply([&](Ticks t) {
|
||||
return Open(request.uid, request.gid, request.path, static_cast<Mode>(request.flags & 3),
|
||||
request.fd, t);
|
||||
});
|
||||
}
|
||||
|
||||
s64 FSDevice::Open(FS::Uid uid, FS::Gid gid, const std::string& path, FS::Mode mode,
|
||||
std::optional<u32> 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<Mode>(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<IPCReply> FSDevice::Close(u32 fd)
|
||||
{
|
||||
u64 ticks = 0;
|
||||
if (m_fd_map[fd].fs_fd != INVALID_FD)
|
||||
return MakeIPCReply([&](Ticks t) { return Close(static_cast<u64>(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<FileStatus> 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<u32>(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<u16>(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<IPCReply> 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<u32> 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<u32> 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<u32> 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<IPCReply> 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<u32> 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<u32> 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<u32> 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<IPCReply> 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<u32> 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<u32> 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<FileStatus> 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<FileStatus> 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<FS::FileStatus> 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)
|
||||
|
|
|
@ -6,7 +6,9 @@
|
|||
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#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<u32> ipc_fd = {}, Ticks ticks = {});
|
||||
s32 Close(u64 fd, Ticks ticks = {});
|
||||
s32 Read(u64 fd, u8* data, u32 size, std::optional<u32> ipc_buffer_addr = {}, Ticks ticks = {});
|
||||
s32 Write(u64 fd, const u8* data, u32 size, std::optional<u32> ipc_buffer_addr = {},
|
||||
Ticks ticks = {});
|
||||
s32 Seek(u64 fd, u32 offset, FS::SeekMode mode, Ticks ticks = {});
|
||||
FS::Result<FS::FileStatus> GetFileStatus(u64 fd, Ticks ticks = {});
|
||||
|
||||
template <typename T>
|
||||
s32 Read(u64 fd, T* data, size_t count, Ticks ticks = {})
|
||||
{
|
||||
return Read(fd, reinterpret_cast<u8*>(data), static_cast<u32>(sizeof(T) * count), {}, ticks);
|
||||
}
|
||||
|
||||
std::shared_ptr<FS::FileSystem> GetFS() const { return m_ios.GetFS(); }
|
||||
|
||||
void DoState(PointerWrap& p) override;
|
||||
|
||||
std::optional<IPCReply> 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<u32, Handle> 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<u64> m_cache_fd;
|
||||
// The first 0x18 IDs are reserved for the PPC.
|
||||
u64 m_next_fd = 0x18;
|
||||
std::map<u64, Handle> m_fd_map;
|
||||
};
|
||||
} // namespace IOS::HLE
|
||||
|
|
|
@ -285,6 +285,11 @@ std::shared_ptr<FS::FileSystem> Kernel::GetFS()
|
|||
return m_fs;
|
||||
}
|
||||
|
||||
std::shared_ptr<FSDevice> Kernel::GetFSDevice()
|
||||
{
|
||||
return std::static_pointer_cast<FSDevice>(m_device_map.at("/dev/fs"));
|
||||
}
|
||||
|
||||
std::shared_ptr<ESDevice> Kernel::GetES()
|
||||
{
|
||||
return std::static_pointer_cast<ESDevice>(m_device_map.at("/dev/es"));
|
||||
|
|
|
@ -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 <typename ResultProducer>
|
||||
IPCReply MakeIPCReply(u64 ticks, const ResultProducer& fn)
|
||||
{
|
||||
const s32 result_value = fn(Ticks{&ticks});
|
||||
return IPCReply{result_value, ticks};
|
||||
}
|
||||
|
||||
template <typename ResultProducer>
|
||||
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<FS::FileSystem> GetFS();
|
||||
std::shared_ptr<FSDevice> GetFSDevice();
|
||||
std::shared_ptr<ESDevice> GetES();
|
||||
|
||||
void SDIO_EventNotify();
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue