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; return IPC_SUCCESS;
} }
void Device::Close() ReturnCode Device::Close(u32 fd)
{ {
m_is_active = false; m_is_active = false;
return IPC_SUCCESS;
} }
IPCCommandResult Device::Unsupported(const Request& request) 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), // Replies to Open and Close requests are sent by the IPC request handler (HandleCommand),
// not by the devices themselves. // not by the devices themselves.
virtual ReturnCode Open(const OpenRequest& request); 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 Seek(const SeekRequest& seek) { return Unsupported(seek); }
virtual IPCCommandResult Read(const ReadWriteRequest& read) { return Unsupported(read); } virtual IPCCommandResult Read(const ReadWriteRequest& read) { return Unsupported(read); }
virtual IPCCommandResult Write(const ReadWriteRequest& write) { return Unsupported(write); } virtual IPCCommandResult Write(const ReadWriteRequest& write) { return Unsupported(write); }

View File

@ -22,12 +22,6 @@ ReturnCode Stub::Open(const OpenRequest& request)
return IPC_SUCCESS; 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) IPCCommandResult Stub::IOCtl(const IOCtlRequest& request)
{ {
WARN_LOG(IOS, "%s faking IOCtl()", m_name.c_str()); 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); Stub(u32 device_id, const std::string& device_name);
ReturnCode Open(const OpenRequest& request) override; ReturnCode Open(const OpenRequest& request) override;
void Close() override;
IPCCommandResult IOCtl(const IOCtlRequest& request) override; IPCCommandResult IOCtl(const IOCtlRequest& request) override;
IPCCommandResult IOCtlV(const IOCtlVRequest& request) override; IPCCommandResult IOCtlV(const IOCtlVRequest& request) override;
}; };

View File

