// SPDX-License-Identifier: MPL-2.0 #ifndef SH_LIB_SMP_H #define SH_LIB_SMP_H #include "std/type.h" #include "std/status.h" #include "cpu/ap.h" #include "cpu/serial.h" #include "cpu/asm.h" // Write GS base register inline void sh_smp_write_gs_base(sh_uint64 value) { sh_uint32 low=(sh_uint32)(value); sh_uint32 high=(sh_uint32)(value>>32); __asm__ volatile ("wrmsr"::"c"(0xC0000101),"a"(low),"d"(high)); } // Return CPU struct of this CPU inline sh_ap_CPU_STRUCT *sh_smp_gs_base() { sh_uint32 low,high; __asm__ volatile ("rdmsr":"=a"(low),"=d"(high):"c"(0xC0000101)); return (sh_ap_CPU_STRUCT*)(((sh_uint64)high<<32)|low); } // Lock structure, unlocked lock have lapic_id to SH_UINT32_MAX typedef struct { volatile sh_uint32 spinlock; sh_uint32 lapic_id; } sh_SPIN_LOCK; // Create an unlocked lock, intended for creation of global variables. #define SH_LOCK() {.spinlock=0,.lapic_id=SH_UINT32_MAX} // Create an unlocked lock, intended for creation of local variables #define SH_LOCK_LOCAL() ((sh_SPIN_LOCK){.spinlock=0,.lapic_id=SH_UINT32_MAX}) // Lock a spinlock inline void sh_spin_lock(sh_SPIN_LOCK *lock) { while (__atomic_test_and_set(&lock->spinlock,__ATOMIC_ACQUIRE)) { while (lock->spinlock) { __asm__ volatile ("pause"); } } if (sh_smp_gs_base()==SH_NULLPTR) { sh_serial_send_byte('!'); while (SH_TRUE) {} } lock->lapic_id=sh_smp_gs_base()->lapic_id; } // Unlock a spinlock inline void sh_spin_unlock(sh_SPIN_LOCK *lock) { lock->lapic_id=SH_UINT32_MAX; __atomic_clear(&lock->spinlock,__ATOMIC_RELEASE); } // Try to lock a spinlock, doesn't wait for it to be unlocked, return SH_TRUE if lock is now owned by us inline sh_bool sh_spin_trylock(sh_SPIN_LOCK *lock) { if (__atomic_test_and_set(&lock->spinlock,__ATOMIC_ACQUIRE)) return SH_FALSE; lock->lapic_id=sh_smp_gs_base()->lapic_id; return SH_TRUE; } // Return which CPU is locking the lock inline sh_uint32 sh_spin_wholock(sh_SPIN_LOCK *lock) { return lock->lapic_id; } // Set CPU count (aka max_cpu_id+1) void sh_smp_set_cpu_count(sh_int16 cpu_count); // Return CPU count, return -1 if this value isn't initialized sh_int16 sh_smp_get_cpu_count(); // Memory barrier inline void sh_mb() { sh_asm_mfence(); } // Read memory barrier inline void sh_rmb() { sh_asm_lfence(); } // Write memory barrier inline void sh_wmb() { sh_asm_sfence(); } #endif