Merge pull request #5707 from leoetlino/content-table

IOS/ES: Fix content table handling
This commit is contained in:
Leo Lam 2017-06-28 10:42:59 +02:00 committed by GitHub
commit 7454a20f4f
4 changed files with 125 additions and 143 deletions

View File

@ -339,34 +339,11 @@ void ES::DoState(PointerWrap& p)
{ {
Device::DoState(p); Device::DoState(p);
p.Do(s_content_file); p.Do(s_content_file);
p.Do(m_AccessIdentID); p.Do(m_content_table);
s_title_context.DoState(p); s_title_context.DoState(p);
for (auto& context : m_contexts) for (auto& context : m_contexts)
context.DoState(p); context.DoState(p);
u32 Count = (u32)(m_ContentAccessMap.size());
p.Do(Count);
if (p.GetMode() == PointerWrap::MODE_READ)
{
for (u32 i = 0; i < Count; i++)
{
u32 cfd = 0;
OpenedContent content;
p.Do(cfd);
p.Do(content);
cfd = OpenTitleContent(cfd, content.m_title_id, content.m_content.index);
}
}
else
{
for (const auto& pair : m_ContentAccessMap)
{
p.Do(pair.first);
p.Do(pair.second);
}
}
} }
ES::ContextArray::iterator ES::FindActiveContext(s32 fd) ES::ContextArray::iterator ES::FindActiveContext(s32 fd)
@ -403,10 +380,6 @@ ReturnCode ES::Close(u32 fd)
context->active = false; context->active = false;
context->ipc_fd = -1; context->ipc_fd = -1;
// FIXME: IOS doesn't clear the content access map here.
m_ContentAccessMap.clear();
m_AccessIdentID = 0;
INFO_LOG(IOS_ES, "ES: Close"); INFO_LOG(IOS_ES, "ES: Close");
m_is_active = false; m_is_active = false;
// clear the NAND content cache to make sure nothing remains open. // clear the NAND content cache to make sure nothing remains open.
@ -441,16 +414,18 @@ IPCCommandResult ES::IOCtlV(const IOCtlVRequest& request)
return ImportTitleCancel(*context, request); return ImportTitleCancel(*context, request);
case IOCTL_ES_GETDEVICEID: case IOCTL_ES_GETDEVICEID:
return GetDeviceId(request); return GetDeviceId(request);
case IOCTL_ES_OPENTITLECONTENT:
return OpenTitleContent(context->uid, request);
case IOCTL_ES_OPENCONTENT: case IOCTL_ES_OPENCONTENT:
return OpenContent(context->uid, request); return OpenContent(context->uid, request);
case IOCTL_ES_OPEN_ACTIVE_TITLE_CONTENT:
return OpenActiveTitleContent(context->uid, request);
case IOCTL_ES_READCONTENT: case IOCTL_ES_READCONTENT:
return ReadContent(context->uid, request); return ReadContent(context->uid, request);
case IOCTL_ES_CLOSECONTENT: case IOCTL_ES_CLOSECONTENT:
return CloseContent(context->uid, request); return CloseContent(context->uid, request);
case IOCTL_ES_SEEKCONTENT: case IOCTL_ES_SEEKCONTENT:
return SeekContent(context->uid, request); return SeekContent(context->uid, request);
case IOCTL_ES_GETTITLEDIR: case IOCTL_ES_GETTITLEDIR:
return GetTitleDirectory(request); return GetTitleDirectory(request);
case IOCTL_ES_GETTITLEID: case IOCTL_ES_GETTITLEID:

View File

