using System.Runtime.InteropServices; namespace XFESystem.NativeKernel; [StructLayout(LayoutKind.Sequential)] public struct ThreadProbe { public ulong Tls, Canary, Steps, Errors, PeerSteps, Done, Deadline, TickAddress, StartTicks; } public static unsafe class ThreadSelfTest { static ulong _event; static ulong _eventStep; [UnmanagedCallersOnly(EntryPoint = "xfe_kernel_event_probe")] public static uint EventProbe(void* argument) { Threads.Sleep(20); _eventStep = 1; if (!KernelEvents.Set(_event)) return 0; return 1; } public static bool Run() { if (!Threads.Ready || !LocalApicTimer.Ready) return false; ulong page = PhysicalMemory.Allocate(); if (page == 0) return false; byte* storage = (byte*)PhysicalMemory.ToVirtual(page); ThreadProbe* a = (ThreadProbe*)storage; ThreadProbe* b = (ThreadProbe*)(storage + 128); ulong* tlsA = (ulong*)(storage + 256), tlsB = (ulong*)(storage + 384); *tlsA = 0x5846455448524431; *tlsB = 0x5846455448524432; a->Tls = (ulong)tlsA; b->Tls = (ulong)tlsB; a->Canary = *tlsA; b->Canary = *tlsB; a->PeerSteps = (ulong)&b->Steps; b->PeerSteps = (ulong)&a->Steps; a->TickAddress = b->TickAddress = (ulong)Native.Storage(14); a->StartTicks = b->StartTicks = LocalApicTimer.Ticks; a->Deadline = b->Deadline = Native.ReadTsc() + MonotonicClock.Frequency * 3; ulong oldPreemptions = Threads.Preemptions; delegate* unmanaged probe = (delegate* unmanaged)Native.ThreadProbeAddress(); if (Threads.Create(probe, a, 65536, a->Tls, false) == 0 || Threads.Create(probe, b, 65536, b->Tls, false) == 0) return false; ulong timeout = MonotonicClock.Nanoseconds() + 4000000000; while ((a->Done == 0 || b->Done == 0) && MonotonicClock.Nanoseconds() < timeout) Threads.Yield(); bool result = a->Done != 0 && b->Done != 0 && a->Errors == 0 && b->Errors == 0 && a->Steps >= 100000 && b->Steps >= 100000 && Threads.Preemptions >= oldPreemptions + 3 && Native.ReadMsr(0xc0000100) == 0; Threads.Yield(); // reap finished guarded stacks only from another stack if (!PhysicalMemory.Release(page)) return false; _event = KernelEvents.Create(false, false); if (_event == 0 || Threads.Create(&EventProbe, null, 65536, 0, false) == 0) return false; ulong eventHandle = _event; uint wait = KernelEvents.Wait(&eventHandle, 1, 1000); if (wait != 0 || _eventStep != 1 || KernelEvents.Wait(&eventHandle, 1, 0) != KernelEvents.Timeout) return false; if (!KernelEvents.Close(_event) || KernelEvents.Set(_event)) return false; ulong manual = KernelEvents.Create(true, true); if (manual == 0 || KernelEvents.Wait(&manual, 1, 0) != 0 || KernelEvents.Wait(&manual, 1, 0) != 0 || !KernelEvents.Reset(manual) || KernelEvents.Wait(&manual, 1, 2) != KernelEvents.Timeout || !KernelEvents.Close(manual)) return false; Threads.Yield(); return result; } }