|
16 | 16 |
|
17 | 17 | #pragma once |
18 | 18 |
|
| 19 | +#include <io.h> |
19 | 20 | #include <stdint.h> |
20 | 21 | #include <stdbool.h> |
21 | 22 |
|
| 23 | + |
22 | 24 | /** |
23 | 25 | * @typedef spinlock_t |
24 | 26 | * @brief Opaque 32-bit integer used as a spinlock state. |
@@ -76,3 +78,96 @@ static inline void unlock_spinlock(spinlock_t* lock) { |
76 | 78 | static inline bool try_lock_spinlock(spinlock_t* lock) { |
77 | 79 | return !__atomic_test_and_set(lock, __ATOMIC_ACQUIRE); |
78 | 80 | } |
| 81 | + |
| 82 | +/** |
| 83 | + * @brief Read the current RFLAGS register. |
| 84 | + * |
| 85 | + * Captures the full 64-bit RFLAGS value from the CPU. |
| 86 | + * |
| 87 | + * @return The current value of the RFLAGS register. |
| 88 | + */ |
| 89 | +static inline uint64_t read_rflags(void) { |
| 90 | + uint64_t flags; |
| 91 | + __asm__ volatile("pushfq; pop %0" : "=r"(flags)); |
| 92 | + return flags; |
| 93 | +} |
| 94 | + |
| 95 | +/** |
| 96 | + * @brief Write a value into the RFLAGS register. |
| 97 | + * |
| 98 | + * Restores the CPU's RFLAGS register to the specified value. |
| 99 | + * Typically used to restore saved interrupt flag state. |
| 100 | + * |
| 101 | + * @param flags The 64-bit value to load into RFLAGS. |
| 102 | + */ |
| 103 | +static inline void write_rflags(uint64_t flags) { |
| 104 | + __asm__ volatile("push %0; popfq" :: "r"(flags) : "memory", "cc"); |
| 105 | +} |
| 106 | + |
| 107 | +/** |
| 108 | + * @brief Query whether interrupts are currently enabled. |
| 109 | + * |
| 110 | + * Uses the IF (Interrupt Flag) in the RFLAGS register to determine |
| 111 | + * if maskable interrupts are permitted. |
| 112 | + * |
| 113 | + * @return true if interrupts are enabled (IF = 1), false otherwise. |
| 114 | + */ |
| 115 | +static inline bool are_interrupts_enabled(void) { |
| 116 | + return (read_rflags() & (1ULL << 9)) != 0; |
| 117 | +} |
| 118 | + |
| 119 | +/** |
| 120 | + * @brief Attempt to acquire a spinlock once (non-blocking). |
| 121 | + * |
| 122 | + * Performs an atomic compare-and-swap on the lock. |
| 123 | + * |
| 124 | + * @param lock Pointer to the spinlock variable. |
| 125 | + * @return true if the lock was acquired, false otherwise. |
| 126 | + */ |
| 127 | +static inline bool try_lock(spinlock_t *lock) { |
| 128 | + uint32_t expected = 0; |
| 129 | + return __atomic_compare_exchange_n(lock, &expected, 1, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED); |
| 130 | +} |
| 131 | + |
| 132 | +/** |
| 133 | + * @brief Busy-wait until a spinlock is acquired. |
| 134 | + * |
| 135 | + * Continuously calls try_lock() until success. |
| 136 | + * Uses the `pause` instruction to reduce contention on hyper-threaded CPUs. |
| 137 | + * |
| 138 | + * @param lock Pointer to the spinlock variable. |
| 139 | + */ |
| 140 | +static inline void spin_until_locked(spinlock_t *lock) { |
| 141 | + while (!try_lock(lock)) { |
| 142 | + __asm__ volatile("pause"); // reduce contention |
| 143 | + } |
| 144 | +} |
| 145 | + |
| 146 | +/** |
| 147 | + * @brief Acquire a spinlock while disabling interrupts. |
| 148 | + * |
| 149 | + * Saves the current interrupt flag (IF) state into @p flags, |
| 150 | + * disables interrupts, then spins until the lock is acquired. |
| 151 | + * |
| 152 | + * @param lock Pointer to the spinlock variable. |
| 153 | + * @param flags Pointer to a uint64_t used to store the saved RFLAGS register. |
| 154 | + */ |
| 155 | +static inline void lock_spinlock_irq(spinlock_t *lock, uint64_t *flags) { |
| 156 | + *flags = read_rflags(); // save current IF |
| 157 | + interrupts_off(); // mask interrupts |
| 158 | + spin_until_locked(lock); // acquire |
| 159 | +} |
| 160 | + |
| 161 | +/** |
| 162 | + * @brief Release a spinlock and restore interrupts. |
| 163 | + * |
| 164 | + * Releases the given spinlock and restores the CPU interrupt flag (IF) |
| 165 | + * from the previously saved @p flags value. |
| 166 | + * |
| 167 | + * @param lock Pointer to the spinlock variable. |
| 168 | + * @param flags RFLAGS value saved by lock_spinlock_irq(). |
| 169 | + */ |
| 170 | +static inline void unlock_spinlock_irq(spinlock_t *lock, uint64_t flags) { |
| 171 | + __atomic_store_n(lock, 0, __ATOMIC_RELEASE); // unlock |
| 172 | + write_rflags(flags); // restore IF exactly as before |
| 173 | +} |
0 commit comments