@ -57,9 +57,11 @@ public:
struct OpenedContent struct OpenedContent
{ {
u64 m_title_id; bool m_opened = false;
u64 m_title_id = 0;
IOS::ES::Content m_content; IOS::ES::Content m_content;
u32 m_position; u32 m_position = 0;
u32 m_uid = 0;
}; };
struct TitleImportContext struct TitleImportContext
@ -152,7 +154,7 @@ private:
IOCTL_ES_ADDTITLEFINISH = 0x06, IOCTL_ES_ADDTITLEFINISH = 0x06,
IOCTL_ES_GETDEVICEID = 0x07, IOCTL_ES_GETDEVICEID = 0x07,
IOCTL_ES_LAUNCH = 0x08, IOCTL_ES_LAUNCH = 0x08,
IOCTL_ES_OPENCONTENT = 0x09, IOCTL_ES_OPEN_ACTIVE_TITLE_CONTENT = 0x09,
IOCTL_ES_READCONTENT = 0x0A, IOCTL_ES_READCONTENT = 0x0A,
IOCTL_ES_CLOSECONTENT = 0x0B, IOCTL_ES_CLOSECONTENT = 0x0B,
IOCTL_ES_GETOWNEDTITLECNT = 0x0C, IOCTL_ES_GETOWNEDTITLECNT = 0x0C,
@ -179,7 +181,7 @@ private:
IOCTL_ES_SETUID = 0x21, IOCTL_ES_SETUID = 0x21,
IOCTL_ES_DELETETITLECONTENT = 0x22, IOCTL_ES_DELETETITLECONTENT = 0x22,
IOCTL_ES_SEEKCONTENT = 0x23, IOCTL_ES_SEEKCONTENT = 0x23,
IOCTL_ES_OPENTITLECONTENT = 0x24, IOCTL_ES_OPENCONTENT = 0x24,
IOCTL_ES_LAUNCHBC = 0x25, IOCTL_ES_LAUNCHBC = 0x25,
IOCTL_ES_EXPORTTITLEINIT = 0x26, IOCTL_ES_EXPORTTITLEINIT = 0x26,
IOCTL_ES_EXPORTCONTENTBEGIN = 0x27, IOCTL_ES_EXPORTCONTENTBEGIN = 0x27,
@ -258,7 +260,7 @@ private:
IPCCommandResult DeleteStreamKey(const IOCtlVRequest& request); IPCCommandResult DeleteStreamKey(const IOCtlVRequest& request);
// Title contents // Title contents
IPCCommandResult OpenTitleContent(u32 uid, const IOCtlVRequest& request); IPCCommandResult OpenActiveTitleContent(u32 uid, const IOCtlVRequest& request);
IPCCommandResult OpenContent(u32 uid, const IOCtlVRequest& request); IPCCommandResult OpenContent(u32 uid, const IOCtlVRequest& request);
IPCCommandResult ReadContent(u32 uid, const IOCtlVRequest& request); IPCCommandResult ReadContent(u32 uid, const IOCtlVRequest& request);
IPCCommandResult CloseContent(u32 uid, const IOCtlVRequest& request); IPCCommandResult CloseContent(u32 uid, const IOCtlVRequest& request);
@ -340,12 +342,10 @@ private:
static const DiscIO::NANDContentLoader& AccessContentDevice(u64 title_id); static const DiscIO::NANDContentLoader& AccessContentDevice(u64 title_id);
u32 OpenTitleContent(u32 CFD, u64 TitleID, u16 Index); s32 OpenContent(const IOS::ES::TMDReader& tmd, u16 content_index, u32 uid);
using ContentAccessMap = std::map<u32, OpenedContent>; using ContentTable = std::array<OpenedContent, 16>;
ContentAccessMap m_ContentAccessMap; ContentTable m_content_table;
u32 m_AccessIdentID = 0;
ContextArray m_contexts; ContextArray m_contexts;
}; };

View File

