Skip to content
Permalink
Browse files
Merge pull request #9508 from leoetlino/ipc-reply-cleanup
IOS: Clean up the way IPC replies are constructed
  • Loading branch information
leoetlino committed Feb 14, 2021
2 parents e62c33c + d0136dd commit effd918
Show file tree
Hide file tree
Showing 62 changed files with 881 additions and 873 deletions.
@@ -63,3 +63,12 @@ s64 GetLocalTimeRTCOffset();
double GetEstimatedEmulationPerformance();

} // namespace SystemTimers

inline namespace SystemTimersLiterals
{
/// Converts timebase ticks to clock ticks.
constexpr u64 operator""_tbticks(unsigned long long value)
{
return value * SystemTimers::TIMER_RATIO;
}
} // namespace SystemTimersLiterals
@@ -57,13 +57,13 @@ void DIDevice::DoState(PointerWrap& p)
p.Do(m_last_length);
}

IPCCommandResult DIDevice::Open(const OpenRequest& request)
std::optional<IPCReply> DIDevice::Open(const OpenRequest& request)
{
InitializeIfFirstTime();
return Device::Open(request);
}

IPCCommandResult DIDevice::IOCtl(const IOCtlRequest& request)
std::optional<IPCReply> DIDevice::IOCtl(const IOCtlRequest& request)
{
InitializeIfFirstTime();

@@ -91,7 +91,7 @@ IPCCommandResult DIDevice::IOCtl(const IOCtlRequest& request)

// FinishIOCtl will be called after the command has been executed
// to reply to the request, so we shouldn't reply here.
return GetNoReply();
return std::nullopt;
}

void DIDevice::ProcessQueuedIOCtl()
@@ -612,7 +612,7 @@ void DIDevice::FinishDICommand(DIResult result)
}
}

IPCCommandResult DIDevice::IOCtlV(const IOCtlVRequest& request)
std::optional<IPCReply> DIDevice::IOCtlV(const IOCtlVRequest& request)
{
// IOCtlVs are not queued since they don't (currently) go into DVDInterface and act
// asynchronously. This does mean that an IOCtlV can be executed while an IOCtl is in progress,
@@ -623,7 +623,7 @@ IPCCommandResult DIDevice::IOCtlV(const IOCtlVRequest& request)
{
ERROR_LOG_FMT(IOS_DI, "IOCtlV: Received bad input buffer size {:#04x}, should be 0x20",
request.in_vectors[0].size);
return GetDefaultReply(static_cast<s32>(DIResult::BadArgument));
return IPCReply{static_cast<s32>(DIResult::BadArgument)};
}
const u8 command = Memory::Read_U8(request.in_vectors[0].address);
if (request.request != command)
@@ -704,7 +704,7 @@ IPCCommandResult DIDevice::IOCtlV(const IOCtlVRequest& request)
ERROR_LOG_FMT(IOS_DI, "Unknown ioctlv {:#04x}", request.request);
request.DumpUnknown(GetDeviceName(), Common::Log::IOS_DI);
}
return GetDefaultReply(static_cast<s32>(return_value));
return IPCReply{static_cast<s32>(return_value)};
}

void DIDevice::ChangePartition(const DiscIO::Partition partition)
@@ -42,9 +42,9 @@ class DIDevice : public Device

void DoState(PointerWrap& p) override;

IPCCommandResult Open(const OpenRequest& request) override;
IPCCommandResult IOCtl(const IOCtlRequest& request) override;
IPCCommandResult IOCtlV(const IOCtlVRequest& request) override;
std::optional<IPCReply> Open(const OpenRequest& request) override;
std::optional<IPCReply> IOCtl(const IOCtlRequest& request) override;
std::optional<IPCReply> IOCtlV(const IOCtlVRequest& request) override;

enum class DIIoctl : u32
{
@@ -160,19 +160,19 @@ void Device::DoStateShared(PointerWrap& p)
p.Do(m_is_active);
}

IPCCommandResult Device::Open(const OpenRequest& request)
std::optional<IPCReply> Device::Open(const OpenRequest& request)
{
m_is_active = true;
return GetDefaultReply(IPC_SUCCESS);
return IPCReply{IPC_SUCCESS};
}

