This commit is contained in:
seta75D
2021-10-11 18:37:13 -03:00
commit ff309bfe1c
14130 changed files with 3180272 additions and 0 deletions

20
sys/sparc/Makefile Normal file
View File

@@ -0,0 +1,20 @@
#
# @(#)Makefile 1.1 94/10/31 SMI
#
FILES= addupc.s copy.s crt.s float.s mcount.s ocsum.s overflow.s \
sparc_subr.s swtch.s underflow.s zs_asm.s
HFILES= a.out.h asm_linkage.h frame.h pcb.h psl.h reg.h setjmp.h trap.h
HDIR=$(DESTDIR)/usr/include/sparc
all install: $(FILES)
cd fpu && $(MAKE) $@
clean:
install_h: $(HFILES) FRC
install -d -m 755 $(HDIR)
install -m 444 $(HFILES) $(HDIR)
cd fpu && $(MAKE) $@
FRC:

181
sys/sparc/a.out.h Normal file
View File

@@ -0,0 +1,181 @@
/* @(#)a.out.h 1.1 94/10/31 SMI; from UCB 4.1 83/05/03 */
#ifndef _sparc_a_out_h
#define _sparc_a_out_h
#include <sys/exec.h>
/*
* memory management parameters
*/
#define PAGSIZ 0x02000
#define SEGSIZ PAGSIZ
#define OLD_PAGSIZ 0x00800 /* Page size under Release 2.0 */
#define OLD_SEGSIZ 0x08000 /* Segment size under Release 2.0 */
/*
* returns 1 if an object file type is invalid, i.e., if the other macros
* defined below will not yield the correct offsets. Note that a file may
* have N_BADMAG(x) = 0 and may be fully linked, but still may not be
* executable.
*/
#define N_BADMAG(x) \
((x).a_magic!=OMAGIC && (x).a_magic!=NMAGIC && (x).a_magic!=ZMAGIC)
/*
* relocation parameters. These are architecture-dependent
* and can be deduced from the machine type. They are used
* to calculate offsets of segments within the object file;
* See N_TXTOFF(x), etc. below.
*/
#define N_PAGSIZ(x) \
((x).a_machtype == M_OLDSUN2? OLD_PAGSIZ : PAGSIZ)
#define N_SEGSIZ(x) \
((x).a_machtype == M_OLDSUN2? OLD_SEGSIZ : SEGSIZ)
/*
* offsets of various sections of an object file.
*/
#define N_TXTOFF(x) \
/* text segment */ \
( (x).a_machtype == M_OLDSUN2 \
? ((x).a_magic==ZMAGIC ? N_PAGSIZ(x) : sizeof (struct exec)) \
: ((x).a_magic==ZMAGIC ? 0 : sizeof (struct exec)) )
#define N_DATOFF(x) /* data segment */ \
(N_TXTOFF(x) + (x).a_text)
#define N_TRELOFF(x) /* text reloc'n */ \
(N_DATOFF(x) + (x).a_data)
#define N_DRELOFF(x) /* data relocation*/ \
(N_TRELOFF(x) + (x).a_trsize)
#define N_SYMOFF(x) \
/* symbol table */ \
(N_TXTOFF(x)+(x).a_text+(x).a_data+(x).a_trsize+(x).a_drsize)
#define N_STROFF(x) \
/* string table */ \
(N_SYMOFF(x) + (x).a_syms)
/*
* Macros which take exec structures as arguments and tell where the
* various pieces will be loaded.
*/
#define _N_BASEADDR(x) \
(((x).a_magic == ZMAGIC) && ((x).a_entry < N_PAGSIZ(x)) ? \
0 : N_PAGSIZ(x))
#define N_TXTADDR(x) \
((x).a_machtype == M_OLDSUN2 ? N_SEGSIZ(x) : _N_BASEADDR(x))
#define N_DATADDR(x) \
(((x).a_magic==OMAGIC)? (N_TXTADDR(x)+(x).a_text) \
: (N_SEGSIZ(x)+((N_TXTADDR(x)+(x).a_text-1) & ~(N_SEGSIZ(x)-1))))
#define N_BSSADDR(x) (N_DATADDR(x)+(x).a_data)
/*
* Format of a relocation datum.
*/
/*
* Sparc relocation types
*/
enum reloc_type
{
RELOC_8, RELOC_16, RELOC_32, /* simplest relocs */
RELOC_DISP8, RELOC_DISP16, RELOC_DISP32, /* Disp's (pc-rel) */
RELOC_WDISP30, RELOC_WDISP22, /* SR word disp's */
RELOC_HI22, RELOC_22, /* SR 22-bit relocs */
RELOC_13, RELOC_LO10, /* SR 13&10-bit relocs*/
RELOC_SFA_BASE, RELOC_SFA_OFF13, /* SR S.F.A. relocs */
RELOC_BASE10, RELOC_BASE13, RELOC_BASE22, /* base_relative pic */
RELOC_PC10, RELOC_PC22, /* special pc-rel pic*/
RELOC_JMP_TBL, /* jmp_tbl_rel in pic */
RELOC_SEGOFF16, /* ShLib offset-in-seg*/
RELOC_GLOB_DAT, RELOC_JMP_SLOT, RELOC_RELATIVE, /* rtld relocs */
};
/*
* Format of a relocation datum.
*/
struct reloc_info_sparc /* used when header.a_machtype == M_SPARC */
{
unsigned long int r_address; /* relocation addr (offset in segment)*/
unsigned int r_index :24; /* segment index or symbol index */
unsigned int r_extern : 1; /* if F, r_index==SEG#; if T, SYM idx */
int : 2; /* <unused> */
enum reloc_type r_type : 5; /* type of relocation to perform */
long int r_addend; /* addend for relocation value */
};
/*
* Format of a symbol table entry
*/
struct nlist {
union {
char *n_name; /* for use when in-core */
long n_strx; /* index into file string table */
} n_un;
unsigned char n_type; /* type flag (N_TEXT,..) */
char n_other; /* unused */
short n_desc; /* see <stab.h> */
unsigned long n_value; /* value of symbol (or sdb offset) */
};
/*
* Simple values for n_type.
*/
#define N_UNDF 0x0 /* undefined */
#define N_ABS 0x2 /* absolute */
#define N_TEXT 0x4 /* text */
#define N_DATA 0x6 /* data */
#define N_BSS 0x8 /* bss */
#define N_COMM 0x12 /* common (internal to ld) */
#define N_FN 0x1e /* file name symbol */
#define N_EXT 01 /* external bit, or'ed in */
#define N_TYPE 0x1e /* mask for all the type bits */
/*
* Dbx entries have some of the N_STAB bits set.
* These are given in <stab.h>
*/
#define N_STAB 0xe0 /* if any of these bits set, a dbx symbol */
/*
* Format for namelist values.
*/
#define N_FORMAT "%08x"
/*
* secondary sections.
* this stuff follows the string table.
* not even its presence or absence is noted in the
* exec header (?). the secondary header gives
* the number of sections. following it is an
* array of "extra_nsects" int's which give the
* sizeof of the individual sections. the presence of
* even the header is optional.
*/
#define EXTRA_MAGIC 1040 /* taxing concept */
#define EXTRA_IDENT 0 /* ident's in 0th extra section */
struct extra_sections {
int extra_magic; /* should be EXTRA_MAGIC */
int extra_nsects; /* number of extra sections */
};
#endif /*!_sparc_a_out_h*/

95
sys/sparc/addupc.s Normal file
View File

