XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFESystem

【Rust】XFE系统,基于Rust和汇编

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Runtime.InteropServices;

namespace XFESystem.NativeKernel;

[StructLayout(LayoutKind.Sequential)]
public unsafe struct KernelThread
{
    public ulong State, Id, Reservation, ReservationBytes, StackLow, StackHigh;
    public delegate* unmanaged<void*, uint> Entry;
    public void* Argument;
    public ulong Tls, OwnTls, WakeAt;
    public void* RuntimeThread;
    public InterruptFrame* Interrupt;
    public ulong WaitHandle, ExitResult, InterruptStackTop;
}
[StructLayout(LayoutKind.Sequential)]
public unsafe struct SwitchContext
{
    public ulong Rbx, Rbp, R12, R13, R14, R15, Rsp, Rip, Rflags, FsBase, Rax, Rdx;
    public fixed byte FxState[512];
}
[StructLayout(LayoutKind.Sequential)]
public struct RuntimeLimitedContext { public ulong Ip, Rsp, Rbp, Rax, Rbx, Rdx, R12, R13, R14, R15; }

// Single-BSP scheduler, actual stacks/context switching, no host thread API.
public static unsafe class Threads
{
    const uint Capacity = 32;
    public const ulong InterruptStackBytes = 65536;
    const ulong Runnable = 1, Running = 2, Waiting = 3, Finished = 4;
    static KernelThread* _threads;
    static byte* _contexts;
    static uint _current;
    static ulong _nextId;
    static bool _scheduling;
    public static bool Ready;
    public static ulong Switches, Preemptions;
    public static KernelThread* Current => Ready ? _threads + _current : null;
    static SwitchContext* Context(uint slot) => (SwitchContext*)(_contexts + slot * 1024);

    public static bool Initialize()
    {
        if (Ready || sizeof(KernelThread) != 128 || sizeof(SwitchContext) != 608) return false;
        _threads = (KernelThread*)Native.Storage(7);
        _contexts = (byte*)Native.Storage(8);
        if (_threads == null || _contexts == null) return false;
        for (uint i = 0; i < Capacity; i++) { _threads[i] = new KernelThread(); for (uint j = 0; j < 1024; j++) _contexts[i * 1024 + j] = 0; }
        _current = 0; _nextId = 1;
        _threads[0].State = Running; _threads[0].Id = _nextId++;
        _threads[0].StackHigh = Native.BootStackTop();
        _threads[0].StackLow = _threads[0].StackHigh - 1024 * 1024;
        // The boot stack belongs to Limine. Reserve only a guarded IRQ stack for
        // the BSP thread; new threads place their user and IRQ stacks in one region.
        ulong interruptRegion = VirtualMemory.Reserve(InterruptStackBytes + 8192, 4096);
        if (interruptRegion == 0) return false;
        if (!VirtualMemory.Commit(interruptRegion + 4096, InterruptStackBytes))
        {
            VirtualMemory.Release(interruptRegion, InterruptStackBytes + 8192);
            return false;
        }
        _threads[0].Reservation = interruptRegion;
        _threads[0].ReservationBytes = InterruptStackBytes + 8192;
        _threads[0].InterruptStackTop = interruptRegion + 4096 + InterruptStackBytes;
        if (!Cpu.SetInterruptStack(_threads[0].InterruptStackTop)) return false;
        Ready = true;
        return true;
    }

