Files
seta75D 2e8a93c394 Init
2021-10-11 18:20:23 -03:00

1226 lines
28 KiB
ArmAsm

/* @(#)swtch.s 1.1 92/07/30 SMI */
/*
* Copyright (c) 1990 by Sun Microsystems, Inc.
*/
/*
* Process switching routines.
*/
#include <machine/param.h>
#include <machine/asm_linkage.h>
#include <machine/mmu.h>
#include <machine/psl.h>
#include <machine/reg.h>
#include <machine/pcb.h>
#include <machine/devaddr.h>
#include "assym.s"
#ifdef MULTIPROCESSOR
#include "percpu_def.h"
#endif MULTIPROCESSOR
.seg "text"
.align 4
/*
* whichqs tells which of the 32 queues qs have processes in them.
* Setrq puts processes into queues, Remrq removes them from queues.
* The running process is on no queue, other processes are on a queue
* related to p->p_pri, divided by 4 actually to shrink the 0-127 range
* of priorities into the 32 available queues.
*/
/*
* setrq(p)
*
* Call should be made at spl6(),
* pslock should be held
* and p->p_stat should be SRUN
*/
ENTRY(setrq)
#ifdef MULTIPROCESSOR
! placing the idle process on the queue
! is fatal: other processors will not be
! able to follow the linked list through
! this proc area, getting their own idleproc
! instead.
set _idleproc, %g1
cmp %o0, %g1
bne 1f
nop
retl
nop
1:
#endif MULTIPROCESSOR
ld [%o0 + P_RLINK], %g1 ! firewall: p->p_rlink must be 0
ldub [%o0 + P_PRI], %g2 ! interlock slot
tst %g1
bz,a 1f
srl %g2, 2, %g4 ! delay slot, p->p_pri / 4
save %sp, -SA(MINFRAME), %sp ! need to buy a window
set 2f, %o0 ! p->p_rlink not 0
call _panic
nop
2:
.asciz "setrq"
.align 4
1:
sll %g4, 3, %g2 ! (p->p_pri / 4) * sizeof(*qs)
set _qs+4, %g3 ! back ptr of queue head
ld [%g3 + %g2], %g3 ! get qs[p->p_pri/4].ph_rlink
mov 1, %g2 ! interlock slot
ld [%g3], %g1 ! insque(p, qs[p->p_pri].ph_rlink)
st %g3, [%o0 + 4]
st %g1, [%o0]
st %o0, [%g1 + 4]
st %o0, [%g3]
sethi %hi(_whichqs), %g7
ld [%g7 + %lo(_whichqs)], %g1
sll %g2, %g4, %g2 ! interlock slot
bset %g2, %g1
st %g1, [%g7 + %lo(_whichqs)]
retl
nop
/*
* remrq(p)
*
* Call should be made at spl6()
* pslock should be held
*/
ENTRY(remrq)
ldub [%o0 + P_PRI], %g3
sethi %hi(_whichqs), %g7
ld [%g7 + %lo(_whichqs)], %g4
srl %g3, 2, %g3 ! p->p_pri / 4
mov 1, %g1 ! test appropriate bit in whichqs
sll %g1, %g3, %g3
btst %g3, %g4
bnz,a 1f
ld [%o0], %g1 ! delay slot, remque(p)
save %sp, -SA(MINFRAME), %sp ! need to buy a window
set 2f, %o0 ! p not in appropriate queue
call _panic
nop
2:
.asciz "remrq"
.align 4
1:
ld [%o0 + 4], %g2
st %g1, [%g2]
st %g2, [%g1 + 4]
cmp %g1, %g2
bne 3f ! queue not empty
andn %g4, %g3, %g4 ! delay slot, queue empty,
st %g4, [%g7 + %lo(_whichqs)]
3:
clr [%o0 + P_RLINK] ! p->p_rlink = 0
retl
nop
#ifdef MULTIPROCESSOR
/*
* onrq(p)
*
* Call should be made at spl6()
* pslock should be held
*/
ENTRY(onrq) ! return true if proc is on the run queue
ldub [%o0 + P_PRI], %o1
set _qs, %o2
srl %o1, 2, %o1 ! p->p_pri / 4
sll %o1, 3, %o1 ! offset into rs
add %o1, %o2, %o1 ! %o1 now has "sentinal"
ld [%o1], %o2 ! get first
1: cmp %o2, %o0 ! if it is our proc
be,a 0f ! return to caller
mov 1, %o0 ! noting success.
cmp %o2, %o1 ! if it is not the sentinal
bne,a 1b ! loop back
ld [%o2], %o2 ! and get the next proc
! else it is the sentinal,
mov 0, %o0 ! note failure
0:
retl ! and return to caller.
nop
#endif MULTIPROCESSOR
.global _qrunflag, _runqueues
/*
* When no processes are on the runq, swtch branches to idle
* to wait for something to come ready.
*/
.global _idle
#ifdef LWP
.global ___Nrunnable, _lwpschedule
#endif LWP
_idle:
#ifdef MULTIPROCESSOR
save %sp, -SA(MINFRAME), %sp
_testq:
#endif MULTIPROCESSOR
sethi %hi(_whichqs), %g1
ld [%g1 + %lo(_whichqs)], %g1
tst %g1
bz 2f ! none check other stuff
nop
sethi %hi(_whichqs), %g1
ld [%g1 + %lo(_whichqs)], %g1
tst %g1 ! check again inside lock
bz 0f ! whichqs bits went away!
nop
!
! Found something.
!
mov %psr, %g1 ! splhi
bclr PSR_PIL, %g1
or %g1, 13 << PSR_PIL_BIT, %g1
mov %g1, %psr
nop ! psr delay
b sw_testq
nop ! more psr delay
0:
!
! Test for other async events.
!
2:
#ifdef LWP
#ifdef MULTIPROCESSOR
mov 1, %g2
GETCPU(%g1)
sll %g2, %g1, %g2
sethi %hi(_force_lwps), %g1 ! check the lwp force bits;
ld [%g1 + %lo(_force_lwps)], %g1
andcc %g1, %g2, %g0 ! if our bit is on,
bnz 0f ! always run lwpschedule
nop
tst %g1 ! if anyone else's is on,
bnz 2f ! do not try to run lwpschedule
nop
#endif MULTIPROCESSOR
sethi %hi(___Nrunnable),%g2
ld [%g2+%lo(___Nrunnable)],%g2
tst %g2 ! if nobody is runable,
bz 2f ! don't waste time in lwpschedule
nop
0:
call _lwpschedule
nop
call _mmu_setctx
mov %g0, %o0
2:
#endif LWP
#ifndef MULTIPROCESSOR
sethi %hi(_qrunflag), %g1 ! need to run stream queues?
ldub [%g1 + %lo(_qrunflag)], %g1
tst %g1
#ifndef SAS
bz _idle ! no
nop
#else SAS
bnz 1f
nop
/*
* If we have nothing to do, snooze in the simulator.
*/
call _mpsas_idle_trap
nop
b _idle
nop
1:
#endif SAS
call _runqueues ! go do it
nop
b _idle
nop
#else MULTIPROCESSOR
/*
if (initting)
jump _testq;
else
return to idlework;
*/
2:
sethi %hi(_initing), %g1
ld [%g1+%lo(_initing)], %g1
cmp %g0, %g1
bne _testq
nop
ret
restore
#endif MULTIPROCESSOR
/*
* swtch()
*/
ENTRY(swtch)
save %sp, -SA(MINFRAME), %sp
mov %psr, %l0 ! save PSR
andn %l0, PSR_PIL, %g1
or %g1, 13 << PSR_PIL_BIT, %g1
mov %g1, %psr ! spl hi
nop
mov 1, %g2
sethi %hi(_noproc), %g7
st %g2, [%g7+%lo(_noproc)]
sethi %hi(_runrun), %g7
clr [%g7+%lo(_runrun)]
sethi %hi(_masterprocp), %g7
ld [%g7+%lo(_masterprocp)], %g7
tst %g7 ! if no proc skip saving state
bz sw_testq ! this happens after exit
nop
sethi %hi(_uunix), %g5 ! XXX - global u register?
ld [%g5+%lo(_uunix)], %g5
ld [%g5+PCB_FLAGS], %g1 ! pcb_flags &= ~AST_SCHED
set AST_SCHED, %g2
bclr %g2, %g1
st %g1, [%g5+PCB_FLAGS]
!
! Loop through whichqs bits looking for a non-empty queue.
! PSLOCK must be acquired before branching here.
!
! %l0 saved psr
! %l1 whichqs
! %l2 marching bit
! %l3 offset into rs
! %l4 procp under consideration
! %l5 sentinal procp
sw_testq:
sethi %hi(_whichqs), %g7
ld [%g7 + %lo(_whichqs)], %l1
mov 1, %l2
mov 0, %l3
btst %l2, %l1
1:
bnz sw_foundq ! found one
nop
sw_nextq:
sll %l2, 1, %l2
add %l3, 8, %l3
tst %l2
bnz,a 1b
btst %l2, %l1 ! delay slot
sw_goidle:
#ifndef MULTIPROCESSOR
!
! Found no procs, goto idle loop
!
mov %psr, %g1 ! allow interrupts
andn %g1, PSR_PIL, %g1
mov %g1, %psr
nop ! psr delay
b _idle
nop
#else MULTIPROCESSOR
!
! Stage the idle process, bypassing
! some of the sanity checking.
!
sethi %hi(_idleproc), %l4
b sw_stage_l4
or %l4, %lo(_idleproc), %l4
#endif MULTIPROCESSOR
sw_foundq:
set _qs, %l5 ! get qs proc list
add %l5, %l3, %l5 ! sentinal = &qs[bit]
ld [%l5], %l4 ! p=qs[bit].ph_link=highest pri process
cmp %l5, %l4 ! is queue empty?
bne,a sw_foundp ! [empty if self pointer]
nop
sw_bad:
set 4f, %o0
call _panic
nop
4:
.asciz "swtch"
.align 4
sw_foundp:
#ifdef P_PAM
! note: the affinity mask test comes before the
! test for "same process", in case we are being
! forced off this processor by a PAM change.
sw_chk_pam:
sethi %hi(_cpuid), %g4
ld [%g4+%lo(_cpuid)], %g4
mov 1, %g3
sll %g3, %g4, %g4 ! form mask for this processor
ld [%l4+P_PAM], %g3 ! get p->p_pam
andcc %g3, %g4, %g0 ! can this proc run here?
beq sw_reject ! if not, get another.
nop
#endif P_PAM
sw_chk_new:
sethi %hi(_masterprocp), %g3
ld [%g3+%lo(_masterprocp)], %g3
cmp %l4, %g3 ! if new is same as old,
beq sw_accept ! accept the process.
nop
#ifdef MULTIPROCESSOR
sw_chk_idl:
set _idleproc, %g1
cmp %l4, %g1 ! if new is idleproc,
beq sw_reject ! reject the process.
nop
#endif MULTIPROCESSOR
#ifdef P_CPUID
sw_chk_cpuid:
ld [%l4 + P_CPUID], %g4
tst %g4 ! if active somewhere ...
bpos sw_reject ! reject the process.
nop
1:
#endif P_CPUID
! If this process' address space is
! active, reject it and try another.
#ifdef A_HAT_CPU
sw_chk_hat:
ld [%l4 + P_AS], %g4
tst %g4
beq sw_hatok ! new proc has no as, contine testing
nop
ldub [%g4 + A_HAT_CPU], %g3
tst %g3
beq sw_hatok ! new proc not active, continue checking
nop
GETMID(%g4) ! get module id, 8..11 (or 15)
cmp %g3, %g4 ! remember, zero is "nobody".
bne sw_reject ! proc's hat active elsewhere, reject it.
nop
sw_hatok:
#endif A_HAT_CPU
! %%% Add other "reject" conditions here
b sw_accept ! All conditions passed - use this proc!
nop
! For some reason, this process is not a candidate
! for execution, even though it is on the run queue.
! This can happen if the affinity mask denies this
! process can run on this processor, or
! if someone goes to sleep, but
! receives a wakeup before the cpu putting it to
! sleep manages to switch (ie. p_cpuid is nonnegative), or
! in the "vfork()" case where the parent or
! the child is heading toward sleep but not yet
! actually there.
sw_reject:
ld [%l4], %l4 ! get next proc on queue
cmp %l4, %l5 ! if there was a proc,
bne sw_foundp ! see if it is a candidate
nop ! else
b sw_nextq ! check next queue
nop
! This process has been cleared for takeoff.
sw_accept:
#ifdef MULTIPROCESSOR
!
! turn off cpu_idle. if we are truely idle,
! the idle process will turn it back on.
!
sethi %hi(_cpu_idle), %g1
st %g0, [%g1+%lo(_cpu_idle)]
#endif MULTIPROCESSOR
ld [%l4], %g1 ! remque(%l4)
ld [%l4+4], %g2
st %g1, [%g2]
st %g2, [%g1 + 4]
bclr %l2, %l1 ! whichqs if queue is now empty
cmp %g1, %g2
be,a 3f ! queue now empty
st %l1, [%g7 + %lo(_whichqs)]
3:
sethi %hi(_noproc), %g7
clr [%g7 + %lo(_noproc)]
ld [%l4 + P_WCHAN], %g1 ! firewalls
ldub [%l4 + P_STAT], %g2
tst %g1 ! p->p_wchan must be 0
bne sw_bad
nop
cmp %g2, SRUN ! p->p_stat must be SRUN
bne sw_bad
nop
clr [%l4 + P_RLINK] ! p->p_rlink = 0
sw_stage_l4: ! common code (sw_goidle joins mainstream here)
sethi %hi(_masterprocp), %l1
ld [%l1 + %lo(_masterprocp)], %l2
set _cnt, %g1 ! cnt.v_swtch++
ld [%g1 + V_SWTCH], %g2
inc 1, %g2
st %g2, [%g1 + V_SWTCH] ! update ctx switch count
#ifdef A_HAT_CPU
! update new procp->p_as->a_hat.hat_oncpu: now active here.
ld [%l4 + P_AS], %g4
tst %g4
beq 1f ! null "as", skip onward.
nop
GETMID(%g3) ! get module id, 8..11 (or 15)
stb %g3, [%g4 + A_HAT_CPU] ! stash it in as->a_hat.hat_oncpu
1:
#endif A_HAT_CPU
cmp %l2, %l4 ! if (p == masterprocp) ...
be 6f ! skip resume() call
nop
call _resume
mov %l4, %o0 ! resume(p)
6:
mov %psr, %g2 ! restore processor priority
andn %g2, PSR_PIL, %g2
and %l0, PSR_PIL, %l0
or %l0, %g2, %l0
mov %l0, %psr
nop ! psr delay
ret
restore
/*
* masterprocp is the pointer to the proc structure for the currently
* mapped u area. It is used to set up the mapping for the u area
* by the debugger since the u area is not in the Sysmap.
*/
.seg "data"
#ifndef MULTIPROCESSOR
.global _masterprocp
_masterprocp:
.word 0 ! struct proc *masterprocp
#endif MULTIPROCESSOR
/*
* XXX The following is a hack so that we don't have to splzs() to
* XXX protect the u_pcb from being clobbered by a BREAK that causes
* XXX kadb to be entered. We also don't want to find masterprocp and
* XXX uunix inconsistent.
* XXX Instead, we set kadb_defer before the critical region and clear
* XXX it afterwards.
* XXX Where zsa_xsint would normally CALL_DEBUG, it first checks
* XXX kadb_defer, and if set it sets kadb_want instead.
* XXX We check kadb_want (after clearing kadb_defer, to avoid races)
* XXX and, if set, we call kadb.
*/
.global _kadb_defer
_kadb_defer:
.word 0 ! int kadb_defer
.global _kadb_want
_kadb_want:
.word 0 ! int kadb_want
.seg "text"
/*
* resume(p)
*/
ENTRY(resume)
save %sp, -SA(MINFRAME), %sp
mov %psr, %l0
sethi %hi(_masterprocp), %l1
ld [%l1 + %lo(_masterprocp)], %l2
tst %l2 ! if there is a current proc,
bnz 1f ! save its state,
! if no proc, skip saving state
! this happens after exit
! trash_windows, no current proc
or %l0, PSR_PIL, %o4 ! spl hi
sethi %hi(_nwindows), %o1
ld [%o1+%lo(_nwindows)], %o3
dec %o3 ! get NW-1
and %l0, 0x1f, %o2 ! mask cwp
cmp %o2, %o3 ! compare cwp to NW-1
be 2f ! calculate wim
mov 1, %o3 ! if equal new wim is 1, wraparound
inc %o2
sll %o3, %o2, %o3 ! if less create wim of sll(1,cwp+1)
2:
mov %o4, %psr ! raise priority
nop;nop ! psr delay
mov (10<<PSR_PIL_BIT), %l6 ! clock priority is 10
mov %o3, %wim ! install new wim
mov %l0, %psr ! restore priority
b 4f
nop ! psr delay
! store state for current proc
1: sethi %hi(_uunix), %g5 ! XXX - global u register?
ld [%g5+%lo(_uunix)], %g5
ld [%g5 + PCB_FPCTXP], %l3 ! is floating point being used
tst %l3 ! by the current process?
bz 5f ! if not skip saving FPU state
st %l0, [%g5 + PCB_PSR] ! save psr (for PSR_PIL)
sethi %hi(_fpu_exists), %l4 ! fpu is present flag
ld [%l4 + %lo(_fpu_exists)], %l4
tst %l4 ! well, do we have FPU hardware?
bz 5f ! no fpu, nothing to do
nop
st %fsr, [%l3 + FPCTX_FSR] ! save current fpu state
STORE_FPREGS(%l3) ! assumes %l3 points at fpreg save area
5:
ld [%l2 + P_AS], %o0 ! does it
tst %o0 ! have an address space?
bz 3f ! skip if not
mov (10<<PSR_PIL_BIT), %l6 ! clock priority is 10
call _rm_asrss ! find out memory claim for
nop ! as associated with this process
st %o0, [%l2 + P_RSSIZE] ! store with process
3:
!
! Flush the register windows to the stack.
! This saves all the windows except the one we're in now.
! The WIM will be immediately ahead of us after this.
!
call _flush_windows
nop
4:
andn %l0, PSR_PIL, %l0 ! clear priotity field
or %l0, %l6, %l0 ! spl clock, or in new priority
mov %l0, %psr ! raise priority
!
! Begin critical region where we can't let BREAK call kadb
!
sethi %hi(_kadb_defer), %g1
mov 1, %g2
st %g2, [%g1 + %lo(_kadb_defer)]
sethi %hi(_uunix), %g5
ld [%g5 + %lo(_uunix)], %g5
st %i7, [%g5 + PCB_PC] ! save ret pc and sp in pcb
st %fp, [%g5 + PCB_SP]
#ifdef P_CPUID
!
! set the P_CPUID field in the new proc area.
!
GETCPU(%g2)
st %g2, [%i0 + P_CPUID]
#endif P_CPUID
!
! switch to kernel context
!
call _mmu_setctx
mov %g0, %o0
#ifdef MULTIPROCESSOR
! call hat_map_percpu so that the level-1 page table
! corresponding to the new uunix has its per-CPU area
! set for the correct CPU.
call _hat_map_percpu
ld [%i0 + P_UAREA], %o0 ! pass uarea as 1st parm
#endif MULTIPROCESSOR
!
! Flush various write buffers before changing uunix
! in order to facilitate asynchronous fault handling.
call _flush_writebuffers
nop
!
! Switch to new user and proc areas.
!
ld [%i0 + P_UAREA], %g5 ! get new user ptr
sethi %hi(_uunix), %g2 ! XXX - global uarea ptr?
ld [%l1 + %lo(_masterprocp)], %l6 ! get old proc ptr
st %g5, [%g2+%lo(_uunix)] ! set new user ptr
st %i0, [%l1 + %lo(_masterprocp)] ! set new proc ptr
!
! We snarf the PC and SP here so we can end the critical
! region that much sooner.
!
ld [%g5 + PCB_PC], %i7
ld [%g5 + PCB_SP], %fp
!
! End critical region where we can't let BREAK call kadb.
! (We just changed uunix, so if we take a BREAK we'll store the
! registers for kadb on the new pcb, which isn't in use now).
! So we clear the flag, and then check to see if we owe kadb a
! call.
!
sethi %hi(_kadb_defer), %g1 ! clear defer first
st %g0, [%g1 + %lo(_kadb_defer)]
sethi %hi(_kadb_want), %g1 ! then check want
ld [%g1 + %lo(_kadb_want)], %g2
tst %g2 ! in case BREAK'd between
bz 1f
ld [%i0 + P_AS], %l0 ! check p->p_as
call _call_debug_from_asm
st %g0, [%g1 + %lo(_kadb_want)]
1:
! If this process has an address space
! and a context, get into it.
tst %l0 ! if ((as = p->p_as) &&
bz 1f
nop
ldub [%l0 + 0x10], %g1
andcc %g1, 0x80, %g1 ! (as->a_hat.hat_ptvalid) &&
bz 1f
nop
ld [%l0 + A_HAT_CTX], %l0 ! (ctx = as->a_hat.hat_ctx))
tst %l0
bz 1f
nop
call _mmu_setctx ! mmu_setctx(
ld [%l0 + C_NUM], %o0 ! ctx->c_num);
1:
!
! %sp must be snarfed before we clear P_CPUID.
!
call _flush_windows ! completely release the old stack
sub %fp, WINDOWSIZE, %sp ! establish sp, for interrupt overflow
#if defined(P_CPUID) || defined(A_HAT_CPU)
!
! If the old process was not null
! and was different from the new one,
! destage the old one.
!
tst %l6
be 1f ! if old proc was not null
cmp %l6, %i0
be 1f ! and if it was a different one,
#ifdef A_HAT_CPU
!
! If we are not sharing an addres space,
! mark the old address space as "nowhere".
!
ld [%l6 + P_AS], %g1
tst %g1
beq 2f
ld [%i0 + P_AS], %g3
cmp %g1, %g3 ! if old and new "as" are different,
bne,a 2f ! (yes, branch if different!)
stb %g0, [%g1 + A_HAT_CPU] ! set as->a_hat.hat_oncpu to "Nowhere"
2:
#endif A_HAT_CPU
#ifdef P_CPUID
!
! Make the old proc's P_CPUID field negative
! so someone else can stage it.
! NOTE: we leave the last bits alone so we can find
! out where the process most recently ran.
! NOTE: if registers get tight, this can be done
! using an LDSTUB.
!
sub %g0, 1, %g1
stb %g1, [%l6 + P_CPUID] ! yes, only store first byte.
#endif P_CPUID
1:
#endif /* P_CPUID || A_HAT_CPU */
!
! if the new process does not have any fp ctx
! we do nothing with regards to the fpu
! if the new process does have fp ctx
! we enable the fpu and load its context now
! if we have fpu hardware
!
sethi %hi(_uunix), %g5 ! XXX - global uarea ptr?
ld [%g5+%lo(_uunix)], %g5 ! reload user ptr
ld [%g5 + PCB_FPCTXP], %g1 ! new process fp ctx pointer
tst %g1 ! if new process fp ctx printer == 0
bz 2f ! it hasn't used the fpu yet
ld [%g5 + PCB_PSR], %l0
sethi %hi(_fp_ctxp), %g2 ! old fp ctx pointer
ld [%g2 + %lo(_fp_ctxp)], %l1
sethi %hi(_fpu_exists), %l2
ld [%l2 + %lo(_fpu_exists)], %l3
tst %l3
bz 2f
st %g1, [%g2 + %lo(_fp_ctxp)] ! install new fp context
#ifdef MULTIPROCESSOR
! If we are multiprocessor, do not check for the current loaded
! fpu context (fp_ctxp). The process could have moved from processor
! to processor. Therefore there will be no optimization of the fpu
! context load in MP case.
!
! We could running on a UP system such Viking but with a MP kernel.
! In this case look for uniprocessor state. If so, then check for
! possible fpu context load optimization.
sethi %hi(_uniprocessor), %l6
ld [%l6+%lo(_uniprocessor)], %l6
cmp %l6, %g0
bz 8f
nop
#endif MULTIPROCESSOR
cmp %g1, %l1 ! if the fp ctx belongs to new process
be 3f ! nothing to do
.empty ! the label below is OK
! maybe branch to 2 instead
8:
!
! New process is different than last process that used the fpu
! Load the new process floating point state
! The fpu must be enabled via the psr as the last
! process may not have used it
!
set PSR_EF, %l6
mov %psr, %g3
or %g3, %l6, %g3
mov %g3, %psr
nop; nop; nop ! psr delay, (paranoid)
ld [%g1 + FPCTX_FSR], %fsr ! restore fsr
LOAD_FPREGS(%g1)
3:
ld [%i0 + P_STACK], %l1
add %l1, (MINFRAME + PSR*4), %l1
ld [%l1], %l3 ! read the psr value from the stack
or %l0, %l6, %l0 ! enable fpu, new psr
or %l3, %l6, %g1 ! enable fpu, psr stored in stack
st %g1, [%l1] ! update stack, sys_rtt will use it
2:
!
! Return to new proc. Restore will cause underflow.
!
mov %psr, %g2
and %l0, PSR_PIL, %l0 ! restore PSR_PIL
andn %g2, PSR_PIL, %g2
or %l0, %g2, %l0
mov %l0, %psr
nop ! psr delay
ret
restore
/*
* start_child(parentp, parentu, childp, nfp, new_context)
* struct proc *parentp, *childp;
* struct user *parentu;
* int *new_context;
* struct file **nfp;
* This saves the parent's state such that when resume'd, the
* parent will return to the caller of startchild.
* Equivalent to the first half of resume.
* It then calls conschild to create the child process
*
* We always resume the parent in the kernel.
* The child usually resumes in userland.
*
* start_child is only called from newproc
*/
ENTRY(start_child)
save %sp, -SA(MINFRAME), %sp
mov %psr, %l0
sethi %hi(_masterprocp), %l1
ld [%l1 + %lo(_masterprocp)], %l2
!
! Begin critical region where we can't let BREAK call kadb
! (This critical region lasts through cons_child, ends in yield_child.)
!
sethi %hi(_kadb_defer), %g1
mov 1, %g2
st %g2, [%g1 + %lo(_kadb_defer)]
!
! arrange for the parent to resume the caller of start child
!
set _uunix, %l3 ! get u pointer, XXX, global u register
ld [%l3], %l4
st %l0, [%l4 + PCB_PSR] ! save psr, ret pc, and sp in pcb
st %i7, [%l4 + PCB_PC]
st %fp, [%l4 + PCB_SP]
ld [%l2 + P_AS], %o0 ! does it have an address space?
sethi %hi(_cons_child), %l5 ! interlock slot
tst %o0
bz 1f ! skip if not
or %l5, %lo(_cons_child), %l5
call _rm_asrss ! find out memory claim for
nop ! as associated with this process
st %o0, [%l2 + P_RSSIZE] ! store with process
1:
jmp %l5 ! cons_child (...)
restore ! give back register window
! args already in the 'out' register
/*
* yield_child(parent_ar0, child_stack, childp, parent_pid, childu, seg)
*
* Should be called at splclock()!
* used in fork() instead of newproc/sleep/swtch/resume
* cons up a child process stack and start it running
* basically copy the needed parts of the kernel stack frame of
* the parent, fixing appropriate entries for the child
*/
ENTRY(yield_child)
save %sp, -WINDOWSIZE, %sp
call _flush_windows ! get the all registers on the stack
sethi %hi(PSR_C), %l2 ! delay slot, set C bit in %l2
! the parent will eventually return
! via resume to cons_child which
! will return to newproc
!
! Saved %l5 flags child is kernel proc. kernel procs never return to
! user land, so we skip setting up the child's u_ar0 regs. This is
! required, since if the parent is itself a kernel proc, it's
! u_ar0 may not be meaningful.
!
ld [%i1 + 5*4], %l5
tst %l5
bnz 1f
nop
! struct regs psr, pc, ncp, y
ld [%i0 + PSR*4], %o4
ld [%i0 + nPC*4], %o5 ! skip trap instruction
andn %o4, %l2, %o4 ! clear carry bit
st %o4, [%i1 + MINFRAME + PSR*4]
st %o5, [%i1 + MINFRAME + PC*4] ! pc = npc
add %o5, 4, %o5 ! npc += 4
st %o5, [%i1 + MINFRAME + nPC*4]
ld [%i0 + Y*4], %o4
st %o4, [%i1 + MINFRAME + Y*4]
! struct regs, user globals
ld [%i0 + G1*4], %o4
st %o4, [%i1 + MINFRAME + G1*4]
ldd [%i0 + G2*4], %o4
std %o4, [%i1 + MINFRAME + G2*4]
ldd [%i0 + G4*4], %o4
std %o4, [%i1 + MINFRAME + G4*4]
ldd [%i0 + G6*4], %o4
std %o4, [%i1 + MINFRAME + G6*4]
! struct regs, user outs
mov %i3, %o4 ! parent pid childs ret val r0
mov 1, %o5 ! 1 childs ret value r1
std %o4, [%i1 + MINFRAME + O0*4]
ldd [%i0 + O2*4], %o4
std %o4, [%i1 + MINFRAME + O2*4]
ldd [%i0 + O4*4], %o4
std %o4, [%i1 + MINFRAME + O4*4]
ldd [%i0 + O6*4], %o4
std %o4, [%i1 + MINFRAME + O6*4]
1:
! fake a return from syscall() to return to the child
mov %psr, %g1 ! spl hi
or %g1, PSR_PIL, %g2
mov %g2, %psr
nop ; nop ! psr delay (paranoid)
#ifdef P_CPUID
!
! set P_CPUID in new proc
!
GETCPU(%g2)
st %g2, [%i2 + P_CPUID]
#endif P_CPUID
#ifdef A_HAT_CPU
!
! If there is an associated AS,
! update hat_oncpu.
!
ld [%i2 + P_AS], %o1
tst %o1
bz 1f ! if address space not null,
nop
GETMID(%g1) ! get module id, 8..11 or 15
stb %g1, [%o1 + A_HAT_CPU] ! stash mid in as->a_hat.hat_oncpu
1:
#endif A_HAT_CPU
#ifdef MULTIPROCESSOR
! call hat_map_percpu so that the level-1 page table
! corresponding to the new uunix has its per-CPU area
! set for the correct CPU.
call _hat_map_percpu
mov %i4, %o0 ! pass uarea as 1st parm
#endif MULTIPROCESSOR
!
! Flush various write buffers before changing uunix
! in order to facilitate asynchronous fault handling.
!
call _flush_writebuffers
nop
!
! change to new user and proc areas
!
sethi %hi(_masterprocp), %o0
sethi %hi(_uunix), %o1 ! XXX - global uarea pointer?
ld [%o0+%lo(_masterprocp)], %l5 ! get old proc ptr
st %i4, [%o1+%lo(_uunix)] ! set new user ptr
st %i2, [%o0+%lo(_masterprocp)] ! set new proc ptr
!
! End critical region where we can't let BREAK call kadb.
! (We just changed uunix, so if we take a BREAK we'll store the
! registers for kadb on the new pcb, which isn't in use now).
! So we clear the flag, and then check to see if we owe kadb a
! call.
!
sethi %hi(_kadb_defer), %g1 ! clear defer first
st %g0, [%g1 + %lo(_kadb_defer)]
sethi %hi(_kadb_want), %g1 ! then check want
ld [%g1 + %lo(_kadb_want)], %g2
tst %g2 ! in case BREAK'd between
bz 1f
ld [%i2 + P_AS], %l4 ! check p->p_as
call _call_debug_from_asm
st %g0, [%g1 + %lo(_kadb_want)]
1:
!
! Check to see if we already have context. If so then set up the
! context. Otherwise we leave the proc in the kernels context which
! will cause it to fault if it ever gets back to userland.
!
tst %l4
mov %g0, %o0 ! default to context zero
bz 1f ! if no address space, skip ahead
ld [%i1 + 6*4], %l6 ! (delay) get proc start address
ld [%l4 + A_HAT_CTX], %l4 ! check (p->p_as->a_hat.hat_ctx)
tst %l4
bnz,a 1f ! if it has a context assigned,
ld [%l4 + C_NUM], %o0 ! get the context number
1:
call _mmu_setctx ! shift to child's context (or kernel's ...)
mov %i1, %fp ! (trailer) install the new childs stack
!
! %sp must be snarfed before we clear P_CPUID.
!
call _flush_windows ! completely release the old stack
sub %fp, WINDOWSIZE, %sp ! establish sp, for interrupt overflow
#if defined(A_HAT_CPU) || defined(P_CPUID)
!
! Now, if the parent and child have different address spaces,
! we can and should mark the parent's address space as
! "nowhere" so the parent can be staged. Doing this before
! switching away from the parent's context number is fatal.
!
tst %l5 ! if oldproc is null,
beq 9f ! handle it gracefully.
nop ! (should never happen)
cmp %i2, %l5 ! if oldproc is "me",
beq 9f ! handle it gracefully.
nop ! (happens when creating proc 0)
#ifdef A_HAT_CPU
ld [%i2+P_AS], %l1 ! child address space
ld [%l5+P_AS], %l3 ! parent address space
tst %l3 ! if parent has an address space,
beq 8f
cmp %l1, %l3 ! and parent and child
bne,a 8f ! are not sharing p_as,
stb %g0, [%l3+A_HAT_CPU] ! mark parent AS as
8: ! "nowhere".
#endif A_HAT_CPU
#ifdef P_CPUID
!
! Make the old proc's P_CPUID field negative
! so someone else can stage it.
! NOTE: we leave the last bits alone so we can find
! out where the process most recently ran.
! NOTE: if registers get tight, this can be done
! using an LDSTUB.
!
sub %g0, 1, %l4
stb %l4, [%l5+P_CPUID] ! yes, only the first byte.
#endif P_CPUID
9:
#endif A_HAT_CPU || P_CPUID
mov %psr, %g1 ! spl 0
andn %g1, PSR_PIL, %g1
mov %g1, %psr
ld [%i1], %i0 ! %l0 has arg, set arg to new proc
jmp %l6
restore ! restore causes underflow
/*
* finishexit(vaddr)
* Done with the current stack;
* switch to the interrupt stack, segu_release, and swtch.
*/
ENTRY(finishexit)
save %sp, -SA(MINFRAME), %sp
! XXX - previous calls caused windows to be flushed
! so the following flush may not be needed
call _flush_windows ! dump reigster file now
nop ! registers needed for post mortem
mov %psr, %l4
or %l4, PSR_PIL, %o5
mov %o5, %psr ! spl hi
!
! If we are on the interrupt stack, panic.
!
#ifdef MULTIPROCESSOR
set intstack, %o2
cmp %sp, %o2 ! check for < intstack
bl 0f ! if true, was not on int stk
nop
#endif MULTIPROCESSOR
set eintstack, %o3
cmp %sp, %o3 ! check for > eintstack
bg 0f ! if true, was not on int stk
nop
set 3f, %o0 ! else, we were on int stack
call _panic ! so just go panic.
nop
3: .asciz "finishexit"
.align 4
0:
!
! Switch to the exit stack.
!
set exitstack, %o3
sub %o3, SA(MINFRAME), %sp ! switch to exit stack
set intu, %o2 ! temporary uarea
sethi %hi(_masterprocp), %o5
sethi %hi(_uunix), %o1
ld [%o5+%lo(_masterprocp)], %l5 ! get old proc ptr
st %o2, [%o1+%lo(_uunix)] ! set new user ptr
st %g0, [%o5+%lo(_masterprocp)] ! set new proc ptr
mov 1, %o1
sethi %hi(_noproc), %o2 ! no process now
st %o1, [%o2+%lo(_noproc)]
call _segu_release
mov %i0, %o0 ! arg to segu_release
#if defined(P_CPUID) || defined(A_HAT_CPU)
!
! If the old proc was not null,
! destage it.
!
tst %l5
be 9f
nop
#ifdef A_HAT_CPU
!
! If it had an AS, flag it as nowhere.
!
ld [%l5 + P_AS], %g1
tst %g1
bne,a 8f
stb %g0, [%g1 + A_HAT_CPU]
8:
#endif A_HAT_CPU
#ifdef P_CPUID
!
! Make the its P_CPUID field negative
! so someone else can stage it.
! NOTE: we leave the last bits alone so we can find
! out where the process most recently ran.
! NOTE: if registers get tight, this can be done
! using an LDSTUB.
!
sub %g0, 1, %g1
stb %g1, [%l5 + P_CPUID]
#endif P_CPUID
9:
#endif P_CPUID || A_HAT_CPU
mov %l4, %psr ! restore previous priority
nop ! psr delay
b _swtch
nop
/*
* Setmmucntxt(procp)
*
* initialize mmu context for a process, if it has one
*/
ENTRY(setmmucntxt)
ld [%o0 + P_AS], %o1
tst %o1
bz 1f ! if proc has an address space,
mov %g0, %o0 ! (delay) default context number is zero
ld [%o1 + A_HAT_CTX], %o1
tst %o1
bz 1f ! and address space has a context,
mov %g0, %o0
ld [%o1 + C_NUM], %o0 ! get context number
1:
b,a _mmu_setctx ! change context number.