feature: add apple silicon native macOS support (#81)

* feature: add apple silicon native macOS support - #81

* (macos): Fix crash

This fixes a crash when viewing the rear camera

* fix(macos): keep interpolated presentation on main thread

* fix(macos): supply Retro-WFC payload during setup

* perf(windows): compile out flat-memory fallback check

* remove duplicate smoke test

* test(macos): name and focus host platform tests

* fix(macos): validate Retro-WFC payload cache

* fix(payload): preserve staged file access failures

* Limit flat-page checks to variable-page hosts

---------

Co-authored-by: patchzyy <64382339+patchzyy@users.noreply.github.com>
This commit is contained in:
Michael G
2026-09-01 12:57:15 -04:00
committed by GitHub
parent ae3096c89b
commit 5c76e2b0df
36 changed files with 1742 additions and 251 deletions
+49
View File
@@ -0,0 +1,49 @@
#include "host_context.h"
#include <cstdlib>
namespace {
// The worker yields twice; each return to the scheduler must preserve both
// context identities and the worker's continuation point.
HostContext::Handle g_scheduler = nullptr;
HostContext::Handle g_worker = nullptr;
int g_steps = 0;
void Worker(void*)
{
if (!HostContext::IsCurrent(g_worker)) {
std::abort();
}
++g_steps;
HostContext::Switch(g_scheduler);
if (!HostContext::IsCurrent(g_worker)) {
std::abort();
}
++g_steps;
HostContext::Switch(g_scheduler);
}
} // namespace
int main()
{
if (!HostContext::InitializeScheduler(&g_scheduler) ||
!HostContext::IsCurrent(g_scheduler)) {
return 1;
}
g_worker = HostContext::Create(64 * 1024, Worker, nullptr);
if (!g_worker) {
return 1;
}
HostContext::Switch(g_worker);
if (g_steps != 1 || !HostContext::IsCurrent(g_scheduler)) {
return 1;
}
HostContext::Switch(g_worker);
if (g_steps != 2 || !HostContext::IsCurrent(g_scheduler)) {
return 1;
}
HostContext::Destroy(g_worker);
HostContext::ShutdownScheduler(g_scheduler);
}
+46
View File
@@ -0,0 +1,46 @@
#include <array>
#include <cstdint>
#include <cstdlib>
#include <iostream>
#include <vector>
extern "C" void mkw_co_switch(void** targetSp, void** sourceSp);
extern "C" void* mkw_co_init(void* stackTop, void (*entry)(void*), void* argument);
namespace {
// Exercise the raw AArch64 context ABI independently of HostContext so a
// callee-saved-register or stack-frame regression is localized to this layer.
std::array<std::byte, 64 * 1024> g_workerStack{};
void* g_schedulerSp = nullptr;
void* g_workerSp = nullptr;
std::vector<int> g_events;
void Worker(void*) {
g_events.push_back(1);
mkw_co_switch(&g_schedulerSp, &g_workerSp);
g_events.push_back(2);
mkw_co_switch(&g_schedulerSp, &g_workerSp);
std::abort();
}
} // namespace
int main() {
g_workerSp = mkw_co_init(g_workerStack.data() + g_workerStack.size(), Worker, nullptr);
if (!g_workerSp) {
std::cerr << "failed to create AArch64 context frame\n";
return 1;
}
mkw_co_switch(&g_workerSp, &g_schedulerSp);
if (g_events != std::vector<int>{1}) {
std::cerr << "worker did not yield to scheduler\n";
return 1;
}
mkw_co_switch(&g_workerSp, &g_schedulerSp);
if (g_events != std::vector<int>{1, 2}) {
std::cerr << "worker did not resume from saved context\n";
return 1;
}
return 0;
}
@@ -0,0 +1,66 @@
#include "guest_flat_memory.h"
#include <cstdint>
#include <iostream>
#include <stdexcept>
#include <unistd.h>
int main() {
GuestFlat::Initialize({
{0x00000000u, 0x4000u, GuestFlat::Backing::Mem1},
{0x80000000u, 0x4000u, GuestFlat::Backing::Mem1},
{0x10000000u, 0x4000u, GuestFlat::Backing::Mem2},
{0x90000000u, 0x4000u, GuestFlat::Backing::Mem2},
});
if (!GuestFlat::IsActive()) {
std::cerr << "guest address space did not become active\n";
return 1;
}
if (GuestFlat::RequiresCheckedAccess() !=
(static_cast<size_t>(getpagesize()) > GuestFlat::kGuestPageSize)) {
std::cerr << "guest access mode does not reflect the host page size\n";
return 1;
}
auto* mem1Physical = GuestFlat::HostPointer(0x00000000u);
auto* mem1Cached = GuestFlat::HostPointer(0x80000000u);
auto* mem2Physical = GuestFlat::HostPointer(0x10000000u);
auto* mem2Cached = GuestFlat::HostPointer(0x90000000u);
if (!mem1Physical || !mem1Cached || !mem2Physical || !mem2Cached) {
std::cerr << "missing host alias\n";
return 1;
}
if (GuestFlat::HostPointer(0x4000u) != nullptr ||
GuestFlat::HostPointer(0xa0000000u) != nullptr) {
std::cerr << "unmapped guest address resolved to host memory\n";
return 1;
}
mem1Physical[7] = 0x5a;
mem2Cached[9] = 0xa5;
const auto* guest = reinterpret_cast<const uint8_t*>(GuestFlat::kFixedFlatGuestBase);
if (mem1Cached[7] != 0x5a || guest[0x80000007u] != 0x5a ||
mem2Physical[9] != 0xa5 || guest[0x10000009u] != 0xa5) {
std::cerr << "guest aliases are not coherent\n";
return 1;
}
auto* guestWritable = reinterpret_cast<uint8_t*>(GuestFlat::kFixedFlatGuestBase);
guestWritable[0x80000008u] = 0x3c;
guestWritable[0x1000000au] = 0xc3;
if (mem1Physical[8] != 0x3c || mem2Cached[10] != 0xc3) {
std::cerr << "guest writes were not visible through host aliases\n";
return 1;
}
GuestFlat::Initialize({
{0x00000000u, 0x4000u, GuestFlat::Backing::Mem1},
{0x80000000u, 0x4000u, GuestFlat::Backing::Mem1},
{0x10000000u, 0x4000u, GuestFlat::Backing::Mem2},
{0x90000000u, 0x4000u, GuestFlat::Backing::Mem2},
});
try {
GuestFlat::Initialize({{0x00000000u, 0x8000u, GuestFlat::Backing::Mem1}});
std::cerr << "guest address space accepted a different layout\n";
return 1;
} catch (const std::runtime_error&) {
}
return 0;
}
+30
View File
@@ -0,0 +1,30 @@
#include "platform/host_platform.h"
#include <iostream>
int main() {
if (!RuntimePlatform::ExecutableDirectory()) {
std::cerr << "unable to resolve the current executable directory\n";
return 1;
}
const auto userData = RuntimePlatform::ApplicationDataDirectory("WiiCompiledPlatformPathsTest");
if (userData.filename() != "WiiCompiledPlatformPathsTest") {
std::cerr << "application-data directory lost its application name: " << userData << '\n';
return 1;
}
if (RuntimePlatform::LogDirectory("WiiCompiledPlatformPathsTest") != userData / "Logs") {
std::cerr << "log directory is not derived from application data\n";
return 1;
}
#if defined(__APPLE__)
if (userData.parent_path().filename() != "Application Support" ||
userData.parent_path().parent_path().filename() != "Library") {
std::cerr << "macOS application-data directory is not under ~/Library/Application Support: "
<< userData << '\n';
return 1;
}
#endif
return 0;
}