1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License, Version 1.0 only
   6  * (the "License").  You may not use this file except in compliance
   7  * with the License.
   8  *
   9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10  * or http://www.opensolaris.org/os/licensing.
  11  * See the License for the specific language governing permissions
  12  * and limitations under the License.
  13  *
  14  * When distributing Covered Code, include this CDDL HEADER in each
  15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16  * If applicable, add the following below this CDDL HEADER, with the
  17  * fields enclosed by brackets "[]" replaced with your own identifying
  18  * information: Portions Copyright [yyyy] [name of copyright owner]
  19  *
  20  * CDDL HEADER END
  21  */
  22 /*
  23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 #pragma ident   "%Z%%M% %I%     %E% SMI"
  28 
  29 #include <sys/param.h>
  30 #include <sys/errno.h>
  31 #include <sys/asm_linkage.h>
  32 #include <sys/vtrace.h>
  33 #include <sys/machthread.h>
  34 #include <sys/clock.h>
  35 #include <sys/asi.h>
  36 #include <sys/fsr.h>
  37 #include <sys/privregs.h>
  38 
  39 #if !defined(lint)
  40 #include "assym.h"
  41 #endif  /* lint */
  42 
  43 #define FP_USED 1
  44 #define LOFAULT_SET 2
  45 
  46 /*
  47  * Error barrier:
  48  * We use membar sync to establish an error barrier for
  49  * deferred errors. Membar syncs are added before any update
  50  * to t_lofault to ensure that deferred errors from earlier
  51  * accesses will not be reported after the membar. This error
  52  * isolation is important when we try to recover from async
  53  * errors which tries to distinguish kernel accesses to user
  54  * data.
  55  */
  56 
  57 /*
  58  * Zero a block of storage.
  59  *
  60  * uzero is used by the kernel to zero a block in user address space.
  61  */
  62 
  63 #if defined(lint)
  64 
  65 /* ARGSUSED */
  66 int
  67 kzero(void *addr, size_t count)
  68 { return(0); }
  69 
  70 /* ARGSUSED */
  71 void
  72 uzero(void *addr, size_t count)
  73 {}
  74 
  75 #else   /* lint */
  76 
  77         ENTRY(uzero)
  78         !
  79         ! Set a new lo_fault handler only if we came in with one
  80         ! already specified.
  81         !
  82         wr      %g0, ASI_USER, %asi
  83         ldn     [THREAD_REG + T_LOFAULT], %o5
  84         tst     %o5
  85         bz,pt   %ncc, .do_zero
  86         sethi   %hi(.zeroerr), %o2
  87         or      %o2, %lo(.zeroerr), %o2
  88         membar  #Sync
  89         ba,pt   %ncc, .do_zero
  90         stn     %o2, [THREAD_REG + T_LOFAULT]
  91 
  92         ENTRY(kzero)
  93         !
  94         ! Always set a lo_fault handler
  95         !
  96         wr      %g0, ASI_P, %asi
  97         ldn     [THREAD_REG + T_LOFAULT], %o5
  98         sethi   %hi(.zeroerr), %o2
  99         or      %o5, LOFAULT_SET, %o5
 100         or      %o2, %lo(.zeroerr), %o2
 101         membar  #Sync
 102         ba,pt   %ncc, .do_zero
 103         stn     %o2, [THREAD_REG + T_LOFAULT]
 104 
 105 /*
 106  * We got here because of a fault during kzero or if
 107  * uzero or bzero was called with t_lofault non-zero.
 108  * Otherwise we've already run screaming from the room.
 109  * Errno value is in %g1. Note that we're here iff
 110  * we did set t_lofault.
 111  */
 112 .zeroerr:
 113         !
 114         ! Undo asi register setting. Just set it to be the
 115         ! kernel default without checking.
 116         !
 117         wr      %g0, ASI_P, %asi
 118         !
 119         ! If saved t_lofault has FP_USED set, clear the %fprs register
 120         !
 121         btst    FP_USED, %o5
 122         bz,pt   %ncc, 1f                ! skip if not used
 123         nop
 124         membar #Sync
 125         wr      %g0, %g0, %fprs         ! clear fprs
 126         andn    %o5, FP_USED, %o5       ! turn off flag bit
 127         !
 128         ! We did set t_lofault. It may well have been zero coming in.
 129         !
 130 1:
 131         tst     %o5
 132         membar #Sync
 133         bne,pn  %ncc, 3f                
 134         andncc  %o5, LOFAULT_SET, %o5
 135 2:
 136         !
 137         ! Old handler was zero. Just return the error.
 138         !
 139         retl                            ! return
 140         mov     %g1, %o0                ! error code from %g1
 141 3:
 142         !
 143         ! We're here because %o5 was non-zero. It was non-zero
 144         ! because either LOFAULT_SET was present, a previous fault
 145         ! handler was present or both. In all cases we need to reset
 146         ! T_LOFAULT to the value of %o5 after clearing LOFAULT_SET
 147         ! before we either simply return the error or we invoke the
 148         ! previously specified handler.
 149         !
 150         be      %ncc, 2b
 151         stn     %o5, [THREAD_REG + T_LOFAULT]
 152         jmp     %o5                     ! goto real handler
 153           nop
 154         SET_SIZE(kzero)
 155         SET_SIZE(uzero)
 156 
 157 #endif  /* lint */
 158 
 159 /*
 160  * Zero a block of storage.
 161  */
 162 
 163 #if defined(lint)
 164 
 165 /* ARGSUSED */
 166 void
 167 bzero(void *addr, size_t count)
 168 {}
 169 
 170 #else   /* lint */
 171 
 172         ENTRY(bzero)
 173         wr      %g0, ASI_P, %asi
 174 
 175         ldn     [THREAD_REG + T_LOFAULT], %o5   ! save old vector
 176         tst     %o5
 177         bz,pt   %ncc, .do_zero
 178         sethi   %hi(.zeroerr), %o2
 179         or      %o2, %lo(.zeroerr), %o2
 180         membar  #Sync                           ! sync error barrier
 181         stn     %o2, [THREAD_REG + T_LOFAULT]   ! install new vector
 182 
 183 .do_zero:
 184         cmp     %o1, 15                 ! check for small counts
 185         blu,pn  %ncc, .byteclr          ! just clear bytes
 186         nop
 187 
 188         cmp     %o1, 192                ! check for large counts
 189         blu     %ncc, .bzero_small
 190         nop
 191 
 192         sethi   %hi(use_hw_bzero), %o2
 193         ld      [%o2 + %lo(use_hw_bzero)], %o2
 194         tst     %o2
 195         bz      %icc, .bzero_small
 196         nop
 197 
 198         rd      %fprs, %o2              ! check for unused fp
 199         btst    FPRS_FEF, %o2
 200         bnz     %icc, .bzero_small
 201         nop
 202 
 203         ldn     [THREAD_REG + T_LWP], %o2
 204         tst     %o2
 205         bz,pn   %ncc, .bzero_small
 206         nop
 207 
 208         ! Check for block alignment
 209         btst    (64-1), %o0
 210         bz      %icc, .bzl_block
 211         nop
 212 
 213         ! Check for double-word alignment
 214         btst    (8-1), %o0
 215         bz      %icc, .bzl_dword
 216         nop
 217 
 218         ! Check for word alignment
 219         btst    (4-1), %o0
 220         bz      %icc, .bzl_word
 221         nop
 222 
 223         ! Clear bytes until word aligned
 224 .bzl_byte:
 225         stba    %g0, [%o0]%asi
 226         add     %o0, 1, %o0
 227         btst    (4-1), %o0
 228         bnz     %icc, .bzl_byte
 229         sub     %o1, 1, %o1
 230 
 231         ! Check for dword-aligned
 232         btst    (8-1), %o0
 233         bz      %icc, .bzl_dword
 234         nop
 235         
 236         ! Clear words until double-word aligned
 237 .bzl_word:
 238         sta     %g0, [%o0]%asi
 239         add     %o0, 4, %o0
 240         btst    (8-1), %o0
 241         bnz     %icc, .bzl_word
 242         sub     %o1, 4, %o1
 243 
 244 .bzl_dword:
 245         ! Clear dwords until block aligned
 246         stxa    %g0, [%o0]%asi
 247         add     %o0, 8, %o0
 248         btst    (64-1), %o0
 249         bnz     %icc, .bzl_dword
 250         sub     %o1, 8, %o1
 251 
 252 .bzl_block:
 253         membar  #StoreStore|#StoreLoad|#LoadStore
 254         wr      %g0, FPRS_FEF, %fprs
 255 
 256         ! Set the lower bit in the saved t_lofault to indicate
 257         ! that we need to clear the %fprs register on the way
 258         ! out
 259         or      %o5, FP_USED, %o5
 260 
 261         ! Clear block
 262         fzero   %d0
 263         fzero   %d2
 264         fzero   %d4
 265         fzero   %d6
 266         fzero   %d8
 267         fzero   %d10
 268         fzero   %d12
 269         fzero   %d14
 270         rd      %asi, %o3
 271         wr      %g0, ASI_BLK_P, %asi
 272         cmp     %o3, ASI_P
 273         bne,a   %icc, 1f
 274         wr      %g0, ASI_BLK_AIUS, %asi
 275 1:      
 276         mov     256, %o3
 277         ba,pt   %ncc, .bzl_doblock
 278         nop
 279 
 280 .bzl_blkstart:  
 281       ! stda    %d0, [%o0+192]%asi  ! in dly slot of branch that got us here
 282         stda    %d0, [%o0+128]%asi
 283         stda    %d0, [%o0+64]%asi
 284         stda    %d0, [%o0]%asi
 285 .bzl_zinst:
 286         add     %o0, %o3, %o0
 287         sub     %o1, %o3, %o1
 288 .bzl_doblock:
 289         cmp     %o1, 256
 290         bgeu,a  %ncc, .bzl_blkstart
 291         stda    %d0, [%o0+192]%asi
 292 
 293         cmp     %o1, 64
 294         blu     %ncc, .bzl_finish
 295         
 296         andn    %o1, (64-1), %o3
 297         srl     %o3, 4, %o2             ! using blocks, 1 instr / 16 words
 298         set     .bzl_zinst, %o4
 299         sub     %o4, %o2, %o4
 300         jmp     %o4
 301         nop
 302 
 303 .bzl_finish:
 304         membar  #StoreLoad|#StoreStore
 305         wr      %g0, %g0, %fprs
 306         andn    %o5, FP_USED, %o5
 307 
 308         rd      %asi, %o4
 309         wr      %g0, ASI_P, %asi
 310         cmp     %o4, ASI_BLK_P
 311         bne,a   %icc, 1f
 312         wr      %g0, ASI_USER, %asi
 313 1:
 314 
 315 .bzlf_dword:
 316         ! double words
 317         cmp     %o1, 8
 318         blu     %ncc, .bzlf_word
 319         nop
 320         stxa    %g0, [%o0]%asi
 321         add     %o0, 8, %o0
 322         sub     %o1, 8, %o1
 323         ba,pt   %ncc, .bzlf_dword
 324         nop
 325 
 326 .bzlf_word:
 327         ! words
 328         cmp     %o1, 4
 329         blu     %ncc, .bzlf_byte
 330         nop
 331         sta     %g0, [%o0]%asi
 332         add     %o0, 4, %o0
 333         sub     %o1, 4, %o1
 334         ba,pt   %ncc, .bzlf_word
 335         nop
 336 
 337 1:
 338         add     %o0, 1, %o0             ! increment address
 339 .bzlf_byte:
 340         subcc   %o1, 1, %o1             ! decrement count
 341         bgeu,a  %ncc, 1b
 342         stba    %g0, [%o0]%asi          ! zero a byte
 343 
 344         !
 345         ! If we used the FP registers, that bit was turned
 346         ! off after we were finished. We're just concerned with
 347         ! whether t_lofault was set when we came in. We end up
 348         ! here from either kzero() or bzero(). kzero() *always*
 349         ! sets a lofault handler. It ors LOFAULT_SET into %o5 
 350         ! to indicate it has done this even if the value of %o5
 351         ! is otherwise zero. bzero() sets a lofault handler *only*
 352         ! if one was previously set. Accordingly we need to examine
 353         ! %o5 and if it is non-zero be sure to clear LOFAULT_SET
 354         ! before resetting the error handler.
 355         !
 356         tst     %o5
 357         bz,pt   %ncc, 1f        
 358         andn    %o5, LOFAULT_SET, %o5
 359         membar  #Sync                           ! sync error barrier
 360         stn     %o5, [THREAD_REG + T_LOFAULT]   ! restore old t_lofault
 361 1:
 362         retl
 363         clr     %o0                     ! return (0)
 364 
 365 .bzero_small:
 366 
 367         !
 368         ! Check for word alignment.
 369         !
 370         btst    3, %o0
 371         bz      .bzero_probe
 372         mov     0x100, %o3              ! constant size of main loop
 373         !
 374         !
 375         ! clear bytes until word aligned
 376         !
 377 1:      stba    %g0,[%o0]%asi
 378         add     %o0, 1, %o0
 379         btst    3, %o0
 380         bnz     1b
 381         sub     %o1, 1, %o1
 382 .bzero_probe:
 383 
 384         !
 385         ! if needed move a word to become double-word aligned.
 386         !
 387         btst    7, %o0                  ! is double aligned?
 388         bz      %icc, .bzero_nobuf
 389         nop
 390         sta     %g0, [%o0]%asi          ! clr to double boundry
 391         sub     %o1, 4, %o1
 392         ba,pt   %ncc, .bzero_nobuf
 393         add     %o0, 4, %o0
 394 
 395         !stxa   %g0, [%o0+0xf8]%asi
 396 .bzero_blk:
 397         stxa    %g0, [%o0+0xf0]%asi
 398         stxa    %g0, [%o0+0xe8]%asi
 399         stxa    %g0, [%o0+0xe0]%asi
 400         stxa    %g0, [%o0+0xd8]%asi
 401         stxa    %g0, [%o0+0xd0]%asi
 402         stxa    %g0, [%o0+0xc8]%asi
 403         stxa    %g0, [%o0+0xc0]%asi
 404         stxa    %g0, [%o0+0xb8]%asi
 405         stxa    %g0, [%o0+0xb0]%asi
 406         stxa    %g0, [%o0+0xa8]%asi
 407         stxa    %g0, [%o0+0xa0]%asi
 408         stxa    %g0, [%o0+0x98]%asi
 409         stxa    %g0, [%o0+0x90]%asi
 410         stxa    %g0, [%o0+0x88]%asi
 411         stxa    %g0, [%o0+0x80]%asi
 412         stxa    %g0, [%o0+0x78]%asi
 413         stxa    %g0, [%o0+0x70]%asi
 414         stxa    %g0, [%o0+0x68]%asi
 415         stxa    %g0, [%o0+0x60]%asi
 416         stxa    %g0, [%o0+0x58]%asi
 417         stxa    %g0, [%o0+0x50]%asi
 418         stxa    %g0, [%o0+0x48]%asi
 419         stxa    %g0, [%o0+0x40]%asi
 420         stxa    %g0, [%o0+0x38]%asi
 421         stxa    %g0, [%o0+0x30]%asi
 422         stxa    %g0, [%o0+0x28]%asi
 423         stxa    %g0, [%o0+0x20]%asi
 424         stxa    %g0, [%o0+0x18]%asi
 425         stxa    %g0, [%o0+0x10]%asi
 426         stxa    %g0, [%o0+0x08]%asi
 427         stxa    %g0, [%o0]%asi
 428 .zinst:
 429         add     %o0, %o3, %o0           ! increment source address
 430         sub     %o1, %o3, %o1           ! decrement count
 431 .bzero_nobuf:
 432         cmp     %o1, 0x100              ! can we do whole chunk?
 433         bgeu,a  %ncc, .bzero_blk
 434         stxa    %g0, [%o0+0xf8]%asi     ! do first double of chunk
 435 
 436         cmp     %o1, 7                  ! can we zero any more double words
 437         bleu    %ncc, .byteclr          ! too small go zero bytes
 438 
 439         andn    %o1, 7, %o3             ! %o3 bytes left, double-word aligned
 440         srl     %o3, 1, %o2             ! using doubles, need 1 instr / 2 words
 441         set     .zinst, %o4             ! address of clr instructions
 442         sub     %o4, %o2, %o4           ! jmp address relative to instr
 443         jmp     %o4
 444         nop
 445         !
 446         ! do leftover bytes
 447         !
 448 3:
 449         add     %o0, 1, %o0             ! increment address
 450 .byteclr:
 451         subcc   %o1, 1, %o1             ! decrement count
 452         bgeu,a  %ncc, 3b
 453         stba    %g0, [%o0]%asi          ! zero a byte
 454 
 455 .bzero_finished:
 456         !
 457         ! We're just concerned with whether t_lofault was set
 458         ! when we came in. We end up here from either kzero()
 459         ! or bzero(). kzero() *always* sets a lofault handler.
 460         ! It ors LOFAULT_SET into %o5 to indicate it has done
 461         ! this even if the value of %o5 is otherwise zero.
 462         ! bzero() sets a lofault handler *only* if one was
 463         ! previously set. Accordingly we need to examine
 464         ! %o5 and if it is non-zero be sure to clear LOFAULT_SET
 465         ! before resetting the error handler.
 466         !
 467         tst     %o5
 468         bz      %ncc, 1f
 469         andn    %o5, LOFAULT_SET, %o5
 470         membar  #Sync                           ! sync error barrier
 471         stn     %o5, [THREAD_REG + T_LOFAULT]   ! restore old t_lofault
 472 1:
 473         retl
 474         clr     %o0                     ! return (0)
 475 
 476         SET_SIZE(bzero)
 477 #endif  /* lint */