IPCCommandResult Device::Close(u32 fd)
std::optional<IPCReply> Device::Close(u32 fd)
{
m_is_active = false;
return GetDefaultReply(IPC_SUCCESS);
return IPCReply{IPC_SUCCESS};
}

IPCCommandResult Device::Unsupported(const Request& request)
std::optional<IPCReply> Device::Unsupported(const Request& request)
{
static const std::map<IPCCommandType, std::string_view> names{{
{IPC_CMD_READ, "Read"},
@@ -183,27 +183,6 @@ IPCCommandResult Device::Unsupported(const Request& request)
}};

WARN_LOG_FMT(IOS, "{} does not support {}()", m_name, names.at(request.command));
return GetDefaultReply(IPC_EINVAL);
}

// Returns an IPCCommandResult for a reply with an average reply time for devices
// Please avoid using this function if more accurate timings are known.
IPCCommandResult Device::GetDefaultReply(const s32 return_value)
{
// Based on a hardware test, a device takes at least ~2700 ticks to reply to an IPC request.
// Depending on how much work a command performs, this can take much longer (10000+)
// especially if the NAND filesystem is accessed.
//
// Because we currently don't emulate timing very accurately, we should not return
// the minimum possible reply time (~960 ticks from the kernel or ~2700 from devices)
// but an average time, otherwise we are going to be much too fast in most cases.
return {return_value, true, 4000 * SystemTimers::TIMER_RATIO};
}

// Returns an IPCCommandResult with no reply. Useful for async commands that will generate a reply
// later. This takes no return value because it won't be used.
IPCCommandResult Device::GetNoReply()
{
return {IPC_SUCCESS, false, 0};
return IPCReply{IPC_EINVAL};
}
} // namespace IOS::HLE
@@ -5,6 +5,7 @@
#pragma once

#include <cstddef>
#include <optional>
#include <string>
#include <vector>

@@ -186,19 +187,23 @@ class Device
const std::string& GetDeviceName() const { return m_name; }
// Replies to Open and Close requests are sent by the IPC request handler (HandleCommand),
// not by the devices themselves.
virtual IPCCommandResult Open(const OpenRequest& request);
virtual IPCCommandResult 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); }
virtual IPCCommandResult IOCtl(const IOCtlRequest& ioctl) { return Unsupported(ioctl); }
virtual IPCCommandResult IOCtlV(const IOCtlVRequest& ioctlv) { return Unsupported(ioctlv); }
virtual std::optional<IPCReply> Open(const OpenRequest& request);
virtual std::optional<IPCReply> Close(u32 fd);
virtual std::optional<IPCReply> Seek(const SeekRequest& seek) { return Unsupported(seek); }
virtual std::optional<IPCReply> Read(const ReadWriteRequest& read) { return Unsupported(read); }
virtual std::optional<IPCReply> Write(const ReadWriteRequest& write)
{
return Unsupported(write);
}
virtual std::optional<IPCReply> IOCtl(const IOCtlRequest& ioctl) { return Unsupported(ioctl); }
virtual std::optional<IPCReply> IOCtlV(const IOCtlVRequest& ioctlv)
{
return Unsupported(ioctlv);
}
virtual void Update() {}
virtual void UpdateWantDeterminism(bool new_want_determinism) {}
virtual DeviceType GetDeviceType() const { return m_device_type; }
virtual bool IsOpened() const { return m_is_active; }
static IPCCommandResult GetDefaultReply(s32 return_value);
static IPCCommandResult GetNoReply();

protected:
Kernel& m_ios;
@@ -209,6 +214,6 @@ class Device
bool m_is_active = false;

private:
IPCCommandResult Unsupported(const Request& request);
std::optional<IPCReply> Unsupported(const Request& request);
};
} // namespace IOS::HLE
@@ -8,22 +8,22 @@

