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 (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright (c) 1993, 2010, Oracle and/or its affiliates. All rights reserved.
  23  */
  24  
  25 /*
  26  * Process switching routines.
  27  */
  28 
  29 #if !defined(lint)
  30 #include "assym.h"
  31 #else   /* lint */
  32 #include <sys/thread.h>
  33 #endif  /* lint */
  34 
  35 #include <sys/param.h>
  36 #include <sys/asm_linkage.h>
  37 #include <sys/mmu.h>
  38 #include <sys/pcb.h>
  39 #include <sys/machthread.h>
  40 #include <sys/machclock.h>
  41 #include <sys/privregs.h>
  42 #include <sys/vtrace.h>
  43 #include <vm/hat_sfmmu.h>
  44 
  45 /*
  46  * resume(kthread_id_t)
  47  *
  48  * a thread can only run on one processor at a time. there
  49  * exists a window on MPs where the current thread on one
  50  * processor is capable of being dispatched by another processor.
  51  * some overlap between outgoing and incoming threads can happen
  52  * when they are the same thread. in this case where the threads
  53  * are the same, resume() on one processor will spin on the incoming 
  54  * thread until resume() on the other processor has finished with
  55  * the outgoing thread.
  56  *
  57  * The MMU context changes when the resuming thread resides in a different
  58  * process.  Kernel threads are known by resume to reside in process 0.
  59  * The MMU context, therefore, only changes when resuming a thread in
  60  * a process different from curproc.
  61  *
  62  * resume_from_intr() is called when the thread being resumed was not 
  63  * passivated by resume (e.g. was interrupted).  This means that the
  64  * resume lock is already held and that a restore context is not needed.
  65  * Also, the MMU context is not changed on the resume in this case.
  66  *
  67  * resume_from_zombie() is the same as resume except the calling thread
  68  * is a zombie and must be put on the deathrow list after the CPU is
  69  * off the stack.
  70  */
  71 
  72 #if defined(lint)
  73 
  74 /* ARGSUSED */
  75 void
  76 resume(kthread_id_t t)
  77 {}
  78 
  79 #else   /* lint */
  80 
  81         ENTRY(resume)
  82         save    %sp, -SA(MINFRAME), %sp         ! save ins and locals
  83 
  84         call    __dtrace_probe___sched_off__cpu ! DTrace probe
  85         mov     %i0, %o0                        ! arg for DTrace probe
  86 
  87         membar  #Sync                           ! flush writebuffers
  88         flushw                                  ! flushes all but this window
  89 
  90         stn     %i7, [THREAD_REG + T_PC]        ! save return address
  91         stn     %fp, [THREAD_REG + T_SP]        ! save sp
  92 
  93         !
  94         ! Save GSR (Graphics Status Register).
  95         !
  96         ! Read fprs, call fp_save if FPRS_FEF set.
  97         ! This handles floating-point state saving.
  98         ! The fprs could be turned on by hw bcopy software,
  99         ! *or* by fp_disabled. Handle it either way.
 100         !
 101         ldn     [THREAD_REG + T_LWP], %o4       ! get lwp pointer
 102         rd      %fprs, %g4                      ! read fprs
 103         brnz,pt %o4, 0f                         ! if user thread skip
 104           ldn   [THREAD_REG + T_CPU], %i1       ! get CPU pointer
 105 
 106         !
 107         ! kernel thread
 108         !       
 109         ! we save fprs at the beginning the stack so we know
 110         ! where to check at resume time
 111         ldn     [THREAD_REG + T_STACK], %i2
 112         ldn     [THREAD_REG + T_CTX], %g3       ! get ctx pointer
 113         andcc   %g4, FPRS_FEF, %g0              ! is FPRS_FEF set
 114         bz,pt   %icc, 1f                        ! nope, skip
 115           st    %g4, [%i2 + SA(MINFRAME) + FPU_FPRS]    ! save fprs
 116           
 117         ! save kernel fp state in stack
 118         add     %i2, SA(MINFRAME), %o0          ! o0 = kfpu_t ptr
 119         rd      %gsr, %g5
 120         call    fp_save
 121         stx     %g5, [%o0 + FPU_GSR]            ! store GSR
 122         ba,a,pt %icc, 1f
 123           nop
 124 
 125 0:
 126         ! user thread
 127         ! o4 = lwp ptr
 128         ! g4 = fprs
 129         ! i1 = CPU ptr
 130         ldn     [%o4 + LWP_FPU], %o0            ! fp pointer
 131         stn     %fp, [THREAD_REG + T_SP]        ! save sp
 132         andcc   %g4, FPRS_FEF, %g0              ! is FPRS_FEF set
 133         st      %g4, [%o0 + FPU_FPRS]           ! store FPRS
 134 #if defined(DEBUG) || defined(NEED_FPU_EXISTS)
 135         sethi   %hi(fpu_exists), %g5
 136         ld      [%g5 + %lo(fpu_exists)], %g5
 137         brz,pn  %g5, 1f
 138           ldn   [THREAD_REG + T_CTX], %g3       ! get ctx pointer
 139 #endif
 140         bz,pt   %icc, 1f                        ! most apps don't use fp
 141           ldn   [THREAD_REG + T_CTX], %g3       ! get ctx pointer
 142         ldn     [%o4 + LWP_FPU], %o0            ! fp pointer
 143         rd      %gsr, %g5
 144         call    fp_save                         ! doesn't touch globals
 145         stx     %g5, [%o0 + FPU_GSR]            ! store GSR
 146 1:
 147         !
 148         ! Perform context switch callback if set.
 149         ! This handles coprocessor state saving.
 150         ! i1 = cpu ptr
 151         ! g3 = ctx pointer
 152         !
 153         wr      %g0, %g0, %fprs                 ! disable fpu and clear fprs
 154         brz,pt  %g3, 2f                         ! skip call when zero
 155         ldn     [%i0 + T_PROCP], %i3            ! delay slot - get proc pointer
 156         call    savectx
 157         mov     THREAD_REG, %o0                 ! delay - arg = thread pointer
 158 2:
 159         ldn     [THREAD_REG + T_PROCP], %i2     ! load old curproc - for mmu
 160 
 161         !
 162         ! Temporarily switch to idle thread's stack
 163         !
 164         ldn     [%i1 + CPU_IDLE_THREAD], %o0    ! idle thread pointer
 165         ldn     [%o0 + T_SP], %o1               ! get onto idle thread stack
 166         sub     %o1, SA(MINFRAME), %sp          ! save room for ins and locals
 167         clr     %fp
 168 
 169         !
 170         ! Set the idle thread as the current thread
 171         !
 172         mov     THREAD_REG, %l3                 ! save %g7 (current thread)
 173         mov     %o0, THREAD_REG                 ! set %g7 to idle
 174         stn     %o0, [%i1 + CPU_THREAD]         ! set CPU's thread to idle
 175 
 176         !
 177         ! Clear and unlock previous thread's t_lock
 178         ! to allow it to be dispatched by another processor.
 179         !
 180         clrb    [%l3 + T_LOCK]                  ! clear tp->t_lock
 181 
 182         !
 183         ! IMPORTANT: Registers at this point must be:
 184         !       %i0 = new thread
 185         !       %i1 = cpu pointer
 186         !       %i2 = old proc pointer
 187         !       %i3 = new proc pointer
 188         !       
 189         ! Here we are in the idle thread, have dropped the old thread.
 190         ! 
 191         ALTENTRY(_resume_from_idle)
 192 
 193         ! SET_KCONTEXTREG(reg0, reg1, reg2, reg3, reg4, label1, label2, label3)
 194         SET_KCONTEXTREG(%o0, %g1, %g2, %g3, %o3, l1, l2, l3)
 195 
 196         cmp     %i2, %i3                ! resuming the same process?
 197         be,pt   %xcc, 5f                ! yes.
 198           nop
 199 
 200         ldx     [%i3 + P_AS], %o0       ! load p->p_as
 201         ldx     [%o0 + A_HAT], %i5      ! %i5 = new proc hat
 202 
 203         !
 204         ! update cpusran field
 205         !
 206         ld      [%i1 + CPU_ID], %o4
 207         add     %i5, SFMMU_CPUSRAN, %o5
 208         CPU_INDEXTOSET(%o5, %o4, %g1)
 209         ldx     [%o5], %o2              ! %o2 = cpusran field
 210         mov     1, %g2
 211         sllx    %g2, %o4, %o4           ! %o4 = bit for this cpu
 212         andcc   %o4, %o2, %g0
 213         bnz,pn  %xcc, 0f                ! bit already set, go to 0
 214           nop
 215 3:
 216         or      %o2, %o4, %o1           ! or in this cpu's bit mask
 217         casx    [%o5], %o2, %o1
 218         cmp     %o2, %o1
 219         bne,a,pn %xcc, 3b
 220           ldx   [%o5], %o2              ! o2 = cpusran field
 221         membar  #LoadLoad|#StoreLoad
 222 
 223 0:
 224         ! 
 225         ! disable interrupts
 226         !
 227         ! if resume from user to kernel thread
 228         !       call sfmmu_setctx_sec
 229         ! if resume from kernel (or a different user) thread to user thread
 230         !       call sfmmu_alloc_ctx
 231         ! sfmmu_load_mmustate
 232         !
 233         ! enable interrupts
 234         !
 235         ! %i5 = new proc hat
 236         !
 237 
 238         sethi   %hi(ksfmmup), %o2
 239         ldx     [%o2 + %lo(ksfmmup)], %o2
 240 
 241         rdpr    %pstate, %i4
 242         cmp     %i5, %o2                ! new proc hat == ksfmmup ?
 243         bne,pt  %xcc, 3f                ! new proc is not kernel as, go to 3
 244           wrpr  %i4, PSTATE_IE, %pstate
 245 
 246         SET_KAS_CTXSEC_ARGS(%i5, %o0, %o1)
 247 
 248         ! new proc is kernel as
 249 
 250         call    sfmmu_setctx_sec                ! switch to kernel context
 251           or    %o0, %o1, %o0
 252         
 253         ba,a,pt %icc, 4f
 254 
 255         !
 256         ! Switch to user address space.
 257         !
 258 3:
 259         mov     %i5, %o0                        ! %o0 = sfmmup
 260         mov     %i1, %o2                        ! %o2 = CPU
 261         set     SFMMU_PRIVATE, %o3              ! %o3 = sfmmu private flag
 262         call    sfmmu_alloc_ctx
 263           mov   %g0, %o1                        ! %o1 = allocate flag = 0
 264 
 265         brz,a,pt %o0, 4f                        ! %o0 == 0, no private alloc'ed
 266           nop
 267 
 268         ldn     [%i5 + SFMMU_SCDP], %o0         ! using shared contexts?
 269         brz,a,pt %o0, 4f
 270           nop
 271 
 272         ldn   [%o0 + SCD_SFMMUP], %o0           ! %o0 = scdp->scd_sfmmup
 273         mov     %i1, %o2                        ! %o2 = CPU
 274         set     SFMMU_SHARED, %o3               ! %o3 = sfmmu shared flag
 275         call    sfmmu_alloc_ctx
 276           mov   1, %o1                          ! %o1 = allocate flag = 1
 277         
 278 4:
 279         call    sfmmu_load_mmustate             ! program MMU registers
 280           mov   %i5, %o0
 281 
 282         wrpr    %g0, %i4, %pstate               ! enable interrupts
 283         
 284 5:
 285         !
 286         ! spin until dispatched thread's mutex has
 287         ! been unlocked. this mutex is unlocked when
 288         ! it becomes safe for the thread to run.
 289         ! 
 290         ldstub  [%i0 + T_LOCK], %o0     ! lock curthread's t_lock
 291 6:
 292         brnz,pn %o0, 7f                 ! lock failed
 293           ldx   [%i0 + T_PC], %i7       ! delay - restore resuming thread's pc
 294 
 295         !
 296         ! Fix CPU structure to indicate new running thread.
 297         ! Set pointer in new thread to the CPU structure.
 298         ! XXX - Move migration statistic out of here
 299         !
 300         ldx     [%i0 + T_CPU], %g2      ! last CPU to run the new thread
 301         cmp     %g2, %i1                ! test for migration
 302         be,pt   %xcc, 4f                ! no migration
 303           ldn   [%i0 + T_LWP], %o1      ! delay - get associated lwp (if any)
 304         ldx     [%i1 + CPU_STATS_SYS_CPUMIGRATE], %g2
 305         inc     %g2
 306         stx     %g2, [%i1 + CPU_STATS_SYS_CPUMIGRATE]
 307         stx     %i1, [%i0 + T_CPU]      ! set new thread's CPU pointer
 308 4:
 309         stx     %i0, [%i1 + CPU_THREAD] ! set CPU's thread pointer
 310         membar  #StoreLoad              ! synchronize with mutex_exit()
 311         mov     %i0, THREAD_REG         ! update global thread register
 312         stx     %o1, [%i1 + CPU_LWP]    ! set CPU's lwp ptr
 313         brz,a,pn %o1, 1f                ! if no lwp, branch and clr mpcb
 314           stx   %g0, [%i1 + CPU_MPCB]
 315         !
 316         ! user thread
 317         ! o1 = lwp
 318         ! i0 = new thread
 319         !
 320         ldx     [%i0 + T_STACK], %o0
 321         stx     %o0, [%i1 + CPU_MPCB]   ! set CPU's mpcb pointer
 322 #ifdef CPU_MPCB_PA
 323         ldx     [%o0 + MPCB_PA], %o0
 324         stx     %o0, [%i1 + CPU_MPCB_PA]
 325 #endif
 326         ! Switch to new thread's stack
 327         ldx     [%i0 + T_SP], %o0       ! restore resuming thread's sp
 328         sub     %o0, SA(MINFRAME), %sp  ! in case of intr or trap before restore
 329         mov     %o0, %fp
 330         !
 331         ! Restore resuming thread's GSR reg and floating-point regs
 332         ! Note that the ld to the gsr register ensures that the loading of
 333         ! the floating point saved state has completed without necessity
 334         ! of a membar #Sync.
 335         !
 336 #if defined(DEBUG) || defined(NEED_FPU_EXISTS)
 337         sethi   %hi(fpu_exists), %g3
 338         ld      [%g3 + %lo(fpu_exists)], %g3
 339         brz,pn  %g3, 2f
 340           ldx   [%i0 + T_CTX], %i5      ! should resumed thread restorectx?
 341 #endif
 342         ldx     [%o1 + LWP_FPU], %o0            ! fp pointer
 343         ld      [%o0 + FPU_FPRS], %g5           ! get fpu_fprs
 344         andcc   %g5, FPRS_FEF, %g0              ! is FPRS_FEF set?
 345         bz,a,pt %icc, 9f                        ! no, skip fp_restore
 346           wr    %g0, FPRS_FEF, %fprs            ! enable fprs so fp_zero works
 347 
 348         ldx     [THREAD_REG + T_CPU], %o4       ! cpu pointer
 349         call    fp_restore
 350           wr    %g5, %g0, %fprs                 ! enable fpu and restore fprs
 351 
 352         ldx     [%o0 + FPU_GSR], %g5            ! load saved GSR data
 353         wr      %g5, %g0, %gsr                  ! restore %gsr data
 354         ba,pt   %icc,2f
 355           ldx   [%i0 + T_CTX], %i5      ! should resumed thread restorectx?
 356 
 357 9:
 358         !
 359         ! Zero resuming thread's fp registers, for *all* non-fp program
 360         ! Remove all possibility of using the fp regs as a "covert channel".
 361         !
 362         call    fp_zero
 363           wr    %g0, %g0, %gsr
 364         ldx     [%i0 + T_CTX], %i5      ! should resumed thread restorectx?
 365         ba,pt   %icc, 2f
 366           wr    %g0, %g0, %fprs                 ! disable fprs
 367 
 368 1:
 369 #ifdef CPU_MPCB_PA
 370         mov     -1, %o1
 371         stx     %o1, [%i1 + CPU_MPCB_PA]
 372 #endif
 373         !
 374         ! kernel thread
 375         ! i0 = new thread
 376         !
 377         ! Switch to new thread's stack
 378         !
 379         ldx     [%i0 + T_SP], %o0       ! restore resuming thread's sp
 380         sub     %o0, SA(MINFRAME), %sp  ! in case of intr or trap before restore
 381         mov     %o0, %fp
 382         !
 383         ! Restore resuming thread's GSR reg and floating-point regs
 384         ! Note that the ld to the gsr register ensures that the loading of
 385         ! the floating point saved state has completed without necessity
 386         ! of a membar #Sync.
 387         !
 388         ldx     [%i0 + T_STACK], %o0
 389         ld      [%o0 + SA(MINFRAME) + FPU_FPRS], %g5    ! load fprs
 390         ldx     [%i0 + T_CTX], %i5              ! should thread restorectx?
 391         andcc   %g5, FPRS_FEF, %g0              ! did we save fp in stack?
 392         bz,a,pt %icc, 2f
 393           wr    %g0, %g0, %fprs                 ! clr fprs
 394 
 395         wr      %g5, %g0, %fprs                 ! enable fpu and restore fprs
 396         call    fp_restore
 397         add     %o0, SA(MINFRAME), %o0          ! o0 = kpu_t ptr
 398         ldx     [%o0 + FPU_GSR], %g5            ! load saved GSR data
 399         wr      %g5, %g0, %gsr                  ! restore %gsr data
 400 
 401 2:
 402         !
 403         ! Restore resuming thread's context
 404         ! i5 = ctx ptr
 405         !
 406         brz,a,pt %i5, 8f                ! skip restorectx() when zero
 407           ld    [%i1 + CPU_BASE_SPL], %o0
 408         call    restorectx              ! thread can not sleep on temp stack
 409           mov   THREAD_REG, %o0         ! delay slot - arg = thread pointer
 410         !
 411         ! Set priority as low as possible, blocking all interrupt threads
 412         ! that may be active.
 413         !
 414         ld      [%i1 + CPU_BASE_SPL], %o0
 415 8:
 416         wrpr    %o0, 0, %pil
 417         wrpr    %g0, WSTATE_KERN, %wstate
 418         !
 419         ! If we are resuming an interrupt thread, store a starting timestamp
 420         ! in the thread structure.
 421         !
 422         lduh    [THREAD_REG + T_FLAGS], %o0
 423         andcc   %o0, T_INTR_THREAD, %g0
 424         bnz,pn  %xcc, 0f
 425           nop
 426 5:              
 427         call    __dtrace_probe___sched_on__cpu  ! DTrace probe
 428         nop
 429 
 430         ret                             ! resume curthread
 431         restore
 432 0:
 433         add     THREAD_REG, T_INTR_START, %o2
 434 1:      
 435         ldx     [%o2], %o1
 436         RD_CLOCK_TICK(%o0,%o3,%g5,__LINE__)
 437         casx    [%o2], %o1, %o0
 438         cmp     %o0, %o1
 439         be,pt   %xcc, 5b
 440           nop
 441         ! If an interrupt occurred while we were attempting to store
 442         ! the timestamp, try again.
 443         ba,pt   %xcc, 1b
 444           nop
 445 
 446         !
 447         ! lock failed - spin with regular load to avoid cache-thrashing.
 448         !
 449 7:
 450         brnz,a,pt %o0, 7b               ! spin while locked
 451           ldub  [%i0 + T_LOCK], %o0
 452         ba      %xcc, 6b
 453           ldstub  [%i0 + T_LOCK], %o0   ! delay - lock curthread's mutex
 454         SET_SIZE(_resume_from_idle)
 455         SET_SIZE(resume)
 456 
 457 #endif  /* lint */
 458 
 459 #if defined(lint)
 460 
 461 /* ARGSUSED */
 462 void
 463 resume_from_zombie(kthread_id_t t)
 464 {}
 465 
 466 #else   /* lint */
 467 
 468         ENTRY(resume_from_zombie)
 469         save    %sp, -SA(MINFRAME), %sp         ! save ins and locals
 470 
 471         call    __dtrace_probe___sched_off__cpu ! DTrace probe
 472         mov     %i0, %o0                        ! arg for DTrace probe
 473 
 474         ldn     [THREAD_REG + T_CPU], %i1       ! cpu pointer
 475                                         
 476         flushw                                  ! flushes all but this window
 477         ldn     [THREAD_REG + T_PROCP], %i2     ! old procp for mmu ctx
 478 
 479         !
 480         ! Temporarily switch to the idle thread's stack so that
 481         ! the zombie thread's stack can be reclaimed by the reaper.
 482         !
 483         ldn     [%i1 + CPU_IDLE_THREAD], %o2    ! idle thread pointer
 484         ldn     [%o2 + T_SP], %o1               ! get onto idle thread stack
 485         sub     %o1, SA(MINFRAME), %sp          ! save room for ins and locals
 486         clr     %fp
 487         !
 488         ! Set the idle thread as the current thread.
 489         ! Put the zombie on death-row.
 490         !       
 491         mov     THREAD_REG, %o0                 ! save %g7 = curthread for arg
 492         mov     %o2, THREAD_REG                 ! set %g7 to idle
 493         stn     %g0, [%i1 + CPU_MPCB]           ! clear mpcb
 494 #ifdef CPU_MPCB_PA
 495         mov     -1, %o1
 496         stx     %o1, [%i1 + CPU_MPCB_PA]
 497 #endif
 498         call    reapq_add                       ! reapq_add(old_thread);
 499         stn     %o2, [%i1 + CPU_THREAD]         ! delay - CPU's thread = idle
 500 
 501         !
 502         ! resume_from_idle args:
 503         !       %i0 = new thread
 504         !       %i1 = cpu
 505         !       %i2 = old proc
 506         !       %i3 = new proc
 507         !       
 508         b       _resume_from_idle               ! finish job of resume
 509         ldn     [%i0 + T_PROCP], %i3            ! new process
 510         SET_SIZE(resume_from_zombie)
 511 
 512 #endif  /* lint */
 513 
 514 #if defined(lint)
 515 
 516 /* ARGSUSED */
 517 void
 518 resume_from_intr(kthread_id_t t)
 519 {}
 520 
 521 #else   /* lint */
 522 
 523         ENTRY(resume_from_intr)
 524         save    %sp, -SA(MINFRAME), %sp         ! save ins and locals
 525 
 526         !
 527         ! We read in the fprs and call fp_save if FPRS_FEF is set
 528         ! to save the floating-point state if fprs has been
 529         ! modified by operations such as hw bcopy or fp_disabled.
 530         ! This is to resolve an issue where an interrupting thread
 531         ! doesn't retain their floating-point registers when
 532         ! switching out of the interrupt context.
 533         !
 534         rd      %fprs, %g4
 535         ldn     [THREAD_REG + T_STACK], %i2
 536         andcc   %g4, FPRS_FEF, %g0              ! is FPRS_FEF set
 537         bz,pt   %icc, 4f
 538           st    %g4, [%i2 + SA(MINFRAME) + FPU_FPRS]    ! save fprs
 539 
 540         ! save kernel fp state in stack
 541         add     %i2, SA(MINFRAME), %o0          ! %o0 = kfpu_t ptr
 542         rd      %gsr, %g5
 543         call fp_save
 544         stx     %g5, [%o0 + FPU_GSR]            ! store GSR
 545 
 546 4:
 547 
 548         flushw                                  ! flushes all but this window
 549         stn     %fp, [THREAD_REG + T_SP]        ! delay - save sp
 550         stn     %i7, [THREAD_REG + T_PC]        ! save return address
 551 
 552         ldn     [%i0 + T_PC], %i7               ! restore resuming thread's pc
 553         ldn     [THREAD_REG + T_CPU], %i1       ! cpu pointer
 554 
 555         !
 556         ! Fix CPU structure to indicate new running thread.
 557         ! The pinned thread we're resuming already has the CPU pointer set.
 558         !
 559         mov     THREAD_REG, %l3         ! save old thread
 560         stn     %i0, [%i1 + CPU_THREAD] ! set CPU's thread pointer
 561         membar  #StoreLoad              ! synchronize with mutex_exit()
 562         mov     %i0, THREAD_REG         ! update global thread register
 563 
 564         !
 565         ! Switch to new thread's stack
 566         !
 567         ldn     [THREAD_REG + T_SP], %o0        ! restore resuming thread's sp
 568         sub     %o0, SA(MINFRAME), %sp ! in case of intr or trap before restore
 569         mov     %o0, %fp
 570         clrb    [%l3 + T_LOCK]          ! clear intr thread's tp->t_lock
 571 
 572         !
 573         ! If we are resuming an interrupt thread, store a timestamp in the
 574         ! thread structure.
 575         !
 576         lduh    [THREAD_REG + T_FLAGS], %o0
 577         andcc   %o0, T_INTR_THREAD, %g0
 578         bnz,pn  %xcc, 0f
 579         !
 580         ! We're resuming a non-interrupt thread.
 581         ! Clear CPU_INTRCNT and check if cpu_kprunrun set?
 582         !
 583         ldub    [%i1 + CPU_KPRUNRUN], %o5       ! delay
 584         brnz,pn %o5, 3f                         ! call kpreempt(KPREEMPT_SYNC);
 585         stub    %g0, [%i1 + CPU_INTRCNT]
 586 1:
 587         ret                             ! resume curthread
 588         restore
 589 0:
 590         !
 591         ! We're an interrupt thread. Update t_intr_start and cpu_intrcnt
 592         !
 593         add     THREAD_REG, T_INTR_START, %o2
 594 2:
 595         ldx     [%o2], %o1
 596         RD_CLOCK_TICK(%o0,%o3,%l1,__LINE__)
 597         casx    [%o2], %o1, %o0
 598         cmp     %o0, %o1
 599         bne,pn  %xcc, 2b
 600         ldn     [THREAD_REG + T_INTR], %l1      ! delay
 601         ! Reset cpu_intrcnt if we aren't pinning anyone
 602         brz,a,pt %l1, 2f
 603         stub    %g0, [%i1 + CPU_INTRCNT]
 604 2:      
 605         ba,pt   %xcc, 1b
 606         nop
 607 3:
 608         !
 609         ! We're a non-interrupt thread and cpu_kprunrun is set. call kpreempt.
 610         !
 611         call    kpreempt
 612         mov     KPREEMPT_SYNC, %o0
 613         ba,pt   %xcc, 1b
 614         nop
 615         SET_SIZE(resume_from_intr)
 616 
 617 #endif /* lint */
 618 
 619 
 620 /*
 621  * thread_start()
 622  *
 623  * the current register window was crafted by thread_run() to contain
 624  * an address of a procedure (in register %i7), and its args in registers
 625  * %i0 through %i5. a stack trace of this thread will show the procedure
 626  * that thread_start() invoked at the bottom of the stack. an exit routine
 627  * is stored in %l0 and called when started thread returns from its called
 628  * procedure.
 629  */
 630 
 631 #if defined(lint)
 632 
 633 void
 634 thread_start(void)
 635 {}
 636 
 637 #else   /* lint */
 638 
 639         ENTRY(thread_start)
 640         mov     %i0, %o0
 641         jmpl    %i7, %o7        ! call thread_run()'s start() procedure.
 642         mov     %i1, %o1
 643 
 644         call    thread_exit     ! destroy thread if it returns.
 645         nop
 646         unimp 0
 647         SET_SIZE(thread_start)
 648 
 649 #endif  /* lint */