20#include "pedigree/kernel/LockGuard.h"
21#include "pedigree/kernel/Log.h"
22#include "pedigree/kernel/process/TerminationDeferral.h"
23#include "pedigree/kernel/process/Thread.h"
24#include "pedigree/kernel/processor/PageFaultHandler.h"
25#include "pedigree/kernel/processor/Processor.h"
26#include "pedigree/kernel/processor/ProcessorInformation.h"
27#include "pedigree/kernel/processor/state.h"
29MemoryTrapHandler::~MemoryTrapHandler() =
default;
31#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
32PageFaultHandler::HandlerPinHook PageFaultHandler::m_HandlerPinHook =
nullptr;
33PageFaultHandler::HandlerPrePinHook PageFaultHandler::m_HandlerPrePinHook =
nullptr;
34PageFaultHandler::AtomicDrainHook PageFaultHandler::m_AtomicDrainHook =
nullptr;
38 : m_Handlers(), m_ActiveDispatches(), m_DispatchWaiters(), m_HandlerLock(false) {}
40size_t PageFaultHandler::makePublication(
size_t generation, SlotMode mode) {
41 return (generation << GenerationShift) |
static_cast<size_t>(mode);
44size_t PageFaultHandler::generationOf(
size_t publication) {
45 return publication >> GenerationShift;
48PageFaultHandler::SlotMode PageFaultHandler::modeOf(
size_t publication) {
49 return static_cast<SlotMode
>(publication & ModeMask);
52bool PageFaultHandler::retireSlot(HandlerSlot& slot,
size_t expectedPublication,
54 const size_t retiringPublication =
55 makePublication(generationOf(expectedPublication), SlotMode::Retiring);
56 if (!__atomic_compare_exchange_n(&slot.publication, &expectedPublication, retiringPublication,
57 false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
62 if (handler != expectedHandler) {
63 size_t expectedRetiringPublication = retiringPublication;
64 __atomic_compare_exchange_n(&slot.publication, &expectedRetiringPublication,
65 expectedPublication,
false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
69 __atomic_store_n(&slot.handler,
nullptr, __ATOMIC_RELEASE);
70 __atomic_store_n(&slot.publication,
71 makePublication(generationOf(retiringPublication), SlotMode::Empty),
76bool PageFaultHandler::publishDispatch(HandlerSlot& slot,
void* owner, DispatchCleanup& cleanup) {
77 void* token = &cleanup;
78 for (
size_t i = 0; i < MaxActiveDispatches; ++i) {
79 ActiveDispatch& dispatch = m_ActiveDispatches[i];
81 __atomic_store_n(&cleanup.activeDispatch, &dispatch, __ATOMIC_RELAXED);
82 void* expectedToken =
nullptr;
83 if (__atomic_compare_exchange_n(&dispatch.token, &expectedToken, token,
false, __ATOMIC_SEQ_CST,
85 __atomic_add_fetch(&dispatch.generation,
static_cast<size_t>(1), __ATOMIC_ACQ_REL);
86 __atomic_store_n(&dispatch.owner, owner, __ATOMIC_RELAXED);
90 __atomic_store_n(&dispatch.slot, &slot, __ATOMIC_SEQ_CST);
98void PageFaultHandler::unpublishDispatch(DispatchCleanup& cleanup) {
99 ActiveDispatch* dispatch = __atomic_load_n(&cleanup.activeDispatch, __ATOMIC_RELAXED);
100 void* token = &cleanup;
102 if (!dispatch || __atomic_load_n(&dispatch->token, __ATOMIC_ACQUIRE) != token) {
106 HandlerSlot* releasedSlot = __atomic_load_n(&dispatch->slot, __ATOMIC_SEQ_CST);
107 __atomic_store_n(&dispatch->slot,
nullptr, __ATOMIC_SEQ_CST);
108 __atomic_store_n(&dispatch->owner,
nullptr, __ATOMIC_RELAXED);
109 __atomic_store_n(&dispatch->token,
nullptr, __ATOMIC_RELEASE);
114 const size_t publication = __atomic_load_n(&releasedSlot->publication, __ATOMIC_SEQ_CST);
115 const SlotMode mode = modeOf(publication);
116 if (mode != SlotMode::Draining && mode != SlotMode::Deferred) {
121 if (hasActiveDispatch(*releasedSlot)) {
125 const size_t drainGeneration = generationOf(publication);
126 auto guard = m_DispatchWaiters.acquire();
127 const size_t finalPublication = __atomic_load_n(&releasedSlot->publication, __ATOMIC_SEQ_CST);
128 if (generationOf(finalPublication) == drainGeneration &&
129 modeOf(finalPublication) == SlotMode::Deferred && !hasActiveDispatch(*releasedSlot)) {
130 MemoryTrapHandler* handler = __atomic_load_n(&releasedSlot->handler, __ATOMIC_ACQUIRE);
132 retireSlot(*releasedSlot, finalPublication, handler);
136 guard.wakeAll(WaitQueue::WakeReason::Signalled,
140void PageFaultHandler::abandonedHandlerCleanup(
void* context) {
141 DispatchCleanup* dispatch =
reinterpret_cast<DispatchCleanup*
>(context);
142 if (dispatch && dispatch->registry) {
143 dispatch->registry->unpublishDispatch(*dispatch);
147bool PageFaultHandler::hasActiveDispatch(HandlerSlot& target)
const {
148 for (
size_t i = 0; i < MaxActiveDispatches; ++i) {
149 const ActiveDispatch& dispatch = m_ActiveDispatches[i];
150 void* token = __atomic_load_n(&dispatch.token, __ATOMIC_ACQUIRE);
155 const size_t generation = __atomic_load_n(&dispatch.generation, __ATOMIC_ACQUIRE);
156 HandlerSlot* slot = __atomic_load_n(&dispatch.slot, __ATOMIC_SEQ_CST);
157 if (slot == &target && __atomic_load_n(&dispatch.token, __ATOMIC_ACQUIRE) == token &&
158 __atomic_load_n(&dispatch.generation, __ATOMIC_ACQUIRE) == generation) {
165bool PageFaultHandler::findCurrentDispatch(
void* owner, HandlerSlot* target,
166 bool& callbackContext)
const {
167 callbackContext =
false;
168 bool foundTarget =
false;
169 for (
size_t i = 0; i < MaxActiveDispatches; ++i) {
170 const ActiveDispatch& dispatch = m_ActiveDispatches[i];
171 void* token = __atomic_load_n(&dispatch.token, __ATOMIC_ACQUIRE);
176 const size_t generation = __atomic_load_n(&dispatch.generation, __ATOMIC_ACQUIRE);
177 void* dispatchOwner = __atomic_load_n(&dispatch.owner, __ATOMIC_RELAXED);
178 HandlerSlot* slot = __atomic_load_n(&dispatch.slot, __ATOMIC_SEQ_CST);
179 if (__atomic_load_n(&dispatch.token, __ATOMIC_ACQUIRE) != token ||
180 __atomic_load_n(&dispatch.generation, __ATOMIC_ACQUIRE) != generation) {
184 if (dispatchOwner == owner && slot) {
185 callbackContext =
true;
186 foundTarget |= slot == target;
192void* PageFaultHandler::currentDispatchOwner() {
194 Thread* thread = information.getCurrentThread();
195 return thread ?
static_cast<void*
>(thread) : static_cast<void*>(&information);
203 bool callbackContext =
false;
204 findCurrentDispatch(currentDispatchOwner(),
nullptr, callbackContext);
205 if (callbackContext) {
210 for (
size_t i = 0; i < MaxMemoryTrapHandlers; ++i) {
212 const size_t publication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
213 if (__atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE) == pHandler &&
214 modeOf(publication) != SlotMode::Empty) {
219 for (
size_t i = 0; i < MaxMemoryTrapHandlers; ++i) {
221 const size_t publication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
222 if (modeOf(publication) == SlotMode::Empty &&
223 !__atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE)) {
224 const size_t generation = generationOf(publication) + 1;
228 __atomic_store_n(&slot.handler, pHandler, __ATOMIC_RELEASE);
229 __atomic_store_n(&slot.publication, makePublication(generation, SlotMode::Enabled),
243 void* owner = currentDispatchOwner();
246 bool callbackContext =
false;
247 findCurrentDispatch(owner,
nullptr, callbackContext);
252 if (!canYield || callbackContext) {
253 for (
size_t i = 0; i < MaxMemoryTrapHandlers; ++i) {
255 size_t publication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
256 if (modeOf(publication) == SlotMode::Empty ||
257 __atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE) != pHandler) {
261 bool currentTargetDispatch =
false;
262 const bool selfUnregister = findCurrentDispatch(owner, &slot, currentTargetDispatch);
263 if (selfUnregister) {
265 const SlotMode mode = modeOf(publication);
266 if (mode == SlotMode::Deferred || mode == SlotMode::Empty || mode == SlotMode::Retiring) {
269 if (mode != SlotMode::Enabled && mode != SlotMode::Draining) {
273 const size_t deferredPublication =
274 makePublication(generationOf(publication), SlotMode::Deferred);
275 if (__atomic_compare_exchange_n(&slot.publication, &publication, deferredPublication,
276 false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
279 if (__atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE) != pHandler) {
285 if (callbackContext || modeOf(publication) != SlotMode::Enabled) {
289 const size_t drainingPublication =
290 makePublication(generationOf(publication), SlotMode::Draining);
291 if (!__atomic_compare_exchange_n(&slot.publication, &publication, drainingPublication,
false,
292 __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
296#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
297 AtomicDrainHook drainHook = __atomic_load_n(&m_AtomicDrainHook, __ATOMIC_ACQUIRE);
303 if (hasActiveDispatch(slot)) {
304 size_t expectedPublication = drainingPublication;
305 __atomic_compare_exchange_n(
306 &slot.publication, &expectedPublication,
307 makePublication(generationOf(drainingPublication), SlotMode::Enabled),
false,
308 __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
312 return retireSlot(slot, drainingPublication, pHandler);
323 size_t publication = 0;
324 for (
size_t i = 0; i < MaxMemoryTrapHandlers; ++i) {
325 const size_t candidatePublication =
326 __atomic_load_n(&
m_Handlers[i].publication, __ATOMIC_SEQ_CST);
327 if (modeOf(candidatePublication) != SlotMode::Empty &&
328 __atomic_load_n(&
m_Handlers[i].handler, __ATOMIC_ACQUIRE) == pHandler) {
330 publication = candidatePublication;
335 if (!slot || modeOf(publication) != SlotMode::Enabled) {
340 size_t expectedPublication = publication;
341 const size_t drainingPublication = makePublication(generationOf(publication), SlotMode::Draining);
342 if (!__atomic_compare_exchange_n(&slot->publication, &expectedPublication, drainingPublication,
343 false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
350 const size_t drainGeneration = generationOf(drainingPublication);
352 auto guard = m_DispatchWaiters.acquire();
353 const size_t finalPublication = __atomic_load_n(&slot->publication, __ATOMIC_SEQ_CST);
354 MemoryTrapHandler* finalHandler = __atomic_load_n(&slot->handler, __ATOMIC_ACQUIRE);
355 if (generationOf(finalPublication) != drainGeneration) {
359 const SlotMode finalMode = modeOf(finalPublication);
360 if (finalMode == SlotMode::Empty) {
361 return finalHandler != pHandler;
363 if (finalHandler != pHandler ||
364 (finalMode != SlotMode::Draining && finalMode != SlotMode::Deferred)) {
367 if (!hasActiveDispatch(*slot)) {
368 if (retireSlot(*slot, finalPublication, pHandler)) {
374 const WaitQueue::WakeReason reason =
375 guard.waitForCompletion(
WaitQueue::Channel(slot, drainGeneration), Thread::CallbackDrain,
376 reinterpret_cast<uintptr_t
>(pHandler));
381bool PageFaultHandler::dispatchHandlers(InterruptState& state, uintptr_t address,
bool bIsWrite,
383 for (
size_t i = 0; i < MaxMemoryTrapHandlers; ++i) {
385 const size_t publication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
386 if (modeOf(publication) != SlotMode::Enabled) {
391 if (!handler || (pOnlyHandler && handler != pOnlyHandler)) {
395#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
396 HandlerPrePinHook prePinHook = __atomic_load_n(&m_HandlerPrePinHook, __ATOMIC_ACQUIRE);
403 DispatchCleanup dispatchCleanup(
this);
407 thread->armAtomicStateCleanup(dispatchCleanup.cleanup, abandonedHandlerCleanup,
411 if (!publishDispatch(slot, currentDispatchOwner(), dispatchCleanup)) {
413 thread->disarmAtomicStateCleanup(dispatchCleanup.cleanup);
415 FATAL_NOLOCK(
"Page-fault callback hazard table exhausted.");
419 if (__atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST) != publication ||
420 __atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE) != handler) {
421 unpublishDispatch(dispatchCleanup);
423 thread->disarmAtomicStateCleanup(dispatchCleanup.cleanup);
428#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
429 HandlerPinHook hook = __atomic_load_n(&m_HandlerPinHook, __ATOMIC_ACQUIRE);
435 const bool handled = handler->
trap(state, address, bIsWrite, bWasPresent);
436 unpublishDispatch(dispatchCleanup);
438 thread->disarmAtomicStateCleanup(dispatchCleanup.cleanup);
449#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
450void PageFaultHandler::setHandlerPinHook(HandlerPinHook hook) {
451 __atomic_store_n(&m_HandlerPinHook, hook, __ATOMIC_RELEASE);
454void PageFaultHandler::setHandlerPrePinHook(HandlerPrePinHook hook) {
455 __atomic_store_n(&m_HandlerPrePinHook, hook, __ATOMIC_RELEASE);
458void PageFaultHandler::setAtomicDrainHook(AtomicDrainHook hook) {
459 __atomic_store_n(&m_AtomicDrainHook, hook, __ATOMIC_RELEASE);
462void PageFaultHandler::withMutationLockForTest(MutationLockHook hook) {
471 InterruptState state;
472 return dispatchHandlers(state, 0,
false,
false, pHandler);
475size_t PageFaultHandler::activeDispatchCountForTest(
MemoryTrapHandler* pHandler) {
477 for (
size_t i = 0; i < MaxActiveDispatches; ++i) {
478 ActiveDispatch& dispatch = m_ActiveDispatches[i];
479 void* token = __atomic_load_n(&dispatch.token, __ATOMIC_ACQUIRE);
483 const size_t generation = __atomic_load_n(&dispatch.generation, __ATOMIC_ACQUIRE);
484 HandlerSlot* slot = __atomic_load_n(&dispatch.slot, __ATOMIC_SEQ_CST);
485 MemoryTrapHandler* handler = slot ? __atomic_load_n(&slot->handler, __ATOMIC_ACQUIRE) : nullptr;
486 if (handler == pHandler && __atomic_load_n(&dispatch.token, __ATOMIC_ACQUIRE) == token &&
487 __atomic_load_n(&dispatch.generation, __ATOMIC_ACQUIRE) == generation) {
virtual bool trap(InterruptState &state, uintptr_t address, bool bIsWrite, bool bWasPresent)=0
HandlerSlot m_Handlers[MaxMemoryTrapHandlers]
EXPORTED_PUBLIC bool unregisterHandler(MemoryTrapHandler *pHandler)
EXPORTED_PUBLIC bool registerHandler(MemoryTrapHandler *pHandler)
PageFaultHandler() INITIALISATION_ONLY
static bool getInterrupts()
static ProcessorInformation & information()
bool acquire(bool recurse=false, bool safe=true)