@@ -0,0 +1,95 @@
! @(#)addupc.s 1.1 94/10/31 SMI
! Copyright (c) 1986 by Sun Microsystems, Inc.
.seg "text"
.align 4
#include <machine/asm_linkage.h>
!
! Add to user profiling counters.
!
! struct uprof { /* profile arguments */
! short *pr_base; /* buffer base */
! unsigned pr_size; /* buffer size */
! unsigned pr_off; /* pc offset */
! unsigned pr_scale; /* pc scaling */
! } ;
!
! addupc( pc, pr, incr)
! register int pc;
! register struct uprof *pr;
! int incr;
! {
! register short *slot;
! short counter;
!
! slot = pr->pr_base
! + (((pc - pr->pr_off) * pr->pr_scale) >> 16)/(sizeof *slot);
! if (slot >= pr->pr_base &&
! slot < (short *)(pr->pr_size + (int)pr->pr_base)) {
! if ((counter=fusword(slot))<0) {
! pr->pr_scale = 0; /* turn off profiling */
! } else {
! counter += incr;
! susword(slot, counter);
! }
! }
! }
!
.global .mul
PR_BASE = 0
PR_SIZE = 4
PR_OFF = 8
PR_SCALE= 12
/*
* addupc(pc, pr, incr)
*/
ENTRY(addupc)
save %sp, -SA(MINFRAME), %sp
ld [%i1 + PR_OFF], %l0 ! pr->pr_off
subcc %i0, %l0, %o0 ! pc - pr->pr_off
bl out
srl %o0, 1, %o0 ! /2, for sign bit
ld [%i1 + PR_SCALE], %o1
call .mul ! ((pc - pr->pr_off) * pr->pr_scale)/4
srl %o1, 1, %o1 ! /2
sra %o1, 14, %g1 ! overflow after >>14?
tst %g1
bnz out
srl %o0, 14, %l0 ! >>14 double word
sll %o1, 32-14, %g1
or %l0, %g1, %l0
bclr 1, %l0 ! make even
ld [%i1 + PR_SIZE], %l1 ! check length
cmp %l0, %l1
bgeu out
ld [%i1 + PR_BASE], %l1 ! add base
add %l1, %l0, %l0
call _fusword ! fetch counter from user
mov %l0, %o0 ! delay slot
tst %o0
bge,a 1f ! fusword succeeded
add %o0, %i2, %o1 ! counter += incr
!
! Fusword failed turn off profiling.
!
clr [%i1 + PR_SCALE]
ret
restore
!
! Store new counter
!
1:
call _susword ! store counter, checking permissions
mov %l0, %o0 ! delay slot
out:
ret
restore

219
sys/sparc/asm_linkage.h Normal file
View File

@@ -0,0 +1,219 @@
/* @(#)asm_linkage.h 1.1 94/10/31 SMI */
/*
* Copyright (c) 1987 by Sun Microsystems, Inc.
*/
/*
* A stack frame looks like:
*
* %fp->| |
* |-------------------------------|
* | Locals, temps, saved floats |
* |-------------------------------|
* | outgoing parameters past 6 |
* |-------------------------------|-\
* | 6 words for callee to dump | |
* | register arguments | |
* |-------------------------------| > minimum stack frame
* | One word struct-ret address | |
* |-------------------------------| |
* | 16 words to save IN and | |
* %sp->| LOCAL register on overflow | |
* |-------------------------------|-/
*/
#ifndef _sparc_asm_linkage_h
#define _sparc_asm_linkage_h
/*
* Constants defining a stack frame.
*/
#define WINDOWSIZE (16*4) /* size of window save area */
#define ARGPUSHSIZE (6*4) /* size of arg dump area */
#define ARGPUSH (WINDOWSIZE+4) /* arg dump area offset */
#define MINFRAME (WINDOWSIZE+ARGPUSHSIZE+4) /* min frame */
/*
* Stack alignment macros.
*/
#define STACK_ALIGN 8
#define SA(X) (((X)+(STACK_ALIGN-1)) & ~(STACK_ALIGN-1))
/*
* Profiling causes defintions of the MCOUNT and RTMCOUNT
* particular to the type. Note: the nop in the following
* macros allows them to be used in a delay slot.
*/
#ifdef GPROF
#define MCOUNT(x) \
nop; \
save %sp, -SA(MINFRAME), %sp; \
call mcount; \
nop ; \
restore ;
#define RTMCOUNT(x) \
nop; \
save %sp, -SA(MINFRAME), %sp; \
call mcount; \
nop ; \
restore ;
#endif GPROF
#ifdef PROF
#define MCOUNT(x) \
nop; \
save %sp, -SA(MINFRAME), %sp; \
sethi %hi(L_/**/x/**/1), %o0; \
call mcount; \
or %o0, %lo(L_/**/x/**/1), %o0; \
restore; \
.reserve L_/**/x/**/1, 4, "data", 4
#define RTMCOUNT(x) \
nop; \
save %sp, -SA(MINFRAME), %sp; \
sethi %hi(L/**/x/**/1), %o0; \
call mcount; \
or %o0, %lo(L/**/x/**/1), %o0; \
restore; \
.reserve L/**/x/**/1, 4, "data", 4
#endif PROF
/*
* if we are not profiling,
* MCOUNT and RTMCOUNT should be defined to nothing
*/
#if !defined(PROF) && !defined(GPROF)
#define MCOUNT(x)
#define RTMCOUNT(x)
#endif !PROF && !GPROF
/*
* Entry macros for assembler subroutines.
* NAME prefixes the underscore before a symbol.
* ENTRY provides a way to insert the calls to mcount for profiling.
* RTENTRY is similar to the above but provided for run-time routines
* whose names should not be prefixed with an underscore.
*/
#define NAME(x) _/**/x
#define ENTRY(x) \
.global NAME(x); \
NAME(x): MCOUNT(x)
#define ENTRY2(x,y) \
.global NAME(x), NAME(y); \
NAME(x): ; \
NAME(y): MCOUNT(x)
#define RTENTRY(x) \
.global x; x: RTMCOUNT(x)
/*
* For additional entry points.
*/
#define ALTENTRY(x) \
.global NAME(x); \
NAME(x):
#ifdef KERNEL
/*
* Macros for saving/restoring registers.
*/
#define SAVE_GLOBALS(RP) \
st %g1, [RP + G1*4]; \
std %g2, [RP + G2*4]; \
std %g4, [RP + G4*4]; \
std %g6, [RP + G6*4]; \
mov %y, %g1; \
st %g1, [RP + Y*4]
#define RESTORE_GLOBALS(RP) \
ld [RP + Y*4], %g1; \
mov %g1, %y; \
ld [RP + G1*4], %g1; \
ldd [RP + G2*4], %g2; \
ldd [RP + G4*4], %g4; \
ldd [RP + G6*4], %g6;
#define SAVE_OUTS(RP) \
std %i0, [RP + O0*4]; \
std %i2, [RP + O2*4]; \
std %i4, [RP + O4*4]; \
std %i6, [RP + O6*4]
#define RESTORE_OUTS(RP) \
ldd [RP + O0*4], %i0; \
ldd [RP + O2*4], %i2; \
ldd [RP + O4*4], %i4; \
ldd [RP + O6*4], %i6;
#define SAVE_WINDOW(SBP) \
std %l0, [SBP + (0*4)]; \
std %l2, [SBP + (2*4)]; \
std %l4, [SBP + (4*4)]; \
std %l6, [SBP + (6*4)]; \
std %i0, [SBP + (8*4)]; \
std %i2, [SBP + (10*4)]; \
std %i4, [SBP + (12*4)]; \
std %i6, [SBP + (14*4)]
#define RESTORE_WINDOW(SBP) \
ldd [SBP + (0*4)], %l0; \
ldd [SBP + (2*4)], %l2; \
ldd [SBP + (4*4)], %l4; \
ldd [SBP + (6*4)], %l6; \
ldd [SBP + (8*4)], %i0; \
ldd [SBP + (10*4)], %i2; \
ldd [SBP + (12*4)], %i4; \
ldd [SBP + (14*4)], %i6;
#define STORE_FPREGS(FCP) \
std %f0, [FCP + FPCTX_REGS]; \
std %f2, [FCP + FPCTX_REGS + 8]; \
std %f4, [FCP + FPCTX_REGS + 16]; \
std %f6, [FCP + FPCTX_REGS + 24]; \
std %f8, [FCP + FPCTX_REGS + 32]; \
std %f10, [FCP + FPCTX_REGS + 40]; \
std %f12, [FCP + FPCTX_REGS + 48]; \
std %f14, [FCP + FPCTX_REGS + 56]; \
std %f16, [FCP + FPCTX_REGS + 64]; \
std %f18, [FCP + FPCTX_REGS + 72]; \
std %f20, [FCP + FPCTX_REGS + 80]; \
std %f22, [FCP + FPCTX_REGS + 88]; \
std %f24, [FCP + FPCTX_REGS + 96]; \
std %f26, [FCP + FPCTX_REGS + 104]; \
std %f28, [FCP + FPCTX_REGS + 112]; \
std %f30, [FCP + FPCTX_REGS + 120]
#define LOAD_FPREGS(FCP) \
ldd [FCP + FPCTX_REGS ], %f0; \
ldd [FCP + FPCTX_REGS + 8], %f2; \
ldd [FCP + FPCTX_REGS + 16], %f4; \
ldd [FCP + FPCTX_REGS + 24], %f6; \
ldd [FCP + FPCTX_REGS + 32], %f8; \
ldd [FCP + FPCTX_REGS + 40], %f10; \
ldd [FCP + FPCTX_REGS + 48], %f12; \
ldd [FCP + FPCTX_REGS + 56], %f14; \
ldd [FCP + FPCTX_REGS + 64], %f16; \
ldd [FCP + FPCTX_REGS + 72], %f18; \
ldd [FCP + FPCTX_REGS + 80], %f20; \
ldd [FCP + FPCTX_REGS + 88], %f22; \
ldd [FCP + FPCTX_REGS + 96], %f24; \
ldd [FCP + FPCTX_REGS + 104], %f26; \
ldd [FCP + FPCTX_REGS + 112], %f28; \
ldd [FCP + FPCTX_REGS + 120], %f30; \
#endif KERNEL
#endif /*!_sparc_asm_linkage_h*/

1955
sys/sparc/copy.s Normal file

File diff suppressed because it is too large Load Diff

1618
sys/sparc/crt.s Normal file

File diff suppressed because it is too large Load Diff

470
sys/sparc/float.s Normal file
View File

@@ -0,0 +1,470 @@
/* @(#)float.s 1.1 94/10/31 SMI */
/*
* Copyright (c) 1989 by Sun Microsystems, Inc.
*/
#include <machine/asm_linkage.h>
#include <machine/trap.h>
#include <machine/psl.h>
#include <machine/reg.h>
#include <sys/signal.h>
#include "assym.s"
/*
* Floating point trap handling.
*
* The FPU may not be in the current configuration.
* If an fp_disabled trap is generated and the EF bit
* in the psr equals 1 (floating point was enabled)
* then there is not a FPU in the configuration
* and the global variable fp_exists is cleared.
*
* When a user process is first started via exec,
* floating point operations will be disabled by default.
* Upon execution of the first floating point instruction,
* a fp_disabled trap will be generated; at which point
* a check is made to see if the FPU exists, (fp_exists > 0),
* if not the instruction is emulated in software, otherwise
* the uarea is updated signifying use of the FPU so that
* future floating point context switches will save and restore
* floating point state. The trapped instruction will be
* restarted and processing will continue as normal.
*
* When a operation occurs that the hardware cannot properly
* handle, an unfinshed fp_op exception will be generated.
* Software routines in the kernel will be executed to
* simulate proper handling of such conditions.
* Exception handling will emulate all instructions
* in the floating point address queue.
*
* At process context switch time we save fp state of
* the current process if it was using the fpu.
* The next user of the FPU will get a fp_disabled
* trap which restores the state if it was already
* using it or initializes it if the trap was because
* the first use of the FPU by a process.
* An optimization in context switch causes a user whose
* context is already in the FPU to have the FPU reenabled
* automatically without taking fp_disabled trap.
*
* The fp context of process is not stored in the uarea.
* Only a pointer to its state is kept in the uarea.
* The variable fp_ctxp points to the floating point
* context save area for the current context loaded in
* the fpu.
*
* Forks currently always execute the child before returning
* to the parent, therefore fp state of the parent is stored into
* its fp context durring the fork. The child starts with the fpu
* enabled inheriting the parent's registers. When the parent runs
* again it will restore the fp state from the context saved
* during the fork.
*
* NOTE: This code DOES NOT SUPPORT KERNEL USE OF THE FPU
*/
.seg "text"
.align 4
/*
* syncfpu() synchronizes the fpu with the
* the iu as it causes the processor to wait until all
* floating point operations in the FPQ complete...
* by the time the kernel gets to the point of executing
* syncfpu, most floating point operations should have completed
* and generally this happens quickly unless there is pending
* exception which has to be handled.
* We are passed the pointer to trap's (or syscall's) register
* structure, which we save in a global for use in fp_traps() in case we
* do take a pending exception. If we didn't, we'd process the
* exception using the kernel's registers and not the user's, and could
* panic if the user was doing an unimplemented fpop (1025966).
*/
ENTRY(syncfpu)
mov %psr, %o5 ! is floating point enabled
sethi %hi(PSR_EF), %o4
btst %o4, %o5
bz 1f
sethi %hi(_fptraprp), %o5
ld [%o5 + %lo(_fptraprp)], %o3
st %o0, [%o5 + %lo(_fptraprp)]
sethi %hi(fsrholder), %o4
!
! synchronize with fpu, take an exception, if one is pending
!
st %fsr, [%o4 + %lo(fsrholder)]
retl
st %o3, [%o5 + %lo(_fptraprp)]
1: retl
.empty ! the following set PSR_EF is ok in delay slot
/*
* FPU probe - try a floating point instruction to see if there
* really is an FPU in the current configuration, exectued once
* from autoconf when booting.
*/
ENTRY(fpu_probe)
set PSR_EF, %g1 ! enable floating-point
mov %psr, %g2 ! read psr, save value in %g2
or %g1, %g2, %g3 ! new psr with fpu enabled
mov %g3, %psr ! write psr
nop;nop ! psr delay...
sethi %hi(_zeros), %g3
fpuprobe_ld:
ld [%g3 + %lo(_zeros)], %fsr ! probe for fpu, maybe trap
!
! If there is not an FPU, we will get a fp_disabled trap
! when we try to load the fsr, which will clear the fpu_exists flag,
! and skip over the ld
! part of fix for 1041977 (fitoX fix can panic kernel)
! snarf the FPU version, if it exists
sethi %hi(_fpu_exists), %g3
ld [%g3 + %lo(_fpu_exists)], %g3
mov 7, %g1 ! assume no FPU
tst %g3
bz 1f
sethi %hi(_fpu_version), %g3
! We know the fpu exists; we are still enabled for it
sethi %hi(fsrholder), %g1
st %fsr, [%g1 + %lo(fsrholder)]
ld [%g1 + %lo(fsrholder)], %g1 ! snarf the FSR
set FSR_VERS, %o0
and %g1, %o0, %g1 ! get version
srl %g1, FSR_VERS_SHIFT, %g1 ! and shift it down
1:
st %g1, [%g3 + %lo(_fpu_version)]
retl
mov %g2, %psr ! restore old psr, turn off FPU
/*
* Floating Point Exceptions.
* handled according to type:
* 0) no_exception
* do nothing
* 1) IEEE_exception
* re-execute the faulty instruction(s) using
* software emulation (must do every instruction in FQ)
* 2) unfinished_fpop
* re-execute the faulty instruction(s) using
* software emulation (must do every instruction in FQ)
* 3) unimplemented_fpop
* an unimplemented instruction, if it is legal,
* will cause emulation of the instruction (and all
* other instuctions in the FQ)
* 4) sequence_error
* panic, this should not happen, and if it does it
* it is the result of a kernel bug
*
* This code assumes the trap preamble has set up the window evironment
* for execution of kernel code.
*/
.global _fp_exception
_fp_exception:
sethi %hi(_fp_ctxp), %l5
ld [%l5 + %lo(_fp_ctxp)], %l5
mov %l5, %g4
st %fsr, [%l5 + FPCTX_FSR] ! get floating point status
ld [%l5 + FPCTX_FSR], %g1
set FSR_FTT, %l4
and %g1, %l4, %g1 ! mask out trap type
srl %g1, FSR_FTT_SHIFT, %l4 ! use ftt after we dump queue
dumpfq:
clr %g2 ! FQ offset
set FSR_QNE, %g3 ! the queue is not empty bit
add %l5, FPCTX_Q, %l3
1:
ld [%l5 + FPCTX_FSR], %g1 ! test fsr
btst %g3, %g1 ! is the queue empty?
bz 2f ! yes, go figure out what to do
! for Calvin FPU, if two instructions in FQ, and exception and
! [%l3 + %g2] not in cache, gives wrong answer. we preload cache
! BUGID 1038405
ld [%l3 + %g2], %g0 ! Calvin FPU FIX KLUDGE XXX HACK
std %fq, [%l3 + %g2] ! store an entry from FQ
! the fpc that works with the ti8847 requires this
nop; nop;
add %g2, 8, %g2 ! increment offset in FQ
b 1b
st %fsr, [%l5 + FPCTX_FSR] ! get floating point status
2:
!
! emulating floating point instructions
!
wr %l0, PSR_ET, %psr ! enable traps
! three cycle delay required, we are
! not changing the wim so the next
! three instructions are safe
#ifdef MULTIPROCESSOR
nop ! psr delay
call _klock_enter
nop
#endif MULTIPROCESSOR
cmp %l4, FTT_SEQ ! sanity check for bogus exceptions
blt,a fpeok
mov FPCTX_Q, %l4 ! offset into stored queue entries
!
! Sequence error or unknown ftt exception.
!
set badfpexcpmsg, %o0 ! panic
call _panic
mov %l4, %o1 ! mov ftt to o1 for stack backtrace
fpeok:
srl %g2, 3, %l6 ! number of entries stored from fpq
st %l6, [%l5 + FPCTX_QCNT] ! store number of entries (for debug)
! run the floating point q
call _fp_runq
add %sp, MINFRAME, %o0
fp_ret:
!
! clear interrupting condition in fsr
!
ld [%l5 + FPCTX_FSR], %o0
set (FSR_FTT|FSR_QNE), %o1 ! clear ftt bits and qne
andn %o0, %o1, %o0
st %o0, [%l5 + FPCTX_FSR]
b sys_rtt
ld [%l5 + FPCTX_FSR], %fsr ! ld new fsr to set condition codes
/*
* fp_enable(fp)
* struct fpu *fp;
*
* Initialization when there is a hardware fpu.
* Clear the fsr and initialize registers to NaN (-1)
* The caller is supposed to update the return psr
* so when the return to usrerland is made, the fpu is enabled.
*/
ENTRY(fp_enable)
mov %psr, %o1 ! enable the fpu
set PSR_EF, %o2
or %o1, %o2, %o3
mov %o3, %psr
nop;nop;nop ! psr delay
LOAD_FPREGS(%o0) ! read in fpu state
retl
ld [%o0 + FPCTX_FSR], %fsr
#
/*
* fp_dumpregs(fp_ctx_pointer)
*
* This routine is called when a fork is done to cause the current
* register set inside the fpu is put into the new context.
*/
ENTRY(fp_dumpregs)
st %fsr, [%o0 + FPCTX_FSR] ! store fsr in current fpctx
STORE_FPREGS(%o0) ! store rest of regsiters
retl
nop
!void _fp_read_pfreg(pf, n)
! FPU_REGS_TYPE *pf; /* Old freg value. */
! unsigned n; /* Want to read register n. */
!
!{
! *pf = %f[n];
!}
!
!void
!_fp_write_pfreg(pf, n)
! FPU_REGS_TYPE *pf; /* New freg value. */
! unsigned n; /* Want to read register n. */
!
!{
! %f[n] = *pf;
!}
.global __fp_read_pfreg
__fp_read_pfreg:
sll %o1, 3, %o1 ! Table entries are 8 bytes each.
set stable, %g1 ! g1 gets base of table.
jmp %g1 + %o1 ! Jump into table
nop ! Can't follow CTI by CTI.
.global __fp_write_pfreg
__fp_write_pfreg:
sll %o1, 3, %o1 ! Table entries are 8 bytes each.
set ltable, %g1 ! g1 gets base of table.
jmp %g1 + %o1 ! Jump into table
nop ! Can't follow CTI by CTI.
#define STOREFP(n) jmp %o7+8 ; st %f/**/n, [%o0]
stable:
STOREFP(0)
STOREFP(1)
STOREFP(2)
STOREFP(3)
STOREFP(4)
STOREFP(5)
STOREFP(6)
STOREFP(7)
STOREFP(8)
STOREFP(9)
STOREFP(10)
STOREFP(11)
STOREFP(12)
STOREFP(13)
STOREFP(14)
STOREFP(15)
STOREFP(16)
STOREFP(17)
STOREFP(18)
STOREFP(19)
STOREFP(20)
STOREFP(21)
STOREFP(22)
STOREFP(23)
STOREFP(24)
STOREFP(25)
STOREFP(26)
STOREFP(27)
STOREFP(28)
STOREFP(29)
STOREFP(30)
STOREFP(31)
#define LOADFP(n) jmp %o7+8 ; ld [%o0],%f/**/n
ltable:
LOADFP(0)
LOADFP(1)
LOADFP(2)
LOADFP(3)
LOADFP(4)
LOADFP(5)
LOADFP(6)
LOADFP(7)
LOADFP(8)
LOADFP(9)
LOADFP(10)
LOADFP(11)
LOADFP(12)
LOADFP(13)
LOADFP(14)
LOADFP(15)
LOADFP(16)
LOADFP(17)
LOADFP(18)
LOADFP(19)
LOADFP(20)
LOADFP(21)
LOADFP(22)
LOADFP(23)
LOADFP(24)
LOADFP(25)
LOADFP(26)
LOADFP(27)
LOADFP(28)
LOADFP(29)
LOADFP(30)
LOADFP(31)
.global __fp_write_pfsr
__fp_write_pfsr:
retl
ld [%o0], %fsr
badfpexcpmsg:
.asciz "unexpected floating point exception %x\n"
fpemptyqmsg:
.asciz "fp exception with empty queue, fsr 0x%x\n"
badfpdisabledmsg:
.asciz "fp_disabled: no FPU but not fpu_probe"
.align 4
.seg "data"
.align 8
fpjunk:
.double 0
.double 0
/* in the case of MP, we assume either both processors have a
* FPU or they don't.
*/
.global _fpu_exists
_fpu_exists:
.word 1 ! assume FPU exists
fsrholder:
.word 0 ! dummy place to write fsr
! part of fix for 1041977 (fitoX fix can panic kernel)
.global _fpu_version
_fpu_version:
.word -1 ! place to store FPU version
.seg "text"
/*
* Floating point disabled trap, run after trap preamble.
* If FPU does not exist, emulate instruction otherwise,
* enable floating point.
*
*/
.global _fp_disabled
_fp_disabled:
!
! lower priority, allow other interrupts, overflows, ...
!
wr %l0, PSR_ET, %psr ! enable traps
#ifdef MULTIPROCESSOR
nop ! psr delay
call _klock_enter
nop
#endif MULTIPROCESSOR
!
! fp_disable trap when the FPU is enabled; should only happen
! once from autoconf when there is not an FPU in the board.
!
set fpuprobe_ld, %l7
cmp %l1, %l7 ! was trap in fpu_probe, above?
bne 2f ! if not let fp_is_disabled handle it
set PSR_EF, %l5 ! set pSR_EF in delay slot
sethi %hi(_fpu_exists), %l6
clr [%l6 + %lo(_fpu_exists)] ! FPU does not exist
!
! Get the system trap handler's version of the psr and fix it up
! so that sys_rtt will restore a psr with fpu disabled.
!
ld [%sp + MINFRAME + PSR*4], %l6
bclr %l5, %l6 ! clear enable fp bit in psr
st %l6, [%sp + MINFRAME + PSR*4]
!
! skip the probe instruction
!
ld [%sp + MINFRAME + nPC*4], %l6 ! skip the probe instruction
st %l6, [%sp + MINFRAME + PC*4] ! pc = npc
add %l6, 4, %l6
b sys_rtt ! return from trap
st %l6, [%sp + MINFRAME + nPC*4] ! npc += 4
2:
call _fp_is_disabled
add %sp, MINFRAME, %o0
ba sys_rtt
nop

19
sys/sparc/fpu/Makefile Normal file
View File

@@ -0,0 +1,19 @@
#
# @(#)Makefile 1.1 94/10/31 SMI
#
FILES= addsub.c compare.c div.c fpu.c fpu_simulator.c iu_simulator.c \
mul.c pack.c unpack.c utility.c uword.c
HFILES= fpu_simulator.h globals.h ieee.h
HDIR=$(DESTDIR)/usr/include/sparc/fpu
all: $(HFILES) $(FILES)
install: $(FILES)
clean:
install_h: $(HFILES) FRC
install -d -m 755 $(HDIR)
install -m 444 $(HFILES) $(HDIR)
FRC:

221
sys/sparc/fpu/addsub.c Normal file
View File

@@ -0,0 +1,221 @@
#ifdef sccsid
static char sccsid[] = "@(#)addsub.c 1.1 94/10/31 SMI";
#endif
/*
* Copyright (c) 1988 by Sun Microsystems, Inc.
*/
#include <machine/fpu/fpu_simulator.h>
#include <machine/fpu/globals.h>
#ifdef sun4m
extern int ross625_flag; /* flag to identify hyperSPARC */
#endif sun4m
/*ARGSUSED*/
PRIVATE void
true_add(pfpsd, px, py, pz)
fp_simd_type *pfpsd;
unpacked *px, *py, *pz;
{
unsigned c;
unpacked *pt;
#ifdef sun4m
if (((int) px->fpclass < (int) py->fpclass) ||
(ross625_flag && ((int) px->fpclass <= (int) py->fpclass)))
#else sun4m
if ((int) px->fpclass < (int) py->fpclass)
#endif sun4m
{ /* Reverse. */
pt = py;
py = px;
px = pt;
}
/* Now class(x) >= class(y). */
switch (px->fpclass) {
case fp_quiet: /* NaN + x -> NaN */
case fp_signaling: /* NaN + x -> NaN */
case fp_infinity: /* Inf + x -> Inf */
case fp_zero: /* 0 + 0 -> 0 */
*pz = *px;
return;
default:
if (py->fpclass == fp_zero) {
*pz = *px;
return;
}
}
/* Now z is normal or subnormal. */
/* Now y is normal or subnormal. */
if (px->exponent < py->exponent) { /* Reverse. */
pt = py;
py = px;
px = pt;
}
/* Now class(x) >= class(y). */
pz->fpclass = px->fpclass;
pz->sign = px->sign;
pz->exponent = px->exponent;
pz->rounded = pz->sticky = 0;
if (px->exponent != py->exponent) { /* pre-alignment required */
fpu_rightshift(py, pz->exponent - py->exponent);
pz->rounded = py->rounded;
pz->sticky = py->sticky;
}
c = 0;
c = fpu_add3wc(&(pz->significand[3]), px->significand[3],
py->significand[3], c);
c = fpu_add3wc(&(pz->significand[2]), px->significand[2],
py->significand[2], c);
c = fpu_add3wc(&(pz->significand[1]), px->significand[1],
py->significand[1], c);
c = fpu_add3wc(&(pz->significand[0]), px->significand[0],
py->significand[0], c);
/* Handle carry out of msb. */
if (pz->significand[0]>=0x20000) {
fpu_rightshift(pz, 1); /* Carried out bit. */
pz->exponent++; /* Renormalize. */
}
return;
}
PRIVATE void
true_sub(pfpsd, px, py, pz)
fp_simd_type *pfpsd; /* Pointer to simulator data */
unpacked *px, *py, *pz;
{
unsigned *z, g, s, r, c;
unpacked *pt;
#ifdef sun4m
if (((int) px->fpclass < (int) py->fpclass) ||
(ross625_flag && ((int) px->fpclass <= (int) py->fpclass)))
#else sun4m
if ((int) px->fpclass < (int) py->fpclass)
#endif sun4m
{ /* Reverse. */
pt = py;
py = px;
px = pt;
}
/* Now class(x) >= class(y). */
*pz = *px; /* Tentative difference: x. */
switch (pz->fpclass) {
case fp_quiet: /* NaN - x -> NaN */
case fp_signaling: /* NaN - x -> NaN */
return;
case fp_infinity: /* Inf - x -> Inf */
if (py->fpclass == fp_infinity) {
fpu_error_nan(pfpsd, pz); /* Inf - Inf -> NaN */
pz->fpclass = fp_quiet;
}
return;
case fp_zero: /* 0 - 0 -> 0 */
pz->sign = (pfpsd->fp_direction == fp_negative);
return;
default:
if (py->fpclass == fp_zero)
return;
}
/* x and y are both normal or subnormal. */
if (px->exponent < py->exponent) { /* Reverse. */
pt = py;
py = px;
px = pt;
}
/* Now exp(x) >= exp(y). */
pz->fpclass = px->fpclass;
pz->sign = px->sign;
pz->exponent = px->exponent;
pz->rounded = 0;
pz->sticky = 0;
z = pz->significand;
if (px->exponent == py->exponent) { /* no pre-alignment required */
c = 0;
c = fpu_sub3wc(&z[3], px->significand[3],
py->significand[3], c);
c = fpu_sub3wc(&z[2], px->significand[2],
py->significand[2], c);
c = fpu_sub3wc(&z[1], px->significand[1],
py->significand[1], c);
c = fpu_sub3wc(&z[0], px->significand[0],
py->significand[0], c);
if ((z[0]|z[1]|z[2]|z[3])==0) { /* exact zero result */
pz->sign = (pfpsd->fp_direction == fp_negative);
pz->fpclass = fp_zero;
return;
}
if (z[0]>=0x20000) { /* sign reversal occurred */
pz->sign = py->sign;
c = 0;
c = fpu_neg2wc(&z[3], z[3], c);
c = fpu_neg2wc(&z[2], z[2], c);
c = fpu_neg2wc(&z[1], z[1], c);
c = fpu_neg2wc(&z[0], z[0], c);
}
fpu_normalize(pz);
return;
} else { /* pre-alignment required */
fpu_rightshift(py, pz->exponent - py->exponent - 1);
r = py->rounded; /* rounded bit */
s = py->sticky; /* sticky bit */
fpu_rightshift(py, 1);
g = py->rounded; /* guard bit */
if (s!=0) r = (r==0);
if ((r|s)!=0) g = (g==0); /* guard and rounded bits of z */
c = ((g|r|s)!=0);
c = fpu_sub3wc(&z[3], px->significand[3],
py->significand[3], c);
c = fpu_sub3wc(&z[2], px->significand[2],
py->significand[2], c);
c = fpu_sub3wc(&z[1], px->significand[1],
py->significand[1], c);
c = fpu_sub3wc(&z[0], px->significand[0],
py->significand[0], c);
if (z[0]>=0x10000) { /* don't need post-shifted */
pz->sticky = s|r;
pz->rounded = g;
} else { /* post-shifted left 1 bit */
pz->sticky = s;
pz->rounded = r;
pz->significand[0] = (z[0]<<1)|((z[1]&0x80000000)>>31);
pz->significand[1] = (z[1]<<1)|((z[2]&0x80000000)>>31);
pz->significand[2] = (z[2]<<1)|((z[3]&0x80000000)>>31);
pz->significand[3] = (z[3]<<1)|g;
pz->exponent -= 1;
if (z[0]<0x10000) fpu_normalize(pz);
}
return;
}
}
void
_fp_add(pfpsd, px, py, pz)
fp_simd_type *pfpsd;
unpacked *px, *py, *pz;
{
if (px->sign == py->sign)
true_add(pfpsd, px, py, pz);
else
true_sub(pfpsd, px, py, pz);
}
void
_fp_sub(pfpsd, px, py, pz)
fp_simd_type *pfpsd;
unpacked *px, *py, *pz;
{
if ((int)py->fpclass < (int)fp_quiet) py->sign= 1- py->sign;
if (px->sign == py->sign)
true_add(pfpsd, px, py, pz);
else
true_sub(pfpsd, px, py, pz);
}

65
sys/sparc/fpu/compare.c Normal file
View File

@@ -0,0 +1,65 @@
#ifdef sccsid
static char sccsid[] = "@(#)compare.c 1.1 94/10/31 Copyr 1988 Sun Micro";
#endif
/*
* Copyright (c) 1988 by Sun Microsystems, Inc.
*/
#include <machine/fpu/fpu_simulator.h>
#include <machine/fpu/globals.h>
enum fcc_type
_fp_compare(pfpsd, px, py, strict)
fp_simd_type *pfpsd; /* simulator data */
unpacked *px, *py;
int strict; /*
* 0 if quiet NaN unexceptional, 1 if
* exceptional
*/
{
enum fcc_type cc;
int n;
if ((px->fpclass == fp_quiet) || (py->fpclass == fp_quiet) ||
(px->fpclass == fp_signaling) || (py->fpclass == fp_signaling)) {
if (strict) /* NaN */
fpu_set_exception(pfpsd, fp_invalid);
cc = fcc_unordered;
} else if ((px->fpclass == fp_zero) && (py->fpclass == fp_zero))
cc = fcc_equal;
/* both zeros */
else if (px->sign < py->sign)
cc = fcc_greater;
else if (px->sign > py->sign)
cc = fcc_less;
else { /* signs the same, compute magnitude cc */
if ((int) px->fpclass > (int) py->fpclass)
cc = fcc_greater;
else if ((int) px->fpclass < (int) py->fpclass)
cc = fcc_less;
else
/* same classes */ if (px->fpclass == fp_infinity)
cc = fcc_equal; /* same infinity */
else if (px->exponent > py->exponent)
cc = fcc_greater;
else if (px->exponent < py->exponent)
cc = fcc_less;
else { /* equal exponents */
n = fpu_cmpli(px->significand, py->significand, 4);
if (n>0) cc = fcc_greater;
else if (n<0) cc = fcc_less;
else cc = fcc_equal;
}
if (px->sign)
switch (cc) { /* negative numbers */
case fcc_less:
cc = fcc_greater;
break;
case fcc_greater:
cc = fcc_less;
break;
}
}
return (cc);
}

292
sys/sparc/fpu/div.c Normal file
View File

@@ -0,0 +1,292 @@
#ifndef lint
static char sccsid[] = "@(#)div.c 1.1 94/10/31 SMI";
#endif
/*
* Copyright (c) 1988 by Sun Microsystems, Inc.
*/
#include <machine/fpu/fpu_simulator.h>
#include <machine/fpu/globals.h>
#ifdef sun4m
extern int ross625_flag; /* to identify hyperSPARC */
#endif sun4m
void
_fp_div(pfpsd, px, py, pz)
fp_simd_type *pfpsd;
unpacked *px, *py, *pz;
{
unsigned r[4], *y, q, c;
int n;
*pz = *px;
if (((int)py->fpclass >= (int)fp_quiet) ||
((int)px->fpclass >= (int)fp_quiet)) {
#ifdef sun4m
if (((int)py->fpclass > (int)px->fpclass) ||
(ross625_flag && ((int)py->fpclass >= (int)px->fpclass)))
*pz = *py;
#else sun4m
if ((int)py->fpclass > (int)px->fpclass) *pz = *py;
#endif sun4m
return;
}
pz->sign = px->sign ^ py->sign;
switch (px->fpclass) {
case fp_quiet:
case fp_signaling:
return;
case fp_zero:
case fp_infinity:
if (px->fpclass == py->fpclass) { /* 0/0 or inf/inf */
fpu_error_nan(pfpsd, pz);
pz->fpclass = fp_quiet;
}
return;
case fp_normal:
switch (py->fpclass) {
case fp_zero: /* number/0 */
fpu_set_exception(pfpsd, fp_division);
pz->fpclass = fp_infinity;
return;
case fp_infinity: /* number/inf */
pz->fpclass = fp_zero;
return;
}
}
/* Now x and y are both normal or subnormal. */
r[0] = px->significand[0];
r[1] = px->significand[1];
r[2] = px->significand[2];
r[3] = px->significand[3];
y = py->significand;
if (fpu_cmpli(r, y, 4) >= 0)
pz->exponent = px->exponent - py->exponent;
else
pz->exponent = px->exponent - py->exponent - 1;
q=0;
while (q<0x10000) { /* generate quo[0] */
q <<= 1;
if (fpu_cmpli(r, y, 4) >= 0) {
q += 1; /* if r>y do r-=y and q+=1 */
c = 0;
c = fpu_sub3wc(&r[3], r[3], y[3], c);
c = fpu_sub3wc(&r[2], r[2], y[2], c);
c = fpu_sub3wc(&r[1], r[1], y[1], c);
c = fpu_sub3wc(&r[0], r[0], y[0], c);
}
r[0] = (r[0]<<1)|((r[1]&0x80000000)>>31); /* r << 1 */
r[1] = (r[1]<<1)|((r[2]&0x80000000)>>31);
r[2] = (r[2]<<1)|((r[3]&0x80000000)>>31);
r[3] = (r[3]<<1);
}
pz->significand[0]=q;
q=0; /* generate quo[1] */
n = 32;
while (n--) {
q <<= 1;
if (fpu_cmpli(r, y, 4) >= 0) {
q += 1; /* if r>y do r-=y and q+=1 */
c = 0;
c = fpu_sub3wc(&r[3], r[3], y[3], c);
c = fpu_sub3wc(&r[2], r[2], y[2], c);
c = fpu_sub3wc(&r[1], r[1], y[1], c);
c = fpu_sub3wc(&r[0], r[0], y[0], c);
}
r[0] = (r[0]<<1)|((r[1]&0x80000000)>>31); /* r << 1 */
r[1] = (r[1]<<1)|((r[2]&0x80000000)>>31);
r[2] = (r[2]<<1)|((r[3]&0x80000000)>>31);
r[3] = (r[3]<<1);
}
pz->significand[1] = q;
q=0; /* generate quo[2] */
n = 32;
while (n--) {
q <<= 1;
if (fpu_cmpli(r, y, 4) >= 0) {
q += 1; /* if r>y do r-=y and q+=1 */
c = 0;
c = fpu_sub3wc(&r[3], r[3], y[3], c);
c = fpu_sub3wc(&r[2], r[2], y[2], c);
c = fpu_sub3wc(&r[1], r[1], y[1], c);
c = fpu_sub3wc(&r[0], r[0], y[0], c);
}
r[0] = (r[0]<<1)|((r[1]&0x80000000)>>31); /* r << 1 */
r[1] = (r[1]<<1)|((r[2]&0x80000000)>>31);
r[2] = (r[2]<<1)|((r[3]&0x80000000)>>31);
r[3] = (r[3]<<1);
}
pz->significand[2] = q;
q=0; /* generate quo[3] */
n = 32;
while (n--) {
q <<= 1;
if (fpu_cmpli(r, y, 4) >= 0) {
q += 1; /* if r>y do r-=y and q+=1 */
c = 0;
c = fpu_sub3wc(&r[3], r[3], y[3], c);
c = fpu_sub3wc(&r[2], r[2], y[2], c);
c = fpu_sub3wc(&r[1], r[1], y[1], c);
c = fpu_sub3wc(&r[0], r[0], y[0], c);
}
r[0] = (r[0]<<1)|((r[1]&0x80000000)>>31); /* r << 1 */
r[1] = (r[1]<<1)|((r[2]&0x80000000)>>31);
r[2] = (r[2]<<1)|((r[3]&0x80000000)>>31);
r[3] = (r[3]<<1);
}
pz->significand[3] = q;
if ((r[0]|r[1]|r[2]|r[3])==0) pz->sticky = pz->rounded = 0;
else {
pz->sticky = 1; /* half way case won't occur */
if (fpu_cmpli(r, y, 4) >= 0) pz->rounded = 1;
}
}
void
_fp_sqrt(pfpsd, px, pz)
fp_simd_type *pfpsd;
unpacked *px, *pz;
{ /* *pz gets sqrt(*px) */
unsigned *x, r, c, q, t[4], s[4];
*pz = *px;
switch (px->fpclass) {
case fp_quiet:
case fp_signaling:
case fp_zero:
return;
case fp_infinity:
if (px->sign == 1) { /* sqrt(-inf) */
fpu_error_nan(pfpsd, pz);
pz->fpclass = fp_quiet;
}
return;
case fp_normal:
if (px->sign == 1) { /* sqrt(-norm) */
fpu_error_nan(pfpsd, pz);
pz->fpclass = fp_quiet;
return;
}
}
/* Now x is normal. */
x = px->significand;
if (px->exponent & 1) { /*
* sqrt(1.f * 2**odd) = sqrt (2.+2f)
* 2**(odd-1)/2
*/
pz->exponent = (px->exponent - 1) / 2;
x[0] = (x[0]<<1)|((x[1]&0x80000000)>>31); /* x<<1 */
x[1] = (x[1]<<1)|((x[2]&0x80000000)>>31);
x[2] = (x[2]<<1)|((x[3]&0x80000000)>>31);
x[3] = (x[3]<<1);
} else { /*
* sqrt(1.f * 2**even) = sqrt (1.f)
* 2**(even)/2
*/
pz->exponent = px->exponent / 2;
}
s[0]=s[1]=s[2]=s[3]=t[0]=t[1]=t[2]=t[3]=0;
q = 0;
r = 0x00010000;
while (r!=0) { /* compute sqrt[0] */
t[0] = s[0]+r;
if (t[0] <= x[0]) {
s[0] = t[0]+r;
x[0] -= t[0];
q += r;
}
x[0] = (x[0]<<1)|((x[1]&0x80000000)>>31); /* x<<1 */
x[1] = (x[1]<<1)|((x[2]&0x80000000)>>31);
x[2] = (x[2]<<1)|((x[3]&0x80000000)>>31);
x[3] = (x[3]<<1);
r >>= 1;
}
pz->significand[0] = q;
q = 0;
r = 0x80000000;
while (r!=0) { /* compute sqrt[1] */
t[1] = s[1]+r; /* no carry */
t[0] = s[0];
if (fpu_cmpli(t, x, 2) <= 0) {
c = 0;
c = fpu_add3wc(&s[1], t[1], r, c);
c = fpu_add3wc(&s[0], t[0], 0, c);
c = 0;
c = fpu_sub3wc(&x[1], x[1], t[1], c);
c = fpu_sub3wc(&x[0], x[0], t[0], c);
q += r;
}
x[0] = (x[0]<<1)|((x[1]&0x80000000)>>31); /* x<<1 */
x[1] = (x[1]<<1)|((x[2]&0x80000000)>>31);
x[2] = (x[2]<<1)|((x[3]&0x80000000)>>31);
x[3] = (x[3]<<1);
r >>= 1;
}
pz->significand[1] = q;
q = 0;
r = 0x80000000;
while (r!=0) { /* compute sqrt[2] */
t[2] = s[2]+r; /* no carry */
t[1] = s[1];
t[0] = s[0];
if (fpu_cmpli(t, x, 3) <= 0) {
c = 0;
c = fpu_add3wc(&s[2], t[2], r, c);
c = fpu_add3wc(&s[1], t[1], 0, c);
c = fpu_add3wc(&s[0], t[0], 0, c);
c = 0;
c = fpu_sub3wc(&x[2], x[2], t[2], c);
c = fpu_sub3wc(&x[1], x[1], t[1], c);
c = fpu_sub3wc(&x[0], x[0], t[0], c);
q += r;
}
x[0] = (x[0]<<1)|((x[1]&0x80000000)>>31); /* x<<1 */
x[1] = (x[1]<<1)|((x[2]&0x80000000)>>31);
x[2] = (x[2]<<1)|((x[3]&0x80000000)>>31);
x[3] = (x[3]<<1);
r >>= 1;
}
pz->significand[2] = q;
q = 0;
r = 0x80000000;
while (r!=0) { /* compute sqrt[3] */
t[3] = s[3]+r; /* no carry */
t[2] = s[2];
t[1] = s[1];
t[0] = s[0];
if (fpu_cmpli(t, x, 4) <= 0) {
c = 0;
c = fpu_add3wc(&s[3], t[3], r, c);
c = fpu_add3wc(&s[2], t[2], 0, c);
c = fpu_add3wc(&s[1], t[1], 0, c);
c = fpu_add3wc(&s[0], t[0], 0, c);
c = 0;
c = fpu_sub3wc(&x[3], x[3], t[3], c);
c = fpu_sub3wc(&x[2], x[2], t[2], c);
c = fpu_sub3wc(&x[1], x[1], t[1], c);
c = fpu_sub3wc(&x[0], x[0], t[0], c);
q += r;
}
x[0] = (x[0]<<1)|((x[1]&0x80000000)>>31); /* x<<1 */
x[1] = (x[1]<<1)|((x[2]&0x80000000)>>31);
x[2] = (x[2]<<1)|((x[3]&0x80000000)>>31);
x[3] = (x[3]<<1);
r >>= 1;
}
pz->significand[3] = q;
if ((x[0]|x[1]|x[2]|x[3])==0) {
pz->sticky = pz->rounded = 0;
} else {
pz->sticky = 1;
if (fpu_cmpli(s, x, 4)<0) pz->rounded=1; else pz->rounded = 0;
}
}

396
sys/sparc/fpu/fpu.c Normal file
View File

@@ -0,0 +1,396 @@
/* @(#)fpu.c 1.1 94/10/31 SMI; */
#include <sys/types.h>
#include <machine/reg.h>
#include <machine/psl.h>
#include <machine/buserr.h>
#include <machine/trap.h>
#include <sun/fault.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/time.h>
#include <sys/file.h>
#include <sys/proc.h>
#include <sys/map.h>
#include <sys/user.h>
#include <sys/core.h>
#include <sys/ptrace.h>
#include <machine/fpu/fpu_simulator.h>
#include <machine/fpu/globals.h>
/*
* the global pointer to the current floating point context
*/
#ifdef MULTIPROCESSOR
extern struct fpu *fp_ctxp;
#else MULTIPROCESSOR
struct fpu *fp_ctxp = (struct fpu *)0;
#endif MULTIPROCESSOR
extern int fpu_exists;
struct fpu *
fpu_ctxalloc()
{
register struct fpu *fp;
register int i;
fp = (struct fpu *)new_kmem_alloc(sizeof (*fp), KMEM_SLEEP);
fp->fpu_fsr = 0; /* zero fsr */
i = -1;
fp->fpu_regs[0] = i; /* initialize registers to NAN */
fp->fpu_regs[1] = i;
fp->fpu_regs[2] = i;
fp->fpu_regs[3] = i;
fp->fpu_regs[4] = i;
fp->fpu_regs[5] = i;
fp->fpu_regs[6] = i;
fp->fpu_regs[7] = i;
fp->fpu_regs[8] = i;
fp->fpu_regs[9] = i;
fp->fpu_regs[10] = i;
fp->fpu_regs[11] = i;
fp->fpu_regs[12] = i;
fp->fpu_regs[13] = i;
fp->fpu_regs[14] = i;
fp->fpu_regs[15] = i;
fp->fpu_regs[16] = i;
fp->fpu_regs[17] = i;
fp->fpu_regs[18] = i;
fp->fpu_regs[19] = i;
fp->fpu_regs[20] = i;
fp->fpu_regs[21] = i;
fp->fpu_regs[22] = i;
fp->fpu_regs[23] = i;
fp->fpu_regs[24] = i;
fp->fpu_regs[25] = i;
fp->fpu_regs[26] = i;
fp->fpu_regs[27] = i;
fp->fpu_regs[28] = i;
fp->fpu_regs[29] = i;
fp->fpu_regs[30] = i;
fp->fpu_regs[31] = i;
return (fp);
}
fpu_ctxfree()
{
register struct pcb *pcb;
pcb = &u.u_pcb;
if (pcb->pcb_fpctxp) {
if (fp_ctxp == pcb->pcb_fpctxp)
fp_ctxp = 0;
kmem_free((caddr_t)pcb->pcb_fpctxp, sizeof (struct fpu));
pcb->pcb_fpctxp = (struct fpu *) 0;
}
}
/* ARGSUSED */
fpu_fork_context(p2, nfp, new_context)
struct proc *p2;
struct file **nfp;
int *new_context;
{
register struct fpu *fp;
register struct pcb *pcb;
/*
* if the current process is using the fpu,
* allocate a new fp context and initialize it
* with the current fp context state.
*
*/
pcb = &u.u_pcb;
if (pcb->pcb_fpctxp)
{
/* we are forking fpu context due to users fork() call.
* At this time the fpu chip registers belong to the
* parent. First dump the registers to the fpu context.
* then copy the context to the child's context.
*
* Child runs first and therefore the fp_ctxp should
* point to childs fp context. However, the child may not
* started due to other errors during the rest of the fork.
* therefore shutoff PSR_EF for the parent and this will be
* copied to the child in fork(). THen make fp_ctxp 0.
* This will ensure the child and parent have different
* fpu contexts.
*/
if (fpu_exists)
fp_dumpregs((caddr_t)pcb->pcb_fpctxp);
fp = fpu_ctxalloc();
*new_context = (int)fp;
bcopy((caddr_t)pcb->pcb_fpctxp, (caddr_t)fp, sizeof *fp);
u.u_ar0[PS] = u.u_ar0[PS] & ~PSR_EF;
fp_ctxp = 0; /* current loaded context does not belong
* to anyone.
*/
}
return (1);
}
fpu_ofile()
{
}
/* ARGSUSED */
fpu_newproc(nfp, new_context, childu)
struct file *nfp;
int new_context;
struct user *childu;
{
/*
* initialize the fp context pointer in the new procs
* uarea, it was allocated above in fpu_fork_context
*/
childu->u_pcb.pcb_fpctxp = (struct fpu *)new_context;
}
fpu_core(corep)
struct core *corep;
{
if (u.u_pcb.pcb_fpctxp)
bcopy((caddr_t)u.u_pcb.pcb_fpctxp, (caddr_t)&corep->c_fpu,
sizeof (struct fpu));
}
fpu_ptrace(req, p, ipc)
enum ptracereq req;
struct proc *p;
struct ipc *ipc;
{
switch (req) {
case PTRACE_GETFPREGS:
runchild(p);
if (copyout((caddr_t)&(ipc->ip_fpu), ipc->ip_addr,
sizeof (struct fpu)) != 0) {
ipc->ip_error = 1;
}
break;
case PTRACE_SETFPREGS:
if (copyin(ipc->ip_addr, (caddr_t)&ipc->ip_fpu,
sizeof (struct fpu)) != 0) {
ipc->ip_error = 1;
}
runchild(p);
break;
}
}
fpu_procxmt(req, ipc)
enum ptracereq req;
struct ipc *ipc;
{
register struct pcb *pcb = &u.u_pcb;
extern int fpu_exists;
extern struct fpu *fpu_ctxalloc();
switch (req) {
case PTRACE_GETFPREGS:
if (pcb->pcb_fpctxp)
bcopy((caddr_t)pcb->pcb_fpctxp, (caddr_t)&ipc->ip_fpu,
sizeof (struct fpu));
break;
case PTRACE_SETFPREGS:
if (fpu_exists && pcb->pcb_fpctxp) {
register u_int i;
for (i = 0; i < 32; i++) {
_fp_write_pfreg((FPU_REGS_TYPE *)&ipc->ip_fpu.fpu_regs[i], i);
}
_fp_write_pfsr(&ipc->ip_fpu.fpu_fsr);
} else {
if (pcb->pcb_fpctxp == (struct fpu *) 0) {
pcb->pcb_fpctxp = fpu_ctxalloc();
}
bcopy((caddr_t)&ipc->ip_fpu, (caddr_t)pcb->pcb_fpctxp,
sizeof (struct fpu));
pcb->pcb_fpflags &= ~FP_UNINITIALIZED;
if (!fpu_exists)
pcb->pcb_fpflags |= FP_DISABLE;
}
break;
default:
return (-1);
}
return (1);
}
/*
* Handle floaing point traps generated by simulation/emulation.
*/
void
fp_traps(pfpsd, ftt, rp)
fp_simd_type *pfpsd; /* Pointer to simulator data */
register enum ftt_type ftt; /* trap type */
register struct regs *rp; /* ptr to regs fro trap */
{
extern void trap();
extern u_int beval; /* beval is set in trap.c for
specific architectures */
struct regs *saved_fptraprp = 0;
/*
* If we take a user's exception in kernel mode, we want to trap
* with the user's registers. Clear fptraprp in case we don't
* return from trap.
*/
saved_fptraprp = fptraprp;
if (fptraprp != 0) {
rp = fptraprp;
fptraprp = 0;
}
switch (ftt) {
case ftt_ieee:
trap(T_FP_EXCEPTION, rp, pfpsd->fp_trapaddr,
pfpsd->fp_trapcode, 0);
break;
case ftt_fault:
trap(T_DATA_FAULT, rp, pfpsd->fp_trapaddr, beval, pfpsd->fp_traprw);
break;
case ftt_alignment:
trap(T_ALIGNMENT, rp, pfpsd->fp_trapaddr, 0, 0);
break;
case ftt_unimplemented:
trap(T_UNIMP_INSTR, rp, pfpsd->fp_trapaddr, 0, 0);
break;
default:
/*
* We don't expect any of the other types here.
*/
panic("fp_traps: bad ftt");
}
fptraprp = saved_fptraprp;
}
/*
* floating point unit disabled:
* One of two possibilities
* 1. There is no fpu in the confguration. If so, emulate in SW.
* 2. THere is a FPU in config., however, this is the first time the
* process doing a fp op. If so, allocate a fpu context and
* turn on the EF bit in the PSR and let the proc go.
*/
fp_is_disabled(rp)
struct regs *rp;
{
struct fpu *fp;
enum ftt_type ftt;
if (u.u_pcb.pcb_fpctxp == 0) {
fp = fpu_ctxalloc(); /* allocate a fpu context */
uunix->u_pcb.pcb_fpctxp = fp;
}
else
fp = uunix->u_pcb.pcb_fpctxp;
fp_ctxp = fp;
if (fpu_exists) { /* there is a fpu, go ahead and enable the fpu */
if (rp->r_psr&PSR_EF) {
panic("fp_disabled: no FPU but EF bit set");
/* NOT REACHED */
}
else {
/* turn on enable fpu bit in the PSR and let the proc go */
rp->r_psr |= PSR_EF;
fp_enable(fp);
}
} else { /* no fpu, emulate the instruction */
fp_simd_type fpsd;
flush_user_windows_to_stack();
if (ftt = fp_emulator(&fpsd, (fp_inst_type *)rp->r_pc,
rp, (struct rwindow *)rp->r_sp,
(struct fpu *) &fp->fpu_regs[0]))
fp_traps(&fpsd, ftt, rp);
}
}
/*
* Process the floating point queue
*
* Each entry in the floating point queue is processed in turn.
* If processing an entry results in an exception fp_traps() is called to
* handle the exception - this usually results in the generation of a signal
* to be delivered to the user. There are 2 possible outcomes to this (note
* that hardware generated signals cannot be held!):
*
* 1. If the signal is being ignored we continue to process the rest
* of the entries in the queue.
*
* 2. If arrangements have been made for return to a user signal handler,
* sendsig() will have copied the floating point queue onto the user's
* signal stack and zero'ed the queue count in the u_pcb. Note that
* this has the side effect of terminating fp_runq's processing loop.
* We will re-run the floating point queue on return from the user
* signal handler if necessary as part of normal setcontext processing.
*/
void
fp_runq(rp)
register struct regs *rp; /* ptr to regs for trap */
{
register struct fpu *fp = u.u_pcb.pcb_fpctxp;
register struct fq *fqp = fp->fpu_q;
fp_simd_type fpsd;
extern int fpu_exists;
/*
* don't preempt while manipulating the queue
*/
while (fp->fpu_qcnt) {
enum ftt_type fptrap;
fptrap = fpu_simulator((fp_simd_type *)&fpsd,
(fp_inst_type *)fqp->FQu.fpq.addr,
(fsr_type *)&fp->fpu_fsr,
fqp->FQu.fpq.instr);
if (fptrap) {
/*
* fpu_simulator uses the fp registers directly but it
* uses the software copy of the fsr. We need to write
* that back to fpu so that fpu's state is current for
* ucontext.
*/
if (fpu_exists)
_fp_write_pfsr(&fp->fpu_fsr);
/* post signal */
fp_traps(&fpsd, fptrap, rp);
/*
* Break from loop to allow signal to be sent.
*/
break;
}
fp->fpu_qcnt--;
fqp++;
}
/*
* fpu_simulator uses the fp registers directly, so we have
* to update the pcb copies to keep current, but it uses the
* software copy of the fsr, so we write that back to fpu
*/
if (fpu_exists) {
register u_int i;
for (i = 0; i < 32; i++)
_fp_read_pfreg((FPU_REGS_TYPE *)&fp->fpu_regs[i], i);
_fp_write_pfsr((FPU_FSR_TYPE *)&fp->fpu_fsr);
}
}

View File

@@ -0,0 +1,247 @@
#ifdef sccsid
static char sccsid[] = "@(#)fpu_simulator.c 1.1 94/10/31 SMI;
#endif
/*
* Copyright (c) 1988 by Sun Microsystems, Inc.
*/
/* Main procedures for Sparc FPU simulator. */
#include <machine/fpu/fpu_simulator.h>
#include <machine/fpu/globals.h>
#include <sys/proc.h>
#include <sys/signal.h>
/* PUBLIC FUNCTIONS */
PRIVATE enum ftt_type
_fp_fpu_simulator(pfpsd, inst, pfsr)
fp_simd_type *pfpsd; /* Pointer to fpu simulotor data */
fp_inst_type inst; /* FPU instruction to simulate. */
fsr_type *pfsr; /* Pointer to image of FSR to read and write. */
{
unpacked us1, us2, ud; /* Unpacked operands and result. */
unsigned nrs1, nrs2, nrd; /* Register number fields. */
fsr_type fsr;
unsigned usr;
unsigned andexcep;
enum fcc_type cc;
nrs1 = inst.rs1;
nrs2 = inst.rs2;
nrd = inst.rd;
fsr = *pfsr;
pfpsd->fp_current_exceptions = 0; /* Init current exceptions. */
pfpsd->fp_fsrtem = fsr.tem; /* Obtain fsr's tem */
pfpsd->fp_direction = fsr.rd; /* Obtain rounding direction. */
pfpsd->fp_precision = fsr.rp; /* Obtain rounding precision. */
switch (inst.opcode) {
case fmovs:
_fp_unpack_word(pfpsd, &usr, nrs2);
_fp_pack_word(pfpsd, &usr, nrd);
break;
case fabss:
_fp_unpack_word(pfpsd, &usr, nrs2);
usr &= 0x7fffffff;
_fp_pack_word(pfpsd, &usr, nrd);
break;
case fnegs:
_fp_unpack_word(pfpsd, &usr, nrs2);
usr ^= 0x80000000;
_fp_pack_word(pfpsd, &usr, nrd);
break;
case fadd:
_fp_unpack(pfpsd, &us1, nrs1, inst.prec);
_fp_unpack(pfpsd, &us2, nrs2, inst.prec);
_fp_add(pfpsd, &us1, &us2, &ud);
_fp_pack(pfpsd, &ud, nrd, inst.prec);
break;
case fsub:
_fp_unpack(pfpsd, &us1, nrs1, inst.prec);
_fp_unpack(pfpsd, &us2, nrs2, inst.prec);
_fp_sub(pfpsd, &us1, &us2, &ud);
_fp_pack(pfpsd, &ud, nrd, inst.prec);
break;
case fmul:
_fp_unpack(pfpsd, &us1, nrs1, inst.prec);
_fp_unpack(pfpsd, &us2, nrs2, inst.prec);
_fp_mul(pfpsd, &us1, &us2, &ud);
_fp_pack(pfpsd, &ud, nrd, inst.prec);
break;
case fsmuld:
case fdmulx:
_fp_unpack(pfpsd, &us1, nrs1, inst.prec);
_fp_unpack(pfpsd, &us2, nrs2, inst.prec);
_fp_mul(pfpsd, &us1, &us2, &ud);
_fp_pack(pfpsd, &ud, nrd, (enum fp_op_type) ((int)inst.prec+1));
break;
case fdiv:
_fp_unpack(pfpsd, &us1, nrs1, inst.prec);
_fp_unpack(pfpsd, &us2, nrs2, inst.prec);
_fp_div(pfpsd, &us1, &us2, &ud);
_fp_pack(pfpsd, &ud, nrd, inst.prec);
break;
case fcmp:
_fp_unpack(pfpsd, &us1, nrs1, inst.prec);
_fp_unpack(pfpsd, &us2, nrs2, inst.prec);
cc = _fp_compare(pfpsd, &us1, &us2, 0);
if (!(pfpsd->fp_current_exceptions & pfpsd->fp_fsrtem))
fsr.fcc = cc;
break;
case fcmpe:
_fp_unpack(pfpsd, &us1, nrs1, inst.prec);
_fp_unpack(pfpsd, &us2, nrs2, inst.prec);
cc = _fp_compare(pfpsd, &us1, &us2, 1);
if (!(pfpsd->fp_current_exceptions & pfpsd->fp_fsrtem))
fsr.fcc = cc;
break;
case fsqrt:
_fp_unpack(pfpsd, &us1, nrs2, inst.prec);
_fp_sqrt(pfpsd, &us1, &ud);
_fp_pack(pfpsd, &ud, nrd, inst.prec);
break;
case ftoi:
_fp_unpack(pfpsd, &us1, nrs2, inst.prec);
pfpsd->fp_direction = fp_tozero;
/* Force rounding toward zero. */
_fp_pack(pfpsd, &us1, nrd, fp_op_integer);
break;
case ftos:
_fp_unpack(pfpsd, &us1, nrs2, inst.prec);
_fp_pack(pfpsd, &us1, nrd, fp_op_single);
break;
case ftod:
_fp_unpack(pfpsd, &us1, nrs2, inst.prec);
_fp_pack(pfpsd, &us1, nrd, fp_op_double);
break;
case ftox:
_fp_unpack(pfpsd, &us1, nrs2, inst.prec);
_fp_pack(pfpsd, &us1, nrd, fp_op_extended);
break;
default:
return (ftt_unimplemented);
}
fsr.cexc = pfpsd->fp_current_exceptions;
if (pfpsd->fp_current_exceptions) { /* Exception(s) occurred. */
andexcep = pfpsd->fp_current_exceptions & fsr.tem;
if (andexcep != 0) { /* Signal an IEEE SIGFPE here. */
if (andexcep & (1 << fp_invalid))
pfpsd->fp_trapcode = FPE_FLTOPERR_TRAP;
else if (andexcep & (1 << fp_overflow))
pfpsd->fp_trapcode = FPE_FLTOVF_TRAP;
else if (andexcep & (1 << fp_underflow))
pfpsd->fp_trapcode = FPE_FLTUND_TRAP;
else if (andexcep & (1 << fp_division))
pfpsd->fp_trapcode = FPE_FLTDIV_TRAP;
else if (andexcep & (1 << fp_inexact))
pfpsd->fp_trapcode = FPE_FLTINEX_TRAP;
else
pfpsd->fp_trapcode = 0;
*pfsr = fsr;
return (ftt_ieee);
} else { /* Just set accrued exception field. */
fsr.aexc |= pfpsd->fp_current_exceptions;
}
}
*pfsr = fsr;
return (ftt_none);
}
/*
* fpu_simulator simulates FPU instructions only;
* reads and writes FPU data registers directly.
*/
enum ftt_type
fpu_simulator(pfpsd, pinst, pfsr, inst)
fp_simd_type *pfpsd; /* Pointer to simulator data */
fp_inst_type *pinst; /* Address of FPU instruction to simulate. */
fsr_type *pfsr; /* Pointer to image of FSR to read and write.*/
unsigned long inst; /* The FPU instruction to simulate */
{
union {
int i;
fp_inst_type inst;
} kluge;
kluge.i = inst;
pfpsd->fp_trapaddr = (char *) pinst;
pfpsd->fp_current_read_freg = _fp_read_pfreg;
pfpsd->fp_current_write_freg = _fp_write_pfreg;
return (_fp_fpu_simulator(pfpsd, kluge.inst, pfsr));
}
/*
* fp_emulator simulates FPU and CPU-FPU instructions; reads and writes FPU
* data registers from image in pfpu.
*/
enum ftt_type
fp_emulator(pfpsd, pinst, pregs, pwindow, pfpu)
fp_simd_type *pfpsd; /* Pointer to simulator data */
fp_inst_type *pinst; /* Pointer to FPU instruction to simulate. */
struct regs *pregs; /* Pointer to PCB image of registers. */
struct rwindow *pwindow; /* Pointer to locals and ins. */
struct fpu *pfpu; /* Pointer to FPU register block. */
{
union {
int i;
fp_inst_type inst;
} kluge;
enum ftt_type ftt;
u_int tfsr;
tfsr = pfpu->fpu_fsr;
pfpsd->fp_current_pfregs = pfpu;
pfpsd->fp_current_read_freg = _fp_read_vfreg;
pfpsd->fp_current_write_freg = _fp_write_vfreg;
pfpsd->fp_trapaddr = (char *) pinst; /* bad inst addr in case we trap */
ftt = _fp_read_word((caddr_t) pinst, &(kluge.i), pfpsd);
if (ftt != ftt_none)
return (ftt);
if ((kluge.inst.hibits == 2) &&
((kluge.inst.op3 == 0x34) || (kluge.inst.op3 == 0x35))) {
ftt = _fp_fpu_simulator(pfpsd, kluge.inst, (fsr_type *)&tfsr);
pfpu->fpu_fsr = tfsr;
/* Do not retry emulated instruction. */
pregs->r_pc = pregs->r_npc;
pregs->r_npc += 4;
} else
ftt = _fp_iu_simulator(pfpsd, kluge.inst, pregs, pwindow, pfpu);
if (ftt != ftt_none)
return (ftt);
again:
/*
* now read next instruction and see if it can be emulated
*/
pinst = (fp_inst_type *)pregs->r_pc;
pfpsd->fp_trapaddr = (char *) pinst; /* bad inst addr in case we trap */
ftt = _fp_read_word((caddr_t) pinst, &(kluge.i), pfpsd);
if (ftt != ftt_none)
return (ftt);
if ((kluge.inst.hibits == 2) && /* fpops */
((kluge.inst.op3 == 0x34) || (kluge.inst.op3 == 0x35))) {
ftt = _fp_fpu_simulator(pfpsd, kluge.inst, (fsr_type *)&tfsr);
/* Do not retry emulated instruction. */
pfpu->fpu_fsr = tfsr;
pregs->r_pc = pregs->r_npc;
pregs->r_npc += 4;
} else if (
/* fldst */
((kluge.inst.hibits == 3) && ((kluge.inst.op3 & 0x38) == 0x20)) ||
/* fbcc */
((kluge.inst.hibits == 0) && (((kluge.i>>21) & 0x7) == 6))) {
ftt = _fp_iu_simulator(pfpsd, kluge.inst, pregs, pwindow, pfpu);
} else
return (ftt);
if (ftt != ftt_none)
return (ftt);
else
goto again;
}

View File

@@ -0,0 +1,193 @@
/* @(#)fpu_simulator.h 1.1 94/10/31 SMI */
/*
* Copyright (c) 1988 by Sun Microsystems, Inc.
*/
/* Sparc floating-point simulator PUBLIC include file. */
#include <machine/reg.h>
#include <sys/ieeefp.h>
#include <sys/types.h>
#include <vm/seg.h>
/* PUBLIC TYPES */
enum fcc_type { /* relationships */
fcc_equal = 0,
fcc_less = 1,
fcc_greater = 2,
fcc_unordered = 3
};
/* FSR types. */
enum ftt_type { /* types of traps */
ftt_none = 0,
ftt_ieee = 1,
ftt_unfinished = 2,
ftt_unimplemented = 3,
ftt_sequence = 4,
ftt_alignment = 5, /* defined by software convention only */
ftt_fault = 6, /* defined by software convention only */
ftt_7 = 7
};
typedef struct { /* Sparc FSR. */
enum fp_direction_type rd : 2; /* rounding direction */
enum fp_precision_type rp : 2; /* rounding precision */
unsigned tem : 5; /* trap enable mask */
unsigned : 6;
enum ftt_type ftt : 3; /* FPU trap type */
unsigned qne : 1; /* FPQ not empty */
unsigned pr : 1; /* partial result */
enum fcc_type fcc : 2; /* FPU condition code */
unsigned aexc : 5; /* accumulated exception */
unsigned cexc : 5; /* current exception */
} fsr_type;
typedef /* FPU register viewed as single components. */
struct {
unsigned sign : 1;
unsigned exponent : 8;
unsigned significand : 23;
} single_type;
typedef /* FPU register viewed as double components. */
struct {
unsigned sign : 1;
unsigned exponent : 11;
unsigned significand : 20;
} double_type;
typedef /* FPU register viewed as extended components. */
struct {
unsigned sign : 1;
unsigned exponent : 15;
unsigned significand : 16;
} extended_type;
typedef /* FPU register with multiple data views. */
union {
int int_reg;
long long_reg;
unsigned unsigned_reg;
float float_reg;
single_type single_reg;
double_type double_reg;
extended_type extended_reg;
} freg_type;
enum fp_op_type { /* Type specifiers in FPU instructions. */
fp_op_integer = 0, /* Not in hardware, but convenient to define. */
fp_op_single = 1,
fp_op_double = 2,
fp_op_extended = 3
};
enum fp_opcode { /* FPU op codes, minus precision and leading 0. */
fmovs = 0x0,
fnegs = 0x1,
fabss = 0x2,
fp_op_3 = 3, fp_op_4 = 4, fp_op_5 = 5, fp_op_6 = 6, fp_op_7 = 7,
fp_op_8 = 0x8,
fp_op_9 = 0x9,
fsqrt = 0xa,
fp_op_b = 0xb, fp_op_c = 0xc, fp_op_d = 0xd,
fp_op_e = 0xe, fp_op_f = 0xf,
fadd = 0x10,
fsub = 0x11,
fmul = 0x12,
fdiv = 0x13,
fcmp = 0x14,
fcmpe = 0x15,
fp_op_16 = 0x16, fp_op_17 = 0x17,
fp_op_18 = 0x18,
fp_op_19 = 0x19,
fsmuld = 0x1a,
fdmulx = 0x1b,
fp_op_20 = 0x20,
fp_op_21 = 0x21,
fp_op_22 = 0x22,
fp_op_23 = 0x23,
fp_op_24 = 0x24, fp_op_25 = 0x25, fp_op_26 = 0x26, fp_op_27 = 0x27,
fp_op_28 = 0x28, fp_op_29 = 0x29, fp_op_2a = 0x2a, fp_op_2b = 0x2b,
fp_op_2c = 0x2c, fp_op_2d = 0x2d, fp_op_2e = 0x2e, fp_op_2f = 0x2f,
fp_op_30 = 0x30,
ftos = 0x31,
ftod = 0x32,
ftox = 0x33,
ftoi = 0x34,
fp_op_35 = 0x35, fp_op_36 = 0x36, fp_op_37 = 0x37,
ft_op_38 = 0x38,
fp_op_39 = 0x39, fp_op_3a = 0x3a, fp_op_3b = 0x3b,
fp_op_3c = 0x3c,
fp_op_3d = 0x3d, fp_op_3e = 0x3e, fp_op_3f = 0x3f
};
typedef /* FPU instruction. */
struct {
unsigned hibits : 2; /* Top two bits. */
unsigned rd : 5; /* Destination. */
unsigned op3 : 6; /* Main op code. */
unsigned rs1 : 5; /* First operand. */
unsigned ibit : 1; /* I format bit. */
enum fp_opcode opcode : 6; /* Floating-point op code. */
enum fp_op_type prec : 2; /* Precision. */
unsigned rs2 : 5; /* Second operand. */
} fp_inst_type;
typedef /* FPU data used by simulator. */
struct {
unsigned fp_fsrtem;
enum fp_direction_type fp_direction;
enum fp_precision_type fp_precision;
unsigned fp_current_exceptions;
struct fpu * fp_current_pfregs;
void (* fp_current_read_freg) ();
void (* fp_current_write_freg) ();
int fp_trapcode;
char *fp_trapaddr;
struct regs *fp_traprp;
enum seg_rw fp_traprw;
} fp_simd_type;
/* PUBLIC FUNCTIONS */
extern enum ftt_type fpu_simulator(/* pfpsd, pinst, pfsr, instr */);
/* fp_simd_type *pfpsd; Pointer to FPU simulator data */
/* fp_inst_type *pinst; Pointer to FPU instruction to simulate. */
/* fsr_type *pfsr; Pointer to image of FSR to read and write. */
/* int instr; Instruction to emulate. */
/*
* fpu_simulator simulates FPU instructions only; reads and writes FPU data
* registers directly.
*/
extern enum ftt_type fp_emulator (/* pfpsd, pinst, pregs, pwindow, pfpu */);
/* fp_simd_type *pfpsd; Pointer to FPU simulator data */
/* fp_inst_type *pinst; Pointer to FPU instruction to simulate. */
/* struct regs *pregs; Pointer to PCB image of registers. */
/* struct rwindow *pwindow; Pointer to locals and ins. */
/* struct fpu *pfpu; Pointer to FPU register block. */
/*
* fp_emulator simulates FPU and CPU-FPU instructions; reads and writes FPU
* data registers from image in pfpu.
*/
extern void fp_traps(/* pfpsd, ftt, rp */);
/* fp_simd_type *pfpsd; Pointer to FPU simulator data */
/* enum ftt_type ftt; Type of trap. */
/* struct regs *rp; Pointer to PCB image of registers. */
/*
* fp_traps handles passing execption conditions to the kernel.
* It is called after fp_simulator or fp_emulator fail (return a non-zero ftt).
*/

128
sys/sparc/fpu/globals.h Normal file
View File

@@ -0,0 +1,128 @@
/* @(#)globals.h 1.1 94/10/31 SMI */
/*
* Copyright (c) 1988 by Sun Microsystems, Inc.
*/
/* Sparc floating-point simulator PRIVATE include file. */
#include <sys/types.h>
#include <vm/seg.h>
/* PRIVATE CONSTANTS */
/* PRIVATE CONSTANTS */
#define INTEGER_BIAS 31
#define SINGLE_BIAS 127
#define DOUBLE_BIAS 1023
#define EXTENDED_BIAS 16383
/* PRIVATE TYPES */
#ifdef DEBUG
#define PRIVATE
#else
#define PRIVATE static
#endif
#define DOUBLE_E(n) (n & 0xfffe) /* More significant word of double. */
#define DOUBLE_F(n) (1+DOUBLE_E(n)) /* Less significant word of double. */
#define EXTENDED_E(n) (n & 0xfffc) /* Sign/exponent/significand of extended. */
#define EXTENDED_F(n) (1+EXTENDED_E(n)) /* 2nd word of extended significand. */
#define EXTENDED_G(n) (2+EXTENDED_E(n)) /* 3rd word of extended significand. */
#define EXTENDED_H(n) (3+EXTENDED_E(n)) /* 4th word of extended significand. */
typedef struct {
int sign;
enum fp_class_type fpclass;
int exponent; /* Unbiased exponent */
unsigned significand[4]; /* Four significand word . */
int rounded; /* rounded bit */
int sticky; /* stick bit */
} unpacked;
struct regs *fptraprp; /* Users regs for syncfpu() */
/* PRIVATE FUNCTIONS */
extern void _fp_read_pfreg(/* pf, n */);
/* FPU_REGS_TYPE *pf Where to put current %fn. */
/* unsigned n; Want to read register n. */
extern void _fp_write_pfreg(/* pf, n */);
/* FPU_REGS_TYPE *pf Where to get new %fn. */
/* unsigned n; Want to read register n. */
/* vfreg routines use "virtual" FPU registers at *_fp_current_pfregs. */
extern void _fp_read_vfreg(/* pf, n, pfpsd */);
/* FPU_REGS_TYPE *pf Where to put current %fn. */
/* unsigned n; Want to read register n. */
extern void _fp_write_vfreg(/* pf, n, pfpsd */);
/* FPU_REGS_TYPE *pf Where to get new %fn. */
/* unsigned n; Want to read register n. */
extern enum ftt_type _fp_iu_simulator(/* pinst, pregs, pwindow, pfpu */);
/* fp_inst_type pinst; FPU instruction to simulate. */
/* struct regs *pregs; Pointer to PCB image of registers. */
/* struct rwindow *pwindow; Pointer to locals and ins. */
/* struct fpu *pfpu; Pointer to FPU register block. */
extern void _fp_unpack(/* pu, n, type */);
/* unpacked *pu; unpacked result */
/* unsigned n; register where data starts */
/* fp_op_type type; type of datum */
extern void _fp_pack(/* pu, n, type */);
/* unpacked *pu; unpacked result */
/* unsigned n; register where data starts */
/* fp_op_type type; type of datum */
extern void _fp_unpack_word(/* pu, n */);
/* unsigned *pu; unpacked result */
/* unsigned n; register where data starts */
extern void _fp_pack_word(/* pu, n, type */);
/* unsigned *pu; unpacked result */
/* unsigned n; register where data starts */
extern void fpu_normalize(/* pu */);
/* unpacked *pu; unpacked operand and result */
extern void fpu_rightshift(/* pu, n */);
/* unpacked *pu; unsigned n; */
/* Right shift significand sticky by n bits. */
extern void fpu_set_exception(/* ex */);
/* enum fp_exception_type ex; exception to be set in curexcep */
extern void fpu_error_nan(/* pu */);
/* unpacked *pu; Set invalid exception and error nan in *pu */
extern void unpacksingle(/* pfpsd, pu, x */);
/* fp_simd_type *pfpsd; simulator data */
/* unpacked *pu; unpacked result */
/* single_type x; packed single */
extern void unpackdouble(/* pfpsd, pu, x, y */);
/* fp_simd_type *pfpsd; simulator data */
/* unpacked *pu; unpacked result */
/* double_type x; packed double */
/* unsigned y; */
extern enum fcc_type _fp_compare(/* px, py */);
extern void _fp_add(/* pfpsd, px, py, pz */);
extern void _fp_sub(/* pfpsd, px, py, pz */);
extern void _fp_mul(/* pfpsd, px, py, pz */);
extern void _fp_div(/* pfpsd, px, py, pz */);
extern void _fp_sqrt(/* pfpsd, px, pz */);
extern enum ftt_type _fp_write_word(/* caddr_t, value */);
extern enum ftt_type _fp_read_word(/* caddr_t, pvalue */);
extern enum ftt_type read_iureg(/* n, pregs, pwindow, pvalue */);

66
sys/sparc/fpu/ieee.h Normal file
View File

@@ -0,0 +1,66 @@
/* @(#)ieee.h 1.1 94/10/31 SMI */
/*
* Copyright (c) 1987 by Sun Microsystems, Inc.
*/
/* Sparc IEEE floating-point support PUBLIC include file. */
/* PUBLIC CONSTANTS */
/* PUBLIC TYPES */
/* IEEE Arithmetic types... numbered to correspond to fsr fields. */
enum fp_rounding_direction /* rounding direction */
{
fp_rd_nearest = 0,
fp_rd_zero = 1,
fp_rd_plus = 2,
fp_rd_minus = 3
} ;
enum fp_rounding_precision /* extended rounding precision */
{
fp_rp_extended = 0,
fp_rp_single = 1,
fp_rp_double = 2,
fp_rp_3 = 3
} ;
enum fp_exception_type /* exceptions according to cexc bit number */
{
fp_inexact = 0,
fp_divide = 1,
fp_underflow = 2,
fp_overflow = 3,
fp_invalid = 4
} ;
enum fp_class_type /* floating-point classes according to fclass */
{
fp_zero = 0,
fp_normal = 1, /* Includes subnormal. */
fp_infinity = 2,
fp_nan = 3, /* Includes quiet and signaling NaN. */
} ;
/* PUBLIC GLOBAL VARIABLES */
unsigned fp_accrued_exceptions ; /* Sticky accumulated exceptions. */
/* PUBLIC FUNCTIONS */
extern enum fp_rounding_direction swap_rounding_direction( /* rd */ ) ;
/*
extern enum fp_rounding_direction rd ;
/* Change rounding mode; return previous. */
extern int swap_accrued_exceptions ( /* x */ ) ;
/*
int x ;
/* Change accrued exceptions ; return previous. */

View File

@@ -0,0 +1,241 @@
#ifdef sccsid
static char sccsid[] = "@(#)iu_simulator.c 1.1 94/10/31 SMI";
#endif
/*
* Copyright (c) 1988 by Sun Microsystems, Inc.
*/
/* Integer Unit simulator for Sparc FPU simulator. */
#include <machine/fpu/fpu_simulator.h>
#include <machine/fpu/globals.h>
#define FPU_REG_FIELD unsigned_reg /* Coordinate with FPU_REGS_TYPE. */
#define FPU_FSR_FIELD unsigned_reg /* Coordinate with FPU_FSR_TYPE. */
PRIVATE enum ftt_type
fbcc(pinst, pregs, pfpu)
fp_inst_type pinst; /* FPU instruction to simulate. */
struct regs *pregs; /* Pointer to PCB image of registers. */
struct fpu *pfpu; /* Pointer to FPU register block. */
{
union {
fsr_type fsr;
long i;
} klugefsr;
union {
fp_inst_type fi;
int i;
} kluge;
enum fcc_type fcc;
enum icc_type {
fbn, fbne, fblg, fbul, fbl, fbug, fbg, fbu,
fba, fbe, fbue, fbge, fbuge, fble, fbule, fbo
} icc;
unsigned annul, takeit;
klugefsr.i = pfpu->fpu_fsr;
fcc = klugefsr.fsr.fcc;
icc = (enum icc_type) (pinst.rd & 0xf);
annul = pinst.rd & 0x10;
switch (icc) {
case fbn:
takeit = 0;
break;
case fbl:
takeit = fcc == fcc_less;
break;
case fbg:
takeit = fcc == fcc_greater;
break;
case fbu:
takeit = fcc == fcc_unordered;
break;
case fbe:
takeit = fcc == fcc_equal;
break;
case fblg:
takeit = (fcc == fcc_less) || (fcc == fcc_greater);
break;
case fbul:
takeit = (fcc == fcc_unordered) || (fcc == fcc_less);
break;
case fbug:
takeit = (fcc == fcc_unordered) || (fcc == fcc_greater);
break;
case fbue:
takeit = (fcc == fcc_unordered) || (fcc == fcc_equal);
break;
case fbge:
takeit = (fcc == fcc_greater) || (fcc == fcc_equal);
break;
case fble:
takeit = (fcc == fcc_less) || (fcc == fcc_equal);
break;
case fbne:
takeit = fcc != fcc_equal;
break;
case fbuge:
takeit = fcc != fcc_less;
break;
case fbule:
takeit = fcc != fcc_greater;
break;
case fbo:
takeit = fcc != fcc_unordered;
break;
case fba:
takeit = 1;
break;
}
if (takeit) { /* Branch taken. */
int tpc;
kluge.fi = pinst;
tpc = pregs->r_pc;
if (annul && (icc == fba)) { /* fba,a is wierd */
pregs->r_pc = tpc + (int) ((kluge.i << 10) >> 8);
pregs->r_npc = pregs->r_pc + 4;
} else {
pregs->r_pc = pregs->r_npc;
pregs->r_npc = tpc + (int) ((kluge.i << 10) >> 8);
}
} else { /* Branch not taken. */
if (annul) { /* Annul next instruction. */
pregs->r_pc = pregs->r_npc + 4;
pregs->r_npc += 8;
} else { /* Execute next instruction. */
pregs->r_pc = pregs->r_npc;
pregs->r_npc += 4;
}
}
return (ftt_none);
}
/*
* Simulator for loads and stores between floating-point unit and memory.
*/
PRIVATE enum ftt_type
fldst(pfpsd, pinst, pregs, pwindow, pfpu)
fp_simd_type *pfpsd; /* FPU simulator data. */
fp_inst_type pinst; /* FPU instruction to simulate. */
struct regs *pregs; /* Pointer to PCB image of registers. */
struct rwindow *pwindow; /* Pointer to locals and ins. */
struct fpu *pfpu; /* Pointer to FPU register block. */
{
unsigned nrs1, nrs2, nrd; /* Register number fields. */
freg_type f;
int ea;
int i;
union {
fp_inst_type fi;
int i;
} kluge;
enum ftt_type ftt;
nrs1 = pinst.rs1;
nrs2 = pinst.rs2;
nrd = pinst.rd;
if (pinst.ibit == 0) { /* effective address = rs1 + rs2 */
if (read_iureg(nrs1, pregs,
(struct rwindow *)pwindow, &ea, pfpsd))
return (ftt_fault);
if (read_iureg(nrs2, pregs,
(struct rwindow *)pwindow, &i, pfpsd))
return (ftt_fault);
ea += i;
} else { /* effective address = rs1 + imm13 */
kluge.fi = pinst;
ea = (kluge.i << 19) >> 19; /* Extract simm13 field. */
if (read_iureg(nrs1, pregs,
(struct rwindow *)pwindow, &i, pfpsd))
return (ftt_fault);
ea += i;
}
pfpsd->fp_trapaddr = (char *) ea; /* setup bad addr in case we trap */
switch (pinst.op3 & 7) {
case 0: /* LDF */
ftt = _fp_read_word((caddr_t) ea, &(f.int_reg), pfpsd);
if (ftt != ftt_none)
return (ftt);
pfpu->fpu_regs[nrd] = f.FPU_REG_FIELD;
break;
case 1: /* LDFSR */
ftt = _fp_read_word((caddr_t) ea, &(f.int_reg), pfpsd);
if (ftt != ftt_none)
return (ftt);
pfpu->fpu_fsr = f.FPU_FSR_FIELD;
break;
case 3: /* LDDF */
if ((ea & 0x7) != 0)
return (ftt_alignment); /* Require double-alignment. */
ftt = _fp_read_word((caddr_t) ea, &(f.int_reg), pfpsd);
if (ftt != ftt_none)
return (ftt);
pfpu->fpu_regs[DOUBLE_E(nrd)] = f.FPU_REG_FIELD;
ftt = _fp_read_word((caddr_t) (ea + 4), &(f.int_reg), pfpsd);
if (ftt != ftt_none)
return (ftt);
pfpu->fpu_regs[DOUBLE_F(nrd)] = f.FPU_REG_FIELD;
break;
case 4: /* STF */
f.FPU_REG_FIELD = pfpu->fpu_regs[nrd];
ftt = _fp_write_word((caddr_t) ea, f.int_reg, pfpsd);
if (ftt != ftt_none)
return (ftt);
break;
case 5: /* STFSR */
f.FPU_FSR_FIELD = pfpu->fpu_fsr;
f.FPU_FSR_FIELD &= ~0x301000; /* Clear reserved bits. */
f.FPU_FSR_FIELD |= 0x0E0000; /* Set version number=7 . */
ftt = _fp_write_word((caddr_t) ea, f.int_reg, pfpsd);
if (ftt != ftt_none)
return (ftt);
break;
case 7: /* STDF */
if ((ea & 0x7) != 0)
return (ftt_alignment); /* Require double-alignment. */
f.FPU_REG_FIELD = pfpu->fpu_regs[DOUBLE_E(nrd)];
ftt = _fp_write_word((caddr_t) ea, f.int_reg, pfpsd);
if (ftt != ftt_none)
return (ftt);
f.FPU_REG_FIELD = pfpu->fpu_regs[DOUBLE_F(nrd)];
ftt = _fp_write_word((caddr_t) (ea + 4), f.int_reg, pfpsd);
if (ftt != ftt_none)
return (ftt);
break;
default:
/* addr of unimp inst */
pfpsd->fp_trapaddr = (char *) pregs->r_pc;
return (ftt_unimplemented);
}
pregs->r_pc = pregs->r_npc; /* Do not retry emulated instruction. */
pregs->r_npc += 4;
return (ftt_none);
}
/* PUBLIC FUNCTIONS */
enum ftt_type
_fp_iu_simulator(pfpsd, pinst, pregs, pwindow, pfpu)
fp_simd_type *pfpsd; /* FPU simulator data. */
fp_inst_type pinst; /* FPU instruction to simulate. */
struct regs *pregs; /* Pointer to PCB image of registers. */
struct rwindow *pwindow; /* Pointer to locals and ins. */
struct fpu *pfpu; /* Pointer to FPU register block. */
{
switch (pinst.hibits) {
case 0:
return (fbcc(pinst, pregs, pfpu));
case 3:
return (fldst(pfpsd, pinst, pregs, pwindow, pfpu));
default:
return (ftt_unimplemented);
}
}

174
sys/sparc/fpu/mul.c Normal file
View File

@@ -0,0 +1,174 @@
#ifdef sccsid
static char sccsid[] = "@(#)mul.c 1.1 94/10/31 SMI";
#endif
/*
* Copyright (c) 1988 by Sun Microsystems, Inc.
*/
#include <machine/fpu/fpu_simulator.h>
#include <machine/fpu/globals.h>
#ifdef sun4m
extern int ross625_flag; /* to identify hyperSPARC */
#endif sun4m
void
_fp_mul(pfpsd, px, py, pz)
fp_simd_type *pfpsd;
unpacked *px, *py, *pz;
{
unpacked *pt;
unsigned acc[4]; /* Product accumulator. */
unsigned j, y, *x, s, r, c;
#ifdef sun4m
if (((int) px->fpclass < (int) py->fpclass) ||
(ross625_flag && ((int) px->fpclass <= (int) py->fpclass)))
#else sun4m
if ((int) px->fpclass < (int) py->fpclass)
#endif sun4m
{
pt = px;
px = py;
py = pt;
}
/* Now class(x) >= class(y). */
*pz = *px;
if ((int)pz->fpclass < (int)fp_quiet) pz->sign = px->sign ^ py->sign;
switch (px->fpclass) {
case fp_quiet:
case fp_signaling:
case fp_zero:
return;
case fp_infinity:
if (py->fpclass == fp_zero) {
fpu_error_nan(pfpsd, pz);
pz->fpclass = fp_quiet;
}
return;
case fp_normal:
if (py->fpclass == fp_zero) {
pz->fpclass = fp_zero;
return;
}
}
/* Now x and y are both normal or subnormal. */
x = px->significand; /* save typing */
s=r=acc[0]=acc[1]=acc[2]=acc[3]=0; /* intialize acc to zero */
y = py->significand[3]; /* py->significand[3] * x */
if (y!=0) {
j=1;
do {
s |= r; /* shift acc right one bit */
r = acc[3]&1;
acc[3] = ((acc[2]&1)<<31)|(acc[3]>>1);
acc[2] = ((acc[1]&1)<<31)|(acc[2]>>1);
acc[1] = ((acc[0]&1)<<31)|(acc[1]>>1);
acc[0] = (acc[0]>>1);
if (j&y) { /* bit i of y != 0, add x to acc */
c = 0;
c = fpu_add3wc(&acc[3], acc[3], x[3], c);
c = fpu_add3wc(&acc[2], acc[2], x[2], c);
c = fpu_add3wc(&acc[1], acc[1], x[1], c);
c = fpu_add3wc(&acc[0], acc[0], x[0], c);
}
j += j;
} while (j!=0);
}
y = py->significand[2]; /* py->significand[2] * x */
if (y!=0) {
j=1;
do {
s |= r; /* shift acc right one bit */
r = acc[3]&1;
acc[3] = ((acc[2]&1)<<31)|(acc[3]>>1);
acc[2] = ((acc[1]&1)<<31)|(acc[2]>>1);
acc[1] = ((acc[0]&1)<<31)|(acc[1]>>1);
acc[0] = (acc[0]>>1);
if (j&y) { /* bit i of y != 0, add x to acc */
c = 0;
c = fpu_add3wc(&acc[3], acc[3], x[3], c);
c = fpu_add3wc(&acc[2], acc[2], x[2], c);
c = fpu_add3wc(&acc[1], acc[1], x[1], c);
c = fpu_add3wc(&acc[0], acc[0], x[0], c);
}
j += j;
} while (j!=0);
} else {
s |= r|(acc[3]&0x7fffffff);
r = (acc[3]&0x80000000)>>31;
acc[3]=acc[2]; acc[2]=acc[1]; acc[1]=acc[0]; acc[0]=0;
}
y = py->significand[1]; /* py->significand[1] * x */
if (y!=0) {
j=1;
do {
s |= r; /* shift acc right one bit */
r = acc[3]&1;
acc[3] = ((acc[2]&1)<<31)|(acc[3]>>1);
acc[2] = ((acc[1]&1)<<31)|(acc[2]>>1);
acc[1] = ((acc[0]&1)<<31)|(acc[1]>>1);
acc[0] = (acc[0]>>1);
if (j&y) { /* bit i of y != 0, add x to acc */
c = 0;
c = fpu_add3wc(&acc[3], acc[3], x[3], c);
c = fpu_add3wc(&acc[2], acc[2], x[2], c);
c = fpu_add3wc(&acc[1], acc[1], x[1], c);
c = fpu_add3wc(&acc[0], acc[0], x[0], c);
}
j += j;
} while (j!=0);
} else {
s |= r|(acc[3]&0x7fffffff);
r = (acc[3]&0x80000000)>>31;
acc[3]=acc[2]; acc[2]=acc[1]; acc[1]=acc[0]; acc[0]=0;
}
/* py->significand[0] * x */
y = py->significand[0]; /* y is of form 0x0001???? */
j=1;
do {
s |= r; /* shift acc right one bit */
r = acc[3]&1;
acc[3] = ((acc[2]&1)<<31)|(acc[3]>>1);
acc[2] = ((acc[1]&1)<<31)|(acc[2]>>1);
acc[1] = ((acc[0]&1)<<31)|(acc[1]>>1);
acc[0] = (acc[0]>>1);
if (j&y) { /* bit i of y != 0, add x to acc */
c = 0;
c = fpu_add3wc(&acc[3], acc[3], x[3], c);
c = fpu_add3wc(&acc[2], acc[2], x[2], c);
c = fpu_add3wc(&acc[1], acc[1], x[1], c);
c = fpu_add3wc(&acc[0], acc[0], x[0], c);
}
j += j;
} while (j<=y);
if (acc[0]>=0x20000) { /* right shift one bit to normalize */
pz->exponent = px->exponent + py->exponent + 1;
pz->sticky = s|r;
pz->rounded = acc[3]&1;
pz->significand[3]=((acc[2]&1)<<31)|(acc[3]>>1);
pz->significand[2]=((acc[1]&1)<<31)|(acc[2]>>1);
pz->significand[1]=((acc[0]&1)<<31)|(acc[1]>>1);
pz->significand[0]=(acc[0]>>1);
} else {
pz->exponent = px->exponent + py->exponent;
pz->sticky = s;
pz->rounded = r;
pz->significand[3]=acc[3];
pz->significand[2]=acc[2];
pz->significand[1]=acc[1];
pz->significand[0]=acc[0];
}
}

479
sys/sparc/fpu/pack.c Normal file
View File

@@ -0,0 +1,479 @@
#ifdef sccsid
static char sccsid[] = "@(#)pack.c 1.1 94/10/31 SMI";
#endif
/*
* Copyright (c) 1988 by Sun Microsystems, Inc.
*/
/* Pack procedures for Sparc FPU simulator. */
#include <machine/fpu/fpu_simulator.h>
#include <machine/fpu/globals.h>
/*
* Returns 1 if overflow should go to infinity, 0 if to max finite.
*/
PRIVATE int
overflow_to_infinity(pfpsd, sign)
fp_simd_type *pfpsd; /* Pointer to simulator data */
int sign;
{
int inf;
switch (pfpsd->fp_direction) {
case fp_nearest:
inf = 1;
break;
case fp_tozero:
inf = 0;
break;
case fp_positive:
inf = !sign;
break;
case fp_negative:
inf = sign;
break;
}
return (inf);
}
/*
* Round according to current rounding mode.
*/
PRIVATE void
round(pfpsd, pu)
register fp_simd_type *pfpsd; /* Pointer to simulator data */
register unpacked *pu;
{
int increment; /* boolean to indicate round up */
int sr;
sr = pu->sticky|pu->rounded;
if (sr == 0)
return;
fpu_set_exception(pfpsd, fp_inexact);
switch (pfpsd->fp_direction) {
case fp_nearest:
increment = pu->rounded;
break;
case fp_tozero:
increment = 0;
break;
case fp_positive:
increment = (pu->sign == 0) & (sr != 0);
break;
case fp_negative:
increment = (pu->sign != 0) & (sr != 0);
break;
}
if (increment) {
pu->significand[3]++;
if (pu->significand[3] == 0) {
pu->significand[2]++;
if (pu->significand[2] == 0) {
pu->significand[1]++;
if (pu->significand[1] == 0) {
pu->significand[0]++; /* rounding carried out */
if (pu->significand[0] == 0x20000) {
pu->exponent++;
pu->significand[0] = 0x10000;
}
}
}
}
}
if ((pfpsd->fp_direction == fp_nearest) &&
(pu->sticky == 0) && increment!=0) { /* ambiguous case */
pu->significand[3] &= 0xfffffffe; /* force round to even */
}
}
PRIVATE void
packinteger(pfpsd, pu, px)
fp_simd_type *pfpsd; /* Pointer to simulator data */
unpacked *pu; /* unpacked result */
int *px; /* packed integer */
{
switch (pu->fpclass) {
case fp_zero:
*px = 0;
break;
case fp_normal:
if (pu->exponent >= 32)
goto overflow;
fpu_rightshift(pu, 112 - pu->exponent);
round(pfpsd, pu);
if (pu->significand[3] >= 0x80000000)
if ((pu->sign == 0)||(pu->significand[3] > 0x80000000))
goto overflow;
*px = pu->significand[3];
if (pu->sign)
*px = -*px;
break;
case fp_infinity:
case fp_quiet:
case fp_signaling:
overflow:
if (pu->sign)
*px = 0x80000000;
else
*px = 0x7fffffff;
pfpsd->fp_current_exceptions &= ~(1 << (int) fp_inexact);
fpu_set_exception(pfpsd, fp_invalid);
break;
}
}
PRIVATE void
packsingle(pfpsd, pu, px)
fp_simd_type *pfpsd; /* Pointer to simulator data */
unpacked *pu; /* unpacked result */
single_type *px; /* packed single */
{
px->sign = pu->sign;
switch (pu->fpclass) {
case fp_zero:
px->exponent = 0;
px->significand = 0;
break;
case fp_infinity:
infinity:
px->exponent = 0xff;
px->significand = 0;
break;
case fp_quiet:
case fp_signaling:
fpu_rightshift(pu, 113-24);
px->exponent = 0xff;
px->significand = 0x400000|(0x3fffff&pu->significand[3]);
break;
case fp_normal:
fpu_rightshift(pu, 113-24);
pu->exponent += SINGLE_BIAS;
if (pu->exponent <= 0) {
px->exponent = 0;
fpu_rightshift(pu, 1 - pu->exponent);
round(pfpsd, pu);
if (pu->significand[3] == 0x800000) { /*
* rounded
* back up to
* normal
*/
px->exponent = 1;
px->significand = 0;
fpu_set_exception(pfpsd, fp_inexact);
} else
px->significand = 0x7fffff & pu->significand[3];
if (pfpsd->fp_current_exceptions & (1 << fp_inexact))
fpu_set_exception(pfpsd, fp_underflow);
if (pfpsd->fp_fsrtem & (1<<fp_underflow)) {
fpu_set_exception(pfpsd, fp_underflow);
pfpsd->fp_current_exceptions &=
~(1 << (int) fp_inexact);
}
return;
}
round(pfpsd, pu);
if (pu->significand[3] == 0x1000000) { /* rounding overflow */
pu->significand[3] = 0x800000;
pu->exponent += 1;
}
if (pu->exponent >= 0xff) {
fpu_set_exception(pfpsd, fp_overflow);
fpu_set_exception(pfpsd, fp_inexact);
if (pfpsd->fp_fsrtem & (1<<fp_overflow)) {
pfpsd->fp_current_exceptions &=
~(1 << (int) fp_inexact);
}
if (overflow_to_infinity(pfpsd, pu->sign))
goto infinity;
px->exponent = 0xfe;
px->significand = 0x7fffff;
return;
}
px->exponent = pu->exponent;
px->significand = 0x7fffff & pu->significand[3];
}
}
PRIVATE void
packdouble(pfpsd, pu, px, py)
fp_simd_type *pfpsd; /* Pointer to simulator data */
unpacked *pu; /* unpacked result */
double_type *px; /* packed double */
unsigned *py;
{
px->sign = pu->sign;
switch (pu->fpclass) {
case fp_zero:
px->exponent = 0;
px->significand = 0;
*py = 0;
break;
case fp_infinity:
infinity:
px->exponent = 0x7ff;
px->significand = 0;
*py = 0;
break;
case fp_quiet:
case fp_signaling:
fpu_rightshift(pu, 113-53);
px->exponent = 0x7ff;
px->significand = 0x80000 | (0x7ffff & pu->significand[2]);
*py = pu->significand[3];
break;
case fp_normal:
fpu_rightshift(pu, 113-53);
pu->exponent += DOUBLE_BIAS;
if (pu->exponent <= 0) { /* underflow */
px->exponent = 0;
fpu_rightshift(pu, 1 - pu->exponent);
round(pfpsd, pu);
if (pu->significand[2] == 0x100000) { /*
* rounded
* back up to
* normal
*/
px->exponent = 1;
px->significand = 0;
*py = 0;
fpu_set_exception(pfpsd, fp_inexact);
} else {
px->exponent = 0;
px->significand = 0xfffff & pu->significand[2];
*py = pu->significand[3];
}
if (pfpsd->fp_current_exceptions & (1 << fp_inexact))
fpu_set_exception(pfpsd, fp_underflow);
if (pfpsd->fp_fsrtem & (1<<fp_underflow)) {
fpu_set_exception(pfpsd, fp_underflow);
pfpsd->fp_current_exceptions &=
~(1 << (int) fp_inexact);
}
return;
}
round(pfpsd, pu);
if (pu->significand[2] == 0x200000) { /* rounding overflow */
pu->significand[2] = 0x100000;
pu->exponent += 1;
}
if (pu->exponent >= 0x7ff) { /* overflow */
fpu_set_exception(pfpsd, fp_overflow);
fpu_set_exception(pfpsd, fp_inexact);
if (pfpsd->fp_fsrtem & (1<<fp_overflow)) {
pfpsd->fp_current_exceptions &=
~(1 << (int) fp_inexact);
}
if (overflow_to_infinity(pfpsd, pu->sign))
goto infinity;
px->exponent = 0x7fe;
px->significand = 0xfffff;
*py = 0xffffffff;
return;
}
px->exponent = pu->exponent;
px->significand = 0xfffff & pu->significand[2];
*py = pu->significand[3];
break;
}
}
PRIVATE void
packextended(pfpsd, pu, px, py, pz, pw)
fp_simd_type *pfpsd; /* Pointer to simulator data */
unpacked *pu; /* unpacked result */
extended_type *px; /* packed extended */
unsigned *py, *pz, *pw;
{
px->sign = pu->sign;
switch (pu->fpclass) {
case fp_zero:
px->exponent = 0;
px->significand = 0;
*pz = 0;
*py = 0;
*pw = 0;
break;
case fp_infinity:
infinity:
px->exponent = 0x7fff;
px->significand = 0;
*pz = 0;
*py = 0;
*pw = 0;
break;
case fp_quiet:
case fp_signaling:
px->exponent = 0x7fff;
px->significand = 0x8000 | pu->significand[0]; /*
* Insure quiet
* nan.
*/
*py = pu->significand[1];
*pz = pu->significand[2];
*pw = pu->significand[3];
break;
case fp_normal:
pu->exponent += EXTENDED_BIAS;
if (pu->exponent <= 0) { /* underflow */
fpu_rightshift(pu, 1-pu->exponent);
round(pfpsd, pu);
if (pu->significand[0] < 0x00010000) { /*
* not rounded
* back up
* to normal
*/
px->exponent = 0;
} else {
px->exponent = 1;
fpu_set_exception(pfpsd, fp_inexact);
}
if (pfpsd->fp_current_exceptions & (1 << fp_inexact))
fpu_set_exception(pfpsd, fp_underflow);
if (pfpsd->fp_fsrtem & (1<<fp_underflow)) {
fpu_set_exception(pfpsd, fp_underflow);
pfpsd->fp_current_exceptions &=
~(1 << (int) fp_inexact);
}
px->significand = pu->significand[0];
*py = pu->significand[1];
*pz = pu->significand[2];
*pw = pu->significand[3];
return;
}
round(pfpsd, pu); /* rounding overflow handled in round() */
if (pu->exponent >= 0x7fff) { /* overflow */
fpu_set_exception(pfpsd, fp_overflow);
fpu_set_exception(pfpsd, fp_inexact);
if (pfpsd->fp_fsrtem & (1<<fp_overflow)) {
pfpsd->fp_current_exceptions &=
~(1 << (int) fp_inexact);
}
if (overflow_to_infinity(pfpsd, pu->sign))
goto infinity;
px->exponent = 0x7ffe; /* overflow to max norm */
px->significand = 0xffff;
*py = 0xffffffff;
*pz = 0xffffffff;
*pw = 0xffffffff;
return;
}
px->exponent = pu->exponent;
px->significand = pu->significand[0];
*py = pu->significand[1];
*pz = pu->significand[2];
*pw = pu->significand[3];
break;
}
}
void
_fp_pack(pfpsd, pu, n, type)
fp_simd_type *pfpsd; /* Pointer to simulator data */
unpacked *pu; /* unpacked operand */
unsigned n; /* register where datum starts */
enum fp_op_type type; /* type of datum */
{
switch (type) {
case fp_op_integer:
{
int x;
packinteger(pfpsd, pu, &x);
if (!(pfpsd->fp_current_exceptions & pfpsd->fp_fsrtem))
pfpsd->fp_current_write_freg(&x, n, pfpsd);
break;
}
case fp_op_single:
{
single_type x;
packsingle(pfpsd, pu, &x);
if (!(pfpsd->fp_current_exceptions & pfpsd->fp_fsrtem))
pfpsd->fp_current_write_freg(&x, n, pfpsd);
break;
}
case fp_op_double:
{
double_type x;
unsigned y;
packdouble(pfpsd, pu, &x, &y);
if (!(pfpsd->fp_current_exceptions &
pfpsd->fp_fsrtem)) {
pfpsd->fp_current_write_freg(&x, DOUBLE_E(n),
pfpsd);
pfpsd->fp_current_write_freg(&y, DOUBLE_F(n),
pfpsd);
}
break;
}
case fp_op_extended:
{
extended_type x;
unsigned y, z, w;
unpacked U;
int k;
switch (pfpsd->fp_precision) { /*
* Implement extended
* rounding precision
* mode.
*/
case fp_single:
{
single_type tx;
packsingle(pfpsd, pu, &tx);
pu = &U;
unpacksingle(pfpsd, pu, tx);
break;
}
case fp_double:
{
double_type tx;
unsigned ty;
packdouble(pfpsd, pu, &tx, &ty);
pu = &U;
unpackdouble(pfpsd, pu, tx, ty);
break;
}
case fp_precision_3: /* rounded to 64 bits */
{
k = pu->exponent + EXTENDED_BIAS;
if (k>=0) k = 113-64;
else k = 113-64-k;
fpu_rightshift(pu, 113-64);
round(pfpsd, pu);
pu->sticky=pu->rounded=0;
pu->exponent += k;
fpu_normalize(pu);
break;
}
}
packextended(pfpsd, pu, &x, &y, &z, &w);
if (!(pfpsd->fp_current_exceptions &
pfpsd->fp_fsrtem)) {
pfpsd->fp_current_write_freg(&x, EXTENDED_E(n),
pfpsd);
pfpsd->fp_current_write_freg(&y, EXTENDED_F(n),
pfpsd);
pfpsd->fp_current_write_freg(&z, EXTENDED_G(n),
pfpsd);
pfpsd->fp_current_write_freg(&w, EXTENDED_H(n),
pfpsd);
}
break;
}
}
}
void
_fp_pack_word(pfpsd, pu, n)
fp_simd_type *pfpsd; /* Pointer to simulator data */
unsigned *pu; /* unpacked operand */
unsigned n; /* register where datum starts */
{
pfpsd->fp_current_write_freg(pu, n, pfpsd);
}

218
sys/sparc/fpu/unpack.c Normal file
View File

@@ -0,0 +1,218 @@
#ifdef sccsid
static char sccsid[] = "@(#)unpack.c 1.1 94/10/31 Copyr 1987 Sun Micro";
#endif
/*
* Copyright (c) 1988 by Sun Microsystems, Inc.
*/
/* Unpack procedures for Sparc FPU simulator. */
#include <machine/fpu/fpu_simulator.h>
#include <machine/fpu/globals.h>
PRIVATE void
unpackinteger(pu, x)
unpacked *pu; /* unpacked result */
int x; /* packed integer */
{
unsigned ux;
pu->sticky = pu->rounded = 0;
if (x == 0) {
pu->sign = 0;
pu->fpclass = fp_zero;
} else {
(*pu).sign = x < 0;
(*pu).fpclass = fp_normal;
(*pu).exponent = INTEGER_BIAS;
if (x<0) ux = -x; else ux = x;
(*pu).significand[0] = ux>>15;
(*pu).significand[1] = (ux&0x7fff)<<17;
(*pu).significand[2] = 0;
(*pu).significand[3] = 0;
fpu_normalize(pu);
}
}
void
unpacksingle(pfpsd, pu, x)
fp_simd_type *pfpsd; /* simulator data */
unpacked *pu; /* unpacked result */
single_type x; /* packed single */
{
unsigned U;
pu->sticky = pu->rounded = 0;
U = x.significand;
(*pu).sign = x.sign;
pu->significand[1] = 0;
pu->significand[2] = 0;
pu->significand[3] = 0;
if (x.exponent == 0) { /* zero or sub */
if (x.significand == 0) { /* zero */
pu->fpclass = fp_zero;
return;
} else { /* subnormal */
pu->fpclass = fp_normal;
pu->exponent = -SINGLE_BIAS-6;
pu->significand[0]=U;
fpu_normalize(pu);
return;
}
} else if (x.exponent == 0xff) { /* inf or nan */
if (x.significand == 0) { /* inf */
pu->fpclass = fp_infinity;
return;
} else { /* nan */
if ((U & 0x400000) != 0) { /* quiet */
pu->fpclass = fp_quiet;
} else { /* signaling */
pu->fpclass = fp_signaling;
fpu_set_exception(pfpsd, fp_invalid);
}
pu->significand[0] = 0x18000 | (U >> 7);
(*pu).significand[1]=((U&0x7f)<<25);
return;
}
}
(*pu).exponent = x.exponent - SINGLE_BIAS;
(*pu).fpclass = fp_normal;
(*pu).significand[0]=0x10000|(U>>7);
(*pu).significand[1]=((U&0x7f)<<25);
}
void
unpackdouble(pfpsd, pu, x, y)
fp_simd_type *pfpsd; /* simulator data */
unpacked *pu; /* unpacked result */
double_type x; /* packed double */
unsigned y;
{
unsigned U;
pu->sticky = pu->rounded = 0;
U = x.significand;
(*pu).sign = x.sign;
pu->significand[1] = y;
pu->significand[2] = 0;
pu->significand[3] = 0;
if (x.exponent == 0) { /* zero or sub */
if ((x.significand == 0) && (y == 0)) { /* zero */
pu->fpclass = fp_zero;
return;
} else { /* subnormal */
pu->fpclass = fp_normal;
pu->exponent = -DOUBLE_BIAS-3;
pu->significand[0] = U;
fpu_normalize(pu);
return;
}
} else if (x.exponent == 0x7ff) { /* inf or nan */
if ((U|y) == 0) { /* inf */
pu->fpclass = fp_infinity;
return;
} else { /* nan */
if ((U & 0x80000) != 0) { /* quiet */
pu->fpclass = fp_quiet;
} else { /* signaling */
pu->fpclass = fp_signaling;
fpu_set_exception(pfpsd, fp_invalid);
}
pu->significand[0] = 0x18000 | (U >> 4);
(*pu).significand[1]=((U&0xf)<<28)|(y>>4);
(*pu).significand[2]=((y&0xf)<<28);
return;
}
}
(*pu).exponent = x.exponent - DOUBLE_BIAS;
(*pu).fpclass = fp_normal;
(*pu).significand[0]=0x10000|(U>>4);
(*pu).significand[1]=((U&0xf)<<28)|(y>>4);
(*pu).significand[2]=((y&0xf)<<28);
}
PRIVATE void
unpackextended(pfpsd, pu, x, y, z, w)
fp_simd_type *pfpsd; /* simulator data */
unpacked *pu; /* unpacked result */
extended_type x; /* packed extended */
unsigned y, z, w;
{
unsigned U;
pu->sticky = pu->rounded = 0;
U = x.significand;
(*pu).sign = x.sign;
(*pu).fpclass = fp_normal;
(*pu).exponent = x.exponent - EXTENDED_BIAS;
(*pu).significand[0] = (x.exponent==0)? U:0x10000|U;
(*pu).significand[1] = y;
(*pu).significand[2] = z;
(*pu).significand[3] = w;
if (x.exponent < 0x7fff) { /* zero, normal, or subnormal */
if ((z|y|w|pu->significand[0]) == 0) { /* zero */
pu->fpclass = fp_zero;
return;
} else { /* normal or subnormal */
if (x.exponent==0) {
fpu_normalize(pu);
pu->exponent += 1;
}
return;
}
} else { /* inf or nan */
if ((U|z|y|w) == 0) { /* inf */
pu->fpclass = fp_infinity;
return;
} else { /* nan */
if ((U & 0x00008000) != 0) { /* quiet */
pu->fpclass = fp_quiet;
} else { /* signaling */
pu->fpclass = fp_signaling;
fpu_set_exception(pfpsd, fp_invalid);
}
pu->significand[0] |= 0x8000; /* make quiet */
return;
}
}
}
void
_fp_unpack(pfpsd, pu, n, dtype)
fp_simd_type *pfpsd; /* simulator data */
unpacked *pu; /* unpacked result */
unsigned n; /* register where data starts */
enum fp_op_type dtype; /* type of datum */
{
freg_type f, fy, fz, fw;
switch ((int) dtype) {
case fp_op_integer:
pfpsd->fp_current_read_freg(&f, n, pfpsd);
unpackinteger(pu, f.int_reg);
break;
case fp_op_single:
pfpsd->fp_current_read_freg(&f, n, pfpsd);
unpacksingle(pfpsd, pu, f.single_reg);
break;
case fp_op_double:
pfpsd->fp_current_read_freg(&f, DOUBLE_E(n), pfpsd);
pfpsd->fp_current_read_freg(&fy, DOUBLE_F(n), pfpsd);
unpackdouble(pfpsd, pu, f.double_reg, fy.unsigned_reg);
break;
case fp_op_extended:
pfpsd->fp_current_read_freg(&f, EXTENDED_E(n), pfpsd);
pfpsd->fp_current_read_freg(&fy, EXTENDED_F(n), pfpsd);
pfpsd->fp_current_read_freg(&fz, EXTENDED_G(n), pfpsd);
pfpsd->fp_current_read_freg(&fw, EXTENDED_H(n), pfpsd);
unpackextended(pfpsd, pu, f.extended_reg, fy.unsigned_reg,
fz.unsigned_reg, fw.unsigned_reg);
break;
}
}
void
_fp_unpack_word(pfpsd, pu, n)
fp_simd_type *pfpsd; /* simulator data */
unsigned *pu; /* unpacked result */
unsigned n; /* register where data starts */
{
pfpsd->fp_current_read_freg(pu, n, pfpsd);
}

294
sys/sparc/fpu/utility.c Normal file
View File

@@ -0,0 +1,294 @@
#ifdef sccsid
static char sccsid[] = "@(#)utility.c 1.1 94/10/31 SMI";
#endif
/*
* Copyright (c) 1988 by Sun Microsystems, Inc.
*/
/* Utility functions for Sparc FPU simulator. */
#include <machine/fpu/fpu_simulator.h>
#include <machine/fpu/globals.h>
void
_fp_read_vfreg(pf, n, pfpsd)
FPU_REGS_TYPE *pf; /* Old freg value. */
unsigned n; /* Want to read register n. */
fp_simd_type *pfpsd;
{
*pf = pfpsd->fp_current_pfregs->fpu_regs[n];
}
void
_fp_write_vfreg(pf, n, pfpsd)
FPU_REGS_TYPE *pf; /* New freg value. */
unsigned n; /* Want to read register n. */
fp_simd_type *pfpsd;
{
pfpsd->fp_current_pfregs->fpu_regs[n] = *pf;
}
/*
* Normalize a number. Does not affect zeros, infs, or NaNs.
* The number will be normalized to 113 bit extended:
* 0x0001####, 0x########, 0x########, 0x########.
*/
void
fpu_normalize(pu)
unpacked *pu;
{
unsigned U, u0, u1, u2, u3, m, n, k;
u0 = pu->significand[0];
u1 = pu->significand[1];
u2 = pu->significand[2];
u3 = pu->significand[3];
if ((*pu).fpclass == fp_normal) {
if ((u0|u1|u2|u3)==0) {
(*pu).fpclass = fp_zero;
return;
}
while (u0 == 0) {
u0 = u1; u1=u2; u2=u3; u3=0;
(*pu).exponent = (*pu).exponent - 32;
}
if (u0 >= 0x20000) { /* u3 should be zero */
n=1; U = u0 >> 1;
while (U >= 0x20000) {
U >>= 1;
n += 1;
}
m = (1 << n)-1;
k = 32-n;
(*pu).exponent += n;
u3 = ((u2&m)<<k)|(u3>>n);
u2 = ((u1&m)<<k)|(u2>>n);
u1 = ((u0&m)<<k)|(u1>>n);
u0 = U;
} else if (u0 < 0x10000) {
n=1; U = u0<<1;
while (U < 0x10000) {
U <<= 1;
n += 1;
}
k = 32-n;
m = -(1 << k);
(*pu).exponent -= n;
u0 = (u0<<n)|((u1&m)>>k);
u1 = (u1<<n)|((u2&m)>>k);
u2 = (u2<<n)|((u3&m)>>k);
u3 = (u3<<n);
}
pu->significand[0] = u0;
pu->significand[1] = u1;
pu->significand[2] = u2;
pu->significand[3] = u3;
}
}
/*
* Right shift significand sticky by n bits.
*/
void
fpu_rightshift(pu, n)
unpacked *pu;
int n;
{
unsigned m, k, j, u0, u1, u2, u3;
if (n > 113) { /* drastic */
if (((*pu).significand[0] | (*pu).significand[1]
| (*pu).significand[2] | (*pu).significand[3]) == 0){
/* really zero */
pu->fpclass = fp_zero;
return;
} else {
pu->rounded = 0;
pu->sticky = 1;
pu->significand[3] = 0;
pu->significand[2] = 0;
pu->significand[1] = 0;
pu->significand[0] = 0;
return;
}
}
while (n >= 32) { /* big shift */
pu->sticky |= pu->rounded | (pu->significand[3]&0x7fffffff);
pu->rounded = (*pu).significand[3] >> 31;
(*pu).significand[3] = (*pu).significand[2];
(*pu).significand[2] = (*pu).significand[1];
(*pu).significand[1] = (*pu).significand[0];
(*pu).significand[0] = 0;
n -= 32;
}
if (n > 0) { /* small shift */
u0 = pu->significand[0];
u1 = pu->significand[1];
u2 = pu->significand[2];
u3 = pu->significand[3];
m = (1<<n)-1;
k = 32 - n;
j = (1<<(n-1))-1;
pu->sticky |= pu->rounded | (u3&j);
pu->rounded = (u3&m)>>(n-1);
pu->significand[3] = ((u2&m)<<k)|(u3>>n);
pu->significand[2] = ((u1&m)<<k)|(u2>>n);
pu->significand[1] = ((u0&m)<<k)|(u1>>n);
pu->significand[0] = u0>>n;
}
}
/*
* Set the exception bit in the current exception register.
*/
void
fpu_set_exception(pfpsd, ex)
fp_simd_type *pfpsd; /* Pointer to simulator data */
enum fp_exception_type ex;
{
pfpsd->fp_current_exceptions |= 1 << (int) ex;
}
/*
* Set invalid exception and error nan in *pu
*/
void
fpu_error_nan(pfpsd, pu)
fp_simd_type *pfpsd; /* Pointer to simulator data */
unpacked *pu;
{
fpu_set_exception(pfpsd, fp_invalid);
pu->sign = 0;
pu->significand[0] = 0x7fffffff;
pu->significand[1] = 0xffffffff;
pu->significand[2] = 0xffffffff;
pu->significand[3] = 0xffffffff;
}
/*
* the following fpu_add3wc should be inlined as
* .inline _fpu_add3wc, 3
* ld [%o1], %o4 ! sum = x
* addcc -1, %o3, %g0 ! restore last carry in cc reg
* addxcc %o4, %o2, %o4 ! sum = sum + y + last carry
* st %o4, [%o0] ! *z = sum
* addx %g0, %g0, %o0 ! return new carry
* .end
*/
#ifndef lint
unsigned
#endif !lint
fpu_add3wc(z, x, y, carry)
unsigned *z, x, y, carry;
{ /* *z = x + y + carry, set carry; */
if (carry==0) {
*z = x+y;
return (*z < y);
} else {
*z = x+y+1;
return (*z <= y);
}
}
/* the following fpu_sub3wc should be inlined as
* .inline _fpu_sub3wc, 3
* ld [%o1], %o4 ! sum = *x
* addcc -1, %o3, %g0 ! restore last carry in cc reg
* subxcc %o4, %o2, %o4 ! sum = sum - y - last carry
* st %o4, [%o0] ! *x = sum
* addx %g0, %g0, %o0 ! return new carry
* .end
*/
#ifndef lint
unsigned
#endif !lint
fpu_sub3wc(z, x, y, carry)
unsigned *z, x, y, carry;
{ /* *z = x - y - carry, set carry; */
if (carry==0) {
*z = x-y;
return (*z > x);
} else {
*z = x-y-1;
return (*z >= x);
}
}
/*
* the following fpu_neg2wc should be inlined as
* .inline _fpu_neg2wc, 2
* ld [%o1], %o3 ! tmp = *x
* addcc -1, %o2, %g0 ! restore last carry in cc reg
* subxcc %g0, %o3, %o3 ! sum = 0 - tmp - last carry
* st %o3, [%o0] ! *x = sum
* addx %g0, %g0, %o0 ! return new carry
* .end
*/
#ifndef lint
unsigned
#endif !lint
fpu_neg2wc(z, x, carry)
unsigned *z, x, carry;
{ /* *x = 0 - *x - carry, set carry; */
if (carry==0) {
*z = -x;
return ((*z) != 0);
} else {
*z = -x-1;
return (1);
}
}
int
fpu_cmpli(x, y, n)
unsigned x[], y[]; int n;
{ /* compare two unsigned array */
int i;
i=0;
while (i < n) {
if (x[i] > y[i]) return 1;
else if (x[i] < y[i]) return -1;
i++;
}
return (0);
}
#ifdef DEBUG
/*
* Print out unpacked record.
*/
void
display_unpacked(pu)
unpacked *pu;
{
(void) printf(" unpacked ");
if (pu->sign)
(void) printf("-");
else
(void) printf("+");
switch (pu->fpclass) {
case fp_zero:
(void) printf("0 ");
break;
case fp_normal:
(void) printf("normal");
break;
case fp_infinity:
(void) printf("Inf ");
break;
case fp_quiet:
case fp_signaling:
(void) printf("nan ");
break;
}
(void) printf(" %X %X %X %X (%X, %X) exponent %X \n",
pu->significand[0], pu->significand[1], pu->significand[2],
pu->significand[3], (pu->rounded!=0),
(pu->sticky!=0), pu->exponent);
}
#endif

100
sys/sparc/fpu/uword.c Normal file
View File

@@ -0,0 +1,100 @@
#ifdef sccsid
static char sccsid[] = "@(#)uword.c 1.1 94/10/31 Copyr 1987 Sun Micro";
#endif
/*
* Copyright (c) 1987 by Sun Microsystems, Inc.
*/
/* Read/write user memory procedures for Sparc FPU simulator. */
#include <machine/param.h>
#include <machine/fpu/fpu_simulator.h>
#include <machine/fpu/globals.h>
#include <vm/seg.h>
extern int fuword();
extern int suword();
extern int fubyte();
enum ftt_type
_fp_read_word(address, pvalue, pfpsd)
caddr_t address;
int *pvalue;
fp_simd_type *pfpsd;
{
int w;
int b;
if (((int) address & 0x3) != 0)
return (ftt_alignment); /* Must be word-aligned. */
w = fuword(address);
if (w == -1) {
b = fubyte(address);
if (b == -1) {
pfpsd->fp_trapaddr = address;
pfpsd->fp_traprw = S_READ;
return (ftt_fault);
}
}
*pvalue = w;
return (ftt_none);
}
enum ftt_type
_fp_write_word(address, value, pfpsd)
caddr_t address;
int value;
fp_simd_type *pfpsd;
{
int w;
if (((int) address & 0x3) != 0)
return (ftt_alignment); /* Must be word-aligned. */
w = suword(address, value);
if (w == -1) {
pfpsd->fp_trapaddr = address;
pfpsd->fp_traprw = S_WRITE;
return (ftt_fault);
} else {
return (ftt_none);
}
}
enum ftt_type
read_iureg(n, pregs, pwindow, pvalue, pfpsd)
unsigned n;
struct regs *pregs; /* Pointer to PCB image of registers. */
struct rwindow *pwindow;/* Pointer to locals and ins. */
int *pvalue;
fp_simd_type *pfpsd;
{ /* Reads integer unit's register n. */
register int *pint;
if (n <= 15) {
if (n == 0) {
*pvalue = 0;
return (ftt_none); /* Read global register 0. */
}
if (n <= 7) { /* globals */
pint = &(pregs->r_g1) + (n - 1);
*pvalue = *pint;
return (ftt_none);
} else { /* outs */
pint = &(pregs->r_o0) + (n - 8);
*pvalue = *pint;
return (ftt_none);
}
} else { /* locals and ins */
if (n <= 23)
pint = &pwindow->rw_local[n - 16];
else
pint = &pwindow->rw_in[n - 24];
if ((int) pint > KERNELBASE){
*pvalue = *pint;
return (ftt_none);
}
return _fp_read_word((char *) pint, pvalue, pfpsd);
}
}

24
sys/sparc/frame.h Normal file
View File

@@ -0,0 +1,24 @@
/* @(#)frame.h 1.1 94/10/31 SMI */
/*
* Copyright (c) 1987 by Sun Microsystems, Inc.
*/
/*
* Definition of the sparc stack frame (when it is pushed on the stack).
*/
#ifndef _sparc_frame_h
#define _sparc_frame_h
struct frame {
int fr_local[8]; /* saved locals */
int fr_arg[6]; /* saved arguments [0 - 5] */
struct frame *fr_savfp; /* saved frame pointer */
int fr_savpc; /* saved program counter */
char *fr_stret; /* struct return addr */
int fr_argd[6]; /* arg dump area */
int fr_argx[1]; /* array of args past the sixth */
};
#endif /*!_sparc_frame_h*/

231
sys/sparc/mcount.s Normal file
View File

@@ -0,0 +1,231 @@
.seg "data"
.asciz "@(#)mcount.s 1.1 94/10/31 Copyr 1987 Sun Micro"
.align 4
! Copyright (c) 1986 by Sun Microsystems, Inc.
#include <machine/asm_linkage.h>
/* don't mess with FROMPC and SELFPC, or you won't be able to return */
#define FROMPC %i7
#define SELFPC %o7
/*
* register usage: we're in the same window as the calling routine, so we
* only use the first 6 output registers, and there's lots of sharing
*/
#define PROFIL %o5
#define FROM_P %o4
#define TOS_P %o3
#define SCRATCH3 TOS_P
#define TOINDEX %o2
#define PREVTOP TOINDEX
#define SCRATCH2 TOINDEX
#define TOP %o1
#define SCRATCH1 TOP
#define PROF_P TOP
#define SCRATCH0 %o0
/* some offsets */
#define T_SELF 0
#define T_CNT 4
#define T_LINK 8
.seg "text"
.proc 0
.global mcount
mcount:
#ifdef MULTIPROCESSOR
! only do mcount work if inside the kernel lock.
mov %tbr, %o0
sethi %hi(_klock), %o1
ld [%o1+%lo(_klock)], %o1
srl %o0, 20, %o0
and %o0, 3, %o0
set 0xFF000008, %o2
or %o0, %o2, %o0
cmp %o0, %o1
bne out
nop
#endif MULTIPROCESSOR
! Make sure that we're profiling, and that we aren't recursively called
sethi %hi(_tolimit), SCRATCH0 !Make sure we haven't run out
ld [SCRATCH0+%lo(_tolimit)], SCRATCH0
sethi %hi(_profiling), PROF_P !moved up to avoid lockout
tst SCRATCH0
be out
sethi %hi(_s_lowpc), SCRATCH3 !moved up to avoid lockout
ld [PROF_P+%lo(_profiling)], PROFIL
sethi %hi(_s_textsize), SCRATCH0 !moved up to avoid lockout
tst PROFIL
bne out
add PROFIL, 1, SCRATCH2 ! increment profiling
st SCRATCH2, [PROF_P+%lo(_profiling)]
! Make sure (frompc-lowpc) > textsize
ld [SCRATCH3+%lo(_s_lowpc)], SCRATCH3
ld [SCRATCH0+%lo(_s_textsize)], SCRATCH0
sub FROMPC, SCRATCH3, FROM_P
cmp FROM_P, SCRATCH0
bgu,a out
st PROFIL, [PROF_P+%lo(_profiling)] !delay slot; clear profiling
! from_p = &froms[(frompc-s_lowpc)/(HASHFRACTION*sizeof(short))]
sethi %hi(_froms), SCRATCH0
ld [SCRATCH0+%lo(_froms)], SCRATCH0
sra FROM_P, 2, FROM_P !divide by 4 then multiply by
sll FROM_P, 1, FROM_P !two; MUST be even
add SCRATCH0, FROM_P, FROM_P
lduh [FROM_P], TOINDEX ! toindex = *from_p
sethi %hi(_tos), TOS_P !for later; locked out anyway
tst TOINDEX
bne,a oldarc
sll TOINDEX, 2, TOINDEX ! delay slot
! This is the first time we've seen a call from here; get new tostruct
ld [TOS_P+%lo(_tos)], TOS_P ! &tos[0]
sethi %hi(_tolimit), SCRATCH1
lduh [TOS_P+T_LINK], TOINDEX !tos[0].link(last used struct)
ld [SCRATCH1+%lo(_tolimit)], SCRATCH0
inc TOINDEX !increment to free struct
sth TOINDEX, [TOS_P+T_LINK]
cmp TOINDEX, SCRATCH0 !too many?
bge,a overflow
st %g0, [SCRATCH1+%lo(_tolimit)] !delay slot;
! Initialize arc
sth TOINDEX, [FROM_P] !*from_p = toindex
sll TOINDEX, 2, TOINDEX
sll TOINDEX, 1, SCRATCH0
add TOINDEX, SCRATCH0, TOINDEX !toindex*sizeof(tostruct))
add TOS_P, TOINDEX, TOP !top = &tos[toindex]
st SELFPC, [TOP+T_SELF] !top->selfpc = selfpc (%i7)
mov 1, SCRATCH0
st SCRATCH0, [TOP+T_CNT] !top->count = 1
b done
sth %g0, [TOP+T_LINK] !delay slot; top->link = 0
! Not a new frompc
oldarc:
sll TOINDEX, 1, SCRATCH0
ld [TOS_P+%lo(_tos)], TOS_P
add TOINDEX, SCRATCH0, TOINDEX !(toindex*sizeof(tostruct))
add TOS_P, TOINDEX, TOP !top = &tos[toindex]
ld [TOP+T_SELF], SCRATCH0 !top->selfpc
ld [TOP+T_CNT], SCRATCH2 !for later; don't want to be locked out
cmp SCRATCH0, SELFPC !is this the right arc?
bne,a chainloop
lduh [TOP+T_LINK], SCRATCH0 !delay slot; top->link
! Our arc was at the head of the chain. This is the most common case.
inc SCRATCH2
b done
st SCRATCH2, [TOP+T_CNT] !delay slot; increment count
! We have to wander down the linked list, looking for our arc. We only get
! here for calls like "*foo()"
chainloop:
tst SCRATCH0 !top->link = 0?
bne,a next
mov TOP, PREVTOP !delay slot; prevtop = top
! We're at the end of the chain and didn't find top->selfpc == selfpc
! Allocate a new tostruct
lduh [TOS_P+T_LINK], TOINDEX !tos[0].link (last used struct)
sethi %hi(_tolimit), SCRATCH1
inc TOINDEX !increment to free struct
ld [SCRATCH1+%lo(_tolimit)], SCRATCH0
sth TOINDEX, [TOS_P+T_LINK]
cmp TOINDEX, SCRATCH0 !check for overflow
bge,a overflow
st %g0, [SCRATCH1+%lo(_tolimit)] !delay slot;
! Now initialize it and put it at the head of the chain
sll TOINDEX, 2, SCRATCH0
sll SCRATCH0, 1, TOP
add SCRATCH0, TOP, TOP !(toindex*sizeof(tostruct))
add TOS_P, TOP, TOP !top=&tos[toindex]
st SELFPC, [TOP+T_SELF] !top->selfpc = selfpc
mov 1, SCRATCH0
st SCRATCH0, [TOP+T_CNT] !top->count = 1
lduh [FROM_P], SCRATCH0
sth TOINDEX, [FROM_P] !*from_p = toindex
b done
sth SCRATCH0, [TOP+T_LINK] !top->link = *from_p
! Check the next arc on the chain
next:
sll SCRATCH0, 2, TOP
sll TOP, 1, SCRATCH0
add TOP, SCRATCH0, TOP
add TOS_P, TOP, TOP !top = &tos[top->link]
ld [TOP+T_SELF], SCRATCH0
cmp SCRATCH0, SELFPC !is top->selfpc = selfpc?
bne,a chainloop
lduh [TOP+T_LINK], SCRATCH0 !delay slot; top->link
! We're home. Increment the count and move it to the head of the chain
ld [TOP+T_CNT], SCRATCH3
lduh [TOP+T_LINK], SCRATCH0
inc SCRATCH3
st SCRATCH3, [TOP+T_CNT] !top->count++
lduh [PREVTOP+T_LINK], SCRATCH3 !temp1 = prevtop->link
sth SCRATCH0, [PREVTOP+T_LINK] !prevtop->link = top->link
lduh [FROM_P], SCRATCH0 !temp2 = *from_p
sth SCRATCH3, [FROM_P] !*from_p = temp1
sth SCRATCH0, [TOP+T_LINK] !top->link = temp2
done:
sethi %hi(_profiling),PROF_P
retl
st PROFIL, [PROF_P+%lo(_profiling)] ! clear profiling
out:
retl
nop
overflow:
save %sp, -SA(MINFRAME), %sp
sethi %hi(OVERMSG), %o0
call _printf, 1 !printf(OVERMSG)
or %o0, %lo(OVERMSG), %o0
ret
restore
.seg "data1"
OVERMSG:
.ascii "mcount: tos overflow\n\0"

