[Cleanup] IOS: Clean up the way ARM binaries are loaded

This commit removes the last usage of NANDContentManager in IOS code.

Another cleanup change is that loading ARM (IOS) binaries is now done
by the kernel in the BootIOS syscall, instead of being handled as a
special case in the MIOS code. This is more similar to how console
works and lets us easily extend the same logic to other IOS binaries
in the future, if we decide to actually load them.
This commit is contained in:
Léo Lam 2017-10-01 17:54:01 +02:00
parent 63a52fa707
commit ff6b3eb9ac
4 changed files with 84 additions and 102 deletions

View File

@ -246,6 +246,39 @@ bool ES::LaunchTitle(u64 title_id, bool skip_reload)
bool ES::LaunchIOS(u64 ios_title_id)
{
// A real Wii goes through several steps before getting to MIOS.
//
// * The System Menu detects a GameCube disc and launches BC (1-100) instead of the game.
// * BC (similar to boot1) lowers the clock speed to the Flipper's and then launches boot2.
// * boot2 sees the lowered clock speed and launches MIOS (1-101) instead of the System Menu.
//
// Because we don't have boot1 and boot2, and BC is only ever used to launch MIOS
// (indirectly via boot2), we can just launch MIOS when BC is launched.
if (ios_title_id == Titles::BC)
{
NOTICE_LOG(IOS, "BC: Launching MIOS...");
return LaunchIOS(Titles::MIOS);
}
// IOS checks whether the system title is installed and returns an error if it isn't.
// Unfortunately, we can't rely on titles being installed as we don't require system titles,
// so only have this check for MIOS (for which having the binary is *required*).
if (ios_title_id == Titles::MIOS)
{
const IOS::ES::TMDReader tmd = FindInstalledTMD(ios_title_id);
const IOS::ES::TicketReader ticket = FindSignedTicket(ios_title_id);
IOS::ES::Content content;
if (!tmd.IsValid() || !ticket.IsValid() || !tmd.GetContent(tmd.GetBootIndex(), &content) ||
!m_ios.BootIOS(ios_title_id, GetContentPath(ios_title_id, content)))
{
PanicAlertT("Could not launch IOS %016" PRIx64 " because it is missing from the NAND.\n"
"The emulated software will likely hang now.",
ios_title_id);
return false;
}
return true;
}
return m_ios.BootIOS(ios_title_id);
}

View File

