Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | /* SPDX-License-Identifier: GPL-2.0-only */ /* * A stand-alone ticket spinlock implementation for use by the non-VHE * KVM hypervisor code running at EL2. * * Copyright (C) 2020 Google LLC * Author: Will Deacon <will@kernel.org> * * Heavily based on the implementation removed by c11090474d70 which was: * Copyright (C) 2012 ARM Ltd. */ #ifndef __ARM64_KVM_NVHE_SPINLOCK_H__ #define __ARM64_KVM_NVHE_SPINLOCK_H__ #include <asm/alternative.h> #include <asm/lse.h> #include <asm/rwonce.h> typedef union hyp_spinlock { u32 __val; struct { #ifdef __AARCH64EB__ u16 next, owner; #else u16 owner, next; #endif }; } hyp_spinlock_t; #define __HYP_SPIN_LOCK_INITIALIZER \ { .__val = 0 } #define __HYP_SPIN_LOCK_UNLOCKED \ ((hyp_spinlock_t) __HYP_SPIN_LOCK_INITIALIZER) #define DEFINE_HYP_SPINLOCK(x) hyp_spinlock_t x = __HYP_SPIN_LOCK_UNLOCKED #define hyp_spin_lock_init(l) \ do { \ *(l) = __HYP_SPIN_LOCK_UNLOCKED; \ } while (0) static inline void hyp_spin_lock(hyp_spinlock_t *lock) { u32 tmp; hyp_spinlock_t lockval, newval; asm volatile( /* Atomically increment the next ticket. */ ARM64_LSE_ATOMIC_INSN( /* LL/SC */ " prfm pstl1strm, %3\n" "1: ldaxr %w0, %3\n" " add %w1, %w0, #(1 << 16)\n" " stxr %w2, %w1, %3\n" " cbnz %w2, 1b\n", /* LSE atomics */ " mov %w2, #(1 << 16)\n" " ldadda %w2, %w0, %3\n" __nops(3)) /* Did we get the lock? */ " eor %w1, %w0, %w0, ror #16\n" " cbz %w1, 3f\n" /* * No: spin on the owner. Send a local event to avoid missing an * unlock before the exclusive load. */ " sevl\n" "2: wfe\n" " ldaxrh %w2, %4\n" " eor %w1, %w2, %w0, lsr #16\n" " cbnz %w1, 2b\n" /* We got the lock. Critical section starts here. */ "3:" : "=&r" (lockval), "=&r" (newval), "=&r" (tmp), "+Q" (*lock) : "Q" (lock->owner) : "memory"); } static inline void hyp_spin_unlock(hyp_spinlock_t *lock) { u64 tmp; asm volatile( ARM64_LSE_ATOMIC_INSN( /* LL/SC */ " ldrh %w1, %0\n" " add %w1, %w1, #1\n" " stlrh %w1, %0", /* LSE atomics */ " mov %w1, #1\n" " staddlh %w1, %0\n" __nops(1)) : "=Q" (lock->owner), "=&r" (tmp) : : "memory"); } static inline bool hyp_spin_is_locked(hyp_spinlock_t *lock) { hyp_spinlock_t lockval = READ_ONCE(*lock); return lockval.owner != lockval.next; } #ifdef CONFIG_NVHE_EL2_DEBUG static inline void hyp_assert_lock_held(hyp_spinlock_t *lock) { /* * The __pkvm_init() path accesses protected data-structures without * holding locks as the other CPUs are guaranteed to not enter EL2 * concurrently at this point in time. The point by which EL2 is * initialized on all CPUs is reflected in the pkvm static key, so * wait until it is set before checking the lock state. */ if (static_branch_likely(&kvm_protected_mode_initialized)) BUG_ON(!hyp_spin_is_locked(lock)); } #else static inline void hyp_assert_lock_held(hyp_spinlock_t *lock) { } #endif #endif /* __ARM64_KVM_NVHE_SPINLOCK_H__ */ |