// Copyright 2026 Dolphin Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "ProbeBroadway.h" #include #include #include #include "ProbeBroadwaySources.h" extern "C" { #include "hw0c.h" } namespace StarletBootstrapProbe { namespace { // The one HAL of the process: the vendored sources' functions are global, so is their HAL. std::optional s_hal; } // namespace bool BroadwayHal::Allowed(u32 address, std::string_view what) { ++m_accesses; if (InBenchWindow(address)) return true; if (m_failure.empty()) { m_failure = fmt::format("{} at {:#010x}, outside the bench window {:#010x}..{:#010x}", what, address, BENCH_WINDOW_BASE, BENCH_WINDOW_END); } return false; } u32 BroadwayHal::Read(u32 address) { return Allowed(address, "a read") ? m_memory.LoadWord(address) : 0; } void BroadwayHal::Write(u32 address, u32 value) { if (Allowed(address, "a write")) m_memory.StoreWord(address, value); } void BroadwayHal::CacheLine(u32 address) { // The line's first word stands for it: the loader passes line-aligned window addresses. Allowed(address & ~u32{3}, "a cache line"); } void BroadwayHal::Hex(u32 value) { m_capture += fmt::format("0x{:08x}", value); } void BroadwayHal::Dec(u32 value) { m_capture += fmt::format("{}", value); } bool BroadwayClient::Start(BroadwayMemory& memory) { if (s_hal) { m_refusal = "a Broadway client already started in this process: the loader's state is global"; return false; } s_hal.emplace(memory, CLIENT_TIMEBASE_BASE); m_started = true; // As the loader before the jump: the window's sentinel and control lines, HW-M4-0D's area, H-1A's // pages and record, the plan and the header's magic last. hw0c_setup(); return true; } void BroadwayClient::Advance(u64 elapsed) { if (!m_started || m_done || Failed()) return; s_hal->Elapse(elapsed); ++m_steps; m_done = hw0c_step(s_hal->Timebase()) != 0; } bool BroadwayClient::Failed() const { return !m_refusal.empty() || (m_started && s_hal->Failed()); } std::string BroadwayClient::Failure() const { if (!m_refusal.empty()) return m_refusal; return m_started ? s_hal->Failure() : std::string{}; } u64 BroadwayClient::Timebase() const { return m_started ? s_hal->Timebase() : 0; } std::string BroadwayClient::Report(std::string_view status) { if (!m_started || m_reported) return {}; m_reported = true; const size_t mark = s_hal->Capture().size(); hw0c_report(); return fmt::format("{}\n{}{}\n", status, s_hal->Capture().substr(mark), CLIENT_END); } std::string_view BroadwayClient::Sources() { return STAROS_LOADER_SHA256; } } // namespace StarletBootstrapProbe // The HAL's C functions (BroadwayHAL/board.h and gecko.h, and the cache and special-register // functions the vendored sources declare under their _HOST macros). extern "C" { uint32_t read32(uint32_t address) { return StarletBootstrapProbe::s_hal ? StarletBootstrapProbe::s_hal->Read(address) : 0; } void write32(uint32_t address, uint32_t value) { if (StarletBootstrapProbe::s_hal) StarletBootstrapProbe::s_hal->Write(address, value); } void sync(void) { } uint64_t timebase(void) { return StarletBootstrapProbe::s_hal ? StarletBootstrapProbe::s_hal->Timebase() : 0; } void dcbf(uint32_t address) { if (StarletBootstrapProbe::s_hal) StarletBootstrapProbe::s_hal->CacheLine(address); } void dcbi(uint32_t address) { if (StarletBootstrapProbe::s_hal) StarletBootstrapProbe::s_hal->CacheLine(address); } // Not a Broadway: HID0 and L2CR, which hw0c.c only reports, read as 0. uint32_t hid0(void) { return 0; } uint32_t l2cr(void) { return 0; } void gecko_puts(const char* text) { if (StarletBootstrapProbe::s_hal) StarletBootstrapProbe::s_hal->Put(text); } void gecko_hex(uint32_t value) { if (StarletBootstrapProbe::s_hal) StarletBootstrapProbe::s_hal->Hex(value); } void gecko_dec(uint32_t value) { if (StarletBootstrapProbe::s_hal) StarletBootstrapProbe::s_hal->Dec(value); } }