lock_stubs.S revision 1.27
1/* $NetBSD: lock_stubs.S,v 1.27 2018/01/07 13:15:23 maxv Exp $ */ 2 3/*- 4 * Copyright (c) 2006, 2007, 2008, 2009 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Andrew Doran. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32/* 33 * AMD64 lock stubs. Calling convention: 34 * 35 * %rdi arg 1 36 * %rsi arg 2 37 * %rdx arg 3 38 * %rax return value 39 */ 40 41#include "opt_multiprocessor.h" 42#include "opt_lockdebug.h" 43 44#include <machine/asm.h> 45#include <machine/frameasm.h> 46 47#include "assym.h" 48 49#define ENDLABEL(name,a) .align a; LABEL(name) 50#define LOCK(num) \ 51 HOTPATCH(HP_NAME_NOLOCK, 1) ; \ 52 lock 53#define RET(num) .Lret ## num: ret; nop; nop; ret 54 55#ifndef LOCKDEBUG 56 57/* 58 * void mutex_enter(kmutex_t *mtx); 59 * 60 * Acquire a mutex and post a load fence. 61 */ 62 .align 64 63 64ENTRY(mutex_enter) 65 movq CPUVAR(CURLWP), %rcx 66 xorq %rax, %rax 67 LOCK(1) 68 cmpxchgq %rcx, (%rdi) 69 jnz 1f 70 RET(1) 711: 72 jmp _C_LABEL(mutex_vector_enter) 73END(mutex_enter) 74 75/* 76 * void mutex_exit(kmutex_t *mtx); 77 * 78 * Release a mutex and post a load fence. 79 * 80 * See comments in mutex_vector_enter() about doing this operation unlocked 81 * on multiprocessor systems, and comments in arch/x86/include/lock.h about 82 * memory ordering on Intel x86 systems. 83 */ 84ENTRY(mutex_exit) 85 movq CPUVAR(CURLWP), %rax 86 xorq %rdx, %rdx 87 cmpxchgq %rdx, (%rdi) 88 jnz 1f 89 ret 901: 91 jmp _C_LABEL(mutex_vector_exit) 92END(mutex_exit) 93 94/* 95 * void mutex_spin_enter(kmutex_t *mtx); 96 * 97 * Acquire a spin mutex and post a load fence. 98 */ 99ENTRY(mutex_spin_enter) 100 movl $1, %eax 101 movl CPUVAR(ILEVEL), %esi 102 movzbl MTX_IPL(%rdi), %ecx /* new SPL */ 103 cmpl %ecx, %esi /* higher? */ 104 cmovgl %esi, %ecx 105 movl %ecx, CPUVAR(ILEVEL) /* splraiseipl() */ 106 subl %eax, CPUVAR(MTX_COUNT) /* decl doesnt set CF */ 107 cmovncl CPUVAR(MTX_OLDSPL), %esi 108 movl %esi, CPUVAR(MTX_OLDSPL) 109 xchgb %al, MTX_LOCK(%rdi) /* lock */ 110#ifdef MULTIPROCESSOR /* XXX for xen */ 111 testb %al, %al 112 jnz 1f 113#endif 114 RET(2) 1151: 116 jmp _C_LABEL(mutex_spin_retry) /* failed; hard case */ 117END(mutex_spin_enter) 118 119/* 120 * void mutex_spin_exit(kmutex_t *mtx); 121 * 122 * Release a spin mutex and post a load fence. 123 */ 124ENTRY(mutex_spin_exit) 125#ifdef DIAGNOSTIC 126 127 movl $0x0001, %eax /* new + expected value */ 128 movq CPUVAR(SELF), %r8 129 cmpxchgb %ah, MTX_LOCK(%rdi) /* unlock */ 130 jnz _C_LABEL(mutex_vector_exit) /* hard case if problems */ 131 movl CPU_INFO_MTX_OLDSPL(%r8), %edi 132 incl CPU_INFO_MTX_COUNT(%r8) 133 jnz 1f 134 cmpl CPU_INFO_ILEVEL(%r8), %edi 135 jae 1f 136 movl CPU_INFO_IUNMASK(%r8,%rdi,4), %esi 137 CLI(ax) 138 testl CPU_INFO_IPENDING(%r8), %esi 139 jnz _C_LABEL(Xspllower) 140 movl %edi, CPU_INFO_ILEVEL(%r8) 141 STI(ax) 1421: rep /* double byte ret as branch */ 143 ret /* target: see AMD docs */ 144 145#else /* DIAGNOSTIC */ 146 147 movq CPUVAR(SELF), %rsi 148 movb $0x00, MTX_LOCK(%rdi) 149 movl CPU_INFO_MTX_OLDSPL(%rsi), %ecx 150 incl CPU_INFO_MTX_COUNT(%rsi) 151 movl CPU_INFO_ILEVEL(%rsi),%edx 152 cmovnzl %edx,%ecx 153 pushq %rbx 154 cmpl %edx,%ecx /* new level is lower? */ 155 jae 2f 1561: 157 movl CPU_INFO_IPENDING(%rsi),%eax 158 testl %eax,CPU_INFO_IUNMASK(%rsi,%rcx,4)/* deferred interrupts? */ 159 jnz 3f 160 movl %eax,%ebx 161 cmpxchg8b CPU_INFO_ISTATE(%rsi) /* swap in new ilevel */ 162 jnz 4f 1632: 164 popq %rbx 165 ret 1663: 167 popq %rbx 168 movl %ecx, %edi 169 jmp _C_LABEL(Xspllower) 1704: 171 jmp 1b 172 173#endif /* DIAGNOSTIC */ 174 175END(mutex_spin_exit) 176 177/* 178 * void rw_enter(krwlock_t *rwl, krw_t op); 179 * 180 * Acquire one hold on a RW lock. 181 */ 182ENTRY(rw_enter) 183 cmpl $RW_READER, %esi 184 jne 2f 185 186 /* 187 * Reader: this is the most common case. 188 */ 189 movq (%rdi), %rax 1900: 191 testb $(RW_WRITE_LOCKED|RW_WRITE_WANTED), %al 192 jnz 3f 193 leaq RW_READ_INCR(%rax), %rdx 194 LOCK(2) 195 cmpxchgq %rdx, (%rdi) 196 jnz 1f 197 RET(3) 1981: 199 jmp 0b 200 201 /* 202 * Writer: if the compare-and-set fails, don't bother retrying. 203 */ 2042: movq CPUVAR(CURLWP), %rcx 205 xorq %rax, %rax 206 orq $RW_WRITE_LOCKED, %rcx 207 LOCK(3) 208 cmpxchgq %rcx, (%rdi) 209 jnz 3f 210 RET(4) 2113: 212 jmp _C_LABEL(rw_vector_enter) 213END(rw_enter) 214 215/* 216 * void rw_exit(krwlock_t *rwl); 217 * 218 * Release one hold on a RW lock. 219 */ 220ENTRY(rw_exit) 221 movq (%rdi), %rax 222 testb $RW_WRITE_LOCKED, %al 223 jnz 2f 224 225 /* 226 * Reader 227 */ 2280: testb $RW_HAS_WAITERS, %al 229 jnz 3f 230 cmpq $RW_READ_INCR, %rax 231 jb 3f 232 leaq -RW_READ_INCR(%rax), %rdx 233 LOCK(4) 234 cmpxchgq %rdx, (%rdi) 235 jnz 1f 236 ret 2371: 238 jmp 0b 239 240 /* 241 * Writer 242 */ 2432: leaq -RW_WRITE_LOCKED(%rax), %rdx 244 subq CPUVAR(CURLWP), %rdx 245 jnz 3f 246 LOCK(5) 247 cmpxchgq %rdx, (%rdi) 248 jnz 3f 249 ret 250 2513: jmp _C_LABEL(rw_vector_exit) 252END(rw_exit) 253 254/* 255 * int rw_tryenter(krwlock_t *rwl, krw_t op); 256 * 257 * Try to acquire one hold on a RW lock. 258 */ 259ENTRY(rw_tryenter) 260 cmpl $RW_READER, %esi 261 jne 2f 262 263 /* 264 * Reader: this is the most common case. 265 */ 266 movq (%rdi), %rax 2670: 268 testb $(RW_WRITE_LOCKED|RW_WRITE_WANTED), %al 269 jnz 4f 270 leaq RW_READ_INCR(%rax), %rdx 271 LOCK(8) 272 cmpxchgq %rdx, (%rdi) 273 jnz 1f 274 movl %edx, %eax /* nonzero */ 275 RET(5) 2761: 277 jmp 0b 278 279 /* 280 * Writer: if the compare-and-set fails, don't bother retrying. 281 */ 2822: movq CPUVAR(CURLWP), %rcx 283 xorq %rax, %rax 284 orq $RW_WRITE_LOCKED, %rcx 285 LOCK(9) 286 cmpxchgq %rcx, (%rdi) 287 movl $0, %eax 288 setz %al 2893: 290 RET(6) 291 ret 2924: 293 xorl %eax, %eax 294 jmp 3b 295END(rw_tryenter) 296 297#endif /* LOCKDEBUG */ 298 299/* 300 * Spinlocks. 301 */ 302ENTRY(__cpu_simple_lock_init) 303 movb $0, (%rdi) 304 ret 305END(__cpu_simple_lock_init) 306 307NENTRY(__cpu_simple_lock) 308 movl $0x0100, %eax 3091: 310 LOCK(6) 311 cmpxchgb %ah, (%rdi) 312 jnz 2f 313 RET(7) 3142: 315 movl $0x0100, %eax 316 pause 317 nop 318 nop 319 cmpb $0, (%rdi) 320 je 1b 321 jmp 2b 322END(__cpu_simple_lock) 323 324NENTRY(__cpu_simple_unlock) 325 movb $0, (%rdi) 326 ret 327END(__cpu_simple_unlock) 328 329ENTRY(__cpu_simple_lock_try) 330 movl $0x0100, %eax 331 LOCK(7) 332 cmpxchgb %ah, (%rdi) 333 movl $0, %eax 334 setz %al 335 RET(8) 336END(__cpu_simple_lock_try) 337 338 .type _C_LABEL(x86_retpatch), @object 339LABEL(x86_retpatch) 340#ifndef LOCKDEBUG 341 .quad .Lret1, .Lret2, .Lret3, .Lret4, .Lret5, .Lret6 342#endif 343 .quad .Lret7, .Lret8 344 .quad 0 345END(x86_retpatch) 346