1/* $NetBSD: lock_stubs_ras.S,v 1.4 2011/08/27 13:23:52 bouyer Exp $ */ 2 3/*- 4 * Copyright (c) 2007 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#include "opt_cputype.h" 33#include "opt_lockdebug.h" 34#include "opt_multiprocessor.h" 35 36#include <sys/errno.h> 37 38#include <machine/asm.h> 39#include <machine/cpu.h> 40 41#include "assym.h" 42 43/* 44 * We rely on mips_vector_init to choose to not use these routines if we are 45 * on a system with multiple CPUs. We can still use this on a simple CPU 46 * with MULTIPROCESSOR since it might be useful to be using preemption. 47 */ 48 49/* 50 * Lock stubs for non-MP kernels. These are implemented using restartable 51 * sequences, since LL/SC are either not available (MIPS1 and a couple of 52 * oddball MIPS3 CPUs) or not desirable (overhead). 53 * 54 * The order of the generated code is particularly important here. Some 55 * assumptions: 56 * 57 * o All of the critical sections are 20 bytes in size, and the second 58 * instruction in each critical section is aligned on a 16 byte boundary 59 * (see top of _restart_lock_ras() for why). The entry is defined here as 60 * the point where a restart occurs if we trap within the section. 61 * 62 * o The entire code block is aligned on a 256 byte boundary, and is 63 * 256 bytes in size. This is to allow us to do an pessimistic check 64 * after taking a trap with: 65 * 66 * if ((addr & ~255) == _lock_ras_start) 67 * addr = _restart_lock_ras(addr); 68 * 69 * See definition of MIPS_LOCK_RAS_SIZE in asm.h. 70 * 71 * o In order to keep the size of the block down, the routines are run 72 * into each other. Use objdump -d to check alignment after making 73 * changes. 74 */ 75#ifdef _LP64 76 .set mips3 77#else 78 .set mips1 79#endif 80 .set noreorder 81 .set noat 82 83/* 84 * to work around the branch prediction engine misbehavior of 85 * Loongson 2F processors we need to clear the branch target buffer before 86 * a j ra. This requires extra instructions which don't fit in the RAS blocks, 87 * so do a PC-relative just to a block of code (this is the same size as 88 * a j ra) where we can let the assembler install the workaround. 89 */ 90#ifdef MIPS3_LOONGSON2F 91#define J_RA j loongson_return 92#else 93#define J_RA j ra 94#endif 95 96 97/* 98 * unsigned long ras_atomic_cas_ulong(volatile unsigned long *val, 99 * unsigned long old, unsigned long new); 100 */ 101 .text 102 .p2align LOG2_MIPS_LOCK_RAS_SIZE 103 104EXPORT(_lock_ras_start) 105STATIC_LEAF(ras_atomic_cas_noupdate) 106 J_RA 107 move v0, t0 108END(ras_atomic_cas_noupdate) 109 110 nop 111 .if ((. - _lock_ras_start) & 15) != 12 112 .error "bas ras offset" 113 .endif 114STATIC_LEAF_NOPROFILE(ras_atomic_cas_ulong) 115 PTR_L t0, (a0) /* <- critical section start */ 116_atomic_cas_ulong_ras_start: 117 nop 118 bne t0, a1, ras_atomic_cas_noupdate 119 nop 120 PTR_S a2, (a0) /* <- critical section end */ 121_atomic_cas_ulong_ras_end: 122 J_RA 123 move v0, a1 124END(ras_atomic_cas_ulong) 125 126/* 127 * unsigned int ras_atomic_cas_uint(volatile unsigned int *val, 128 * unsigned int old, unsigned int new); 129 */ 130 nop 131 .if ((. - _lock_ras_start) & 15) != 12 132 .error "bas ras offset" 133 .endif 134STATIC_LEAF_NOPROFILE(ras_atomic_cas_uint) 135 INT_L t0, (a0) /* <- critical section start */ 136_atomic_cas_uint_ras_start: 137 nop 138 bne t0, a1, ras_atomic_cas_noupdate 139 nop 140 INT_S a2, (a0) /* <- critical section end */ 141_atomic_cas_uint_ras_end: 142 J_RA 143 move v0, a1 144END(ras_atomic_cas_uint) 145 146/* 147 * int _ucas_ulong_ras(volatile u_long *val, u_long old, u_long new, 148 * u_long *retp); 149 */ 150 nop 151 .if ((. - _lock_ras_start) & 15) != 12 152 .error "bas ras offset" 153 .endif 154STATIC_LEAF_NOPROFILE(_ucas_ulong_ras) 155 LONG_L t0, (a0) /* <- critical section start */ 156_ucas_ulong_ras_start: 157 nop 158 bne t0, a1, _ucas_ulong_ras_end 159 nop 160 LONG_S a2, (a0) /* <- critical section end */ 161_ucas_ulong_ras_end: 162 PTR_S zero, PCB_ONFAULT(v1) 163 J_RA 164 LONG_S t0, 0(a3) 165END(_ucas_ulong_ras) 166 167/* 168 * int _ucas_uint_ras(volatile u_int *val, u_int old, u_int new, u_int *retp); 169 */ 170 .if ((. - _lock_ras_start) & 15) != 12 171 .error "bad ras offset" 172 .endif 173STATIC_LEAF_NOPROFILE(_ucas_uint_ras) 174 INT_L t0, (a0) /* <- critical section start */ 175_ucas_uint_ras_start: 176 nop 177 bne t0, a1, _ucas_uint_ras_end 178 nop 179 INT_S a2, (a0) /* <- critical section end */ 180_ucas_uint_ras_end: 181 PTR_S zero, PCB_ONFAULT(v1) 182 J_RA 183 INT_S t0, 0(a3) 184END(_ucas_uint_ras) 185 186#ifndef LOCKDEBUG 187/* 188 * void ras_mutex_enter(kmutex_t *mtx); 189 */ 190 .if ((. - _lock_ras_start) & 15) != 12 191 .error "bad ras offset" 192 .endif 193STATIC_LEAF_NOPROFILE(ras_mutex_enter) 194 PTR_L t0, (a0) /* <- critical section start */ 195_mutex_enter_ras_start: 196 nop 197 bnez t0, ras_mutex_vector_enter 198 nop 199 PTR_S MIPS_CURLWP, (a0)/* <- critical section end */ 200_mutex_enter_ras_end: 201 J_RA 202 nop 203END(ras_mutex_enter) 204 205/* 206 * int ras_mutex_exit(kmutex_t *mtx); 207 */ 208 nop 209 .if ((. - _lock_ras_start) & 15) != 12 210 .error "bas ras offset" 211 .endif 212STATIC_LEAF_NOPROFILE(ras_mutex_exit) 213 PTR_L t0, (a0) /* <- critical section start */ 214_mutex_exit_ras_start: 215 nop 216 bne t0, MIPS_CURLWP, ras_mutex_vector_exit 217 nop 218 PTR_S zero, (a0) /* <- critical section end */ 219_mutex_exit_ras_exit: 220 J_RA 221 nop 222END(ras_mutex_exit) 223 224/* 225 * These could moved out to fit in more RAS sequences. 226 */ 227STATIC_LEAF_NOPROFILE(ras_mutex_vector_enter) 228 j _C_LABEL(mutex_vector_enter) 229 nop 230END(ras_mutex_vector_enter) 231 232STATIC_LEAF_NOPROFILE(ras_mutex_vector_exit) 233 j _C_LABEL(mutex_vector_exit) 234 nop 235END(ras_mutex_vector_exit) 236#endif /* !LOCKDEBUG */ 237 238 .p2align LOG2_MIPS_LOCK_RAS_SIZE /* Get out of the RAS block */ 239 240 .set at 241#ifdef MIPS3_LOONGSON2F 242loongson_return: 243 j ra 244 nop 245#endif 246 247/* 248 * Patch up the given address. We arrive here if we might have trapped 249 * within one of the critical sections above. Do: 250 * 251 * if ((addr & ~15) == ras) 252 * return ras - 4; 253 * ... check next ... 254 * return addr; 255 * 256 * Registers on entry: 257 * 258 * k1 fault PC 259 * ra return address 260 * 261 * On exit: 262 * 263 * k1 adjusted fault PC 264 * ra return address 265 * t0 clobbered 266 * t1 clobbered 267 */ 268 269#define RAS_MKMASK(a) (1 << (((a)-_lock_ras_start) >> 4)) 270 271/* 272 * Since each RAS is aligned on a 16 byte boundary, we can use its offset 273 * from _lock_ras_start to construct a bitmask of the valid RAS within. 274 */ 275#ifndef LOCKDEBUG 276#define MUTEX_RAS_MASK (RAS_MKMASK(_mutex_enter_ras_start) \ 277 |RAS_MKMASK(_mutex_exit_ras_start)) 278#else 279#define MUTEX_RAS_MASK 0 280#endif 281 282#define RAS_MASK (RAS_MKMASK(_atomic_cas_ulong_ras_start) \ 283 |RAS_MKMASK(_atomic_cas_uint_ras_start) \ 284 |RAS_MKMASK(_ucas_ulong_ras_start) \ 285 |RAS_MKMASK(_ucas_uint_ras_start) \ 286 |MUTEX_RAS_MASK) 287 288/* 289 * The caller has already determined that 290 * _lock_ras_start == (k1 & -MIPS_LOCK_RAS_SIZE) 291 */ 292LEAF_NOPROFILE(_restart_lock_ras) 293 and t0, k1, MIPS_LOCK_RAS_SIZE - 1 294 /* look at addr bits in ras region */ 295 srl t0, 4 /* focus on each set of 16 bytes */ 296 li t1, 1 /* need this to make a bitmask */ 297 sllv t1, t1, t0 /* now we have a bitmask of the PC */ 298 andi t1, RAS_MASK /* was the PC in a RAS? */ 299 bnez t1, 1f /* yes, adjust PC */ 300 and t0, k1, 15 /* get offset in RAS */ 301 302 j ra 303 nop 3041: 305 addu t0, 4 /* bias offset by one more instruction */ 306 j ra 307 PTR_SUBU k1, t0 /* and subtract that from the PC */ 308END(_restart_lock_ras) 309 310/* 311 * int ras_ucas_uint(volatile u_int *ptr, u_int old, u_int new, u_int *retp); 312 */ 313STATIC_LEAF(ras_ucas_uint) 314 PTR_L v1, L_PCB(MIPS_CURLWP) 315 PTR_LA v0, _C_LABEL(ras_ucaserr) 316 PTR_S v0, PCB_ONFAULT(v1) 317 bltz a0, _C_LABEL(ras_ucaserr) 318 nop 319 b _C_LABEL(_ucas_uint_ras) 320 move v0, zero # assume success 321END(ras_ucas_uint) 322 323/* 324 * int ras_ucas_ulong(volatile u_long *ptr, u_long old, u_long new, u_long *retp); 325 */ 326STATIC_LEAF(ras_ucas_ulong) 327 PTR_L v1, L_PCB(MIPS_CURLWP) 328 PTR_LA v0, _C_LABEL(ras_ucaserr) 329 PTR_S v0, PCB_ONFAULT(v1) 330 bltz a0, _C_LABEL(ras_ucaserr) 331 nop 332 b _C_LABEL(_ucas_ulong_ras) 333 move v0, zero # assume success 334END(ras_ucas_ulong) 335 336/* 337 * 338 */ 339STATIC_LEAF(ras_ucaserr) 340 PTR_S zero, PCB_ONFAULT(v1) # reset fault handler 341 j ra 342 li v0, EFAULT # return EFAULT on error 343END(ras_ucaserr) 344 345#ifndef LOCKDEBUG 346/* 347 * void mutex_spin_enter(kmutex_t *mtx); 348 */ 349STATIC_NESTED(ras_mutex_spin_enter, CALLFRAME_SIZ, ra) 350 move t0, a0 351 PTR_L t2, L_CPU(MIPS_CURLWP) 352 INT_L a0, MTX_IPL(t0) 353#ifdef PARANOIA 354 INT_L ta1, CPU_INFO_CPL(t2) # get current cpl 355#endif 356 357 /* 358 * We need to raise our IPL. 359 * call splraise (only uses a0-a3, v0-v1, and ra) 360 */ 361 move t3, ra 362 jal _C_LABEL(splraise) 363 nop 364 move ra, t3 365 366 /* 367 * If this is the first lock of the mutex, store the previous IPL 368 * for exit. 369 */ 3701: 371 INT_L ta2, CPU_INFO_MTX_COUNT(t2) 372 nop 373 INT_ADDU ta3, ta2, -1 374 INT_S ta3, CPU_INFO_MTX_COUNT(t2) 375 376 bnez ta2, 2f 377 nop 378 INT_S v0, CPU_INFO_MTX_OLDSPL(t2) /* returned by splraise */ 3792: 380#if defined(DIAGNOSTIC) 381 INT_L t3, MTX_LOCK(t0) 382 li t1, 1 383 bnez t3, 3f 384 nop 385 j ra 386 INT_S t1, MTX_LOCK(t0) 3873: 388 j _C_LABEL(mutex_spin_retry) 389 nop 390#else /* DIAGNOSTIC */ 391 j ra 392 nop 393#endif /* DIAGNOSTIC */ 394END(ras_mutex_spin_enter) 395 396/* 397 * void mutex_spin_exit(kmutex_t *mtx); 398 */ 399LEAF(ras_mutex_spin_exit) 400 PTR_L t2, L_CPU(MIPS_CURLWP) 401 nop 402#if defined(DIAGNOSTIC) 403 INT_L t0, MTX_LOCK(a0) 404 nop 405 beqz t0, 2f 406 nop 407 INT_S zero, MTX_LOCK(a0) 408#endif 409 410 /* 411 * We need to grab this before the mutex count is incremented 412 * because if we get an interrupt, it may see the count as zero 413 * and overwrite the oldspl value with a bogus value. 414 */ 415#ifdef PARANOIA 416 INT_L a2, MTX_IPL(a0) 417#endif 418 INT_L a0, CPU_INFO_MTX_OLDSPL(t2) 419 420 /* 421 * Increment the mutex count 422 */ 423 INT_L t0, CPU_INFO_MTX_COUNT(t2) 424 nop 425 INT_ADDU t0, t0, 1 426 INT_S t0, CPU_INFO_MTX_COUNT(t2) 427 428 /* 429 * If the IPL doesn't change, nothing to do 430 */ 431 INT_L a1, CPU_INFO_CPL(t2) 432 nop 433 434#ifdef PARANOIA 435 sltu v0, a1, a2 # v0 = cpl < mtx_ipl 436 sltu v1, a1, a0 # v1 = cpl < oldspl 437 sll v0, 1 438 or v0, v1 43912: bnez v0, 12b # loop forever if either is true 440 nop 441#endif /* PARANOIA */ 442 443 beq a0, a1, 1f # if oldspl == cpl 444 nop # no reason to drop ipl 445 446 bltz t0, 1f # there are still holders 447 nop # so don't drop IPL 448 449 /* 450 * Mutex count is zero so we need to restore the old IPL 451 */ 452#ifdef PARANOIA 453 sltiu v0, a0, IPL_HIGH+1 45413: beqz v0, 13b # loop forever if ipl > IPL_HIGH 455 nop 456#endif 457 j _C_LABEL(splx) 458 nop 4591: 460 j ra 461 nop 462#if defined(DIAGNOSTIC) 4632: 464 j _C_LABEL(mutex_vector_exit) 465 nop 466#endif 467END(ras_mutex_spin_exit) 468#endif /* !LOCKDEBUG */ 469 470 .data 471EXPORT(mips_locore_atomicvec) 472 PTR_WORD ras_atomic_cas_uint 473 PTR_WORD ras_atomic_cas_ulong 474 PTR_WORD ras_ucas_uint 475 PTR_WORD ras_ucas_ulong 476#ifdef LOCKDEBUG 477 PTR_WORD mutex_enter 478 PTR_WORD mutex_exit 479 PTR_WORD mutex_spin_enter 480 PTR_WORD mutex_spin_exit 481#else 482 PTR_WORD ras_mutex_enter 483 PTR_WORD ras_mutex_exit 484 PTR_WORD ras_mutex_spin_enter 485 PTR_WORD ras_mutex_spin_exit 486#endif /* !LOCKDEBUG */ 487