IOS/ES: Handle contexts properly

This changes the IOS code to handle ES contexts inside of ES, instead
of leaking out implementation details into the IPC request dispatcher.

The intent is to clarify what's shared between every single ES context,
and what is specific to an ES context. (Not much.) This should reduce
the number of static members in the ES class.

The other changes are there just because we now keep track of the
IPC FD inside of ES.

Future plans:

* After the WAD direct launch hack is dropped, the title context
  will be made a class member.

* Have proper function prototypes, instead of having every single one
  of them take ioctlv requests. This will allow reusing IOS code in
  other parts of the Dolphin codebase without having to construct
  ioctlv requests.
This commit is contained in:
Léo Lam 2017-04-27 21:22:04 +02:00
parent f2035384e5
commit c4136d0365
23 changed files with 241 additions and 222 deletions

View File

@ -154,9 +154,10 @@ ReturnCode Device::Open(const OpenRequest& request)
return IPC_SUCCESS;
}
void Device::Close()
ReturnCode Device::Close(u32 fd)
{
m_is_active = false;
return IPC_SUCCESS;
}
IPCCommandResult Device::Unsupported(const Request& request)

View File

@ -185,7 +185,7 @@ public:
// Replies to Open and Close requests are sent by the IPC request handler (HandleCommand),
// not by the devices themselves.
virtual ReturnCode Open(const OpenRequest& request);
virtual void Close();
virtual ReturnCode Close(u32 fd);
virtual IPCCommandResult Seek(const SeekRequest& seek) { return Unsupported(seek); }
virtual IPCCommandResult Read(const ReadWriteRequest& read) { return Unsupported(read); }
virtual IPCCommandResult Write(const ReadWriteRequest& write) { return Unsupported(write); }

View File

@ -22,12 +22,6 @@ ReturnCode Stub::Open(const OpenRequest& request)
return IPC_SUCCESS;
}
void Stub::Close()
{
WARN_LOG(IOS, "%s faking Close()", m_name.c_str());
m_is_active = false;
}
IPCCommandResult Stub::IOCtl(const IOCtlRequest& request)
{
WARN_LOG(IOS, "%s faking IOCtl()", m_name.c_str());

View File

@ -22,7 +22,6 @@ public:
Stub(u32 device_id, const std::string& device_name);
ReturnCode Open(const OpenRequest& request) override;
void Close() override;
IPCCommandResult IOCtl(const IOCtlRequest& request) override;
IPCCommandResult IOCtlV(const IOCtlVRequest& request) override;
};

View File

