The Pedigree Project 0.1
IrqHandlerRegistry.h
1/*
2 * Copyright (c) 2026, Pedigree Developers
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted.
6 */
7
8#ifndef PEDIGREE_KERNEL_MACHINE_IRQHANDLERREGISTRY_H
9#define PEDIGREE_KERNEL_MACHINE_IRQHANDLERREGISTRY_H
10#include "pedigree/kernel/Spinlock.h"
11#include "pedigree/kernel/compiler.h"
12#include "pedigree/kernel/machine/IrqManager.h"
13#include "pedigree/kernel/process/AtomicStateCleanup.h"
14#include "pedigree/kernel/processor/state_forward.h"
15#include "pedigree/kernel/processor/types.h"
16
17#include <config.h>
18
19class HardIrqHandler;
20class IrqHandler;
21class IrqHandlerBase;
22class Thread;
23enum class HardIrqDisposition;
24
35class EXPORTED_PUBLIC IrqHandlerRegistry {
36 public:
38 bool handled;
39 bool allowRearm;
40 };
41
43 size_t epoch;
44 size_t occurrenceEpoch;
45 size_t readerToken;
46 };
47
49 AdmissionCutoff hard;
50 AdmissionCutoff threaded;
51 };
52
53 enum class UnregisterResult {
54 Completed,
55 Deferred,
56 Rejected,
57 NotFound,
58 };
59
60 enum class LineMode {
61 Empty,
62 Threaded,
63 HardOnly,
64 Mixed,
65 };
66
70 : mutationGeneration(0),
71 handlerCount(0),
72 mode(LineMode::Empty),
73 policyConfigured(false),
74 trigger(IrqTrigger::Edge),
75 controllerAck(IrqControllerAck::None),
76 lineRelease(IrqLineRelease::AfterHardStage) {}
77
78 size_t mutationGeneration;
79 size_t handlerCount;
80 LineMode mode;
81 bool policyConfigured;
82 IrqTrigger trigger;
83 IrqControllerAck controllerAck;
84 IrqLineRelease lineRelease;
85 };
86
88
90 bool registerThreadedHandler(uint8_t irq, IrqHandler* handler);
91
93 bool registerThreadedHandler(uint8_t irq, IrqHandler* handler, const IrqPolicy& policy);
94
96 bool registerHardHandler(uint8_t irq, HardIrqHandler* handler);
97
99 bool registerHardHandler(uint8_t irq, HardIrqHandler* handler, const IrqPolicy& policy);
100
113 UnregisterResult unregisterHandler(uint8_t irq, IrqHandlerBase* handler);
114
121 UnregisterResult unregisterHandler(uint8_t irq, IrqHandlerBase* handler,
122 LineMode& removedDelivery);
123
131 bool dispatchHard(uint8_t irq, InterruptState& state, HardIrqDisposition& disposition,
132 HardIrqHandler* onlyHandler = nullptr, size_t dispatchGeneration = 0);
133 bool dispatchHard(uint8_t irq, InterruptState& state, HardIrqDisposition& disposition,
134 HardIrqHandler* onlyHandler, size_t dispatchGeneration,
135 AdmissionCutoff admissionCutoff);
136
138 bool hardLineQuarantined(uint8_t irq) const;
139
150 bool publishThreadedDispatch(uint8_t irq, size_t dispatchGeneration);
151 bool publishThreadedDispatch(uint8_t irq, size_t dispatchGeneration,
152 AdmissionCutoff admissionCutoff);
153
158 bool captureAdmissionCutoff(uint8_t irq, AdmissionCutoff& cutoff);
159
167 bool captureMixedAdmissionCutoffs(uint8_t irq, MixedAdmissionCutoffs& cutoffs);
168
170 void releaseAdmissionCutoff(AdmissionCutoff admissionCutoff);
171
173 void invalidateThreadedLine(uint8_t irq, size_t throughGeneration);
174
182 void invalidateThreadedGenerationFromInterrupt(uint8_t irq, size_t throughGeneration);
183
194 bool dispatchThreaded(uint8_t irq, size_t dispatchGeneration, ThreadedDispatchResult& result,
195 IrqHandler* onlyHandler = nullptr);
196
197 size_t handlerCount(uint8_t irq);
198
200 LineMode lineMode(uint8_t irq);
201
209 bool snapshotLineConfiguration(uint8_t irq, LineConfiguration& configuration) const;
210
218 size_t hardDispatchState(uint8_t irq, size_t& exactGeneration) const;
219
228 size_t threadedDispatchState(uint8_t irq, uintptr_t& exactHandlerIdentity) const;
229
230#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
231 enum class HandlerHazardStage {
232 BeforeClaim,
233 Claimed,
234 Committed,
235 Released,
236 RetirementBoundaryPublished,
237 BeforeClaimFinalization,
238 FinalizationContended,
239 BeforeActionMutationPin,
240 QuiescedObserved,
241 BeforePendingExchange,
242 PendingExchanged,
243 BeforeQuiescedExchange,
244 QuiescedExchanged,
245 };
246
247 enum class OccurrenceCaptureStage {
248 BankZeroClaimed,
249 BankOneClaimed,
250 EpochSampled,
251 UnusedBankReleased,
252 };
253
254 using HandlerPinHook = void (*)(IrqHandlerBase*);
255 using HandlerPrePinHook = void (*)(IrqHandlerBase*);
256 using HandlerHazardHook = void (*)(IrqHandlerBase*, HandlerHazardStage);
257 using DispatchAbandonHook = void (*)(void*, bool);
258 using MutationLockHook = void (*)();
259 using OccurrenceCaptureHook = void (*)(IrqHandlerRegistry*, uint8_t, OccurrenceCaptureStage,
260 size_t);
261
262 void setHandlerPinHook(HandlerPinHook hook);
263 void setHandlerPrePinHook(HandlerPrePinHook hook);
264 void setHandlerHazardHook(HandlerHazardHook hook);
265 void setDispatchAbandonHook(DispatchAbandonHook hook);
266 void setOccurrenceCaptureHookForTest(OccurrenceCaptureHook hook);
267 void withMutationLockForTest(MutationLockHook hook);
268 void withMutationEpochForTest(MutationLockHook hook);
269 size_t activeDispatchCountForTest(IrqHandlerBase* handler);
270 size_t claimedDispatchCountForOwnerForTest(void* owner);
271 bool containsHandlerForTest(uint8_t irq, IrqHandlerBase* handler);
272 size_t tombstoneCountForTest(uint8_t irq) const;
273 size_t threadedActionMutationWriterCountForTest() const;
274 bool setThreadedActionLanesForTest(IrqHandlerBase* handler, size_t pendingGeneration,
275 size_t claimedGeneration, size_t quiescedGeneration);
276 bool consumeThreadedQuiescedForTest(IrqHandlerBase* handler, size_t generation);
277 bool publishControllerQuiescedForTest(IrqHandlerBase* handler, uint8_t irq, size_t generation);
278#endif
279
280 private:
281 enum class Delivery : size_t {
282 Threaded,
283 HardOnly,
284 };
285
286 static constexpr size_t MaxHandlerSlots = 64;
287 static constexpr size_t MaxActiveDispatches = 64;
288 static constexpr size_t IrqCount = 256;
289 // Bucket collisions delay tombstone reuse but cannot admit a stale slot.
290 static constexpr size_t GraceBucketCount = 16;
291 static constexpr uint8_t InvalidIrq = 0xFF;
292
293 enum class SlotMode : size_t {
294 Empty = 0,
295 Enabled,
296 Draining,
297 Cancelling,
298 Closed,
299 Retiring,
300 Tombstone,
301 };
302
303 enum class QuiescedLane : size_t {
305 Callback,
306 Retirement,
307 Count,
308 };
309
310 static constexpr size_t QuiescedLaneCount = static_cast<size_t>(QuiescedLane::Count);
311
312 static constexpr size_t ModeBits = 3;
313 static constexpr size_t ModeMask = (1 << ModeBits) - 1;
314 static constexpr size_t DeliveryBits = 1;
315 static constexpr size_t DeliveryShift = ModeBits;
316 static constexpr size_t DeliveryMask = ((1 << DeliveryBits) - 1) << DeliveryShift;
317 static constexpr size_t IrqShift = DeliveryShift + DeliveryBits;
318 static constexpr size_t IrqMask = 0xFF << IrqShift;
319 static constexpr size_t GenerationShift = IrqShift + 8;
320 static constexpr size_t MaximumPublicationGeneration = ~static_cast<size_t>(0) >> GenerationShift;
321
322 static constexpr size_t PolicyValid = 1U << 0;
323 static constexpr size_t PolicyTriggerShift = 1;
324 static constexpr size_t PolicyControllerAckShift = 3;
325 static constexpr size_t PolicyLineReleaseShift = 5;
326 static constexpr size_t PolicyTriggerMask = 3U << PolicyTriggerShift;
327 static constexpr size_t PolicyControllerAckMask = 3U << PolicyControllerAckShift;
328 static constexpr size_t PolicyLineReleaseMask = 1U << PolicyLineReleaseShift;
329 static constexpr size_t PolicyMixedCompatibilityMask =
330 PolicyTriggerMask | PolicyControllerAckMask;
331 static constexpr size_t LineSnapshotAttempts = 2;
332
333 struct HandlerSlot;
334
337 : token(nullptr),
338 generation(0),
339 owner(nullptr),
340 admittedPublication(0),
341 controllerGeneration(0),
342 slot(nullptr),
343 callback(0) {}
344
345 void* token;
346 size_t generation;
347 void* owner;
348 size_t admittedPublication;
349 size_t controllerGeneration;
350 HandlerSlot* slot;
351 size_t callback;
352 };
353
354 struct HandlerSlot {
356 : handler(nullptr),
357 publication(0),
358 policy(0),
359 admissionEpoch(0),
360 pendingThreadedGeneration(0),
361 claimedThreadedGeneration(0),
362 quiescedThreadedGenerations(),
363 retirementEpoch(0),
364 hardHandoffState(0),
365 finalizationGate(0) {}
366
367 IrqHandlerBase* handler;
368 size_t publication;
369 size_t policy;
370 size_t admissionEpoch;
371 size_t pendingThreadedGeneration;
372 size_t claimedThreadedGeneration;
373 size_t quiescedThreadedGenerations[QuiescedLaneCount];
374 size_t retirementEpoch;
377 size_t finalizationGate;
378 };
379
381 DispatchCleanup(IrqHandlerRegistry* registry, HandlerSlot* handlerSlot, void* dispatchOwner,
382 size_t admittedPublication, bool isCallback = true)
383 : registry(registry),
384 slot(handlerSlot),
385 owner(dispatchOwner),
386 publication(admittedPublication),
387 callback(isCallback),
388 previousDeviceHardIrqDepth(0),
389 restoreDeviceHardIrqDepth(false),
390 previousInterruptState(false),
391 restoreInterruptState(false),
392 cleanup() {}
393
394 IrqHandlerRegistry* registry;
395 HandlerSlot* slot;
396 void* owner;
397 size_t publication;
398 bool callback;
399 size_t previousDeviceHardIrqDepth;
400 bool restoreDeviceHardIrqDepth;
401 bool previousInterruptState;
402 bool restoreInterruptState;
404 };
405
408 : registry(owner), cutoff(admissionCutoff), thread(nullptr), ownsCutoff(false), cleanup() {}
409
410 IrqHandlerRegistry* registry;
411 AdmissionCutoff cutoff;
412 Thread* thread;
413 bool ownsCutoff;
415 };
416
419 : registry(owner), thread(nullptr), cleanup() {}
420
421 IrqHandlerRegistry* registry;
422 Thread* thread;
424 };
425
426 static size_t makePublication(size_t generation, uint8_t irq, SlotMode mode, Delivery delivery);
427 static size_t generationOf(size_t publication);
428 static uint8_t irqOf(size_t publication);
429 static SlotMode modeOf(size_t publication);
430 static Delivery deliveryOf(size_t publication);
431 static bool generationReached(size_t current, size_t target);
432 static size_t encodePolicy(const IrqPolicy* policy);
433 static void decodePolicy(size_t policy, LineConfiguration& configuration);
434 static bool mixedPoliciesCompatible(size_t first, size_t second);
435 static size_t effectiveMixedPolicy(size_t hard, size_t threaded);
436 static LineMode lineModeForDelivery(Delivery delivery);
437
438 bool registerHandler(uint8_t irq, IrqHandlerBase* handler, Delivery delivery, size_t policy);
439
440 bool acquireOccurrenceReaderLeases(uint8_t irq, size_t readerBank, size_t count);
441 void releaseOccurrenceReaderLeases(uint8_t irq, size_t readerBank, size_t count);
442 void beginAdmissionCutoffCleanup(AdmissionCutoffCleanup& cleanup);
443 void finishAdmissionCutoffCleanup(AdmissionCutoffCleanup& cleanup);
444 static void abandonAdmissionCutoff(void* context);
445
446 void beginMutation();
447 void finishMutation();
448 void beginThreadedActionMutation(ThreadedActionMutationCleanup& cleanup);
449 void finishThreadedActionMutation(ThreadedActionMutationCleanup& cleanup);
450 void completeThreadedActionMutation();
451 static void abandonThreadedActionMutation(void* context);
452
453 bool retireSlot(HandlerSlot& slot, size_t expectedPublication, IrqHandlerBase* expectedHandler);
454 bool retireSlotOrObserveClosed(HandlerSlot& slot, size_t expectedPublication,
455 IrqHandlerBase* expectedHandler);
456 bool closeSlotAdmission(HandlerSlot& slot, size_t expectedPublication, size_t& closedPublication);
457 void tryReclaimTombstones(uint8_t irq);
458 bool occurrencePrecedesRetirement(const HandlerSlot& slot, AdmissionCutoff admissionCutoff) const;
459 ActiveDispatch* publishDispatch(HandlerSlot& slot, void* owner, void* token,
460 size_t admittedPublication, size_t controllerGeneration,
461 bool callback = true);
462 bool unpublishDispatch(void* token, HandlerSlot& slot, size_t admittedPublication, bool required);
463 static void abandonDispatch(void* context);
464 static void restoreDispatchInterruptState(DispatchCleanup& dispatch);
465 static bool canWaitForActionFinalization();
466 bool acquireFinalizationGate(HandlerSlot& slot, bool canWait);
467 static void releaseFinalizationGate(HandlerSlot& slot);
468 bool pinActionMutation(HandlerSlot& slot, size_t publication, DispatchCleanup& cleanup,
469 Thread* thread);
470 void unpinActionMutation(HandlerSlot& slot, size_t publication, DispatchCleanup& cleanup,
471 Thread* thread);
472 bool hasActiveDispatch(HandlerSlot& slot, size_t admittedPublication) const;
473 bool findCurrentDispatch(void* owner, HandlerSlot* target, size_t targetPublication,
474 bool& callbackContext) const;
475 static void* currentDispatchOwner();
476 bool threadedGenerationValid(uint8_t irq, size_t generation) const;
477 void publishSlotQuiesced(HandlerSlot& slot, uint8_t irq, size_t dispatchGeneration,
478 QuiescedLane lane);
479 void publishSlotQuiescedValue(HandlerSlot& slot, uint8_t irq, size_t dispatchGeneration,
480 QuiescedLane lane);
481 static size_t* quiescedLane(HandlerSlot& slot, QuiescedLane lane);
482 static const size_t* quiescedLane(const HandlerSlot& slot, QuiescedLane lane);
483 static bool hasQuiescedGeneration(const HandlerSlot& slot);
484
485 HandlerSlot m_Handlers[MaxHandlerSlots];
486 ActiveDispatch m_ActiveDispatches[MaxActiveDispatches];
488 size_t m_HardHandoffEpochs[GraceBucketCount];
489 size_t m_ThreadedInvalidationGenerations[IrqCount];
490 size_t m_ThreadedActionMutationGeneration;
491 size_t m_ThreadedActionMutationWriters;
492 size_t m_OccurrenceEpochs[GraceBucketCount];
493 size_t m_OccurrenceReaders[GraceBucketCount][2];
494 size_t m_OccurrenceBoundaryLocks[GraceBucketCount];
495 Spinlock m_HandlerLock;
496 size_t m_AdmissionEpoch;
497 size_t m_MutationGeneration;
498 size_t m_MutationWriters;
499
500#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
501 void observeOccurrenceCaptureForTest(uint8_t irq, OccurrenceCaptureStage stage,
502 size_t occurrenceEpoch);
503 HandlerPinHook m_HandlerPinHook;
504 HandlerPrePinHook m_HandlerPrePinHook;
505 HandlerHazardHook m_HandlerHazardHook;
506 DispatchAbandonHook m_DispatchAbandonHook;
507 OccurrenceCaptureHook m_OccurrenceCaptureHook;
508#endif
509};
510
511#endif
HardIrqDisposition
Definition IrqHandler.h:44