| /* |
| * Kernel internals needed by qrwlock for standalone implementation. |
| */ |
| |
| #ifndef __KERNEL_H |
| #define __KERNEL_H |
| |
| #include "compiler.h" |
| #include "rmem.h" |
| |
| /* Different ways to exit the model */ |
| #define assert(cond) ({ if (!(cond)) __rmem_stop(_THIS_IP_); }) |
| #define abort() __rmem_stop(-1) |
| #define exit() __rmem_stop(0) |
| |
| /* Type definitions */ |
| typedef unsigned char u8; |
| typedef unsigned short u16; |
| typedef unsigned int u32; |
| typedef unsigned long long u64; |
| |
| typedef struct { |
| int counter; |
| } atomic_t; |
| |
| #define ATOMIC_INIT(i) { (i) } |
| |
| typedef struct { |
| u16 owner; |
| u16 next; |
| } __aligned(4) arch_spinlock_t; |
| |
| #define __ARCH_SPIN_LOCK_UNLOCKED { 0 , 0 } |
| |
| typedef struct qrwlock { |
| union { |
| atomic_t cnts; |
| struct { |
| u8 wlocked; /* Locked for write? */ |
| u8 __lstate[3]; |
| }; |
| }; |
| arch_spinlock_t wait_lock; |
| } arch_rwlock_t; |
| |
| #define __ARCH_RW_LOCK_UNLOCKED { \ |
| { .cnts = ATOMIC_INIT(0), }, \ |
| .wait_lock = __ARCH_SPIN_LOCK_UNLOCKED, \ |
| } |
| |
| /* |
| * TODO: We should do something more interesting here, since this affects |
| * the reader slowpath. |
| */ |
| #define in_interrupt() 0 |
| |
| /* READ_ONCE */ |
| #define __READ_ONCE_SIZE \ |
| ({ \ |
| switch (size) { \ |
| case 1: *(u8 *)res = *(volatile u8 *)p; break; \ |
| case 2: *(u16 *)res = *(volatile u16 *)p; break; \ |
| case 4: *(u32 *)res = *(volatile u32 *)p; break; \ |
| case 8: *(u64 *)res = *(volatile u64 *)p; break; \ |
| default: \ |
| abort(); /* hack for userspace */ \ |
| } \ |
| }) |
| |
| static __always_inline |
| void __read_once_size(const volatile void *p, void *res, int size) |
| { |
| __READ_ONCE_SIZE; |
| } |
| |
| #define __READ_ONCE(x) \ |
| ({ \ |
| union { typeof(x) __val; char __c[1]; } __u; \ |
| __read_once_size(&(x), __u.__c, sizeof(x)); \ |
| __u.__val; \ |
| }) |
| |
| #define READ_ONCE(x) __READ_ONCE(x) |
| |
| /* cmpxchg */ |
| #define __CMPXCHG_CASE(w, sz, name, mb, acq, rel, cl) \ |
| static inline unsigned long \ |
| __cmpxchg_case_##name(volatile void *ptr, \ |
| unsigned long old, \ |
| unsigned long new) \ |
| { \ |
| unsigned long tmp, oldval; \ |
| \ |
| asm volatile( \ |
| " prfm pstl1strm, %[v]\n" \ |
| "1: ld" #acq "xr" #sz "\t%" #w "[oldval], %[v]\n" \ |
| " eor %" #w "[tmp], %" #w "[oldval], %" #w "[old]\n" \ |
| " cbnz %" #w "[tmp], 2f\n" \ |
| " st" #rel "xr" #sz "\t%w[tmp], %" #w "[new], %[v]\n" \ |
| " cbnz %w[tmp], 1b\n" \ |
| " " #mb "\n" \ |
| "2:" \ |
| : [tmp] "=&r" (tmp), [oldval] "=&r" (oldval), \ |
| [v] "+Q" (*(unsigned long *)ptr) \ |
| : [old] "Lr" (old), [new] "r" (new) \ |
| : cl); \ |
| \ |
| return oldval; \ |
| } \ |
| |
| __CMPXCHG_CASE(w, b, 1, , , , ) |
| __CMPXCHG_CASE(w, h, 2, , , , ) |
| __CMPXCHG_CASE(w, , 4, , , , ) |
| __CMPXCHG_CASE( , , 8, , , , ) |
| __CMPXCHG_CASE(w, b, acq_1, , a, , "memory") |
| __CMPXCHG_CASE(w, h, acq_2, , a, , "memory") |
| __CMPXCHG_CASE(w, , acq_4, , a, , "memory") |
| __CMPXCHG_CASE( , , acq_8, , a, , "memory") |
| |
| #define __CMPXCHG_GEN(sfx) \ |
| static inline unsigned long __cmpxchg##sfx(volatile void *ptr, \ |
| unsigned long old, \ |
| unsigned long new, \ |
| int size) \ |
| { \ |
| switch (size) { \ |
| case 1: \ |
| return __cmpxchg_case##sfx##_1(ptr, (u8)old, new); \ |
| case 2: \ |
| return __cmpxchg_case##sfx##_2(ptr, (u16)old, new); \ |
| case 4: \ |
| return __cmpxchg_case##sfx##_4(ptr, old, new); \ |
| case 8: \ |
| return __cmpxchg_case##sfx##_8(ptr, old, new); \ |
| default: \ |
| abort(); /* hack for userspace */ \ |
| } \ |
| } |
| |
| __CMPXCHG_GEN() |
| __CMPXCHG_GEN(_acq) |
| |
| #define __cmpxchg_wrapper(sfx, ptr, o, n) \ |
| ({ \ |
| __typeof__(*(ptr)) __ret; \ |
| __ret = (__typeof__(*(ptr))) \ |
| __cmpxchg##sfx((ptr), (unsigned long)(o), \ |
| (unsigned long)(n), sizeof(*(ptr))); \ |
| __ret; \ |
| }) |
| |
| #define cmpxchg_relaxed(...) __cmpxchg_wrapper( , __VA_ARGS__) |
| #define cmpxchg_acquire(...) __cmpxchg_wrapper(_acq, __VA_ARGS__) |
| |
| /* load-acquire/store-release */ |
| #define __smp_store_release(p, v) \ |
| do { \ |
| union { typeof(*p) __val; char __c[1]; } __u = \ |
| { .__val = (__force typeof(*p)) (v) }; \ |
| switch (sizeof(*p)) { \ |
| case 1: \ |
| asm volatile ("stlrb %w1, %0" \ |
| : "=Q" (*p) \ |
| : "r" (*(u8 *)__u.__c) \ |
| : "memory"); \ |
| break; \ |
| case 2: \ |
| asm volatile ("stlrh %w1, %0" \ |
| : "=Q" (*p) \ |
| : "r" (*(u16 *)__u.__c) \ |
| : "memory"); \ |
| break; \ |
| case 4: \ |
| asm volatile ("stlr %w1, %0" \ |
| : "=Q" (*p) \ |
| : "r" (*(u32 *)__u.__c) \ |
| : "memory"); \ |
| break; \ |
| case 8: \ |
| asm volatile ("stlr %1, %0" \ |
| : "=Q" (*p) \ |
| : "r" (*(u64 *)__u.__c) \ |
| : "memory"); \ |
| break; \ |
| } \ |
| } while (0) |
| #define smp_store_release(p, v) __smp_store_release(p, v) |
| |
| #define __smp_load_acquire(p) \ |
| ({ \ |
| union { typeof(*p) __val; char __c[1]; } __u; \ |
| switch (sizeof(*p)) { \ |
| case 1: \ |
| asm volatile ("ldarb %w0, %1" \ |
| : "=r" (*(u8 *)__u.__c) \ |
| : "Q" (*p) : "memory"); \ |
| break; \ |
| case 2: \ |
| asm volatile ("ldarh %w0, %1" \ |
| : "=r" (*(u16 *)__u.__c) \ |
| : "Q" (*p) : "memory"); \ |
| break; \ |
| case 4: \ |
| asm volatile ("ldar %w0, %1" \ |
| : "=r" (*(u32 *)__u.__c) \ |
| : "Q" (*p) : "memory"); \ |
| break; \ |
| case 8: \ |
| asm volatile ("ldar %0, %1" \ |
| : "=r" (*(u64 *)__u.__c) \ |
| : "Q" (*p) : "memory"); \ |
| break; \ |
| } \ |
| __u.__val; \ |
| }) |
| #define smp_load_acquire(p) __smp_load_acquire(p) |
| |
| /* smp_cond_load_acquire */ |
| #define __CMPWAIT_CASE(w, sz, name) \ |
| static inline void __cmpwait_case_##name(volatile void *ptr, \ |
| unsigned long val) \ |
| { \ |
| unsigned long tmp; \ |
| \ |
| asm volatile( \ |
| " ldxr" #sz "\t%" #w "[tmp], %[v]\n" \ |
| " eor %" #w "[tmp], %" #w "[tmp], %" #w "[val]\n" \ |
| " cbnz %" #w "[tmp], 1f\n" \ |
| /* " wfe\n" */ /* TODO: wfe support */ \ |
| "1:" \ |
| : [tmp] "=&r" (tmp), [v] "+Q" (*(unsigned long *)ptr) \ |
| : [val] "r" (val)); \ |
| } |
| |
| __CMPWAIT_CASE(w, b, 1); |
| __CMPWAIT_CASE(w, h, 2); |
| __CMPWAIT_CASE(w, , 4); |
| __CMPWAIT_CASE( , , 8); |
| |
| #define __CMPWAIT_GEN(sfx) \ |
| static inline void __cmpwait##sfx(volatile void *ptr, \ |
| unsigned long val, \ |
| int size) \ |
| { \ |
| switch (size) { \ |
| case 1: \ |
| return __cmpwait_case##sfx##_1(ptr, (u8)val); \ |
| case 2: \ |
| return __cmpwait_case##sfx##_2(ptr, (u16)val); \ |
| case 4: \ |
| return __cmpwait_case##sfx##_4(ptr, val); \ |
| case 8: \ |
| return __cmpwait_case##sfx##_8(ptr, val); \ |
| default: \ |
| abort(); /* hack for userspace */ \ |
| } \ |
| } |
| |
| __CMPWAIT_GEN() |
| |
| #define __cmpwait_relaxed(ptr, val) \ |
| __cmpwait((ptr), (unsigned long)(val), sizeof(*(ptr))) |
| |
| #define smp_cond_load_acquire(ptr, cond_expr) \ |
| ({ \ |
| typeof(ptr) __PTR = (ptr); \ |
| typeof(*ptr) VAL; \ |
| for (;;) { \ |
| VAL = smp_load_acquire(__PTR); \ |
| if (cond_expr) \ |
| break; \ |
| __cmpwait_relaxed(__PTR, VAL); \ |
| } \ |
| VAL; \ |
| }) |
| |
| /* Atomics */ |
| #define atomic_read(v) READ_ONCE((v)->counter) |
| |
| #define ATOMIC_OP(op, asm_op) \ |
| static inline void \ |
| atomic_##op(int i, atomic_t *v) \ |
| { \ |
| unsigned long tmp; \ |
| int result; \ |
| \ |
| asm volatile("// atomic_" #op "\n" \ |
| " prfm pstl1strm, %2\n" \ |
| "1: ldxr %w0, %2\n" \ |
| " " #asm_op " %w0, %w0, %w3\n" \ |
| " stxr %w1, %w0, %2\n" \ |
| " cbnz %w1, 1b" \ |
| : "=&r" (result), "=&r" (tmp), "+Q" (v->counter) \ |
| : "Ir" (i)); \ |
| } \ |
| |
| #define ATOMIC_OP_RETURN(name, mb, acq, rel, cl, op, asm_op) \ |
| static inline int \ |
| atomic_##op##_return##name(int i, atomic_t *v) \ |
| { \ |
| unsigned long tmp; \ |
| int result; \ |
| \ |
| asm volatile("// atomic_" #op "_return" #name "\n" \ |
| " prfm pstl1strm, %2\n" \ |
| "1: ld" #acq "xr %w0, %2\n" \ |
| " " #asm_op " %w0, %w0, %w3\n" \ |
| " st" #rel "xr %w1, %w0, %2\n" \ |
| " cbnz %w1, 1b\n" \ |
| " " #mb \ |
| : "=&r" (result), "=&r" (tmp), "+Q" (v->counter) \ |
| : "Ir" (i) \ |
| : cl); \ |
| \ |
| return result; \ |
| } \ |
| |
| ATOMIC_OP(add, add) |
| ATOMIC_OP(sub, sub) |
| ATOMIC_OP_RETURN(_acquire, , a, , "memory", add, add) |
| ATOMIC_OP_RETURN(_release, , , l, "memory", sub, sub) |
| |
| #define atomic_cmpxchg_relaxed(v, old, new) \ |
| cmpxchg_relaxed(&((v)->counter), (old), (new)) |
| #define atomic_cmpxchg_acquire(v, old, new) \ |
| cmpxchg_acquire(&((v)->counter), (old), (new)) |
| |
| #define atomic_cond_read_acquire(v, c) smp_cond_load_acquire(&(v)->counter, (c)) |
| |
| /* Ticket spinlock */ |
| #define TICKET_SHIFT 16 |
| |
| static inline void arch_spin_lock(arch_spinlock_t *lock) |
| { |
| unsigned int tmp; |
| arch_spinlock_t lockval, newval; |
| |
| asm volatile( |
| /* Atomically increment the next ticket. */ |
| " prfm pstl1strm, %3\n" |
| "1: ldaxr %w0, %3\n" |
| " add %w1, %w0, %w5\n" |
| " stxr %w2, %w1, %3\n" |
| " cbnz %w2, 1b\n" |
| |
| /* 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" TODO: wfe/sev support */ |
| "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), "I" (1 << TICKET_SHIFT) |
| : "memory"); |
| } |
| |
| static inline void arch_spin_unlock(arch_spinlock_t *lock) |
| { |
| unsigned long tmp; |
| |
| asm volatile( |
| " ldrh %w1, %0\n" |
| " add %w1, %w1, #1\n" |
| " stlrh %w1, %0" |
| : "=Q" (lock->owner), "=&r" (tmp) |
| : |
| : "memory"); |
| } |
| |
| #endif /* __KERNEL_H */ |