@ -4,6 +4,7 @@
#include "Core/IOS/ES/ES.h"
#include <algorithm>
#include <cinttypes>
#include <cstdio>
#include <memory>
@ -27,17 +28,13 @@ namespace HLE
{
namespace Device
{
// Shared across all ES instances.
// TODO: drop this and convert the title context into a member once the WAD launch hack is gone.
static std::string s_content_file;
static TitleContext s_title_context;
// Title to launch after IOS has been reset and reloaded (similar to /sys/launch.sys).
static u64 s_title_to_launch;
ES::ES(u32 device_id, const std::string& device_name) : Device(device_id, device_name)
{
}
static void FinishAllStaleImports()
{
const std::vector<u64> titles = IOS::ES::GetTitleImports();
@ -58,7 +55,7 @@ static void FinishAllStaleImports()
File::CreateDir(import_dir);
}
void ES::Init()
ES::ES(u32 device_id, const std::string& device_name) : Device(device_id, device_name)
{
FinishAllStaleImports();
@ -182,14 +179,14 @@ static ReturnCode CheckIsAllowedToSetUID(const u32 caller_uid)
return caller_uid == system_menu_uid ? IPC_SUCCESS : ES_EINVAL;
}
IPCCommandResult ES::SetUID(const IOCtlVRequest& request)
IPCCommandResult ES::SetUID(u32 uid, const IOCtlVRequest& request)
{
if (!request.HasNumberOfValidVectors(1, 0) || request.in_vectors[0].size != 8)
return GetDefaultReply(ES_EINVAL);
const u64 title_id = Memory::Read_U64(request.in_vectors[0].address);
const s32 ret = CheckIsAllowedToSetUID(m_caller_uid);
const s32 ret = CheckIsAllowedToSetUID(uid);
if (ret < 0)
{
ERROR_LOG(IOS_ES, "SetUID: Permission check failed with error %d", ret);
@ -279,6 +276,24 @@ bool ES::LaunchPPCTitle(u64 title_id, bool skip_reload)
return BootstrapPPC(content_loader);
}
void ES::Context::DoState(PointerWrap& p)
{
p.Do(uid);
p.Do(gid);
title_import.tmd.DoState(p);
p.Do(title_import.content_id);
p.Do(title_import.content_buffer);
p.Do(title_export.valid);
title_export.tmd.DoState(p);
p.Do(title_export.title_key);
p.Do(title_export.contents);
p.Do(active);
p.Do(ipc_fd);
}
void ES::DoState(PointerWrap& p)
{
Device::DoState(p);
@ -286,17 +301,8 @@ void ES::DoState(PointerWrap& p)
p.Do(m_AccessIdentID);
s_title_context.DoState(p);
m_addtitle_tmd.DoState(p);
p.Do(m_addtitle_content_id);
p.Do(m_addtitle_content_buffer);
p.Do(m_caller_uid);
p.Do(m_caller_gid);
p.Do(m_export_title_context.valid);
m_export_title_context.tmd.DoState(p);
p.Do(m_export_title_context.title_key);
p.Do(m_export_title_context.contents);
for (auto& context : m_contexts)
context.DoState(p);
u32 Count = (u32)(m_ContentAccessMap.size());
p.Do(Count);
@ -322,16 +328,41 @@ void ES::DoState(PointerWrap& p)
}
}
ES::ContextArray::iterator ES::FindActiveContext(u32 fd)
{
return std::find_if(m_contexts.begin(), m_contexts.end(),
[fd](const auto& context) { return context.ipc_fd == fd && context.active; });
}
ES::ContextArray::iterator ES::FindInactiveContext()
{
return std::find_if(m_contexts.begin(), m_contexts.end(),
[](const auto& context) { return !context.active; });
}
ReturnCode ES::Open(const OpenRequest& request)
{
m_caller_uid = request.uid;
m_caller_gid = request.gid;
auto context = FindInactiveContext();
if (context == m_contexts.end())
return ES_FD_EXHAUSTED;
context->active = true;
context->uid = request.uid;
context->gid = request.gid;
context->ipc_fd = request.fd;
return Device::Open(request);
}
void ES::Close()
ReturnCode ES::Close(u32 fd)
{
// XXX: does IOS really clear the content access map here?
auto context = FindActiveContext(fd);
if (context == m_contexts.end())
return ES_EINVAL;
context->active = false;
context->ipc_fd = -1;
// FIXME: IOS doesn't clear the content access map here.
m_ContentAccessMap.clear();
m_AccessIdentID = 0;
@ -339,48 +370,52 @@ void ES::Close()
m_is_active = false;
// clear the NAND content cache to make sure nothing remains open.
DiscIO::CNANDContentManager::Access().ClearCache();
return IPC_SUCCESS;
}
IPCCommandResult ES::IOCtlV(const IOCtlVRequest& request)
{
DEBUG_LOG(IOS_ES, "%s (0x%x)", GetDeviceName().c_str(), request.request);
auto context = FindActiveContext(request.fd);
if (context == m_contexts.end())
return GetDefaultReply(ES_EINVAL);
switch (request.request)
{
case IOCTL_ES_ADDTICKET:
return AddTicket(request);
case IOCTL_ES_ADDTMD:
return AddTMD(request);
return AddTMD(*context, request);
case IOCTL_ES_ADDTITLESTART:
return AddTitleStart(request);
return AddTitleStart(*context, request);
case IOCTL_ES_ADDCONTENTSTART:
return AddContentStart(request);
return AddContentStart(*context, request);
case IOCTL_ES_ADDCONTENTDATA:
return AddContentData(request);
return AddContentData(*context, request);
case IOCTL_ES_ADDCONTENTFINISH:
return AddContentFinish(request);
return AddContentFinish(*context, request);
case IOCTL_ES_ADDTITLEFINISH:
return AddTitleFinish(request);
return AddTitleFinish(*context, request);
case IOCTL_ES_ADDTITLECANCEL:
return AddTitleCancel(request);
return AddTitleCancel(*context, request);
case IOCTL_ES_GETDEVICEID:
return GetConsoleID(request);
case IOCTL_ES_OPENTITLECONTENT:
return OpenTitleContent(request);
return OpenTitleContent(context->uid, request);
case IOCTL_ES_OPENCONTENT:
return OpenContent(request);
return OpenContent(context->uid, request);
case IOCTL_ES_READCONTENT:
return ReadContent(request);
return ReadContent(context->uid, request);
case IOCTL_ES_CLOSECONTENT:
return CloseContent(request);
return CloseContent(context->uid, request);
case IOCTL_ES_SEEKCONTENT:
return SeekContent(request);
return SeekContent(context->uid, request);
case IOCTL_ES_GETTITLEDIR:
return GetTitleDirectory(request);
case IOCTL_ES_GETTITLEID:
return GetTitleID(request);
case IOCTL_ES_SETUID:
return SetUID(request);
return SetUID(context->uid, request);
case IOCTL_ES_DIVERIFY:
return DIVerify(request);
@ -441,23 +476,23 @@ IPCCommandResult ES::IOCtlV(const IOCtlVRequest& request)
case IOCTL_ES_GETSTOREDTMD:
return GetStoredTMD(request);
case IOCTL_ES_ENCRYPT:
return Encrypt(request);
return Encrypt(context->uid, request);
case IOCTL_ES_DECRYPT:
return Decrypt(request);
return Decrypt(context->uid, request);
case IOCTL_ES_LAUNCH:
return Launch(request);
case IOCTL_ES_LAUNCHBC:
return LaunchBC(request);
case IOCTL_ES_EXPORTTITLEINIT:
return ExportTitleInit(request);
return ExportTitleInit(*context, request);
case IOCTL_ES_EXPORTCONTENTBEGIN:
return ExportContentBegin(request);
return ExportContentBegin(*context, request);
case IOCTL_ES_EXPORTCONTENTDATA:
return ExportContentData(request);
return ExportContentData(*context, request);
case IOCTL_ES_EXPORTCONTENTEND:
return ExportContentEnd(request);
return ExportContentEnd(*context, request);
case IOCTL_ES_EXPORTTITLEDONE:
return ExportTitleDone(request);
return ExportTitleDone(*context, request);
case IOCTL_ES_CHECKKOREAREGION:
return CheckKoreaRegion(request);
case IOCTL_ES_GETDEVICECERT:

View File

@ -45,9 +45,6 @@ class ES final : public Device
public:
ES(u32 device_id, const std::string& device_name);
// Called after an IOS reload.
static void Init();
static s32 DIVerify(const IOS::ES::TMDReader& tmd, const IOS::ES::TicketReader& ticket);
static void LoadWAD(const std::string& _rContentFile);
static bool LaunchTitle(u64 title_id, bool skip_reload = false);
@ -58,7 +55,7 @@ public:
void DoState(PointerWrap& p) override;
ReturnCode Open(const OpenRequest& request) override;
void Close() override;
ReturnCode Close(u32 fd) override;
IPCCommandResult IOCtlV(const IOCtlVRequest& request) override;
private:
@ -155,20 +152,57 @@ private:
u8 padding[0x3c];
};
struct TitleImportContext
{
IOS::ES::TMDReader tmd;
u32 content_id = 0xFFFFFFFF;
std::vector<u8> content_buffer;
};
// TODO: merge this with TitleImportContext. Also reuse the global content table.
struct TitleExportContext
{
struct ExportContent
{
OpenedContent content;
std::array<u8, 16> iv{};
};
bool valid = false;
IOS::ES::TMDReader tmd;
std::vector<u8> title_key;
std::map<u32, ExportContent> contents;
};
struct Context
{
void DoState(PointerWrap& p);
u16 gid = 0;
u32 uid = 0;
TitleImportContext title_import;
TitleExportContext title_export;
bool active = false;
// We use this to associate an IPC fd with an ES context.
u32 ipc_fd = -1;
};
// ES can only have 3 contexts at one time.
using ContextArray = std::array<Context, 3>;
// Title management
IPCCommandResult AddTicket(const IOCtlVRequest& request);
IPCCommandResult AddTMD(const IOCtlVRequest& request);
IPCCommandResult AddTitleStart(const IOCtlVRequest& request);
IPCCommandResult AddContentStart(const IOCtlVRequest& request);
IPCCommandResult AddContentData(const IOCtlVRequest& request);
IPCCommandResult AddContentFinish(const IOCtlVRequest& request);
IPCCommandResult AddTitleFinish(const IOCtlVRequest& request);
IPCCommandResult AddTitleCancel(const IOCtlVRequest& request);
IPCCommandResult ExportTitleInit(const IOCtlVRequest& request);
IPCCommandResult ExportContentBegin(const IOCtlVRequest& request);
IPCCommandResult ExportContentData(const IOCtlVRequest& request);
IPCCommandResult ExportContentEnd(const IOCtlVRequest& request);
IPCCommandResult ExportTitleDone(const IOCtlVRequest& request);
IPCCommandResult AddTMD(Context& context, const IOCtlVRequest& request);
IPCCommandResult AddTitleStart(Context& context, const IOCtlVRequest& request);
IPCCommandResult AddContentStart(Context& context, const IOCtlVRequest& request);
IPCCommandResult AddContentData(Context& context, const IOCtlVRequest& request);
IPCCommandResult AddContentFinish(Context& context, const IOCtlVRequest& request);
IPCCommandResult AddTitleFinish(Context& context, const IOCtlVRequest& request);
IPCCommandResult AddTitleCancel(Context& context, const IOCtlVRequest& request);
IPCCommandResult ExportTitleInit(Context& context, const IOCtlVRequest& request);
IPCCommandResult ExportContentBegin(Context& context, const IOCtlVRequest& request);
IPCCommandResult ExportContentData(Context& context, const IOCtlVRequest& request);
IPCCommandResult ExportContentEnd(Context& context, const IOCtlVRequest& request);
IPCCommandResult ExportTitleDone(Context& context, const IOCtlVRequest& request);
IPCCommandResult DeleteTitle(const IOCtlVRequest& request);
IPCCommandResult DeleteTicket(const IOCtlVRequest& request);
IPCCommandResult DeleteTitleContent(const IOCtlVRequest& request);
@ -178,11 +212,11 @@ private:
IPCCommandResult GetDeviceCertificate(const IOCtlVRequest& request);
IPCCommandResult CheckKoreaRegion(const IOCtlVRequest& request);
IPCCommandResult Sign(const IOCtlVRequest& request);
IPCCommandResult Encrypt(const IOCtlVRequest& request);
IPCCommandResult Decrypt(const IOCtlVRequest& request);
IPCCommandResult Encrypt(u32 uid, const IOCtlVRequest& request);
IPCCommandResult Decrypt(u32 uid, const IOCtlVRequest& request);
// Misc
IPCCommandResult SetUID(const IOCtlVRequest& request);
IPCCommandResult SetUID(u32 uid, const IOCtlVRequest& request);
IPCCommandResult GetTitleDirectory(const IOCtlVRequest& request);
IPCCommandResult GetTitleID(const IOCtlVRequest& request);
IPCCommandResult GetConsumption(const IOCtlVRequest& request);
@ -191,11 +225,11 @@ private:
IPCCommandResult DIVerify(const IOCtlVRequest& request);
// Title contents
IPCCommandResult OpenTitleContent(const IOCtlVRequest& request);
IPCCommandResult OpenContent(const IOCtlVRequest& request);
IPCCommandResult ReadContent(const IOCtlVRequest& request);
IPCCommandResult CloseContent(const IOCtlVRequest& request);
IPCCommandResult SeekContent(const IOCtlVRequest& request);
IPCCommandResult OpenTitleContent(u32 uid, const IOCtlVRequest& request);
IPCCommandResult OpenContent(u32 uid, const IOCtlVRequest& request);
IPCCommandResult ReadContent(u32 uid, const IOCtlVRequest& request);
IPCCommandResult CloseContent(u32 uid, const IOCtlVRequest& request);
IPCCommandResult SeekContent(u32 uid, const IOCtlVRequest& request);
// Title information
IPCCommandResult GetTitleCount(const std::vector<u64>& titles, const IOCtlVRequest& request);
@ -228,6 +262,9 @@ private:
IPCCommandResult DIGetTMDSize(const IOCtlVRequest& request);
IPCCommandResult DIGetTMD(const IOCtlVRequest& request);
ContextArray::iterator FindActiveContext(u32 fd);
ContextArray::iterator FindInactiveContext();
static bool LaunchIOS(u64 ios_title_id);
static bool LaunchPPCTitle(u64 title_id, bool skip_reload);
static TitleContext& GetTitleContext();
@ -241,27 +278,7 @@ private:
u32 m_AccessIdentID = 0;
// For title installation (ioctls IOCTL_ES_ADDTITLE*).
IOS::ES::TMDReader m_addtitle_tmd;
u32 m_addtitle_content_id = 0xFFFFFFFF;
std::vector<u8> m_addtitle_content_buffer;
u32 m_caller_uid = 0;
u16 m_caller_gid = 0;
struct TitleExportContext
{
struct ExportContent
{
OpenedContent content;
std::array<u8, 16> iv{};
};
bool valid = false;
IOS::ES::TMDReader tmd;
std::vector<u8> title_key;
std::map<u32, ExportContent> contents;
} m_export_title_context;
ContextArray m_contexts;
};
} // namespace Device
} // namespace HLE