@ -4,6 +4,7 @@
#include "Core/IOS/ES/ES.h" #include "Core/IOS/ES/ES.h"
#include <algorithm>
#include <cinttypes> #include <cinttypes>
#include <cstdio> #include <cstdio>
#include <memory> #include <memory>
@ -27,17 +28,13 @@ namespace HLE
{ {
namespace Device 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 std::string s_content_file;
static TitleContext s_title_context; static TitleContext s_title_context;
// Title to launch after IOS has been reset and reloaded (similar to /sys/launch.sys). // Title to launch after IOS has been reset and reloaded (similar to /sys/launch.sys).
static u64 s_title_to_launch; static u64 s_title_to_launch;
ES::ES(u32 device_id, const std::string& device_name) : Device(device_id, device_name)
{
}
static void FinishAllStaleImports() static void FinishAllStaleImports()
{ {
const std::vector<u64> titles = IOS::ES::GetTitleImports(); const std::vector<u64> titles = IOS::ES::GetTitleImports();
@ -58,7 +55,7 @@ static void FinishAllStaleImports()
File::CreateDir(import_dir); File::CreateDir(import_dir);
} }
void ES::Init() ES::ES(u32 device_id, const std::string& device_name) : Device(device_id, device_name)
{ {
FinishAllStaleImports(); FinishAllStaleImports();
@ -182,14 +179,14 @@ static ReturnCode CheckIsAllowedToSetUID(const u32 caller_uid)
return caller_uid == system_menu_uid ? IPC_SUCCESS : ES_EINVAL; 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) if (!request.HasNumberOfValidVectors(1, 0) || request.in_vectors[0].size != 8)
return GetDefaultReply(ES_EINVAL); return GetDefaultReply(ES_EINVAL);
const u64 title_id = Memory::Read_U64(request.in_vectors[0].address); 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) if (ret < 0)
{ {
ERROR_LOG(IOS_ES, "SetUID: Permission check failed with error %d", ret); 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); 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) void ES::DoState(PointerWrap& p)
{ {
Device::DoState(p); Device::DoState(p);
@ -286,17 +301,8 @@ void ES::DoState(PointerWrap& p)
p.Do(m_AccessIdentID); p.Do(m_AccessIdentID);
s_title_context.DoState(p); s_title_context.DoState(p);
m_addtitle_tmd.DoState(p); for (auto& context : m_contexts)
p.Do(m_addtitle_content_id); context.DoState(p);
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);
u32 Count = (u32)(m_ContentAccessMap.size()); u32 Count = (u32)(m_ContentAccessMap.size());
p.Do(Count); 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) ReturnCode ES::Open(const OpenRequest& request)
{ {
m_caller_uid = request.uid; auto context = FindInactiveContext();
m_caller_gid = request.gid; 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); 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_ContentAccessMap.clear();
m_AccessIdentID = 0; m_AccessIdentID = 0;
@ -339,48 +370,52 @@ void 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.
DiscIO::CNANDContentManager::Access().ClearCache(); DiscIO::CNANDContentManager::Access().ClearCache();
return IPC_SUCCESS;
} }
IPCCommandResult ES::IOCtlV(const IOCtlVRequest& request) IPCCommandResult ES::IOCtlV(const IOCtlVRequest& request)
{ {
DEBUG_LOG(IOS_ES, "%s (0x%x)", GetDeviceName().c_str(), request.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) switch (request.request)
{ {
case IOCTL_ES_ADDTICKET: case IOCTL_ES_ADDTICKET:
return AddTicket(request); return AddTicket(request);
case IOCTL_ES_ADDTMD: case IOCTL_ES_ADDTMD:
return AddTMD(request); return AddTMD(*context, request);
case IOCTL_ES_ADDTITLESTART: case IOCTL_ES_ADDTITLESTART:
return AddTitleStart(request); return AddTitleStart(*context, request);
case IOCTL_ES_ADDCONTENTSTART: case IOCTL_ES_ADDCONTENTSTART:
return AddContentStart(request); return AddContentStart(*context, request);
case IOCTL_ES_ADDCONTENTDATA: case IOCTL_ES_ADDCONTENTDATA:
return AddContentData(request); return AddContentData(*context, request);
case IOCTL_ES_ADDCONTENTFINISH: case IOCTL_ES_ADDCONTENTFINISH:
return AddContentFinish(request); return AddContentFinish(*context, request);
case IOCTL_ES_ADDTITLEFINISH: case IOCTL_ES_ADDTITLEFINISH:
return AddTitleFinish(request); return AddTitleFinish(*context, request);
case IOCTL_ES_ADDTITLECANCEL: case IOCTL_ES_ADDTITLECANCEL:
return AddTitleCancel(request); return AddTitleCancel(*context, request);
case IOCTL_ES_GETDEVICEID: case IOCTL_ES_GETDEVICEID:
return GetConsoleID(request); return GetConsoleID(request);
case IOCTL_ES_OPENTITLECONTENT: case IOCTL_ES_OPENTITLECONTENT:
return OpenTitleContent(request); return OpenTitleContent(context->uid, request);
case IOCTL_ES_OPENCONTENT: case IOCTL_ES_OPENCONTENT:
return OpenContent(request); return OpenContent(context->uid, request);
case IOCTL_ES_READCONTENT: case IOCTL_ES_READCONTENT:
return ReadContent(request); return ReadContent(context->uid, request);
case IOCTL_ES_CLOSECONTENT: case IOCTL_ES_CLOSECONTENT:
return CloseContent(request); return CloseContent(context->uid, request);
case IOCTL_ES_SEEKCONTENT: case IOCTL_ES_SEEKCONTENT:
return SeekContent(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:
return GetTitleID(request); return GetTitleID(request);
case IOCTL_ES_SETUID: case IOCTL_ES_SETUID:
return SetUID(request); return SetUID(context->uid, request);
case IOCTL_ES_DIVERIFY: case IOCTL_ES_DIVERIFY:
return DIVerify(request); return DIVerify(request);
@ -441,23 +476,23 @@ IPCCommandResult ES::IOCtlV(const IOCtlVRequest& request)
case IOCTL_ES_GETSTOREDTMD: case IOCTL_ES_GETSTOREDTMD:
return GetStoredTMD(request); return GetStoredTMD(request);
case IOCTL_ES_ENCRYPT: case IOCTL_ES_ENCRYPT:
return Encrypt(request); return Encrypt(context->uid, request);
case IOCTL_ES_DECRYPT: case IOCTL_ES_DECRYPT:
return Decrypt(request); return Decrypt(context->uid, request);
case IOCTL_ES_LAUNCH: case IOCTL_ES_LAUNCH:
return Launch(request); return Launch(request);
case IOCTL_ES_LAUNCHBC: case IOCTL_ES_LAUNCHBC:
return LaunchBC(request); return LaunchBC(request);
case IOCTL_ES_EXPORTTITLEINIT: case IOCTL_ES_EXPORTTITLEINIT:
return ExportTitleInit(request); return ExportTitleInit(*context, request);
case IOCTL_ES_EXPORTCONTENTBEGIN: case IOCTL_ES_EXPORTCONTENTBEGIN:
return ExportContentBegin(request); return ExportContentBegin(*context, request);
case IOCTL_ES_EXPORTCONTENTDATA: case IOCTL_ES_EXPORTCONTENTDATA:
return ExportContentData(request); return ExportContentData(*context, request);
case IOCTL_ES_EXPORTCONTENTEND: case IOCTL_ES_EXPORTCONTENTEND:
return ExportContentEnd(request); return ExportContentEnd(*context, request);
case IOCTL_ES_EXPORTTITLEDONE: case IOCTL_ES_EXPORTTITLEDONE:
return ExportTitleDone(request); return ExportTitleDone(*context, request);
case IOCTL_ES_CHECKKOREAREGION: case IOCTL_ES_CHECKKOREAREGION:
return CheckKoreaRegion(request); return CheckKoreaRegion(request);
case IOCTL_ES_GETDEVICECERT: case IOCTL_ES_GETDEVICECERT:

View File

@ -45,9 +45,6 @@ class ES final : public Device
public: public:
ES(u32 device_id, const std::string& device_name); 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 s32 DIVerify(const IOS::ES::TMDReader& tmd, const IOS::ES::TicketReader& ticket);
static void LoadWAD(const std::string& _rContentFile); static void LoadWAD(const std::string& _rContentFile);
static bool LaunchTitle(u64 title_id, bool skip_reload = false); static bool LaunchTitle(u64 title_id, bool skip_reload = false);
@ -58,7 +55,7 @@ public:
void DoState(PointerWrap& p) override; void DoState(PointerWrap& p) override;
ReturnCode Open(const OpenRequest& request) override; ReturnCode Open(const OpenRequest& request) override;
void Close() override; ReturnCode Close(u32 fd) override;
IPCCommandResult IOCtlV(const IOCtlVRequest& request) override; IPCCommandResult IOCtlV(const IOCtlVRequest& request) override;
private: private:
@ -155,20 +152,57 @@ private:
u8 padding[0x3c]; 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 // Title management
IPCCommandResult AddTicket(const IOCtlVRequest& request); IPCCommandResult AddTicket(const IOCtlVRequest& request);
IPCCommandResult AddTMD(const IOCtlVRequest& request); IPCCommandResult AddTMD(Context& context, const IOCtlVRequest& request);
IPCCommandResult AddTitleStart(const IOCtlVRequest& request); IPCCommandResult AddTitleStart(Context& context, const IOCtlVRequest& request);
IPCCommandResult AddContentStart(const IOCtlVRequest& request); IPCCommandResult AddContentStart(Context& context, const IOCtlVRequest& request);
IPCCommandResult AddContentData(const IOCtlVRequest& request); IPCCommandResult AddContentData(Context& context, const IOCtlVRequest& request);
IPCCommandResult AddContentFinish(const IOCtlVRequest& request); IPCCommandResult AddContentFinish(Context& context, const IOCtlVRequest& request);
IPCCommandResult AddTitleFinish(const IOCtlVRequest& request); IPCCommandResult AddTitleFinish(Context& context, const IOCtlVRequest& request);
IPCCommandResult AddTitleCancel(const IOCtlVRequest& request); IPCCommandResult AddTitleCancel(Context& context, const IOCtlVRequest& request);
IPCCommandResult ExportTitleInit(const IOCtlVRequest& request); IPCCommandResult ExportTitleInit(Context& context, const IOCtlVRequest& request);
IPCCommandResult ExportContentBegin(const IOCtlVRequest& request); IPCCommandResult ExportContentBegin(Context& context, const IOCtlVRequest& request);
IPCCommandResult ExportContentData(const IOCtlVRequest& request); IPCCommandResult ExportContentData(Context& context, const IOCtlVRequest& request);
IPCCommandResult ExportContentEnd(const IOCtlVRequest& request); IPCCommandResult ExportContentEnd(Context& context, const IOCtlVRequest& request);
IPCCommandResult ExportTitleDone(const IOCtlVRequest& request); IPCCommandResult ExportTitleDone(Context& context, const IOCtlVRequest& request);
IPCCommandResult DeleteTitle(const IOCtlVRequest& request); IPCCommandResult DeleteTitle(const IOCtlVRequest& request);
IPCCommandResult DeleteTicket(const IOCtlVRequest& request); IPCCommandResult DeleteTicket(const IOCtlVRequest& request);
IPCCommandResult DeleteTitleContent(const IOCtlVRequest& request); IPCCommandResult DeleteTitleContent(const IOCtlVRequest& request);
@ -178,11 +212,11 @@ private:
IPCCommandResult GetDeviceCertificate(const IOCtlVRequest& request); IPCCommandResult GetDeviceCertificate(const IOCtlVRequest& request);
IPCCommandResult CheckKoreaRegion(const IOCtlVRequest& request); IPCCommandResult CheckKoreaRegion(const IOCtlVRequest& request);
IPCCommandResult Sign(const IOCtlVRequest& request); IPCCommandResult Sign(const IOCtlVRequest& request);
IPCCommandResult Encrypt(const IOCtlVRequest& request); IPCCommandResult Encrypt(u32 uid, const IOCtlVRequest& request);
IPCCommandResult Decrypt(const IOCtlVRequest& request); IPCCommandResult Decrypt(u32 uid, const IOCtlVRequest& request);
// Misc // Misc
IPCCommandResult SetUID(const IOCtlVRequest& request); IPCCommandResult SetUID(u32 uid, const IOCtlVRequest& request);
IPCCommandResult GetTitleDirectory(const IOCtlVRequest& request); IPCCommandResult GetTitleDirectory(const IOCtlVRequest& request);
IPCCommandResult GetTitleID(const IOCtlVRequest& request); IPCCommandResult GetTitleID(const IOCtlVRequest& request);
IPCCommandResult GetConsumption(const IOCtlVRequest& request); IPCCommandResult GetConsumption(const IOCtlVRequest& request);
@ -191,11 +225,11 @@ private:
IPCCommandResult DIVerify(const IOCtlVRequest& request); IPCCommandResult DIVerify(const IOCtlVRequest& request);
// Title contents // Title contents
IPCCommandResult OpenTitleContent(const IOCtlVRequest& request); IPCCommandResult OpenTitleContent(u32 uid, const IOCtlVRequest& request);
IPCCommandResult OpenContent(const IOCtlVRequest& request); IPCCommandResult OpenContent(u32 uid, const IOCtlVRequest& request);
IPCCommandResult ReadContent(const IOCtlVRequest& request); IPCCommandResult ReadContent(u32 uid, const IOCtlVRequest& request);
IPCCommandResult CloseContent(const IOCtlVRequest& request); IPCCommandResult CloseContent(u32 uid, const IOCtlVRequest& request);
IPCCommandResult SeekContent(const IOCtlVRequest& request); IPCCommandResult SeekContent(u32 uid, const IOCtlVRequest& request);
// Title information // Title information
IPCCommandResult GetTitleCount(const std::vector<u64>& titles, const IOCtlVRequest& request); IPCCommandResult GetTitleCount(const std::vector<u64>& titles, const IOCtlVRequest& request);
@ -228,6 +262,9 @@ private:
IPCCommandResult DIGetTMDSize(const IOCtlVRequest& request); IPCCommandResult DIGetTMDSize(const IOCtlVRequest& request);
IPCCommandResult DIGetTMD(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 LaunchIOS(u64 ios_title_id);
static bool LaunchPPCTitle(u64 title_id, bool skip_reload); static bool LaunchPPCTitle(u64 title_id, bool skip_reload);
static TitleContext& GetTitleContext(); static TitleContext& GetTitleContext();
@ -241,27 +278,7 @@ private:
u32 m_AccessIdentID = 0; u32 m_AccessIdentID = 0;
// For title installation (ioctls IOCTL_ES_ADDTITLE*). ContextArray m_contexts;
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;
}; };
} // namespace Device } // namespace Device
} // namespace HLE } // namespace HLE

View File

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

View File

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

View File

@ -66,7 +66,7 @@ IPCCommandResult ES::AddTicket(const IOCtlVRequest& request)
return GetDefaultReply(IPC_SUCCESS); return GetDefaultReply(IPC_SUCCESS);
} }
IPCCommandResult ES::AddTMD(const IOCtlVRequest& request) IPCCommandResult ES::AddTMD(Context& context, const IOCtlVRequest& request)
{ {
if (!request.HasNumberOfValidVectors(1, 0)) if (!request.HasNumberOfValidVectors(1, 0))
return GetDefaultReply(ES_EINVAL); 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 // 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. // 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. // 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); 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(FS_EIO);
return GetDefaultReply(IPC_SUCCESS); return GetDefaultReply(IPC_SUCCESS);
} }
IPCCommandResult ES::AddTitleStart(const IOCtlVRequest& request) IPCCommandResult ES::AddTitleStart(Context& context, const IOCtlVRequest& request)
{ {
if (!request.HasNumberOfValidVectors(4, 0)) if (!request.HasNumberOfValidVectors(4, 0))
return GetDefaultReply(ES_EINVAL); return GetDefaultReply(ES_EINVAL);
@ -96,19 +96,20 @@ IPCCommandResult ES::AddTitleStart(const IOCtlVRequest& request)
std::vector<u8> tmd(request.in_vectors[0].size); std::vector<u8> tmd(request.in_vectors[0].size);
Memory::CopyFromEmu(tmd.data(), request.in_vectors[0].address, request.in_vectors[0].size); Memory::CopyFromEmu(tmd.data(), request.in_vectors[0].address, request.in_vectors[0].size);
m_addtitle_tmd.SetBytes(tmd); context.title_import.tmd.SetBytes(tmd);
if (!m_addtitle_tmd.IsValid()) if (!context.title_import.tmd.IsValid())
{ {
ERROR_LOG(IOS_ES, "Invalid TMD while adding title (size = %zd)", tmd.size()); ERROR_LOG(IOS_ES, "Invalid TMD while adding title (size = %zd)", tmd.size());
return GetDefaultReply(ES_EINVAL); return GetDefaultReply(ES_EINVAL);
} }
// Finish a previous import (if it exists). // 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()) if (previous_tmd.IsValid())
FinishImport(previous_tmd); FinishImport(previous_tmd);
if (!IOS::ES::InitImport(m_addtitle_tmd.GetTitleId())) if (!IOS::ES::InitImport(context.title_import.tmd.GetTitleId()))
return GetDefaultReply(FS_EIO); return GetDefaultReply(FS_EIO);
// TODO: check and use the other vectors. // TODO: check and use the other vectors.
@ -116,7 +117,7 @@ IPCCommandResult ES::AddTitleStart(const IOCtlVRequest& request)
return GetDefaultReply(IPC_SUCCESS); return GetDefaultReply(IPC_SUCCESS);
} }
IPCCommandResult ES::AddContentStart(const IOCtlVRequest& request) IPCCommandResult ES::AddContentStart(Context& context, const IOCtlVRequest& request)
{ {
if (!request.HasNumberOfValidVectors(2, 0)) if (!request.HasNumberOfValidVectors(2, 0))
return GetDefaultReply(ES_EINVAL); 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); u64 title_id = Memory::Read_U64(request.in_vectors[0].address);
u32 content_id = Memory::Read_U32(request.in_vectors[1].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 " ERROR_LOG(IOS_ES, "Trying to add content when we haven't finished adding "
"another content. Unsupported."); "another content. Unsupported.");
return GetDefaultReply(ES_EINVAL); 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 ", " INFO_LOG(IOS_ES, "IOCTL_ES_ADDCONTENTSTART: title id %016" PRIx64 ", "
"content id %08x", "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); 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 " != " ERROR_LOG(IOS_ES, "IOCTL_ES_ADDCONTENTSTART: title id %016" PRIx64 " != "
"TMD title id %016" PRIx64 ", ignoring", "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 // 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); return GetDefaultReply(content_fd);
} }
IPCCommandResult ES::AddContentData(const IOCtlVRequest& request) IPCCommandResult ES::AddContentData(Context& context, const IOCtlVRequest& request)
{ {
if (!request.HasNumberOfValidVectors(2, 0)) if (!request.HasNumberOfValidVectors(2, 0))
return GetDefaultReply(ES_EINVAL); 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_start = Memory::GetPointer(request.in_vectors[1].address);
u8* data_end = data_start + request.in_vectors[1].size; 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); 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); 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)) if (!request.HasNumberOfValidVectors(1, 0))
return GetDefaultReply(ES_EINVAL); return GetDefaultReply(ES_EINVAL);
if (m_addtitle_content_id == 0xFFFFFFFF) if (context.title_import.content_id == 0xFFFFFFFF)
return GetDefaultReply(ES_EINVAL); return GetDefaultReply(ES_EINVAL);
u32 content_fd = Memory::Read_U32(request.in_vectors[0].address); u32 content_fd = Memory::Read_U32(request.in_vectors[0].address);
INFO_LOG(IOS_ES, "IOCTL_ES_ADDCONTENTFINISH: content fd %08x", content_fd); 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); return GetDefaultReply(ES_EINVAL);
// Try to find the title key from a pre-installed ticket. // 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()) if (!ticket.IsValid())
{ {
return GetDefaultReply(ES_NO_TICKET); 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 // The IV for title content decryption is the lower two bytes of the
// content index, zero extended. // content index, zero extended.
IOS::ES::Content content_info; 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); return GetDefaultReply(ES_EINVAL);
} }
u8 iv[16] = {0}; u8 iv[16] = {0};
iv[0] = (content_info.index >> 8) & 0xFF; iv[0] = (content_info.index >> 8) & 0xFF;
iv[1] = content_info.index & 0xFF; iv[1] = content_info.index & 0xFF;
std::vector<u8> decrypted_data(m_addtitle_content_buffer.size()); std::vector<u8> decrypted_data(context.title_import.content_buffer.size());
mbedtls_aes_crypt_cbc(&aes_ctx, MBEDTLS_AES_DECRYPT, m_addtitle_content_buffer.size(), iv, mbedtls_aes_crypt_cbc(&aes_ctx, MBEDTLS_AES_DECRYPT, context.title_import.content_buffer.size(),
m_addtitle_content_buffer.data(), decrypted_data.data()); iv, context.title_import.content_buffer.data(), decrypted_data.data());
if (!CheckIfContentHashMatches(decrypted_data, content_info)) if (!CheckIfContentHashMatches(decrypted_data, content_info))
{ {
ERROR_LOG(IOS_ES, "AddContentFinish: Hash for content %08x doesn't match", content_info.id); 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 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); File::CreateFullPath(content_path);
const std::string temp_path = Common::RootUserPath(Common::FROM_SESSION_ROOT) + 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); File::CreateFullPath(temp_path);
{ {
@ -258,32 +261,33 @@ IPCCommandResult ES::AddContentFinish(const IOCtlVRequest& request)
return GetDefaultReply(ES_EIO); return GetDefaultReply(ES_EIO);
} }
m_addtitle_content_id = 0xFFFFFFFF; context.title_import.content_id = 0xFFFFFFFF;
return GetDefaultReply(IPC_SUCCESS); 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); return GetDefaultReply(ES_EINVAL);
if (!WriteImportTMD(m_addtitle_tmd)) if (!WriteImportTMD(context.title_import.tmd))
return GetDefaultReply(ES_EIO); return GetDefaultReply(ES_EIO);
if (!FinishImport(m_addtitle_tmd)) if (!FinishImport(context.title_import.tmd))
return GetDefaultReply(FS_EIO); return GetDefaultReply(FS_EIO);
INFO_LOG(IOS_ES, "IOCTL_ES_ADDTITLEFINISH"); INFO_LOG(IOS_ES, "IOCTL_ES_ADDTITLEFINISH");
m_addtitle_tmd.SetBytes({}); context.title_import.tmd.SetBytes({});
return GetDefaultReply(IPC_SUCCESS); 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); 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()) if (!original_tmd.IsValid())
{ {
// This should never happen unless someone messed with the installed TMD directly. // 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)) if (!FinishImport(original_tmd))
return GetDefaultReply(FS_EIO); return GetDefaultReply(FS_EIO);
m_addtitle_tmd.SetBytes({}); context.title_import.tmd.SetBytes({});
return GetDefaultReply(IPC_SUCCESS); return GetDefaultReply(IPC_SUCCESS);
} }
@ -363,40 +367,40 @@ IPCCommandResult ES::DeleteTitleContent(const IOCtlVRequest& request)
return GetDefaultReply(IPC_SUCCESS); 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) if (!request.HasNumberOfValidVectors(1, 1) || request.in_vectors[0].size != 8)
return GetDefaultReply(ES_EINVAL); return GetDefaultReply(ES_EINVAL);
// No concurrent title import/export is allowed. // No concurrent title import/export is allowed.
if (m_export_title_context.valid) if (context.title_export.valid)
return GetDefaultReply(ES_EINVAL); return GetDefaultReply(ES_EINVAL);
const auto tmd = IOS::ES::FindInstalledTMD(Memory::Read_U64(request.in_vectors[0].address)); const auto tmd = IOS::ES::FindInstalledTMD(Memory::Read_U64(request.in_vectors[0].address));
if (!tmd.IsValid()) if (!tmd.IsValid())
return GetDefaultReply(FS_ENOENT); 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()) if (!ticket.IsValid())
return GetDefaultReply(ES_NO_TICKET); 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); 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()) if (request.io_vectors[0].size != raw_tmd.size())
return GetDefaultReply(ES_EINVAL); return GetDefaultReply(ES_EINVAL);
Memory::CopyToEmu(request.io_vectors[0].address, raw_tmd.data(), raw_tmd.size()); 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); 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 || if (!request.HasNumberOfValidVectors(2, 0) || request.in_vectors[0].size != 8 ||
request.in_vectors[1].size != 4) 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 u64 title_id = Memory::Read_U64(request.in_vectors[0].address);
const u32 content_id = Memory::Read_U32(request.in_vectors[1].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."); ERROR_LOG(IOS_ES, "Tried to use ExportContentBegin with an invalid title export context.");
return GetDefaultReply(ES_EINVAL); return GetDefaultReply(ES_EINVAL);
@ -426,7 +430,7 @@ IPCCommandResult ES::ExportContentBegin(const IOCtlVRequest& request)
content->m_Data->Open(); content->m_Data->Open();
u32 cid = 0; 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++; cid++;
TitleExportContext::ExportContent content_export; 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[0] = (content->m_metadata.index >> 8) & 0xFF;
content_export.iv[1] = content->m_metadata.index & 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. // IOS returns a content ID which is passed to further content calls.
return GetDefaultReply(cid); 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 || if (!request.HasNumberOfValidVectors(1, 1) || request.in_vectors[0].size != 4 ||
request.io_vectors[0].size == 0) 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 content_id = Memory::Read_U32(request.in_vectors[0].address);
const u32 bytes_to_read = request.io_vectors[0].size; const u32 bytes_to_read = request.io_vectors[0].size;
const auto iterator = m_export_title_context.contents.find(content_id); const auto iterator = context.title_export.contents.find(content_id);
if (!m_export_title_context.valid || iterator == m_export_title_context.contents.end() || if (!context.title_export.valid || iterator == context.title_export.contents.end() ||
iterator->second.content.m_position >= iterator->second.content.m_content.size) iterator->second.content.m_position >= iterator->second.content.m_content.size)
{ {
return GetDefaultReply(ES_EINVAL); return GetDefaultReply(ES_EINVAL);
@ -479,7 +483,7 @@ IPCCommandResult ES::ExportContentData(const IOCtlVRequest& request)
std::vector<u8> output(buffer.size()); std::vector<u8> output(buffer.size());
mbedtls_aes_context aes_ctx; 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(), const int ret = mbedtls_aes_crypt_cbc(&aes_ctx, MBEDTLS_AES_ENCRYPT, buffer.size(),
iterator->second.iv.data(), buffer.data(), output.data()); iterator->second.iv.data(), buffer.data(), output.data());
if (ret != 0) if (ret != 0)
@ -494,15 +498,15 @@ IPCCommandResult ES::ExportContentData(const IOCtlVRequest& request)
return GetDefaultReply(IPC_SUCCESS); 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) if (!request.HasNumberOfValidVectors(1, 0) || request.in_vectors[0].size != 4)
return GetDefaultReply(ES_EINVAL); return GetDefaultReply(ES_EINVAL);
const u32 content_id = Memory::Read_U32(request.in_vectors[0].address); const u32 content_id = Memory::Read_U32(request.in_vectors[0].address);
const auto iterator = m_export_title_context.contents.find(content_id); const auto iterator = context.title_export.contents.find(content_id);
if (!m_export_title_context.valid || iterator == m_export_title_context.contents.end() || if (!context.title_export.valid || iterator == context.title_export.contents.end() ||
iterator->second.content.m_position != iterator->second.content.m_content.size) iterator->second.content.m_position != iterator->second.content.m_content.size)
{ {
return GetDefaultReply(ES_EINVAL); 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); const auto& content_loader = AccessContentDevice(iterator->second.content.m_title_id);
content_loader.GetContentByID(iterator->second.content.m_content.id)->m_Data->Close(); 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); 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); return GetDefaultReply(ES_EINVAL);
m_export_title_context.valid = false; context.title_export.valid = false;
return GetDefaultReply(IPC_SUCCESS); return GetDefaultReply(IPC_SUCCESS);
} }
} // namespace Device } // 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); INFO_LOG(IOS_FILEIO, "FileIO: Close %s (DeviceID=%08x)", m_name.c_str(), m_device_id);
m_Mode = 0; m_Mode = 0;
@ -86,6 +86,7 @@ void FileIO::Close()
m_file.reset(); m_file.reset();
m_is_active = false; m_is_active = false;
return IPC_SUCCESS;
} }
ReturnCode FileIO::Open(const OpenRequest& request) ReturnCode FileIO::Open(const OpenRequest& request)

