/*
 * @author  xamidev <xamidev@riseup.net>
 * @brief   Spinlock implementation
 * @license GPL-3.0-only
 */

#include <stdatomic.h>
#include <stdbool.h>
#include <kernel.h>
#include <sched/spinlock.h>

/*
 * spinlock_acquire - Lock a lock
 * @lock: pointer to desired spinlock
 *
 * Saves the RFLAGS register, then acquires a lock.
 * Pause instruction is used to ease the CPU.
 */
void spinlock_acquire(struct spinlock* lock)
{
    uint64_t rflags;
    asm volatile("pushfq ; pop %0 ; cli" : "=rm"(rflags) : : "memory");

    while (__atomic_test_and_set(&lock->locked, __ATOMIC_ACQUIRE)) {
        __builtin_ia32_pause();
    }

    lock->rflags = rflags;
}

/*
 * spinlock_release - Unlock a lock
 * @lock: pointer to desired spinlock
 *
 * Gets saved RFLAGS register from the lock and
 * unlocks it (clears locked state).
 * RFLAGS is then restored.
 */
void spinlock_release(struct spinlock* lock)
{
    uint64_t rflags = lock->rflags;
    __atomic_clear(&lock->locked, __ATOMIC_RELEASE);
    asm volatile("push %0 ; popfq" : : "rm"(rflags) : "memory");
}