namespace IOS::HLE
{
IPCCommandResult DeviceStub::Open(const OpenRequest& request)
std::optional<IPCReply> DeviceStub::Open(const OpenRequest& request)
{
WARN_LOG_FMT(IOS, "{} faking Open()", m_name);
m_is_active = true;
return GetDefaultReply(IPC_SUCCESS);
return IPCReply(IPC_SUCCESS);
}

IPCCommandResult DeviceStub::IOCtl(const IOCtlRequest& request)
std::optional<IPCReply> DeviceStub::IOCtl(const IOCtlRequest& request)
{
WARN_LOG_FMT(IOS, "{} faking IOCtl()", m_name);
return GetDefaultReply(IPC_SUCCESS);
return IPCReply(IPC_SUCCESS);
}

IPCCommandResult DeviceStub::IOCtlV(const IOCtlVRequest& request)
std::optional<IPCReply> DeviceStub::IOCtlV(const IOCtlVRequest& request)
{
WARN_LOG_FMT(IOS, "{} faking IOCtlV()", m_name);
return GetDefaultReply(IPC_SUCCESS);
return IPCReply(IPC_SUCCESS);
}
} // namespace IOS::HLE
@@ -17,8 +17,8 @@ class DeviceStub final : public Device
public:
// Inherit the constructor from the Device class, since we don't need to do anything special.
using Device::Device;
IPCCommandResult Open(const OpenRequest& request) override;
IPCCommandResult IOCtl(const IOCtlRequest& request) override;
IPCCommandResult IOCtlV(const IOCtlVRequest& request) override;
std::optional<IPCReply> Open(const OpenRequest& request) override;
std::optional<IPCReply> IOCtl(const IOCtlRequest& request) override;
std::optional<IPCReply> IOCtlV(const IOCtlVRequest& request) override;
};
} // namespace IOS::HLE
@@ -34,49 +34,49 @@ enum

};

IPCCommandResult GetSystemTime(const IOCtlVRequest& request)
IPCReply GetSystemTime(const IOCtlVRequest& request)
{
if (!request.HasNumberOfValidVectors(0, 1))
{
return DolphinDevice::GetDefaultReply(IPC_EINVAL);
return IPCReply(IPC_EINVAL);
}

if (request.io_vectors[0].size != 4)
{
return DolphinDevice::GetDefaultReply(IPC_EINVAL);
return IPCReply(IPC_EINVAL);
}

const u32 milliseconds = Common::Timer::GetTimeMs();

Memory::Write_U32(milliseconds, request.io_vectors[0].address);
return DolphinDevice::GetDefaultReply(IPC_SUCCESS);
return IPCReply(IPC_SUCCESS);
}

IPCCommandResult GetVersion(const IOCtlVRequest& request)
IPCReply GetVersion(const IOCtlVRequest& request)
{
if (!request.HasNumberOfValidVectors(0, 1))
{
return DolphinDevice::GetDefaultReply(IPC_EINVAL);
return IPCReply(IPC_EINVAL);
}

const auto length = std::min(size_t(request.io_vectors[0].size), std::strlen(SCM_DESC_STR));

Memory::Memset(request.io_vectors[0].address, 0, request.io_vectors[0].size);
Memory::CopyToEmu(request.io_vectors[0].address, SCM_DESC_STR, length);

return DolphinDevice::GetDefaultReply(IPC_SUCCESS);
return IPCReply(IPC_SUCCESS);
}

IPCCommandResult GetCPUSpeed(const IOCtlVRequest& request)
IPCReply GetCPUSpeed(const IOCtlVRequest& request)
{
if (!request.HasNumberOfValidVectors(0, 1))
{
return DolphinDevice::GetDefaultReply(IPC_EINVAL);
return IPCReply(IPC_EINVAL);
}

if (request.io_vectors[0].size != 4)
{
return DolphinDevice::GetDefaultReply(IPC_EINVAL);
return IPCReply(IPC_EINVAL);
}

const SConfig& config = SConfig::GetInstance();
@@ -86,88 +86,86 @@ IPCCommandResult GetCPUSpeed(const IOCtlVRequest& request)

Memory::Write_U32(core_clock, request.io_vectors[0].address);

return DolphinDevice::GetDefaultReply(IPC_SUCCESS);
return IPCReply(IPC_SUCCESS);
}