View File

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

View File

@ -75,9 +75,7 @@ static std::mutex s_device_map_mutex;
// STATE_TO_SAVE // STATE_TO_SAVE
constexpr u8 IPC_MAX_FDS = 0x18; 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::Device> s_fdmap[IPC_MAX_FDS];
static std::shared_ptr<Device::ES> s_es_handles[ES_MAX_COUNT];
using IPCMsgQueue = std::deque<u32>; using IPCMsgQueue = std::deque<u32>;
static IPCMsgQueue s_request_queue; // ppc -> arm static IPCMsgQueue s_request_queue; // ppc -> arm
@ -595,11 +593,7 @@ static void AddStaticDevices()
AddDevice<Device::STMImmediate>("/dev/stm/immediate"); AddDevice<Device::STMImmediate>("/dev/stm/immediate");
AddDevice<Device::STMEventHook>("/dev/stm/eventhook"); AddDevice<Device::STMEventHook>("/dev/stm/eventhook");
AddDevice<Device::FS>("/dev/fs"); AddDevice<Device::FS>("/dev/fs");
AddDevice<Device::ES>("/dev/es");
// IOS allows three ES devices at a time
for (auto& es_device : s_es_handles)
es_device = AddDevice<Device::ES>("/dev/es");
AddDevice<Device::DI>("/dev/di"); AddDevice<Device::DI>("/dev/di");
AddDevice<Device::NetKDRequest>("/dev/net/kd/request"); AddDevice<Device::NetKDRequest>("/dev/net/kd/request");
AddDevice<Device::NetKDTime>("/dev/net/kd/time"); AddDevice<Device::NetKDTime>("/dev/net/kd/time");
@ -643,7 +637,7 @@ static void Reset()
{ {
if (!device) if (!device)
continue; continue;
device->Close(); device->Close(0);
device.reset(); device.reset();
} }
@ -701,7 +695,6 @@ bool Reload(const u64 ios_title_id)
AddStaticDevices(); AddStaticDevices();
Device::ES::Init();
return true; 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 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. // 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(); const s32 new_fd = GetFreeDeviceID();
INFO_LOG(IOS, "Opening %s (mode %d, fd %d)", request.path.c_str(), request.flags, new_fd); 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"); ERROR_LOG(IOS, "Couldn't get a free fd, too many open files");
return FS_EFDEXHAUSTED; return FS_EFDEXHAUSTED;
} }
request.fd = new_fd;
std::shared_ptr<Device::Device> device; std::shared_ptr<Device::Device> device;
if (request.path == "/dev/es") if (request.path.find("/dev/usb/oh0/") == 0 && !GetDeviceByName(request.path))
{
device = GetUnusedESDevice();
if (!device)
return ES_FD_EXHAUSTED;
}
else if (request.path.find("/dev/usb/oh0/") == 0 && !GetDeviceByName(request.path))
{ {
device = std::make_shared<Device::OH0Device>(new_fd, 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: case IPC_CMD_CLOSE:
s_fdmap[request.fd].reset(); s_fdmap[request.fd].reset();
device->Close(); return Device::Device::GetDefaultReply(device->Close(request.fd));
return Device::Device::GetDefaultReply(IPC_SUCCESS);
case IPC_CMD_READ: case IPC_CMD_READ:
return device->Read(ReadWriteRequest{request.address}); return device->Read(ReadWriteRequest{request.address});
case IPC_CMD_WRITE: case IPC_CMD_WRITE:

View File

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

View File

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

View File

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

View File

@ -55,7 +55,7 @@ class STMEventHook final : public Device
{ {
public: public:
STMEventHook(u32 device_id, const std::string& device_name) : Device(device_id, device_name) {} 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; IPCCommandResult IOCtl(const IOCtlRequest& request) override;
void DoState(PointerWrap& p) override; void DoState(PointerWrap& p) override;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -71,7 +71,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 = 81; // Last changed in PR 5317 static const u32 STATE_VERSION = 82; // Last changed in PR 5333
// 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,