IOS LLE: checkpoint NAND conformance and diagnostic tooling
Preserve formatted NAND boot and Wii Remote pairing, add DMA/SRAM conformance tests and a Wii comparison probe. Keep P1 profiling tools and native update A/B evidence; reject the unhelpful targeted SRAM shortcut.
This commit is contained in:
1 parent
b931671fde
commit
c230971f07
64 files changed
+6488
-238
No files matched your search
@@ -0,0 +1,172 @@
|
||||
// Copyright 2026 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <fat.h>
|
||||
#include <gccore.h>
|
||||
#include <ogc/es.h>
|
||||
#include <ogc/ios.h>
|
||||
#include <ogc/ipc.h>
|
||||
#include <ogc/isfs.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// The same DOL must be run on the console and in Dolphin. Do not depend on
|
||||
// timings or on allocated IOS file descriptor numbers in the comparison.
|
||||
#define SCHEMA_VERSION 1
|
||||
#define ROUNDS 64
|
||||
#define RECORDS_PER_ROUND 29
|
||||
#define SKIPPED (-32768)
|
||||
#define SD_TEST_FILE "sd:/wii-conformance-data.bin"
|
||||
|
||||
static FILE* output;
|
||||
static unsigned int sequence;
|
||||
|
||||
static void record(const char* test, unsigned int round, int value, int extra)
|
||||
{
|
||||
if (output)
|
||||
fprintf(output, "%u,%s,%u,%d,%d\n", sequence, test, round, value, extra);
|
||||
++sequence;
|
||||
}
|
||||
|
||||
static void probe_open(const char* name, const char* path, unsigned int round, unsigned int mode)
|
||||
{
|
||||
char test[48];
|
||||
const s32 fd = IOS_Open(path, mode);
|
||||
snprintf(test, sizeof(test), "open_%s_mode%u", name, mode);
|
||||
// File descriptor allocation can legitimately differ between runs.
|
||||
record(test, round, fd >= 0 ? 0 : fd, 0);
|
||||
snprintf(test, sizeof(test), "close_%s_mode%u", name, mode);
|
||||
record(test, round, fd >= 0 ? IOS_Close(fd) : SKIPPED, 0);
|
||||
}
|
||||
|
||||
static void probe_sd(unsigned int round)
|
||||
{
|
||||
static u8 expected[4096] __attribute__((aligned(32)));
|
||||
static u8 actual[4096] __attribute__((aligned(32)));
|
||||
const size_t length = 1 + ((round * 67) % sizeof(expected));
|
||||
for (size_t i = 0; i < length; ++i)
|
||||
expected[i] = (u8)(round * 131 + i * 17);
|
||||
|
||||
FILE* file = fopen(SD_TEST_FILE, "wb");
|
||||
record("sd_open_write", round, file ? 0 : -1, (int)length);
|
||||
record("sd_write_bytes", round, file ? (int)fwrite(expected, 1, length, file) : SKIPPED,
|
||||
(int)length);
|
||||
record("sd_close_write", round, file ? fclose(file) : SKIPPED, 0);
|
||||
|
||||
file = fopen(SD_TEST_FILE, "rb");
|
||||
record("sd_open_read", round, file ? 0 : -1, 0);
|
||||
const size_t read_count = file ? fread(actual, 1, length, file) : 0;
|
||||
record("sd_read_bytes", round, file ? (int)read_count : SKIPPED, (int)length);
|
||||
u32 hash = 2166136261u;
|
||||
for (size_t i = 0; i < read_count; ++i)
|
||||
hash = (hash ^ actual[i]) * 16777619u;
|
||||
record("sd_read_hash", round, file ? (int)hash : SKIPPED, 0);
|
||||
record("sd_data_equal", round,
|
||||
file ? (read_count == length && memcmp(expected, actual, length) == 0) : SKIPPED, 0);
|
||||
record("sd_close_read", round, file ? fclose(file) : SKIPPED, 0);
|
||||
record("sd_remove", round, remove(SD_TEST_FILE), 0);
|
||||
}
|
||||
|
||||
static void init_display(void)
|
||||
{
|
||||
VIDEO_Init();
|
||||
GXRModeObj* mode = VIDEO_GetPreferredMode(NULL);
|
||||
void* framebuffer = MEM_K0_TO_K1(SYS_AllocateFramebuffer(mode));
|
||||
console_init(framebuffer, 20, 20, mode->fbWidth - 40, mode->xfbHeight - 40,
|
||||
mode->fbWidth * VI_DISPLAY_PIX_SZ);
|
||||
VIDEO_Configure(mode);
|
||||
VIDEO_SetNextFramebuffer(framebuffer);
|
||||
VIDEO_SetBlack(FALSE);
|
||||
VIDEO_Flush();
|
||||
VIDEO_WaitVSync();
|
||||
if (mode->viTVMode & VI_NON_INTERLACE)
|
||||
VIDEO_WaitVSync();
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
init_display();
|
||||
printf("Wii IOS conformance probe v%d\n", SCHEMA_VERSION);
|
||||
|
||||
if (fatInitDefault())
|
||||
output = fopen("sd:/wii-conformance.csv", "wb");
|
||||
if (!output)
|
||||
{
|
||||
printf("Cannot write sd:/wii-conformance.csv\n");
|
||||
printf("Insert a writable SD card and restart.\n");
|
||||
for (int i = 0; i < 600; ++i)
|
||||
VIDEO_WaitVSync();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Metadata is checked before comparing individual observations.
|
||||
fprintf(output, "schema,%d\n", SCHEMA_VERSION);
|
||||
fprintf(output, "rounds,%d\n", ROUNDS);
|
||||
fprintf(output, "observations,%d\n", 4 + ROUNDS * RECORDS_PER_ROUND);
|
||||
fprintf(output, "ios_version,%d\n", IOS_GetVersion());
|
||||
fprintf(output, "ios_revision,%d\n", IOS_GetRevision());
|
||||
fprintf(output, "seq,test,round,value,extra\n");
|
||||
|
||||
const s32 fs_init = ISFS_Initialize();
|
||||
record("isfs_init", 0, fs_init, 0);
|
||||
const s32 es_init = __ES_Init();
|
||||
record("es_init", 0, es_init, 0);
|
||||
|
||||
for (unsigned int round = 0; round < ROUNDS; ++round)
|
||||
{
|
||||
// A missing resource is a useful stable IPC error-path test.
|
||||
probe_open("missing", "/dev/wii_conformance_probe_absent", round, IPC_OPEN_NONE);
|
||||
|
||||
for (unsigned int mode = IPC_OPEN_NONE; mode <= IPC_OPEN_RW; ++mode)
|
||||
{
|
||||
probe_open("fs", "/dev/fs", round, mode);
|
||||
probe_open("es", "/dev/es", round, mode);
|
||||
}
|
||||
|
||||
if (fs_init >= 0)
|
||||
{
|
||||
u32 count __attribute__((aligned(32))) = 0;
|
||||
const s32 result = ISFS_ReadDir("/", NULL, &count);
|
||||
record("isfs_root_count", round, result, result >= 0 ? (int)count : 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
record("isfs_root_count", round, SKIPPED, 0);
|
||||
}
|
||||
|
||||
if (es_init >= 0)
|
||||
{
|
||||
u32 title_count __attribute__((aligned(32))) = 0;
|
||||
const s32 title_result = ES_GetNumTitles(&title_count);
|
||||
record("es_title_count", round, title_result,
|
||||
title_result >= 0 ? (int)title_count : 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
record("es_title_count", round, SKIPPED, 0);
|
||||
}
|
||||
|
||||
probe_sd(round);
|
||||
}
|
||||
|
||||
if (fs_init >= 0)
|
||||
record("isfs_deinit", 0, ISFS_Deinitialize(), 0);
|
||||
else
|
||||
record("isfs_deinit", 0, SKIPPED, 0);
|
||||
if (es_init >= 0)
|
||||
record("es_deinit", 0, __ES_Close(), 0);
|
||||
else
|
||||
record("es_deinit", 0, SKIPPED, 0);
|
||||
|
||||
const int write_error = ferror(output);
|
||||
const int close_error = fclose(output);
|
||||
printf("%u observations. CSV: sd:/wii-conformance.csv\n", sequence);
|
||||
printf("Write status: %d, close status: %d\n", write_error, close_error);
|
||||
printf("Returning to loader in about 10 seconds.\n");
|
||||
for (int i = 0; i < 600; ++i)
|
||||
VIDEO_WaitVSync();
|
||||
return write_error || close_error ? 1 : 0;
|
||||
}
|
||||
Reference in new issue
Block a user