IPCCommandResult GetSpeedLimit(const IOCtlVRequest& request)
IPCReply GetSpeedLimit(const IOCtlVRequest& request)
{
// get current speed limit
if (!request.HasNumberOfValidVectors(0, 1))
{
return DolphinDevice::GetDefaultReply(IPC_EINVAL);
return IPCReply(IPC_EINVAL);
}

if (request.io_vectors[0].size != 4)
{
return DolphinDevice::GetDefaultReply(IPC_EINVAL);
return IPCReply(IPC_EINVAL);
}

const SConfig& config = SConfig::GetInstance();
const u32 speed_percent = config.m_EmulationSpeed * 100;
Memory::Write_U32(speed_percent, request.io_vectors[0].address);

return DolphinDevice::GetDefaultReply(IPC_SUCCESS);
return IPCReply(IPC_SUCCESS);
}

IPCCommandResult SetSpeedLimit(const IOCtlVRequest& request)
IPCReply SetSpeedLimit(const IOCtlVRequest& request)
{
// set current speed limit
if (!request.HasNumberOfValidVectors(1, 0))
{
return DolphinDevice::GetDefaultReply(IPC_EINVAL);
return IPCReply(IPC_EINVAL);
}

if (request.in_vectors[0].size != 4)
{
return DolphinDevice::GetDefaultReply(IPC_EINVAL);
return IPCReply(IPC_EINVAL);
}

const float speed = float(Memory::Read_U32(request.in_vectors[0].address)) / 100.0f;
SConfig::GetInstance().m_EmulationSpeed = speed;
BootManager::SetEmulationSpeedReset(true);

return DolphinDevice::GetDefaultReply(IPC_SUCCESS);
return IPCReply(IPC_SUCCESS);
}

IPCCommandResult GetRealProductCode(const IOCtlVRequest& request)
IPCReply GetRealProductCode(const IOCtlVRequest& request)
{
if (!request.HasNumberOfValidVectors(0, 1))
{
return DolphinDevice::GetDefaultReply(IPC_EINVAL);
return IPCReply(IPC_EINVAL);
}

const std::string backup_file_path = File::GetUserPath(D_BACKUP_IDX) + DIR_SEP + WII_SETTING;

File::IOFile file(backup_file_path, "rb");
if (!file)
return DolphinDevice::GetDefaultReply(IPC_ENOENT);
return IPCReply(IPC_ENOENT);

Common::SettingsHandler::Buffer data;

if (!file.ReadBytes(data.data(), data.size()))
return DolphinDevice::GetDefaultReply(IPC_ENOENT);
return IPCReply(IPC_ENOENT);

Common::SettingsHandler gen;
gen.SetBytes(std::move(data));
const std::string code = gen.GetValue("CODE");

const size_t length = std::min<size_t>(request.io_vectors[0].size, code.length());
if (length == 0)
return DolphinDevice::GetDefaultReply(IPC_ENOENT);
return IPCReply(IPC_ENOENT);

Memory::Memset(request.io_vectors[0].address, 0, request.io_vectors[0].size);
Memory::CopyToEmu(request.io_vectors[0].address, code.c_str(), length);
return DolphinDevice::GetDefaultReply(IPC_SUCCESS);
return IPCReply(IPC_SUCCESS);
}

} // namespace

IPCCommandResult DolphinDevice::IOCtlV(const IOCtlVRequest& request)
std::optional<IPCReply> DolphinDevice::IOCtlV(const IOCtlVRequest& request)
{
if (Core::WantsDeterminism())
{
return DolphinDevice::GetDefaultReply(IPC_EACCES);
}
return IPCReply(IPC_EACCES);

switch (request.request)
{
@@ -184,7 +182,7 @@ IPCCommandResult DolphinDevice::IOCtlV(const IOCtlVRequest& request)
case IOCTL_DOLPHIN_GET_REAL_PRODUCTCODE:
return GetRealProductCode(request);
default:
return GetDefaultReply(IPC_EINVAL);
return IPCReply(IPC_EINVAL);
}
}
} // namespace IOS::HLE

0 comments on commit effd918

Please sign in to comment.