View File

@ -63,7 +63,7 @@ IPCCommandResult ES::GetConsoleID(const IOCtlVRequest& request)
return GetDefaultReply(IPC_SUCCESS);
}
IPCCommandResult ES::Encrypt(const IOCtlVRequest& request)
IPCCommandResult ES::Encrypt(u32 uid, const IOCtlVRequest& request)
{
if (!request.HasNumberOfValidVectors(3, 2))
return GetDefaultReply(ES_EINVAL);
@ -85,7 +85,7 @@ IPCCommandResult ES::Encrypt(const IOCtlVRequest& request)
return GetDefaultReply(IPC_SUCCESS);
}
IPCCommandResult ES::Decrypt(const IOCtlVRequest& request)
IPCCommandResult ES::Decrypt(u32 uid, const IOCtlVRequest& request)
{
if (!request.HasNumberOfValidVectors(3, 2))
return GetDefaultReply(ES_EINVAL);

View File

@ -48,7 +48,7 @@ u32 ES::OpenTitleContent(u32 CFD, u64 TitleID, u16 Index)
return CFD;
}
IPCCommandResult ES::OpenTitleContent(const IOCtlVRequest& request)
IPCCommandResult ES::OpenTitleContent(u32 uid, const IOCtlVRequest& request)
{
if (!request.HasNumberOfValidVectors(3, 0))
return GetDefaultReply(ES_EINVAL);
@ -64,7 +64,7 @@ IPCCommandResult ES::OpenTitleContent(const IOCtlVRequest& request)
return GetDefaultReply(CFD);
}
IPCCommandResult ES::OpenContent(const IOCtlVRequest& request)
IPCCommandResult ES::OpenContent(u32 uid, const IOCtlVRequest& request)
{
if (!request.HasNumberOfValidVectors(1, 0))
return GetDefaultReply(ES_EINVAL);
@ -79,7 +79,7 @@ IPCCommandResult ES::OpenContent(const IOCtlVRequest& request)
return GetDefaultReply(CFD);
}
IPCCommandResult ES::ReadContent(const IOCtlVRequest& request)
IPCCommandResult ES::ReadContent(u32 uid, const IOCtlVRequest& request)
{
if (!request.HasNumberOfValidVectors(1, 1))
return GetDefaultReply(ES_EINVAL);
@ -131,7 +131,7 @@ IPCCommandResult ES::ReadContent(const IOCtlVRequest& request)
return GetDefaultReply(Size);
}
IPCCommandResult ES::CloseContent(const IOCtlVRequest& request)
IPCCommandResult ES::CloseContent(u32 uid, const IOCtlVRequest& request)
{
if (!request.HasNumberOfValidVectors(1, 0))
return GetDefaultReply(ES_EINVAL);
@ -160,7 +160,7 @@ IPCCommandResult ES::CloseContent(const IOCtlVRequest& request)
return GetDefaultReply(IPC_SUCCESS);
}
IPCCommandResult ES::SeekContent(const IOCtlVRequest& request)
IPCCommandResult ES::SeekContent(u32 uid, const IOCtlVRequest& request)
{
if (!request.HasNumberOfValidVectors(3, 0))
return GetDefaultReply(ES_EINVAL);

View File

@ -66,7 +66,7 @@ IPCCommandResult ES::AddTicket(const IOCtlVRequest& request)
return GetDefaultReply(IPC_SUCCESS);
}
IPCCommandResult ES::AddTMD(const IOCtlVRequest& request)
IPCCommandResult ES::AddTMD(Context& context, const IOCtlVRequest& request)
{
if (!request.HasNumberOfValidVectors(1, 0))
return GetDefaultReply(ES_EINVAL);
@ -76,18 +76,18 @@ IPCCommandResult ES::AddTMD(const IOCtlVRequest& request)
// Ioctlv 0x2b writes the TMD to /tmp/title.tmd (for imports) and doesn't seem to write it
// to either /import or /title. So here we simply have to set the import TMD.
m_addtitle_tmd.SetBytes(std::move(tmd));
context.title_import.tmd.SetBytes(std::move(tmd));
// TODO: validate TMDs and return the proper error code (-1027) if the signature type is invalid.
if (!m_addtitle_tmd.IsValid())
if (!context.title_import.tmd.IsValid())
return GetDefaultReply(ES_EINVAL);
if (!IOS::ES::InitImport(m_addtitle_tmd.GetTitleId()))
if (!IOS::ES::InitImport(context.title_import.tmd.GetTitleId()))
return GetDefaultReply(FS_EIO);
return GetDefaultReply(IPC_SUCCESS);
}
IPCCommandResult ES::AddTitleStart(const IOCtlVRequest& request)
IPCCommandResult ES::AddTitleStart(Context& context, const IOCtlVRequest& request)
{
if (!request.HasNumberOfValidVectors(4, 0))
return GetDefaultReply(ES_EINVAL);
@ -96,19 +96,20 @@ IPCCommandResult ES::AddTitleStart(const IOCtlVRequest& request)
std::vector<u8> tmd(request.in_vectors[0].size);
Memory::CopyFromEmu(tmd.data(), request.in_vectors[0].address, request.in_vectors[0].size);
m_addtitle_tmd.SetBytes(tmd);
if (!m_addtitle_tmd.IsValid())
context.title_import.tmd.SetBytes(tmd);
if (!context.title_import.tmd.IsValid())
{
ERROR_LOG(IOS_ES, "Invalid TMD while adding title (size = %zd)", tmd.size());
return GetDefaultReply(ES_EINVAL);
}
// Finish a previous import (if it exists).
const IOS::ES::TMDReader previous_tmd = IOS::ES::FindImportTMD(m_addtitle_tmd.GetTitleId());
const IOS::ES::TMDReader previous_tmd =
IOS::ES::FindImportTMD(context.title_import.tmd.GetTitleId());
if (previous_tmd.IsValid())
FinishImport(previous_tmd);
if (!IOS::ES::InitImport(m_addtitle_tmd.GetTitleId()))
if (!IOS::ES::InitImport(context.title_import.tmd.GetTitleId()))
return GetDefaultReply(FS_EIO);
// TODO: check and use the other vectors.
@ -116,7 +117,7 @@ IPCCommandResult ES::AddTitleStart(const IOCtlVRequest& request)
return GetDefaultReply(IPC_SUCCESS);
}
IPCCommandResult ES::AddContentStart(const IOCtlVRequest& request)
IPCCommandResult ES::AddContentStart(Context& context, const IOCtlVRequest& request)
{
if (!request.HasNumberOfValidVectors(2, 0))
return GetDefaultReply(ES_EINVAL);
@ -124,28 +125,28 @@ IPCCommandResult ES::AddContentStart(const IOCtlVRequest& request)
u64 title_id = Memory::Read_U64(request.in_vectors[0].address);
u32 content_id = Memory::Read_U32(request.in_vectors[1].address);
if (m_addtitle_content_id != 0xFFFFFFFF)
if (context.title_import.content_id != 0xFFFFFFFF)
{
ERROR_LOG(IOS_ES, "Trying to add content when we haven't finished adding "
"another content. Unsupported.");
return GetDefaultReply(ES_EINVAL);
}
m_addtitle_content_id = content_id;
context.title_import.content_id = content_id;
m_addtitle_content_buffer.clear();
context.title_import.content_buffer.clear();
INFO_LOG(IOS_ES, "IOCTL_ES_ADDCONTENTSTART: title id %016" PRIx64 ", "
"content id %08x",
title_id, m_addtitle_content_id);
title_id, context.title_import.content_id);
if (!m_addtitle_tmd.IsValid())
if (!context.title_import.tmd.IsValid())
return GetDefaultReply(ES_EINVAL);
if (title_id != m_addtitle_tmd.GetTitleId())
if (title_id != context.title_import.tmd.GetTitleId())
{
ERROR_LOG(IOS_ES, "IOCTL_ES_ADDCONTENTSTART: title id %016" PRIx64 " != "
"TMD title id %016" PRIx64 ", ignoring",
title_id, m_addtitle_tmd.GetTitleId());
title_id, context.title_import.tmd.GetTitleId());
}
// We're supposed to return a "content file descriptor" here, which is
@ -156,7 +157,7 @@ IPCCommandResult ES::AddContentStart(const IOCtlVRequest& request)
return GetDefaultReply(content_fd);
}
IPCCommandResult ES::AddContentData(const IOCtlVRequest& request)
IPCCommandResult ES::AddContentData(Context& context, const IOCtlVRequest& request)
{
if (!request.HasNumberOfValidVectors(2, 0))
return GetDefaultReply(ES_EINVAL);
@ -168,7 +169,8 @@ IPCCommandResult ES::AddContentData(const IOCtlVRequest& request)
u8* data_start = Memory::GetPointer(request.in_vectors[1].address);
u8* data_end = data_start + request.in_vectors[1].size;
m_addtitle_content_buffer.insert(m_addtitle_content_buffer.end(), data_start, data_end);
context.title_import.content_buffer.insert(context.title_import.content_buffer.end(), data_start,
data_end);
return GetDefaultReply(IPC_SUCCESS);
}
@ -184,22 +186,22 @@ static std::string GetImportContentPath(u64 title_id, u32 content_id)
return Common::GetImportTitlePath(title_id) + StringFromFormat("/content/%08x.app", content_id);
}
IPCCommandResult ES::AddContentFinish(const IOCtlVRequest& request)
IPCCommandResult ES::AddContentFinish(Context& context, const IOCtlVRequest& request)
{
if (!request.HasNumberOfValidVectors(1, 0))
return GetDefaultReply(ES_EINVAL);
if (m_addtitle_content_id == 0xFFFFFFFF)
if (context.title_import.content_id == 0xFFFFFFFF)
return GetDefaultReply(ES_EINVAL);
u32 content_fd = Memory::Read_U32(request.in_vectors[0].address);
INFO_LOG(IOS_ES, "IOCTL_ES_ADDCONTENTFINISH: content fd %08x", content_fd);
if (!m_addtitle_tmd.IsValid())
if (!context.title_import.tmd.IsValid())
return GetDefaultReply(ES_EINVAL);
// Try to find the title key from a pre-installed ticket.
IOS::ES::TicketReader ticket = DiscIO::FindSignedTicket(m_addtitle_tmd.GetTitleId());
IOS::ES::TicketReader ticket = DiscIO::FindSignedTicket(context.title_import.tmd.GetTitleId());
if (!ticket.IsValid())
{
return GetDefaultReply(ES_NO_TICKET);
@ -211,16 +213,16 @@ IPCCommandResult ES::AddContentFinish(const IOCtlVRequest& request)
// The IV for title content decryption is the lower two bytes of the
// content index, zero extended.
IOS::ES::Content content_info;
if (!m_addtitle_tmd.FindContentById(m_addtitle_content_id, &content_info))
if (!context.title_import.tmd.FindContentById(context.title_import.content_id, &content_info))
{
return GetDefaultReply(ES_EINVAL);
}
u8 iv[16] = {0};
iv[0] = (content_info.index >> 8) & 0xFF;
iv[1] = content_info.index & 0xFF;
std::vector<u8> decrypted_data(m_addtitle_content_buffer.size());
mbedtls_aes_crypt_cbc(&aes_ctx, MBEDTLS_AES_DECRYPT, m_addtitle_content_buffer.size(), iv,
m_addtitle_content_buffer.data(), decrypted_data.data());
std::vector<u8> decrypted_data(context.title_import.content_buffer.size());
mbedtls_aes_crypt_cbc(&aes_ctx, MBEDTLS_AES_DECRYPT, context.title_import.content_buffer.size(),
iv, context.title_import.content_buffer.data(), decrypted_data.data());
if (!CheckIfContentHashMatches(decrypted_data, content_info))
{
ERROR_LOG(IOS_ES, "AddContentFinish: Hash for content %08x doesn't match", content_info.id);
@ -235,12 +237,13 @@ IPCCommandResult ES::AddContentFinish(const IOCtlVRequest& request)
}
else
{
content_path = GetImportContentPath(m_addtitle_tmd.GetTitleId(), m_addtitle_content_id);
content_path = GetImportContentPath(context.title_import.tmd.GetTitleId(),
context.title_import.content_id);
}
File::CreateFullPath(content_path);
const std::string temp_path = Common::RootUserPath(Common::FROM_SESSION_ROOT) +
StringFromFormat("/tmp/%08x.app", m_addtitle_content_id);
StringFromFormat("/tmp/%08x.app", context.title_import.content_id);
File::CreateFullPath(temp_path);
{
@ -258,32 +261,33 @@ IPCCommandResult ES::AddContentFinish(const IOCtlVRequest& request)
return GetDefaultReply(ES_EIO);
}
m_addtitle_content_id = 0xFFFFFFFF;
context.title_import.content_id = 0xFFFFFFFF;
return GetDefaultReply(IPC_SUCCESS);
}
IPCCommandResult ES::AddTitleFinish(const IOCtlVRequest& request)
IPCCommandResult ES::AddTitleFinish(Context& context, const IOCtlVRequest& request)
{
if (!request.HasNumberOfValidVectors(0, 0) || !m_addtitle_tmd.IsValid())
if (!request.HasNumberOfValidVectors(0, 0) || !context.title_import.tmd.IsValid())
return GetDefaultReply(ES_EINVAL);
if (!WriteImportTMD(m_addtitle_tmd))
if (!WriteImportTMD(context.title_import.tmd))
return GetDefaultReply(ES_EIO);
if (!FinishImport(m_addtitle_tmd))
if (!FinishImport(context.title_import.tmd))
return GetDefaultReply(FS_EIO);
INFO_LOG(IOS_ES, "IOCTL_ES_ADDTITLEFINISH");
m_addtitle_tmd.SetBytes({});
context.title_import.tmd.SetBytes({});
return GetDefaultReply(IPC_SUCCESS);
}
IPCCommandResult ES::AddTitleCancel(const IOCtlVRequest& request)
IPCCommandResult ES::AddTitleCancel(Context& context, const IOCtlVRequest& request)
{
if (!request.HasNumberOfValidVectors(0, 0) || !m_addtitle_tmd.IsValid())
if (!request.HasNumberOfValidVectors(0, 0) || !context.title_import.tmd.IsValid())
return GetDefaultReply(ES_EINVAL);
const IOS::ES::TMDReader original_tmd = IOS::ES::FindInstalledTMD(m_addtitle_tmd.GetTitleId());
const IOS::ES::TMDReader original_tmd =
IOS::ES::FindInstalledTMD(context.title_import.tmd.GetTitleId());
if (!original_tmd.IsValid())
{
// This should never happen unless someone messed with the installed TMD directly.
@ -294,7 +298,7 @@ IPCCommandResult ES::AddTitleCancel(const IOCtlVRequest& request)
if (!FinishImport(original_tmd))
return GetDefaultReply(FS_EIO);
m_addtitle_tmd.SetBytes({});
context.title_import.tmd.SetBytes({});
return GetDefaultReply(IPC_SUCCESS);
}
@ -363,40 +367,40 @@ IPCCommandResult ES::DeleteTitleContent(const IOCtlVRequest& request)
return GetDefaultReply(IPC_SUCCESS);
}
IPCCommandResult ES::ExportTitleInit(const IOCtlVRequest& request)
IPCCommandResult ES::ExportTitleInit(Context& context, const IOCtlVRequest& request)
{
if (!request.HasNumberOfValidVectors(1, 1) || request.in_vectors[0].size != 8)
return GetDefaultReply(ES_EINVAL);
// No concurrent title import/export is allowed.
if (m_export_title_context.valid)
if (context.title_export.valid)
return GetDefaultReply(ES_EINVAL);
const auto tmd = IOS::ES::FindInstalledTMD(Memory::Read_U64(request.in_vectors[0].address));
if (!tmd.IsValid())
return GetDefaultReply(FS_ENOENT);
m_export_title_context.tmd = tmd;
context.title_export.tmd = tmd;
const auto ticket = DiscIO::FindSignedTicket(m_export_title_context.tmd.GetTitleId());
const auto ticket = DiscIO::FindSignedTicket(context.title_export.tmd.GetTitleId());
if (!ticket.IsValid())
return GetDefaultReply(ES_NO_TICKET);
if (ticket.GetTitleId() != m_export_title_context.tmd.GetTitleId())
if (ticket.GetTitleId() != context.title_export.tmd.GetTitleId())
return GetDefaultReply(ES_EINVAL);
m_export_title_context.title_key = ticket.GetTitleKey();
context.title_export.title_key = ticket.GetTitleKey();
const auto& raw_tmd = m_export_title_context.tmd.GetRawTMD();
const auto& raw_tmd = context.title_export.tmd.GetRawTMD();
if (request.io_vectors[0].size != raw_tmd.size())
return GetDefaultReply(ES_EINVAL);
Memory::CopyToEmu(request.io_vectors[0].address, raw_tmd.data(), raw_tmd.size());
m_export_title_context.valid = true;
context.title_export.valid = true;
return GetDefaultReply(IPC_SUCCESS);
}
IPCCommandResult ES::ExportContentBegin(const IOCtlVRequest& request)
IPCCommandResult ES::ExportContentBegin(Context& context, const IOCtlVRequest& request)
{
if (!request.HasNumberOfValidVectors(2, 0) || request.in_vectors[0].size != 8 ||
request.in_vectors[1].size != 4)
@ -405,7 +409,7 @@ IPCCommandResult ES::ExportContentBegin(const IOCtlVRequest& request)
const u64 title_id = Memory::Read_U64(request.in_vectors[0].address);
const u32 content_id = Memory::Read_U32(request.in_vectors[1].address);
if (!m_export_title_context.valid || m_export_title_context.tmd.GetTitleId() != title_id)
if (!context.title_export.valid || context.title_export.tmd.GetTitleId() != title_id)
{
ERROR_LOG(IOS_ES, "Tried to use ExportContentBegin with an invalid title export context.");
return GetDefaultReply(ES_EINVAL);
@ -426,7 +430,7 @@ IPCCommandResult ES::ExportContentBegin(const IOCtlVRequest& request)
content->m_Data->Open();
u32 cid = 0;
while (m_export_title_context.contents.find(cid) != m_export_title_context.contents.end())
while (context.title_export.contents.find(cid) != context.title_export.contents.end())
cid++;
TitleExportContext::ExportContent content_export;
@ -434,12 +438,12 @@ IPCCommandResult ES::ExportContentBegin(const IOCtlVRequest& request)
content_export.iv[0] = (content->m_metadata.index >> 8) & 0xFF;
content_export.iv[1] = content->m_metadata.index & 0xFF;
m_export_title_context.contents.emplace(cid, content_export);
context.title_export.contents.emplace(cid, content_export);
// IOS returns a content ID which is passed to further content calls.
return GetDefaultReply(cid);
}
IPCCommandResult ES::ExportContentData(const IOCtlVRequest& request)
IPCCommandResult ES::ExportContentData(Context& context, const IOCtlVRequest& request)
{
if (!request.HasNumberOfValidVectors(1, 1) || request.in_vectors[0].size != 4 ||
request.io_vectors[0].size == 0)
@ -450,8 +454,8 @@ IPCCommandResult ES::ExportContentData(const IOCtlVRequest& request)
const u32 content_id = Memory::Read_U32(request.in_vectors[0].address);
const u32 bytes_to_read = request.io_vectors[0].size;
const auto iterator = m_export_title_context.contents.find(content_id);
if (!m_export_title_context.valid || iterator == m_export_title_context.contents.end() ||
const auto iterator = context.title_export.contents.find(content_id);
if (!context.title_export.valid || iterator == context.title_export.contents.end() ||
iterator->second.content.m_position >= iterator->second.content.m_content.size)
{
return GetDefaultReply(ES_EINVAL);
@ -479,7 +483,7 @@ IPCCommandResult ES::ExportContentData(const IOCtlVRequest& request)
std::vector<u8> output(buffer.size());
mbedtls_aes_context aes_ctx;
mbedtls_aes_setkey_enc(&aes_ctx, m_export_title_context.title_key.data(), 128);
mbedtls_aes_setkey_enc(&aes_ctx, context.title_export.title_key.data(), 128);
const int ret = mbedtls_aes_crypt_cbc(&aes_ctx, MBEDTLS_AES_ENCRYPT, buffer.size(),
iterator->second.iv.data(), buffer.data(), output.data());
if (ret != 0)
@ -494,15 +498,15 @@ IPCCommandResult ES::ExportContentData(const IOCtlVRequest& request)
return GetDefaultReply(IPC_SUCCESS);
}
IPCCommandResult ES::ExportContentEnd(const IOCtlVRequest& request)
IPCCommandResult ES::ExportContentEnd(Context& context, const IOCtlVRequest& request)
{
if (!request.HasNumberOfValidVectors(1, 0) || request.in_vectors[0].size != 4)
return GetDefaultReply(ES_EINVAL);
const u32 content_id = Memory::Read_U32(request.in_vectors[0].address);
const auto iterator = m_export_title_context.contents.find(content_id);
if (!m_export_title_context.valid || iterator == m_export_title_context.contents.end() ||
const auto iterator = context.title_export.contents.find(content_id);
if (!context.title_export.valid || iterator == context.title_export.contents.end() ||
iterator->second.content.m_position != iterator->second.content.m_content.size)
{
return GetDefaultReply(ES_EINVAL);
@ -513,16 +517,16 @@ IPCCommandResult ES::ExportContentEnd(const IOCtlVRequest& request)
const auto& content_loader = AccessContentDevice(iterator->second.content.m_title_id);
content_loader.GetContentByID(iterator->second.content.m_content.id)->m_Data->Close();
m_export_title_context.contents.erase(iterator);
context.title_export.contents.erase(iterator);
return GetDefaultReply(IPC_SUCCESS);
}
IPCCommandResult ES::ExportTitleDone(const IOCtlVRequest& request)
IPCCommandResult ES::ExportTitleDone(Context& context, const IOCtlVRequest& request)
{
if (!m_export_title_context.valid)
if (!context.title_export.valid)
return GetDefaultReply(ES_EINVAL);
m_export_title_context.valid = false;
context.title_export.valid = false;
return GetDefaultReply(IPC_SUCCESS);
}
} // namespace Device

View File

@ -76,7 +76,7 @@ FileIO::FileIO(u32 device_id, const std::string& device_name)
{
}
void FileIO::Close()
ReturnCode FileIO::Close(u32 fd)
{
INFO_LOG(IOS_FILEIO, "FileIO: Close %s (DeviceID=%08x)", m_name.c_str(), m_device_id);
m_Mode = 0;
@ -86,6 +86,7 @@ void FileIO::Close()
m_file.reset();
m_is_active = false;
return IPC_SUCCESS;
}
ReturnCode FileIO::Open(const OpenRequest& request)

View File

@ -32,7 +32,7 @@ class FileIO : public Device
public:
FileIO(u32 device_id, const std::string& device_name);
void Close() override;
ReturnCode Close(u32 fd) override;
ReturnCode Open(const OpenRequest& request) override;
IPCCommandResult Seek(const SeekRequest& request) override;
IPCCommandResult Read(const ReadWriteRequest& request) override;

View File

@ -75,9 +75,7 @@ static std::mutex s_device_map_mutex;
// STATE_TO_SAVE
constexpr u8 IPC_MAX_FDS = 0x18;
constexpr u8 ES_MAX_COUNT = 3;
static std::shared_ptr<Device::Device> s_fdmap[IPC_MAX_FDS];
static std::shared_ptr<Device::ES> s_es_handles[ES_MAX_COUNT];
using IPCMsgQueue = std::deque<u32>;
static IPCMsgQueue s_request_queue; // ppc -> arm
@ -595,11 +593,7 @@ static void AddStaticDevices()
AddDevice<Device::STMImmediate>("/dev/stm/immediate");
AddDevice<Device::STMEventHook>("/dev/stm/eventhook");
AddDevice<Device::FS>("/dev/fs");
// IOS allows three ES devices at a time
for (auto& es_device : s_es_handles)
es_device = AddDevice<Device::ES>("/dev/es");
AddDevice<Device::ES>("/dev/es");
AddDevice<Device::DI>("/dev/di");
AddDevice<Device::NetKDRequest>("/dev/net/kd/request");
AddDevice<Device::NetKDTime>("/dev/net/kd/time");
@ -643,7 +637,7 @@ static void Reset()
{
if (!device)
continue;
device->Close();
device->Close(0);
device.reset();
}
@ -701,7 +695,6 @@ bool Reload(const u64 ios_title_id)
AddStaticDevices();
Device::ES::Init();
return true;
}
@ -855,13 +848,6 @@ void DoState(PointerWrap& p)
}
}
}
for (auto& es_device : s_es_handles)
{
const u32 handle_id = es_device->GetDeviceID();
p.Do(handle_id);
es_device = std::static_pointer_cast<Device::ES>(AccessDeviceByID(handle_id));
}
}
else
{
@ -884,24 +870,11 @@ void DoState(PointerWrap& p)
}
}
}
for (const auto& es_device : s_es_handles)
{
const u32 handle_id = es_device->GetDeviceID();
p.Do(handle_id);
}
}
}
static std::shared_ptr<Device::Device> GetUnusedESDevice()
{
const auto iterator = std::find_if(std::begin(s_es_handles), std::end(s_es_handles),
[](const auto& es_device) { return !es_device->IsOpened(); });
return (iterator != std::end(s_es_handles)) ? *iterator : nullptr;
}
// Returns the FD for the newly opened device (on success) or an error code.
static s32 OpenDevice(const OpenRequest& request)
static s32 OpenDevice(OpenRequest& request)
{
const s32 new_fd = GetFreeDeviceID();
INFO_LOG(IOS, "Opening %s (mode %d, fd %d)", request.path.c_str(), request.flags, new_fd);
@ -910,15 +883,10 @@ static s32 OpenDevice(const OpenRequest& request)
ERROR_LOG(IOS, "Couldn't get a free fd, too many open files");
return FS_EFDEXHAUSTED;
}
request.fd = new_fd;
std::shared_ptr<Device::Device> device;
if (request.path == "/dev/es")
{
device = GetUnusedESDevice();
if (!device)
return ES_FD_EXHAUSTED;
}
else if (request.path.find("/dev/usb/oh0/") == 0 && !GetDeviceByName(request.path))
if (request.path.find("/dev/usb/oh0/") == 0 && !GetDeviceByName(request.path))
{
device = std::make_shared<Device::OH0Device>(new_fd, request.path);
}
@ -961,8 +929,7 @@ static IPCCommandResult HandleCommand(const Request& request)
{
case IPC_CMD_CLOSE:
s_fdmap[request.fd].reset();
device->Close();
return Device::Device::GetDefaultReply(IPC_SUCCESS);
return Device::Device::GetDefaultReply(device->Close(request.fd));
case IPC_CMD_READ:
return device->Read(ReadWriteRequest{request.address});
case IPC_CMD_WRITE:

View File

@ -83,13 +83,13 @@ ReturnCode SDIOSlot0::Open(const OpenRequest& request)
return IPC_SUCCESS;
}
void SDIOSlot0::Close()
ReturnCode SDIOSlot0::Close(u32 fd)
{
m_Card.Close();
m_BlockLength = 0;
m_BusWidth = 0;
m_is_active = false;
return Device::Close(fd);
}
IPCCommandResult SDIOSlot0::IOCtl(const IOCtlRequest& request)