88
sys/sparc/ocsum.s Normal file
View File

@@ -0,0 +1,88 @@
! @(#)ocsum.s 1.1 94/10/31 SMI
! Copyright (c) 1989 by Sun Microsystems, Inc.
.seg "text"
.align 4
#include <machine/asm_linkage.h>
/*
* ocsum(address, halfword_count)
* Do a 16 bit one's complement sum of a given number of (16-bit)
* halfwords. The halfword pointer must not be odd.
* %o0 address; %o1 count; %o3 accumulator; %o4 temp
* %g2 and %g3 used in main loop
*/
ENTRY(ocsum)
clr %o3 ! clear accumulator
cmp %o1, 31 ! less than 32 bytes?
bl,a dohw ! just do halfwords
tst %o1 ! delay slot, test count
btst 31, %o0 ! (delay slot)
bz 2f ! if 32 byte aligned, skip
nop
!
! Do first halfwords until 32-byte aligned
!
1:
lduh [%o0], %g2 ! read data
add %o0, 2, %o0 ! increment address
add %o3, %g2, %o3 ! add to accumulator, don't need carry yet
btst 31, %o0 ! 32 byte aligned?
bnz 1b
sub %o1, 1, %o1 ! decrement count
!
! loop to add in 32 byte chunks
! The loads and adds are staggered to help avoid load/use
! interlocks on highly pipelined implementations, and double
! loads are used for 64-bit wide memory systems.
!
2:
sub %o1, 16, %o1 ! decrement count to aid testing
4:
ldd [%o0], %g2 ! read data
ldd [%o0+8], %o4 ! read more data
addcc %o3, %g2, %o3 ! add to accumulator
addxcc %o3, %g3, %o3 ! add to accumulator with carry
ldd [%o0+16], %g2 ! read more data
addxcc %o3, %o4, %o3 ! add to accumulator with carry
addxcc %o3, %o5, %o3 ! add to accumulator with carry
ldd [%o0+24], %o4 ! read more data
addxcc %o3, %g2, %o3 ! add to accumulator with carry
addxcc %o3, %g3, %o3 ! add to accumulator with carry
addxcc %o3, %o4, %o3 ! add to accumulator
addxcc %o3, %o5, %o3 ! add to accumulator with carry
addxcc %o3, 0, %o3 ! if final carry, add it in
subcc %o1, 16, %o1 ! decrement count (in halfwords)
bge 4b
add %o0, 32, %o0 ! delay slot, increment address
add %o1, 16, %o1 ! add back in
!
! Do any remaining halfwords
!
b dohw
tst %o1 ! delay slot, for more to do
3:
add %o0, 2, %o0 ! increment address
addcc %o3, %g2, %o3 ! add to accumulator
addxcc %o3, 0, %o3 ! if carry, add it in
subcc %o1, 1, %o1 ! decrement count
dohw:
bg,a 3b ! more to do?
lduh [%o0], %g2 ! read data
!
! at this point the 32-bit accumulator
! has the result that needs to be returned in 16-bits
!
sll %o3, 16, %o4 ! put low halfword in high halfword %o4
addcc %o4, %o3, %o3 ! add the 2 halfwords in high %o3, set carry
srl %o3, 16, %o3 ! shift to low halfword
retl ! return
addxcc %o3, 0, %o0 ! add in carry if any. result in %o0

347
sys/sparc/overflow.s Normal file
View File

@@ -0,0 +1,347 @@
/* @(#)overflow.s 1.1 94/10/31 SMI */
/*
* Copyright (c) 1989 by Sun Microsystems, Inc.
*/
#include <machine/asm_linkage.h>
#include <machine/param.h>
#include <machine/psl.h>
#include <machine/trap.h>
#include <machine/mmu.h>
#include <machine/pte.h>
#include <machine/reg.h>
#include <machine/buserr.h>
#include <machine/pcb.h>
#include "assym.s"
.seg "text"
.align 4
PROT = (PG_V | PG_W) >> PG_S_BIT
P_INVAL = (PG_V) >> PG_S_BIT
/*
* window overflow trap handler
*/
.global window_overflow
window_overflow:
#ifdef PERFMETER
sethi %hi(_overflowcnt), %l5
ld [%l5 + %lo(_overflowcnt)], %l7
inc %l7
st %l7, [%l5 + %lo(_overflowcnt)]
#endif PERFMETER
! wim stored into %l3 by trap vector
mov %g1, %l7 ! save %g1
mov %g2, %l5 ! save %g2
srl %l3, 1, %g1 ! next WIM = %g1 = ror(WIM, 1, NW)
sll %l3, %l6, %l4 ! trap vector set %l6 = NW - 1
btst PSR_PS, %l0 ! test for user or sup trap
bz wo_user
or %l4, %g1, %g1 ! delay slot
!
! Overflow from supervisor mode. Determine whether the window
! to be saved is a user window.
! U.u_pcb.pcb_uwm has a bit set for each user window which is still
! in the register file. If u.u_pcb.pcb_uwm has any bits on, then it
! is a user window that must be saved.
!
sethi %hi(_uunix), %g2 ! XXX - global u register?
ld [%g2 + %lo(_uunix)], %g2
ld [%g2+PCB_UWM], %l4 ! if (u.u_pcb.pcb_uwm != 0) sup window
tst %l4
bne,a wo_user_window
bclr %g1, %l4 ! delay slot, UWM &= ~(new WIM)
!
! Window to be saved is a supervisor window.
! Put it on the stack.
!
save ! get into window to be saved
mov %g1, %wim ! install new wim
wo_stack_res:
SAVE_WINDOW(%sp)
restore ! go back to trap window
mov %l5, %g2 ! restore g2
mov %l0, %psr ! reinstall system PSR_CC
mov %l7, %g1 ! restore g1
jmp %l1 ! reexecute save
rett %l2
wo_user_window:
!
! Window to be saved is a user window.
!
sethi %hi(_uunix), %g2
ld [%g2 + %lo(_uunix)], %g2
st %l4, [%g2+PCB_UWM] ! update u.u_uwm
wo_user:
!
! The window to be saved is a user window.
! We must check whether the user stack is resident where the window
! will be saved, which is pointed to by the window's sp.
! We must also check that the sp is aligned to a word boundary.
!
save ! get into window to be saved
mov %g1, %wim ! install new wim
!
! Normally, we would check the alignment, and then probe the top
! and bottom of the save area on the stack. However we optimize
! this by checking that both ends of the save area are within a
! 4k unit (the biggest mask we can generate in one cycle), and
! the alignment in one shot. This allows us to do one probe to
! the page map. NOTE: this assumes a page size of at least 4k.
!
and %sp, 0xfff, %g1
#ifdef VA_HOLE
! check if the sp points into the hole in the address space
sethi %hi(_hole_shift), %g2 ! hole shift address
ld [%g2 + %lo(_hole_shift)], %g2
add %g1, (14*4), %g1 ! interlock, bottom of save area
sra %sp, %g2, %g2
inc %g2
andncc %g2, 1, %g2
bz 1f
andncc %g1, 0xff8, %g0
b,a wo_stack_not_res ! stack page is in the hole
1:
#else
add %g1, (14*4), %g1
andncc %g1, 0xff8, %g0
#endif VA_HOLE
bz,a wo_sp_bot
lda [%sp]ASI_PM, %g1 ! check for stack page resident
!
! Stack is either misaligned or crosses a 4k boundary.
!
btst 0x7, %sp ! test sp alignment
bz wo_sp_top
add %sp, (14*4), %g1 ! delay slot, check top of save area
!
! Misaligned sp. If this is a userland trap fake a memory alignment
! trap. Otherwise, put the window in the window save buffer so that
! we can catch it again later.
!
mov %psr, %g1 ! get psr (we are not in trap window)
btst PSR_PS, %g1 ! test for user or sup trap
bnz wo_save_to_buf ! sup trap, save window in uarea buf
nop
restore ! get back to orig window
mov %l5, %g2 ! restore g2
mov %l7, %g1 ! restore g1
mov %l3, %wim ! restore old wim, so regs are dumped
b sys_trap
mov T_ALIGNMENT, %l4 ! delay slot, fake alignment trap
wo_sp_top:
#ifdef VA_HOLE
sethi %hi(_hole_shift), %g2 ! hole shift address
ld [%g2 + %lo(_hole_shift)], %g2
sra %g1, %g2, %g2
inc %g2
andncc %g2, 1, %g2
bz,a 1f
lda [%g1]ASI_PM, %g1 ! get pme for this address
b,a wo_stack_not_res ! stack page can never be resident
1:
sethi %hi(_hole_shift), %g2 ! hole shift address
ld [%g2 + %lo(_hole_shift)], %g2
srl %g1, PG_S_BIT, %g1 ! get vws bits
sra %sp, %g2, %g2
inc %g2
andncc %g2, 1, %g2
bz,a 1f
cmp %g1, PROT ! look for valid, writeable, user
b,a wo_stack_not_res ! stack page can never be resident
1:
#else
lda [%g1]ASI_PM, %g1 ! get pme for this address
srl %g1, PG_S_BIT, %g1 ! get vws bits
cmp %g1, PROT ! look for valid, writeable, user
#endif VA_HOLE
be,a wo_sp_bot
lda [%sp]ASI_PM, %g1 ! delay slot, check bottom of save area
b wo_stack_not_res ! stack page not resident
bset 1, %sp ! note that this is top of save area
wo_sp_bot:
srl %g1, PG_S_BIT, %g1 ! get vws bits
cmp %g1, PROT ! look for valid, writeable, user
be wo_ustack_res
nop ! extra nop
wo_stack_not_res:
!
! The stack save area for user window is not resident.
!
mov %psr, %g1 ! get psr (we are not in trap window)
btst PSR_PS, %g1 ! test for user or sup trap
bnz,a wo_save_to_buf ! sup trap, save window in uarea buf
bclr 1, %sp ! no need to know which end failed
btst 1, %sp ! reconstruct fault address
bz,a 1f ! top of save area?
mov %sp, %g1 ! no, use sp
bclr 1, %sp ! yes, clear "which end" flag
add %sp, (14*4), %g1 ! add appropriate amount
1:
!
! We first save the window in the first window buffer in the u area.
! Then we fake a user data fault. If the fault succeeds, we will
! reexecute the save and overflow again, but this time the page
! will be resident
!
sethi %hi(_uunix), %g2
ld [%g2 + %lo(_uunix)], %g2
st %sp, [%g2+PCB_SPBUF] ! save sp
SAVE_WINDOW(%g2+PCB_WBUF)
restore ! get back into original window
!
! Set the save buffer ptr to next buffer
!
mov 1, %l4
st %l4, [%g2+PCB_WBCNT] ! u->u_pcb.pcb_wbcnt = 1
!
! Compute the user window mask (u.u_pcb.pcb_uwm), which is a mask of
! which windows contain user data. In this case it is all the register
! except the one at the old WIM and the one we just saved.
!
mov %wim, %l4 ! get new WIM
or %l4, %l3, %l4 ! or in old WIM
not %l4
mov -2, %g2
sll %g2, %l6, %g2
andn %l4, %g2, %l4
sethi %hi(_uunix), %g2
ld [%g2 + %lo(_uunix)], %g2
st %l4, [%g2+PCB_UWM] ! u->u_pcb.pcb_uwm = ~(OWIM|NWIM)
set _masterprocp, %g2
ld [%g2], %g2
ld [%g2 + P_STACK], %sp
mov %g1, %l6 ! save fault address, arg to _trap
mov %l7, %g1 ! restore g1, so we can save it
mov %l5, %g2
SAVE_GLOBALS(%sp + MINFRAME)
SAVE_OUTS(%sp + MINFRAME)
st %l0, [%sp + MINFRAME + PSR*4] ! psr
st %l1, [%sp + MINFRAME + PC*4] ! pc
st %l2, [%sp + MINFRAME + nPC*4] ! npc
#ifdef VA_HOLE
sethi %hi(_hole_shift), %g1 ! hole shift address
ld [%g1 + %lo(_hole_shift)], %g1
sra %l6, %g1, %g1
inc %g1
andncc %g1, 1, %g1
bz,a 2f
lda [%l6]ASI_PM, %g1 ! get pme for this address
b 1f ! stack page in the hole
mov GENERIC_INVALID, %o3
2:
#else
lda [%l6]ASI_PM, %g1 ! compute proper bus error reg
#endif VA_HOLE
mov GENERIC_INVALID, %o3
srl %g1, PG_S_BIT, %g1
btst P_INVAL, %g1
bnz,a 1f
mov GENERIC_PROTERR, %o3
1:
wr %l0, PSR_ET, %psr ! enable traps
mov T_DATA_FAULT, %o0
add %sp, MINFRAME, %o1
mov %l6, %o2
call _trap ! trap(T_DATA_FAULT,
mov S_WRITE, %o4 ! rp, addr, be, S_WRITE)
b,a sys_rtt ! return
wo_save_to_buf:
!
! The user's stack is not accessable while trying to save a user window
! during a supervisor overflow. We save the window in the u area to
! be processed when we return to the user.
!
sethi %hi(_uunix), %g2
ld [%g2 + %lo(_uunix)], %g2
ld [%g2 + PCB_WBCNT], %g1
sll %g1, 2, %g1
add %g1, %g2, %g1
st %sp, [%g1 + PCB_SPBUF] ! save sp
sub %g1, %g2, %g1
sll %g1, 4, %g1 ! convert to offset
add %g2, PCB_WBUF, %g2
add %g1, %g2, %g1
SAVE_WINDOW(%g1)
sub %g1, %g2, %g1
sub %g2, PCB_WBUF, %g2
srl %g1, 6, %g1 ! increment u.u_pcb.pcb_wbcnt
add %g1, 1, %g1
set _uunix, %g2
ld [%g2], %g2
st %g1, [%g2 + PCB_WBCNT]
restore ! get back to orig window
!
! Return to supervisor. Rett will not underflow since traps
! were never disabled.
!
mov %l0, %psr ! reinstall system PSR_CC
mov %l7, %g1 ! restore g1
mov %l5, %g2 ! restore g2
jmp %l1 ! reexecute save
rett %l2
!
! The user's save area is resident. Save the window.
!
wo_ustack_res:
SAVE_WINDOW(%sp)
restore ! go back to trap window
wo_out:
sethi %hi(_uunix), %l3
ld [%l3 + %lo(_uunix)], %l3
mov %l5, %g2 ! restore g2
ld [%l3+PCB_FLAGS], %l3 ! check for clean window maintenance
mov %l7, %g1 ! restore g1
btst CLEAN_WINDOWS, %l3
bz 1f
mov %l0, %psr ! reinstall system PSR_CC
!
! Maintain clean windows.
!
mov %l1, %o6 ! put pc, npc in an unobtrusive place
mov %l2, %o7
clr %l0 ! clean the rest
clr %l1
clr %l2
clr %l3
clr %l4
clr %l5
clr %l6
clr %l7
clr %o0
clr %o1
clr %o2
clr %o3
clr %o4
clr %o5
jmp %o6 ! reexecute save
rett %o7
1:
jmp %l1 ! reexecute save
rett %l2

70
sys/sparc/pcb.h Normal file
View File

@@ -0,0 +1,70 @@
/* @(#)pcb.h 1.1 94/10/31 SMI */
/*
* Copyright (c) 1985 by Sun Microsystems, Inc.
*/
#ifndef _sparc_pcb_h
#define _sparc_pcb_h
/*
* Sun software process control block
*/
#include <machine/reg.h>
#define MAXWIN 12 /* max number of windows currently supported */
/*
* The system actually supports one more than the above number.
* There is always one window reserved for trap handlers that
* never has to be saved into the pcb struct.
*/
#ifndef LOCORE
#ifdef STD_REF_MMU
#include <machine/vm_hat.h>
#endif
struct pcb {
#ifdef STD_REF_MMU
/* this struct MUST be 1K aligned -- checked for in assym.s */
struct l1pt pcb_l1pt; /* level 1 page table */
#endif
label_t pcb_regs; /* saved pc and sp */
int pcb_psr; /* processor status word */
int pcb_uwm; /* user window mask */
struct rwindow pcb_wbuf[MAXWIN]; /* user window save buffer */
char *pcb_spbuf[MAXWIN]; /* sp's for each wbuf */
int pcb_wbcnt; /* number of saved windows in pcb_wbuf */
int *pcb_psrp; /* psr pointer to en/disable coprocessors */
struct fpu *pcb_fpctxp;/* pointer to fpu state */
int pcb_fpflags; /* fpu enable flags */
int *pcb_cpctxp; /* pointer to coprocessor state */
int pcb_cpflags; /* coprocessor enable flags */
int pcb_flags; /* various state flags */
int pcb_wocnt; /* window overflow count */
int pcb_wucnt; /* window underflow count */
};
#define pcb_pc pcb_regs.val[0]
#define pcb_sp pcb_regs.val[1]
#define aston() {u.u_pcb.pcb_flags |= AST_SCHED;}
#define astoff() {u.u_pcb.pcb_flags &= ~AST_SCHED;}
#endif !LOCORE
/* pcb_flags */
#define AST_SCHED 0x80000000 /* force a reschedule */
#define AST_CLR 0x80000000
#define PME_CLR 0
#define AST_NONE 0
/* pcb_flags */
#define CLEAN_WINDOWS 0x1 /* keep user regs clean */
#define FIX_ALIGNMENT 0x2 /* fix unaligned references */
#endif /*!_sparc_pcb_h*/

69
sys/sparc/psl.h Normal file
View File

@@ -0,0 +1,69 @@
/* @(#)psl.h 1.1 94/10/31 SMI */
/*
* Copyright (c) 1986 by Sun Microsystems, Inc.
*/
#ifndef _sparc_psl_h
#define _sparc_psl_h
/*
* Definition of bits in the Sun-4 PSR (Processor Status Register)
* ________________________________________________________________________
* | IMPL | VER | ICC | resvd | EC | EF | PIL | S | PS | ET | CWP |
* | | | N | Z | V | C | | | | | | | | |
* |------|-----|---|---|---|---|-------|----|----|-----|---|----|----|-----|
* 31 28 27 24 23 22 21 20 19 14 13 12 11 8 7 6 5 4 0
*
* Reserved bits are defined to be initialized to zero and must
* be preserved if written, for compatibility with future revisions.
*/
#define PSR_CWP 0x0000001F /* current window pointer */
#define PSR_ET 0x00000020 /* enable traps */
#define PSR_PS 0x00000040 /* previous supervisor mode */
#define PSR_S 0x00000080 /* supervisor mode */
#define PSR_PIL 0x00000F00 /* processor interrupt level */
#define PSR_EF 0x00001000 /* enable floating point unit */
#define PSR_EC 0x00002000 /* enable coprocessor */
#define PSR_RSV 0x000FC000 /* reserved */
#define PSR_ICC 0x00F00000 /* integer condition codes */
#define PSR_C 0x00100000 /* carry bit */
#define PSR_V 0x00200000 /* overflow bit */
#define PSR_Z 0x00400000 /* zero bit */
#define PSR_N 0x00800000 /* negative bit */
#define PSR_VER 0x0F000000 /* mask version */
#define PSR_IMPL 0xF0000000 /* implementation */
#define PSL_ALLCC PSR_ICC /* for portability */
#define SR_SMODE PSR_PS
/*
* Handy psr values.
*/
#define PSL_USER (PSR_S) /* initial user psr */
#define PSL_USERMASK (PSR_ICC) /* user variable psr bits */
#define PSR_PIL1 0x100 /* for rounding up interrupt pri */
/*
* Macros to decode psr.
*/
#define USERMODE(ps) (((ps) & PSR_PS) == 0)
#define BASEPRI(ps) (((ps) & PSR_PIL) == 0)
/*
* Convert system interrupt priorities (0-7) into a psr for splx.
* In general, the processor priority (0-15) should be 2 times
* the system pririty.
*/
#define pritospl(n) ((n) << 9)
/*
* Convert a hardware interrupt priority level (0-15) into a psr for splx.
* Also are macros to convert back.
*/
#define ipltospl(n) (((n) & 0xf) << 8)
#define spltoipl(n) (((n) >> 8) & 0xf)
#define spltopri(n) ((((n) + (1 << 8)) >> 9) & 0xf)
#endif /*!_sparc_psl_h*/

254
sys/sparc/reg.h Normal file
View File

@@ -0,0 +1,254 @@
/* @(#)reg.h 1.1 94/10/31 SMI */
/*
* Copyright (c) 1985 by Sun Microsystems, Inc.
*/
#ifndef _sparc_reg_h
#define _sparc_reg_h
/*
* Location of the users' stored
* registers relative to R0.
* Usage is u.u_ar0[XX].
*/
#define PSR (0)
#define PC (1)
#define nPC (2)
#define Y (3)
#define G1 (4)
#define G2 (5)
#define G3 (6)
#define G4 (7)
#define G5 (8)
#define G6 (9)
#define G7 (10)
#define O0 (11)
#define O1 (12)
#define O2 (13)
#define O3 (14)
#define O4 (15)
#define O5 (16)
#define O6 (17)
#define O7 (18)
/* the following defines are for portability */
#define PS PSR
#define SP O6
#define R0 O0
#define R1 O1
/*
* And now for something completely the same...
*/
#ifndef LOCORE
struct regs {
int r_psr; /* processor status register */
int r_pc; /* program counter */
int r_npc; /* next program counter */
int r_y; /* the y register */
int r_g1; /* user global regs */
int r_g2;
int r_g3;
int r_g4;
int r_g5;
int r_g6;
int r_g7;
int r_o0;
int r_o1;
int r_o2;
int r_o3;
int r_o4;
int r_o5;
int r_o6;
int r_o7;
};
#define r_ps r_psr /* for portablility */
#define r_r0 r_o0
#define r_sp r_o6
#endif !LOCORE
/*
* Floating point definitions.
*/
#define FPU /* we have an external float unit */
#ifndef LOCORE
#define FQ_DEPTH 16 /* maximum instuctions in FQ */
/*
* struct fp_status is the floating point processor state
* struct fpu is the sum total of all possible floating point state
* which includes the state of external floating point hardware,
* fpa registers, etc..., if it exists.
*/
struct fpq {
unsigned long *addr; /* address */
unsigned long instr; /* instruction */
};
struct fq {
union { /* FPU inst/addr queue */
double whole;
struct fpq fpq;
} FQu;
};
#define FPU_REGS_TYPE unsigned
#define FPU_FSR_TYPE unsigned
struct fp_status {
union { /* FPU floating point regs */
FPU_REGS_TYPE Fpu_regs[32]; /* 32 singles */
double Fpu_dregs[16]; /* 16 doubles */
} fpu_fr;
FPU_FSR_TYPE Fpu_fsr; /* FPU status register */
unsigned Fpu_flags; /* control flags */
unsigned Fpu_extra; /* extra word */
unsigned Fpu_qcnt; /* count of valid entries in fps_q */
struct fq Fpu_q[FQ_DEPTH]; /* FPU instruction address queue */
};
#define fpu_regs f_fpstatus.fpu_fr.Fpu_regs
#define fpu_dregs f_fpstatus.fpu_fr.Fpu_dregs
#define fpu_fsr f_fpstatus.Fpu_fsr
#define fpu_flags f_fpstatus.Fpu_flags
#define fpu_extra f_fpstatus.Fpu_extra
#define fpu_q f_fpstatus.Fpu_q
#define fpu_qcnt f_fpstatus.Fpu_qcnt
struct fpu {
struct fp_status f_fpstatus;
};
#endif !LOCORE
/*
* Definition of bits in the Sun-4 FSR (Floating-point Status Register)
* ________________________________________________________________________
* | RD | RP | TEM | NS | res | vers | FTT | QNE | PR | FCC | AEXC | CEXC |
* |-----|---- |-----|----|-----|------|-----|-----|----|-----|------|------|
* 31 30 29 28 27 23 22 21 20 19 17 16 14 13 12 11 10 9 5 4 0
*/
#define FSR_CEXC 0x0000001f /* Current Exception */
#define FSR_AEXC 0x000003e0 /* ieee accrued exceptions */
#define FSR_FCC 0x00000c00 /* Floating-point Condition Codes */
#define FSR_PR 0x00001000 /* Partial Remainder */
#define FSR_QNE 0x00002000 /* Queue not empty */
#define FSR_FTT 0x0001c000 /* Floating-point Trap Type */
#define FSR_VERS 0x000e0000 /* version field */
#define FSR_RESV 0x00300000 /* reserved */
#define FSR_NS 0x00400000 /* non-standard fp */
#define FSR_TEM 0x0f800000 /* ieee Trap Enable Mask */
#define FSR_RP 0x30000000 /* Rounding Precision */
#define FSR_RD 0xc0000000 /* Rounding Direction */
#define FSR_VERS_SHIFT (17) /* amount to shift version field */
/*
* Definition of CEXC (Current EXCeption) bit field of fsr
*/
#define FSR_CEXC_NX 0x00000001 /* inexact */
#define FSR_CEXC_DZ 0x00000002 /* divide-by-zero */
#define FSR_CEXC_UF 0x00000004 /* underflow */.
#define FSR_CEXC_OF 0x00000008 /* overflow */
#define FSR_CEXC_NV 0x00000010 /* invalid */
/*
* Definition of AEXC (Accrued EXCeption) bit field of fsr
*/
#define FSR_AEXC_NX (0x1 << 5) /* inexact */
#define FSR_AEXC_DZ (0x2 << 5) /* divide-by-zero */
#define FSR_AEXC_UF (0x4 << 5) /* underflow */.
#define FSR_AEXC_OF (0x8 << 5) /* overflow */
#define FSR_AEXC_NV (0x10 << 5) /* invalid */
/*
* Defintion of FTT (Floating-point Trap Type) field within the FSR
*/
#define FTT_NONE 0 /* no excepitons */
#define FTT_IEEE 1 /* IEEE exception */
#define FTT_UNFIN 2 /* unfinished fpop */
#define FTT_UNIMP 3 /* unimplemented fpop */
#define FTT_SEQ 4 /* sequence error */
#define FTT_ALIGN 5 /* alignment, by software convention */
#define FTT_DFAULT 6 /* data fault, by software convention */
#define FSR_FTT_SHIFT 14 /* shift needed to justfy ftt field */
#define FSR_FTT_IEEE (FTT_IEEE << FSR_FTT_SHIFT)
#define FSR_FTT_UNFIN (FTT_UNFIN << FSR_FTT_SHIFT)
#define FSR_FTT_UNIMP (FTT_UNIMP << FSR_FTT_SHIFT)
#define FSR_FTT_SEQ (FTT_SEQ << FSR_FTT_SHIFT)
#define FSR_FTT_ALIGN (FTT_ALIGN << FSR_FTT_SHIFT)
#define FSR_FTT_DFAULT (FTT_DFAULT << FSR_FTT_SHIFT)
/*
* Values of VERS (version) field within the FSR
* NOTE: these values are overloaded; the cpu type must be used to
* further discriminate amongst these. For that reason, no #defines are
* provided.
*
* Version cpu = 21-22, 51-54 cpu = 23-24, 55-57
* 0 Weitek 1164/5 (FAB 1-4) TI 8847
* 1 Weitek 1164/5 (FAB 5-6) LSI L64814
* 2 TI 8847 TI TMS390C602A
* 3 Weitek 3170 Weitek 3171
* 4 Meiko ?
* 5 ? ?
* 6 ? ?
* 7 No FP Hardware No FP Hardware
*/
/*
* Definition of TEM (Trap Enable Mask) bit field of fsr
*/
#define FSR_TEM_NX (0x1 << 23) /* inexact */
#define FSR_TEM_DZ (0x2 << 23) /* divide-by-zero */
#define FSR_TEM_UF (0x4 << 23) /* underflow */.
#define FSR_TEM_OF (0x8 << 23) /* overflow */
#define FSR_TEM_NV (0x10 << 23) /* invalid */
/*
* Definition of RP (Rounding Precision) field of fsr
*/
#define RP_DBLEXT 0 /* double-extended */
#define RP_SINGLE 1 /* single */
#define RP_DOUBLE 2 /* double */
#define RP_RESERVED 3 /* unused and reserved */
/*
* Defintion of RD (Rounding Direction) field of fsr
*/
#define RD_NEAR 0 /* nearest or even if tie */
#define RD_ZER0 1 /* to zero */
#define RD_POSINF 2 /* positive infinity */
#define RD_NEGINF 3 /* negative infinity */
/*
* Definition of the FP enable flags of the pcb struct
* Normal operation, all flags are zero
*/
#define FP_UNINITIALIZED 1
#define FP_STARTSIG 2
#define FP_DISABLE 4
#define FP_ENABLE 8
#ifndef LOCORE
/*
* How a register window looks on the stack.
*/
struct rwindow {
int rw_local[8]; /* locals */
int rw_in[8]; /* ins */
};
#define rw_fp rw_in[6] /* frame pointer */
#define rw_rtn rw_in[7] /* return address */
#endif !LOCORE
#endif /*!_sparc_reg_h*/

34
sys/sparc/setjmp.h Normal file
View File

@@ -0,0 +1,34 @@
/* @(#)setjmp.h 1.1 94/10/31 SMI; from UCB 4.1 83/05/03 */
#ifndef __sparc_setjmp_h
#define __sparc_setjmp_h
/*
* onsstack,sigmask,sp,pc,npc,psr,g1,o0,wbcnt (sigcontext).
* All else recovered by under/over(flow) handling.
*/
#define _JBLEN 9
typedef int jmp_buf[_JBLEN];
/*
* One extra word for the "signal mask saved here" flag.
*/
typedef int sigjmp_buf[_JBLEN+1];
int setjmp(/* jmp_buf env */);
int _setjmp(/* jmp_buf env */);
int sigsetjmp(/* sigjmp_buf env, int savemask */);
void longjmp(/* jmp_buf env, int val */);
void _longjmp(/* jmp_buf env, int val */);
void siglongjmp(/* sigjmp_buf env, int val */);
/*
* Routines that call setjmp have strange control flow graphs,
* since a call to a routine that calls resume/longjmp will eventually
* return at the setjmp site, not the original call site. This
* utterly wrecks control flow analysis.
*/
#pragma unknown_control_flow(sigsetjmp, setjmp, _setjmp)
#endif /* !__sparc_setjmp_h */

654
sys/sparc/sparc_subr.s Normal file
View File

@@ -0,0 +1,654 @@
/* @(#)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

788
sys/sparc/swtch.s Normal file
View File

@@ -0,0 +1,788 @@
/* @(#)swtch.s 1.1 94/10/31 SMI */
/*
* Copyright (c) 1987 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 "assym.s"
.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(), and p->p_stat should be SRUN
*/
ENTRY(setrq)
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
retl
st %g1, [%g7 + %lo(_whichqs)]
/*
* remrq(p)
*
* Call should be made at spl6().
*/
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:
retl
clr [%o0 + P_RLINK] ! p->p_rlink = 0
.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:
sethi %hi(_whichqs), %g1
ld [%g1 + %lo(_whichqs)], %g1
tst %g1
bz 1f ! none check other stuff
nop
!
! Found something.
!
mov %psr, %g1 ! splhi
bclr PSR_PIL, %g1
or %g1, 10 << PSR_PIL_BIT, %g1
mov %g1, %psr
nop ! psr delay
b sw_testq
nop ! more psr delay
!
! Test for other async events.
!
1:
#ifdef LWP
sethi %hi(___Nrunnable),%g2
ld [%g2+%lo(___Nrunnable)],%g2
tst %g2
bz 2f
nop
call _lwpschedule
nop
set CONTEXT_REG, %g2
stba %g0, [%g2]ASI_CTL
2:
#endif LWP
sethi %hi(_qrunflag), %g1 ! need to run stream queues?
ldub [%g1 + %lo(_qrunflag)], %g1
tst %g1
#ifndef SAS
bz _idle ! no
nop
#else SAS
/*
* Instead of idle-looping when we have nothing to do,
* fake a bunch of clock ticks.
*/
bnz 1f
nop
call _fake_clockticks
nop
b,a _idle
1:
#endif SAS
call _runqueues ! go do it
nop
b,a _idle
/*
* swtch()
*/
ENTRY(swtch)
save %sp, -SA(MINFRAME), %sp
mov %psr, %l0 ! save PSR
andn %l0, PSR_PIL, %g1
or %g1, 10 << PSR_PIL_BIT, %g1
mov %g1, %psr ! spl hi
nop
mov 1, %g2
sethi %hi(_noproc), %g6
st %g2, [%g6 + %lo(_noproc)]
sethi %hi(_runrun), %g6
clr [%g6 + %lo(_runrun)]
sethi %hi(_masterprocp), %l1
ld [%l1 + %lo(_masterprocp)], %l2
tst %l2 ! if no proc skip saving state
bz sw_testq ! this happens after exit
.empty ! hush assembler warnings
set _uunix, %g5 ! XXX - global u register?
ld [%g5], %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.
!
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
sll %l2, 1, %l2
add %l3, 8, %l3
tst %l2
bnz,a 1b
btst %l2, %l1 ! delay slot
!
! Found no procs, goto idle loop
!
mov %psr, %g1 ! allow interrupts
andn %g1, PSR_PIL, %g1
mov %g1, %psr
b,a _idle
sw_foundq:
set _qs, %l4 ! get qs proc list
ld [%l4 + %l3], %l4 ! p=qs[bit].ph_link=highest pri process
ld [%l4], %g1 ! remque(p)
cmp %l4, %g1 ! is queue empty?
bne,a 2f
ld [%l4 + 4], %g2 ! delay slot
sw_bad:
set 4f, %o0
call _panic
nop
4:
.asciz "swtch"
.align 4
2:
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), %g6
clr [%g6 + %lo(_noproc)]
ld [%l4 + P_WCHAN], %g1 ! firewalls
ldub [%l4 + P_STAT], %g2
tst %g1
bz,a 4f ! p->p_wchan must be 0
cmp %g2, SRUN ! p->p_stat must be SRUN
b,a sw_bad
4:
be,a 5f
clr [%l4 + P_RLINK] ! p->p_rlink = 0
b,a sw_bad
5:
sethi %hi(_masterprocp), %l1
ld [%l1 + %lo(_masterprocp)], %l2
set _cnt, %g1 ! cnt.v_swtch++
ld [%g1 + V_SWTCH], %g2
cmp %l2, %l4 ! if (p == masterprocp) ...
be 6f ! skip resume() call
inc 1, %g2 ! delay slot
mov %l4, %o0 ! resume(p)
call _resume
.empty
6:
st %g2, [%g1 + V_SWTCH] ! delay slot, update ctx switch count
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"
.global _masterprocp
_masterprocp:
.word 0 ! struct proc *masterprocp
/*
* 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
set _scb, %o1 ! get NW-1, which is
ldub [%o1 + 31], %o3 ! last byte of text fault trap vector
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
b 4f
mov %l0, %psr ! restore priority
! store state for current proc
1: set _uunix, %g5 ! XXX - global u register?
ld [%g5], %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
! (this also serves as a psr delay)
!
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]
st %i0, [%l1 + %lo(_masterprocp)]
!
! Switch to new u area.
!
set CONTEXT_REG, %l3 ! setup kernel context (0)
stba %g0, [%l3]ASI_CTL
ld [%i0 + P_UAREA], %g5 ! address of uarea for new proc
set _uunix, %g2 ! interlock, set uunix - XXX
st %g5, [%g2] ! install new uarea
!
! 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
mov %g5, %l1 ! save %g5 (uptr) over call
call _call_debug_from_asm
st %g0, [%g1 + %lo(_kadb_want)]
mov %l1, %g5 ! restore %g5
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 %l0
bz 1f ! if zero, skip ahead
nop ! delay slot
ld [%l0 + A_HAT_CTX], %l0 ! check (p->p_as->a_hat.hat_ctx)
tst %l0
bz 1f ! if zero, skip ahead
nop ! delay slot
!
! Switch to the procs context.
!
ldub [%l0 + C_NUM], %g1 ! get context number
stba %g1, [%l3]ASI_CTL ! set it up
sethi %hi(_ctx_time), %l1
lduh [%l1 + %lo(_ctx_time)], %g1
add %g1, 1, %g2
sth %g1, [%l0 + C_TIME] ! Set context clock to LRU clock
sth %g2, [%l1 + %lo(_ctx_time)]! ++ctx_time
ldub [%l0], %g1 ! get the context bits
andn %g1, C_CLEAN_BITMASK, %g1 ! zero the context's clean bit
stb %g1, [%l0]
1:
!
! 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
!
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
!
! if this is the same process that last used the fpu
! we can skip loading its state
!
cmp %g1, %l1 ! if the fp ctx belongs to new process
be 3f ! nothing to do
! maybe branch to 2 instead
!
! 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 ! initial kernel sp for this process
add %l1, (MINFRAME + PSR*4), %l1 ! psr addr on process stack
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.
!
sub %fp, WINDOWSIZE, %sp ! establish sp, for interrupt overflow
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
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
set _masterprocp, %o0 ! install child proc as master
st %i2, [%o0]
set _uunix, %o1 ! install child uarea - XXX
st %i4, [%o1] ! global uarea pointer?
!
! 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.
!
set CONTEXT_REG, %l3 ! setup context reg pointer
tst %l4
bz 1f ! if zero, skip ahead
! delay slot, get new process start address
ld [%i1 + 6*4], %l5 ! %l6 has proc start address
ld [%l4 + A_HAT_CTX], %l4 ! check (p->p_as->a_hat.hat_ctx)
tst %l4
bnz 2f ! if zero, skip ahead
.empty ! hush assembler warnings
1: mov %i1, %fp ! install the new childs stack
stba %g0, [%l3]ASI_CTL ! if no context, use kernels
3: sub %fp, WINDOWSIZE, %sp ! establish sp, for interrupt overflow
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 %l5
restore ! restore causes underflow
!
! Switch to the procs context.
!
2: ldub [%l4 + C_NUM], %g1 ! get context number
stba %g1, [%l3]ASI_CTL ! set it up
sethi %hi(_ctx_time), %l1
lduh [%l1 + %lo(_ctx_time)], %g1
add %g1, 1, %g2
sth %g1, [%l4 + C_TIME] ! Set context clock to LRU clock
ldub [%l4], %g1 ! get the context bits
andn %g1, C_CLEAN_BITMASK, %g1 ! zero the context's clean bit
stb %g1, [%l4]
b 3b
sth %g2, [%l1 + %lo(_ctx_time)]! ++ctx_time
/*
* finishexit(vaddr)
* Done with the current stack;
* switch to the exit 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
set eintstack, %o3 ! Are we on interrupt stack?
cmp %sp, %o3 ! If so, panic
ble 1f
.empty
set eexitstack, %o3
sub %o3, SA(MINFRAME), %sp ! switch to exit stack
set intu, %o2 ! temporary uarea
set _uunix, %o1
st %o2, [%o1]
set _noproc, %o2 ! no process now
mov 1, %o1
st %o1, [%o2]
call _segu_release
mov %i0, %o0 ! arg to segu_release
set _masterprocp, %o5
clr [%o5]
mov %l4, %psr ! restore previous priority
nop
b,a _swtch
/* NOTREACHED */
1:
set 3f, %o0
call _panic
nop
3: .asciz "finishexit"
.align 4
/*
* Setmmucntxt(procp)
*
* initialize mmu context for a process, if it has one
*/
ENTRY(setmmucntxt)
ld [%o0 + P_AS], %o1 ! check p->p_as
set CONTEXT_REG, %o4
tst %o1
bz 1f ! if zero, skip ahead
mov %g0, %o0
ld [%o1 + A_HAT_CTX], %o1 ! check (p->p_as->a_hat.hat_ctx)
tst %o1
bz 1f ! if zero, skip ahead
mov %g0, %o0
!
! Switch to the procs context.
!
ldub [%o1 + C_NUM], %o0 ! get context number
sethi %hi(_ctx_time), %o5
lduh [%o5 + %lo(_ctx_time)], %o2
add %o2, 1, %o3
sth %o2, [%o1 + C_TIME] ! Set context clock to LRU clock
sth %o3, [%o5 + %lo(_ctx_time)]! ++ctx_time
ldub [%o1], %g1 ! get the context bits
andn %g1, C_CLEAN_BITMASK, %g1 ! zero the context's clean bit
stb %g1, [%o1]
1:
retl
stba %o0, [%o4]ASI_CTL ! set it up