@ -19,6 +19,7 @@
#include "Common/CommonTypes.h"
#include "Common/Logging/Log.h"
#include "Core/Boot/DolReader.h"
#include "Core/Boot/ElfReader.h"
#include "Core/CommonTitles.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h"
@ -296,23 +297,60 @@ bool Kernel::BootstrapPPC(const std::string& boot_content_path)
return true;
}
struct ARMBinary final
{
explicit ARMBinary(std::vector<u8>&& bytes) : m_bytes(std::move(bytes)) {}
bool IsValid() const
{
// The header is at least 0x10.
if (m_bytes.size() < 0x10)
return false;
return m_bytes.size() >= (GetHeaderSize() + GetElfOffset() + GetElfSize());
}
std::vector<u8> GetElf() const
{
const auto iterator = m_bytes.cbegin() + GetHeaderSize() + GetElfOffset();
return std::vector<u8>(iterator, iterator + GetElfSize());
}
u32 GetHeaderSize() const { return Common::swap32(m_bytes.data()); }
u32 GetElfOffset() const { return Common::swap32(m_bytes.data() + 0x4); }
u32 GetElfSize() const { return Common::swap32(m_bytes.data() + 0x8); }
private:
std::vector<u8> m_bytes;
};
// Similar to syscall 0x42 (ios_boot); this is used to change the current active IOS.
// IOS writes the new version to 0x3140 before restarting, but it does *not* poke any
// of the other constants to the memory. Warning: this resets the kernel instance.
bool Kernel::BootIOS(const u64 ios_title_id)
//
// Passing a boot content path is optional because we do not require IOSes
// to be installed at the moment. If one is passed, the boot binary must exist
// on the NAND, or the call will fail like on a Wii.
bool Kernel::BootIOS(const u64 ios_title_id, const std::string& boot_content_path)
{
// A real Wii goes through several steps before getting to MIOS.
//
// * The System Menu detects a GameCube disc and launches BC (1-100) instead of the game.
// * BC (similar to boot1) lowers the clock speed to the Flipper's and then launches boot2.
// * boot2 sees the lowered clock speed and launches MIOS (1-101) instead of the System Menu.
//
// Because we currently don't have boot1 and boot2, and BC is only ever used to launch MIOS
// (indirectly via boot2), we can just launch MIOS when BC is launched.
if (ios_title_id == Titles::BC)
if (!boot_content_path.empty())
{
NOTICE_LOG(IOS, "BC: Launching MIOS...");
return BootIOS(Titles::MIOS);
// Load the ARM binary to memory (if possible).
// Because we do not actually emulate the Starlet, only load the sections that are in MEM1.
File::IOFile file{boot_content_path, "rb"};
// TODO: should return IPC_ERROR_MAX.
if (file.GetSize() > 0xB00000)
return false;
std::vector<u8> data(file.GetSize());
if (!file.ReadBytes(data.data(), data.size()))
return false;
ARMBinary binary{std::move(data)};
if (!binary.IsValid())
return false;
ElfReader elf{binary.GetElf()};
if (!elf.LoadIntoMemory(true))
return false;
}
// Shut down the active IOS first before switching to the new one.

View File

@ -109,7 +109,7 @@ public:
u16 GetGidForPPC() const;
bool BootstrapPPC(const std::string& boot_content_path);
bool BootIOS(u64 ios_title_id);
bool BootIOS(u64 ios_title_id, const std::string& boot_content_path = "");
u32 GetVersion() const;
IOSC& GetIOSC();

View File

@ -6,16 +6,12 @@
#include <cstring>
#include <utility>
#include <vector>
#include "Common/CommonTypes.h"
#include "Common/FileUtil.h"
#include "Common/Logging/Log.h"
#include "Common/MsgHandler.h"
#include "Common/NandPaths.h"
#include "Common/Swap.h"
#include "Core/Boot/ElfReader.h"
#include "Core/CommonTitles.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h"
#include "Core/DSPEmulator.h"
@ -24,10 +20,8 @@
#include "Core/HW/DVD/DVDInterface.h"
#include "Core/HW/Memmap.h"
#include "Core/HW/SystemTimers.h"
#include "Core/IOS/ES/Formats.h"
#include "Core/PowerPC/PPCSymbolDB.h"
#include "Core/PowerPC/PowerPC.h"
#include "DiscIO/NANDContentLoader.h"
namespace IOS
{
@ -35,73 +29,6 @@ namespace HLE
{
namespace MIOS
{
// Source: https://wiibrew.org/wiki/ARM_Binaries
struct ARMBinary final
{
explicit ARMBinary(const std::vector<u8>& bytes);
explicit ARMBinary(std::vector<u8>&& bytes);
bool IsValid() const;
std::vector<u8> GetElf() const;
u32 GetHeaderSize() const;
u32 GetElfOffset() const;
u32 GetElfSize() const;
private:
std::vector<u8> m_bytes;
};
ARMBinary::ARMBinary(const std::vector<u8>& bytes) : m_bytes(bytes)
{
}
ARMBinary::ARMBinary(std::vector<u8>&& bytes) : m_bytes(std::move(bytes))
{
}
bool ARMBinary::IsValid() const
{
// The header is at least 0x10.
if (m_bytes.size() < 0x10)
return false;
return m_bytes.size() >= (GetHeaderSize() + GetElfOffset() + GetElfSize());
}
std::vector<u8> ARMBinary::GetElf() const
{
const auto iterator = m_bytes.cbegin() + GetHeaderSize() + GetElfOffset();
return std::vector<u8>(iterator, iterator + GetElfSize());
}
u32 ARMBinary::GetHeaderSize() const
{
return Common::swap32(m_bytes.data());
}
u32 ARMBinary::GetElfOffset() const
{
return Common::swap32(m_bytes.data() + 0x4);
}
u32 ARMBinary::GetElfSize() const
{
return Common::swap32(m_bytes.data() + 0x8);
}
static std::vector<u8> GetMIOSBinary()
{
const auto& loader =
DiscIO::NANDContentManager::Access().GetNANDLoader(Titles::MIOS, Common::FROM_SESSION_ROOT);
if (!loader.IsValid())
return {};
const auto* content = loader.GetContentByIndex(loader.GetTMD().GetBootIndex());
if (!content)
return {};
return content->m_Data->Get();
}
static void ReinitHardware()
{
SConfig::GetInstance().bWii = false;
@ -125,22 +52,6 @@ bool Load()
Memory::Write_U32(0x00000000, ADDRESS_INIT_SEMAPHORE);
Memory::Write_U32(0x09142001, 0x3180);
ARMBinary mios{GetMIOSBinary()};
if (!mios.IsValid())
{
PanicAlertT("Failed to load MIOS. It is required for launching GameCube titles from Wii mode.");
Core::QueueHostJob(Core::Stop);
return false;
}
ElfReader elf{mios.GetElf()};
if (!elf.LoadIntoMemory(true))
{
PanicAlertT("Failed to load MIOS ELF into memory.");
Core::QueueHostJob(Core::Stop);
return false;
}
ReinitHardware();
NOTICE_LOG(IOS, "Reinitialised hardware.");