View File

@ -31,7 +31,7 @@ public:
void DoState(PointerWrap& p) override;
ReturnCode Open(const OpenRequest& request) override;
void Close() override;
ReturnCode Close(u32 fd) override;
IPCCommandResult IOCtl(const IOCtlRequest& request) override;
IPCCommandResult IOCtlV(const IOCtlVRequest& request) override;

View File

@ -67,10 +67,10 @@ IPCCommandResult STMImmediate::IOCtl(const IOCtlRequest& request)
return GetDefaultReply(return_value);
}
void STMEventHook::Close()
ReturnCode STMEventHook::Close(u32 fd)
{
s_event_hook_request.reset();
m_is_active = false;
return Device::Close(fd);
}
IPCCommandResult STMEventHook::IOCtl(const IOCtlRequest& request)

View File

@ -55,7 +55,7 @@ class STMEventHook final : public Device
{
public:
STMEventHook(u32 device_id, const std::string& device_name) : Device(device_id, device_name) {}
void Close() override;
ReturnCode Close(u32 fd) override;
IPCCommandResult IOCtl(const IOCtlRequest& request) override;
void DoState(PointerWrap& p) override;

View File

@ -147,7 +147,7 @@ bool BluetoothEmu::RemoteDisconnect(u16 _connectionHandle)
return SendEventDisconnect(_connectionHandle, 0x13);
}
void BluetoothEmu::Close()
ReturnCode BluetoothEmu::Close(u32 fd)
{
// Clean up state
m_ScanEnable = 0;
@ -156,7 +156,7 @@ void BluetoothEmu::Close()
m_HCIEndpoint.reset();
m_ACLEndpoint.reset();
m_is_active = false;
return Device::Close(fd);
}
IPCCommandResult BluetoothEmu::IOCtlV(const IOCtlVRequest& request)