126
sys/sparc/trap.h Normal file
View File

@@ -0,0 +1,126 @@
/* @(#)trap.h 1.1 94/10/31 SMI */
/*
* Copyright (c) 1985 by Sun Microsystems, Inc.
*/
#ifndef _sparc_trap_h
#define _sparc_trap_h
/*
* Trap type values.
*/
#define TT(X) ((X)<<4)
/*
* The Coprocessor bit.
*/
#define CP_BIT 0x20
#define SPARC_V8_TRAPS
#ifdef SPARC_V8_TRAPS
/*
* The Error (versus Fault==Exception) bit.
*/
#define ERR_BIT 0x20
#endif
/*
* Hardware traps.
*/
#define T_RESET 0x00
#define T_TEXT_FAULT 0x01
#ifdef SPARC_V8_TRAPS
#define T_TEXT_ERROR (0x1 | ERR_BIT)
#endif
#define T_UNIMP_INSTR 0x02
#define T_PRIV_INSTR 0x03
#define T_FP_DISABLED 0x04
#define T_CP_DISABLED (0x4 | CP_BIT)
#define T_WIN_OVERFLOW 0x05
#define T_WIN_UNDERFLOW 0x06
#define T_ALIGNMENT 0x07
#define T_FP_EXCEPTION 0x08
#define T_CP_EXCEPTION (0x8 | CP_BIT)
#define T_DATA_FAULT 0x09
#ifdef SPARC_V8_TRAPS
#define T_DATA_ERROR (0x9 | ERR_BIT)
#endif
#define T_TAG_OVERFLOW 0x0A
#define T_INT 0x10
#define T_INT_LEVEL 0x0F
#define T_INT_LEVEL_1 0x11
#define T_INT_LEVEL_2 0x12
#define T_INT_LEVEL_3 0x13
#define T_INT_LEVEL_4 0x14
#define T_INT_LEVEL_5 0x15
#define T_INT_LEVEL_6 0x16
#define T_INT_LEVEL_7 0x17
#define T_INT_LEVEL_8 0x18
#define T_INT_LEVEL_9 0x19
#define T_INT_LEVEL_10 0x1A
#define T_INT_LEVEL_11 0x1B
#define T_INT_LEVEL_12 0x1C
#define T_INT_LEVEL_13 0x1D
#define T_INT_LEVEL_14 0x1E
#define T_INT_LEVEL_15 0x1F
#ifdef SPARC_V8_TRAPS
#define T_UMIMP_FLUSH 0x25
#endif
#define T_IDIV0 0x2A
#define T_DATA_STORE 0x2B
#ifdef SPARC_V8_TRAPS
#define T_DATA_MISS 0x2C
#define T_TEXT_MISS 0x3C
#endif
/*
* Software traps (ticc instructions).
*/
#define ST_SYSCALL 0x00
#define ST_BREAKPOINT 0x01
#define ST_DIV0 0x02
#define ST_FLUSH_WINDOWS 0x03
#define ST_CLEAN_WINDOWS 0x04
#define ST_RANGE_CHECK 0x05
#define ST_FIX_ALIGN 0x06
#define ST_INT_OVERFLOW 0x07
#define ST_GETCC 0x20
#define ST_SETCC 0x21
#if defined(sun4c) || defined(sun4m)
#define ST_MON_BREAKPOINT 0x7F
#endif sun4c
/*
* Software trap vectors 16 - 31 are reserved for use by the user
* and will not be usurped by Sun.
*/
/*
* Software trap type values.
*/
#define T_SOFTWARE_TRAP 0x80
#define T_ESOFTWARE_TRAP 0xFF
#define T_SYSCALL (T_SOFTWARE_TRAP + ST_SYSCALL)
#define T_BREAKPOINT (T_SOFTWARE_TRAP + ST_BREAKPOINT)
#define T_DIV0 (T_SOFTWARE_TRAP + ST_DIV0)
#define T_FLUSH_WINDOWS (T_SOFTWARE_TRAP + ST_FLUSH_WINDOWS)
#define T_CLEAN_WINDOWS (T_SOFTWARE_TRAP + ST_CLEAN_WINDOWS)
#define T_RANGE_CHECK (T_SOFTWARE_TRAP + ST_RANGE_CHECK)
#define T_FIX_ALIGN (T_SOFTWARE_TRAP + ST_FIX_ALIGN)
#define T_INT_OVERFLOW (T_SOFTWARE_TRAP + ST_INT_OVERFLOW)
#define T_GETCC (T_SOFTWARE_TRAP + ST_GETCC)
#define T_SETCC (T_SOFTWARE_TRAP + ST_SETCC)
/*
* Pseudo traps.
*/
#define T_INTERRUPT 0x100
#define T_SPURIOUS (T_INTERRUPT | T_INT)
#define T_FAULT 0x200
#define T_AST 0x400
#define T_ZERO 0x00
#endif /*!_sparc_trap_h*/

