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