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

XFESystem

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

公开
关注 0 Fork 0 Star 0
UTF-8
namespace XFESystem.NativeKernel;

public static unsafe class LocalApicTimer
{
    static ulong _base;
    static bool _x2;
    public static bool Ready;
    static ulong* _ticks;
    public static ulong Ticks => _ticks == null ? 0 : *_ticks;
    static uint Read(uint register) => _x2 ? (uint)Native.ReadMsr(0x800 + register / 16) : Native.MmioRead32(_base + register);
    static void Write(uint register, uint value)
    {
        if (_x2) Native.WriteMsr(0x800 + register / 16, value);
        else Native.MmioWrite32(_base + register, value);
    }
    public static bool Initialize()
    {
        uint* cpu = stackalloc uint[4];
        Native.CpuId(1, 0, cpu);
        if ((cpu[3] & (1u << 9)) == 0 || !MonotonicClock.Ready) return false;
        _ticks = (ulong*)Native.Storage(14);
        if (_ticks == null) return false;
        *_ticks = 0;
        ulong apic = Native.ReadMsr(0x1b);
        _x2 = (apic & (1UL << 10)) != 0;
        Native.WriteMsr(0x1b, apic | (1UL << 11));
        if (!_x2)
        {
            _base = VirtualMemory.MapDevice(apic & 0xfffff000, 4096);
            if (_base == 0) return false;
        }
        Native.Out8(0x21, 255); Native.Out8(0xa1, 255); // PS/2 is polled; legacy PIC IRQs stay masked.
        Write(0xf0, 0x1ff);
        Write(0x3e0, 3); // divide by 16
        Write(0x320, (1u << 16) | 32);
        Write(0x380, 0xffffffff);
        ulong start = MonotonicClock.Nanoseconds();
        bool elapsed = false;
        for (uint poll = 0; poll < 5000000; poll++)
        {
            if (MonotonicClock.Nanoseconds() - start >= 20000000) { elapsed = true; break; }
            Native.Pause();
        }
        uint count = 0xffffffff - Read(0x390);
        if (!elapsed || count < 100) return false;
        ulong elapsedNanoseconds = MonotonicClock.Nanoseconds() - start;
        if (!TimerCalibration.TryInitialCount(count, elapsedNanoseconds, TimerCalibration.DesktopPeriodNanoseconds, out uint initialCount)) return false;
        Write(0x320, (1u << 17) | 32);
        Write(0x380, initialCount);
        Serial.Write("XFE_TIMER_CONFIGURATION|TargetPeriodNs|"); Serial.Number(TimerCalibration.DesktopPeriodNanoseconds);
        Serial.Write("|CalibrationNs|"); Serial.Number(elapsedNanoseconds);
        Serial.Write("|InitialCount|"); Serial.Number(initialCount); Serial.Line("|HardwareRefreshMeasured|0");
        Ready = true;
        return true;
    }
    public static void Acknowledge() { if (Ready) Write(0xb0, 0); }
    public static void Tick() { if (_ticks != null) (*_ticks)++; Acknowledge(); }
    public static bool InitializeWorker()
    {
        if (!Ready) return false;
        ulong apic = Native.ReadMsr(0x1b);
        if (((apic & (1UL << 10)) != 0) != _x2) return false;
        Native.WriteMsr(0x1b, apic | (1UL << 11));
        Write(0xf0, 0x1ff);
        Write(0x320, (1u << 16) | 32); // Allocation-free workers do not run the BSP scheduler.
        return true;
    }
    public static bool WakeWorker(uint apicId)
    {
        if (!Ready || (!_x2 && apicId > 255)) return false;
        // Only the BSP submits work. Exclude its thread preemption while writing
        // the split xAPIC ICR, but keep IRQs enabled during the actual join.
        ulong flags = Native.IrqSave();
        if (_x2) Native.WriteMsr(0x830, ((ulong)apicId << 32) | 240UL);
        else
        {
            bool idle = false;
            for (uint poll = 0; poll < 1000000; poll++)
                if ((Read(0x300) & (1u << 12)) == 0) { idle = true; break; }
                else Native.Pause();
            if (!idle) { Native.IrqRestore(flags); return false; }
            Write(0x310, apicId << 24); Write(0x300, 240);
        }
        Native.IrqRestore(flags);
        return true;
    }
}