Files
seta75D ff309bfe1c Init
2021-10-11 18:37:13 -03:00

655 lines
16 KiB
ArmAsm

/* @(#)sparc_subr.s 1.1 94/10/31 SMI */
/*
* Copyright (c) 1989 by Sun Microsystems, Inc.
*/
/*
* General assembly language routines.
* It is the intent of this file to contain routines that are
* independent of the specific kernel architecture, and those that are
* common across kernel architectures.
* As architectures diverge, and implementations of specific
* architecture-dependent routines change, the routines should be moved
* from this file into the respective ../`arch -k`/subr.s file.
* For example, this file used to contain getidprom(), but the sun4c
* getidprom() diverged from the sun4 getidprom() so getidprom() was
* moved.
*/
#include <machine/param.h>
#include <machine/asm_linkage.h>
#include <machine/psl.h>
#include <machine/mmu.h>
#include <machine/intreg.h>
#include <machine/enable.h>
#include "assym.s"
.seg "text"
.align 4
/*
* Macro to raise processor priority level.
* Avoid dropping processor priority if already at high level.
* Note, these macros return the priority field
* as it appears in place in the psr
*/
#define RAISE(level) \
mov %psr, %o0; \
and %o0, PSR_PIL, %g1; \
cmp %g1, (level << PSR_PIL_BIT); \
bl,a 1f; \
andn %o0, PSR_PIL, %g1; \
retl; \
nop; \
1: or %g1, (level << PSR_PIL_BIT), %g1; \
mov %g1, %psr; \
nop; \
retl; \
nop;
#define SETPRI(level) \
mov %psr, %o0; \
andn %o0, PSR_PIL, %g1; \
or %g1, (level << PSR_PIL_BIT), %g1; \
mov %g1, %psr; \
nop; \
retl; \
nop;
/*
* Berkley 4.3 introduced symbolically named interrupt levels
* as a way deal with priority in a machine independent fashion.
* Numbered priorities are machine specific, and should be
* discouraged where possible.
*
* Note, for the machine specific priorities there are
* examples listed for devices that use a particular priority.
* It should not be construed that all devices of that
* type should be at that priority. It is currently were
* the current devices fit into the priority scheme based
* upon time criticalness.
*
* The underlying assumption of these assignments is that
* SPARC IPL 10 is the highest level from which a device
* routine can call wakeup. Devices that interrupt from higher
* levels are restricted in what they can do. If they need
* kernels services they should schedule a routine at a lower
* level (via software interrupt) to do the required
* processing.
*
* Examples of this higher usage:
* Level Usage
* 15 Asynchronous memory exceptions (Non-maskable)
* 14 Profiling clock (and PROM uart polling clock)
* 13 Audio device (on sun4c)
* 12 Serial ports
* 11 Floppy controller (on sun4c)
*
* The serial ports request lower level processing on level 6.
* Audio and floppy request lower level processing on level 4.
*
* Also, almost all splN routines (where N is a number or a
* mnemonic) will do a RAISE(), on the assumption that they are
* never used to lower our priority.
* The exceptions are:
* spl8() Because you can't be above 15 to begin with!
* splzs() Because this is used at boot time to lower our
* priority, to allow the PROM to poll the uart.
* spl1() Used by queuerun() to lower priority
* spl0() Used to lower priority to 0.
* splsoftclock() Used by hardclock to lower priority.
*/
/* locks out all interrupts, including memory errors */
ENTRY(spl8)
SETPRI(15)
/* just below the level that profiling runs */
#if defined(sun4c) || defined(sun4m)
ALTENTRY(splaudio) /* only meaningful on 4c, 4m */
#endif /* sun4c || sun4m */
ENTRY(spl7)
RAISE(13)
/* sun specific - highest priority onboard serial i/o zs ports */
ALTENTRY(splzs)
SETPRI(12) /* Can't be a RAISE, as it's used to lower us */
/*
* should lock out clocks and all interrupts,
* as you can see, there are exceptions
*/
ALTENTRY(splhigh)
/* the standard clock interrupt priority */
ALTENTRY(splclock)
/* highest priority for any tty handling */
ALTENTRY(spltty)
/* highest priority required for protection of buffered io system */
ALTENTRY(splbio)
/* machine specific */
ENTRY2(spl6,spl5)
RAISE(10)
/*
* machine specific
* for sun, some frame buffers must be at this priority
*/
ENTRY(spl4)
RAISE(8)
/* highest level that any network device will use */
/* must be lower than highest interrupting ether device */
ENTRY(splimp)
RAISE(7)
/*
* machine specific
* for sun, devices with limited buffering: tapes, ethernet
*/
ENTRY(spl3)
RAISE(6)
/*
* machine specific - not as time critical as above
* for sun, disks
*/
ENTRY(spl2)
RAISE(4)
ENTRY(spl1)
SETPRI(2)
/* highest level that any protocol handler will run */
ENTRY(splnet)
RAISE(1)
/* softcall priority */
/* used by hardclock to LOWER priority */
ENTRY(splsoftclock)
SETPRI(1)
/* allow all interrupts */
ENTRY(spl0)
SETPRI(0)
/*
* splvm will raise the processor priority to the value implied by splvm_val
* which is initialized by the autoconfiguration code to a sane default.
*/
ENTRY(splvm)
sethi %hi(_splvm_val), %o0
ld [%o0 + %lo(_splvm_val)], %o0
! fall through to splr
/*
* splr(psr_pri_field)
* splr is like splx but will only raise the priority and never drop it
*/
ENTRY(splr)
mov %psr, %o1; ! get current psr value
and %o1, PSR_PIL, %o3 ! interlock slot, mask out rest of psr
and %o0, PSR_PIL, %o0 ! only look at priority, paranoid
cmp %o3, %o0 ! if current pri < new pri, set new pri
bl pri_common ! use common code to set priority
andn %o1, PSR_PIL, %o2 ! zero current priority level
retl ! return old priority
mov %o3, %o0
/*
* splx(psr_pri_field)
* set the priority to given value
* Note, this takes a sun4 priority number and 'ors' it into the psr
* A spl() returns this type of cookie so that a splx() does the right thing
*/
ENTRY(splx)
pri_set_common:
mov %psr, %o1 ! get current priority level
andn %o1, PSR_PIL, %o2 ! zero current priority level
and %o0, PSR_PIL, %o0 ! only look at priority, paranoid
pri_common:
or %o2, %o0, %o2 ! or in new priority level
mov %o2, %psr ! set priority
and %o1, PSR_PIL, %o0 ! psr delay...
retl
.empty ! next instruction ok in delay slot
/*
* no_fault()
* turn off fault catching.
*/
ENTRY(no_fault)
sethi %hi(_uunix), %o2
ld [%o2 + %lo(_uunix)], %o2
retl
clr [%o2+U_LOFAULT] ! turn off lofault
/*
* on_fault()
* Catch lofault faults. Like setjmp except it returns one
* if code following causes uncorrectable fault. Turned off
* by calling no_fault().
*/
ENTRY(on_fault)
set lfault, %o0 ! use special jmp_buf
set catch_fault, %o1
sethi %hi(_uunix), %o2
ld [%o2 + %lo(_uunix)], %o2
b _setjmp ! let setjmp do the rest
st %o1, [%o2+U_LOFAULT] ! put catch_fault in u.u_lofault
catch_fault:
save %sp, -WINDOWSIZE, %sp ! goto next window so that we can rtn
set lfault, %o0 ! use special jmp_buf
sethi %hi(_uunix), %o2
ld [%o2 + %lo(_uunix)], %o2
b _longjmp ! let longjmp do the rest
clr [%o2+U_LOFAULT] ! turn off lofault
.reserve lfault, (2*4), "data", 4 ! special jmp_buf for on_fault
/*
* Setjmp and longjmp implement non-local gotos using state vectors
* type label_t.
*
* setjmp(lp)
* label_t *lp;
*/
ENTRY(setjmp)
st %o7, [%o0 + L_PC] ! save return address
st %sp, [%o0 + L_SP] ! save stack ptr
retl
clr %o0 ! return 0
/*
* longjmp(lp)
* label_t *lp;
*/
ENTRY(longjmp)
!
! The following save is required so that an extra register
! window is flushed. Flush_windows flushes nwindows-2
! register windows. If setjmp and longjmp are called from
! within the same window, that window will not get pushed
! out onto the stack without the extra save below. Tail call
! optimization can lead to callers of longjmp executing
! from a window that could be the same as the setjmp,
! thus the need for the following save.
!
save %sp, -SA(WINDOWSIZE), %sp
call _flush_windows ! flush all but this window
nop
ld [%i0 + L_PC], %i7 ! restore return addr
ld [%i0 + L_SP], %fp ! restore sp for dest on foreign stack
ret ! return 1
restore %g0, 1, %o0 ! takes underflow, switches stacks
#ifdef sun4
/*
* Enable and disable video.
*/
ENTRY(setvideoenable)
tst %o0
bnz _on_enablereg
mov ENA_VIDEO, %o0
b,a _off_enablereg
#endif sun4
#ifndef sun4m
/*
* Enable DVMA.
*/
ENTRY(enable_dvma)
mov ENA_SDVMA, %o0 ! enable system DVMA
! fall through to on_enablereg
/*
* Turn on a bit in the system enable register.
* on_enablereg((u_char)bit)
*/
ENTRY(on_enablereg)
mov %psr, %o3
or %o3, PSR_PIL, %g1 ! spl hi to lock enable reg update
mov %g1, %psr
nop; nop; ! psr delay
sethi %hi(_enablereg), %o1 ! software copy of enable register
ldub [%o1 + %lo(_enablereg)], %g1 ! get software copy
set ENABLEREG, %o2 ! address of real version in hardware
bset %o0, %g1 ! turn on bit
stb %g1, [%o1 + %lo(_enablereg)] ! update software copy
stba %g1, [%o2]ASI_CTL ! write out new enable register
stb %g1, [%o1 + %lo(_enablereg)] ! update soft copy, for cache off
mov %o3, %psr ! restore psr
nop ! psr delay
retl
nop
#endif !sun4m
/*
* usec_delay(n)
* int n;
* delay for n microseconds. numbers <= 0 delay 1 usec
*
* the inner loop counts for current sun4s, initialized in startup():
* inner loop is 2 cycles, the outer loop adds 3 more cycles.
* Cpudelay*cycletime(ns)*2 + cycletime(ns)*3 >= 1000
*
* model cycletime(ns) Cpudelay
* 110 66 7
* 260 60 7
* 330 40 11
* 470 30 16
*
* sun4m var. calculated in machdep.c
*/
ENTRY(usec_delay)
sethi %hi(_Cpudelay), %o5
ld [%o5 + %lo(_Cpudelay)], %o4 ! microsecond countdown counter
orcc %o4, 0, %o3 ! set cc bits to nz
1: bnz 1b ! microsecond countdown loop
subcc %o3, 1, %o3 ! 2 instructions in loop
subcc %o0, 1, %o0 ! now, for each microsecond...
bg 1b ! go n times through above loop
orcc %o4, 0, %o3
retl
nop
/*
* movtuc(length, from, to, table)
*
* VAX movtuc instruction (sort of).
*/
ENTRY(movtuc)
tst %o0
ble 2f ! check length
clr %o4
ldub [%o1 + %o4], %g1 ! get next byte in string
0:
ldub [%o3 + %g1], %g1 ! get corresponding table entry
tst %g1 ! escape char?
bnz 1f
stb %g1, [%o2 + %o4] ! delay slot, store it
retl ! return (bytes moved)
mov %o4, %o0
1:
inc %o4 ! increment index
cmp %o4, %o0 ! index < length ?
bl,a 0b
ldub [%o1 + %o4], %g1 ! delay slot, get next byte in string
2:
retl ! return (bytes moved)
mov %o4, %o0
#ifndef sun4m
/*
* Disable DVMA.
*/
ENTRY(disable_dvma)
mov ENA_SDVMA, %o0 ! disable system DVMA
! fall through to off_enablereg
/*
* Turn off a bit in the system enable register.
* off_enablereg((u_char)bit)
*/
ENTRY(off_enablereg)
mov %psr, %o3
or %o3, PSR_PIL, %g1 ! spl hi to lock enable reg update
mov %g1, %psr
nop; nop; ! psr delay
sethi %hi(_enablereg), %o1 ! software copy of enable register
ldub [%o1 + %lo(_enablereg)], %g1 ! get software copy
set ENABLEREG, %o2 ! address of real version in hardware
bclr %o0, %g1 ! turn off bit
stb %g1, [%o1 + %lo(_enablereg)] ! update software copy
stba %g1, [%o2]ASI_CTL ! write out new enable register
mov %o3, %psr ! restore psr
nop ! psr delay
retl
nop
#endif !sun4m
/*
* scanc(length, string, table, mask)
*
* VAX scanc instruction.
*/
ENTRY(scanc)
tst %o0
ble 1f ! check length
clr %o4
0:
ldub [%o1 + %o4], %g1 ! get next byte in string
cmp %o4, %o0 ! interlock slot, index < length ?
ldub [%o2 + %g1], %g1 ! get corresponding table entry
bge 1f ! interlock slot
btst %o3, %g1 ! apply the mask
bz,a 0b
inc %o4 ! delay slot, increment index
1:
retl ! return(length - index)
sub %o0, %o4, %o0
/*
* if a() calls b() calls caller(),
* caller() returns return address in a().
*/
ALTENTRY(caller)
retl
mov %i7, %o0
/*
* if a() calls callee(), callee() returns the
* return address in a();
*/
ALTENTRY(callee)
retl
mov %o7, %o0
/*
* Get vector base register
*/
ALTENTRY(gettbr)
ALTENTRY(getvbr)
retl ! leaf routine return
mov %tbr, %o0 ! read trap base register
/*
* Get processor state register
*/
ALTENTRY(getpsr)
retl ! leaf routine return
mov %psr, %o0 ! read program status register
/*
* return the current stack pointer
*/
ALTENTRY(getsp)
retl ! leaf routine return
mov %sp, %o0 ! return sp
/*
* insque(entryp, predp)
*
* Insert entryp after predp in a doubly linked list.
*/
ENTRY(_insque)
ld [%o1], %g1 ! predp->forw
st %o1, [%o0 + 4] ! entryp->back = predp
st %g1, [%o0] ! entryp->forw = predp->forw
st %o0, [%o1] ! predp->forw = entryp
retl
st %o0, [%g1 + 4] ! predp->forw->back = entryp
/*
* remque(entryp)
*
* Remove entryp from a doubly linked list
*/
ENTRY(_remque)
ld [%o0], %g1 ! entryp->forw
ld [%o0 + 4], %g2 ! entryp->back
st %g1, [%g2] ! entryp->back = entryp->forw
retl
st %g2, [%g1 + 4] ! entryp->forw = entryp->back
#ifndef sun4m
/*
* Turn on or off bits in the interrupt register.
* We must lock out interrupts, since we don't have an atomic or/and to mem.
* set_intreg(bit, flag)
* int bit; bit mask in interrupt reg
* int flag; 0 = off, otherwise on
*/
ALTENTRY(set_intreg)
ALTENTRY(set_intmask) ! alt entry point, mask is here.
mov %psr, %g2
or %g2, PSR_PIL, %g1 ! spl hi to protect intreg update
mov %g1, %psr
nop; ! psr delay
tst %o1
set INTREG_ADDR, %o3 ! interrupt register address
ldub [%o3], %g1 ! read interrupt register
bnz,a 1f
bset %o0, %g1 ! on
bclr %o0, %g1 ! off
1:
stb %g1, [%o3] ! request a level 1 interrupt
mov %g2, %psr ! splx
nop ! psr delay
retl
nop
#endif
/*
* return 1 if an interrupt is being serviced (on interrupt stack),
* otherwise return 0.
#ifdef MULTIPROCESSOR
* The interrupt stack resides in per-cpu storage high in the
* virtual address space, higher than the user stacks and lower
* than the kernel stack. Use a range check instead of just
* assuming any stack ptr less than eintstack is on this stack.
#endif MULTIPROCESSOR
*/
ALTENTRY(servicing_interrupt)
clr %o0 ! assume no
#ifdef MULTIPROCESSOR
set intstack, %g1
cmp %sp, %g1 ! check if on int stack
blu 1f ! if no, return.
.empty ! first half of following set ok in trailer.
#endif MULTIPROCESSOR
set eintstack, %g1
cmp %sp, %g1 ! check if on int stack
bleu,a 1f ! no, we annul the next instruction
mov 1,%o0
1:
retl ! leaf routine return
nop !
/*
* Turn on a software interrupt (H/W level 1).
*/
ALTENTRY(siron)
set IR_SOFT_INT1, %o0
b _set_intreg
mov 1, %o1
#ifdef sun4
/*
* Enable and disable video interrupt. (sun4 only)
* setintrenable(value)
* int value; 0 = off, otherwise on
*/
ALTENTRY(setintrenable)
mov %o0, %o1
b _set_intreg
mov IR_ENA_VID8, %o0
#endif sun4
/***********************************************************************
* Hardware Access Routines
*/
/*
* Make the "LDSTUB" instruction available
* for C code to use. Parameter is ptr to char;
* sets byte to 0xFF and returns the old value.
*/
ALTENTRY(ldstub)
mov %o0, %o1
retl
ldstub [%o1], %o0
/*
* Make the "SWAPL" instruction available
* for C code to use. First parm is new value,
* second parm is ptr to int; returns old value.
*/
ALTENTRY(swapl)
#ifdef JUNK_AROUND_SWAPL
mov %psr, %o4
andn %o4, PSR_ET, %o3
mov %o3, %psr ! disable traps
nop ; nop ; nop ! complete paranoia
mov 1, %o5 ! what we will write to ECC register ...
ld [%o1], %g0 ! prevent tlb miss on swap
#endif
swap [%o1], %o0
#ifdef JUNK_AROUND_SWAPL
sta %o5, [%g0]0x2F ! force some MBus activity (write ECC reg)
nop ; nop ; nop ! complete paranoia
mov %o4, %psr ! restore trap enable bit
nop ; nop ; nop ! complete paranoia
#endif
retl
nop
/*
* Generic "test and set" function
*/
ALTENTRY(tas)
mov %o0, %o1
b _swapl
sub %g0, 1, %o0
/*
* Generic "test and clear" function
*/
ALTENTRY(tac)
mov %o0, %o1
b _swapl
mov %g0, %o0
/*
* stack register access
*/
ALTENTRY(stackpointer)
retl
mov %o6, %o0
ALTENTRY(framepointer)
retl
mov %i6, %o0
/*
* flush(va)
*/
ALTENTRY(flush)
retl
iflush %o0