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;
}
}
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;
}
}