Track exact StarletOS bootstrap integration and checkpoint provenance
Recover 16 modified and 6 previously untracked integration files. All 22 source SHA-256s match the pre-publication StarletOS snapshot. Add handoff instructions, byte-preserving attributes and private backup exclusion. This commit publishes existing behavior; it does not claim a new full build or hardware qualification. The two preceding local commits and staged files were screened before publication.
This commit is contained in:
1 parent
a8dbbab62d
commit
54f5a74bbf
28 files changed
+9161
-7407
No files matched your search
@@ -1,27 +1,32 @@
|
||||
enable_testing()
|
||||
add_custom_target(unittests)
|
||||
add_custom_command(TARGET unittests POST_BUILD COMMAND ${CMAKE_CTEST_COMMAND} "--output-on-failure")
|
||||
|
||||
string(APPEND CMAKE_RUNTIME_OUTPUT_DIRECTORY "/Tests")
|
||||
|
||||
add_executable(tests EXCLUDE_FROM_ALL UnitTestsMain.cpp StubHost.cpp)
|
||||
set_target_properties(tests PROPERTIES FOLDER Tests)
|
||||
target_link_libraries(tests PRIVATE fmt::fmt gtest::gtest core uicommon)
|
||||
add_test(NAME tests COMMAND tests)
|
||||
add_custom_command(TARGET tests POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E remove_directory "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Sys"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_SOURCE_DIR}/Data/Sys" "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Sys"
|
||||
)
|
||||
enable_testing()
|
||||
add_custom_target(unittests)
|
||||
add_custom_command(TARGET unittests POST_BUILD COMMAND ${CMAKE_CTEST_COMMAND} "--output-on-failure")
|
||||
|
||||
string(APPEND CMAKE_RUNTIME_OUTPUT_DIRECTORY "/Tests")
|
||||
|
||||
add_executable(tests EXCLUDE_FROM_ALL UnitTestsMain.cpp StubHost.cpp)
|
||||
set_target_properties(tests PROPERTIES FOLDER Tests)
|
||||
target_link_libraries(tests PRIVATE fmt::fmt gtest::gtest core uicommon)
|
||||
add_test(NAME tests COMMAND tests)
|
||||
add_custom_command(TARGET tests POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E remove_directory "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Sys"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_SOURCE_DIR}/Data/Sys" "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Sys"
|
||||
)
|
||||
add_dependencies(unittests tests)
|
||||
|
||||
macro(add_dolphin_test target)
|
||||
add_library(${target} OBJECT ${ARGN})
|
||||
target_link_libraries(${target} PUBLIC fmt::fmt gtest::gtest PRIVATE core uicommon)
|
||||
target_link_libraries(tests PRIVATE ${target})
|
||||
endmacro()
|
||||
|
||||
add_subdirectory(Common)
|
||||
add_subdirectory(Core)
|
||||
# Runs a real Starlet ELF through the fork's CPU and SRAM bus without NAND or a frontend.
|
||||
add_executable(StarletBootstrapProbe EXCLUDE_FROM_ALL
|
||||
${CMAKE_SOURCE_DIR}/Tools/StarletBootstrapProbe/StarletBootstrapProbe.cpp StubHost.cpp)
|
||||
target_link_libraries(StarletBootstrapProbe PRIVATE fmt::fmt core uicommon)
|
||||
|
||||
macro(add_dolphin_test target)
|
||||
add_library(${target} OBJECT ${ARGN})
|
||||
target_link_libraries(${target} PUBLIC fmt::fmt gtest::gtest PRIVATE core uicommon)
|
||||
target_link_libraries(tests PRIVATE ${target})
|
||||
endmacro()
|
||||
|
||||
add_subdirectory(Common)
|
||||
add_subdirectory(Core)
|
||||
add_subdirectory(VideoCommon)
|
||||
|
||||
# Opt-in local trace qualification; never starts a console or accesses a NAND dump.
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
add_dolphin_test(MMIOTest MMIOTest.cpp)
|
||||
add_dolphin_test(PageFaultTest PageFaultTest.cpp)
|
||||
add_dolphin_test(CoreTimingTest CoreTimingTest.cpp)
|
||||
add_dolphin_test(PatchAllowlistTest PatchAllowlistTest.cpp)
|
||||
|
||||
add_dolphin_test(DSPAcceleratorTest DSP/DSPAcceleratorTest.cpp)
|
||||
add_dolphin_test(DSPAssemblyTest
|
||||
DSP/DSPAssemblyTest.cpp
|
||||
DSP/DSPTestBinary.cpp
|
||||
DSP/DSPTestText.cpp
|
||||
DSP/HermesBinary.cpp
|
||||
DSP/HermesText.cpp
|
||||
)
|
||||
|
||||
add_dolphin_test(MMIOTest MMIOTest.cpp)
|
||||
add_dolphin_test(PageFaultTest PageFaultTest.cpp)
|
||||
add_dolphin_test(CoreTimingTest CoreTimingTest.cpp)
|
||||
add_dolphin_test(PatchAllowlistTest PatchAllowlistTest.cpp)
|
||||
|
||||
add_dolphin_test(DSPAcceleratorTest DSP/DSPAcceleratorTest.cpp)
|
||||
add_dolphin_test(DSPAssemblyTest
|
||||
DSP/DSPAssemblyTest.cpp
|
||||
DSP/DSPTestBinary.cpp
|
||||
DSP/DSPTestText.cpp
|
||||
DSP/HermesBinary.cpp
|
||||
DSP/HermesText.cpp
|
||||
)
|
||||
|
||||
add_dolphin_test(ESFormatsTest IOS/ES/FormatsTest.cpp)
|
||||
|
||||
add_dolphin_test(StarletARMCoreTest IOS/Starlet/ARMCoreTest.cpp)
|
||||
add_dolphin_test(StarletBootstrapELFTest IOS/Starlet/BootstrapELFTest.cpp)
|
||||
add_dolphin_test(StarletDMATest IOS/Starlet/DMATest.cpp)
|
||||
add_dolphin_test(StarletSRAMReadTest IOS/Starlet/SRAMReadTest.cpp)
|
||||
add_dolphin_test(StarletSRAMWriteTest IOS/Starlet/SRAMWriteTest.cpp)
|
||||
@@ -24,36 +25,36 @@ add_dolphin_test(StarletEthernetTest IOS/Starlet/AX88772Test.cpp)
|
||||
target_include_directories(StarletEthernetTest PRIVATE ${PROJECT_SOURCE_DIR}/Externals/libslirp/include)
|
||||
|
||||
add_dolphin_test(FileSystemTest IOS/FS/FileSystemTest.cpp)
|
||||
|
||||
add_dolphin_test(SkylandersTest IOS/USB/SkylandersTest.cpp)
|
||||
|
||||
if(_M_X86_64)
|
||||
add_dolphin_test(PowerPCTest
|
||||
PowerPC/DivUtilsTest.cpp
|
||||
PowerPC/PageTableHostMappingTest.cpp
|
||||
|
||||
add_dolphin_test(SkylandersTest IOS/USB/SkylandersTest.cpp)
|
||||
|
||||
if(_M_X86_64)
|
||||
add_dolphin_test(PowerPCTest
|
||||
PowerPC/DivUtilsTest.cpp
|
||||
PowerPC/PageTableHostMappingTest.cpp
|
||||
PowerPC/Jit64Common/ConvertDoubleToSingle.cpp
|
||||
PowerPC/Jit64Common/DCache.cpp
|
||||
PowerPC/Jit64Common/Fres.cpp
|
||||
PowerPC/Jit64Common/Frsqrte.cpp
|
||||
)
|
||||
elseif(_M_ARM_64)
|
||||
add_dolphin_test(PowerPCTest
|
||||
PowerPC/DivUtilsTest.cpp
|
||||
PowerPC/PageTableHostMappingTest.cpp
|
||||
PowerPC/JitArm64/ConvertSingleDouble.cpp
|
||||
PowerPC/JitArm64/FPRF.cpp
|
||||
PowerPC/JitArm64/Fres.cpp
|
||||
PowerPC/JitArm64/Frsqrte.cpp
|
||||
PowerPC/JitArm64/MovI2R.cpp
|
||||
)
|
||||
else()
|
||||
add_dolphin_test(PowerPCTest
|
||||
PowerPC/DivUtilsTest.cpp
|
||||
PowerPC/PageTableHostMappingTest.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
target_sources(PowerPCTest PRIVATE
|
||||
PowerPC/TestValues.h
|
||||
StubJit.h
|
||||
)
|
||||
PowerPC/Jit64Common/Fres.cpp
|
||||
PowerPC/Jit64Common/Frsqrte.cpp
|
||||
)
|
||||
elseif(_M_ARM_64)
|
||||
add_dolphin_test(PowerPCTest
|
||||
PowerPC/DivUtilsTest.cpp
|
||||
PowerPC/PageTableHostMappingTest.cpp
|
||||
PowerPC/JitArm64/ConvertSingleDouble.cpp
|
||||
PowerPC/JitArm64/FPRF.cpp
|
||||
PowerPC/JitArm64/Fres.cpp
|
||||
PowerPC/JitArm64/Frsqrte.cpp
|
||||
PowerPC/JitArm64/MovI2R.cpp
|
||||
)
|
||||
else()
|
||||
add_dolphin_test(PowerPCTest
|
||||
PowerPC/DivUtilsTest.cpp
|
||||
PowerPC/PageTableHostMappingTest.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
target_sources(PowerPCTest PRIVATE
|
||||
PowerPC/TestValues.h
|
||||
StubJit.h
|
||||
)
|
||||
@@ -0,0 +1,236 @@
|
||||
// Copyright 2026 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "Core/IOS/Starlet/ARMCore.h"
|
||||
#include "Core/IOS/Starlet/BootstrapELF.h"
|
||||
|
||||
namespace IOS::LLE
|
||||
{
|
||||
namespace
|
||||
{
|
||||
constexpr u32 TEXT_BASE = 0xffff0000;
|
||||
constexpr u32 DATA_BASE = 0xfffe0000;
|
||||
|
||||
class RecordingBus final : public ARMBus
|
||||
{
|
||||
public:
|
||||
u8 Read8(u32 address) override
|
||||
{
|
||||
const auto it = bytes.find(address);
|
||||
return it == bytes.end() ? 0 : it->second;
|
||||
}
|
||||
void Write8(u32 address, u8 value) override { bytes[address] = value; }
|
||||
|
||||
std::unordered_map<u32, u8> bytes;
|
||||
};
|
||||
|
||||
void Put16(std::array<u8, 0x300>& image, size_t offset, u16 value)
|
||||
{
|
||||
image[offset] = static_cast<u8>(value >> 8);
|
||||
image[offset + 1] = static_cast<u8>(value);
|
||||
}
|
||||
|
||||
void Put32(std::array<u8, 0x300>& image, size_t offset, u32 value)
|
||||
{
|
||||
image[offset] = static_cast<u8>(value >> 24);
|
||||
image[offset + 1] = static_cast<u8>(value >> 16);
|
||||
image[offset + 2] = static_cast<u8>(value >> 8);
|
||||
image[offset + 3] = static_cast<u8>(value);
|
||||
}
|
||||
|
||||
std::array<u8, 0x300> MakeImage()
|
||||
{
|
||||
std::array<u8, 0x300> image{};
|
||||
image[0] = 0x7f;
|
||||
image[1] = 'E';
|
||||
image[2] = 'L';
|
||||
image[3] = 'F';
|
||||
image[4] = 1; // ELF32
|
||||
image[5] = 2; // big endian
|
||||
image[6] = 1;
|
||||
Put16(image, 16, 2); // ET_EXEC
|
||||
Put16(image, 18, 40); // ARM
|
||||
Put32(image, 20, 1); // ELF version
|
||||
Put32(image, 24, TEXT_BASE);
|
||||
Put32(image, 28, 52); // program headers
|
||||
Put32(image, 32, 0x200); // section headers
|
||||
Put16(image, 40, 52);
|
||||
Put16(image, 42, 32);
|
||||
Put16(image, 44, 2);
|
||||
Put16(image, 46, 40);
|
||||
Put16(image, 48, 3);
|
||||
|
||||
Put32(image, 52, 1);
|
||||
Put32(image, 56, 0x100);
|
||||
Put32(image, 60, TEXT_BASE);
|
||||
Put32(image, 64, TEXT_BASE);
|
||||
Put32(image, 68, 4);
|
||||
Put32(image, 72, 8);
|
||||
Put32(image, 76, 5);
|
||||
image[0x100] = 0xea; // ARM branch instruction
|
||||
|
||||
Put32(image, 84, 1);
|
||||
Put32(image, 88, 0x110);
|
||||
Put32(image, 92, DATA_BASE);
|
||||
Put32(image, 96, DATA_BASE);
|
||||
Put32(image, 100, 4);
|
||||
Put32(image, 104, 0x420);
|
||||
Put32(image, 108, 6);
|
||||
image[0x110] = 0x12;
|
||||
image[0x111] = 0x34;
|
||||
image[0x112] = 0x56;
|
||||
image[0x113] = 0x78;
|
||||
|
||||
constexpr char names[] = "\0REV_TRACE\0__fault_code\0__fault_lr\0__fault_spsr";
|
||||
for (size_t index = 0; index < sizeof(names); ++index)
|
||||
image[0x180 + index] = static_cast<u8>(names[index]);
|
||||
Put32(image, 0x228 + 4, 3); // SHT_STRTAB
|
||||
Put32(image, 0x228 + 16, 0x180);
|
||||
Put32(image, 0x228 + 20, sizeof(names));
|
||||
Put32(image, 0x250 + 4, 2); // SHT_SYMTAB
|
||||
Put32(image, 0x250 + 16, 0x120);
|
||||
Put32(image, 0x250 + 20, 5 * 16);
|
||||
Put32(image, 0x250 + 24, 1);
|
||||
Put32(image, 0x250 + 36, 16);
|
||||
|
||||
const auto symbol = [&image](size_t index, u32 name, u32 address, u32 size) {
|
||||
const size_t offset = 0x120 + index * 16;
|
||||
Put32(image, offset, name);
|
||||
Put32(image, offset + 4, address);
|
||||
Put32(image, offset + 8, size);
|
||||
};
|
||||
symbol(1, 1, DATA_BASE + 8, 1032);
|
||||
symbol(2, 11, DATA_BASE + 0x410, 4);
|
||||
symbol(3, 24, DATA_BASE + 0x414, 4);
|
||||
symbol(4, 35, DATA_BASE + 0x418, 4);
|
||||
return image;
|
||||
}
|
||||
|
||||
TEST(BootstrapELFTest, LoadsSegmentsAndZerosUninitializedData)
|
||||
{
|
||||
const auto image = MakeImage();
|
||||
RecordingBus bus;
|
||||
BootstrapELFInfo info;
|
||||
std::string error;
|
||||
ASSERT_TRUE(LoadBootstrapELF(image, bus, &info, &error)) << error;
|
||||
EXPECT_EQ(info.entry, TEXT_BASE);
|
||||
EXPECT_EQ(info.trace, DATA_BASE + 8);
|
||||
EXPECT_EQ(info.fault_spsr, DATA_BASE + 0x418);
|
||||
EXPECT_EQ(bus.Read8(TEXT_BASE), 0xea);
|
||||
EXPECT_EQ(bus.Read8(TEXT_BASE + 4), 0);
|
||||
EXPECT_EQ(bus.Read32(DATA_BASE), 0x12345678u);
|
||||
EXPECT_EQ(bus.Read32(DATA_BASE + 0x41c), 0u);
|
||||
EXPECT_EQ(bus.bytes.size(), size_t{8 + 0x420});
|
||||
}
|
||||
|
||||
TEST(BootstrapELFTest, RejectsMalformedImageBeforeWritingMemory)
|
||||
{
|
||||
auto image = MakeImage();
|
||||
RecordingBus bus;
|
||||
BootstrapELFInfo info;
|
||||
std::string error;
|
||||
image[5] = 1;
|
||||
EXPECT_FALSE(LoadBootstrapELF(image, bus, &info, &error));
|
||||
EXPECT_TRUE(bus.bytes.empty());
|
||||
|
||||
image = MakeImage();
|
||||
Put32(image, 0x120 + 2 * 16 + 4, DATA_BASE + 0x420);
|
||||
EXPECT_FALSE(LoadBootstrapELF(image, bus, &info, &error));
|
||||
EXPECT_TRUE(bus.bytes.empty());
|
||||
|
||||
image = MakeImage();
|
||||
Put32(image, 88, 0x300);
|
||||
EXPECT_FALSE(LoadBootstrapELF(image, bus, &info, &error));
|
||||
EXPECT_TRUE(bus.bytes.empty());
|
||||
}
|
||||
|
||||
// Revolution IOS Phase 06H: the optional MEM2 text segment of the emulator-only
|
||||
// disposable-image kernel, accepted only when the caller allows it.
|
||||
constexpr u32 MEM2_TEXT = 0x12600000;
|
||||
|
||||
std::array<u8, 0x300> MakeMem2Image(u32 address, u32 flags)
|
||||
{
|
||||
auto image = MakeImage();
|
||||
Put16(image, 44, 3);
|
||||
Put32(image, 116, 1);
|
||||
Put32(image, 120, 0x114);
|
||||
Put32(image, 124, address);
|
||||
Put32(image, 128, address);
|
||||
Put32(image, 132, 4);
|
||||
Put32(image, 136, 0x10);
|
||||
Put32(image, 140, flags);
|
||||
Put32(image, 0x114, 0xe12fff1e); // BX LR
|
||||
return image;
|
||||
}
|
||||
|
||||
TEST(BootstrapELFTest, Mem2TextSegmentOnlyWhenAllowed)
|
||||
{
|
||||
RecordingBus bus;
|
||||
BootstrapELFInfo info;
|
||||
std::string error;
|
||||
auto image = MakeMem2Image(MEM2_TEXT, 5);
|
||||
EXPECT_FALSE(LoadBootstrapELF(image, bus, &info, &error));
|
||||
EXPECT_TRUE(bus.bytes.empty());
|
||||
|
||||
ASSERT_TRUE(LoadBootstrapELF(image, bus, &info, &error, true)) << error;
|
||||
EXPECT_EQ(bus.Read32(MEM2_TEXT), 0xe12fff1eu);
|
||||
EXPECT_EQ(bus.Read32(MEM2_TEXT + 0xc), 0u);
|
||||
EXPECT_EQ(bus.Read8(TEXT_BASE), 0xea);
|
||||
EXPECT_EQ(bus.Read32(DATA_BASE), 0x12345678u);
|
||||
EXPECT_EQ(bus.bytes.size(), size_t{8 + 0x420 + 0x10});
|
||||
|
||||
RecordingBus plain;
|
||||
const auto two_segments = MakeImage();
|
||||
EXPECT_TRUE(LoadBootstrapELF(two_segments, plain, &info, &error, true)) << error;
|
||||
}
|
||||
|
||||
TEST(BootstrapELFTest, RejectsInvalidMem2TextSegmentBeforeWritingMemory)
|
||||
{
|
||||
RecordingBus bus;
|
||||
BootstrapELFInfo info;
|
||||
std::string error;
|
||||
// Writable, outside MEM2, crossing its end, or a second SRAM text segment.
|
||||
for (const auto& image : {MakeMem2Image(MEM2_TEXT, 6), MakeMem2Image(0x0fff0000, 5),
|
||||
MakeMem2Image(0x13fffff8, 5), MakeMem2Image(TEXT_BASE, 5)})
|
||||
{
|
||||
EXPECT_FALSE(LoadBootstrapELF(image, bus, &info, &error, true));
|
||||
EXPECT_TRUE(bus.bytes.empty());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ARMCoreTest, Armv5DoublewordTransfersAndAlignmentAbort)
|
||||
{
|
||||
RecordingBus bus;
|
||||
bus.Write32(TEXT_BASE, 0xe1c020d0); // LDRD r2, r3, [r0]
|
||||
bus.Write32(TEXT_BASE + 4, 0xe1c120f0); // STRD r2, r3, [r1]
|
||||
bus.Write32(DATA_BASE, 0x11223344);
|
||||
bus.Write32(DATA_BASE + 4, 0x55667788);
|
||||
|
||||
ARMCore core(bus);
|
||||
core.Reset(TEXT_BASE);
|
||||
core.SetBigEndian(true);
|
||||
core.SetRegister(0, DATA_BASE);
|
||||
core.SetRegister(1, DATA_BASE + 8);
|
||||
core.Step();
|
||||
EXPECT_EQ(core.GetRegister(2), 0x11223344u);
|
||||
EXPECT_EQ(core.GetRegister(3), 0x55667788u);
|
||||
core.Step();
|
||||
EXPECT_EQ(bus.Read32(DATA_BASE + 8), 0x11223344u);
|
||||
EXPECT_EQ(bus.Read32(DATA_BASE + 12), 0x55667788u);
|
||||
|
||||
core.Reset(TEXT_BASE);
|
||||
core.SetBigEndian(true);
|
||||
core.SetRegister(0, DATA_BASE + 4);
|
||||
core.Step();
|
||||
EXPECT_EQ(core.GetRegister(15), TEXT_BASE + 0x10);
|
||||
EXPECT_EQ(core.GetRegister(14), TEXT_BASE + 8);
|
||||
EXPECT_EQ(core.GetCP15State().fault_address, DATA_BASE + 4);
|
||||
}
|
||||
} // namespace
|
||||
} // namespace IOS::LLE
|
||||
@@ -135,6 +135,21 @@ protected:
|
||||
EXPECT_EQ(m_memory->GetDMAMemoryPointer(address, 4), nullptr);
|
||||
}
|
||||
|
||||
void CheckBootstrapStoredKeys()
|
||||
{
|
||||
m_memory->PrepareBootstrap();
|
||||
for (const u8 read_all : {0, 1})
|
||||
{
|
||||
SCOPED_TRACE(read_all);
|
||||
m_memory->m_ohci1_hci_events.clear();
|
||||
m_memory->ExecuteOHCI1HCICommand({0x0d, 0x0c, 7, 0, 0, 0, 0, 0, 0, read_all});
|
||||
ASSERT_EQ(m_memory->m_ohci1_hci_events.size(), 1u);
|
||||
// Command Complete: capacity 255, zero stored keys, no Return Link Keys event.
|
||||
EXPECT_EQ(m_memory->m_ohci1_hci_events.front(),
|
||||
(std::vector<u8>{0x0e, 8, 1, 0x0d, 0x0c, 0, 0xff, 0, 0, 0}));
|
||||
}
|
||||
}
|
||||
|
||||
void CheckOHCI()
|
||||
{
|
||||
for (const u32 base : {0x10000U, 0x10010000U, 0x0d404000U})
|
||||
@@ -381,6 +396,11 @@ TEST_F(StarletDMATest, OHCIUsesTwoPhysicalPagesAndLittleEndianDescriptors)
|
||||
CheckOHCI();
|
||||
}
|
||||
|
||||
TEST_F(StarletDMATest, BootstrapReadStoredKeysHasNoWiimoteDevices)
|
||||
{
|
||||
CheckBootstrapStoredKeys();
|
||||
}
|
||||
|
||||
TEST_F(StarletDMATest, NANDSeparatesDataSpareAndECCIncludingPartialTransfers)
|
||||
{
|
||||
CheckNAND();
|
||||
|
||||
Reference in new issue
Block a user