    public static ulong Create(delegate* unmanaged<void*, uint> entry, void* argument, ulong stackBytes, ulong tls, bool ownTls)
    {
        ulong flags = Native.IrqSave();
        ulong result = CreateLocked(entry, argument, stackBytes, tls, ownTls);
        Native.IrqRestore(flags);
        return result;
    }
    static ulong CreateLocked(delegate* unmanaged<void*, uint> entry, void* argument, ulong stackBytes, ulong tls, bool ownTls)
    {
        if (!Ready || entry == null || stackBytes < 16384 || stackBytes > 32 * 1024 * 1024) return 0;
        Reap();
        uint slot = Capacity;
        for (uint i = 1; i < Capacity; i++) if (_threads[i].State == 0) { slot = i; break; }
        if (slot == Capacity) return 0;
        stackBytes = (stackBytes + 4095) & ~4095UL;
        ulong regionBytes = stackBytes + InterruptStackBytes + 3 * 4096;
        ulong reservation = VirtualMemory.Reserve(regionBytes, 4096);
        ulong interruptLow = reservation + stackBytes + 8192;
        if (reservation == 0 || !VirtualMemory.Commit(reservation + 4096, stackBytes) ||
            !VirtualMemory.Commit(interruptLow, InterruptStackBytes))
        {
            if (reservation != 0) VirtualMemory.Release(reservation, regionBytes);
            return 0;
        }
        SwitchContext* context = Context(slot);
        for (uint i = 0; i < 1024; i++) _contexts[slot * 1024 + i] = 0;
        _threads[slot] = new KernelThread { State = Runnable, Id = _nextId++, Reservation = reservation,
            ReservationBytes = regionBytes, StackLow = reservation + 4096, StackHigh = reservation + 4096 + stackBytes,
            Entry = entry, Argument = argument, Tls = tls, OwnTls = ownTls ? 1UL : 0UL,
            InterruptStackTop = interruptLow + InterruptStackBytes };
        context->Rbx = slot;
        context->Rsp = _threads[slot].StackHigh - 8;
        context->Rip = Native.ThreadTrampolineAddress();
        context->Rflags = 0x202;
        context->FsBase = tls;
        Native.ContextInitialize(context);
        return _threads[slot].Id;
    }