@ -20,103 +20,114 @@ namespace HLE
{ {
namespace Device namespace Device
{ {
u32 ES::OpenTitleContent(u32 CFD, u64 TitleID, u16 Index) s32 ES::OpenContent(const IOS::ES::TMDReader& tmd, u16 content_index, u32 uid)
{ {
const DiscIO::NANDContentLoader& Loader = AccessContentDevice(TitleID); const u64 title_id = tmd.GetTitleId();
const DiscIO::NANDContentLoader& loader = AccessContentDevice(title_id);
if (!Loader.IsValid() || !Loader.GetTMD().IsValid() || !Loader.GetTicket().IsValid()) if (!loader.IsValid())
return FS_ENOENT;
const DiscIO::NANDContent* content = loader.GetContentByIndex(content_index);
if (!content)
return FS_ENOENT;
for (size_t i = 0; i < m_content_table.size(); ++i)
{ {
WARN_LOG(IOS_ES, "ES: loader not valid for %" PRIx64, TitleID); OpenedContent& entry = m_content_table[i];
return 0xffffffff; if (entry.m_opened)
continue;
entry.m_opened = true;
entry.m_position = 0;
entry.m_content = content->m_metadata;
entry.m_title_id = title_id;
entry.m_uid = uid;
INFO_LOG(IOS_ES, "OpenContent: title ID %016" PRIx64 ", UID 0x%x, CFD %zu", title_id, uid, i);
return static_cast<s32>(i);
} }
const DiscIO::NANDContent* pContent = Loader.GetContentByIndex(Index); return FS_EFDEXHAUSTED;
if (pContent == nullptr)
{
return 0xffffffff; // TODO: what is the correct error value here?
}
OpenedContent content;
content.m_position = 0;
content.m_content = pContent->m_metadata;
content.m_title_id = TitleID;
pContent->m_Data->Open();
m_ContentAccessMap[CFD] = content;
return CFD;
}
IPCCommandResult ES::OpenTitleContent(u32 uid, const IOCtlVRequest& request)
{
if (!request.HasNumberOfValidVectors(3, 0))
return GetDefaultReply(ES_EINVAL);
u64 TitleID = Memory::Read_U64(request.in_vectors[0].address);
u32 Index = Memory::Read_U32(request.in_vectors[2].address);
s32 CFD = OpenTitleContent(m_AccessIdentID++, TitleID, Index);
INFO_LOG(IOS_ES, "IOCTL_ES_OPENTITLECONTENT: TitleID: %016" PRIx64 " Index %i -> got CFD %x",
TitleID, Index, CFD);
return GetDefaultReply(CFD);
} }
IPCCommandResult ES::OpenContent(u32 uid, const IOCtlVRequest& request) IPCCommandResult ES::OpenContent(u32 uid, const IOCtlVRequest& request)
{ {
if (!request.HasNumberOfValidVectors(1, 0)) if (!request.HasNumberOfValidVectors(3, 0) || request.in_vectors[0].size != sizeof(u64) ||
request.in_vectors[1].size != sizeof(IOS::ES::TicketView) ||
request.in_vectors[2].size != sizeof(u32))
{
return GetDefaultReply(ES_EINVAL); return GetDefaultReply(ES_EINVAL);
u32 Index = Memory::Read_U32(request.in_vectors[0].address); }
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 GetDefaultReply(FS_ENOENT);
return GetDefaultReply(OpenContent(tmd, content_index, uid));
}
IPCCommandResult ES::OpenActiveTitleContent(u32 caller_uid, const IOCtlVRequest& request)
{
if (!request.HasNumberOfValidVectors(1, 0) || request.in_vectors[0].size != sizeof(u32))
return GetDefaultReply(ES_EINVAL);
const u32 content_index = Memory::Read_U32(request.in_vectors[0].address);
if (!GetTitleContext().active) if (!GetTitleContext().active)
return GetDefaultReply(ES_EINVAL); return GetDefaultReply(ES_EINVAL);
s32 CFD = OpenTitleContent(m_AccessIdentID++, GetTitleContext().tmd.GetTitleId(), Index); IOS::ES::UIDSys uid_map{Common::FROM_SESSION_ROOT};
INFO_LOG(IOS_ES, "IOCTL_ES_OPENCONTENT: Index %i -> got CFD %x", Index, CFD); const u32 uid = uid_map.GetOrInsertUIDForTitle(GetTitleContext().tmd.GetTitleId());
if (caller_uid != 0 && caller_uid != uid)
return GetDefaultReply(ES_EACCES);
return GetDefaultReply(CFD); return GetDefaultReply(OpenContent(GetTitleContext().tmd, content_index, caller_uid));
} }
IPCCommandResult ES::ReadContent(u32 uid, const IOCtlVRequest& request) IPCCommandResult ES::ReadContent(u32 uid, const IOCtlVRequest& request)
{ {
if (!request.HasNumberOfValidVectors(1, 1)) if (!request.HasNumberOfValidVectors(1, 1) || request.in_vectors[0].size != sizeof(u32))
return GetDefaultReply(ES_EINVAL); return GetDefaultReply(ES_EINVAL);
u32 CFD = Memory::Read_U32(request.in_vectors[0].address); const u32 cfd = Memory::Read_U32(request.in_vectors[0].address);
u32 Size = request.io_vectors[0].size; u32 size = request.io_vectors[0].size;
u32 Addr = request.io_vectors[0].address; const u32 addr = request.io_vectors[0].address;
auto itr = m_ContentAccessMap.find(CFD); if (cfd >= m_content_table.size())
if (itr == m_ContentAccessMap.end()) return GetDefaultReply(ES_EINVAL);
OpenedContent& entry = m_content_table[cfd];
if (entry.m_uid != uid)
return GetDefaultReply(ES_EACCES);
if (!entry.m_opened)
return GetDefaultReply(IPC_EINVAL);
// XXX: make this reuse the FS code... ES just does a simple "IOS_Read" call here
// instead of all this duplicated filesystem logic.
if (entry.m_position + size > entry.m_content.size)
size = static_cast<u32>(entry.m_content.size) - entry.m_position;
if (size > 0)
{ {
return GetDefaultReply(-1); if (addr)
}
OpenedContent& rContent = itr->second;
u8* pDest = Memory::GetPointer(Addr);
if (rContent.m_position + Size > rContent.m_content.size)
{
Size = static_cast<u32>(rContent.m_content.size) - rContent.m_position;
}
if (Size > 0)
{
if (pDest)
{ {
const DiscIO::NANDContentLoader& ContentLoader = AccessContentDevice(rContent.m_title_id); const DiscIO::NANDContentLoader& ContentLoader = AccessContentDevice(entry.m_title_id);
// ContentLoader should never be invalid; rContent has been created by it. // ContentLoader should never be invalid; rContent has been created by it.
if (ContentLoader.IsValid() && ContentLoader.GetTicket().IsValid()) if (ContentLoader.IsValid() && ContentLoader.GetTicket().IsValid())
{ {
const DiscIO::NANDContent* pContent = const DiscIO::NANDContent* pContent =
ContentLoader.GetContentByIndex(rContent.m_content.index); ContentLoader.GetContentByIndex(entry.m_content.index);
if (!pContent->m_Data->GetRange(rContent.m_position, Size, pDest)) pContent->m_Data->Open();
ERROR_LOG(IOS_ES, "ES: failed to read %u bytes from %u!", Size, rContent.m_position); if (!pContent->m_Data->GetRange(entry.m_position, size, Memory::GetPointer(addr)))
ERROR_LOG(IOS_ES, "ES: failed to read %u bytes from %u!", size, entry.m_position);
} }
rContent.m_position += Size; entry.m_position += size;
} }
else else
{ {
@ -124,39 +135,35 @@ IPCCommandResult ES::ReadContent(u32 uid, const IOCtlVRequest& request)
} }
} }
DEBUG_LOG(IOS_ES, return GetDefaultReply(size);
"IOCTL_ES_READCONTENT: CFD %x, Address 0x%x, Size %i -> stream pos %i (Index %i)", CFD,
Addr, Size, rContent.m_position, rContent.m_content.index);
return GetDefaultReply(Size);
} }
IPCCommandResult ES::CloseContent(u32 uid, const IOCtlVRequest& request) IPCCommandResult ES::CloseContent(u32 uid, const IOCtlVRequest& request)
{ {
if (!request.HasNumberOfValidVectors(1, 0)) if (!request.HasNumberOfValidVectors(1, 0) || request.in_vectors[0].size != sizeof(u32))
return GetDefaultReply(ES_EINVAL); return GetDefaultReply(ES_EINVAL);
u32 CFD = Memory::Read_U32(request.in_vectors[0].address); const u32 cfd = Memory::Read_U32(request.in_vectors[0].address);
if (cfd >= m_content_table.size())
return GetDefaultReply(ES_EINVAL);
INFO_LOG(IOS_ES, "IOCTL_ES_CLOSECONTENT: CFD %x", CFD); OpenedContent& entry = m_content_table[cfd];
if (entry.m_uid != uid)
return GetDefaultReply(ES_EACCES);
if (!entry.m_opened)
return GetDefaultReply(IPC_EINVAL);
auto itr = m_ContentAccessMap.find(CFD); // XXX: again, this should be a simple IOS_Close.
if (itr == m_ContentAccessMap.end()) const DiscIO::NANDContentLoader& ContentLoader = AccessContentDevice(entry.m_title_id);
{
return GetDefaultReply(-1);
}
const DiscIO::NANDContentLoader& ContentLoader = AccessContentDevice(itr->second.m_title_id);
// ContentLoader should never be invalid; we shouldn't be here if ES_OPENCONTENT failed before. // ContentLoader should never be invalid; we shouldn't be here if ES_OPENCONTENT failed before.
if (ContentLoader.IsValid()) if (ContentLoader.IsValid())
{ {
const DiscIO::NANDContent* pContent = const DiscIO::NANDContent* content = ContentLoader.GetContentByIndex(entry.m_content.index);
ContentLoader.GetContentByIndex(itr->second.m_content.index); content->m_Data->Close();
pContent->m_Data->Close();
} }
m_ContentAccessMap.erase(itr); entry = {};
INFO_LOG(IOS_ES, "CloseContent: CFD %u", cfd);
return GetDefaultReply(IPC_SUCCESS); return GetDefaultReply(IPC_SUCCESS);
} }
@ -165,36 +172,36 @@ IPCCommandResult ES::SeekContent(u32 uid, const IOCtlVRequest& request)
if (!request.HasNumberOfValidVectors(3, 0)) if (!request.HasNumberOfValidVectors(3, 0))
return GetDefaultReply(ES_EINVAL); return GetDefaultReply(ES_EINVAL);
u32 CFD = Memory::Read_U32(request.in_vectors[0].address); const u32 cfd = Memory::Read_U32(request.in_vectors[0].address);
if (cfd >= m_content_table.size())
return GetDefaultReply(ES_EINVAL);
OpenedContent& entry = m_content_table[cfd];
if (entry.m_uid != uid)
return GetDefaultReply(ES_EACCES);
if (!entry.m_opened)
return GetDefaultReply(IPC_EINVAL);
u32 Addr = Memory::Read_U32(request.in_vectors[1].address); u32 Addr = Memory::Read_U32(request.in_vectors[1].address);
u32 Mode = Memory::Read_U32(request.in_vectors[2].address); u32 Mode = Memory::Read_U32(request.in_vectors[2].address);
auto itr = m_ContentAccessMap.find(CFD); // XXX: This should be a simple IOS_Seek.
if (itr == m_ContentAccessMap.end())
{
return GetDefaultReply(-1);
}
OpenedContent& rContent = itr->second;
switch (Mode) switch (Mode)
{ {
case 0: // SET case 0: // SET
rContent.m_position = Addr; entry.m_position = Addr;
break; break;
case 1: // CUR case 1: // CUR
rContent.m_position += Addr; entry.m_position += Addr;
break; break;
case 2: // END case 2: // END
rContent.m_position = static_cast<u32>(rContent.m_content.size) + Addr; entry.m_position = static_cast<u32>(entry.m_content.size) + Addr;
break; break;
} }
DEBUG_LOG(IOS_ES, "IOCTL_ES_SEEKCONTENT: CFD %x, Address 0x%x, Mode %i -> Pos %i", CFD, Addr, return GetDefaultReply(entry.m_position);
Mode, rContent.m_position);
return GetDefaultReply(rContent.m_position);
} }
} // namespace Device } // namespace Device
} // namespace HLE } // namespace HLE

View File

@ -73,7 +73,7 @@ static Common::Event g_compressAndDumpStateSyncEvent;
static std::thread g_save_thread; static std::thread g_save_thread;
// Don't forget to increase this after doing changes on the savestate system // Don't forget to increase this after doing changes on the savestate system
static const u32 STATE_VERSION = 86; // Last changed in PR 2353 static const u32 STATE_VERSION = 87; // Last changed in PR 5707
// Maps savestate versions to Dolphin versions. // Maps savestate versions to Dolphin versions.
// Versions after 42 don't need to be added to this list, // Versions after 42 don't need to be added to this list,