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