/* Copyright (C) 2021,2022 fef . All rights reserved. */ #pragma once #include #include static __always_inline void enable_intr(void) { __asm__ volatile("sti"); } static __always_inline void disable_intr(void) { __asm__ volatile("cli" ::: "memory"); } static inline register_t read_flags(void) { register_t flags; #ifdef __x86_64__ __asm__( " pushfq \n" " popq %0 \n" : "=r"(flags) ); #else __asm__( " pushfl \n" " popl %0 \n" : "=r"(flags) ); #endif return flags; } /** * @brief Temporarily disable interrupts. * * It should be noted that yes, there is also `disable_intr()`, which does the * exact same thing except it has no return value. This is yet another instance * of me finding it funny to cause unnecessary confusion, but this time it's * also because FreeBSD uses the same unfortunate naming scheme. * * @return The contents of the status register before disabling interrupts. * This should be passed to `intr_restore()` after the atomic operation * is complete so that the interrupt enable flag can be restored to what * it was before. */ static inline register_t intr_disable(void) { register_t eflags = read_flags(); disable_intr(); return eflags; } static inline void intr_restore(register_t flags) { /* bit 9 is IF (Interrupt Enable Flag) */ if (flags & (1 << 9)) enable_intr(); } static inline bool intr_enabled(void) { register_t eflags = read_flags(); return (eflags & (1 << 9)) != 0; } static __always_inline void halt(void) { __asm__ volatile("hlt"); } struct x86_cpuid_ret { u32 eax; u32 ebx; u32 ecx; u32 edx; }; static inline void x86_cpuid(struct x86_cpuid_ret *ret, u32 eax, u32 ecx) { __asm__ volatile( "cpuid" : "=a"(ret->eax), "=b"(ret->ebx), "=c"(ret->ecx), "=d"(ret->edx) : "0"(eax), "c"(ecx) ); } static __always_inline void x86_pause(void) { __asm__ volatile("pause"); } /** @brief Use this macro to build spin-wait loops. */ #define spin_loop for (;; x86_pause())