View File

@ -49,7 +49,7 @@ public:
virtual ~BluetoothEmu();
void Close() override;
ReturnCode Close(u32 fd) override;
IPCCommandResult IOCtlV(const IOCtlVRequest& request) override;
void Update() override;

View File

@ -149,7 +149,7 @@ ReturnCode BluetoothReal::Open(const OpenRequest& request)
return IPC_SUCCESS;
}
void BluetoothReal::Close()
ReturnCode BluetoothReal::Close(u32 fd)
{
if (m_handle)
{
@ -159,7 +159,7 @@ void BluetoothReal::Close()
m_handle = nullptr;
}
m_is_active = false;
return Device::Close(fd);
}
IPCCommandResult BluetoothReal::IOCtlV(const IOCtlVRequest& request)

View File

@ -52,7 +52,7 @@ public:
~BluetoothReal() override;
ReturnCode Open(const OpenRequest& request) override;
void Close() override;
ReturnCode Close(u32 fd) override;
IPCCommandResult IOCtlV(const IOCtlVRequest& request) override;
void DoState(PointerWrap& p) override;

View File

@ -68,9 +68,10 @@ ReturnCode OH0Device::Open(const OpenRequest& request)
return return_code;
}
void OH0Device::Close()
ReturnCode OH0Device::Close(u32 fd)
{
m_oh0->DeviceClose(m_device_id);
return Device::Close(fd);
}
IPCCommandResult OH0Device::IOCtl(const IOCtlRequest& request)

View File

@ -25,7 +25,7 @@ public:
OH0Device(u32 device_id, const std::string& device_name);
ReturnCode Open(const OpenRequest& request) override;
void Close() override;
ReturnCode Close(u32 fd) override;
IPCCommandResult IOCtl(const IOCtlRequest& request) override;
IPCCommandResult IOCtlV(const IOCtlVRequest& request) override;
void DoState(PointerWrap& p) override;

View File

@ -71,7 +71,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
static const u32 STATE_VERSION = 81; // Last changed in PR 5317
static const u32 STATE_VERSION = 82; // Last changed in PR 5333
// Maps savestate versions to Dolphin versions.
// Versions after 42 don't need to be added to this list,