    static void Reap()
    {
        for (uint i = 1; i < Capacity; i++)
        {
            if (i == _current || _threads[i].State != Finished) continue;
            if (!VirtualMemory.Release(_threads[i].Reservation, _threads[i].ReservationBytes)) Kernel.Fail("THREAD_STACK_RELEASE");
            if (_threads[i].OwnTls != 0 && Native.ThreadTlsRelease(_threads[i].Tls) != 1) Kernel.Fail("THREAD_TLS_RELEASE");
            _threads[i].State = 0;
        }
    }
    static void WakeExpired()
    {
        ulong now = MonotonicClock.Nanoseconds();
        for (uint i = 0; i < Capacity; i++)
            if (_threads[i].State == Waiting && _threads[i].WakeAt != ~0UL && now >= _threads[i].WakeAt)
                _threads[i].State = Runnable;
    }
    static uint Choose()
    {
        for (uint step = 1; step <= Capacity; step++)
        {
            uint slot = (_current + step) % Capacity;
            if (_threads[slot].State == Runnable) return slot;
        }
        return Capacity;
    }
    static void Schedule()
    {
        if (!Ready || _scheduling) return;
        _scheduling = true;
        Reap(); WakeExpired();
        uint old = _current;
        if (_threads[old].State == Running) _threads[old].State = Runnable;
        uint next = Choose();
        while (next == Capacity)
        {
            Native.Idle(); WakeExpired(); next = Choose();
        }
        if (next == old) { _threads[old].State = Running; _scheduling = false; return; }
        _threads[next].State = Running;
        _current = next; Switches++;
        if (!Cpu.SetInterruptStack(_threads[next].InterruptStackTop)) Kernel.Fail("THREAD_INTERRUPT_STACK");
        Native.ContextSwitch(Context(old), Context(next));
        _scheduling = false;
        Reap();
    }
    public static void Yield()
    {
        ulong flags = Native.IrqSave(); Schedule(); Native.IrqRestore(flags);
    }
    public static void Sleep(uint milliseconds)
    {
        if (!Ready) return;
        if (milliseconds == 0) { Yield(); return; }
        ulong flags = Native.IrqSave();
        Current->WakeAt = MonotonicClock.Nanoseconds() + (ulong)milliseconds * 1000000;
        Current->WaitHandle = 0; Current->State = Waiting;
        Schedule(); Native.IrqRestore(flags);
    }
    public static void ParkEvent(ulong handle, ulong deadline)
    {
        if (!Ready) return;
        Current->WaitHandle = handle; Current->WakeAt = deadline; Current->State = Waiting;
        Schedule();
        Current->WaitHandle = 0;
    }
    public static void WakeEvent(ulong handle)
    {
        if (!Ready) return;
        for (uint i = 0; i < Capacity; i++)
            if (_threads[i].State == Waiting && (_threads[i].WaitHandle == handle || _threads[i].WaitHandle == ~0UL))
                _threads[i].State = Runnable;
    }
    public static void Timer(InterruptFrame* frame)
    {
        LocalApicTimer.Tick();
        WakeExpired();
        if (!Ready || _scheduling) return;
        uint slot = _current;
        ulong frameAddress = (ulong)frame, top = _threads[slot].InterruptStackTop;
        if (frame == null || frameAddress < top - InterruptStackBytes || frameAddress > top - (ulong)sizeof(InterruptFrame))
            Kernel.Fail("IRQ_STACK_BOUNDS");
        _threads[slot].Interrupt = frame;
        Preemptions++;
        Schedule();
        _threads[slot].Interrupt = null;
    }
    [UnmanagedCallersOnly(EntryPoint = "xfe_kernel_thread_entry")]
    public static void Run(ulong slot)
    {
        if (!Ready || slot >= Capacity || slot != _current) Kernel.Fail("THREAD_ENTRY");
        _scheduling = false;
        Native.IrqRestore(0x202);
        uint result = _threads[slot].Entry(_threads[slot].Argument);
        ulong flags = Native.IrqSave();
        if (Native.ThreadRuntimeExit(_threads[slot].RuntimeThread) != 1) Kernel.Fail("RUNTIME_THREAD_EXIT");
        _threads[slot].ExitResult = result;
        _threads[slot].State = Finished;
        Schedule();
        Native.IrqRestore(flags);
        Kernel.Fail("DEAD_THREAD_RESUMED");
    }
    public static bool StackBounds(void** low, void** high)
    {
        if (!Ready || low == null || high == null) return false;
        *low = (void*)Current->StackLow; *high = (void*)Current->StackHigh;
        return Current->StackLow < Current->StackHigh;
    }
    public static void Attach(void* runtimeThread)
    {
        ulong flags = Native.IrqSave();
        if (!Ready || runtimeThread == null || (Current->RuntimeThread != null && Current->RuntimeThread != runtimeThread)) Kernel.Fail("RUNTIME_ATTACH");
        Current->RuntimeThread = runtimeThread;
        Native.IrqRestore(flags);
    }
    public static int RequestHijack(void* runtimeThread)
    {
        if (!Ready || runtimeThread == null) return -1;
        ulong deadline = MonotonicClock.Nanoseconds() + 5000000000;
        do
        {
            ulong flags = Native.IrqSave();
            int result = -1;
            for (uint slot = 0; slot < Capacity; slot++)
            {
                if (_threads[slot].State == 0 || _threads[slot].State == Finished || _threads[slot].RuntimeThread != runtimeThread) continue;
                RuntimeLimitedContext context;
                InterruptFrame* frame = _threads[slot].Interrupt;
                if (frame != null)
                    context = new RuntimeLimitedContext { Ip = frame->Rip, Rsp = frame->Rsp, Rbp = frame->Rbp,
                        Rax = frame->Rax, Rbx = frame->Rbx, Rdx = frame->Rdx, R12 = frame->R12, R13 = frame->R13, R14 = frame->R14, R15 = frame->R15 };
                else
                {
                    SwitchContext* saved = Context(slot);
                    context = new RuntimeLimitedContext { Ip = saved->Rip, Rsp = saved->Rsp, Rbp = saved->Rbp,
                        Rax = saved->Rax, Rbx = saved->Rbx, Rdx = saved->Rdx, R12 = saved->R12, R13 = saved->R13, R14 = saved->R14, R15 = saved->R15 };
                }
                result = Native.TryHijack(runtimeThread, &context);
                break;
            }
            Native.IrqRestore(flags);
            if (result == 1) return 1;
            if (result < 0) return -1;
            Yield(); // inconvenient context: let the real thread run to a GC poll, then retry
        } while (MonotonicClock.Nanoseconds() < deadline);
        return 0;
    }
}