247
sys/sparc/underflow.s Normal file
View File

@@ -0,0 +1,247 @@
/* @(#)underflow.s 1.1 94/10/31 SMI */
/*
* Copyright (c) 1989 by Sun Microsystems, Inc.
*/
#include <machine/asm_linkage.h>
#include <machine/param.h>
#include <machine/psl.h>
#include <machine/trap.h>
#include <machine/mmu.h>
#include <machine/pte.h>
#include <machine/reg.h>
#include <machine/buserr.h>
#include <machine/pcb.h>
#include "assym.s"
.seg "text"
.align 4
PROT_INVAL = (PG_V) >> PG_S_BIT
VALID = (PG_V) >> PG_S_BIT
USER_INVAL = (PG_S) >> PG_S_BIT
/*
* Window underflow trap handler.
*/
.global window_underflow
window_underflow:
#ifdef PERFMETER
sethi %hi(_underflowcnt), %l5
ld [%l5 + %lo(_underflowcnt)], %l7
inc %l7
st %l7, [%l5 + %lo(_underflowcnt)]
#endif PERFMETER
! wim stored into %l3 by trap vector
sll %l3, 1, %l4 ! next WIM = rol(WIM, 1, NW)
srl %l3, %l6, %l5 ! trap vector set %l6 = NW-1
or %l5, %l4, %l5
mov %l5, %wim ! install it
btst PSR_PS, %l0 ! (wim delay 1) test for user trap
bz wu_user ! (wim delay 2)
restore ! delay slot
!
! Supervisor underflow.
! We do one more restore to get into the window to be restored.
! The first one was done in the delay slot coming here.
! We then restore from the stack.
!
restore ! get into window to be restored
wu_stack_res:
RESTORE_WINDOW(%sp)
save ! get back to original window
save
mov %l0, %psr ! reinstall sup PSR_CC
nop ! psr delay
jmp %l1 ! reexecute restore
rett %l2
wu_user:
!
! User underflow. Window to be restored is a user window.
! We must check whether the user stack is resident where the window
! will be restored from, which is pointed to by the windows sp.
! The sp is the fp of the window which tried to do the restore,
! so that it is still valid.
!
restore ! get into window to be restored
!
! Normally, we would check the alignment, and then probe the top
! and bottom of the save area on the stack. However we optimize
! this by checking that both ends of the save area are within a
! 4k unit (the biggest mask we can generate in one cycle), and
! the alignment in one shot. This allows us to do one probe to
! the page map. NOTE: this assumes a page size of at least 4k.
!
and %sp, 0xfff, %l0
#ifdef VA_HOLE
! check if the sp points into the hole in the address space
sethi %hi(_hole_shift), %l5 ! hole shift address
ld [%l5 + %lo(_hole_shift)], %l7
add %l0, (14*4), %l0 ! interlock, bottom of save area
sra %sp, %l7, %l5
inc %l5
andncc %l5, 1, %l5
bz 1f
andncc %l0, 0xff8, %g0
b,a wu_stack_not_res ! sp is in the hole
1:
#else
add %l0, (14*4), %l0
andncc %l0, 0xff8, %g0
#endif VA_HOLE
bz,a wu_sp_bot
lda [%sp]ASI_PM, %l1 ! check for stack page resident
!
! Stack is either misaligned or crosses a 4k boundary.
!
btst 0x7, %sp ! test sp alignment
bz wu_sp_top
add %sp, (14*4), %l0 ! delay slot, check top of save area
!
! A user underflow trap has happened with a misaligned sp.
! Fake a memory alignment trap.
!
save ! get back to orig window
save
mov %l3, %wim ! restore old wim, so regs are dumped
b sys_trap
mov T_ALIGNMENT, %l4 ! delay slot, fake alignment trap
wu_sp_top:
#ifdef VA_HOLE
sra %l0, %l7, %l5
inc %l5
andncc %l5, 1, %l5
bz,a 1f
lda [%l0]ASI_PM, %l1 ! get pme for this address
b,a wu_stack_not_res ! address is in the hole
1:
srl %l1, PG_S_BIT, %l1 ! get vws bits
sra %sp, %l7, %l5
inc %l5
andncc %l5, 1, %l5
bz 1f
andcc %l1, VALID, %g0 ! look for valid bit
b,a wu_stack_not_res ! stack page can never be resident
1:
#else
lda [%l0]ASI_PM, %l1 ! get pme for this address
srl %l1, PG_S_BIT, %l1 ! get vws bits
andcc %l1, VALID, %g0 ! look for valid bit
#endif VA_HOLE
bnz,a wu_sp_bot
lda [%sp]ASI_PM, %l1 ! delay slot, check bottom of save area
b,a wu_stack_not_res ! stack page not resident
wu_sp_bot:
srl %l1, PG_S_BIT, %l1 ! get vws bits
!!! SECURITY NOTE: check for user access to kernel space
!!! XXX - assumes that kernel space is always resident
andcc %l1, USER_INVAL, %g0 ! look for supervisor bit
bnz wu_stack_not_user
nop
andcc %l1, VALID, %g0 ! look for valid bit
bnz wu_ustack_res
nop
mov %sp, %l0 ! save fault address
wu_stack_not_res:
!
! Restore area on user stack is not resident.
! We punt and fake a page fault so that trap can bring the page in.
! If the page fault is successful we will reexecute the restore,
! and underflow with the page now resident.
!
sethi %hi(_masterprocp), %l6
ld [%l6 + %lo(_masterprocp)], %l6
ld [%l6 + P_STACK], %l7 ! setup kernel stack
SAVE_GLOBALS(%l7 + MINFRAME)
mov %l7, %g1 ! save computed sp
mov %l0, %g2 ! save fault address
mov GENERIC_INVALID, %g3 ! compute bus error reg code
btst PROT_INVAL, %l1
bnz,a 1f
mov GENERIC_PROTERR, %g3
1:
save ! back to last user window
mov %psr, %g4 ! get CWP
save ! back to trap window
!
! save remaining user state
!
mov %g1, %sp ! setup kernel stack
SAVE_OUTS(%sp + MINFRAME)
st %l0, [%sp + MINFRAME + PSR*4] ! psr
st %l1, [%sp + MINFRAME + PC*4] ! pc
st %l2, [%sp + MINFRAME + nPC*4] ! npc
mov %l3, %wim ! reinstall old wim
mov 1, %g1 ! UWM = 0x01 << CWP
sll %g1, %g4, %g1
sethi %hi(_uunix), %l6
ld [%l6 + %lo(_uunix)], %l6
st %g1, [%l6 + PCB_UWM] ! setup u.u_pcb.pcb_uwm
clr [%l6 + PCB_WBCNT]
wr %l0, PSR_ET, %psr ! enable traps
mov T_DATA_FAULT, %o0
add %sp, MINFRAME, %o1
mov %g2, %o2
mov %g3, %o3
call _trap ! trap(T_DATA_FAULT,
mov S_READ, %o4 ! rp, addr, be, S_READ)
b,a sys_rtt
!
! The user's save area is resident. Restore the window.
!
wu_ustack_res:
RESTORE_WINDOW(%sp)
save ! get back to original window
save
wu_out:
sethi %hi(_uunix), %l3
ld [%l3 + %lo(_uunix)], %l3
ld [%l3+PCB_FLAGS], %l3
clr %l5 ! interlock slot, clear a used reg
btst CLEAN_WINDOWS, %l3 ! check for clean window maint.
bz 1f
mov %l0, %psr ! reinstall system PSR_CC
!
! Maintain clean windows. We only need to clean the registers
! used in underflow as we know this is a user window.
!
mov %l1, %o6 ! put pc, npc in an unobtrusive place
mov %l2, %o7
clr %l0 ! clean the used ones
clr %l1
clr %l2
clr %l3
clr %l4
jmp %o6 ! reexecute restore
rett %o7
1:
nop
jmp %l1 ! reexecute restore
rett %l2
wu_stack_not_user:
mov PROT_INVAL, %l1
b wu_stack_not_res
mov %sp, %l0 ! [fake up SEVAR]

