replace the VHD/diskpart disk image pipeline with a directory-based vvfat root, we'll see when it breaks, fix /O2, add an all_tests, generalize the fat32 driver into a fat 16+32 driver, change policy on volume drivers, collapse KCPU's fields into a single atomic KE_CPU_STATE state machine, move scheduler ticking to per-CPU LAPIC timers, gate verbose SMP topology logging behind selftest diagnostics and add SMP pool-checkpoint validation around AP admission, add checked intrusive list helpers behind a new KE_DEBUG_LIST_CHECKS flag, add ObInitializeObjectHeader/ObTryReferenceObject, add waitable display clock objects and a SYS_OPEN_DISPLAY_CLOCK syscall for vsync-style waits, refactor kernel input into a per-queue KE_INPUT_CONTEXT with its own key/mouse ring buffers and DPC-driven key repeat, add KeInputQueuePeekEvent/ReadEvent/WaitKey and a SYS_QUERY_GLOBAL_INPUT_STATE syscall, split the kernel pool allocator out of mm_virt, new video files!
This commit is contained in:
1 parent
485a6bbfe7
commit
8b254f84f4
74 files changed
+4488
-2503
No files matched your search
@@ -15,6 +15,9 @@
|
||||
#include <kernel/ke_work.h>
|
||||
#include <kernel/ke_mutex.h>
|
||||
#include <kernel/ke_sched.h>
|
||||
#include <kernel/ke_event.h>
|
||||
#include <kernel/ke_list.h>
|
||||
#include <kernel/ob_object.h>
|
||||
#include <kernel/ke_irql.h>
|
||||
|
||||
#define DISPLAY_DEFAULT_FG 0x00CCCCCCu
|
||||
@@ -32,6 +35,8 @@
|
||||
#define DISPLAY_CURSOR_POS_INVALID (-2147483647 - 1)
|
||||
#define DISPLAY_OUTPUT_BUFFER_CAPACITY 4096u
|
||||
#define DISPLAY_OUTPUT_DRAIN_COUNT 128u
|
||||
#define DISPLAY_CLOCK_DEFAULT_PERIOD_MS 1ull
|
||||
#define DISPLAY_CLOCK_SIGNAL_BATCH 32u
|
||||
|
||||
typedef struct DISPLAY_OUTPUT_GLYPH {
|
||||
char Character;
|
||||
@@ -58,6 +63,10 @@ static KWORK_ITEM gDisplayCursorWorkItem;
|
||||
static volatile long gDisplayCursorWorkInitialized;
|
||||
static bool gDisplayTransitionInProgress;
|
||||
static uint64_t gDisplayGeneration;
|
||||
static LIST_ENTRY gDisplayClockList;
|
||||
static SPINLOCK gDisplayClockLock = SPINLOCK_INIT;
|
||||
static volatile long gDisplayClockInitialized;
|
||||
static uint64_t gDisplayFrameGeneration;
|
||||
|
||||
static DISPLAY_DEVICE gBootFbDevice;
|
||||
static BOOTFB_CONTEXT gBootFb;
|
||||
@@ -125,6 +134,43 @@ static bool KiDisplayOutputSuspended(void);
|
||||
static void KiDisplayCursorWorker(void* context);
|
||||
static void KiDisplayDirtyAll(void);
|
||||
|
||||
// Converts a display-clock period to scheduler ticks with minimum one-tick progress.
|
||||
static uint64_t KiDisplayClockMillisecondsToTicks(uint64_t milliseconds) {
|
||||
if (milliseconds == 0)
|
||||
milliseconds = DISPLAY_CLOCK_DEFAULT_PERIOD_MS;
|
||||
|
||||
uint64_t ticks = milliseconds / SCHED_TICK_MS;
|
||||
if ((milliseconds % SCHED_TICK_MS) != 0)
|
||||
ticks++;
|
||||
return ticks ? ticks : 1;
|
||||
}
|
||||
|
||||
// Reads the global scheduler tick used by display-clock deadlines.
|
||||
static uint64_t KiDisplayClockReadSchedulerTick(void) {
|
||||
KE_SCHED_STATS stats;
|
||||
KeGetSchedulerStats(&stats);
|
||||
return stats.Ticks;
|
||||
}
|
||||
|
||||
// Destroys a display clock and removes it from the manager tick list.
|
||||
static void KiDisplayClockDestroy(OBJECT* object) {
|
||||
DISPLAY_CLOCK_OBJECT* clock = (DISPLAY_CLOCK_OBJECT*)object;
|
||||
if (!clock)
|
||||
return;
|
||||
|
||||
uintptr_t flags = KeAcquireSpinlock(&gDisplayClockLock);
|
||||
if (clock->Linked) {
|
||||
ListRemove(&clock->Link);
|
||||
clock->Linked = 0;
|
||||
}
|
||||
KeReleaseSpinlock(&gDisplayClockLock, flags);
|
||||
|
||||
if (clock->Event) {
|
||||
ObDereferenceObject(&clock->Event->Header);
|
||||
clock->Event = (EVENT*)0;
|
||||
}
|
||||
}
|
||||
|
||||
static bool KiDisplayHasCurrentThread(void) {
|
||||
return KeGetCurrentThread() != (THREAD*)0;
|
||||
}
|
||||
@@ -1105,6 +1151,10 @@ void KeDisplayInitialize(const HAL_API* hal) {
|
||||
gCursorCommittedY = DISPLAY_CURSOR_POS_INVALID;
|
||||
gDisplayTransitionInProgress = false;
|
||||
gDisplayGeneration = 1;
|
||||
gDisplayClockLock = (SPINLOCK)SPINLOCK_INIT;
|
||||
LIST_INIT(&gDisplayClockList);
|
||||
gDisplayFrameGeneration = 0;
|
||||
_InterlockedExchange(&gDisplayClockInitialized, 1);
|
||||
if (!gDisplayProviderMutex)
|
||||
(void)KeMutexCreate(&gDisplayProviderMutex);
|
||||
KeInitializeWorkItem(&gDisplayCursorWorkItem, KiDisplayCursorWorker, 0);
|
||||
@@ -1178,6 +1228,149 @@ void KeDisplayInitialize(const HAL_API* hal) {
|
||||
KiDisplayRegisterOutputEndpoint();
|
||||
}
|
||||
|
||||
DISPLAY_API KSTATUS KeDisplayOpenClock(uint64_t periodMilliseconds, DISPLAY_CLOCK_OBJECT** outClock) {
|
||||
if (!outClock)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
*outClock = (DISPLAY_CLOCK_OBJECT*)0;
|
||||
|
||||
if (!_InterlockedCompareExchange(&gDisplayClockInitialized, 1, 1))
|
||||
return NTLSTATUS_NOT_SUPPORTED;
|
||||
|
||||
uintptr_t displayFlags = KeAcquireSpinlock(&gDisplayLock);
|
||||
bool hasDevice = KiDisplayHasDevice();
|
||||
KeReleaseSpinlock(&gDisplayLock, displayFlags);
|
||||
if (!hasDevice)
|
||||
return NTLSTATUS_NOT_SUPPORTED;
|
||||
|
||||
DISPLAY_CLOCK_OBJECT* clock = (DISPLAY_CLOCK_OBJECT*)ObAllocateObject(ObjectTypeDisplayClock,
|
||||
sizeof(DISPLAY_CLOCK_OBJECT));
|
||||
if (!clock)
|
||||
return NTLSTATUS_NO_MEMORY;
|
||||
|
||||
clock->Header.Destroy = KiDisplayClockDestroy;
|
||||
LIST_INIT(&clock->Link);
|
||||
clock->PeriodTicks = KiDisplayClockMillisecondsToTicks(periodMilliseconds);
|
||||
uint64_t now = KiDisplayClockReadSchedulerTick();
|
||||
clock->NextTick = (UINT64_MAX - now < clock->PeriodTicks) ? UINT64_MAX : now + clock->PeriodTicks;
|
||||
|
||||
KSTATUS s = KeEventCreate(EventAutoReset, 0, &clock->Event);
|
||||
if (!KSUCCESS(s)) {
|
||||
ObDereferenceObject(&clock->Header);
|
||||
return s;
|
||||
}
|
||||
|
||||
uintptr_t flags = KeAcquireSpinlock(&gDisplayClockLock);
|
||||
clock->LastFrameGeneration = gDisplayFrameGeneration;
|
||||
clock->Linked = 1;
|
||||
ListInsertTail(&gDisplayClockList, &clock->Link);
|
||||
KeReleaseSpinlock(&gDisplayClockLock, flags);
|
||||
|
||||
*outClock = clock;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
DISPLAY_API EVENT* KeDisplayClockWaitEvent(DISPLAY_CLOCK_OBJECT* clock) {
|
||||
if (!clock)
|
||||
return (EVENT*)0;
|
||||
return clock->Event;
|
||||
}
|
||||
|
||||
// Signals every display clock once for a provider-owned frame generation.
|
||||
static void KiDisplaySignalFrameGeneration(uint64_t generation) {
|
||||
for (;;) {
|
||||
EVENT* events[DISPLAY_CLOCK_SIGNAL_BATCH];
|
||||
uint32_t count = 0;
|
||||
|
||||
uintptr_t flags = KeAcquireSpinlock(&gDisplayClockLock);
|
||||
LIST_ENTRY* node = gDisplayClockList.Fwd;
|
||||
while (node != &gDisplayClockList && count < DISPLAY_CLOCK_SIGNAL_BATCH) {
|
||||
DISPLAY_CLOCK_OBJECT* clock = CONTAINING_RECORD(node, DISPLAY_CLOCK_OBJECT, Link);
|
||||
node = node->Fwd;
|
||||
|
||||
if (!clock->Linked || !clock->Event || clock->LastFrameGeneration == generation)
|
||||
continue;
|
||||
|
||||
if (!ObTryReferenceObject(&clock->Event->Header))
|
||||
continue;
|
||||
clock->LastFrameGeneration = generation;
|
||||
events[count++] = clock->Event;
|
||||
}
|
||||
KeReleaseSpinlock(&gDisplayClockLock, flags);
|
||||
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
KeSignalEvent(events[i]);
|
||||
ObDereferenceObject(&events[i]->Header);
|
||||
}
|
||||
|
||||
if (count < DISPLAY_CLOCK_SIGNAL_BATCH)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Publishes a frame-ready event from the current primary display provider.
|
||||
DISPLAY_API void KeDisplayNotifyFrame(DISPLAY_DEVICE* device) {
|
||||
if (!device || !_InterlockedCompareExchange(&gDisplayClockInitialized, 1, 1))
|
||||
return;
|
||||
|
||||
uintptr_t displayFlags = KeAcquireSpinlock(&gDisplayLock);
|
||||
bool accepted = device == gPrimaryDisplay &&
|
||||
(device->Caps & DISPLAY_CAP_FRAME_EVENT) != 0;
|
||||
KeReleaseSpinlock(&gDisplayLock, displayFlags);
|
||||
if (!accepted)
|
||||
return;
|
||||
|
||||
uintptr_t flags = KeAcquireSpinlock(&gDisplayClockLock);
|
||||
uint64_t generation = ++gDisplayFrameGeneration;
|
||||
if (generation == 0)
|
||||
generation = ++gDisplayFrameGeneration;
|
||||
KeReleaseSpinlock(&gDisplayClockLock, flags);
|
||||
|
||||
KiDisplaySignalFrameGeneration(generation);
|
||||
}
|
||||
|
||||
DISPLAY_API void KeDisplayTick(void) {
|
||||
if (!_InterlockedCompareExchange(&gDisplayClockInitialized, 1, 1))
|
||||
return;
|
||||
|
||||
uintptr_t displayFlags = KeAcquireSpinlock(&gDisplayLock);
|
||||
bool providerOwnsFrames = gPrimaryDisplay &&
|
||||
(gPrimaryDisplay->Caps & DISPLAY_CAP_FRAME_EVENT) != 0;
|
||||
KeReleaseSpinlock(&gDisplayLock, displayFlags);
|
||||
if (providerOwnsFrames)
|
||||
return;
|
||||
|
||||
uint64_t now = KiDisplayClockReadSchedulerTick();
|
||||
for (;;) {
|
||||
EVENT* events[DISPLAY_CLOCK_SIGNAL_BATCH];
|
||||
uint32_t count = 0;
|
||||
|
||||
uintptr_t flags = KeAcquireSpinlock(&gDisplayClockLock);
|
||||
LIST_ENTRY* node = gDisplayClockList.Fwd;
|
||||
while (node != &gDisplayClockList && count < DISPLAY_CLOCK_SIGNAL_BATCH) {
|
||||
DISPLAY_CLOCK_OBJECT* clock = CONTAINING_RECORD(node, DISPLAY_CLOCK_OBJECT, Link);
|
||||
node = node->Fwd;
|
||||
|
||||
if (!clock->Linked || clock->NextTick > now || !clock->Event)
|
||||
continue;
|
||||
|
||||
if (!ObTryReferenceObject(&clock->Event->Header))
|
||||
continue;
|
||||
uint64_t next = now + clock->PeriodTicks;
|
||||
clock->NextTick = next < now ? UINT64_MAX : next;
|
||||
events[count++] = clock->Event;
|
||||
}
|
||||
KeReleaseSpinlock(&gDisplayClockLock, flags);
|
||||
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
KeSignalEvent(events[i]);
|
||||
ObDereferenceObject(&events[i]->Header);
|
||||
}
|
||||
|
||||
if (count < DISPLAY_CLOCK_SIGNAL_BATCH)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DISPLAY_API KSTATUS KeDisplayRegisterDevice(DISPLAY_DEVICE* device) {
|
||||
if (!device || !device->Name || !device->Ops || !device->Ops->GetMode)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
Reference in new issue
Block a user