1/* $NetBSD: lock_stubs_ras.S,v 1.3 2011/04/29 22:04:42 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#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 * unsigned long ras_atomic_cas_ulong(volatile unsigned long *val, 85 * unsigned long old, unsigned long new); 86 */ 87 .text 88 .p2align LOG2_MIPS_LOCK_RAS_SIZE 89 90EXPORT(_lock_ras_start) 91STATIC_LEAF(ras_atomic_cas_noupdate) 92 j ra 93 move v0, t0 94END(ras_atomic_cas_noupdate) 95 96 nop 97 .if ((. - _lock_ras_start) & 15) != 12 98 .error "bas ras offset" 99 .endif 100STATIC_LEAF_NOPROFILE(ras_atomic_cas_ulong) 101 PTR_L t0, (a0) /* <- critical section start */ 102_atomic_cas_ulong_ras_start: 103 nop 104 bne t0, a1, ras_atomic_cas_noupdate 105 nop 106 PTR_S a2, (a0) /* <- critical section end */ 107_atomic_cas_ulong_ras_end: 108 j ra 109 move v0, a1 110END(ras_atomic_cas_ulong) 111 112/* 113 * unsigned int ras_atomic_cas_uint(volatile unsigned int *val, 114 * unsigned int old, unsigned int new); 115 */ 116 nop 117 .if ((. - _lock_ras_start) & 15) != 12 118 .error "bas ras offset" 119 .endif 120STATIC_LEAF_NOPROFILE(ras_atomic_cas_uint) 121 INT_L t0, (a0) /* <- critical section start */ 122_atomic_cas_uint_ras_start: 123 nop 124 bne t0, a1, ras_atomic_cas_noupdate 125 nop 126 INT_S a2, (a0) /* <- critical section end */ 127_atomic_cas_uint_ras_end: 128 j ra 129 move v0, a1 130END(ras_atomic_cas_uint) 131 132/* 133 * int _ucas_ulong_ras(volatile u_long *val, u_long old, u_long new, 134 * u_long *retp); 135 */ 136 nop 137 .if ((. - _lock_ras_start) & 15) != 12 138 .error "bas ras offset" 139 .endif 140STATIC_LEAF_NOPROFILE(_ucas_ulong_ras) 141 LONG_L t0, (a0) /* <- critical section start */ 142_ucas_ulong_ras_start: 143 nop 144 bne t0, a1, _ucas_ulong_ras_end 145 nop 146 LONG_S a2, (a0) /* <- critical section end */ 147_ucas_ulong_ras_end: 148 PTR_S zero, PCB_ONFAULT(v1) 149 j ra 150 LONG_S t0, 0(a3) 151END(_ucas_ulong_ras) 152 153/* 154 * int _ucas_uint_ras(volatile u_int *val, u_int old, u_int new, u_int *retp); 155 */ 156 .if ((. - _lock_ras_start) & 15) != 12 157 .error "bad ras offset" 158 .endif 159STATIC_LEAF_NOPROFILE(_ucas_uint_ras) 160 INT_L t0, (a0) /* <- critical section start */ 161_ucas_uint_ras_start: 162 nop 163 bne t0, a1, _ucas_uint_ras_end 164 nop 165 INT_S a2, (a0) /* <- critical section end */ 166_ucas_uint_ras_end: 167 PTR_S zero, PCB_ONFAULT(v1) 168 j ra 169 INT_S t0, 0(a3) 170END(_ucas_uint_ras) 171 172#ifndef LOCKDEBUG 173/* 174 * void ras_mutex_enter(kmutex_t *mtx); 175 */ 176 .if ((. - _lock_ras_start) & 15) != 12 177 .error "bad ras offset" 178 .endif 179STATIC_LEAF_NOPROFILE(ras_mutex_enter) 180 PTR_L t0, (a0) /* <- critical section start */ 181_mutex_enter_ras_start: 182 nop 183 bnez t0, ras_mutex_vector_enter 184 nop 185 PTR_S MIPS_CURLWP, (a0)/* <- critical section end */ 186_mutex_enter_ras_end: 187 j ra 188 nop 189END(ras_mutex_enter) 190 191/* 192 * int ras_mutex_exit(kmutex_t *mtx); 193 */ 194 nop 195 .if ((. - _lock_ras_start) & 15) != 12 196 .error "bas ras offset" 197 .endif 198STATIC_LEAF_NOPROFILE(ras_mutex_exit) 199 PTR_L t0, (a0) /* <- critical section start */ 200_mutex_exit_ras_start: 201 nop 202 bne t0, MIPS_CURLWP, ras_mutex_vector_exit 203 nop 204 PTR_S zero, (a0) /* <- critical section end */ 205_mutex_exit_ras_exit: 206 j ra 207 nop 208END(ras_mutex_exit) 209 210/* 211 * These could moved out to fit in more RAS sequences. 212 */ 213STATIC_LEAF_NOPROFILE(ras_mutex_vector_enter) 214 j _C_LABEL(mutex_vector_enter) 215 nop 216END(ras_mutex_vector_enter) 217 218STATIC_LEAF_NOPROFILE(ras_mutex_vector_exit) 219 j _C_LABEL(mutex_vector_exit) 220 nop 221END(ras_mutex_vector_exit) 222#endif /* !LOCKDEBUG */ 223 224 .p2align LOG2_MIPS_LOCK_RAS_SIZE /* Get out of the RAS block */ 225 226/* 227 * Patch up the given address. We arrive here if we might have trapped 228 * within one of the critical sections above. Do: 229 * 230 * if ((addr & ~15) == ras) 231 * return ras - 4; 232 * ... check next ... 233 * return addr; 234 * 235 * Registers on entry: 236 * 237 * k1 fault PC 238 * ra return address 239 * 240 * On exit: 241 * 242 * k1 adjusted fault PC 243 * ra return address 244 * t0 clobbered 245 * t1 clobbered 246 */ 247 248#define RAS_MKMASK(a) (1 << (((a)-_lock_ras_start) >> 4)) 249 250/* 251 * Since each RAS is aligned on a 16 byte boundary, we can use its offset 252 * from _lock_ras_start to construct a bitmask of the valid RAS within. 253 */ 254#ifndef LOCKDEBUG 255#define MUTEX_RAS_MASK (RAS_MKMASK(_mutex_enter_ras_start) \ 256 |RAS_MKMASK(_mutex_exit_ras_start)) 257#else 258#define MUTEX_RAS_MASK 0 259#endif 260 261#define RAS_MASK (RAS_MKMASK(_atomic_cas_ulong_ras_start) \ 262 |RAS_MKMASK(_atomic_cas_uint_ras_start) \ 263 |RAS_MKMASK(_ucas_ulong_ras_start) \ 264 |RAS_MKMASK(_ucas_uint_ras_start) \ 265 |MUTEX_RAS_MASK) 266 267/* 268 * The caller has already determined that 269 * _lock_ras_start == (k1 & -MIPS_LOCK_RAS_SIZE) 270 */ 271LEAF_NOPROFILE(_restart_lock_ras) 272 and t0, k1, MIPS_LOCK_RAS_SIZE - 1 273 /* look at addr bits in ras region */ 274 srl t0, 4 /* focus on each set of 16 bytes */ 275 li t1, 1 /* need this to make a bitmask */ 276 sllv t1, t1, t0 /* now we have a bitmask of the PC */ 277 andi t1, RAS_MASK /* was the PC in a RAS? */ 278 bnez t1, 1f /* yes, adjust PC */ 279 and t0, k1, 15 /* get offset in RAS */ 280 281 j ra 282 nop 2831: 284 addu t0, 4 /* bias offset by one more instruction */ 285 j ra 286 PTR_SUBU k1, t0 /* and subtract that from the PC */ 287END(_restart_lock_ras) 288 289/* 290 * int ras_ucas_uint(volatile u_int *ptr, u_int old, u_int new, u_int *retp); 291 */ 292STATIC_LEAF(ras_ucas_uint) 293 PTR_L v1, L_PCB(MIPS_CURLWP) 294 PTR_LA v0, _C_LABEL(ras_ucaserr) 295 PTR_S v0, PCB_ONFAULT(v1) 296 bltz a0, _C_LABEL(ras_ucaserr) 297 nop 298 b _C_LABEL(_ucas_uint_ras) 299 move v0, zero # assume success 300END(ras_ucas_uint) 301 302/* 303 * int ras_ucas_ulong(volatile u_long *ptr, u_long old, u_long new, u_long *retp); 304 */ 305STATIC_LEAF(ras_ucas_ulong) 306 PTR_L v1, L_PCB(MIPS_CURLWP) 307 PTR_LA v0, _C_LABEL(ras_ucaserr) 308 PTR_S v0, PCB_ONFAULT(v1) 309 bltz a0, _C_LABEL(ras_ucaserr) 310 nop 311 b _C_LABEL(_ucas_ulong_ras) 312 move v0, zero # assume success 313END(ras_ucas_ulong) 314 315/* 316 * 317 */ 318STATIC_LEAF(ras_ucaserr) 319 PTR_S zero, PCB_ONFAULT(v1) # reset fault handler 320 j ra 321 li v0, EFAULT # return EFAULT on error 322END(ras_ucaserr) 323 324#ifndef LOCKDEBUG 325/* 326 * void mutex_spin_enter(kmutex_t *mtx); 327 */ 328STATIC_NESTED(ras_mutex_spin_enter, CALLFRAME_SIZ, ra) 329 move t0, a0 330 PTR_L t2, L_CPU(MIPS_CURLWP) 331 INT_L a0, MTX_IPL(t0) 332#ifdef PARANOIA 333 INT_L ta1, CPU_INFO_CPL(t2) # get current cpl 334#endif 335 336 /* 337 * We need to raise our IPL. 338 * call splraise (only uses a0-a3, v0-v1, and ra) 339 */ 340 move t3, ra 341 jal _C_LABEL(splraise) 342 nop 343 move ra, t3 344 345 /* 346 * If this is the first lock of the mutex, store the previous IPL 347 * for exit. 348 */ 3491: 350 INT_L ta2, CPU_INFO_MTX_COUNT(t2) 351 nop 352 INT_ADDU ta3, ta2, -1 353 INT_S ta3, CPU_INFO_MTX_COUNT(t2) 354 355 bnez ta2, 2f 356 nop 357 INT_S v0, CPU_INFO_MTX_OLDSPL(t2) /* returned by splraise */ 3582: 359#if defined(DIAGNOSTIC) 360 INT_L t3, MTX_LOCK(t0) 361 li t1, 1 362 bnez t3, 3f 363 nop 364 j ra 365 INT_S t1, MTX_LOCK(t0) 3663: 367 j _C_LABEL(mutex_spin_retry) 368 nop 369#else /* DIAGNOSTIC */ 370 j ra 371 nop 372#endif /* DIAGNOSTIC */ 373END(ras_mutex_spin_enter) 374 375/* 376 * void mutex_spin_exit(kmutex_t *mtx); 377 */ 378LEAF(ras_mutex_spin_exit) 379 PTR_L t2, L_CPU(MIPS_CURLWP) 380 nop 381#if defined(DIAGNOSTIC) 382 INT_L t0, MTX_LOCK(a0) 383 nop 384 beqz t0, 2f 385 nop 386 INT_S zero, MTX_LOCK(a0) 387#endif 388 389 /* 390 * We need to grab this before the mutex count is incremented 391 * because if we get an interrupt, it may see the count as zero 392 * and overwrite the oldspl value with a bogus value. 393 */ 394#ifdef PARANOIA 395 INT_L a2, MTX_IPL(a0) 396#endif 397 INT_L a0, CPU_INFO_MTX_OLDSPL(t2) 398 399 /* 400 * Increment the mutex count 401 */ 402 INT_L t0, CPU_INFO_MTX_COUNT(t2) 403 nop 404 INT_ADDU t0, t0, 1 405 INT_S t0, CPU_INFO_MTX_COUNT(t2) 406 407 /* 408 * If the IPL doesn't change, nothing to do 409 */ 410 INT_L a1, CPU_INFO_CPL(t2) 411 nop 412 413#ifdef PARANOIA 414 sltu v0, a1, a2 # v0 = cpl < mtx_ipl 415 sltu v1, a1, a0 # v1 = cpl < oldspl 416 sll v0, 1 417 or v0, v1 41812: bnez v0, 12b # loop forever if either is true 419 nop 420#endif /* PARANOIA */ 421 422 beq a0, a1, 1f # if oldspl == cpl 423 nop # no reason to drop ipl 424 425 bltz t0, 1f # there are still holders 426 nop # so don't drop IPL 427 428 /* 429 * Mutex count is zero so we need to restore the old IPL 430 */ 431#ifdef PARANOIA 432 sltiu v0, a0, IPL_HIGH+1 43313: beqz v0, 13b # loop forever if ipl > IPL_HIGH 434 nop 435#endif 436 j _C_LABEL(splx) 437 nop 4381: 439 j ra 440 nop 441#if defined(DIAGNOSTIC) 4422: 443 j _C_LABEL(mutex_vector_exit) 444 nop 445#endif 446END(ras_mutex_spin_exit) 447#endif /* !LOCKDEBUG */ 448 449 .data 450EXPORT(mips_locore_atomicvec) 451 PTR_WORD ras_atomic_cas_uint 452 PTR_WORD ras_atomic_cas_ulong 453 PTR_WORD ras_ucas_uint 454 PTR_WORD ras_ucas_ulong 455#ifdef LOCKDEBUG 456 PTR_WORD mutex_enter 457 PTR_WORD mutex_exit 458 PTR_WORD mutex_spin_enter 459 PTR_WORD mutex_spin_exit 460#else 461 PTR_WORD ras_mutex_enter 462 PTR_WORD ras_mutex_exit 463 PTR_WORD ras_mutex_spin_enter 464 PTR_WORD ras_mutex_spin_exit 465#endif /* !LOCKDEBUG */ 466