138
sys/sparc/zs_asm.s Normal file
View File

@@ -0,0 +1,138 @@
/*
* Copyright (c) 1987, 1988, 1989 by Sun Microsystems, Inc.
*/
#include "assym.s"
#include <machine/param.h>
#include <machine/asm_linkage.h>
#include <machine/mmu.h>
#include <machine/psl.h>
#include <sundev/zsreg.h>
#include <machine/intreg.h>
.seg "data"
.asciz "@(#)zs_asm.s 1.1 94/10/31"
.align 4
.seg "text"
#ifndef sun4c
/*
* UARTs require 1.6 usec recovery time
* note ZSRECOVER can't be used in a leaf routine
* this is overkill by .4 usec but it scales properly for all cpus
*/
#define ZSRECOVER() \
call _usec_delay; \
mov 2, %o0;
#else sun4c
/* SPARCstation 1 doesn't need a delay. */
#define ZSRECOVER()
#endif sun4c
/*
* We come here for auto-vectored interrupts
* We assume that the most recently interrupting
* chip is interrupting again and read the vector out of the
* chip and branch to the right routine. The "special receive"
* interrupt routine gets control if the assumption was wrong
* and performs the longer task of figuring out who is really interrupting
* and why.
*/
.global _zscurr
.global _zsNcurr
ENTRY(zslevel12)
save %sp, -SA(MINFRAME), %sp ! get a new window
set _zscurr, %g1 ! most active channel desc pointer
ld [%g1], %l0 ! read it
mov 2, %g2 ! register number 2 in channel B
ld [%l0 + ZS_ADDR], %l1 ! get address of hardware channel
stb %g2, [%l1] ! set register to read
ZSRECOVER();
ldub [%l1], %l2 ! get interrupt vector number
ZSRECOVER();
btst 8, %l2 ! channel A?
bnz,a 1f ! if not anull next instruction
sub %l0, ZSSIZE, %l0 ! point to channel A's descriptor
1:
set _zsNcurr, %g1 ! stash the [modified] channel
st %l0, [%g1] ! where it MIGHT be changed
and %l2, 6, %l2 ! get vetor bits
add %l2, %l2, %l2 ! get multiple of 4
ld [%l0 + %l2], %g1 ! get address of routine to call
call %g1 ! go to it
mov %l0, %o0 ! delay slot, fix argument
set _zsNcurr, %g1 ! restore the channel, since
ld [%g1], %l0 ! it might have been changed
ld [%l0 + ZS_ADDR], %l1 ! get address of hardware channel
mov ZSWR0_CLR_INTR, %g2 ! value to clear interrupt
stb %g2, [%l1] ! clear interrupt
ZSRECOVER();
ret
restore
/*
* Turn on a zs soft interrupt.
*/
ENTRY(setzssoft)
mov %psr, %g2
or %g2, PSR_PIL, %g1 ! spl hi to protect intreg update
mov %g1, %psr
nop; nop; ! psr delay
set INTREG_ADDR, %o0 ! set address of interrupt register
ldub [%o0], %o1 ! read it
bset IR_SOFT_INT6, %o1 ! set zs soft interrupt bit
stb %o1, [%o0] ! write it out
mov %g2, %psr ! restore psr
nop ! psr delay
retl ! leaf routine return
nop
/*
* Test and clear a zs soft interrupt.
*/
ENTRY(clrzssoft)
set INTREG_ADDR, %o1 ! set address of interrupt register
ldub [%o1], %o2 ! read it
btst IR_SOFT_INT6, %o2 ! was there an interrupt pending?
bz,a 1f ! no, simply return 0
mov 0, %o0 ! return value
mov %psr, %g2
or %g2, PSR_PIL, %g1 ! spl hi to protect intreg update
mov %g1, %psr
nop; nop; ! psr delay
mov 1, %o0 ! otherwise one was pending
ldub [%o1], %o2 ! read it
bclr IR_SOFT_INT6, %o2 ! clear zs soft int bit
stb %o2, [%o1] ! write interrupt register
mov %g2, %psr ! restore psr
nop ! psr delay
1:
retl
nop
/*
* Read a control register in the chip
*/
ENTRY(zszread)
save %sp, -SA(MINFRAME), %sp ! get a new window
stb %i1, [%i0] ! write register number
ZSRECOVER();
ldub [%i0], %i0 ! read register
ZSRECOVER();
ret
restore
/*
* Write a control register in the chip
*/
ENTRY(zszwrite)
save %sp, -SA(MINFRAME), %sp ! get a new window
stb %i1, [%i0] ! write register number
ZSRECOVER();
stb %i2, [%i0] ! write data
ZSRECOVER();
ret
restore