From ff6b3eb9acd25104a3b788910fae1971fabe0427 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Sun, 1 Oct 2017 17:54:01 +0200 Subject: [PATCH] [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. --- Source/Core/Core/IOS/ES/ES.cpp | 33 +++++++++++++ Source/Core/Core/IOS/IOS.cpp | 62 ++++++++++++++++++----- Source/Core/Core/IOS/IOS.h | 2 +- Source/Core/Core/IOS/MIOS.cpp | 89 ---------------------------------- 4 files changed, 84 insertions(+), 102 deletions(-) diff --git a/Source/Core/Core/IOS/ES/ES.cpp b/Source/Core/Core/IOS/ES/ES.cpp index d12114c14c..eb2ba6222e 100644 --- a/Source/Core/Core/IOS/ES/ES.cpp +++ b/Source/Core/Core/IOS/ES/ES.cpp @@ -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); } diff --git a/Source/Core/Core/IOS/IOS.cpp b/Source/Core/Core/IOS/IOS.cpp index 073323d347..667e92e490 100644 --- a/Source/Core/Core/IOS/IOS.cpp +++ b/Source/Core/Core/IOS/IOS.cpp @@ -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&& 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 GetElf() const + { + const auto iterator = m_bytes.cbegin() + GetHeaderSize() + GetElfOffset(); + return std::vector(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 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 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. diff --git a/Source/Core/Core/IOS/IOS.h b/Source/Core/Core/IOS/IOS.h index 73f17cbe7a..9280412698 100644 --- a/Source/Core/Core/IOS/IOS.h +++ b/Source/Core/Core/IOS/IOS.h @@ -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(); diff --git a/Source/Core/Core/IOS/MIOS.cpp b/Source/Core/Core/IOS/MIOS.cpp index fc0e70541a..2f0f02a422 100644 --- a/Source/Core/Core/IOS/MIOS.cpp +++ b/Source/Core/Core/IOS/MIOS.cpp @@ -6,16 +6,12 @@ #include #include -#include #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& bytes); - explicit ARMBinary(std::vector&& bytes); - - bool IsValid() const; - std::vector GetElf() const; - u32 GetHeaderSize() const; - u32 GetElfOffset() const; - u32 GetElfSize() const; - -private: - std::vector m_bytes; -}; - -ARMBinary::ARMBinary(const std::vector& bytes) : m_bytes(bytes) -{ -} - -ARMBinary::ARMBinary(std::vector&& 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 ARMBinary::GetElf() const -{ - const auto iterator = m_bytes.cbegin() + GetHeaderSize() + GetElfOffset(); - return std::vector(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 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.");