diff options
author | Kenji Aoyama <aoyama@cvs.openbsd.org> | 2004-04-21 15:24:16 +0000 |
---|---|---|
committer | Kenji Aoyama <aoyama@cvs.openbsd.org> | 2004-04-21 15:24:16 +0000 |
commit | fa26623fb9b8bea0664d8b457f0cb9117f8b650b (patch) | |
tree | 3abec74dfa27f8152e5f2d45ed4e404eac62be3c /sys/arch | |
parent | ea460b0cb47a034a8e87cb24dd8a38b9f83756e6 (diff) |
Initial commit for OpenBSD/luna88k, based on OpenBSD/mvme88k, NetBSD/luna68k and CMU Mach.
Diffstat (limited to 'sys/arch')
91 files changed, 33395 insertions, 0 deletions
diff --git a/sys/arch/luna88k/conf/GENERIC b/sys/arch/luna88k/conf/GENERIC new file mode 100644 index 00000000000..97100bc3e9f --- /dev/null +++ b/sys/arch/luna88k/conf/GENERIC @@ -0,0 +1,46 @@ +# $OpenBSD: GENERIC,v 1.1 2004/04/21 15:23:44 aoyama Exp $ + +machine luna88k + +include "../../../conf/GENERIC" + +option "NCPUS=1" # number of CPUs supported (max 4) +#option DEBUG # print debugging statements +#option EH_DEBUG # debugging code for exception handlers + +# Define this if your processor has the xxx.usr bug (mask C82N) +option ERRATA__XXX_USR + +maxusers 64 + +config bsd swap generic + +# +# devices +# + +mainbus0 at root +clock0 at mainbus0 +le0 at mainbus0 +sio0 at mainbus0 +siotty0 at sio0 +ws0 at sio0 +fb0 at mainbus0 +spc0 at mainbus0 +spc1 at mainbus0 + +# Workstation Console attachments +wsdisplay* at fb? +wskbd* at ws? console ? +wsmouse* at ws? + +# SCSI bus support +scsibus* at spc? + +# SCSI devices +sd* at scsibus? target ? lun ? +st* at scsibus? target ? lun ? +cd* at scsibus? target ? lun ? +ch* at scsibus? target ? lun ? +ss* at scsibus? target ? lun ? +uk* at scsibus? target ? lun ? diff --git a/sys/arch/luna88k/conf/Makefile.luna88k b/sys/arch/luna88k/conf/Makefile.luna88k new file mode 100644 index 00000000000..9c67c8fca69 --- /dev/null +++ b/sys/arch/luna88k/conf/Makefile.luna88k @@ -0,0 +1,206 @@ +# $OpenBSD: Makefile.luna88k,v 1.1 2004/04/21 15:23:47 aoyama Exp $ +# +# Makefile for OpenBSD +# +# This makefile is constructed from a machine description: +# config machineid +# Most changes should be made in the machine description +# /sys/arch/luna88k/conf/``machineid'' +# after which you should do +# config machineid +# Machine generic makefile changes should be made in +# /sys/arch/luna88k/conf/Makefile.luna88k +# after which config should be rerun for all machines of that type. +# +# N.B.: NO DEPENDENCIES ON FOLLOWING FLAGS ARE VISIBLE TO MAKEFILE +# IF YOU CHANGE THE DEFINITION OF ANY OF THESE RECOMPILE EVERYTHING +# +# -DTRACE compile in kernel tracing hooks +# -DQUOTA compile in file system quotas + +# DEBUG is set to -g by config if debugging is requested (config -g). +# PROF is set to -pg by config if profiling is requested (config -p). + +.include <bsd.own.mk> + +MKDEP?= mkdep +SIZE?= size +STRIP?= strip + +# source tree is located via $S relative to the compilation directory +.ifndef S +S!= cd ../../../..; pwd +.endif +LUNA88K= $S/arch/luna88k + +INCLUDES= -nostdinc -I. -I$S/arch -I$S +CPPFLAGS= ${INCLUDES} ${IDENT} ${PARAM} -D_KERNEL -Dluna88k -Dm88k +CWARNFLAGS= -Wall -Werror -Wmissing-prototypes \ + -Wno-uninitialized -Wno-format -Wno-main -Wstrict-prototypes +CMACHFLAGS= -mno-check-zero-division -mmemcpy \ + -fno-builtin-printf -fno-builtin-log +.if ${IDENT:M-DNO_PROPOLICE} +CMACHFLAGS+= -fno-stack-protector +.endif +COPTS?= -O2 +CFLAGS= ${DEBUG} ${CWARNFLAGS} ${CMACHFLAGS} ${COPTS} ${PIPE} +AFLAGS= -x assembler-with-cpp -traditional-cpp -D_LOCORE ${CMACHFLAGS} +LINKFLAGS= -N -Ttext 0x20000 -e start +STRIPFLAGS= -d + +HOSTCC?= ${CC} +HOSTED_CPPFLAGS?=${CPPFLAGS:S/^-nostdinc$//} +HOSTED_CFLAGS?= ${CFLAGS} + +### CPU configuration + +CPPFLAGS+= -DM88100 + +### find out what to use for libkern +.include "$S/lib/libkern/Makefile.inc" +.ifndef PROF +LIBKERN= ${KERNLIB} +.else +LIBKERN= ${KERNLIB_PROF} +.endif + +### find out what to use for libcompat +.include "$S/compat/common/Makefile.inc" +.ifndef PROF +LIBCOMPAT= ${COMPATLIB} +.else +LIBCOMPAT= ${COMPATLIB_PROF} +.endif + +# compile rules: rules are named ${TYPE}_${SUFFIX}${CONFIG_DEP} +# where TYPE is NORMAL, DRIVER, or PROFILE}; SUFFIX is the file suffix, +# capitalized (e.g. C for a .c file), and CONFIG_DEP is _C if the file +# is marked as config-dependent. + +NORMAL_C= ${CC} ${CFLAGS} ${CPPFLAGS} ${PROF} -c $< +NORMAL_S= ${CC} ${AFLAGS} ${CPPFLAGS} -c $< + +HOSTED_C= ${HOSTCC} ${HOSTED_CFLAGS} ${HOSTED_CPPFLAGS} -c $< + +PROFILE_C= ${CC} -S -c ${CFLAGS} ${CPPFLAGS} $<; \ + sed -e s/_mcount/mcount/ -e s/subrmcount/subr_mcount/ <$*.s | \ + ${AS} -o $@; \ + rm -f $*.s + +%OBJS + +%CFILES + +%SFILES + +# load lines for config "xxx" will be emitted as: +# xxx: ${SYSTEM_DEP} swapxxx.o +# ${SYSTEM_LD_HEAD} +# ${SYSTEM_LD} swapxxx.o +# ${SYSTEM_LD_TAIL} +# Kernel is linked as a ZMAGIC executable, with start at 10020 +SYSTEM_OBJ= locore.o \ + param.o ioconf.o ${OBJS} ${LIBKERN} ${LIBCOMPAT} +SYSTEM_DEP= Makefile ${SYSTEM_OBJ} +SYSTEM_LD_HEAD= @rm -f $@ +SYSTEM_LD= @echo ${LD} ${LINKFLAGS} -o $@ '$${SYSTEM_OBJ}' vers.o; \ + ${LD} ${LINKFLAGS} -o $@ ${SYSTEM_OBJ} vers.o +SYSTEM_LD_TAIL= @${SIZE} $@; chmod 755 $@; + +DEBUG?= +.if ${DEBUG} == "-g" +LINKFLAGS+= -X +SYSTEM_LD_TAIL+= \ + echo cp $@ $@.gdb; rm -f $@.gdb; cp $@ $@.gdb; \ + echo ${STRIP} ${STRIPFLAGS} $@; ${STRIP} ${STRIPFLAGS} $@ +.else +LINKFLAGS+= -x +.endif + +%LOAD + +assym.h: $S/kern/genassym.sh ${LUNA88K}/luna88k/genassym.cf Makefile + sh $S/kern/genassym.sh ${CC} ${CFLAGS} ${CPPFLAGS} \ + ${PARAM} < ${LUNA88K}/luna88k/genassym.cf > assym.h.tmp && \ + mv -f assym.h.tmp assym.h + +param.c: $S/conf/param.c + rm -f param.c + cp $S/conf/param.c . + +param.o: param.c Makefile + ${NORMAL_C} + +ioconf.o: ioconf.c + ${NORMAL_C} + +newvers: ${SYSTEM_DEP} ${SYSTEM_SWAP_DEP} + sh $S/conf/newvers.sh + ${CC} ${CFLAGS} ${CPPFLAGS} ${PROF} -c vers.c + +clean:: + rm -f eddep *bsd bsd.gdb tags *.[io] [a-z]*.s \ + [Ee]rrs linterrs makelinks assym.h + +lint: + @lint -hbxncez -DGENERIC -Dvolatile= ${CPPFLAGS} ${PARAM} -UKGDB \ + ${CFILES} ioconf.c param.c | \ + grep -v 'struct/union .* never defined' | \ + grep -v 'possible pointer alignment problem' + +tags: + @echo "see $S/kern/Makefile for tags" + +links: + egrep '#if' ${CFILES} | sed -f $S/conf/defines | \ + sed -e 's/:.*//' -e 's/\.c/.o/' | sort -u > dontlink + echo ${CFILES} | tr -s ' ' '\12' | sed 's/\.c/.o/' | \ + sort -u | comm -23 - dontlink | \ + sed 's,../.*/\(.*.o\),rm -f \1;ln -s ../GENERIC/\1 \1,' > makelinks + sh makelinks && rm -f dontlink + +SRCS= param.c ioconf.c ${CFILES} ${SFILES} + +depend:: .depend +.depend: ${SRCS} assym.h param.c + ${MKDEP} -a ${CFLAGS} ${CPPFLAGS} param.c ioconf.c ${CFILES} +.if !empty(SFILES) + ${MKDEP} -a ${AFLAGS} ${CPPFLAGS} ${SFILES} +.endif + sh $S/kern/genassym.sh ${MKDEP} -f assym.dep ${CFLAGS} \ + ${CPPFLAGS} < ${LUNA88K}/luna88k/genassym.cf + @sed -e 's/.*\.o:.* /assym.h: /' < assym.dep >> .depend + @rm -f assym.dep + +# depend on root or device configuration +autoconf.o conf.o: Makefile + +# depend on network or filesystem configuration +uipc_domain.o uipc_proto.o vfs_conf.o: Makefile +if_tun.o if_loop.o if_ethersubr.o: Makefile +in_proto.o: Makefile + +# depend on maxusers +assym.h machdep.o: Makefile + +# depend on CPU configuration +locore.o machdep.o: Makefile +process.o locore_asm_routines.o eh.o: assym.h +m88100_fp.o : assym.h + +locore.o: ${LUNA88K}/luna88k/locore.S assym.h + ${NORMAL_S} + +# The install target can be redefined by putting a +# install-kernel-${MACHINE_NAME} target into /etc/mk.conf +MACHINE_NAME!= uname -n +install: install-kernel-${MACHINE_NAME} +.if !target(install-kernel-${MACHINE_NAME}}) +install-kernel-${MACHINE_NAME}: + rm -f /obsd + ln /bsd /obsd + cp bsd /nbsd + mv /nbsd /bsd +.endif + +%RULES diff --git a/sys/arch/luna88k/conf/files.luna88k b/sys/arch/luna88k/conf/files.luna88k new file mode 100644 index 00000000000..b60d9268090 --- /dev/null +++ b/sys/arch/luna88k/conf/files.luna88k @@ -0,0 +1,101 @@ +# $OpenBSD: files.luna88k,v 1.1 2004/04/21 15:23:47 aoyama Exp $ +# +maxpartitions 16 + +device mainbus {} +attach mainbus at root +file arch/luna88k/luna88k/mainbus.c + +device clock +attach clock at mainbus +file arch/luna88k/dev/timekeeper.c clock + +attach le at mainbus +file arch/luna88k/dev/if_le.c le + +device sio { [channel = -1] } +attach sio at mainbus +file arch/luna88k/dev/sio.c sio + +# this should be removed after bringup +#device romtty: tty +#attach romtty at mainbus +#file arch/luna88k/dev/romtty.c romtty needs-flag + +device siotty: tty +attach siotty at sio +file arch/luna88k/dev/siotty.c siotty needs-flag + +device ws: wskbddev,wsmousedev +attach ws at sio +file arch/luna88k/dev/lunaws.c ws + +device fb: wsemuldisplaydev,wsrasteremulops +attach fb at mainbus +file arch/luna88k/dev/lunafb.c fb +file arch/luna88k/dev/omrasops.c fb + +# Raster operations +include "dev/rasops/files.rasops" +include "dev/wsfont/files.wsfont" +# "Workstation Console" glue. +include "dev/wscons/files.wscons" + +device cpu +attach cpu at mainbus + +include "../../../scsi/files.scsi" + +major { sd = 4 } +major { st = 5 } +major { cd = 6 } +major { rd = 7 } +major { vnd = 8 } + +device spc: scsi +attach spc at mainbus +file arch/luna88k/dev/spc.c spc +file arch/luna88k/dev/mb89352.c spc + +# XXX: now testing +#device pcm: audio, auconv, mulaw +#attach pcm at mainbus +#file arch/luna88k/dev/nec86.c pcm needs-flag +#file arch/luna88k/dev/nec86hw.c pcm needs-flag +#file arch/luna88k/dev/nec86_luna88k.c pcm needs-flag + +# list of standard files +file dev/cons.c +file dev/cninit.c +file dev/clock_subr.c + +file netinet/in_cksum.c +file netns/ns_cksum.c ns + +file arch/luna88k/luna88k/clock.c + +file arch/luna88k/luna88k/autoconf.c +file arch/luna88k/luna88k/conf.c +file arch/luna88k/luna88k/cmmu.c +file arch/luna88k/luna88k/m8820x.c +file arch/luna88k/luna88k/disksubr.c +#file arch/luna88k/luna88k/dkbad.c +file arch/luna88k/luna88k/eh.S +file arch/luna88k/luna88k/isr.c +file arch/luna88k/luna88k/locore_asm_routines.S +file arch/luna88k/luna88k/locore_c_routines.c +file arch/luna88k/luna88k/m88100_fp.S +file arch/luna88k/luna88k/machdep.c +file arch/luna88k/luna88k/mem.c +file arch/luna88k/luna88k/pmap.c +file arch/luna88k/luna88k/pmap_table.c +file arch/luna88k/luna88k/process.S +file arch/luna88k/luna88k/process_machdep.c +file arch/luna88k/luna88k/trap.c +file arch/luna88k/luna88k/vectors_88100.S +file arch/luna88k/luna88k/vm_machdep.c +file arch/luna88k/ddb/db_disasm.c ddb +file arch/luna88k/ddb/db_interface.c ddb +file arch/luna88k/ddb/db_sstep.c ddb +file arch/luna88k/ddb/db_trace.c ddb +file arch/luna88k/dev/lcd.c diff --git a/sys/arch/luna88k/ddb/db_disasm.c b/sys/arch/luna88k/ddb/db_disasm.c new file mode 100644 index 00000000000..feef3d4afe7 --- /dev/null +++ b/sys/arch/luna88k/ddb/db_disasm.c @@ -0,0 +1,689 @@ +/* $OpenBSD: db_disasm.c,v 1.1 2004/04/21 15:23:49 aoyama Exp $ */ +/* + * Mach Operating System + * Copyright (c) 1993-1991 Carnegie Mellon University + * Copyright (c) 1991 OMRON Corporation + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON AND OMRON ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON AND OMRON DISCLAIM ANY LIABILITY OF ANY KIND + * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie the + * rights to redistribute these changes. + */ + +/* + * m88k disassembler for use in ddb + */ + +#include <sys/param.h> /* cputyp and friends */ +#include <sys/types.h> + +#include <machine/db_machdep.h> + +#include <ddb/db_sym.h> /* DB_STGY_PROC, db_printsym() */ +#include <ddb/db_access.h> /* db_get_value() */ +#include <ddb/db_output.h> /* db_printf() */ +#include <ddb/db_interface.h> + +static const char *instwidth[4] = { + ".d", " ", ".h", ".b" +}; + +static const char *condname[6] = { + "gt0 ", "eq0 ", "ge0 ", "lt0 ", "ne0 ", "le0 " +}; + +static const char *m88100_ctrlreg[64] = { + "cr0(PID) ", + "cr1(PSR) ", + "cr2(EPSR) ", + "cr3(SSBR) ", + "cr4(SXIP) ", + "cr5(SNIP) ", + "cr6(SFIP) ", + "cr7(VBR) ", + "cr8(DMT0) ", + "cr9(DMD0) ", + "cr10(DMA0) ", + "cr11(DMT1) ", + "cr12(DMD1) ", + "cr13(DMA1) ", + "cr14(DMT2) ", + "cr15(DMD2) ", + "cr16(DMA2) ", + "cr17(SR0) ", + "cr18(SR1) ", + "cr19(SR2) ", + "cr20(SR3) ", + "fcr0(FPECR)", + "fcr1(FPHS1)", + "fcr2(FPLS1)", + "fcr3(FPHS2)", + "fcr4(FPLS2)", + "fcr5(FPPT) ", + "fcr6(FPRH) ", + "fcr7(FPRL) ", + "fcr8(FPIT) ", + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + "fcr62(FPSR)", + "fcr63(FPCR)" +}; + +static const char *m88110_ctrlreg[64] = { + "cr0(PID) ", + "cr1(PSR) ", + "cr2(EPSR) ", + NULL, + "cr4(EXIP) ", + "cr5(ENIP) ", + NULL, + "cr7(VBR) ", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + "cr14(RES1) ", + "cr15(RES2) ", + "cr16(SR0) ", + "cr17(SR1) ", + "cr18(SR2) ", + "cr19(SR3) ", + "cr20(SR4) ", + "fcr0(FPECR)", + NULL, + NULL, + NULL, + "cr25(ICMD) ", + "cr26(ICTL) ", + "cr27(ISAR) ", + "cr28(ISAP) ", + "cr29(IUAP) ", + "cr30(IIR) ", + "cr31(IBP) ", + "cr32(IPPU) ", + "cr33(IPPL) ", + "cr34(ISR) ", + "cr35(ILAR) ", + "cr36(IPAR) ", + NULL, + NULL, + NULL, + "cr40(DCMD) ", + "cr41(DCTL) ", + "cr42(DSAR) ", + "cr43(DSAP) ", + "cr44(DUAP) ", + "cr45(DIR) ", + "cr46(DBP) ", + "cr47(DPPU) ", + "cr48(DPPL) ", + "cr49(DSR) ", + "cr50(DLAR) ", + "cr51(DPAR) ", + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, + "fcr62(FPSR)", + "fcr63(FPCR)" +}; + +#define printval(x) \ + do { \ + if ((x) < 0) \ + db_printf("-0x%X", -(x)); \ + else \ + db_printf("0x%X", (x)); \ + } while (0) + +/* prototypes */ +void oimmed(int, const char *, long); +void ctrlregs(int, const char *, long); +void printsod(int); +void sindou(int, const char *, long); +void jump(int, const char *, long); +void instset(int, const char *, long); +void symofset(int, int, int); +void obranch(int, const char *, long); +void brcond(int, const char *, long); +void otrap(int, const char *, long); +void obit(int, const char *, long); +void bitman(int, const char *, long); +void immem(int, const char *, long); +void nimmem(int, const char *, long); +void lognim(int, const char *, long); +void onimmed(int, const char *, long); + +/* Handlers immediate integer arithmetic instructions */ +void +oimmed(int inst, const char *opcode, long iadr) +{ + int Linst = inst & 0177777; + int Hinst = inst >> 16; + int H6inst = Hinst >> 10; + int rs1 = Hinst & 037; + int rd = ( Hinst >> 5 ) & 037; + + if (( H6inst > 017 ) && ( H6inst < 030 ) && ( H6inst & 01) == 1 ) + db_printf("\t%s.u",opcode); + else { + db_printf("\t%s",opcode); + db_printf(" "); + } + db_printf("\t\tr%-3d,r%-3d,", rd, rs1); + printval(Linst); +} + + +/* Handles instructions dealing with control registers */ +void +ctrlregs(int inst, const char *opcode, long iadr) +{ + int L6inst = (inst >> 11) & 037; + int creg = (inst >> 5) & 077; + int rd = (inst >> 21) & 037; + int rs1 = (inst >> 16) & 037; + + db_printf("\t%s",opcode); + + if ( L6inst == 010 || L6inst == 011 ) + db_printf("\t\tr%-3d,%s", rd, + cputyp == CPU_88100 ? m88100_ctrlreg[creg] : m88110_ctrlreg[creg]); + else if ( L6inst == 020 || L6inst == 021 ) + db_printf("\t\tr%-3d,%s", rs1, + cputyp == CPU_88100 ? m88100_ctrlreg[creg] : m88110_ctrlreg[creg]); + else + db_printf("\t\tr%-3d,r%-3d,%s", rd, rs1, + cputyp == CPU_88100 ? m88100_ctrlreg[creg] : m88110_ctrlreg[creg]); +} + + +void +printsod(int t) +{ + if ( t == 0 ) + db_printf("s"); + else + db_printf("d"); +} + +/* Handles floating point instructions */ +void +sindou(int inst, const char *opcode, long iadr) +{ + int rs2 = inst & 037; + int td = ( inst >> 5 ) & 03; + int t2 = ( inst >> 7 ) & 03; + int t1 = ( inst >> 9 ) & 03; + int rs1 = ( inst >> 16 ) & 037; + int rd = ( inst >> 21 ) & 037; + int checkbits = ( inst >> 11 ) & 037; + + db_printf("\t%s.",opcode); + printsod(td); + if (( checkbits > 010 && checkbits < 014 ) || ( checkbits == 04 )) { + printsod(t2); + db_printf(" "); + if ( checkbits == 012 || checkbits == 013 ) + db_printf("\t\tr%-3d,r%-3d", rd, rs2); + else + db_printf("\t\tr%-3d,r%-3d", rd, rs2); + } else { + printsod(t1);printsod(t2); + db_printf("\t\tr%-3d,r%-3d,r%-3d", rd, rs1, rs2); + } +} + + +void +jump(int inst, const char *opcode, long iadr) +{ + int rs2 = inst & 037; + int Nbit = ( inst >> 10 ) & 01; + + db_printf("\t%s",opcode); + if ( Nbit == 1 ) + db_printf(".n"); + else + db_printf(" "); + db_printf("\t\tr%-3d",rs2); +} + + +/* Handles ff1, ff0, tbnd and rte instructions */ +void +instset(int inst, const char *opcode, long iadr) +{ + int rs2 = inst & 037; + int rs1 = ( inst >> 16 ) & 037; + int rd = ( inst >> 21 ) & 037; + int checkbits = ( inst >> 10 ) & 077; + int H6inst = ( inst >> 26 ) & 077; + + db_printf("\t%s",opcode); + if ( H6inst == 076 ) { + db_printf("\t\tr%-3d,",rs1); + printval(inst & 0177777); + } else if (( checkbits == 072 ) || ( checkbits == 073 )) + db_printf("\t\tr%-3d,r%-3d", rd, rs2); + else if ( checkbits == 076 ) + db_printf("\t\tr%-3d,r%-3d",rs1,rs2); +} + +void +symofset(int disp, int bit, int iadr) +{ + long addr; + + if ( disp & (1 << (bit-1)) ) { + /* negative value */ + addr = iadr + ((disp << 2) | (~0 << bit)); + } else { + addr = iadr + (disp << 2); + } + db_printsym(addr,DB_STGY_PROC, db_printf); + return; +} + +void +obranch(int inst, const char *opcode, long iadr) +{ + int cond = ( inst >> 26 ) & 01; + int disp = inst &0377777777; + + if ( cond == 0 ) { + db_printf("\t%s\t\t",opcode); + symofset(disp, 26, iadr); + } else { + db_printf("\t%s.n\t\t",opcode); + symofset(disp, 26, iadr); + } +} + + +/* Handles branch on conditions instructions */ +void +brcond(int inst, const char *opcode, long iadr) +{ + int cond = ( inst >> 26 ) & 1; + int match = ( inst >> 21 ) & 037; + int rs = ( inst >> 16 ) & 037; + int disp = ( inst & 0177777 ); + + if ( cond == 0 ) + db_printf("\t%s\t\t", opcode); + else + db_printf("\t%s.n\t\t", opcode); + if ( ( ( inst >> 27 ) & 03 ) == 1 ) + switch (match) { + case 1 : db_printf("%s,", condname[0]); break; + case 2 : db_printf("%s,", condname[1]); break; + case 3 : db_printf("%s,", condname[2]); break; + case 12: db_printf("%s,", condname[3]); break; + case 13: db_printf("%s,", condname[4]); break; + case 14: db_printf("%s,", condname[5]); break; + default: printval(match); + db_printf(","); + } else { + printval(match); + db_printf(","); + } + + db_printf("r%-3d,", rs); + symofset(disp, 16, iadr); +} + + +void +otrap(int inst, const char *opcode, long iadr) +{ + int vecno = inst & 0777; + int match = ( inst >> 21 ) & 037; + int rs = ( inst >> 16 ) & 037; + + db_printf("\t%s\t",opcode); + if ( ( ( inst >> 12 ) & 017 ) == 0xe ) + switch (match) { + case 1 : db_printf("%s,", condname[0]);break; + case 2 : db_printf("%s,", condname[1]);break; + case 3 : db_printf("%s,", condname[2]);break; + case 12: db_printf("%s,", condname[3]);break; + case 13: db_printf("%s,", condname[4]);break; + case 14: db_printf("%s,", condname[5]);break; + default: printval(match); + db_printf(","); + } else { + printval(match); + db_printf(","); + } + db_printf("\tr%-3d,", rs); + printval(vecno); +} + + +/* Handles 10 bit immediate bit field operations */ +void +obit(int inst, const char *opcode, long iadr) +{ + int rs = ( inst >> 16 ) & 037; + int rd = ( inst >> 21 ) & 037; + int width = ( inst >> 5 ) & 037; + int offset = ( inst & 037 ); + + db_printf("\t%s\t\tr%-3d,r%-3d,", opcode, rd, rs); + if ( ( ( inst >> 10 ) & 077 ) == 052 ) { + db_printf("<"); + printval(offset); + db_printf(">"); + } else { + printval(width); + db_printf("<"); + printval(offset); + db_printf(">"); + } +} + + +/* Handles triadic mode bit field instructions */ +void +bitman(int inst, const char *opcode, long iadr) +{ + + int rs1 = ( inst >> 16 ) & 037; + int rd = ( inst >> 21 ) & 037; + int rs2 = inst & 037; + + db_printf("\t%s\t\tr%-3d,r%-3d,r%-3d", opcode, rd, rs1, rs2); +} + + +/* Handles immediate load/store/exchange instructions */ +void +immem(int inst, const char *opcode, long iadr) +{ + int immed = inst & 0xFFFF; + int rd = (inst >> 21) & 037; + int rs = (inst >> 16) & 037; + int st_lda = (inst >> 28) & 03; + int aryno = (inst >> 26) & 03; + char c = ' '; + + if (!st_lda) { + if ((aryno == 0) || (aryno == 01)) + opcode = "xmem"; + else + opcode = "ld"; + if (aryno == 0) + aryno = 03; + if (!(aryno == 01)) + c = 'u'; + } else + if (st_lda == 01) + opcode = "ld"; + + db_printf("\t%s%s%c\t\tr%-3d,r%-3d,", opcode, instwidth[aryno], + c, rd, rs); + printval(immed); +} + + +/* Handles triadic mode load/store/exchange instructions */ +void +nimmem(int inst, const char *opcode, long iadr) +{ + int scaled = (inst >> 9) & 01; + int rd = (inst >> 21) & 037; + int rs1 = (inst >> 16) & 037; + int rs2 = inst & 037; + int st_lda = (inst >> 12) & 03; + int aryno = (inst >> 10) & 03; + int user_bit = 0; + int signed_fg = 1; + char *user = " "; + char c = ' '; + + if (!st_lda) { + if ((aryno == 0) || (aryno == 01)) + opcode = "xmem"; + else + opcode = "ld"; + if (aryno == 0) + aryno = 03; + if (!(aryno == 01)) { + c = 'u'; + signed_fg = 0; + } + } else + if (st_lda == 01) + opcode = "ld"; + + if (!(st_lda == 03)) { + user_bit = (inst >> 8) & 01; + if (user_bit) + user = ".usr"; + } + + if (user_bit && signed_fg && (aryno == 01)) { + if (st_lda) + db_printf("\t%s%s\tr%-3d,r%-3d", opcode, + user, rd, rs1); + else + db_printf("\t%s%s\tr%-3d,r%-3d", opcode, + user, rd, rs1); + } else + if (user_bit && signed_fg) + db_printf("\t%s%s%s\tr%-3d,r%-3d", opcode, + instwidth[aryno], user, rd, rs1); + else + db_printf("\t%s%s%c%s\tr%-3d,r%-3d", opcode, + instwidth[aryno], c, user, rd, rs1); + + if (scaled) + db_printf("[r%-3d]", rs2); + else + db_printf(",r%-3d", rs2); +} + + +/* Handles triadic mode logical instructions */ +void +lognim(int inst, const char *opcode, long iadr) +{ + int rd = (inst >> 21) & 037; + int rs1 = (inst >> 16) & 037; + int rs2 = inst & 037; + int complemt = (inst >> 10) & 01; + char *c = " "; + + if (complemt) + c = ".c"; + + db_printf("\t%s%s\t\tr%-3d,r%-3d,r%-3d", opcode, c, rd, rs1, rs2); +} + + +/* Handles triadic mode arithmetic instructions */ +void +onimmed(int inst, const char *opcode, long iadr) +{ + int rd = (inst >> 21) & 037; + int rs1 = (inst >> 16) & 037; + int rs2 = inst & 037; + int carry = (inst >> 8) & 03; + int nochar = (inst >> 10) & 07; + int nodecode = (inst >> 11) & 01; + char *tab, *c ; + + if (nochar > 02) + tab = "\t\t"; + else + tab = "\t"; + + if (!nodecode) { + if (carry == 01) + c = ".co "; + else + if (carry == 02) + c = ".ci "; + else + if (carry == 03) + c = ".cio"; + else + c = " "; + } else + c = " "; + + db_printf("\t%s%s%sr%-3d,r%-3d,r%-3d", opcode, c, + tab, rd, rs1, rs2); +} + +static const struct opdesc { + unsigned mask, match; + void (*opfun)(int, const char *, long); + const char *farg; +} opdecode[] = { + + /* ORDER IS IMPORTANT BELOW */ + + { 0xF0000000U, 0x00000000U, immem, NULL}, + { 0xF0000000U, 0x10000000U, immem, NULL}, + { 0xF0000000U, 0x20000000U, immem, "st"}, + { 0xF0000000U, 0x30000000U, immem, "lda"}, + + { 0xF8000000U, 0x40000000U, oimmed, "and"}, + { 0xF8000000U, 0x48000000U, oimmed, "mask"}, + { 0xF8000000U, 0x50000000U, oimmed, "xor"}, + { 0xF8000000U, 0x58000000U, oimmed, "or"}, + { 0xFC000000U, 0x60000000U, oimmed, "addu"}, + { 0xFC000000U, 0x64000000U, oimmed, "subu"}, + { 0xFC000000U, 0x68000000U, oimmed, "divu"}, + { 0xFC000000U, 0x6C000000U, oimmed, "mul"}, + { 0xFC000000U, 0x70000000U, oimmed, "add"}, + { 0xFC000000U, 0x74000000U, oimmed, "sub"}, + { 0xFC000000U, 0x78000000U, oimmed, "div"}, + { 0xFC000000U, 0x7C000000U, oimmed, "cmp"}, + + { 0xFC00F800U, 0x80004000U, ctrlregs, "ldcr"}, + { 0xFC00F800U, 0x80004800U, ctrlregs, "fldcr"}, + { 0xFC00F800U, 0x80008000U, ctrlregs, "stcr"}, + { 0xFC00F800U, 0x80008800U, ctrlregs, "fstcr"}, + { 0xFC00F800U, 0x8000C000U, ctrlregs, "xcr"}, + { 0xFC00F800U, 0x8000C800U, ctrlregs, "fxcr"}, + + { 0xFC00F800U, 0x84000000U, sindou, "fmul"}, + { 0xFC1FFF80U, 0x84002000U, sindou, "flt"}, + { 0xFC00F800U, 0x84002800U, sindou, "fadd"}, + { 0xFC00F800U, 0x84003000U, sindou, "fsub"}, + { 0xFC00F860U, 0x84003800U, sindou, "fcmp"}, + { 0xFC1FFE60U, 0x84004800U, sindou, "int"}, + { 0xFC1FFE60U, 0x84005000U, sindou, "nint"}, + { 0xFC1FFE60U, 0x84005800U, sindou, "trnc"}, + { 0xFC00F800U, 0x84007000U, sindou, "fdiv"}, + + { 0xF8000000U, 0xC0000000U, obranch, "br"}, + { 0xF8000000U, 0xC8000000U, obranch, "bsr"}, + + { 0xF8000000U, 0xD0000000U, brcond, "bb0"}, + { 0xF8000000U, 0xD8000000U, brcond, "bb1"}, + { 0xF8000000U, 0xE8000000U, brcond, "bcnd"}, + + { 0xFC00FC00U, 0xF0008000U, obit, "clr"}, + { 0xFC00FC00U, 0xF0008800U, obit, "set"}, + { 0xFC00FC00U, 0xF0009000U, obit, "ext"}, + { 0xFC00FC00U, 0xF0009800U, obit, "extu"}, + { 0xFC00FC00U, 0xF000A000U, obit, "mak"}, + { 0xFC00FC00U, 0xF000A800U, obit, "rot"}, + + { 0xFC00FE00U, 0xF000D000U, otrap, "tb0"}, + { 0xFC00FE00U, 0xF000D800U, otrap, "tb1"}, + { 0xFC00FE00U, 0xF000E800U, otrap, "tcnd"}, + + { 0xFC00F2E0U, 0xF4000000U, nimmem, NULL}, + { 0xFC00F2E0U, 0xF4000200U, nimmem, NULL}, + { 0xFC00F2E0U, 0xF4001000U, nimmem, NULL}, + { 0xFC00F2E0U, 0xF4001200U, nimmem, NULL}, + { 0xFC00F2E0U, 0xF4002000U, nimmem, "st"}, + { 0xFC00F2E0U, 0xF4002200U, nimmem, "st"}, + { 0xFC00F2E0U, 0xF4003000U, nimmem, "lda"}, + { 0xFC00F2E0U, 0xF4003200U, nimmem, "lda"}, + + { 0xFC00FBE0U, 0xF4004000U, lognim, "and"}, + { 0xFC00FBE0U, 0xF4005000U, lognim, "xor"}, + { 0xFC00FBE0U, 0xF4005800U, lognim, "or"}, + + { 0xFC00FCE0U, 0xF4006000U, onimmed, "addu"}, + { 0xFC00FCE0U, 0xF4006400U, onimmed, "subu"}, + { 0xFC00FCE0U, 0xF4006800U, onimmed, "divu"}, + { 0xFC00FCE0U, 0xF4006C00U, onimmed, "mul"}, + { 0xFC00FCE0U, 0xF4007000U, onimmed, "add"}, + { 0xFC00FCE0U, 0xF4007400U, onimmed, "sub"}, + { 0xFC00FCE0U, 0xF4007800U, onimmed, "div"}, + { 0xFC00FCE0U, 0xF4007C00U, onimmed, "cmp"}, + + { 0xFC00FFE0U, 0xF4008000U, bitman, "clr"}, + { 0xFC00FFE0U, 0xF4008800U, bitman, "set"}, + { 0xFC00FFE0U, 0xF4009000U, bitman, "ext"}, + { 0xFC00FFE0U, 0xF4009800U, bitman, "extu"}, + { 0xFC00FFE0U, 0xF400A000U, bitman, "mak"}, + { 0xFC00FFE0U, 0xF400A800U, bitman, "rot"}, + + { 0xFC00FBE0U, 0xF400C000U, jump, "jmp"}, + { 0xFC00FBE0U, 0xF400C800U, jump, "jsr"}, + + { 0xFC00FFE0U, 0xF400E800U, instset, "ff1"}, + { 0xFC00FFE0U, 0xF400EC00U, instset, "ff0"}, + { 0xFC00FFE0U, 0xF400F800U, instset, "tbnd"}, + { 0xFC00FFE0U, 0xF400FC00U, instset, "rte"}, + { 0xFC000000U, 0xF8000000U, instset, "tbnd"}, + { 0, 0, NULL, NULL} +}; + +static const char *badop = "\t???"; + +int +m88k_print_instruction(unsigned iadr, long inst) +{ + const struct opdesc *p; + + /* this messes up "orb" instructions ever so slightly, */ + /* but keeps us in sync between routines... */ + if (inst == 0) { + db_printf ("\t.word 0"); + } else { + for (p = opdecode; p->mask; p++) + if ((inst & p->mask) == p->match) { + (*p->opfun) (inst, p->farg, iadr); + break; + } + if (!p->mask) + db_printf (badop); + } + + return iadr+4; +} + +db_addr_t +db_disasm(db_addr_t loc, boolean_t altfmt) +{ + m88k_print_instruction(loc, db_get_value(loc, 4, FALSE)); + db_printf ("\n"); + return loc+4; +} diff --git a/sys/arch/luna88k/ddb/db_interface.c b/sys/arch/luna88k/ddb/db_interface.c new file mode 100644 index 00000000000..f08df609f78 --- /dev/null +++ b/sys/arch/luna88k/ddb/db_interface.c @@ -0,0 +1,827 @@ +/* $OpenBSD: db_interface.c,v 1.1 2004/04/21 15:23:50 aoyama Exp $ */ +/* + * Mach Operating System + * Copyright (c) 1993-1991 Carnegie Mellon University + * Copyright (c) 1991 OMRON Corporation + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON AND OMRON ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON AND OMRON DISCLAIM ANY LIABILITY OF ANY KIND + * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie the + * rights to redistribute these changes. + */ + +/* + * m88k interface to ddb debugger + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/proc.h> +#include <sys/reboot.h> + +#include <uvm/uvm_extern.h> + +#include <machine/asm_macro.h> /* flush_pipeline() */ +#include <machine/cmmu.h> /* CMMU defs */ +#include <machine/trap.h> /* current_thread() */ +#include <machine/db_machdep.h> /* local ddb stuff */ +#include <machine/locore.h> +#include <machine/cpu_number.h> +#ifdef M88100 +#include <machine/m88100.h> +#include <machine/m8820x.h> +#endif + +#include <ddb/db_access.h> +#include <ddb/db_command.h> +#include <ddb/db_extern.h> +#include <ddb/db_interface.h> +#include <ddb/db_output.h> +#include <ddb/db_sym.h> + +extern label_t *db_recover; +extern unsigned db_trace_get_val(vaddr_t, unsigned *); +extern int frame_is_sane(db_regs_t *, int); +extern void cnpollc(int); +void kdbprinttrap(int, int); + +void m88k_db_trap(int, struct trapframe *); +int ddb_nmi_trap(int, db_regs_t *); +void ddb_error_trap(char *, db_regs_t *); +void db_putc(int); +int db_getc(void); +int m88k_dmx_print(unsigned, unsigned, unsigned, unsigned); +void m88k_db_pause(unsigned); +void m88k_db_print_frame(db_expr_t, int, db_expr_t, char *); +void m88k_db_registers(db_expr_t, int, db_expr_t, char *); +void m88k_db_where(db_expr_t, int, db_expr_t, char *); +void m88k_db_frame_search(db_expr_t, int, db_expr_t, char *); +void m88k_db_iflush(db_expr_t, int, db_expr_t, char *); +void m88k_db_dflush(db_expr_t, int, db_expr_t, char *); +void m88k_db_peek(db_expr_t, int, db_expr_t, char *); +void m88k_db_noise(db_expr_t, int, db_expr_t, char *); +void m88k_db_translate(db_expr_t, int, db_expr_t, char *); +void m88k_db_cmmucfg(db_expr_t, int, db_expr_t, char *); +void m88k_db_prom_cmd(db_expr_t, int, db_expr_t, char *); + +int db_active; +int db_noisy; + +db_regs_t ddb_regs; + +/* + * + * If you really feel like understanding the following procedure and + * macros, see pages 6-22 to 6-30 (Section 6.7.3) of + * + * MC88100 RISC Microprocessor User's Manual Second Edition + * (Motorola Order: MC88100UM/AD REV 1) + * + * and ERRATA-5 (6-23, 6-24, 6-24) of + * + * Errata to MC88100 User's Manual Second Edition MC88100UM/AD Rev 1 + * (Oct 2, 1990) + * (Motorola Order: MC88100UMAD/AD) + * + */ + +#ifdef M88100 +/* macros for decoding dmt registers */ + +/* + * return 1 if the printing of the next stage should be suppressed + */ +int +m88k_dmx_print(t, d, a, no) + unsigned t, d, a, no; +{ + static const unsigned addr_mod[16] = { + 0, 3, 2, 2, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 + }; + static const char *mode[16] = { + "?", ".b", ".b", ".h", ".b", "?", "?", "?", + ".b", "?", "?" , "?" , ".h" , "?", "?", "" + }; + static const unsigned mask[16] = { + 0, 0xff, 0xff00, 0xffff, + 0xff0000, 0, 0, 0, + 0xff000000, 0, 0, 0, + 0xffff0000, 0, 0, 0xffffffff + }; + static const unsigned shift[16] = { + 0, 0, 8, 0, 16, 0, 0, 0, + 24, 0, 0, 0, 16, 0, 0, 0 + }; + int reg = DMT_DREGBITS(t); + + if (ISSET(t, DMT_LOCKBAR)) { + db_printf("xmem%s%s r%d(0x%x) <-> mem(0x%x),", + DMT_ENBITS(t) == 0x0f ? "" : ".bu", ISSET(t, DMT_DAS) ? "" : ".usr", reg, + (((t)>>2 & 0xf) == 0xf) ? d : (d & 0xff), a); + return 1; + } else { + if (DMT_ENBITS(t) == 0xf) { + /* full or double word */ + if (ISSET(t, DMT_WRITE)) { + if (ISSET(t, DMT_DOUB1) && no == 2) + db_printf("st.d%s -> mem(0x%x) (** restart sxip **)", + ISSET(t, DMT_DAS) ? "" : ".usr", a); + else + db_printf("st%s (0x%x) -> mem(0x%x)", + ISSET(t, DMT_DAS) ? "" : ".usr", d, a); + } else { + /* load */ + if (ISSET(t, DMT_DOUB1) && no == 2) + db_printf("ld.d%s r%d <- mem(0x%x), r%d <- mem(0x%x)", + ISSET(t, DMT_DAS) ? "" : ".usr", reg, a, reg+1, a+4); + else + db_printf("ld%s r%d <- mem(0x%x)", + ISSET(t, DMT_DAS) ? "" : ".usr", reg, a); + } + } else { + /* fractional word - check if load or store */ + a += addr_mod[DMT_ENBITS(t)]; + if (ISSET(t, DMT_WRITE)) + db_printf("st%s%s (0x%x) -> mem(0x%x)", + mode[DMT_ENBITS(t)], ISSET(t, DMT_DAS) ? "" : ".usr", + (d & mask[DMT_ENBITS(t)]) >> shift[DMT_ENBITS(t)], a); + else + db_printf("ld%s%s%s r%d <- mem(0x%x)", + mode[DMT_ENBITS(t)], + ISSET(t, DMT_SIGNED) ? "" : "u", + ISSET(t, DMT_DAS) ? "" : ".usr", reg, a); + } + } + return 0; +} +#endif /* M88100 */ + +void +m88k_db_print_frame(addr, have_addr, count, modif) + db_expr_t addr; + int have_addr; + db_expr_t count; + char *modif; +{ + struct trapframe *s = (struct trapframe *)addr; + char *name; + db_expr_t offset; +#ifdef M88100 + int suppress1 = 0, suppress2 = 0; +#endif + int c, force = 0, help = 0; + + if (!have_addr) { + db_printf("requires address of frame\n"); + help = 1; + } + + while (modif && *modif) { + switch (c = *modif++, c) { + case 'f': + force = 1; + break; + case 'h': + help = 1; + break; + default: + db_printf("unknown modifier [%c]\n", c); + help = 1; + break; + } + } + + if (help) { + db_printf("usage: mach frame/[f] ADDRESS\n"); + db_printf(" /f force printing of insane frames.\n"); + return; + } + + if (badwordaddr((vaddr_t)s) || + badwordaddr((vaddr_t)(&((db_regs_t*)s)->fpit))) { + db_printf("frame at %8p is unreadable\n", s); + return; + } + + if (frame_is_sane((db_regs_t *)s, 0) == 0) { /* see db_trace.c */ + if (force == 0) + return; + } + +#define R(i) s->tf_r[i] +#define IPMASK(x) ((x) & ~(3)) + db_printf("R00-05: 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", + R(0), R(1), R(2), R(3), R(4), R(5)); + db_printf("R06-11: 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", + R(6), R(7), R(8), R(9), R(10), R(11)); + db_printf("R12-17: 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", + R(12), R(13), R(14), R(15), R(16), R(17)); + db_printf("R18-23: 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", + R(18), R(19), R(20), R(21), R(22), R(23)); + db_printf("R24-29: 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", + R(24), R(25), R(26), R(27), R(28), R(29)); + db_printf("R30-31: 0x%08x 0x%08x\n", R(30), R(31)); + + db_printf("%cxip: 0x%08x ", + cputyp == CPU_88110 ? 'e' : 's', s->tf_sxip & ~3); + db_find_xtrn_sym_and_offset((db_addr_t)IPMASK(s->tf_sxip), + &name, &offset); + if (name != NULL && (unsigned)offset <= db_maxoff) + db_printf("%s+0x%08x", name, (unsigned)offset); + db_printf("\n"); + + if (s->tf_snip != s->tf_sxip + 4) { + db_printf("%cnip: 0x%08x ", + cputyp == CPU_88110 ? 'e' : 's', s->tf_snip); + db_find_xtrn_sym_and_offset((db_addr_t)IPMASK(s->tf_snip), + &name, &offset); + if (name != NULL && (unsigned)offset <= db_maxoff) + db_printf("%s+0x%08x", name, (unsigned)offset); + db_printf("\n"); + } + + if (cputyp != CPU_88110) { + if (s->tf_sfip != s->tf_snip + 4) { + db_printf("sfip: 0x%08x ", s->tf_sfip); + db_find_xtrn_sym_and_offset((db_addr_t)IPMASK(s->tf_sfip), + &name, &offset); + if (name != NULL && (unsigned)offset <= db_maxoff) + db_printf("%s+0x%08x", name, (unsigned)offset); + db_printf("\n"); + } + } else { + db_printf("fpsr: 0x%08x fpcr: 0x%08x fpecr: 0x%08x\n", + s->tf_fpsr, s->tf_fpcr, s->tf_fpecr); + db_printf("dsap 0x%08x duap 0x%08x dsr 0x%08x dlar 0x%08x dpar 0x%08x\n", + s->tf_dsap, s->tf_duap, s->tf_dsr, s->tf_dlar, s->tf_dpar); + db_printf("isap 0x%08x iuap 0x%08x isr 0x%08x ilar 0x%08x ipar 0x%08x\n", + s->tf_isap, s->tf_iuap, s->tf_isr, s->tf_ilar, s->tf_ipar); + } + + db_printf("epsr: 0x%08x current process: %p\n", + s->tf_epsr, curproc); + db_printf("vector: 0x%02x interrupt mask: 0x%08x\n", + s->tf_vector, s->tf_mask); + + /* + * If the vector indicates trap, instead of an exception or + * interrupt, skip the check of dmt and fp regs. + * + * Interrupt and exceptions are vectored at 0-10 and 114-127. + */ + + if (!(s->tf_vector <= 10 || (114 <= s->tf_vector && s->tf_vector <= 127))) { + db_printf("\n"); + return; + } + +#ifdef M88100 + if (cputyp != CPU_88110) { + if (s->tf_vector == /*data*/3 || s->tf_dmt0 & DMT_VALID) { + db_printf("dmt,d,a0: 0x%08x 0x%08x 0x%08x ", + s->tf_dmt0, s->tf_dmd0, s->tf_dma0); + db_find_xtrn_sym_and_offset((db_addr_t)s->tf_dma0, &name, &offset); + if (name != NULL && (unsigned)offset <= db_maxoff) + db_printf("%s+0x%08x", name, (unsigned)offset); + db_printf("\n "); + + suppress1 = m88k_dmx_print(s->tf_dmt0, s->tf_dmd0, s->tf_dma0, 0); + db_printf("\n"); + + if ((s->tf_dmt1 & DMT_VALID) && (!suppress1)) { + db_printf("dmt,d,a1: 0x%08x 0x%08x 0x%08x ", + s->tf_dmt1, s->tf_dmd1, s->tf_dma1); + db_find_xtrn_sym_and_offset((db_addr_t)s->tf_dma1, + &name, &offset); + if (name != NULL && + (unsigned)offset <= db_maxoff) + db_printf("%s+0x%08x", name, (unsigned)offset); + db_printf("\n "); + suppress2 = m88k_dmx_print(s->tf_dmt1, s->tf_dmd1, s->tf_dma1, 1); + db_printf("\n"); + + if ((s->tf_dmt2 & DMT_VALID) && (!suppress2)) { + db_printf("dmt,d,a2: 0x%08x 0x%08x 0x%08x ", + s->tf_dmt2, s->tf_dmd2, s->tf_dma2); + db_find_xtrn_sym_and_offset((db_addr_t)s->tf_dma2, + &name, &offset); + if (name != 0 && (unsigned)offset <= db_maxoff) + db_printf("%s+0x%08x", name, (unsigned)offset); + db_printf("\n "); + m88k_dmx_print(s->tf_dmt2, s->tf_dmd2, s->tf_dma2, 2); + db_printf("\n"); + } + } + + db_printf("fault code %d\n", + CMMU_PFSR_FAULT(s->tf_dpfsr)); + } + } +#endif /* M88100 */ + + if (s->tf_fpecr & 255) { /* floating point error occurred */ + db_printf("fpecr: 0x%08x fpsr: 0x%08x fpcr: 0x%08x\n", + s->tf_fpecr, s->tf_fpsr, s->tf_fpcr); +#ifdef M88100 + if (cputyp != CPU_88110) { + db_printf("fcr1-4: 0x%08x 0x%08x 0x%08x 0x%08x\n", + s->tf_fphs1, s->tf_fpls1, s->tf_fphs2, s->tf_fpls2); + db_printf("fcr5-8: 0x%08x 0x%08x 0x%08x 0x%08x\n", + s->tf_fppt, s->tf_fprh, s->tf_fprl, s->tf_fpit); + } +#endif + } + db_printf("\n"); +} + +void +m88k_db_registers(addr, have_addr, count, modif) + db_expr_t addr; + int have_addr; + db_expr_t count; + char *modif; +{ + m88k_db_print_frame((db_expr_t)DDB_REGS, TRUE, 0, modif); +} + +/* + * pause for 2*ticks many cycles + */ +void +m88k_db_pause(ticks) + unsigned volatile ticks; +{ + while (ticks) + ticks -= 1; +} + +/* + * m88k_db_trap - field a TRACE or BPT trap + * Note that only the tf_regs part of the frame is valid - some ddb routines + * invoke this function with a promoted struct reg! + */ +void +m88k_db_trap(type, frame) + int type; + struct trapframe *frame; +{ + + if (get_psr() & (1 << PSR_INTERRUPT_DISABLE_BIT)) + db_printf("WARNING: entered debugger with interrupts disabled\n"); + + switch(type) { + case T_KDB_BREAK: + case T_KDB_TRACE: + case T_KDB_ENTRY: + break; + case -1: + break; + default: + kdbprinttrap(type, 0); + if (db_recover != 0) { + db_error("Caught exception in ddb.\n"); + /*NOTREACHED*/ + } + } + + ddb_regs = frame->tf_regs; + + db_active++; + cnpollc(TRUE); + db_trap(type, 0); + cnpollc(FALSE); + db_active--; + + frame->tf_regs = ddb_regs; +} + +extern const char *trap_type[]; +extern const int trap_types; + +/* + * Print trap reason. + */ +void +kdbprinttrap(type, code) + int type, code; +{ + printf("kernel: "); + if (type >= trap_types || type < 0) + printf("type %d", type); + else + printf("%s", trap_type[type]); + printf(" trap\n"); +} + +void +Debugger() +{ + asm (ENTRY_ASM); /* entry trap */ + /* ends up at ddb_entry_trap below */ +} + +/* fielded a non maskable interrupt */ +int +ddb_nmi_trap(level, eframe) + int level; + db_regs_t *eframe; +{ + if (db_noisy) + db_printf("kernel: nmi interrupt\n"); + m88k_db_trap(T_KDB_ENTRY, (struct trapframe *)eframe); + + return 0; +} + +/* + * When the below routine is entered interrupts should be on + * but spl should be high + * + * The following routine is for breakpoint and watchpoint entry. + */ + +/* breakpoint/watchpoint entry */ +int +ddb_break_trap(type, eframe) + int type; + db_regs_t *eframe; +{ + m88k_db_trap(type, (struct trapframe *)eframe); + + if (type == T_KDB_BREAK) { + /* + * back up an instruction and retry the instruction + * at the breakpoint address. mc88110's exip reg + * already has the address of the exception instruction. + */ + if (cputyp != CPU_88110) { + eframe->sfip = eframe->snip; + eframe->snip = eframe->sxip; + } + } + + return 0; +} + +/* enter at splhigh */ +int +ddb_entry_trap(level, eframe) + int level; + db_regs_t *eframe; +{ + m88k_db_trap(T_KDB_ENTRY, (struct trapframe *)eframe); + + return 0; +} + +/* + * When the below routine is entered interrupts should be on + * but spl should be high + */ +/* error trap - unreturnable */ +void +ddb_error_trap(error, regs) + char *error; + db_regs_t *regs; +{ + db_printf("KERNEL: terminal error [%s]\n", error); + db_printf("KERNEL: Exiting debugger will cause abort to rom\n"); + db_printf("at 0x%x ", regs->sxip & XIP_ADDR); + db_printf("dmt0 0x%x dma0 0x%x", regs->dmt0, regs->dma0); + m88k_db_pause(1000000); + m88k_db_trap(T_KDB_BREAK, (struct trapframe *)regs); +} + +/* + * Read bytes from kernel address space for debugger. + */ +void +db_read_bytes(db_addr_t addr, size_t size, char *data) +{ + char *src; + + src = (char *)addr; + + while(size-- > 0) { + *data++ = *src++; + } +} + +/* + * Write bytes to kernel address space for debugger. + * This should make a text page writable to be able + * to plant a break point (right now text is mapped with + * write access in pmap_bootstrap()). XXX nivas + */ +void +db_write_bytes(db_addr_t addr, size_t size, char *data) +{ + char *dst; + paddr_t physaddr; + psize_t psize = size; + + dst = (char *)addr; + + while (size-- > 0) { + *dst++ = *data++; + } + /* XXX test return value */ + pmap_extract(pmap_kernel(), (vaddr_t)addr, &physaddr); + cmmu_flush_cache(cpu_number(), physaddr, psize); +} + +#define __ROM_FUNC_TABLE ((int **)0x00001100) + +/* to print a character to the console */ +void +db_putc(c) + int c; +{ + (*(void (*)(int))__ROM_FUNC_TABLE[4])(c & 0xff); +} + +/* to peek at the console; returns -1 if no character is there */ +int +db_getc() +{ + int c; + do { + c = (*(int (*)(void))__ROM_FUNC_TABLE[3])(); + } while (c == -1); + return c; +} + +/* display where all the cpus are stopped at */ +void +m88k_db_where(addr, have_addr, count, modif) + db_expr_t addr; + int have_addr; + db_expr_t count; + char *modif; +{ + char *name; + db_expr_t offset; + db_addr_t l; + + l = PC_REGS(DDB_REGS); /* clear low bits */ + + db_find_xtrn_sym_and_offset(l, &name, &offset); + if (name && (unsigned)offset <= db_maxoff) + db_printf("stopped at 0x%lx (%s+0x%x)\n", l, name, offset); + else + db_printf("stopped at 0x%lx\n", l); +} + +/* + * Walk back a stack, looking for exception frames. + * These frames are recognized by the routine frame_is_sane. Frames + * only start with zero, so we only call frame_is_sane if the + * current address contains zero. + * + * If addr is given, it is assumed to an address on the stack to be + * searched. Otherwise, r31 of the current cpu is used. + */ +void +m88k_db_frame_search(addr, have_addr, count, modif) + db_expr_t addr; + int have_addr; + db_expr_t count; + char *modif; +{ + if (have_addr) + addr &= ~3; /* round to word */ + else + addr = (DDB_REGS->r[31]); + + /* walk back up stack until 8k boundry, looking for 0 */ + while (addr & ((8 * 1024) - 1)) { + if (frame_is_sane((db_regs_t *)addr, 1) != 0) + db_printf("frame found at 0x%x\n", addr); + addr += 4; + } + + db_printf("(Walked back until 0x%x)\n",addr); +} + +/* flush icache */ +void +m88k_db_iflush(addr, have_addr, count, modif) + db_expr_t addr; + int have_addr; + db_expr_t count; + char *modif; +{ + addr = 0; +#ifdef may_be_removed + cmmu_remote_set(addr, CMMU_SCR, 0, CMMU_FLUSH_CACHE_CBI_ALL); +#endif +} + +/* flush dcache */ + +void +m88k_db_dflush(addr, have_addr, count, modif) + db_expr_t addr; + int have_addr; + db_expr_t count; + char *modif; +{ + addr = 0; +#ifdef may_be_removed + cmmu_remote_set(addr, CMMU_SCR, 1, CMMU_FLUSH_CACHE_CBI_ALL); +#endif +} + +/* probe my cache */ +void +m88k_db_peek(addr, have_addr, count, modif) + db_expr_t addr; + int have_addr; + db_expr_t count; + char *modif; +{ +#ifdef may_be_removed + int pa12; + int valmask; + + pa12 = addr & ~((1<<12) -1); + + /* probe dcache */ + cmmu_remote_set(0, CMMU_SAR, 1, addr); + + valmask = cmmu_remote_get(0, CMMU_CSSP, 1); + db_printf("dcache valmask 0x%x\n", (unsigned)valmask); + db_printf("dcache tag ports 0x%x 0x%x 0x%x 0x%x\n", + (unsigned)cmmu_remote_get(0, CMMU_CTP0, 1), + (unsigned)cmmu_remote_get(0, CMMU_CTP1, 1), + (unsigned)cmmu_remote_get(0, CMMU_CTP2, 1), + (unsigned)cmmu_remote_get(0, CMMU_CTP3, 1)); + + /* probe icache */ + cmmu_remote_set(0, CMMU_SAR, 0, addr); + + valmask = cmmu_remote_get(0, CMMU_CSSP, 0); + db_printf("icache valmask 0x%x\n", (unsigned)valmask); + db_printf("icache tag ports 0x%x 0x%x 0x%x 0x%x\n", + (unsigned)cmmu_remote_get(0, CMMU_CTP0, 0), + (unsigned)cmmu_remote_get(0, CMMU_CTP1, 0), + (unsigned)cmmu_remote_get(0, CMMU_CTP2, 0), + (unsigned)cmmu_remote_get(0, CMMU_CTP3, 0)); +#endif +} + + +/* + * control how much info the debugger prints about itself + */ +void +m88k_db_noise(addr, have_addr, count, modif) + db_expr_t addr; + int have_addr; + db_expr_t count; + char *modif; +{ + if (!have_addr) { + /* if off make noisy; if noisy or very noisy turn off */ + if (db_noisy) { + db_printf("changing debugger status from %s to quiet\n", + db_noisy == 1 ? "noisy" : + db_noisy == 2 ? "very noisy" : "violent"); + db_noisy = 0; + } else { + db_printf("changing debugger status from quiet to noisy\n"); + db_noisy = 1; + } + } else if (addr < 0 || addr > 3) + db_printf("invalid noise level to m88k_db_noisy; should be 0, 1, 2, or 3\n"); + else { + db_noisy = addr; + db_printf("debugger noise level set to %s\n", + db_noisy == 0 ? "quiet" : + (db_noisy == 1 ? "noisy" : + db_noisy==2 ? "very noisy" : "violent")); + } +} + +/* + * See how a virtual address translates. + * Must have an address. + */ +void +m88k_db_translate(addr, have_addr, count, modif) + db_expr_t addr; + int have_addr; + db_expr_t count; + char *modif; +{ + char c; + int verbose_flag = 0; + int supervisor_flag = 1; + int wanthelp = 0; + + if (!have_addr) + wanthelp = 1; + else { + while (c = *modif++, c != 0) { + switch (c) { + default: + db_printf("bad modifier [%c]\n", c); + wanthelp = 1; + break; + case 'h': + wanthelp = 1; + break; + case 'v': + verbose_flag++; + break; + case 's': + supervisor_flag = 1; + break; + case 'u': + supervisor_flag = 0; + break; + } + } + } + + if (wanthelp) { + db_printf("usage: translate[/vvsu] address\n"); + db_printf("flags: v - be verbose (vv - be very verbose)\n"); + db_printf(" s - use cmmu's supervisor area pointer (default)\n"); + db_printf(" u - use cmmu's user area pointer\n"); + return; + } + cmmu_show_translation(addr, supervisor_flag, verbose_flag, -1); +} + +void +m88k_db_cmmucfg(addr, have_addr, count, modif) + db_expr_t addr; + int have_addr; + db_expr_t count; + char *modif; +{ + if (modif != NULL && *modif != 0) { + db_printf("usage: mach cmmucfg\n"); + return; + } + + cmmu_dump_config(); +} + +void +m88k_db_prom_cmd(addr, have_addr, count, modif) + db_expr_t addr; + int have_addr; + db_expr_t count; + char *modif; +{ + db_printf("m88k_db_prom_cmd() is not implemented\n"); +} + +/************************/ +/* COMMAND TABLE / INIT */ +/************************/ + +struct db_command m88k_cache_cmds[] = { + { "iflush", m88k_db_iflush, 0, NULL }, + { "dflush", m88k_db_dflush, 0, NULL }, + { "peek", m88k_db_peek, 0, NULL }, + { NULL, NULL, 0, NULL } +}; + +struct db_command db_machine_cmds[] = { + { "cache", NULL, 0, m88k_cache_cmds }, + { "frame", m88k_db_print_frame, 0, NULL }, + { "regs", m88k_db_registers, 0, NULL }, + { "noise", m88k_db_noise, 0, NULL }, + { "searchframe",m88k_db_frame_search, 0, NULL }, + { "translate", m88k_db_translate, 0, NULL }, + { "cmmucfg", m88k_db_cmmucfg, 0, NULL }, + { "where", m88k_db_where, 0, NULL }, + { "prom", m88k_db_prom_cmd, 0, NULL }, + { NULL, NULL, 0, NULL } +}; + +void +db_machine_init() +{ + db_machine_commands_install(db_machine_cmds); +} diff --git a/sys/arch/luna88k/ddb/db_sstep.c b/sys/arch/luna88k/ddb/db_sstep.c new file mode 100644 index 00000000000..7c9a2da3254 --- /dev/null +++ b/sys/arch/luna88k/ddb/db_sstep.c @@ -0,0 +1,337 @@ +/* $OpenBSD: db_sstep.c,v 1.1 2004/04/21 15:23:51 aoyama Exp $ */ +/* + * Mach Operating System + * Copyright (c) 1993-1991 Carnegie Mellon University + * Copyright (c) 1991 OMRON Corporation + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON AND OMRON ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON AND OMRON DISCLAIM ANY LIABILITY OF ANY KIND + * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie the + * rights to redistribute these changes. + */ + +#include <sys/param.h> +#include <sys/systm.h> + +#include <machine/db_machdep.h> + +#include <ddb/db_access.h> /* db_get_value() */ +#include <ddb/db_break.h> /* db_breakpoint_t */ + +/* + * Support routines for software single step. + * + * Author: Daniel Stodolsky (danner@cs.cmu.edu) + * + */ + +boolean_t inst_delayed(unsigned int ins); + +#ifdef INTERNAL_SSTEP +db_breakpoint_t db_not_taken_bkpt = 0; +db_breakpoint_t db_taken_bkpt = 0; +#endif + +/* + * Returns TRUE is the instruction a branch or jump instruction + * (br, bb0, bb1, bcnd, jmp) but not a function call (bsr or jsr) + */ +boolean_t +inst_branch(ins) + unsigned int ins; +{ + /* check high five bits */ + switch (ins >> (32 - 5)) { + case 0x18: /* br */ + case 0x1a: /* bb0 */ + case 0x1b: /* bb1 */ + case 0x1d: /* bcnd */ + return TRUE; + break; + case 0x1e: /* could be jmp */ + if ((ins & 0xfffffbe0U) == 0xf400c000U) + return TRUE; + } + + return FALSE; +} + +/* + * inst_load(ins) + * Returns the number of words the instruction loads. byte, + * half and word count as 1; double word as 2 + */ +unsigned +inst_load(ins) + unsigned int ins; +{ + /* look at the top six bits, for starters */ + switch (ins >> (32 - 6)) { + case 0x0: /* xmem byte imm */ + case 0x1: /* xmem word imm */ + + case 0x2: /* unsigned half-word load imm */ + case 0x3: /* unsigned byte load imm */ + case 0x5: /* signed word load imm */ + case 0x6: /* signed half-word load imm */ + case 0x7: /* signed byte load imm */ + return 1; + + case 0x4: /* signed double word load imm */ + return 2; + + case 0x3d: /* load/store/xmem scaled/unscaled instruction */ + if ((ins & 0xf400c0e0U) == 0xf4000000U) /* is load/xmem */ + switch ((ins & 0x0000fce0) >> 5) { /* look at bits 15-5, but mask bits 8-9 */ + case 0x0: /* xmem byte */ + case 0x1: /* xmem word */ + case 0x2: /* unsigned half word */ + case 0x3: /* unsigned byte load */ + case 0x5: /* signed word load */ + case 0x6: /* signed half-word load */ + case 0x7: /* signed byte load */ + return 1; + + case 0x4: /* signed double word load */ + return 2; + } /* end switch load/xmem */ + break; + } /* end switch 32-6 */ + + return 0; +} + +/* + * inst_store + * Like inst_load, except for store instructions. + */ +unsigned +inst_store(ins) + unsigned int ins; +{ + /* decode top 6 bits again */ + switch (ins >> (32 - 6)) { + case 0x0: /* xmem byte imm */ + case 0x1: /* xmem word imm */ + case 0x9: /* store word imm */ + case 0xa: /* store half-word imm */ + case 0xb: /* store byte imm */ + return 1; + + case 0x8: /* store double word */ + return 2; + case 0x3d: /* load/store/xmem scaled/unscaled instruction */ + /* check bits 15,14,12,7,6,5 are all 0 */ + if ((ins & 0x0000d0e0U) == 0) + switch ((ins & 0x00003c00U) >> 10) { /* decode bits 10-13 */ + case 0x0: /* xmem byte imm */ + case 0x1: /* xmem word imm */ + case 0x9: /* store word */ + case 0xa: /* store half-word */ + case 0xb: /* store byte */ + return 1; + + case 0x8: /* store double word */ + return 2; + } /* end switch store/xmem */ + break; + } /* end switch 32-6 */ + + return 0; +} + +/* + * inst_delayed + * Returns TRUE if this instruction is followed by a delay slot. + * Could be br.n, bsr.n bb0.n, bb1.n, bcnd.n or jmp.n or jsr.n + */ +boolean_t +inst_delayed(ins) + unsigned int ins; +{ + /* check the br, bsr, bb0, bb1, bcnd cases */ + switch ((ins & 0xfc000000U) >> (32 - 6)) { + case 0x31: /* br */ + case 0x33: /* bsr */ + case 0x35: /* bb0 */ + case 0x37: /* bb1 */ + case 0x3b: /* bcnd */ + return TRUE; + } + + /* check the jmp, jsr cases */ + /* mask out bits 0-4, bit 11 */ + return ((ins & 0xfffff7e0U) == 0xf400c400U) ? TRUE : FALSE; +} + +/* + * next_instr_address(pc,delay_slot,task) has the following semantics. + * Let inst be the instruction at pc. + * If delay_slot = 1, next_instr_address should return + * the address of the instruction in the delay slot; if this instruction + * does not have a delay slot, it should return pc. + * If delay_slot = 0, next_instr_address should return the + * address of next sequential instruction, or pc if the instruction is + * followed by a delay slot. + * + * 91-11-28 jfriedl: I think the above is wrong. I think it should be: + * if delay_slot true, return address of the delay slot if there is one, + * return pc otherwise. + * if delay_slot false, return (pc + 4) regardless. + * + */ +db_addr_t +next_instr_address(pc, delay_slot) + db_addr_t pc; + unsigned delay_slot; +{ + if (delay_slot == 0) + return pc + 4; + else { + if (inst_delayed(db_get_value(pc, sizeof(int), FALSE))) + return pc + 4; + else + return pc; + } +} + + +/* + * branch_taken(instruction, program counter, func, func_data) + * + * instruction will be a control flow instruction location at address pc. + * Branch taken is supposed to return the address to which the instruction + * would jump if the branch is taken. Func can be used to get the current + * register values when invoked with a register number and func_data as + * arguments. + * + * If the instruction is not a control flow instruction, panic. + */ +db_addr_t +branch_taken(inst, pc, func, func_data) + u_int inst; + db_addr_t pc; + db_expr_t (*func)(db_regs_t *, int); + db_regs_t *func_data; +{ + /* check if br/bsr */ + if ((inst & 0xf0000000U) == 0xc0000000U) { + /* signed 26 bit pc relative displacement, shift left two bits */ + inst = (inst & 0x03ffffffU) << 2; + /* check if sign extension is needed */ + if (inst & 0x08000000U) + inst |= 0xf0000000U; + return pc + inst; + } + + /* check if bb0/bb1/bcnd case */ + switch ((inst & 0xf8000000U)) { + case 0xd0000000U: /* bb0 */ + case 0xd8000000U: /* bb1 */ + case 0xe8000000U: /* bcnd */ + /* signed 16 bit pc relative displacement, shift left two bits */ + inst = (inst & 0x0000ffffU) << 2; + /* check if sign extension is needed */ + if (inst & 0x00020000U) + inst |= 0xfffc0000U; + return pc + inst; + } + + /* check jmp/jsr case */ + /* check bits 5-31, skipping 10 & 11 */ + if ((inst & 0xfffff3e0U) == 0xf400c000U) { + return (*func)(func_data, (inst & 0x0000001fU)); /* the register value */ + } + + + panic("branch_taken"); + return 0; /* keeps compiler happy */ +} + +/* + * getreg_val - handed a register number and an exception frame. + * Returns the value of the register in the specified + * frame. Only makes sense for general registers. + */ + +db_expr_t +getreg_val(frame, regno) + db_regs_t *frame; + int regno; +{ + if (regno == 0) + return 0; + else if (regno < 31) + return frame->r[regno]; + else + panic("bad register number (%d) to getreg_val.", regno); +} + +#ifdef INTERNAL_SSTEP +void +db_set_single_step(regs) + db_regs_t *regs; +{ + if (cputyp == CPU_88110) { + ((regs)->epsr |= (PSR_TRACE | PSR_SER)); + } else { + db_addr_t pc = PC_REGS(regs); +#ifndef SOFTWARE_SSTEP_EMUL + db_addr_t brpc; + u_int inst; + + /* + * User was stopped at pc, e.g. the instruction + * at pc was not executed. + */ + inst = db_get_value(pc, sizeof(int), FALSE); + if (inst_branch(inst) || inst_call(inst) || inst_return(inst)) { + brpc = branch_taken(inst, pc, getreg_val, regs); + if (brpc != pc) { /* self-branches are hopeless */ + db_taken_bkpt = db_set_temp_breakpoint(brpc); + } +#if 0 + /* XXX this seems like a true bug, no? */ + pc = next_instr_address(pc, 1); +#endif + } +#endif /*SOFTWARE_SSTEP_EMUL*/ + pc = next_instr_address(pc, 0); + db_not_taken_bkpt = db_set_temp_breakpoint(pc); + } +} + +void +db_clear_single_step(regs) + db_regs_t *regs; +{ + if (cputyp == CPU_88110) { + ((regs)->epsr &= ~(PSR_TRACE | PSR_SER)); + } else { + if (db_taken_bkpt != 0) { + db_delete_temp_breakpoint(db_taken_bkpt); + db_taken_bkpt = 0; + } + if (db_not_taken_bkpt != 0) { + db_delete_temp_breakpoint(db_not_taken_bkpt); + db_not_taken_bkpt = 0; + } + } +} +#endif diff --git a/sys/arch/luna88k/ddb/db_trace.c b/sys/arch/luna88k/ddb/db_trace.c new file mode 100644 index 00000000000..3c24e6c4105 --- /dev/null +++ b/sys/arch/luna88k/ddb/db_trace.c @@ -0,0 +1,1143 @@ +/* $OpenBSD: db_trace.c,v 1.1 2004/04/21 15:23:52 aoyama Exp $ */ +/* + * Mach Operating System + * Copyright (c) 1993-1991 Carnegie Mellon University + * Copyright (c) 1991 OMRON Corporation + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON AND OMRON ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON AND OMRON DISCLAIM ANY LIABILITY OF ANY KIND + * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie the + * rights to redistribute these changes. + */ + +#include <sys/param.h> +#include <sys/systm.h> + +#include <machine/db_machdep.h> +#include <machine/locore.h> + +#include <ddb/db_variables.h> /* db_variable, DB_VAR_GET, etc. */ +#include <ddb/db_output.h> /* db_printf */ +#include <ddb/db_sym.h> /* DB_STGY_PROC, etc. */ +#include <ddb/db_command.h> /* db_recover */ +#include <ddb/db_access.h> +#include <ddb/db_interface.h> + +union instruction { + unsigned rawbits; + + struct { + unsigned int : 5; + unsigned int n: 1; + signed int d26:26; + } br; + + struct { + unsigned int : 4; + unsigned int isbb1: 1; /* isbb1==0 means bb0, isbb1==1 means bb1 */ + unsigned int n : 1; + unsigned int b5 : 5; + unsigned int s1 : 5; + signed int d16 :16; + } bb; /* bcnd too, except "isbb1" makes no sense for bcnd */ + + struct { + unsigned int : 6; + unsigned int b5 : 5; + unsigned int s1 : 5; + unsigned int : 7; + unsigned int vec9 : 9; + } tb; /* tcnd too */ + + struct { + unsigned int :21; + unsigned int n : 1; + unsigned int : 5; + unsigned int s2 : 5; + } jump; /* jmp, jsr */ + + struct { + unsigned int : 6; + unsigned int d : 5; + unsigned int s1 : 5; + unsigned int i16 :16; + } diatic; /* general reg/reg/i16 instructions */ + + struct { + unsigned int : 6; + unsigned int d : 5; + unsigned int s1 : 5; + unsigned int :11; + unsigned int s2 : 5; + } triatic; /* general reg/reg/reg instructions */ + +}; + +static inline unsigned br_dest(unsigned addr, union instruction inst) +{ + return addr + inst.br.d26 * 4; +} + + +#define TRACE_DEBUG /* undefine to disable debugging */ + +int frame_is_sane(db_regs_t *regs, int); +char *m88k_exception_name(unsigned vector); +unsigned db_trace_get_val(vaddr_t addr, unsigned *ptr); + +/* + * Some macros to tell if the given text is the instruction. + */ +#define JMPN_R1(I) ( (I) == 0xf400c401U) /* jmp.n r1 */ +#define JMP_R1(I) ( (I) == 0xf400c001U) /* jmp r1 */ + +/* gets the IMM16 value from an instruction */ +#define IMM16VAL(I) (((union instruction)(I)).diatic.i16) + +/* subu r31, r31, IMM */ +#define SUBU_R31_R31_IMM(I) (((I) & 0xffff0000U) == 0x67ff0000U) + +/* st r1, r31, IMM */ +#define ST_R1_R31_IMM(I) (((I) & 0xffff0000U) == 0x243f0000U) + +static int trace_flags; +#define TRACE_DEBUG_FLAG 0x01 +#define TRACE_SHOWCALLPRESERVED_FLAG 0x02 +#define TRACE_SHOWADDRESS_FLAG 0x04 +#define TRACE_SHOWFRAME_FLAG 0x08 +#define TRACE_USER_FLAG 0x10 + +#ifdef TRACE_DEBUG + #define DEBUGGING_ON (trace_flags & TRACE_DEBUG_FLAG) +#endif + +#ifndef TRACE_DEBUG + #define SHOW_INSTRUCTION(Addr, Inst, Note) { /*nothing*/ } +#else + #define SHOW_INSTRUCTION(Addr, Inst, Note) if (DEBUGGING_ON) { \ + db_printf("%s0x%x: (0x%08x) ", Note, (unsigned)(Addr), (Inst)); \ + m88k_print_instruction((unsigned)(Addr), (Inst)); \ + db_printf("\n"); \ + } +#endif + +extern label_t *db_recover; + +/* + * m88k trace/register state interface for ddb. + */ + +/* lifted from mips */ +static int +db_setf_regs(struct db_variable *vp, + db_expr_t *valuep, + int op) /* read/write */ +{ + int *regp = (int *) ((char *) DDB_REGS + (int) (vp->valuep)); + + if (op == DB_VAR_GET) + *valuep = *regp; + else if (op == DB_VAR_SET) + *regp = *valuep; + + return (0); /* silence warning */ +} + +#define N(s, x) {s, (long *)&(((db_regs_t *) 0)->x), db_setf_regs} + +struct db_variable db_regs[] = { + N("r1", r[1]), N("r2", r[2]), N("r3", r[3]), N("r4", r[4]), + N("r5", r[5]), N("r6", r[6]), N("r7", r[7]), N("r8", r[8]), + N("r9", r[9]), N("r10", r[10]), N("r11", r[11]), N("r12", r[12]), + N("r13", r[13]), N("r14", r[14]), N("r15", r[15]), N("r16", r[16]), + N("r17", r[17]), N("r18", r[18]), N("r19", r[19]), N("r20", r[20]), + N("r21", r[21]), N("r22", r[22]), N("r23", r[23]), N("r24", r[24]), + N("r25", r[25]), N("r26", r[26]), N("r27", r[27]), N("r28", r[28]), + N("r29", r[29]), N("r30", r[30]), N("r31", r[31]), N("epsr", epsr), + N("sxip", sxip), N("snip", snip), N("sfip", sfip), N("ssbr", ssbr), + N("dmt0", dmt0), N("dmd0", dmd0), N("dma0", dma0), N("dmt1", dmt1), + N("dmd1", dmd1), N("dma1", dma1), N("dmt2", dmt2), N("dmd2", dmd2), + N("dma2", dma2), N("fpecr", fpecr),N("fphs1", fphs1),N("fpls1", fpls1), + N("fphs2", fphs2), N("fpls2", fpls2),N("fppt", fppt), N("fprh", fprh), + N("fprl", fprl), N("fpit", fpit), N("fpsr", fpsr), N("fpcr", fpcr), +}; +#undef N + +struct db_variable *db_eregs = db_regs + sizeof(db_regs)/sizeof(db_regs[0]); + + +#define TRASHES 0x001 /* clobbers instruction field D */ +#define STORE 0x002 /* does a store to S1+IMM16 */ +#define LOAD 0x004 /* does a load from S1+IMM16 */ +#define DOUBLE 0x008 /* double-register */ +#define FLOW_CTRL 0x010 /* flow-control instruction */ +#define DELAYED 0x020 /* delayed flow control */ +#define JSR 0x040 /* flow-control is a jsr[.n] */ +#define BSR 0x080 /* flow-control is a bsr[.n] */ + +/* + * Given a word of instruction text, return some flags about that + * instruction (flags defined above). + */ +static unsigned +m88k_instruction_info(unsigned instruction) +{ + static const struct { + unsigned mask, value, flags; + } *ptr, control[] = { + /* runs in the same order as 2nd Ed 88100 manual Table 3-14 */ + { 0xf0000000U, 0x00000000U, /* xmem */ TRASHES | STORE | LOAD}, + { 0xec000000U, 0x00000000U, /* ld.d */ TRASHES | LOAD | DOUBLE}, + { 0xe0000000U, 0x00000000U, /* load */ TRASHES | LOAD}, + { 0xfc000000U, 0x20000000U, /* st.d */ STORE | DOUBLE}, + { 0xf0000000U, 0x20000000U, /* store */ STORE}, + { 0xc0000000U, 0x40000000U, /* arith */ TRASHES}, + { 0xfc004000U, 0x80004000U, /* ld cr */ TRASHES}, + { 0xfc004000U, 0x80000000U, /* st cr */ 0}, + { 0xfc008060U, 0x84000000U, /* f */ TRASHES}, + { 0xfc008060U, 0x84000020U, /* f.d */ TRASHES | DOUBLE}, + { 0xfc000000U, 0xcc000000U, /* bsr.n */ FLOW_CTRL | DELAYED | BSR}, + { 0xfc000000U, 0xc8000000U, /* bsr */ FLOW_CTRL | BSR}, + { 0xe4000000U, 0xc4000000U, /* br/bb.n */ FLOW_CTRL | DELAYED}, + { 0xe4000000U, 0xc0000000U, /* br/bb */ FLOW_CTRL}, + { 0xfc000000U, 0xec000000U, /* bcnd.n */ FLOW_CTRL | DELAYED}, + { 0xfc000000U, 0xe8000000U, /* bcnd */ FLOW_CTRL}, + { 0xfc00c000U, 0xf0008000U, /* bits */ TRASHES}, + { 0xfc00c000U, 0xf000c000U, /* trap */ 0}, + { 0xfc00f0e0U, 0xf4002000U, /* st */ 0}, + { 0xfc00cce0U, 0xf4000000U, /* ld.d */ TRASHES | DOUBLE}, + { 0xfc00c0e0U, 0xf4000000U, /* ld */ TRASHES}, + { 0xfc00c0e0U, 0xf4004000U, /* arith */ TRASHES}, + { 0xfc00c3e0U, 0xf4008000U, /* bits */ TRASHES}, + { 0xfc00ffe0U, 0xf400cc00U, /* jsr.n */ FLOW_CTRL | DELAYED | JSR}, + { 0xfc00ffe0U, 0xf400c800U, /* jsr */ FLOW_CTRL | JSR}, + { 0xfc00ffe0U, 0xf400c400U, /* jmp.n */ FLOW_CTRL | DELAYED}, + { 0xfc00ffe0U, 0xf400c000U, /* jmp */ FLOW_CTRL}, + { 0xfc00fbe0U, 0xf400e800U, /* ff */ TRASHES}, + { 0xfc00ffe0U, 0xf400f800U, /* tbnd */ 0}, + { 0xfc00ffe0U, 0xf400fc00U, /* rte */ FLOW_CTRL}, + { 0xfc000000U, 0xf8000000U, /* tbnd */ 0}, + }; +#define ctrl_count (sizeof(control)/sizeof(control[0])) + for (ptr = &control[0]; ptr < &control[ctrl_count]; ptr++) + if ((instruction & ptr->mask) == ptr->value) + return ptr->flags; + SHOW_INSTRUCTION(0, instruction, "bad m88k_instruction_info"); + return 0; +} + +static int +hex_value_needs_0x(unsigned value) +{ + int i; + unsigned last = 0; + unsigned char c; + unsigned have_a_hex_digit = 0; + + if (value <= 9) + return 0; + + for (i = 0; i < 8; i++) { + c = value & 0xf; + value >>= 4; + if (c) + last = c; + if (c > 9) + have_a_hex_digit = 1; + } + if (have_a_hex_digit == 0) + return 1; + if (last > 9) + return 1; + return 0; +} + +/* + * returns + * 1 if regs seems to be a reasonable kernel exception frame. + * 2 if regs seems to be a reasonable user exception frame + * (in the current task). + * 0 if this looks like neither. + */ +int +frame_is_sane(db_regs_t *regs, int quiet) +{ + /* no good if we can't read the whole frame */ + if (badwordaddr((vaddr_t)regs) || badwordaddr((vaddr_t)®s->fpit)) { + if (quiet == 0) + db_printf("[WARNING: frame at %p : unreadable]\n", regs); + return 0; + } + + /* r0 must be 0 (obviously) */ + if (regs->r[0] != 0) { + if (quiet == 0) + db_printf("[WARNING: frame at %p : r[0] != 0]\n", regs); + return 0; + } + + /* stack sanity ... r31 must be nonzero, and must be word aligned */ + if (regs->r[31] == 0 || (regs->r[31] & 3) != 0) { + if (quiet == 0) + db_printf("[WARNING: frame at %p : r[31] == 0 or not word aligned]\n", regs); + return 0; + } + + if (cputyp != CPU_88110) { + /* sxip is reasonable */ +#if 0 + if ((regs->sxip & 1) == 1) + return 0; +#endif + /* snip is reasonable */ + if ((regs->snip & 3) != 2) + return 0; + /* sfip is reasonable */ + if ((regs->sfip & 3) != 2) + return 0; + } + + /* epsr sanity */ + if ((regs->epsr & PSR_MODE)) { /* kernel mode */ + if (regs->epsr & PSR_BO) + return 0; + return 1; + } + if (!(regs->epsr & PSR_MODE)) { /* user mode */ + if (regs->epsr & PSR_BO) + return 0; + return 2; + } + if (quiet == 0) + db_printf("[WARNING: not an exception frame?]\n"); + return 0; +} + +char * +m88k_exception_name(unsigned vector) +{ + switch (vector) { + default: + case 0: return "Reset"; + case 1: return "Interrupt"; + case 2: return "Instruction Access Exception"; + case 3: return "Data Access Exception"; + case 4: return "Misaligned Access Exception"; + case 5: return "Unimplemented Opcode Exception"; + case 6: return "Privilege Violation"; + case 7: return "Bounds Check"; + case 8: return "Integer Divide Exception"; + case 9: return "Integer Overflow Exception"; + case 10: return "Error Exception"; + case 11: return "Non Maskable Interrupt"; + case 114: return "FPU precise"; + case 115: return "FPU imprecise"; + case DDB_ENTRY_BKPT_NO: + return "ddb break"; + case DDB_ENTRY_TRACE_NO: + return "ddb trace"; + case DDB_ENTRY_TRAP_NO: + return "ddb trap"; + case 451: return "Syscall"; + } +} + +/* + * Read a word at address addr. + * Return 1 if was able to read, 0 otherwise. + */ +unsigned +db_trace_get_val(vaddr_t addr, unsigned *ptr) +{ + label_t db_jmpbuf; + label_t *prev = db_recover; + + if (setjmp((db_recover = &db_jmpbuf)) != 0) { + db_recover = prev; + return 0; + } else { + db_read_bytes(addr, 4, (char *)ptr); + db_recover = prev; + return 1; + } +} + +#define FIRST_CALLPRESERVED_REG 14 +#define LAST_CALLPRESERVED_REG 29 +#define FIRST_ARG_REG 2 +#define LAST_ARG_REG 9 +#define RETURN_VAL_REG 1 + +static unsigned global_saved_list = 0x0; /* one bit per register */ +static unsigned local_saved_list = 0x0; /* one bit per register */ +static unsigned trashed_list = 0x0; /* one bit per register */ +static unsigned saved_reg[32]; /* one value per register */ + +#define reg_bit(reg) (1<<((reg)%32)) + +static void +save_reg(int reg, unsigned value) +{ +#ifdef TRACE_DEBUG + if (DEBUGGING_ON) db_printf("save_reg(%d, %x)\n", reg, value); +#endif + if (trashed_list & reg_bit(reg)) { +#ifdef TRACE_DEBUG + if (DEBUGGING_ON) db_printf("<trashed>\n"); +#endif + return; /* don't save trashed registers */ + } + saved_reg[(reg%32)] = value; + global_saved_list |= reg_bit(reg); + local_saved_list |= reg_bit(reg); +} + +#define mark_reg_trashed(reg) (trashed_list |= reg_bit(reg)) + +#define have_global_reg(reg) (global_saved_list & (1<<(reg))) +#define have_local_reg(reg) (local_saved_list & (1<<(reg))) + +#define clear_local_saved_regs() { local_saved_list = trashed_list = 0; } +#define clear_global_saved_regs() { local_saved_list = global_saved_list = 0; } + +#define saved_reg_value(reg) (saved_reg[(reg)]) + +/* + * Show any arguments that we might have been able to determine. + */ +static void +print_args(void) +{ + int reg, last_arg; + + /* find the highest argument register saved */ + for (last_arg = LAST_ARG_REG; last_arg >= FIRST_ARG_REG; last_arg--) + if (have_local_reg(last_arg)) + break; + if (last_arg < FIRST_ARG_REG) + return; /* none were saved */ + + db_printf("("); + + /* print each one, up to the highest */ + for (reg = FIRST_ARG_REG; /*nothing */; reg++) { + if (!have_local_reg(reg)) + db_printf("?"); + else { + unsigned value = saved_reg_value(reg); + db_printf("%s%x", hex_value_needs_0x(value) ? + "0x" : "", value); + } + if (reg == last_arg) + break; + else + db_printf(", "); + } + db_printf(")"); +} + + +#define JUMP_SOURCE_IS_BAD 0 +#define JUMP_SOURCE_IS_OK 1 +#define JUMP_SOURCE_IS_UNLIKELY 2 + +/* + * Give an address to where we return, and an address to where we'd jumped, + * Decided if it all makes sense. + * + * Gcc sometimes optimized something like + * if (condition) + * func1(); + * else + * OtherStuff... + * to + * bcnd !condition mark + * bsr.n func1 + * or r1, r0, mark2 + * mark: + * OtherStuff... + * mark2: + * + * So RETURN_TO will be MARK2, even though we really did branch via + * 'bsr.n func1', so this makes it difficult to be certaian about being + * wrong. + */ +static int +is_jump_source_ok(unsigned return_to, unsigned jump_to) +{ + unsigned flags; + union instruction instruction; + + /* + * Delayed branches are most common... look two instructions before + * where we were going to return to to see if it's a delayed branch. + */ + if (!db_trace_get_val(return_to - 8, &instruction.rawbits)) + return JUMP_SOURCE_IS_BAD; + flags = m88k_instruction_info(instruction.rawbits); + + if ((flags & FLOW_CTRL) && (flags & DELAYED) && (flags & (JSR|BSR))) { + if (flags & JSR) + return JUMP_SOURCE_IS_OK; /* have to assume it's correct */ + /* calculate the offset */ + if (br_dest(return_to - 8, instruction) == jump_to) + return JUMP_SOURCE_IS_OK; /* exactamundo! */ + else + return JUMP_SOURCE_IS_UNLIKELY; /* seems wrong */ + } + + /* + * Try again, looking for a non-delayed jump one back. + */ + if (!db_trace_get_val(return_to - 4, &instruction.rawbits)) + return JUMP_SOURCE_IS_BAD; + flags = m88k_instruction_info(instruction.rawbits); + + if ((flags & FLOW_CTRL) && !(flags & DELAYED) && (flags & (JSR|BSR))) { + if (flags & JSR) + return JUMP_SOURCE_IS_OK; /* have to assume it's correct */ + /* calculate the offset */ + if (br_dest(return_to - 4, instruction) == jump_to) + return JUMP_SOURCE_IS_OK; /* exactamundo! */ + else + return JUMP_SOURCE_IS_UNLIKELY; /* seems wrong */ + } + + return JUMP_SOURCE_IS_UNLIKELY; +} + +static char *note = 0; +static int next_address_likely_wrong = 0; + +/* How much slop we expect in the stack trace */ +#define FRAME_PLAY 8 + +/* + * Stack decode - + * unsigned addr; program counter + * unsigned *stack; IN/OUT stack pointer + * + * given an address within a function and a stack pointer, + * try to find the function from which this one was called + * and the stack pointer for that function. + * + * The return value is zero (if we get confused) or + * we determine that the return address has not yet + * been saved (early in the function prologue). Otherwise + * the return value is the address from which this function + * was called. + * + * Note that even is zero is returned (the second case) the + * stack pointer can be adjusted. + * + */ +static int +stack_decode(db_addr_t addr, unsigned *stack, int (*pr)(const char *, ...)) +{ + db_sym_t proc; + db_expr_t offset_from_proc; + unsigned instructions_to_search; + db_addr_t check_addr; + db_addr_t function_addr; /* start of function */ + unsigned r31 = *stack; /* the r31 of the function */ + unsigned inst; /* text of an instruction */ + unsigned ret_addr; /* address to which we return */ + unsigned tried_to_save_r1 = 0; + +#ifdef TRACE_DEBUG + if (DEBUGGING_ON) + (*pr)("\n>>>stack_decode(addr=%x, stack=%x)\n", + addr, *stack); +#endif + + /* get what we hope will be the db_sym_t for the function name */ + proc = db_search_symbol(addr, DB_STGY_PROC, &offset_from_proc); + if (offset_from_proc == addr) /* i.e. no symbol found */ + proc = DB_SYM_NULL; + + /* + * Somehow, find the start of this function. + * If we found a symbol above, it'll have the address. + * Otherwise, we've got to search for it.... + */ + if (proc != DB_SYM_NULL) { + char *names; + db_symbol_values(proc, &names, &function_addr); + if (names == 0) + return 0; +#ifdef TRACE_DEBUG + if (DEBUGGING_ON) (*pr)("name %s address 0x%x\n", + names, function_addr); +#endif + } else { + int instructions_to_check = 400; + /* + * hmm - unable to find symbol. Search back + * looking for a function prolog. + */ + for (check_addr = addr; instructions_to_check-- > 0; check_addr -= 4) { + if (!db_trace_get_val(check_addr, &inst)) + break; + + if (SUBU_R31_R31_IMM(inst)) { +#if 0 + /* + * If the next instruction is "st r1, r31, ####" + * then we can feel safe we have the start of + * a function. + */ + if (!db_trace_get_val(check_addr + 4, &inst)) + continue; + if (ST_R1_R31_IMM(instr)) + break; /* success */ +#else + /* + * Latest GCC optimizer is just too good... the store + * of r1 might come much later... so we'll have to + * settle for just the "subr r31, r31, ###" to mark + * the start.... + */ + break; +#endif + } + /* + * if we come across a [jmp r1] or [jmp.n r1] assume we have hit + * the previous functions epilogue and stop our search. + * Since we know we would have hit the "subr r31, r31" if it was + * right in front of us, we know this doesn't have one so + * we just return failure.... + */ + if (JMP_R1(inst) || JMPN_R1(inst)) { +#ifdef TRACE_DEBUG + if (DEBUGGING_ON) + (*pr)("ran into a [jmp r1] at %x (addr=%x)\n", + check_addr, addr); +#endif + return 0; + } + } + if (instructions_to_check < 0) { +#ifdef TRACE_DEBUG + if (DEBUGGING_ON) + (*pr)("couldn't find func start (addr=%x)\n", addr); +#endif + return 0; /* bummer, couldn't find it */ + } + function_addr = check_addr; + } + + /* + * We now know the start of the function (function_addr). + * If we're stopped right there, or if it's not a + * subu r31, r31, #### + * then we're done. + */ + if (addr == function_addr) { +#ifdef TRACE_DEBUG + if (DEBUGGING_ON) (*pr)("at start of func\n"); +#endif + return 0; + } + if (!db_trace_get_val(function_addr, &inst)) { +#ifdef TRACE_DEBUG + if (DEBUGGING_ON) (*pr)("couldn't read %x at line %d\n", + function_addr, __LINE__); +#endif + return 0; + } + SHOW_INSTRUCTION(function_addr, inst, "start of function: "); + if (!SUBU_R31_R31_IMM(inst)) { +#ifdef TRACE_DEBUG + if (DEBUGGING_ON) (*pr)("<not subu,r31,r31,imm>\n"); +#endif + return 0; + } + + /* add the size of this frame to the stack (for the next frame) */ + *stack += IMM16VAL(inst); + + /* + * Search from the beginning of the function (funstart) to where we are + * in the function (addr) looking to see what kind of registers have + * been saved on the stack. + * + * We'll stop looking before we get to ADDR if we hit a branch. + */ + clear_local_saved_regs(); + check_addr = function_addr + 4; /* we know the first inst isn't a store */ + + for (instructions_to_search = (addr - check_addr)/sizeof(long); + instructions_to_search-- > 0; + check_addr += 4) { + union instruction instruction; + unsigned flags; + + /* read the instruction */ + if (!db_trace_get_val(check_addr, &instruction.rawbits)) { +#ifdef TRACE_DEBUG + if (DEBUGGING_ON) (*pr)("couldn't read %x at line %d\n", + check_addr, __LINE__); +#endif + break; + } + + SHOW_INSTRUCTION(check_addr, instruction.rawbits, "prolog: "); + + /* find out the particulars about this instruction */ + flags = m88k_instruction_info(instruction.rawbits); + + /* if a store to something off the stack pointer, note the value */ + if ((flags & STORE) && instruction.diatic.s1 == /*stack pointer*/31) { + unsigned value; + if (!have_local_reg(instruction.diatic.d)) { + if (instruction.diatic.d == 1) + tried_to_save_r1 = r31 + instruction.diatic.i16 ; + if (db_trace_get_val(r31 + instruction.diatic.i16, &value)) + save_reg(instruction.diatic.d, value); + } + if ((flags & DOUBLE) && !have_local_reg(instruction.diatic.d + 1)) { + if (instruction.diatic.d == 0) + tried_to_save_r1 = r31+instruction.diatic.i16 +4; + if (db_trace_get_val(r31+instruction.diatic.i16 +4, &value)) + save_reg(instruction.diatic.d + 1, value); + } + } + + /* if an inst that kills D (and maybe D+1), note that */ + if (flags & TRASHES) { + mark_reg_trashed(instruction.diatic.d); + if (flags & DOUBLE) + mark_reg_trashed(instruction.diatic.d + 1); + } + + /* if a flow control instruction, stop now (or next if delayed) */ + if ((flags & FLOW_CTRL) && instructions_to_search != 0) + instructions_to_search = (flags & DELAYED) ? 1 : 0; + } + + /* + * If we didn't save r1 at some point, we're hosed. + */ + if (!have_local_reg(1)) { + if (tried_to_save_r1) { + (*pr)(" <return value of next fcn unreadable in %08x>\n", + tried_to_save_r1); + } +#ifdef TRACE_DEBUG + if (DEBUGGING_ON) (*pr)("didn't save r1\n"); +#endif + return 0; + } + + ret_addr = saved_reg_value(1); + +#ifdef TRACE_DEBUG + if (DEBUGGING_ON) + (*pr)("Return value is = %x, function_addr is %x.\n", + ret_addr, function_addr); +#endif + + /* + * In support of this, continuation.s puts the low bit on the + * return address for continuations (the return address will never + * be used, so it's ok to do anything you want to it). + */ + if (ret_addr & 1) { + note = "<<can not trace past a continuation>>"; + ret_addr = 0; + } else if (ret_addr != 0x00) { + switch (is_jump_source_ok(ret_addr, function_addr)) { + case JUMP_SOURCE_IS_OK: + break; /* excellent */ + + case JUMP_SOURCE_IS_BAD: +#ifdef TRACE_DEBUG + if (DEBUGGING_ON) (*pr)("jump is bad\n"); +#endif + return 0; /* bummer */ + + case JUMP_SOURCE_IS_UNLIKELY: + next_address_likely_wrong = 1; + break; + } + } + + return ret_addr; +} + +static void +db_stack_trace_cmd2(db_regs_t *regs, int (*pr)(const char *, ...)) +{ + unsigned stack; + unsigned depth=1; + unsigned where; + unsigned ft; + unsigned pair[2]; + int i; + + /* + * Frame_is_sane returns: + * 1 if regs seems to be a reasonable kernel exception frame. + * 2 if regs seems to be a reasonable user exception frame + * (in the current task). + * 0 if this looks like neither. + */ + if ((ft = frame_is_sane(regs, 1)) == 0) { + (*pr)("Register frame 0x%x is suspicious; skipping trace\n", regs); + return; + } + + /* if user space and no user space trace specified, puke */ + if (ft == 2 && !(trace_flags & TRACE_USER_FLAG)) + return; + + /* fetch address */ + /* use sxip if valid, otherwise try snip or sfip */ + if (cputyp == CPU_88110) { + where = regs->exip & ~3; + } else { + where = ((regs->sxip & 2) ? regs->sxip : + ((regs->snip & 2) ? regs->snip : + regs->sfip) ) & ~3; + } + stack = regs->r[31]; + (*pr)("stack base = 0x%x\n", stack); + (*pr)("(0) "); /*depth of trace */ + if (trace_flags & TRACE_SHOWADDRESS_FLAG) + (*pr)("%08x ", where); + db_printsym(where, DB_STGY_PROC, pr); + clear_global_saved_regs(); + + /* see if this routine had a stack frame */ + if ((where=stack_decode(where, &stack, pr))==0) { + where = regs->r[1]; + (*pr)("(stackless)"); + } else { + print_args(); + if (trace_flags & TRACE_SHOWFRAME_FLAG) + (*pr)(" [frame 0x%x]", stack); + } + (*pr)("\n"); + if (note) { + (*pr)(" %s\n", note); + note = 0; + } + + do { + /* + * If requested, show preserved registers at the time + * the next-shown call was made. Only registers known to have + * changed from the last exception frame are shown, as others + * can be gotten at by looking at the exception frame. + */ + if (trace_flags & TRACE_SHOWCALLPRESERVED_FLAG) { + int r, title_printed = 0; + + for (r = FIRST_CALLPRESERVED_REG; r<=LAST_CALLPRESERVED_REG; r++) { + if (have_global_reg(r)) { + unsigned value = saved_reg_value(r); + if (title_printed == 0) { + title_printed = 1; + (*pr)("[in next func:"); + } + if (value == 0) + (*pr)(" r%d", r); + else if (value <= 9) + (*pr)(" r%d=%x", r, value); + else + (*pr)(" r%d=x%x", r, value); + } + } + if (title_printed) + (*pr)("]\n"); + } + + (*pr)("(%d)%c", depth++, next_address_likely_wrong ? '?':' '); + next_address_likely_wrong = 0; + + if (trace_flags & TRACE_SHOWADDRESS_FLAG) + (*pr)("%08x ", where); + db_printsym(where, DB_STGY_PROC, pr); + where = stack_decode(where, &stack, pr); + print_args(); + if (trace_flags & TRACE_SHOWFRAME_FLAG) + (*pr)(" [frame 0x%x]", stack); + (*pr)("\n"); + if (note) { + (*pr)(" %s\n", note); + note = 0; + } + } while (where); + + /* try to trace back over trap/exception */ + + stack &= ~7; /* double word aligned */ + /* take last top of stack, and try to find an exception frame near it */ + + i = FRAME_PLAY; + +#ifdef TRACE_DEBUG + if (DEBUGGING_ON) + (*pr)("(searching for exception frame at 0x%x)\n", stack); +#endif + + while (i) { + /* + * On the stack, a pointer to the exception frame is written + * in two adjacent words. In the case of a fault from the kernel, + * this should point to the frame right above them: + * + * Exception Frame Top + * .. + * Exception Frame Bottom <-- frame addr + * frame addr + * frame addr <-- stack pointer + * + * In the case of a fault from user mode, the top of stack + * will just have the address of the frame + * replicated twice. + * + * frame addr <-- top of stack + * frame addr + * + * Here we are just looking for kernel exception frames. + */ + + if (badwordaddr((vaddr_t)stack) || + badwordaddr((vaddr_t)(stack+4))) + break; + + db_read_bytes((vaddr_t)stack, 2*sizeof(int), (char *)pair); + + /* the pairs should match and equal stack+8 */ + if (pair[0] == pair[1]) { + if (pair[0] != stack+8) { + /* + if (!badwordaddr((vaddr_t)pair[0]) && (pair[0]!=0)) + (*pr)("stack_trace:found pair 0x%x but != to stack+8\n", + pair[0]); + */ + } + + else if (frame_is_sane((db_regs_t*)pair[0], 1) != 0) { + struct trapframe *frame = + (struct trapframe *)pair[0]; + + (*pr)("-------------- %s [EF: 0x%x] -------------\n", + m88k_exception_name(frame->tf_vector), + frame); + db_stack_trace_cmd2(&frame->tf_regs, pr); + return; + } +#ifdef TRACE_DEBUG + else if (DEBUGGING_ON) + (*pr)("pair matched, but frame at 0x%x looks insane\n", + stack+8); +#endif + } + stack += 8; + i--; + } + + /* + * If we go here, crawling back on the stack failed to find us + * a previous exception frame. Look for a user frame pointer + * pointed to by a word 8 bytes off of the top of the stack + * if the "u" option was specified. + */ + if (trace_flags & TRACE_USER_FLAG) { + struct trapframe *user; + + /* Make sure we are back on the right page */ + stack -= 4*FRAME_PLAY; + stack = stack & ~(KERNEL_STACK_SIZE-1); /* point to the bottom */ + stack += KERNEL_STACK_SIZE - 8; + + if (badwordaddr((vaddr_t)stack) || + badwordaddr((vaddr_t)stack)) + return; + + db_read_bytes((vaddr_t)stack, 2*sizeof(int), (char *)pair); + if (pair[0] != pair[1]) + return; + + /* have a hit */ + user = *((struct trapframe **)stack); + + if (frame_is_sane(&user->tf_regs, 1) == 2) { + (*pr)("---------------- %s [EF : 0x%x] -------------\n", + m88k_exception_name(user->tf_vector), user); + db_stack_trace_cmd2(&user->tf_regs, pr); + } + } +} + +/* + * stack trace - needs a pointer to a m88k saved state. + * + * If argument f is given, the stack pointer of each call frame is + * printed. + */ +void +db_stack_trace_print(db_expr_t addr, + int have_addr, + db_expr_t count, + char *modif, + int (*pr)(const char *, ...)) +{ + enum { + Default, Stack, Frame + } style = Default; + db_regs_t frame; + db_regs_t *regs; + union { + db_regs_t *frame; + db_expr_t num; + } arg; + + arg.num = addr; + + trace_flags = 0; /* flags will be set via modifers */ + + while (modif && *modif) { + switch (*modif++) { + case 'd': +#ifdef TRACE_DEBUG + trace_flags |= TRACE_DEBUG_FLAG; +#else + db_printtf("<debug trace not compiled in, ignoring>\n"); +#endif + break; + + case 's': style = Stack ; break; + case 'f': style = Frame ; break; + case 'p': trace_flags |= TRACE_SHOWCALLPRESERVED_FLAG; break; + case 'a': trace_flags |= TRACE_SHOWADDRESS_FLAG; break; + case 'F': trace_flags |= TRACE_SHOWFRAME_FLAG; break; + case 'u': trace_flags |= TRACE_USER_FLAG; break; + default: + (*pr)("unknown trace modifier [%c]\n", modif[-1]); + /*FALLTHROUGH*/ + case 'h': + (*pr)("usage: trace/[MODIFIER] [ARG]\n"); + (*pr)(" u = include user trace\n"); + (*pr)(" F = print stack frames\n"); + (*pr)(" a = show return addresses\n"); + (*pr)(" p = show call-preserved registers\n"); + (*pr)(" s = ARG is a stack pointer\n"); + (*pr)(" f = ARG is a frame pointer\n"); +#ifdef TRACE_DEBUG + (*pr)(" d = trace-debugging output\n"); +#endif + return; + } + } + + if (!have_addr && style != Default) { + (*pr)("expecting argument with /s or /f\n"); + return; + } + if (have_addr && style == Default) + style = Frame; + + switch (style) { + case Default: + regs = DDB_REGS; + break; + case Frame: + regs = arg.frame; + break; + case Stack: + { + unsigned val1, val2, sxip; + unsigned ptr; + bzero((void *)&frame, sizeof(frame)); +#define REASONABLE_FRAME_DISTANCE 2048 + + /* + * We've got to find the top of a stack frame so we can get both + * a PC and and real SP. + */ + for (ptr = arg.num;/**/; ptr += 4) { + /* Read a word from the named stack */ + if (db_trace_get_val(ptr, &val1) == 0) { + (*pr)("can't read from %x, aborting.\n", ptr); + return; + } + + /* + * See if it's a frame pointer.... if so it will be larger than + * the address it was taken from (i.e. point back up the stack) + * and we'll be able to read where it points. + */ + if (val1 <= ptr || + (val1 & 3) || + val1 > (ptr + REASONABLE_FRAME_DISTANCE)) + continue; + + /* peek at the next word to see if it could be a return address */ + if (db_trace_get_val(ptr, &sxip) == 0) { + (*pr)("can't read from %x, aborting.\n", ptr); + return; + } + if (sxip == 0 || !db_trace_get_val(sxip, &val2)) + continue; + + if (db_trace_get_val(val1, &val2) == 0) { + (*pr)("can't read from %x, aborting.\n", val1); + continue; + } + + /* + * The value we've just read will be either another frame pointer, + * or the start of another exception frame. + */ + if ( +#ifdef JEFF_DEBUG + val2 == 0 +#else + val2 == 0x12345678 +#endif + && db_trace_get_val(val1-4, &val2) && val2 == val1 + && db_trace_get_val(val1-8, &val2) && val2 == val1) { + /* we've found a frame, so the stack must have been good */ + (*pr)("%x looks like a frame, accepting %x\n",val1,ptr); + break; + } + + if (val2 > val1 && (val2 & 3) == 0) { + /* well, looks close enough to be another frame pointer */ + (*pr)("*%x = %x looks like a stack frame pointer, accepting %x\n", val1, val2, ptr); + break; + } + } + frame.r[31] = ptr; + frame.epsr = 0x800003f0U; + if (cputyp != CPU_88110) { + frame.sxip = sxip | 2; + frame.snip = frame.sxip + 4; + frame.sfip = frame.snip + 4; + } + (*pr)("[r31=%x, %sxip=%x]\n", frame.r[31], + cputyp == CPU_88110 ? "e" : "s", frame.sxip); + regs = &frame; + } + } + db_stack_trace_cmd2(regs, pr); +} diff --git a/sys/arch/luna88k/dev/if_le.c b/sys/arch/luna88k/dev/if_le.c new file mode 100644 index 00000000000..4192bbd21d8 --- /dev/null +++ b/sys/arch/luna88k/dev/if_le.c @@ -0,0 +1,204 @@ +/* $OpenBSD: if_le.c,v 1.1 2004/04/21 15:23:52 aoyama Exp $ */ +/* $NetBSD: if_le.c,v 1.33 1996/11/20 18:56:52 gwr Exp $ */ + +/*- + * Copyright (c) 1996 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Adam Glass and Gordon W. Ross. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* based on OpenBSD: sys/arch/sun3/dev/if_le.c */ + +#include "bpfilter.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/syslog.h> +#include <sys/socket.h> +#include <sys/device.h> + +#include <net/if.h> + +#ifdef INET +#include <netinet/in.h> +#include <netinet/if_ether.h> +#endif + +#include <net/if_media.h> + +#include <machine/autoconf.h> +#include <machine/cpu.h> + +#include <dev/ic/am7990reg.h> +#include <dev/ic/am7990var.h> + +#include <luna88k/luna88k/isr.h> + +/* + * LANCE registers. + * The real stuff is in dev/ic/am7990reg.h + */ +struct lereg1 { + volatile u_int16_t ler1_rdp; /* data port */ + volatile unsigned : 16 ; /* LUNA-88K2 has a 16 bit gap */ + volatile u_int16_t ler1_rap; /* register select port */ +}; + +/* + * Ethernet software status per interface. + * The real stuff is in dev/ic/am7990var.h + */ +struct le_softc { + struct am7990_softc sc_am7990; /* glue to MI code */ + + struct lereg1 *sc_r1; /* LANCE registers */ +}; + +static int le_match(struct device *, void *, void *); +static void le_attach(struct device *, struct device *, void *); + +struct cfattach le_ca = { + sizeof(struct le_softc), le_match, le_attach +}; + +hide void lewrcsr(struct am7990_softc *, u_int16_t, u_int16_t); +hide u_int16_t lerdcsr(struct am7990_softc *, u_int16_t); +hide void myetheraddr(u_int8_t *); + +hide void +lewrcsr(sc, port, val) + struct am7990_softc *sc; + u_int16_t port, val; +{ + register struct lereg1 *ler1 = ((struct le_softc *)sc)->sc_r1; + + ler1->ler1_rap = port; + ler1->ler1_rdp = val; +} + +hide u_int16_t +lerdcsr(sc, port) + struct am7990_softc *sc; + u_int16_t port; +{ + register struct lereg1 *ler1 = ((struct le_softc *)sc)->sc_r1; + u_int16_t val; + + ler1->ler1_rap = port; + val = ler1->ler1_rdp; + return (val); +} + +static int +le_match(parent, cf, aux) + struct device *parent; + void *cf, *aux; +{ + struct mainbus_attach_args *ma = aux; + + if (strcmp(ma->ma_name, le_cd.cd_name)) + return (0); + + return (1); +} + +void +le_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct le_softc *lesc = (struct le_softc *)self; + struct am7990_softc *sc = &lesc->sc_am7990; + struct mainbus_attach_args *ma = aux; + + lesc->sc_r1 = (struct lereg1 *)ma->ma_addr; /* LANCE */ + + sc->sc_mem = (void *)0x71000000; /* SRAM */ + sc->sc_conf3 = LE_C3_BSWP; + sc->sc_addr = (u_long)sc->sc_mem & 0xffffff; + sc->sc_memsize = 64 * 1024; /* 64KB */ + + myetheraddr(sc->sc_arpcom.ac_enaddr); + + sc->sc_copytodesc = am7990_copytobuf_contig; + sc->sc_copyfromdesc = am7990_copyfrombuf_contig; + sc->sc_copytobuf = am7990_copytobuf_contig; + sc->sc_copyfrombuf = am7990_copyfrombuf_contig; + sc->sc_zerobuf = am7990_zerobuf_contig; + + sc->sc_rdcsr = lerdcsr; + sc->sc_wrcsr = lewrcsr; + sc->sc_hwreset = NULL; + sc->sc_hwinit = NULL; + + am7990_config(sc); + + isrlink_autovec(am7990_intr, (void *)sc, ma->ma_ilvl, ISRPRI_NET); +} + +/* + * Taken from NetBSD/luna68k + * + * LUNA-88K2 (and LUNA-88K?) has 16Kbit NVSRAM on its ethercard, whose + * contents are accessible 4bit-wise by ctl register operation. The + * register is mapped at 0xF1000008. + */ + +hide void +myetheraddr(ether) + u_int8_t *ether; +{ + unsigned i, loc; + volatile struct { u_int32_t ctl; } *ds1220; + + ds1220 = (void *)0xF1000008; + loc = 12; + for (i = 0; i < 6; i++) { + unsigned u, l, hex; + + ds1220->ctl = (loc) << 16; + u = 0xf0 & (ds1220->ctl >> 12); + ds1220->ctl = (loc + 1) << 16; + l = 0x0f & (ds1220->ctl >> 16); + hex = (u < '9') ? l : l + 9; + + ds1220->ctl = (loc + 2) << 16; + u = 0xf0 & (ds1220->ctl >> 12); + ds1220->ctl = (loc + 3) << 16; + l = 0x0f & (ds1220->ctl >> 16); + + ether[i] = ((u < '9') ? l : l + 9) | (hex << 4); + loc += 4; + } +} diff --git a/sys/arch/luna88k/dev/lcd.c b/sys/arch/luna88k/dev/lcd.c new file mode 100644 index 00000000000..cfba031871c --- /dev/null +++ b/sys/arch/luna88k/dev/lcd.c @@ -0,0 +1,173 @@ +/* $OpenBSD: lcd.c,v 1.1 2004/04/21 15:23:52 aoyama Exp $ */ +/* $NetBSD: lcd.c,v 1.2 2000/01/07 05:13:08 nisimura Exp $ */ + +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Tohru Nishimura. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* Taken from NetBSD/luna68k */ + +/* + * XXX + * Following code segments are subject to change. + * XXX + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> + +#define PIO1_MODE_OUTPUT 0x84 +#define PIO1_MODE_INPUT 0x94 + +#define POWER 0x10 + +#define ENABLE 0x80 +#define DISABLE 0x00 + +#define WRITE_CMD (0x00 | 0x00) +#define WRITE_DATA (0x00 | 0x40) +#define READ_BUSY (0x20 | 0x00) +#define READ_DATA (0x20 | 0x40) + +#define LCD_INIT 0x38 +#define LCD_ENTRY 0x06 +#define LCD_ON 0x0c +#define LCD_CLS 0x01 +#define LCD_HOME 0x02 +#define LCD_LOCATE(X, Y) (((Y) & 1 ? 0xc0 : 0x80) | ((X) & 0x0f)) + +struct pio { + volatile u_int8_t portA; + volatile unsigned : 24; + volatile u_int8_t portB; + volatile unsigned : 24; + volatile u_int8_t portC; + volatile unsigned : 24; + volatile u_int8_t cntrl; + volatile unsigned : 24; +}; + +void lcdbusywait(void); +void lcdput(int); +void lcdctrl(int); +void lcdshow(const char *); +void greeting(void); + /* "1234567890123456" */ +static const char lcd_boot_message1[] = "OpenBSD/luna88k "; +static const char lcd_boot_message2[] = " SX-9100/DT "; + +void +lcdbusywait() +{ + struct pio *p1 = (struct pio *)0x4D000000; + int msb, s; + + s = splhigh(); + p1->cntrl = PIO1_MODE_INPUT; + p1->portC = POWER | READ_BUSY | ENABLE; + splx(s); + + do { + msb = p1->portA & ENABLE; + delay(5); + } while (msb != 0); + + s = splhigh(); + p1->portC = POWER | READ_BUSY | DISABLE; + splx(s); +} + +void +lcdput(cc) + int cc; +{ + struct pio *p1 = (struct pio *)0x4D000000; + int s; + + lcdbusywait(); + + s = splhigh(); + p1->cntrl = PIO1_MODE_OUTPUT; + + p1->portC = POWER | WRITE_DATA | ENABLE; + p1->portA = cc; + p1->portC = POWER | WRITE_DATA | DISABLE; + splx(s); +} + +void +lcdctrl(cc) + int cc; +{ + struct pio *p1 = (struct pio *)0x4D000000; + int s; + + lcdbusywait(); + + s = splhigh(); + p1->cntrl = PIO1_MODE_OUTPUT; + + p1->portC = POWER | WRITE_CMD | ENABLE; + p1->portA = cc; + p1->portC = POWER | WRITE_CMD | DISABLE; + splx(s); +} + +void +lcdshow(s) + const char *s; +{ + int cc; + + while ((cc = *s++) != '\0') + lcdput(cc); +} + +void +greeting() +{ + lcdctrl(LCD_INIT); + lcdctrl(LCD_ENTRY); + lcdctrl(LCD_ON); + + lcdctrl(LCD_CLS); + lcdctrl(LCD_HOME); + + lcdctrl(LCD_LOCATE(0, 0)); + lcdshow(lcd_boot_message1); + lcdctrl(LCD_LOCATE(0, 1)); + lcdshow(lcd_boot_message2); +} diff --git a/sys/arch/luna88k/dev/lunafb.c b/sys/arch/luna88k/dev/lunafb.c new file mode 100644 index 00000000000..931c5e7a607 --- /dev/null +++ b/sys/arch/luna88k/dev/lunafb.c @@ -0,0 +1,510 @@ +/* $OpenBSD: lunafb.c,v 1.1 2004/04/21 15:23:52 aoyama Exp $ */ +/* $NetBSD: lunafb.c,v 1.7.6.1 2002/08/07 01:48:34 lukem Exp $ */ + +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Tohru Nishimura. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/conf.h> +#include <sys/device.h> +#include <sys/ioctl.h> +#include <sys/malloc.h> +#include <sys/mman.h> +#include <sys/proc.h> +#include <sys/tty.h> +#include <sys/errno.h> +#include <sys/buf.h> + +#include <uvm/uvm_extern.h> + +#include <dev/rcons/raster.h> +#include <dev/wscons/wsconsio.h> +#include <dev/wscons/wscons_raster.h> +#include <dev/wscons/wsdisplayvar.h> + +#include <machine/cpu.h> +#include <machine/autoconf.h> + +struct bt454 { + u_int8_t bt_addr; /* map address register */ + u_int8_t bt_cmap; /* colormap data register */ +}; + +struct bt458 { + u_int8_t bt_addr; /* map address register */ + unsigned :24; + u_int8_t bt_cmap; /* colormap data register */ + unsigned :24; + u_int8_t bt_ctrl; /* control register */ + unsigned :24; + u_int8_t bt_omap; /* overlay (cursor) map register */ + unsigned :24; +}; + +#define OMFB_RFCNT 0xB1000000 /* video h-origin/v-origin */ +#define OMFB_PLANEMASK 0xB1040000 /* planemask register */ +#define OMFB_FB_WADDR 0xB1080008 /* common plane */ +#define OMFB_FB_RADDR 0xB10C0008 /* plane #0 */ +#define OMFB_ROPFUNC 0xB12C0000 /* ROP function code */ +#define OMFB_RAMDAC 0xC1100000 /* Bt454/Bt458 RAMDAC */ +#define OMFB_SIZE (0xB1300000 - 0xB1080000 + NBPG) + +struct om_hwdevconfig { + int dc_wid; /* width of frame buffer */ + int dc_ht; /* height of frame buffer */ + int dc_depth; /* depth, bits per pixel */ + int dc_rowbytes; /* bytes in a FB scan line */ + int dc_cmsize; /* colormap size */ + vaddr_t dc_videobase; /* base of flat frame buffer */ + struct raster dc_raster; /* raster description */ + struct rcons dc_rcons; /* raster blitter control info */ +}; + +struct hwcmap { +#define CMAP_SIZE 256 + u_int8_t r[CMAP_SIZE]; + u_int8_t g[CMAP_SIZE]; + u_int8_t b[CMAP_SIZE]; +}; + +struct omfb_softc { + struct device sc_dev; /* base device */ + struct om_hwdevconfig *sc_dc; /* device configuration */ + struct hwcmap sc_cmap; /* software copy of colormap */ + int nscreens; +}; + +int omgetcmap(struct omfb_softc *, struct wsdisplay_cmap *); +int omsetcmap(struct omfb_softc *, struct wsdisplay_cmap *); + +struct om_hwdevconfig omfb_console_dc; +void omfb_getdevconfig(paddr_t, struct om_hwdevconfig *); + +extern struct wsdisplay_emulops omfb_emulops; + +struct wsscreen_descr omfb_stdscreen = { + "std", 0, 0, + &omfb_emulops, + 0, 0, + 0 +}; + +const struct wsscreen_descr *_omfb_scrlist[] = { + &omfb_stdscreen, +}; + +const struct wsscreen_list omfb_screenlist = { + sizeof(_omfb_scrlist) / sizeof(struct wsscreen_descr *), _omfb_scrlist +}; + +int omfbioctl(void *, u_long, caddr_t, int, struct proc *); +paddr_t omfbmmap(void *, off_t, int); +int omfb_alloc_screen(void *, const struct wsscreen_descr *, + void **, int *, int *, long *); +void omfb_free_screen(void *, void *); +int omfb_show_screen(void *, void *, int, + void (*) (void *, int, int), void *); + +const struct wsdisplay_accessops omfb_accessops = { + omfbioctl, + omfbmmap, + omfb_alloc_screen, + omfb_free_screen, + omfb_show_screen, + NULL, /* load_font */ + NULL, /* scrollback */ + NULL, /* getchar */ + NULL /* burner */ +}; + +int omfbmatch(struct device *, void *, void *); +void omfbattach(struct device *, struct device *, void *); + +const struct cfattach fb_ca = { + sizeof(struct omfb_softc), omfbmatch, omfbattach +}; + +const struct cfdriver fb_cd = { + NULL, "fb", DV_DULL +}; + +extern int hwplanemask; /* hardware planemask; retrieved at boot */ + +int omfb_console; +int omfb_cnattach(void); + +int +omfbmatch(parent, cf, aux) + struct device *parent; + void *cf, *aux; +{ + struct mainbus_attach_args *ma = aux; + + if (strcmp(ma->ma_name, fb_cd.cd_name)) + return (0); +#if 0 /* XXX badaddr() bombs if no framebuffer is installed */ + if (badaddr((caddr_t)ma->ma_addr, 4)) + return (0); +#else + if (hwplanemask == 0) + return (0); +#endif + return (1); +} + +void +omfbattach(parent, self, args) + struct device *parent, *self; + void *args; +{ + struct omfb_softc *sc = (struct omfb_softc *)self; + struct wsemuldisplaydev_attach_args waa; + + if (omfb_console) { + sc->sc_dc = &omfb_console_dc; + sc->nscreens = 1; + } + else { + sc->sc_dc = (struct om_hwdevconfig *) + malloc(sizeof(struct om_hwdevconfig), M_DEVBUF, M_WAITOK); + omfb_getdevconfig(OMFB_FB_WADDR, sc->sc_dc); + } + printf(": %d x %d, %dbpp\n", sc->sc_dc->dc_wid, sc->sc_dc->dc_ht, + sc->sc_dc->dc_depth); + +#if 0 /* WHITE on BLACK */ + cm = &sc->sc_cmap; + memset(cm, 255, sizeof(struct hwcmap)); + cm->r[0] = cm->g[0] = cm->b[0] = 0; +#endif + waa.console = omfb_console; + waa.scrdata = &omfb_screenlist; + waa.accessops = &omfb_accessops; + waa.accesscookie = sc; + + config_found(self, &waa, wsemuldisplaydevprint); +} + +/* EXPORT */ int +omfb_cnattach() +{ + struct om_hwdevconfig *dc = &omfb_console_dc; + long defattr; + + omfb_getdevconfig(OMFB_FB_WADDR, dc); + (*omfb_emulops.alloc_attr)(&dc->dc_rcons, 0, 0, 0, &defattr); + wsdisplay_cnattach(&omfb_stdscreen, &dc->dc_rcons, 0, 0, defattr); + omfb_console = 1; + return (0); +} + +int +omfbioctl(v, cmd, data, flag, p) + void *v; + u_long cmd; + caddr_t data; + int flag; + struct proc *p; +{ + struct omfb_softc *sc = v; + struct om_hwdevconfig *dc = sc->sc_dc; + + switch (cmd) { + case WSDISPLAYIO_GTYPE: + *(u_int *)data = WSDISPLAY_TYPE_UNKNOWN; /* XXX for now */ + break; + + case WSDISPLAYIO_GINFO: +#define wsd_fbip ((struct wsdisplay_fbinfo *)data) + wsd_fbip->height = dc->dc_ht; + wsd_fbip->width = dc->dc_wid; + wsd_fbip->depth = dc->dc_depth; + wsd_fbip->cmsize = dc->dc_cmsize; +#undef fbt + break; + + case WSDISPLAYIO_GETCMAP: + return omgetcmap(sc, (struct wsdisplay_cmap *)data); + + case WSDISPLAYIO_PUTCMAP: + return omsetcmap(sc, (struct wsdisplay_cmap *)data); + + case WSDISPLAYIO_SVIDEO: + case WSDISPLAYIO_GVIDEO: + case WSDISPLAYIO_GCURPOS: + case WSDISPLAYIO_SCURPOS: + case WSDISPLAYIO_GCURMAX: + case WSDISPLAYIO_GCURSOR: + case WSDISPLAYIO_SCURSOR: + return (-1); + } + + return (0); +} + +/* + * Return the address that would map the given device at the given + * offset, allowing for the given protection, or return -1 for error. + */ + +paddr_t +omfbmmap(v, offset, prot) + void *v; + off_t offset; + int prot; +{ + struct omfb_softc *sc = v; + + if (offset & PGOFSET) + return (-1); + if (offset >= OMFB_SIZE || offset < 0) + return (-1); + + return atop(trunc_page(sc->sc_dc->dc_videobase) + offset); +} + +int +omgetcmap(sc, p) + struct omfb_softc *sc; + struct wsdisplay_cmap *p; +{ + u_int index = p->index, count = p->count; + unsigned int cmsize; + int error; + + cmsize = sc->sc_dc->dc_cmsize; + if (index >= cmsize || count > cmsize - index) + return (EINVAL); + + error = copyout(&sc->sc_cmap.r[index], p->red, count); + if (error != 0) + return (error); + error = copyout(&sc->sc_cmap.g[index], p->green, count); + if (error != 0) + return (error); + error = copyout(&sc->sc_cmap.b[index], p->blue, count); + if (error != 0) + return (error); + + return (0); +} + +int +omsetcmap(sc, p) + struct omfb_softc *sc; + struct wsdisplay_cmap *p; +{ + u_int index = p->index, count = p->count; + unsigned int cmsize, i; + int error; + + cmsize = sc->sc_dc->dc_cmsize; + if (index >= cmsize || count > cmsize - index) + return (EINVAL); + + error = copyin(p->red, &sc->sc_cmap.r[index], count); + if (error != 0) + return (error); + error = copyin(p->green, &sc->sc_cmap.g[index], count); + if (error != 0) + return (error); + error = copyin(p->blue, &sc->sc_cmap.b[index], count); + if (error != 0) + return (error); + + if (hwplanemask == 0x0f) { + struct bt454 *odac = (struct bt454 *)OMFB_RAMDAC; + odac->bt_addr = index; + for (i = index; i < count; i++) { + odac->bt_cmap = sc->sc_cmap.r[i]; + odac->bt_cmap = sc->sc_cmap.g[i]; + odac->bt_cmap = sc->sc_cmap.b[i]; + } + } + else if (hwplanemask == 0xff) { + struct bt458 *ndac = (struct bt458 *)OMFB_RAMDAC; + ndac->bt_addr = index; + for (i = index; i < count; i++) { + ndac->bt_cmap = sc->sc_cmap.r[i]; + ndac->bt_cmap = sc->sc_cmap.g[i]; + ndac->bt_cmap = sc->sc_cmap.b[i]; + } + } + return (0); +} + +void +omfb_getdevconfig(paddr, dc) + paddr_t paddr; + struct om_hwdevconfig *dc; +{ + int bpp, i; + struct raster *rap; + struct rcons *rcp; + union { + struct { short h, v; } p; + u_int32_t u; + } rfcnt; + + switch (hwplanemask) { + case 0xff: + bpp = 8; /* XXX check monochrome bit in DIPSW */ + break; + default: + case 0x0f: + bpp = 4; /* XXX check monochrome bit in DIPSW */ + break; + case 1: + bpp = 1; + break; + } + dc->dc_wid = 1280; + dc->dc_ht = 1024; + dc->dc_depth = bpp; + dc->dc_rowbytes = 2048 / 8; + dc->dc_cmsize = (bpp == 1) ? 0 : 1 << bpp; + dc->dc_videobase = paddr; + +#if 0 /* WHITE on BLACK XXX experiment resulted in WHITE on SKYBLUE... */ + if (hwplanemask == 0x0f) { + /* XXX Need Bt454 initialization */ + struct bt454 *odac = (struct bt454 *)OMFB_RAMDAC; + odac->bt_addr = 0; + odac->bt_cmap = 0; + odac->bt_cmap = 0; + odac->bt_cmap = 0; + for (i = 1; i < 16; i++) { + odac->bt_cmap = 255; + odac->bt_cmap = 255; + odac->bt_cmap = 255; + } + } + else if (hwplanemask == 0xff) { + struct bt458 *ndac = (struct bt458 *)OMFB_RAMDAC; + + ndac->bt_addr = 0x04; + ndac->bt_ctrl = 0xff; /* all planes will be read */ + ndac->bt_ctrl = 0x00; /* all planes have non-blink */ + ndac->bt_ctrl = 0x43; /* pallete enabled, ovly plane */ + ndac->bt_ctrl = 0x00; /* no test mode */ + ndac->bt_addr = 0; + ndac->bt_cmap = 0; + ndac->bt_cmap = 0; + ndac->bt_cmap = 0; + for (i = 1; i < 256; i++) { + ndac->bt_cmap = 255; + ndac->bt_cmap = 255; + ndac->bt_cmap = 255; + } + } +#endif + + /* adjust h/v orgin on screen */ + rfcnt.p.h = 7; + rfcnt.p.v = -27; + *(u_int32_t *)OMFB_RFCNT = rfcnt.u; /* single write of 0x007ffe6 */ + + /* clear the screen */ + *(u_int32_t *)OMFB_PLANEMASK = 0xff; + ((u_int32_t *)OMFB_ROPFUNC)[5] = ~0; /* ROP copy */ + for (i = 0; i < dc->dc_ht * dc->dc_rowbytes/sizeof(u_int32_t); i++) + *((u_int32_t *)dc->dc_videobase + i) = 0; + *(u_int32_t *)OMFB_PLANEMASK = 0x01; + + /* initialize the raster */ + rap = &dc->dc_raster; + rap->width = dc->dc_wid; + rap->height = dc->dc_ht; + rap->depth = dc->dc_depth; + rap->linelongs = dc->dc_rowbytes / sizeof(u_int32_t); + rap->pixels = (u_int32_t *)dc->dc_videobase; + + /* initialize the raster console blitter */ + rcp = &dc->dc_rcons; + rcp->rc_sp = rap; + rcp->rc_crow = rcp->rc_ccol = -1; + rcp->rc_crowp = &rcp->rc_crow; + rcp->rc_ccolp = &rcp->rc_ccol; + rcons_init(rcp, 34, 80); + + omfb_stdscreen.nrows = dc->dc_rcons.rc_maxrow; + omfb_stdscreen.ncols = dc->dc_rcons.rc_maxcol; +} + +int +omfb_alloc_screen(v, type, cookiep, curxp, curyp, attrp) + void *v; + const struct wsscreen_descr *type; + void **cookiep; + int *curxp, *curyp; + long *attrp; +{ + struct omfb_softc *sc = v; + long defattr; + + if (sc->nscreens > 0) + return (ENOMEM); + + *cookiep = &sc->sc_dc->dc_rcons; /* one and only for now */ + *curxp = 0; + *curyp = 0; + (*omfb_emulops.alloc_attr)(&sc->sc_dc->dc_rcons, 0, 0, 0, &defattr); + *attrp = defattr; + sc->nscreens++; + return (0); +} + +void +omfb_free_screen(v, cookie) + void *v; + void *cookie; +{ + struct omfb_softc *sc = v; + + sc->nscreens--; +} + +int +omfb_show_screen(v, cookie, waitok, cb, cbarg) + void *v; + void *cookie; + int waitok; + void (*cb)(void *, int, int); + void *cbarg; +{ + return 0; +} diff --git a/sys/arch/luna88k/dev/lunaws.c b/sys/arch/luna88k/dev/lunaws.c new file mode 100644 index 00000000000..e76ab8df98c --- /dev/null +++ b/sys/arch/luna88k/dev/lunaws.c @@ -0,0 +1,545 @@ +/* $NetBSD: lunaws.c,v 1.6 2002/03/17 19:40:42 atatat Exp $ */ + +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Tohru Nishimura. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "wsmouse.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/conf.h> +#include <sys/device.h> + +#include <dev/wscons/wsconsio.h> +#include <dev/wscons/wskbdvar.h> +#include <dev/wscons/wsksymdef.h> +#include <dev/wscons/wsksymvar.h> +#include <dev/wscons/wsmousevar.h> + +#include <luna88k/dev/sioreg.h> +#include <luna88k/dev/siovar.h> + +static const u_int8_t ch1_regs[6] = { + WR0_RSTINT, /* Reset E/S Interrupt */ + WR1_RXALLS, /* Rx per char, No Tx */ + 0, /* */ + WR3_RX8BIT | WR3_RXENBL, /* Rx */ + WR4_BAUD96 | WR4_STOP1 | WR4_NPARITY, /* Tx/Rx */ + WR5_TX8BIT | WR5_TXENBL, /* Tx */ +}; + +struct ws_softc { + struct device sc_dv; + struct sioreg *sc_ctl; + u_int8_t sc_wr[6]; + struct device *sc_wskbddev; +#if NWSMOUSE > 0 + struct device *sc_wsmousedev; + int sc_msreport; + int buttons, dx, dy; +#endif +}; + +void omkbd_input(void *, int); +void omkbd_decode(void *, int, u_int *, int *); +int omkbd_enable(void *, int); +void omkbd_set_leds(void *, int); +int omkbd_ioctl(void *, u_long, caddr_t, int, struct proc *); + +struct wscons_keydesc omkbd_keydesctab[]; + +const struct wskbd_mapdata omkbd_keymapdata = { + omkbd_keydesctab, +#ifdef OMKBD_LAYOUT + OMKBD_LAYOUT, +#else + KB_JP, +#endif +}; + +const struct wskbd_accessops omkbd_accessops = { + omkbd_enable, + omkbd_set_leds, + omkbd_ioctl, +}; + +void ws_cnattach(void); +void ws_cngetc(void *, u_int *, int *); +void ws_cnpollc(void *, int); +const struct wskbd_consops ws_consops = { + ws_cngetc, + ws_cnpollc, + NULL /* bell */ +}; + +#if NWSMOUSE > 0 +int omms_enable(void *); +int omms_ioctl(void *, u_long, caddr_t, int, struct proc *); +void omms_disable(void *); + +const struct wsmouse_accessops omms_accessops = { + omms_enable, + omms_ioctl, + omms_disable, +}; +#endif + +void wsintr(int); + +int wsmatch(struct device *, void *, void *); +void wsattach(struct device *, struct device *, void *); +int ws_submatch_kbd(struct device *, void *, void *); +#if NWSMOUSE > 0 +int ws_submatch_mouse(struct device *, void *, void *); +#endif + +const struct cfattach ws_ca = { + sizeof(struct ws_softc), wsmatch, wsattach +}; + +const struct cfdriver ws_cd = { + NULL, "ws", DV_TTY +}; + +extern int syscngetc(dev_t); +extern void syscnputc(dev_t, int); + +int +wsmatch(parent, match, aux) + struct device *parent; + void *match, *aux; +{ + struct sio_attach_args *args = aux; + + if (args->channel != 1) + return 0; + return 1; +} + +void +wsattach(parent, self, aux) + struct device *parent; + struct device *self; + void *aux; +{ + struct ws_softc *sc = (struct ws_softc *)self; + struct sio_softc *scp = (struct sio_softc *)parent; + struct sio_attach_args *args = aux; + struct wskbddev_attach_args a; + + sc->sc_ctl = (struct sioreg *)scp->scp_ctl + 1; + bcopy(ch1_regs, sc->sc_wr, sizeof(ch1_regs)); + scp->scp_intr[1] = wsintr; + + setsioreg(sc->sc_ctl, WR0, sc->sc_wr[WR0]); + setsioreg(sc->sc_ctl, WR4, sc->sc_wr[WR4]); + setsioreg(sc->sc_ctl, WR3, sc->sc_wr[WR3]); + setsioreg(sc->sc_ctl, WR5, sc->sc_wr[WR5]); + setsioreg(sc->sc_ctl, WR0, sc->sc_wr[WR0]); + setsioreg(sc->sc_ctl, WR1, sc->sc_wr[WR1]); + + syscnputc((dev_t)1, 0x20); /* keep quiet mouse */ + + printf("\n"); + + a.console = (args->hwflags == 1); + a.keymap = &omkbd_keymapdata; + a.accessops = &omkbd_accessops; + a.accesscookie = (void *)sc; + sc->sc_wskbddev = config_found_sm(self, &a, wskbddevprint, + ws_submatch_kbd); + +#if NWSMOUSE > 0 + { + struct wsmousedev_attach_args b; + b.accessops = &omms_accessops; + b.accesscookie = (void *)sc; + sc->sc_wsmousedev = config_found_sm(self, &b, wsmousedevprint, + ws_submatch_mouse); + sc->sc_msreport = 0; + } +#endif +} + +int +ws_submatch_kbd(parent, match, aux) + struct device *parent; + void *match, *aux; +{ + struct cfdata *cf = match; + + if (strcmp(cf->cf_driver->cd_name, "wskbd")) + return (0); + return ((*cf->cf_attach->ca_match)(parent, cf, aux)); +} + +#if NWSMOUSE > 0 + +int +ws_submatch_mouse(parent, match, aux) + struct device *parent; + void *match, *aux; +{ + struct cfdata *cf = match; + + if (strcmp(cf->cf_driver->cd_name, "wsmouse")) + return (0); + return ((*cf->cf_attach->ca_match)(parent, cf, aux)); +} + +#endif + +/*ARGSUSED*/ +void +wsintr(chan) + int chan; +{ + struct ws_softc *sc = ws_cd.cd_devs[0]; + struct sioreg *sio = sc->sc_ctl; + u_int code; + int rr; + + rr = getsiocsr(sio); + if (rr & RR_RXRDY) { + do { + code = sio->sio_data; + if (rr & (RR_FRAMING | RR_OVERRUN | RR_PARITY)) { + sio->sio_cmd = WR0_ERRRST; + continue; + } +#if NWSMOUSE > 0 + /* + * if (code >= 0x80 && code <= 0x87), then + * it's the first byte of 3 byte long mouse report + * code[0] & 07 -> LMR button condition + * code[1], [2] -> x,y delta + * otherwise, key press or release event. + */ + if (sc->sc_msreport == 0) { + if (code < 0x80 || code > 0x87) { + omkbd_input(sc, code); + continue; + } + code = (code & 07) ^ 07; + /* LMR->RML: wsevent counts 0 for leftmost */ + sc->buttons = (code & 02); + if (code & 01) + sc->buttons |= 04; + if (code & 04) + sc->buttons |= 01; + sc->sc_msreport = 1; + } + else if (sc->sc_msreport == 1) { + sc->dx = (signed char)code; + sc->sc_msreport = 2; + } + else if (sc->sc_msreport == 2) { + sc->dy = (signed char)code; + if (sc->sc_wsmousedev != NULL) + wsmouse_input(sc->sc_wsmousedev, + sc->buttons, sc->dx, sc->dy, 0, 0); + sc->sc_msreport = 0; + } +#else + omkbd_input(sc, code); +#endif + } while ((rr = getsiocsr(sio)) & RR_RXRDY); + } + if (rr && RR_TXRDY) + sio->sio_cmd = WR0_RSTPEND; + /* not capable of transmit, yet */ +} + +void +omkbd_input(v, data) + void *v; + int data; +{ + struct ws_softc *sc = v; + u_int type; + int key; + + omkbd_decode(v, data, &type, &key); + if (sc->sc_wskbddev != NULL) + wskbd_input(sc->sc_wskbddev, type, key); +} + +void +omkbd_decode(v, datain, type, dataout) + void *v; + int datain; + u_int *type; + int *dataout; +{ + *type = (datain & 0x80) ? WSCONS_EVENT_KEY_UP : WSCONS_EVENT_KEY_DOWN; + *dataout = datain & 0x7f; +} + +#define KC(n) KS_KEYCODE(n) + +static const keysym_t omkbd_keydesc_1[] = { +/* pos command normal shifted */ + KC(0x9), KS_Tab, + KC(0xa), KS_Control_L, + KC(0xb), KS_Mode_switch, /* Kana */ + KC(0xc), KS_Shift_R, + KC(0xd), KS_Shift_L, + KC(0xe), KS_Caps_Lock, + KC(0xf), KS_Meta_L, /* Zenmen */ + KC(0x10), KS_Escape, + KC(0x11), KS_BackSpace, + KC(0x12), KS_Return, + KC(0x14), KS_space, + KC(0x15), KS_Delete, + KC(0x16), KS_Alt_L, /* Henkan */ + KC(0x17), KS_Alt_R, /* Kakutei */ + KC(0x18), KS_f11, /* Shokyo */ + KC(0x19), KS_f12, /* Yobidashi */ + KC(0x1a), KS_f13, /* Bunsetsu L */ + KC(0x1b), KS_f14, /* Bunsetsu R */ + KC(0x1c), KS_KP_Up, + KC(0x1d), KS_KP_Left, + KC(0x1e), KS_KP_Right, + KC(0x1f), KS_KP_Down, + /* KC(0x20), KS_f11, */ + /* KC(0x21), KS_f12, */ + KC(0x22), KS_1, KS_exclam, + KC(0x23), KS_2, KS_quotedbl, + KC(0x24), KS_3, KS_numbersign, + KC(0x25), KS_4, KS_dollar, + KC(0x26), KS_5, KS_percent, + KC(0x27), KS_6, KS_ampersand, + KC(0x28), KS_7, KS_apostrophe, + KC(0x29), KS_8, KS_parenleft, + KC(0x2a), KS_9, KS_parenright, + KC(0x2b), KS_0, + KC(0x2c), KS_minus, KS_equal, + KC(0x2d), KS_asciicircum, KS_asciitilde, + KC(0x2e), KS_backslash, KS_bar, + /* KC(0x30), KS_f13, */ + /* KC(0x31), KS_f14, */ + KC(0x32), KS_q, + KC(0x33), KS_w, + KC(0x34), KS_e, + KC(0x35), KS_r, + KC(0x36), KS_t, + KC(0x37), KS_y, + KC(0x38), KS_u, + KC(0x39), KS_i, + KC(0x3a), KS_o, + KC(0x3b), KS_p, + KC(0x3c), KS_at, KS_grave, + KC(0x3d), KS_bracketleft, KS_braceleft, + KC(0x42), KS_a, + KC(0x43), KS_s, + KC(0x44), KS_d, + KC(0x45), KS_f, + KC(0x46), KS_g, + KC(0x47), KS_h, + KC(0x48), KS_j, + KC(0x49), KS_k, + KC(0x4a), KS_l, + KC(0x4b), KS_semicolon, KS_plus, + KC(0x4c), KS_colon, KS_asterisk, + KC(0x4d), KS_bracketright, KS_braceright, + KC(0x52), KS_z, + KC(0x53), KS_x, + KC(0x54), KS_c, + KC(0x55), KS_v, + KC(0x56), KS_b, + KC(0x57), KS_n, + KC(0x58), KS_m, + KC(0x59), KS_comma, KS_less, + KC(0x5a), KS_period, KS_greater, + KC(0x5b), KS_slash, KS_question, + KC(0x5c), KS_underscore, + KC(0x60), KS_KP_Delete, + KC(0x61), KS_KP_Add, + KC(0x62), KS_KP_Subtract, + KC(0x63), KS_KP_7, + KC(0x64), KS_KP_8, + KC(0x65), KS_KP_9, + KC(0x66), KS_KP_4, + KC(0x67), KS_KP_5, + KC(0x68), KS_KP_6, + KC(0x69), KS_KP_1, + KC(0x6a), KS_KP_2, + KC(0x6b), KS_KP_3, + KC(0x6c), KS_KP_0, + KC(0x6d), KS_KP_Decimal, + KC(0x6e), KS_KP_Enter, + KC(0x72), KS_f1, + KC(0x73), KS_f2, + KC(0x74), KS_f3, + KC(0x75), KS_f4, + KC(0x76), KS_f5, + KC(0x77), KS_f6, + KC(0x78), KS_f7, + KC(0x79), KS_f8, + KC(0x7a), KS_f9, + KC(0x7b), KS_f10, + KC(0x7c), KS_KP_Multiply, + KC(0x7d), KS_KP_Divide, + KC(0x7e), KS_KP_Equal, + KC(0x7f), KS_KP_Separator, +}; + +#define SIZE(map) (sizeof(map)/sizeof(keysym_t)) + +struct wscons_keydesc omkbd_keydesctab[] = { + { KB_JP, 0, SIZE(omkbd_keydesc_1), omkbd_keydesc_1, }, + { 0, 0, 0, 0 }, +}; + +void +ws_cngetc(v, type, data) + void *v; + u_int *type; + int *data; +{ + int code; + + code = syscngetc((dev_t)1); + omkbd_decode(v, code, type, data); +} + +void +ws_cnpollc(v, on) + void *v; + int on; +{ +} + +/* EXPORT */ void +ws_cnattach() +{ + static int voidfill; + + /* XXX need CH.B initialization XXX */ + + wskbd_cnattach(&ws_consops, &voidfill, &omkbd_keymapdata); +} + +int +omkbd_enable(v, on) + void *v; + int on; +{ + return 0; +} + +void +omkbd_set_leds(v, leds) + void *v; + int leds; +{ +#if 0 + syscnputc((dev_t)1, 0x10); /* kana LED on */ + syscnputc((dev_t)1, 0x00); /* kana LED off */ + syscnputc((dev_t)1, 0x11); /* caps LED on */ + syscnputc((dev_t)1, 0x01); /* caps LED off */ +#endif +} + +int +omkbd_ioctl(v, cmd, data, flag, p) + void *v; + u_long cmd; + caddr_t data; + int flag; + struct proc *p; +{ +#if 0 + struct ws_softc *sc = v; +#endif + + switch (cmd) { + case WSKBDIO_GTYPE: + *(int *)data = 0; /* XXX for now */ + return 0; + case WSKBDIO_SETLEDS: + case WSKBDIO_GETLEDS: + case WSKBDIO_COMPLEXBELL: /* XXX capable of complex bell */ + return -1; + } + return -1; +} + +#if NWSMOUSE > 0 + +int +omms_enable(v) + void *v; +{ + struct ws_softc *sc = v; + + syscnputc((dev_t)1, 0x60); /* enable 3 byte long mouse reporting */ + sc->sc_msreport = 0; + return 0; +} + +/*ARGUSED*/ +int +omms_ioctl(v, cmd, data, flag, p) + void *v; + u_long cmd; + caddr_t data; + int flag; + struct proc *p; +{ +#if 0 + struct ws_softc *sc = v; +#endif + + switch (cmd) { + case WSMOUSEIO_GTYPE: + *(u_int *)data = 0; /* XXX for now*/ + return 0; + } + + return -1; +} + +void +omms_disable(v) + void *v; +{ + struct ws_softc *sc = v; + + syscnputc((dev_t)1, 0x20); /* quiet mouse */ + sc->sc_msreport = 0; +} +#endif diff --git a/sys/arch/luna88k/dev/mb89352.c b/sys/arch/luna88k/dev/mb89352.c new file mode 100644 index 00000000000..6ed3d6adceb --- /dev/null +++ b/sys/arch/luna88k/dev/mb89352.c @@ -0,0 +1,2184 @@ +/* $OpenBSD: mb89352.c,v 1.1 2004/04/21 15:23:54 aoyama Exp $ */ +/* $NetBSD: mb89352.c,v 1.5 2000/03/23 07:01:31 thorpej Exp $ */ +/* NecBSD: mb89352.c,v 1.4 1998/03/14 07:31:20 kmatsuda Exp */ + +#ifdef DDB +#define integrate +#else +#define integrate __inline static +#endif + +/*- + * Copyright (c) 1996,97,98,99 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Charles M. Hannum, Masaru Oki and Kouichi Matsuda. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Charles M. Hannum. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Copyright (c) 1994 Jarle Greipsland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/* + * [NetBSD for NEC PC-98 series] + * Copyright (c) 1996, 1997, 1998 + * NetBSD/pc98 porting staff. All rights reserved. + * Copyright (c) 1996, 1997, 1998 + * Kouichi Matsuda. All rights reserved. + */ + +/* + * Acknowledgements: Many of the algorithms used in this driver are + * inspired by the work of Julian Elischer (julian@tfs.com) and + * Charles Hannum (mycroft@duality.gnu.ai.mit.edu). Thanks a million! + */ + +/* TODO list: + * 1) Get the DMA stuff working. + * 2) Get the iov/uio stuff working. Is this a good thing ??? + * 3) Get the synch stuff working. + * 4) Rewrite it to use malloc for the acb structs instead of static alloc.? + */ + +/* + * A few customizable items: + */ + +/* Use doubleword transfers to/from SCSI chip. Note: This requires + * motherboard support. Basicly, some motherboard chipsets are able to + * split a 32 bit I/O operation into two 16 bit I/O operations, + * transparently to the processor. This speeds up some things, notably long + * data transfers. + */ +#define SPC_USE_DWORDS 0 + +/* Synchronous data transfers? */ +#define SPC_USE_SYNCHRONOUS 0 +#define SPC_SYNC_REQ_ACK_OFS 8 + +/* Wide data transfers? */ +#define SPC_USE_WIDE 0 +#define SPC_MAX_WIDTH 0 + +/* Max attempts made to transmit a message */ +#define SPC_MSG_MAX_ATTEMPT 3 /* Not used now XXX */ + +/* + * Some spin loop parameters (essentially how long to wait some places) + * The problem(?) is that sometimes we expect either to be able to transmit a + * byte or to get a new one from the SCSI bus pretty soon. In order to avoid + * returning from the interrupt just to get yanked back for the next byte we + * may spin in the interrupt routine waiting for this byte to come. How long? + * This is really (SCSI) device and processor dependent. Tuneable, I guess. + */ +#define SPC_MSGIN_SPIN 1 /* Will spinwait upto ?ms for a new msg byte */ +#define SPC_MSGOUT_SPIN 1 + +/* Include debug functions? At the end of this file there are a bunch of + * functions that will print out various information regarding queued SCSI + * commands, driver state and chip contents. You can call them from the + * kernel debugger. If you set SPC_DEBUG to 0 they are not included (the + * kernel uses less memory) but you lose the debugging facilities. + */ +#define SPC_DEBUG 1 + +#define SPC_ABORT_TIMEOUT 2000 /* time to wait for abort */ + +/* End of customizable parameters */ + +/* + * MB89352 SCSI Protocol Controller (SPC) routines. + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/errno.h> +#include <sys/ioctl.h> +#include <sys/device.h> +#include <sys/buf.h> +#include <sys/proc.h> +#include <sys/user.h> +#include <sys/queue.h> + +#include <machine/intr.h> +#include <machine/bus.h> + +#include <scsi/scsi_all.h> +#include <scsi/scsi_message.h> +#include <scsi/scsiconf.h> + +#include <luna88k/dev/mb89352reg.h> +#include <luna88k/dev/mb89352var.h> + +#ifndef DDB +#define Debugger() panic("should call debugger here (mb89352.c)") +#endif /* ! DDB */ + +#if SPC_DEBUG +int spc_debug = 0x00; /* SPC_SHOWSTART|SPC_SHOWMISC|SPC_SHOWTRACE; */ +#endif + +void spc_minphys (struct buf *); +void spc_done (struct spc_softc *, struct spc_acb *); +void spc_dequeue (struct spc_softc *, struct spc_acb *); +int spc_scsi_cmd (struct scsi_xfer *); +int spc_poll (struct spc_softc *, struct scsi_xfer *, int); +integrate void spc_sched_msgout(struct spc_softc *, u_char); +integrate void spc_setsync(struct spc_softc *, struct spc_tinfo *); +void spc_select (struct spc_softc *, struct spc_acb *); +void spc_timeout (void *); +void spc_scsi_reset (struct spc_softc *); +void spc_reset (struct spc_softc *); +void spc_free_acb (struct spc_softc *, struct spc_acb *, int); +struct spc_acb* spc_get_acb(struct spc_softc *, int); +int spc_reselect (struct spc_softc *, int); +void spc_sense (struct spc_softc *, struct spc_acb *); +void spc_msgin (struct spc_softc *); +void spc_abort (struct spc_softc *, struct spc_acb *); +void spc_msgout (struct spc_softc *); +int spc_dataout_pio (struct spc_softc *, u_char *, int); +int spc_datain_pio (struct spc_softc *, u_char *, int); +#if SPC_DEBUG +void spc_print_acb (struct spc_acb *); +void spc_dump_driver (struct spc_softc *); +void spc_dump89352 (struct spc_softc *); +void spc_show_scsi_cmd(struct spc_acb *); +void spc_print_active_acb(void); +#endif + +extern struct cfdriver spc_cd; + +struct scsi_device spc_dev = { + NULL, /* Use default error handler */ + NULL, /* have a queue, served by this */ + NULL, /* have no async handler */ + NULL, /* Use default 'done' routine */ +}; + +/* + * INITIALIZATION ROUTINES (probe, attach ++) + */ + +/* + * Do the real search-for-device. + * Prerequisite: sc->sc_iobase should be set to the proper value + */ +int +spc_find(iot, ioh, bdid) + bus_space_tag_t iot; + bus_space_handle_t ioh; + int bdid; +{ + long timeout = SPC_ABORT_TIMEOUT; + + SPC_TRACE(("spc: probing for spc-chip\n")); + /* + * Disable interrupts then reset the FUJITSU chip. + */ + bus_space_write_1(iot, ioh, SCTL, SCTL_DISABLE | SCTL_CTRLRST); + bus_space_write_1(iot, ioh, SCMD, 0); + bus_space_write_1(iot, ioh, PCTL, 0); + bus_space_write_1(iot, ioh, TEMP, 0); + bus_space_write_1(iot, ioh, TCH, 0); + bus_space_write_1(iot, ioh, TCM, 0); + bus_space_write_1(iot, ioh, TCL, 0); + bus_space_write_1(iot, ioh, INTS, 0); + bus_space_write_1(iot, ioh, SCTL, SCTL_DISABLE | SCTL_ABRT_ENAB | SCTL_PARITY_ENAB | SCTL_RESEL_ENAB); + bus_space_write_1(iot, ioh, BDID, bdid); + delay(400); + bus_space_write_1(iot, ioh, SCTL, bus_space_read_1(iot, ioh, SCTL) & ~SCTL_DISABLE); + + /* The following detection is derived from spc.c + * (by Takahide Matsutsuka) in FreeBSD/pccard-test. + */ + while (bus_space_read_1(iot, ioh, PSNS) && timeout) + timeout--; + if (!timeout) { + printf("spc: find failed\n"); + return 0; + } + + SPC_START(("SPC found")); + return 1; +} + +void +/* spc_attach(sc) */ +spc_attach(sc, adapter) + struct spc_softc *sc; + struct scsi_adapter *adapter; +{ + + SPC_TRACE(("spc_attach ")); + sc->sc_state = SPC_INIT; + + sc->sc_freq = 20; /* XXXX Assume 20 MHz. */ + +#if SPC_USE_SYNCHRONOUS + /* + * These are the bounds of the sync period, based on the frequency of + * the chip's clock input and the size and offset of the sync period + * register. + * + * For a 20Mhz clock, this gives us 25, or 100nS, or 10MB/s, as a + * maximum transfer rate, and 112.5, or 450nS, or 2.22MB/s, as a + * minimum transfer rate. + */ + sc->sc_minsync = (2 * 250) / sc->sc_freq; + sc->sc_maxsync = (9 * 250) / sc->sc_freq; +#endif + + spc_init(sc); /* Init chip and driver */ + + /* + * Fill in the adapter. + */ + sc->sc_link.adapter_softc = sc; + sc->sc_link.adapter_target = sc->sc_initiator; + sc->sc_link.adapter = adapter; + sc->sc_link.device = &spc_dev; + sc->sc_link.openings = 2; + + /* + * ask the adapter what subunits are present + */ + config_found(&sc->sc_dev, &sc->sc_link, scsiprint); +} + +/* + * Initialize MB89352 chip itself + * The following conditions should hold: + * spc_isa_probe should have succeeded, i.e. the iobase address in spc_softc + * must be valid. + */ +void +spc_reset(sc) + struct spc_softc *sc; +{ + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + + SPC_TRACE(("spc_reset ")); + /* + * Disable interrupts then reset the FUJITSU chip. + */ + bus_space_write_1(iot, ioh, SCTL, SCTL_DISABLE | SCTL_CTRLRST); + bus_space_write_1(iot, ioh, SCMD, 0); + bus_space_write_1(iot, ioh, PCTL, 0); + bus_space_write_1(iot, ioh, TEMP, 0); + bus_space_write_1(iot, ioh, TCH, 0); + bus_space_write_1(iot, ioh, TCM, 0); + bus_space_write_1(iot, ioh, TCL, 0); + bus_space_write_1(iot, ioh, INTS, 0); + bus_space_write_1(iot, ioh, SCTL, SCTL_DISABLE | SCTL_ABRT_ENAB | SCTL_PARITY_ENAB | SCTL_RESEL_ENAB); + bus_space_write_1(iot, ioh, BDID, sc->sc_initiator); + delay(400); + bus_space_write_1(iot, ioh, SCTL, bus_space_read_1(iot, ioh, SCTL) & ~SCTL_DISABLE); +} + + +/* + * Pull the SCSI RST line for 500us. + */ +void +spc_scsi_reset(sc) + struct spc_softc *sc; +{ + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + + SPC_TRACE(("spc_scsi_reset ")); + bus_space_write_1(iot, ioh, SCMD, bus_space_read_1(iot, ioh, SCMD) | SCMD_RST); + delay(500); + bus_space_write_1(iot, ioh, SCMD, bus_space_read_1(iot, ioh, SCMD) & ~SCMD_RST); + delay(50); +} + +/* + * Initialize spc SCSI driver. + */ +void +spc_init(sc) + struct spc_softc *sc; +{ + struct spc_acb *acb; + int r; + + SPC_TRACE(("spc_init ")); + spc_reset(sc); + spc_scsi_reset(sc); + spc_reset(sc); + + if (sc->sc_state == SPC_INIT) { + /* First time through; initialize. */ + TAILQ_INIT(&sc->ready_list); + TAILQ_INIT(&sc->nexus_list); + TAILQ_INIT(&sc->free_list); + sc->sc_nexus = NULL; + acb = sc->sc_acb; + bzero(acb, sizeof(sc->sc_acb)); + for (r = 0; r < sizeof(sc->sc_acb) / sizeof(*acb); r++) { + TAILQ_INSERT_TAIL(&sc->free_list, acb, chain); + acb++; + } + bzero(&sc->sc_tinfo, sizeof(sc->sc_tinfo)); + } else { + /* Cancel any active commands. */ + sc->sc_state = SPC_CLEANING; + if ((acb = sc->sc_nexus) != NULL) { + acb->xs->error = XS_DRIVER_STUFFUP; + timeout_del(&acb->xs->stimeout); + spc_done(sc, acb); + } + while ((acb = sc->nexus_list.tqh_first) != NULL) { + acb->xs->error = XS_DRIVER_STUFFUP; + timeout_del(&acb->xs->stimeout); + spc_done(sc, acb); + } + } + + sc->sc_prevphase = PH_INVALID; + for (r = 0; r < 8; r++) { + struct spc_tinfo *ti = &sc->sc_tinfo[r]; + + ti->flags = 0; +#if SPC_USE_SYNCHRONOUS + ti->flags |= DO_SYNC; + ti->period = sc->sc_minsync; + ti->offset = SPC_SYNC_REQ_ACK_OFS; +#else + ti->period = ti->offset = 0; +#endif +#if SPC_USE_WIDE + ti->flags |= DO_WIDE; + ti->width = SPC_MAX_WIDTH; +#else + ti->width = 0; +#endif + } + + sc->sc_state = SPC_IDLE; + bus_space_write_1(sc->sc_iot, sc->sc_ioh, SCTL, + bus_space_read_1(sc->sc_iot, sc->sc_ioh, SCTL) | SCTL_INTR_ENAB); +} + +void +spc_free_acb(sc, acb, flags) + struct spc_softc *sc; + struct spc_acb *acb; + int flags; +{ + int s; + + SPC_TRACE(("spc_free_acb ")); + s = splbio(); + + acb->flags = 0; + TAILQ_INSERT_HEAD(&sc->free_list, acb, chain); + + /* + * If there were none, wake anybody waiting for one to come free, + * starting with queued entries. + */ + if (acb->chain.tqe_next == 0) + wakeup(&sc->free_list); + + splx(s); +} + +struct spc_acb * +spc_get_acb(sc, flags) + struct spc_softc *sc; + int flags; +{ + struct spc_acb *acb; + int s; + + SPC_TRACE(("spc_get_acb ")); + s = splbio(); + + while ((acb = sc->free_list.tqh_first) == NULL && + (flags & SCSI_NOSLEEP) == 0) + tsleep(&sc->free_list, PRIBIO, "spcacb", 0); + if (acb) { + TAILQ_REMOVE(&sc->free_list, acb, chain); + acb->flags |= ACB_ALLOC; + } + + splx(s); + return acb; +} + +/* + * DRIVER FUNCTIONS CALLABLE FROM HIGHER LEVEL DRIVERS + */ + +/* + * Expected sequence: + * 1) Command inserted into ready list + * 2) Command selected for execution + * 3) Command won arbitration and has selected target device + * 4) Send message out (identify message, eventually also sync.negotiations) + * 5) Send command + * 5a) Receive disconnect message, disconnect. + * 5b) Reselected by target + * 5c) Receive identify message from target. + * 6) Send or receive data + * 7) Receive status + * 8) Receive message (command complete etc.) + * 9) If status == SCSI_CHECK construct a synthetic request sense SCSI cmd. + * Repeat 2-8 (no disconnects please...) + */ + +/* + * Start a SCSI-command + * This function is called by the higher level SCSI-driver to queue/run + * SCSI-commands. + */ +int +spc_scsi_cmd(xs) + struct scsi_xfer *xs; +{ + struct scsi_link *sc_link = xs->sc_link; + struct spc_softc *sc = sc_link->adapter_softc; + struct spc_acb *acb; + int s, flags; + + SPC_TRACE(("spc_scsi_cmd ")); + SPC_CMDS(("[0x%x, %d]->%d ", (int)xs->cmd->opcode, xs->cmdlen, + sc_link->target)); + + flags = xs->flags; + if ((acb = spc_get_acb(sc, flags)) == NULL) { + xs->error = XS_DRIVER_STUFFUP; + return TRY_AGAIN_LATER; + } + + /* Initialize acb */ + acb->xs = xs; + acb->timeout = xs->timeout; + + if (xs->flags & SCSI_RESET) { + acb->flags |= ACB_RESET; + acb->scsi_cmd_length = 0; + acb->data_length = 0; + } else { + bcopy(xs->cmd, &acb->scsi_cmd, xs->cmdlen); + acb->scsi_cmd_length = xs->cmdlen; + acb->data_addr = xs->data; + acb->data_length = xs->datalen; + } + acb->target_stat = 0; + + s = splbio(); + + TAILQ_INSERT_TAIL(&sc->ready_list, acb, chain); + /* + * Start scheduling unless a queue process is in progress. + */ + if (sc->sc_state == SPC_IDLE) + spc_sched(sc); + /* + * After successful sending, check if we should return just now. + * If so, return SUCCESSFULLY_QUEUED. + */ + + splx(s); + + if ((flags & SCSI_POLL) == 0) + return SUCCESSFULLY_QUEUED; + + /* Not allowed to use interrupts, use polling instead */ + s = splbio(); + if (spc_poll(sc, xs, acb->timeout)) { + spc_timeout(acb); + if (spc_poll(sc, xs, acb->timeout)) + spc_timeout(acb); + } + splx(s); + return COMPLETE; +} + +/* + * Adjust transfer size in buffer structure + */ +void +spc_minphys(bp) + struct buf *bp; +{ + + SPC_TRACE(("spc_minphys ")); + minphys(bp); +} + +/* + * Used when interrupt driven I/O isn't allowed, e.g. during boot. + */ +int +spc_poll(sc, xs, count) + struct spc_softc *sc; + struct scsi_xfer *xs; + int count; +{ + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + + SPC_TRACE(("spc_poll ")); + while (count) { + /* + * If we had interrupts enabled, would we + * have got an interrupt? + */ + if (bus_space_read_1(iot, ioh, INTS) != 0) + spc_intr(sc); + if ((xs->flags & ITSDONE) != 0) + return 0; + delay(1000); + count--; + } + return 1; +} + +/* + * LOW LEVEL SCSI UTILITIES + */ + +integrate void +spc_sched_msgout(sc, m) + struct spc_softc *sc; + u_char m; +{ + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + + SPC_TRACE(("spc_sched_msgout ")); + if (sc->sc_msgpriq == 0) + bus_space_write_1(iot, ioh, SCMD, SCMD_SET_ATN); + sc->sc_msgpriq |= m; +} + +/* + * Set synchronous transfer offset and period. + */ +integrate void +spc_setsync(sc, ti) + struct spc_softc *sc; + struct spc_tinfo *ti; +{ +#if SPC_USE_SYNCHRONOUS + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + + SPC_TRACE(("spc_setsync ")); + if (ti->offset != 0) + bus_space_write_1(iot, ioh, TMOD, + ((ti->period * sc->sc_freq) / 250 - 2) << 4 | ti->offset); + else + bus_space_write_1(iot, ioh, TMOD, 0); +#endif +} + +/* + * Start a selection. This is used by spc_sched() to select an idle target, + * and by spc_done() to immediately reselect a target to get sense information. + */ +void +spc_select(sc, acb) + struct spc_softc *sc; + struct spc_acb *acb; +{ + struct scsi_link *sc_link = acb->xs->sc_link; + int target = sc_link->target; + struct spc_tinfo *ti = &sc->sc_tinfo[target]; + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + + SPC_TRACE(("spc_select ")); + spc_setsync(sc, ti); + +#if 0 + bus_space_write_1(iot, ioh, SCMD, SCMD_SET_ATN); +#endif + + bus_space_write_1(iot, ioh, PCTL, 0); + bus_space_write_1(iot, ioh, TEMP, (1 << sc->sc_initiator) | (1 << target)); + /* + * Setup BSY timeout (selection timeout). + * 250ms according to the SCSI specification. + * T = (X * 256 + 15) * Tclf * 2 (Tclf = 200ns on x68k) + * To setup 256ms timeout, + * 128000ns/200ns = X * 256 + 15 + * 640 - 15 = X * 256 + * X = 625 / 256 + * X = 2 + 113 / 256 + * ==> tch = 2, tcm = 113 (correct?) + */ + bus_space_write_1(iot, ioh, TCH, 2); + bus_space_write_1(iot, ioh, TCM, 113); + /* Time to the information transfer phase start. */ + bus_space_write_1(iot, ioh, TCL, 3); + bus_space_write_1(iot, ioh, SCMD, SCMD_SELECT); + + sc->sc_state = SPC_SELECTING; +} + +int +spc_reselect(sc, message) + struct spc_softc *sc; + int message; +{ + u_char selid, target, lun; + struct spc_acb *acb; + struct scsi_link *sc_link; + struct spc_tinfo *ti; + + SPC_TRACE(("spc_reselect ")); + /* + * The SCSI chip made a snapshot of the data bus while the reselection + * was being negotiated. This enables us to determine which target did + * the reselect. + */ + selid = sc->sc_selid & ~(1 << sc->sc_initiator); + if (selid & (selid - 1)) { + printf("%s: reselect with invalid selid %02x; sending DEVICE RESET\n", + sc->sc_dev.dv_xname, selid); + SPC_BREAK(); + goto reset; + } + + /* + * Search wait queue for disconnected cmd + * The list should be short, so I haven't bothered with + * any more sophisticated structures than a simple + * singly linked list. + */ + target = ffs(selid) - 1; + lun = message & 0x07; + for (acb = sc->nexus_list.tqh_first; acb != NULL; + acb = acb->chain.tqe_next) { + sc_link = acb->xs->sc_link; + if (sc_link->target == target && + sc_link->lun == lun) + break; + } + if (acb == NULL) { + printf("%s: reselect from target %d lun %d with no nexus; sending ABORT\n", + sc->sc_dev.dv_xname, target, lun); + SPC_BREAK(); + goto abort; + } + + /* Make this nexus active again. */ + TAILQ_REMOVE(&sc->nexus_list, acb, chain); + sc->sc_state = SPC_CONNECTED; + sc->sc_nexus = acb; + ti = &sc->sc_tinfo[target]; + ti->lubusy |= (1 << lun); + spc_setsync(sc, ti); + + if (acb->flags & ACB_RESET) + spc_sched_msgout(sc, SEND_DEV_RESET); + else if (acb->flags & ACB_ABORT) + spc_sched_msgout(sc, SEND_ABORT); + + /* Do an implicit RESTORE POINTERS. */ + sc->sc_dp = acb->data_addr; + sc->sc_dleft = acb->data_length; + sc->sc_cp = (u_char *)&acb->scsi_cmd; + sc->sc_cleft = acb->scsi_cmd_length; + + return (0); + +reset: + spc_sched_msgout(sc, SEND_DEV_RESET); + return (1); + +abort: + spc_sched_msgout(sc, SEND_ABORT); + return (1); +} + +/* + * Schedule a SCSI operation. This has now been pulled out of the interrupt + * handler so that we may call it from spc_scsi_cmd and spc_done. This may + * save us an unecessary interrupt just to get things going. Should only be + * called when state == SPC_IDLE and at bio pl. + */ +void +spc_sched(sc) + struct spc_softc *sc; +{ + struct spc_acb *acb; + struct scsi_link *sc_link; + struct spc_tinfo *ti; + + /* missing the hw, just return and wait for our hw */ + if (sc->sc_flags & SPC_INACTIVE) + return; + SPC_TRACE(("spc_sched ")); + /* + * Find first acb in ready queue that is for a target/lunit pair that + * is not busy. + */ + for (acb = sc->ready_list.tqh_first; acb != NULL; + acb = acb->chain.tqe_next) { + sc_link = acb->xs->sc_link; + ti = &sc->sc_tinfo[sc_link->target]; + if ((ti->lubusy & (1 << sc_link->lun)) == 0) { + SPC_MISC(("selecting %d:%d ", + sc_link->target, sc_link->lun)); + TAILQ_REMOVE(&sc->ready_list, acb, chain); + sc->sc_nexus = acb; + spc_select(sc, acb); + return; + } else + SPC_MISC(("%d:%d busy\n", + sc_link->target, sc_link->lun)); + } + SPC_MISC(("idle ")); + /* Nothing to start; just enable reselections and wait. */ +} + +void +spc_sense(sc, acb) + struct spc_softc *sc; + struct spc_acb *acb; +{ + struct scsi_xfer *xs = acb->xs; + struct scsi_link *sc_link = xs->sc_link; + struct spc_tinfo *ti = &sc->sc_tinfo[sc_link->target]; + struct scsi_sense *ss = (void *)&acb->scsi_cmd; + + SPC_MISC(("requesting sense ")); + /* Next, setup a request sense command block */ + bzero(ss, sizeof(*ss)); + ss->opcode = REQUEST_SENSE; + ss->byte2 = sc_link->lun << 5; + ss->length = sizeof(struct scsi_sense_data); + acb->scsi_cmd_length = sizeof(*ss); + acb->data_addr = (char *)&xs->sense; + acb->data_length = sizeof(struct scsi_sense_data); + acb->flags |= ACB_SENSE; + ti->senses++; + if (acb->flags & ACB_NEXUS) + ti->lubusy &= ~(1 << sc_link->lun); + if (acb == sc->sc_nexus) { + spc_select(sc, acb); + } else { + spc_dequeue(sc, acb); + TAILQ_INSERT_HEAD(&sc->ready_list, acb, chain); + if (sc->sc_state == SPC_IDLE) + spc_sched(sc); + } +} + +/* + * POST PROCESSING OF SCSI_CMD (usually current) + */ +void +spc_done(sc, acb) + struct spc_softc *sc; + struct spc_acb *acb; +{ + struct scsi_xfer *xs = acb->xs; + struct scsi_link *sc_link = xs->sc_link; + struct spc_tinfo *ti = &sc->sc_tinfo[sc_link->target]; + + SPC_TRACE(("spc_done ")); + + /* + * Now, if we've come here with no error code, i.e. we've kept the + * initial XS_NOERROR, and the status code signals that we should + * check sense, we'll need to set up a request sense cmd block and + * push the command back into the ready queue *before* any other + * commands for this target/lunit, else we lose the sense info. + * We don't support chk sense conditions for the request sense cmd. + */ + if (xs->error == XS_NOERROR) { + if (acb->flags & ACB_ABORT) { + xs->error = XS_DRIVER_STUFFUP; + } else if (acb->flags & ACB_SENSE) { + xs->error = XS_SENSE; + } else { + switch (acb->target_stat) { + case SCSI_CHECK: + /* First, save the return values */ + xs->resid = acb->data_length; + xs->status = acb->target_stat; + spc_sense(sc, acb); + return; + case SCSI_BUSY: + xs->error = XS_BUSY; + break; + case SCSI_OK: + xs->resid = acb->data_length; + break; + default: + xs->error = XS_DRIVER_STUFFUP; +#if SPC_DEBUG + printf("%s: spc_done: bad stat 0x%x\n", + sc->sc_dev.dv_xname, acb->target_stat); +#endif + break; + } + } + } + + xs->flags |= ITSDONE; + +#if SPC_DEBUG + if ((spc_debug & SPC_SHOWMISC) != 0) { + if (xs->resid != 0) + printf("resid=%d ", xs->resid); + if (xs->error == XS_SENSE) + printf("sense=0x%02x\n", xs->sense.error_code); + else + printf("error=%d\n", xs->error); + } +#endif + + /* + * Remove the ACB from whatever queue it happens to be on. + */ + if (acb->flags & ACB_NEXUS) + ti->lubusy &= ~(1 << sc_link->lun); + if (acb == sc->sc_nexus) { + sc->sc_nexus = NULL; + sc->sc_state = SPC_IDLE; + spc_sched(sc); + } else + spc_dequeue(sc, acb); + + spc_free_acb(sc, acb, xs->flags); + ti->cmds++; + scsi_done(xs); +} + +void +spc_dequeue(sc, acb) + struct spc_softc *sc; + struct spc_acb *acb; +{ + + SPC_TRACE(("spc_dequeue ")); + if (acb->flags & ACB_NEXUS) { + TAILQ_REMOVE(&sc->nexus_list, acb, chain); + } else { + TAILQ_REMOVE(&sc->ready_list, acb, chain); + } +} + +/* + * INTERRUPT/PROTOCOL ENGINE + */ + +#define IS1BYTEMSG(m) (((m) != 0x01 && (m) < 0x20) || (m) >= 0x80) +#define IS2BYTEMSG(m) (((m) & 0xf0) == 0x20) +#define ISEXTMSG(m) ((m) == 0x01) + +/* + * Precondition: + * The SCSI bus is already in the MSGI phase and there is a message byte + * on the bus, along with an asserted REQ signal. + */ +void +spc_msgin(sc) + struct spc_softc *sc; +{ + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + int n; + + SPC_TRACE(("spc_msgin ")); + + if (sc->sc_prevphase == PH_MSGIN) { + /* This is a continuation of the previous message. */ + n = sc->sc_imp - sc->sc_imess; + goto nextbyte; + } + + /* This is a new MESSAGE IN phase. Clean up our state. */ + sc->sc_flags &= ~SPC_DROP_MSGIN; + +nextmsg: + n = 0; + sc->sc_imp = &sc->sc_imess[n]; + +nextbyte: + /* + * Read a whole message, but don't ack the last byte. If we reject the + * message, we have to assert ATN during the message transfer phase + * itself. + */ + for (;;) { +#if 0 + for (;;) { + if ((bus_space_read_1(iot, ioh, PSNS) & PSNS_REQ) != 0) + break; + /* Wait for REQINIT. XXX Need timeout. */ + } +#endif + if (bus_space_read_1(iot, ioh, INTS) != 0) { + /* + * Target left MESSAGE IN, probably because it + * a) noticed our ATN signal, or + * b) ran out of messages. + */ + goto out; + } + + /* If parity error, just dump everything on the floor. */ + if ((bus_space_read_1(iot, ioh, SERR) & + (SERR_SCSI_PAR|SERR_SPC_PAR)) != 0) { + sc->sc_flags |= SPC_DROP_MSGIN; + spc_sched_msgout(sc, SEND_PARITY_ERROR); + } + + /* send TRANSFER command. */ + bus_space_write_1(iot, ioh, TCH, 0); + bus_space_write_1(iot, ioh, TCM, 0); + bus_space_write_1(iot, ioh, TCL, 1); + bus_space_write_1(iot, ioh, PCTL, + sc->sc_phase | PCTL_BFINT_ENAB); +#ifdef x68k + bus_space_write_1(iot, ioh, SCMD, SCMD_XFR); /* | SCMD_PROG_XFR */ +#else + bus_space_write_1(iot, ioh, SCMD, SCMD_XFR | SCMD_PROG_XFR); /* XXX */ +#endif + for (;;) { + /*if ((bus_space_read_1(iot, ioh, SSTS) & SSTS_BUSY) != 0 + && (bus_space_read_1(iot, ioh, SSTS) & SSTS_DREG_EMPTY) != 0)*/ + if ((bus_space_read_1(iot, ioh, SSTS) & SSTS_DREG_EMPTY) == 0) + break; + if (bus_space_read_1(iot, ioh, INTS) != 0) + goto out; + } + + /* Gather incoming message bytes if needed. */ + if ((sc->sc_flags & SPC_DROP_MSGIN) == 0) { + if (n >= SPC_MAX_MSG_LEN) { + (void) bus_space_read_1(iot, ioh, DREG); + sc->sc_flags |= SPC_DROP_MSGIN; + spc_sched_msgout(sc, SEND_REJECT); + } else { + *sc->sc_imp++ = bus_space_read_1(iot, ioh, DREG); + n++; + /* + * This testing is suboptimal, but most + * messages will be of the one byte variety, so + * it should not affect performance + * significantly. + */ + if (n == 1 && IS1BYTEMSG(sc->sc_imess[0])) + break; + if (n == 2 && IS2BYTEMSG(sc->sc_imess[0])) + break; + if (n >= 3 && ISEXTMSG(sc->sc_imess[0]) && + n == sc->sc_imess[1] + 2) + break; + } + } else + (void) bus_space_read_1(iot, ioh, DREG); + + /* + * If we reach this spot we're either: + * a) in the middle of a multi-byte message, or + * b) dropping bytes. + */ +#if 0 + /* Ack the last byte read. */ + /*(void) bus_space_read_1(iot, ioh, DREG);*/ + while ((bus_space_read_1(iot, ioh, PSNS) & ACKI) != 0) + ; +#endif + } + + SPC_MISC(("n=%d imess=0x%02x ", n, sc->sc_imess[0])); + + /* We now have a complete message. Parse it. */ + switch (sc->sc_state) { + struct spc_acb *acb; + struct scsi_link *sc_link; + struct spc_tinfo *ti; + + case SPC_CONNECTED: + SPC_ASSERT(sc->sc_nexus != NULL); + acb = sc->sc_nexus; + ti = &sc->sc_tinfo[acb->xs->sc_link->target]; + + switch (sc->sc_imess[0]) { + case MSG_CMDCOMPLETE: + if (sc->sc_dleft < 0) { + sc_link = acb->xs->sc_link; + printf("%s: %d extra bytes from %d:%d\n", + sc->sc_dev.dv_xname, -sc->sc_dleft, + sc_link->target, sc_link->lun); + acb->data_length = 0; + } + acb->xs->resid = acb->data_length = sc->sc_dleft; + sc->sc_state = SPC_CMDCOMPLETE; + break; + + case MSG_PARITY_ERROR: + /* Resend the last message. */ + spc_sched_msgout(sc, sc->sc_lastmsg); + break; + + case MSG_MESSAGE_REJECT: + SPC_MISC(("message rejected %02x ", sc->sc_lastmsg)); + switch (sc->sc_lastmsg) { +#if SPC_USE_SYNCHRONOUS + SPC_USE_WIDE + case SEND_IDENTIFY: + ti->flags &= ~(DO_SYNC | DO_WIDE); + ti->period = ti->offset = 0; + spc_setsync(sc, ti); + ti->width = 0; + break; +#endif +#if SPC_USE_SYNCHRONOUS + case SEND_SDTR: + ti->flags &= ~DO_SYNC; + ti->period = ti->offset = 0; + spc_setsync(sc, ti); + break; +#endif +#if SPC_USE_WIDE + case SEND_WDTR: + ti->flags &= ~DO_WIDE; + ti->width = 0; + break; +#endif + case SEND_INIT_DET_ERR: + spc_sched_msgout(sc, SEND_ABORT); + break; + } + break; + + case MSG_NOOP: + break; + + case MSG_DISCONNECT: + ti->dconns++; + sc->sc_state = SPC_DISCONNECT; + break; + + case MSG_SAVEDATAPOINTER: + acb->data_addr = sc->sc_dp; + acb->data_length = sc->sc_dleft; + break; + + case MSG_RESTOREPOINTERS: + sc->sc_dp = acb->data_addr; + sc->sc_dleft = acb->data_length; + sc->sc_cp = (u_char *)&acb->scsi_cmd; + sc->sc_cleft = acb->scsi_cmd_length; + break; + + case MSG_EXTENDED: + switch (sc->sc_imess[2]) { +#if SPC_USE_SYNCHRONOUS + case MSG_EXT_SDTR: + if (sc->sc_imess[1] != 3) + goto reject; + ti->period = sc->sc_imess[3]; + ti->offset = sc->sc_imess[4]; + ti->flags &= ~DO_SYNC; + if (ti->offset == 0) { + } else if (ti->period < sc->sc_minsync || + ti->period > sc->sc_maxsync || + ti->offset > 8) { + ti->period = ti->offset = 0; + spc_sched_msgout(sc, SEND_SDTR); + } else { + sc_print_addr(acb->xs->sc_link); + printf("sync, offset %d, period %dnsec\n", + ti->offset, ti->period * 4); + } + spc_setsync(sc, ti); + break; +#endif + +#if SPC_USE_WIDE + case MSG_EXT_WDTR: + if (sc->sc_imess[1] != 2) + goto reject; + ti->width = sc->sc_imess[3]; + ti->flags &= ~DO_WIDE; + if (ti->width == 0) { + } else if (ti->width > SPC_MAX_WIDTH) { + ti->width = 0; + spc_sched_msgout(sc, SEND_WDTR); + } else { + sc_print_addr(acb->xs->sc_link); + printf("wide, width %d\n", + 1 << (3 + ti->width)); + } + break; +#endif + + default: + printf("%s: unrecognized MESSAGE EXTENDED; sending REJECT\n", + sc->sc_dev.dv_xname); + SPC_BREAK(); + goto reject; + } + break; + + default: + printf("%s: unrecognized MESSAGE; sending REJECT\n", + sc->sc_dev.dv_xname); + SPC_BREAK(); + reject: + spc_sched_msgout(sc, SEND_REJECT); + break; + } + break; + + case SPC_RESELECTED: + if (!MSG_ISIDENTIFY(sc->sc_imess[0])) { + printf("%s: reselect without IDENTIFY; sending DEVICE RESET\n", + sc->sc_dev.dv_xname); + SPC_BREAK(); + goto reset; + } + + (void) spc_reselect(sc, sc->sc_imess[0]); + break; + + default: + printf("%s: unexpected MESSAGE IN; sending DEVICE RESET\n", + sc->sc_dev.dv_xname); + SPC_BREAK(); + reset: + spc_sched_msgout(sc, SEND_DEV_RESET); + break; + +#ifdef notdef + abort: + spc_sched_msgout(sc, SEND_ABORT); + break; +#endif + } + + /* Ack the last message byte. */ +#if 0 /* XXX? */ + (void) bus_space_read_1(iot, ioh, DREG); + while ((bus_space_read_1(iot, ioh, PSNS) & ACKI) != 0) + ; +#endif + + /* Go get the next message, if any. */ + goto nextmsg; + +out: + bus_space_write_1(iot, ioh, SCMD, SCMD_RST_ACK); + SPC_MISC(("n=%d imess=0x%02x ", n, sc->sc_imess[0])); +} + +/* + * Send the highest priority, scheduled message. + */ +void +spc_msgout(sc) + struct spc_softc *sc; +{ + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; +#if SPC_USE_SYNCHRONOUS + struct spc_tinfo *ti; +#endif + int n; + + SPC_TRACE(("spc_msgout ")); + + if (sc->sc_prevphase == PH_MSGOUT) { + if (sc->sc_omp == sc->sc_omess) { + /* + * This is a retransmission. + * + * We get here if the target stayed in MESSAGE OUT + * phase. Section 5.1.9.2 of the SCSI 2 spec indicates + * that all of the previously transmitted messages must + * be sent again, in the same order. Therefore, we + * requeue all the previously transmitted messages, and + * start again from the top. Our simple priority + * scheme keeps the messages in the right order. + */ + SPC_MISC(("retransmitting ")); + sc->sc_msgpriq |= sc->sc_msgoutq; + /* + * Set ATN. If we're just sending a trivial 1-byte + * message, we'll clear ATN later on anyway. + */ + bus_space_write_1(iot, ioh, SCMD, SCMD_SET_ATN); /* XXX? */ + } else { + /* This is a continuation of the previous message. */ + n = sc->sc_omp - sc->sc_omess; + goto nextbyte; + } + } + + /* No messages transmitted so far. */ + sc->sc_msgoutq = 0; + sc->sc_lastmsg = 0; + +nextmsg: + /* Pick up highest priority message. */ + sc->sc_currmsg = sc->sc_msgpriq & -sc->sc_msgpriq; + sc->sc_msgpriq &= ~sc->sc_currmsg; + sc->sc_msgoutq |= sc->sc_currmsg; + + /* Build the outgoing message data. */ + switch (sc->sc_currmsg) { + case SEND_IDENTIFY: + SPC_ASSERT(sc->sc_nexus != NULL); + sc->sc_omess[0] = + MSG_IDENTIFY(sc->sc_nexus->xs->sc_link->lun, 1); + n = 1; + break; + +#if SPC_USE_SYNCHRONOUS + case SEND_SDTR: + SPC_ASSERT(sc->sc_nexus != NULL); + ti = &sc->sc_tinfo[sc->sc_nexus->xs->sc_link->target]; + sc->sc_omess[4] = MSG_EXTENDED; + sc->sc_omess[3] = 3; + sc->sc_omess[2] = MSG_EXT_SDTR; + sc->sc_omess[1] = ti->period >> 2; + sc->sc_omess[0] = ti->offset; + n = 5; + break; +#endif + +#if SPC_USE_WIDE + case SEND_WDTR: + SPC_ASSERT(sc->sc_nexus != NULL); + ti = &sc->sc_tinfo[sc->sc_nexus->xs->sc_link->target]; + sc->sc_omess[3] = MSG_EXTENDED; + sc->sc_omess[2] = 2; + sc->sc_omess[1] = MSG_EXT_WDTR; + sc->sc_omess[0] = ti->width; + n = 4; + break; +#endif + + case SEND_DEV_RESET: + sc->sc_flags |= SPC_ABORTING; + sc->sc_omess[0] = MSG_BUS_DEV_RESET; + n = 1; + break; + + case SEND_REJECT: + sc->sc_omess[0] = MSG_MESSAGE_REJECT; + n = 1; + break; + + case SEND_PARITY_ERROR: + sc->sc_omess[0] = MSG_PARITY_ERROR; + n = 1; + break; + + case SEND_INIT_DET_ERR: + sc->sc_omess[0] = MSG_INITIATOR_DET_ERR; + n = 1; + break; + + case SEND_ABORT: + sc->sc_flags |= SPC_ABORTING; + sc->sc_omess[0] = MSG_ABORT; + n = 1; + break; + + default: + printf("%s: unexpected MESSAGE OUT; sending NOOP\n", + sc->sc_dev.dv_xname); + SPC_BREAK(); + sc->sc_omess[0] = MSG_NOOP; + n = 1; + break; + } + sc->sc_omp = &sc->sc_omess[n]; + +nextbyte: + /* Send message bytes. */ + /* send TRANSFER command. */ + bus_space_write_1(iot, ioh, TCH, n >> 16); + bus_space_write_1(iot, ioh, TCM, n >> 8); + bus_space_write_1(iot, ioh, TCL, n); + bus_space_write_1(iot, ioh, PCTL, sc->sc_phase | PCTL_BFINT_ENAB); +#ifdef x68k + bus_space_write_1(iot, ioh, SCMD, SCMD_XFR); /* XXX */ +#else + bus_space_write_1(iot, ioh, SCMD, SCMD_XFR | SCMD_PROG_XFR | SCMD_ICPT_XFR); +#endif + for (;;) { + if ((bus_space_read_1(iot, ioh, SSTS) & SSTS_BUSY) != 0) + break; + if (bus_space_read_1(iot, ioh, INTS) != 0) + goto out; + } + for (;;) { +#if 0 + for (;;) { + if ((bus_space_read_1(iot, ioh, PSNS) & PSNS_REQ) != 0) + break; + /* Wait for REQINIT. XXX Need timeout. */ + } +#endif + if (bus_space_read_1(iot, ioh, INTS) != 0) { + /* + * Target left MESSAGE OUT, possibly to reject + * our message. + * + * If this is the last message being sent, then we + * deassert ATN, since either the target is going to + * ignore this message, or it's going to ask for a + * retransmission via MESSAGE PARITY ERROR (in which + * case we reassert ATN anyway). + */ +#if 0 + if (sc->sc_msgpriq == 0) + bus_space_write_1(iot, ioh, SCMD, SCMD_RST_ATN); +#endif + goto out; + } + +#if 0 + /* Clear ATN before last byte if this is the last message. */ + if (n == 1 && sc->sc_msgpriq == 0) + bus_space_write_1(iot, ioh, SCMD, SCMD_RST_ATN); +#endif + + while ((bus_space_read_1(iot, ioh, SSTS) & SSTS_DREG_FULL) != 0) + ; + /* Send message byte. */ + bus_space_write_1(iot, ioh, DREG, *--sc->sc_omp); + --n; + /* Keep track of the last message we've sent any bytes of. */ + sc->sc_lastmsg = sc->sc_currmsg; +#if 0 + /* Wait for ACK to be negated. XXX Need timeout. */ + while ((bus_space_read_1(iot, ioh, PSNS) & ACKI) != 0) + ; +#endif + + if (n == 0) + break; + } + + /* We get here only if the entire message has been transmitted. */ + if (sc->sc_msgpriq != 0) { + /* There are more outgoing messages. */ + goto nextmsg; + } + + /* + * The last message has been transmitted. We need to remember the last + * message transmitted (in case the target switches to MESSAGE IN phase + * and sends a MESSAGE REJECT), and the list of messages transmitted + * this time around (in case the target stays in MESSAGE OUT phase to + * request a retransmit). + */ + +out: + /* Disable REQ/ACK protocol. */ +} + +/* + * spc_dataout_pio: perform a data transfer using the FIFO datapath in the spc + * Precondition: The SCSI bus should be in the DOUT phase, with REQ asserted + * and ACK deasserted (i.e. waiting for a data byte) + * + * This new revision has been optimized (I tried) to make the common case fast, + * and the rarer cases (as a result) somewhat more comlex + */ +int +spc_dataout_pio(sc, p, n) + struct spc_softc *sc; + u_char *p; + int n; +{ + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + u_char intstat = 0; + int out = 0; +#define DOUTAMOUNT 8 /* Full FIFO */ + + SPC_TRACE(("spc_dataout_pio ")); + /* send TRANSFER command. */ + bus_space_write_1(iot, ioh, TCH, n >> 16); + bus_space_write_1(iot, ioh, TCM, n >> 8); + bus_space_write_1(iot, ioh, TCL, n); + bus_space_write_1(iot, ioh, PCTL, sc->sc_phase | PCTL_BFINT_ENAB); +#ifdef x68k + bus_space_write_1(iot, ioh, SCMD, SCMD_XFR); /* XXX */ +#else + bus_space_write_1(iot, ioh, SCMD, SCMD_XFR | SCMD_PROG_XFR | SCMD_ICPT_XFR); /* XXX */ +#endif + for (;;) { + if ((bus_space_read_1(iot, ioh, SSTS) & SSTS_BUSY) != 0) + break; + if (bus_space_read_1(iot, ioh, INTS) != 0) + break; + } + + /* + * I have tried to make the main loop as tight as possible. This + * means that some of the code following the loop is a bit more + * complex than otherwise. + */ + while (n > 0) { + int xfer; + + for (;;) { + intstat = bus_space_read_1(iot, ioh, INTS); + /* Wait till buffer is empty. */ + if ((bus_space_read_1(iot, ioh, SSTS) & SSTS_DREG_EMPTY) != 0) + break; + /* Break on interrupt. */ + if (intstat != 0) + goto phasechange; + } + + xfer = min(DOUTAMOUNT, n); + + SPC_MISC(("%d> ", xfer)); + + n -= xfer; + out += xfer; + + while (xfer-- > 0) { + bus_space_write_1(iot, ioh, DREG, *p++); + } + } + + if (out == 0) { + for (;;) { + if (bus_space_read_1(iot, ioh, INTS) != 0) + break; + } + SPC_MISC(("extra data ")); + } else { + /* See the bytes off chip */ + for (;;) { + /* Wait till buffer is empty. */ + if ((bus_space_read_1(iot, ioh, SSTS) & SSTS_DREG_EMPTY) != 0) + break; + intstat = bus_space_read_1(iot, ioh, INTS); + /* Break on interrupt. */ + if (intstat != 0) + goto phasechange; + } + } + +phasechange: + /* Stop the FIFO data path. */ + + if (intstat != 0) { + /* Some sort of phase change. */ + int amount; + + amount = ((bus_space_read_1(iot, ioh, TCH) << 16) | + (bus_space_read_1(iot, ioh, TCM) << 8) | + bus_space_read_1(iot, ioh, TCL)); + if (amount > 0) { + out -= amount; + SPC_MISC(("+%d ", amount)); + } + } + + /* Turn on ENREQINIT again. */ + + return out; +} + +/* + * spc_datain_pio: perform data transfers using the FIFO datapath in the spc + * Precondition: The SCSI bus should be in the DIN phase, with REQ asserted + * and ACK deasserted (i.e. at least one byte is ready). + * + * For now, uses a pretty dumb algorithm, hangs around until all data has been + * transferred. This, is OK for fast targets, but not so smart for slow + * targets which don't disconnect or for huge transfers. + */ +int +spc_datain_pio(sc, p, n) + struct spc_softc *sc; + u_char *p; + int n; +{ + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + u_short intstat; + int in = 0; +#define DINAMOUNT 8 /* Full FIFO */ + + SPC_TRACE(("spc_datain_pio ")); + /* send TRANSFER command. */ + bus_space_write_1(iot, ioh, TCH, n >> 16); + bus_space_write_1(iot, ioh, TCM, n >> 8); + bus_space_write_1(iot, ioh, TCL, n); + bus_space_write_1(iot, ioh, PCTL, sc->sc_phase | PCTL_BFINT_ENAB); +#ifdef x68k + bus_space_write_1(iot, ioh, SCMD, SCMD_XFR); /* XXX */ +#else + bus_space_write_1(iot, ioh, SCMD, SCMD_XFR | SCMD_PROG_XFR); /* XXX */ +#endif + for (;;) { + if ((bus_space_read_1(iot, ioh, SSTS) & SSTS_BUSY) != 0) + break; + if (bus_space_read_1(iot, ioh, INTS) != 0) + goto phasechange; + } + + /* + * We leave this loop if one or more of the following is true: + * a) phase != PH_DATAIN && FIFOs are empty + * b) reset has occurred or busfree is detected. + */ + while (n > 0) { + int xfer; + +#define INTSMASK 0xff + /* Wait for fifo half full or phase mismatch */ + for (;;) { + intstat = ((bus_space_read_1(iot, ioh, SSTS) << 8) | + bus_space_read_1(iot, ioh, INTS)); + if ((intstat & (INTSMASK | (SSTS_DREG_FULL << 8))) != + 0) + break; + if ((intstat & (SSTS_DREG_EMPTY << 8)) == 0) + break; + } + +#if 1 + if ((intstat & INTSMASK) != 0) + goto phasechange; +#else + if ((intstat & INTSMASK) != 0 && + (intstat & (SSTS_DREG_EMPTY << 8))) + goto phasechange; +#endif + if ((intstat & (SSTS_DREG_FULL << 8)) != 0) + xfer = min(DINAMOUNT, n); + else + xfer = min(1, n); + + SPC_MISC((">%d ", xfer)); + + n -= xfer; + in += xfer; + + while (xfer-- > 0) { + *p++ = bus_space_read_1(iot, ioh, DREG); + } + + if ((intstat & INTSMASK) != 0) + goto phasechange; + } + + /* + * Some SCSI-devices are rude enough to transfer more data than what + * was requested, e.g. 2048 bytes from a CD-ROM instead of the + * requested 512. Test for progress, i.e. real transfers. If no real + * transfers have been performed (n is probably already zero) and the + * FIFO is not empty, waste some bytes.... + */ + if (in == 0) { + for (;;) { + if (bus_space_read_1(iot, ioh, INTS) != 0) + break; + } + SPC_MISC(("extra data ")); + } + +phasechange: + /* Stop the FIFO data path. */ + + /* Turn on ENREQINIT again. */ + + return in; +} + +/* + * Catch an interrupt from the adaptor + */ +/* + * This is the workhorse routine of the driver. + * Deficiencies (for now): + * 1) always uses programmed I/O + */ +int +spc_intr(arg) + void *arg; +{ + struct spc_softc *sc = arg; + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + u_char ints; + struct spc_acb *acb; + struct scsi_link *sc_link; + struct spc_tinfo *ti; + int n; + + /* + * Disable interrupt. + */ + bus_space_write_1(iot, ioh, SCTL, bus_space_read_1(iot, ioh, SCTL) & ~SCTL_INTR_ENAB); + + SPC_TRACE(("spc_intr ")); + +loop: + /* + * Loop until transfer completion. + */ + /* + * First check for abnormal conditions, such as reset. + */ +#ifdef x68k /* XXX? */ + while ((ints = bus_space_read_1(iot, ioh, INTS)) == 0) + delay(1); + SPC_MISC(("ints = 0x%x ", ints)); +#else + ints = bus_space_read_1(iot, ioh, INTS); + SPC_MISC(("ints = 0x%x ", ints)); +#endif + + if ((ints & INTS_RST) != 0) { + printf("%s: SCSI bus reset\n", sc->sc_dev.dv_xname); + goto reset; + } + + /* + * Check for less serious errors. + */ + if ((bus_space_read_1(iot, ioh, SERR) & (SERR_SCSI_PAR|SERR_SPC_PAR)) != 0) { + printf("%s: SCSI bus parity error\n", sc->sc_dev.dv_xname); + if (sc->sc_prevphase == PH_MSGIN) { + sc->sc_flags |= SPC_DROP_MSGIN; + spc_sched_msgout(sc, SEND_PARITY_ERROR); + } else + spc_sched_msgout(sc, SEND_INIT_DET_ERR); + } + + /* + * If we're not already busy doing something test for the following + * conditions: + * 1) We have been reselected by something + * 2) We have selected something successfully + * 3) Our selection process has timed out + * 4) This is really a bus free interrupt just to get a new command + * going? + * 5) Spurious interrupt? + */ + switch (sc->sc_state) { + case SPC_IDLE: + case SPC_SELECTING: + SPC_MISC(("ints:0x%02x ", ints)); + + if ((ints & INTS_SEL) != 0) { + /* + * We don't currently support target mode. + */ + printf("%s: target mode selected; going to BUS FREE\n", + sc->sc_dev.dv_xname); + + goto sched; + } else if ((ints & INTS_RESEL) != 0) { + SPC_MISC(("reselected ")); + + /* + * If we're trying to select a target ourselves, + * push our command back into the ready list. + */ + if (sc->sc_state == SPC_SELECTING) { + SPC_MISC(("backoff selector ")); + SPC_ASSERT(sc->sc_nexus != NULL); + acb = sc->sc_nexus; + sc->sc_nexus = NULL; + TAILQ_INSERT_HEAD(&sc->ready_list, acb, chain); + } + + /* Save reselection ID. */ + sc->sc_selid = bus_space_read_1(iot, ioh, TEMP); + + sc->sc_state = SPC_RESELECTED; + } else if ((ints & INTS_CMD_DONE) != 0) { + SPC_MISC(("selected ")); + + /* + * We have selected a target. Things to do: + * a) Determine what message(s) to send. + * b) Verify that we're still selecting the target. + * c) Mark device as busy. + */ + if (sc->sc_state != SPC_SELECTING) { + printf("%s: selection out while idle; resetting\n", + sc->sc_dev.dv_xname); + SPC_BREAK(); + goto reset; + } + SPC_ASSERT(sc->sc_nexus != NULL); + acb = sc->sc_nexus; + sc_link = acb->xs->sc_link; + ti = &sc->sc_tinfo[sc_link->target]; + + sc->sc_msgpriq = SEND_IDENTIFY; + if (acb->flags & ACB_RESET) + sc->sc_msgpriq |= SEND_DEV_RESET; + else if (acb->flags & ACB_ABORT) + sc->sc_msgpriq |= SEND_ABORT; + else { +#if SPC_USE_SYNCHRONOUS + if ((ti->flags & DO_SYNC) != 0) + sc->sc_msgpriq |= SEND_SDTR; +#endif +#if SPC_USE_WIDE + if ((ti->flags & DO_WIDE) != 0) + sc->sc_msgpriq |= SEND_WDTR; +#endif + } + + acb->flags |= ACB_NEXUS; + ti->lubusy |= (1 << sc_link->lun); + + /* Do an implicit RESTORE POINTERS. */ + sc->sc_dp = acb->data_addr; + sc->sc_dleft = acb->data_length; + sc->sc_cp = (u_char *)&acb->scsi_cmd; + sc->sc_cleft = acb->scsi_cmd_length; + + /* On our first connection, schedule a timeout. */ + if ((acb->xs->flags & SCSI_POLL) == 0) { + timeout_set(&acb->xs->stimeout, spc_timeout, acb); + timeout_add(&acb->xs->stimeout, + (acb->timeout * hz) / 1000); + } + sc->sc_state = SPC_CONNECTED; + } else if ((ints & INTS_TIMEOUT) != 0) { + SPC_MISC(("selection timeout ")); + + if (sc->sc_state != SPC_SELECTING) { + printf("%s: selection timeout while idle; resetting\n", + sc->sc_dev.dv_xname); + SPC_BREAK(); + goto reset; + } + SPC_ASSERT(sc->sc_nexus != NULL); + acb = sc->sc_nexus; + + delay(250); + + acb->xs->error = XS_SELTIMEOUT; + goto finish; + } else { + if (sc->sc_state != SPC_IDLE) { + printf("%s: BUS FREE while not idle; state=%d\n", + sc->sc_dev.dv_xname, sc->sc_state); + SPC_BREAK(); + goto out; + } + + goto sched; + } + + /* + * Turn off selection stuff, and prepare to catch bus free + * interrupts, parity errors, and phase changes. + */ + + sc->sc_flags = 0; + sc->sc_prevphase = PH_INVALID; + goto dophase; + } + + if ((ints & INTS_DISCON) != 0) { + /* We've gone to BUS FREE phase. */ + bus_space_write_1(iot, ioh, PCTL, + bus_space_read_1(iot, ioh, PCTL) & ~PCTL_BFINT_ENAB); + /* disable disconnect interrupt */ + bus_space_write_1(iot, ioh, INTS, ints); + /* XXX reset interrput */ + + switch (sc->sc_state) { + case SPC_RESELECTED: + goto sched; + + case SPC_CONNECTED: + SPC_ASSERT(sc->sc_nexus != NULL); + acb = sc->sc_nexus; + +#if SPC_USE_SYNCHRONOUS + SPC_USE_WIDE + if (sc->sc_prevphase == PH_MSGOUT) { + /* + * If the target went to BUS FREE phase during + * or immediately after sending a SDTR or WDTR + * message, disable negotiation. + */ + sc_link = acb->xs->sc_link; + ti = &sc->sc_tinfo[sc_link->target]; + switch (sc->sc_lastmsg) { +#if SPC_USE_SYNCHRONOUS + case SEND_SDTR: + ti->flags &= ~DO_SYNC; + ti->period = ti->offset = 0; + break; +#endif +#if SPC_USE_WIDE + case SEND_WDTR: + ti->flags &= ~DO_WIDE; + ti->width = 0; + break; +#endif + } + } +#endif + + if ((sc->sc_flags & SPC_ABORTING) == 0) { + /* + * Section 5.1.1 of the SCSI 2 spec suggests + * issuing a REQUEST SENSE following an + * unexpected disconnect. Some devices go into + * a contingent allegiance condition when + * disconnecting, and this is necessary to + * clean up their state. + */ + printf("%s: unexpected disconnect; sending REQUEST SENSE\n", + sc->sc_dev.dv_xname); + SPC_BREAK(); + spc_sense(sc, acb); + goto out; + } + + acb->xs->error = XS_DRIVER_STUFFUP; + goto finish; + + case SPC_DISCONNECT: + SPC_ASSERT(sc->sc_nexus != NULL); + acb = sc->sc_nexus; + TAILQ_INSERT_HEAD(&sc->nexus_list, acb, chain); + sc->sc_nexus = NULL; + goto sched; + + case SPC_CMDCOMPLETE: + SPC_ASSERT(sc->sc_nexus != NULL); + acb = sc->sc_nexus; + goto finish; + } + } + else if ((ints & INTS_CMD_DONE) != 0 && + sc->sc_prevphase == PH_MSGIN && sc->sc_state != SPC_CONNECTED) + goto out; + +dophase: +#if 0 + if ((bus_space_read_1(iot, ioh, PSNS) & PSNS_REQ) == 0) { + /* Wait for REQINIT. */ + goto out; + } +#else + bus_space_write_1(iot, ioh, INTS, ints); + ints = 0; + while ((bus_space_read_1(iot, ioh, PSNS) & PSNS_REQ) == 0) + delay(1); /* need timeout XXX */ +#endif + + /* + * State transition. + */ + sc->sc_phase = bus_space_read_1(iot, ioh, PSNS) & PH_MASK; +/* bus_space_write_1(iot, ioh, PCTL, sc->sc_phase);*/ + + SPC_MISC(("phase=%d\n", sc->sc_phase)); + switch (sc->sc_phase) { + case PH_MSGOUT: + if (sc->sc_state != SPC_CONNECTED && + sc->sc_state != SPC_RESELECTED) + break; + spc_msgout(sc); + sc->sc_prevphase = PH_MSGOUT; + goto loop; + + case PH_MSGIN: + if (sc->sc_state != SPC_CONNECTED && + sc->sc_state != SPC_RESELECTED) + break; + spc_msgin(sc); + sc->sc_prevphase = PH_MSGIN; + goto loop; + + case PH_CMD: + if (sc->sc_state != SPC_CONNECTED) + break; +#if SPC_DEBUG + if ((spc_debug & SPC_SHOWMISC) != 0) { + SPC_ASSERT(sc->sc_nexus != NULL); + acb = sc->sc_nexus; + printf("cmd=0x%02x+%d ", + acb->scsi_cmd.opcode, acb->scsi_cmd_length-1); + } +#endif + n = spc_dataout_pio(sc, sc->sc_cp, sc->sc_cleft); + sc->sc_cp += n; + sc->sc_cleft -= n; + sc->sc_prevphase = PH_CMD; + goto loop; + + case PH_DATAOUT: + if (sc->sc_state != SPC_CONNECTED) + break; + SPC_MISC(("dataout dleft=%d ", sc->sc_dleft)); + n = spc_dataout_pio(sc, sc->sc_dp, sc->sc_dleft); + sc->sc_dp += n; + sc->sc_dleft -= n; + sc->sc_prevphase = PH_DATAOUT; + goto loop; + + case PH_DATAIN: + if (sc->sc_state != SPC_CONNECTED) + break; + SPC_MISC(("datain ")); + n = spc_datain_pio(sc, sc->sc_dp, sc->sc_dleft); + sc->sc_dp += n; + sc->sc_dleft -= n; + sc->sc_prevphase = PH_DATAIN; + goto loop; + + case PH_STAT: + if (sc->sc_state != SPC_CONNECTED) + break; + SPC_ASSERT(sc->sc_nexus != NULL); + acb = sc->sc_nexus; + /*acb->target_stat = bus_space_read_1(iot, ioh, DREG);*/ + spc_datain_pio(sc, &acb->target_stat, 1); + SPC_MISC(("target_stat=0x%02x ", acb->target_stat)); + sc->sc_prevphase = PH_STAT; + goto loop; + } + + printf("%s: unexpected bus phase; resetting\n", sc->sc_dev.dv_xname); + SPC_BREAK(); +reset: + spc_init(sc); + return 1; + +finish: + timeout_del(&acb->xs->stimeout); + bus_space_write_1(iot, ioh, INTS, ints); + ints = 0; + spc_done(sc, acb); + goto out; + +sched: + sc->sc_state = SPC_IDLE; + spc_sched(sc); + goto out; + +out: + if (ints) + bus_space_write_1(iot, ioh, INTS, ints); + bus_space_write_1(iot, ioh, SCTL, + bus_space_read_1(iot, ioh, SCTL) | SCTL_INTR_ENAB); + return 1; +} + +void +spc_abort(sc, acb) + struct spc_softc *sc; + struct spc_acb *acb; +{ + + /* 2 secs for the abort */ + acb->timeout = SPC_ABORT_TIMEOUT; + acb->flags |= ACB_ABORT; + + if (acb == sc->sc_nexus) { + /* + * If we're still selecting, the message will be scheduled + * after selection is complete. + */ + if (sc->sc_state == SPC_CONNECTED) + spc_sched_msgout(sc, SEND_ABORT); + } else { + spc_dequeue(sc, acb); + TAILQ_INSERT_HEAD(&sc->ready_list, acb, chain); + if (sc->sc_state == SPC_IDLE) + spc_sched(sc); + } +} + +void +spc_timeout(arg) + void *arg; +{ + struct spc_acb *acb = arg; + struct scsi_xfer *xs = acb->xs; + struct scsi_link *sc_link = xs->sc_link; + struct spc_softc *sc = sc_link->adapter_softc; + int s; + + sc_print_addr(sc_link); + printf("timed out"); + + s = splbio(); + + if (acb->flags & ACB_ABORT) { + /* abort timed out */ + printf(" AGAIN\n"); + /* XXX Must reset! */ + } else { + /* abort the operation that has timed out */ + printf("\n"); + acb->xs->error = XS_TIMEOUT; + spc_abort(sc, acb); + } + + splx(s); +} + +#ifdef SPC_DEBUG +/* + * The following functions are mostly used for debugging purposes, either + * directly called from the driver or from the kernel debugger. + */ + +void +spc_show_scsi_cmd(acb) + struct spc_acb *acb; +{ + u_char *b = (u_char *)&acb->scsi_cmd; + struct scsi_link *sc_link = acb->xs->sc_link; + int i; + + sc_print_addr(sc_link); + if ((acb->xs->flags & SCSI_RESET) == 0) { + for (i = 0; i < acb->scsi_cmd_length; i++) { + if (i) + printf(","); + printf("%x", b[i]); + } + printf("\n"); + } else + printf("RESET\n"); +} + +void +spc_print_acb(acb) + struct spc_acb *acb; +{ + + printf("acb@%p xs=%p flags=%x", acb, acb->xs, acb->flags); + printf(" dp=%p dleft=%d target_stat=%x\n", + acb->data_addr, acb->data_length, acb->target_stat); + spc_show_scsi_cmd(acb); +} + +void +spc_print_active_acb() +{ + struct spc_acb *acb; + struct spc_softc *sc = spc_cd.cd_devs[0]; /* XXX */ + + printf("ready list:\n"); + for (acb = sc->ready_list.tqh_first; acb != NULL; + acb = acb->chain.tqe_next) + spc_print_acb(acb); + printf("nexus:\n"); + if (sc->sc_nexus != NULL) + spc_print_acb(sc->sc_nexus); + printf("nexus list:\n"); + for (acb = sc->nexus_list.tqh_first; acb != NULL; + acb = acb->chain.tqe_next) + spc_print_acb(acb); +} + +void +spc_dump89352(sc) + struct spc_softc *sc; +{ + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + + printf("mb89352: BDID=%x SCTL=%x SCMD=%x TMOD=%x\n", + bus_space_read_1(iot, ioh, BDID), + bus_space_read_1(iot, ioh, SCTL), + bus_space_read_1(iot, ioh, SCMD), + bus_space_read_1(iot, ioh, TMOD)); + printf(" INTS=%x PSNS=%x SSTS=%x SERR=%x PCTL=%x\n", + bus_space_read_1(iot, ioh, INTS), + bus_space_read_1(iot, ioh, PSNS), + bus_space_read_1(iot, ioh, SSTS), + bus_space_read_1(iot, ioh, SERR), + bus_space_read_1(iot, ioh, PCTL)); + printf(" MBC=%x DREG=%x TEMP=%x TCH=%x TCM=%x\n", + bus_space_read_1(iot, ioh, MBC), +#if 0 + bus_space_read_1(iot, ioh, DREG), +#else + 0, +#endif + bus_space_read_1(iot, ioh, TEMP), + bus_space_read_1(iot, ioh, TCH), + bus_space_read_1(iot, ioh, TCM)); + printf(" TCL=%x EXBF=%x\n", + bus_space_read_1(iot, ioh, TCL), + bus_space_read_1(iot, ioh, EXBF)); +} + +void +spc_dump_driver(sc) + struct spc_softc *sc; +{ + struct spc_tinfo *ti; + int i; + + printf("nexus=%p prevphase=%x\n", sc->sc_nexus, sc->sc_prevphase); + printf("state=%x msgin=%x msgpriq=%x msgoutq=%x lastmsg=%x currmsg=%x\n", + sc->sc_state, sc->sc_imess[0], + sc->sc_msgpriq, sc->sc_msgoutq, sc->sc_lastmsg, sc->sc_currmsg); + for (i = 0; i < 7; i++) { + ti = &sc->sc_tinfo[i]; + printf("tinfo%d: %d cmds %d disconnects %d timeouts", + i, ti->cmds, ti->dconns, ti->touts); + printf(" %d senses flags=%x\n", ti->senses, ti->flags); + } +} +#endif diff --git a/sys/arch/luna88k/dev/mb89352reg.h b/sys/arch/luna88k/dev/mb89352reg.h new file mode 100644 index 00000000000..5a30b391173 --- /dev/null +++ b/sys/arch/luna88k/dev/mb89352reg.h @@ -0,0 +1,238 @@ +/* $OpenBSD: mb89352reg.h,v 1.1 2004/04/21 15:23:54 aoyama Exp $ */ +/* $NetBSD: mb89352reg.h,v 1.3 2003/08/07 16:31:02 agc Exp $ */ +/* NecBSD: mb89352reg.h,v 1.3 1998/03/14 07:04:34 kmatsuda Exp */ + +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Charles M. Hannum, Masaru Oki and Kouichi Matsuda. + * + * This code is derived from software contributed to Berkeley by + * Van Jacobson of Lawrence Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)scsireg.h 8.1 (Berkeley) 6/10/93 + */ + +/*- + * Copyright (c) 1996,97,98,99 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Charles M. Hannum, Masaru Oki and Kouichi Matsuda. + * + * This code is derived from software contributed to Berkeley by + * Van Jacobson of Lawrence Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)scsireg.h 8.1 (Berkeley) 6/10/93 + */ +/* + * [NetBSD for NEC PC-98 series] + * Copyright (c) 1996, 1997, 1998 + * NetBSD/pc98 porting staff. All rights reserved. + * Copyright (c) 1996, 1997, 1998 + * Kouichi Matsuda. All rights reserved. + */ + +/* + * FUJITSU MB89352A SCSI Protocol Controler Hardware Description. + */ + +/* Definitions, most of them has turned out to be unneccesary, but here they + * are anyway. + */ + +#define BDID 0x00 /* Bus Device ID (R/W) */ +#define SCTL 0x01 /* SPC Control register (R/W) */ +#define SCMD 0x02 /* Command Register (R/W) */ +#define TMOD 0x03 /* Transmit Mode Register (synch models) */ +#define INTS 0x04 /* Interrupt sense (R); Interrupt Reset (W) */ +#define PSNS 0x05 /* Phase Sence (R); SPC Diagnostic Control (W) */ +#define SSTS 0x06 /* SPC status (R/O) */ +#define SERR 0x07 /* SPC error status (R/O) */ +#define PCTL 0x08 /* Phase Control (R/W) */ +#define MBC 0x09 /* Modified Byte Counter (R/O) */ +#define DREG 0x0a /* Data Register (R/W) */ +#define TEMP 0x0b /* Temporary Register (R/W) */ +#define TCH 0x0c /* Transfer Counter High (R/W) */ +#define TCM 0x0d /* Transfer Counter Middle (R/W) */ +#define TCL 0x0e /* Transfer Counter Low (R/W) */ +#define EXBF 0x0f /* External Buffer (synch models) */ + +/* What all the bits do */ + +/* SCSI_BDID */ +/* SCSI selection/reselection ID (both target *and* initiator) */ +#define SELID7 0x80 +#define SELID6 0x40 +#define SELID5 0x20 +#define SELID4 0x10 +#define SELID3 0x08 +#define SELID2 0x04 +#define SELID1 0x02 +#define SELID0 0x01 + +/* SCSI_SCTL */ +#define SCTL_DISABLE 0x80 +#define SCTL_CTRLRST 0x40 +#define SCTL_DIAG 0x20 +#define SCTL_ABRT_ENAB 0x10 +#define SCTL_PARITY_ENAB 0x08 +#define SCTL_SEL_ENAB 0x04 +#define SCTL_RESEL_ENAB 0x02 +#define SCTL_INTR_ENAB 0x01 + +/* SCSI_SCMD */ +#define SCMD_RST 0x10 +#define SCMD_ICPT_XFR 0x08 +#define SCMD_PROG_XFR 0x04 +#define SCMD_PAD 0x01 /* if initiator */ +#define SCMD_PERR_STOP 0x01 /* if target */ + /* command codes */ +#define SCMD_BUS_REL 0x00 +#define SCMD_SELECT 0x20 +#define SCMD_RST_ATN 0x40 +#define SCMD_SET_ATN 0x60 +#define SCMD_XFR 0x80 +#define SCMD_XFR_PAUSE 0xa0 +#define SCMD_RST_ACK 0xc0 +#define SCMD_SET_ACK 0xe0 + +/* SCSI_TMOD */ +#define TMOD_SYNC 0x80 + +/* SCSI_INTS */ +#define INTS_SEL 0x80 +#define INTS_RESEL 0x40 +#define INTS_DISCON 0x20 +#define INTS_CMD_DONE 0x10 +#define INTS_SRV_REQ 0x08 +#define INTS_TIMEOUT 0x04 +#define INTS_HARD_ERR 0x02 +#define INTS_RST 0x01 + +/* SCSI_PSNS */ +#define PSNS_REQ 0x80 +#define PSNS_ACK 0x40 +#define PSNS_ATN 0x20 +#define PSNS_SEL 0x10 +#define PSNS_BSY 0x08 + +/* PSNS */ +#define REQI 0x80 +#define ACKI 0x40 +#define ATNI 0x20 +#define SELI 0x10 +#define BSYI 0x08 +#define MSGI 0x04 +#define CDI 0x02 +#define IOI 0x01 + +/* Important! The 3 most significant bits of this register, in initiator mode, + * represents the "expected" SCSI bus phase and can be used to trigger phase + * mismatch and phase change interrupts. But more important: If there is a + * phase mismatch the chip will not transfer any data! This is actually a nice + * feature as it gives us a bit more control over what is happening when we are + * bursting data (in) through the FIFOs and the phase suddenly changes from + * DATA IN to STATUS or MESSAGE IN. The transfer will stop and wait for the + * proper phase to be set in this register instead of dumping the bits into the + * FIFOs. + */ +#if 0 +#define REQO 0x80 +#define ACKO 0x40 +#define ATNO 0x20 +#define SELO 0x10 +#define BSYO 0x08 +#endif +/* PCTL */ +#define MSGO 0x04 +#define CDO 0x02 +#define IOO 0x01 + +/* Information transfer phases */ +#define PH_DATAOUT (0) +#define PH_DATAIN (IOI) +#define PH_CMD (CDI) +#define PH_STAT (CDI | IOI) +#define PH_MSGOUT (MSGI | CDI) +#define PH_MSGIN (MSGI | CDI | IOI) + +#define PH_MASK (MSGI | CDI | IOI) + +#define PH_INVALID 0xff + +/* SCSI_SSTS */ +#define SSTS_INITIATOR 0x80 +#define SSTS_TARGET 0x40 +#define SSTS_BUSY 0x20 +#define SSTS_XFR 0x10 +#define SSTS_ACTIVE (SSTS_INITIATOR|SSTS_XFR) +#define SSTS_RST 0x08 +#define SSTS_TCZERO 0x04 +#define SSTS_DREG_FULL 0x02 +#define SSTS_DREG_EMPTY 0x01 + +/* SCSI_SERR */ +#define SERR_SCSI_PAR 0x80 +#define SERR_SPC_PAR 0x40 +#define SERR_TC_PAR 0x08 +#define SERR_PHASE_ERR 0x04 +#define SERR_SHORT_XFR 0x02 +#define SERR_OFFSET 0x01 + +/* SCSI_PCTL */ +#define PCTL_BFINT_ENAB 0x80 diff --git a/sys/arch/luna88k/dev/mb89352var.h b/sys/arch/luna88k/dev/mb89352var.h new file mode 100644 index 00000000000..e77f0649a05 --- /dev/null +++ b/sys/arch/luna88k/dev/mb89352var.h @@ -0,0 +1,214 @@ +/* $OpenBSD: mb89352var.h,v 1.1 2004/04/21 15:23:55 aoyama Exp $ */ +/* $NetBSD: mb89352var.h,v 1.6 2003/08/02 12:48:09 tsutsui Exp $ */ +/* NecBSD: mb89352var.h,v 1.4 1998/03/14 07:31:22 kmatsuda Exp */ + +/*- + * Copyright (c) 1996,97,98,99 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Charles M. Hannum, Masaru Oki and Kouichi Matsuda. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Charles M. Hannum. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Copyright (c) 1994 Jarle Greipsland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/* + * [NetBSD for NEC PC-98 series] + * Copyright (c) 1996, 1997, 1998 + * NetBSD/pc98 porting staff. All rights reserved. + * Copyright (c) 1996, 1997, 1998 + * Kouich Matsuda. All rights reserved. + */ + +#ifndef _MB89352VAR_H_ +#define _MB89352VAR_H_ + +/* + * ACB. Holds additional information for each SCSI command Comments: We + * need a separate scsi command block because we may need to overwrite it + * with a request sense command. Basicly, we refrain from fiddling with + * the scsi_xfer struct (except do the expected updating of return values). + * We'll generally update: xs->{flags,resid,error,sense,status} and + * occasionally xs->retries. + */ +struct spc_acb { + struct scsi_generic scsi_cmd; + int scsi_cmd_length; + u_char *data_addr; /* Saved data pointer */ + int data_length; /* Residue */ + + u_char target_stat; /* SCSI status byte */ + +#ifdef notdef + struct spc_dma_seg dma[SPC_NSEG]; /* Physical addresses+len */ +#endif + + TAILQ_ENTRY(spc_acb) chain; + struct scsi_xfer *xs; /* SCSI xfer ctrl block from above */ + int flags; +#define ACB_ALLOC 0x01 +#define ACB_NEXUS 0x02 +#define ACB_SENSE 0x04 +#define ACB_ABORT 0x40 +#define ACB_RESET 0x80 + int timeout; +}; + +/* + * Some info about each (possible) target on the SCSI bus. This should + * probably have been a "per target+lunit" structure, but we'll leave it at + * this for now. + */ +struct spc_tinfo { + int cmds; /* #commands processed */ + int dconns; /* #disconnects */ + int touts; /* #timeouts */ + int perrs; /* #parity errors */ + int senses; /* #request sense commands sent */ + ushort lubusy; /* What local units/subr. are busy? */ + u_char flags; +#define DO_SYNC 0x01 /* (Re)Negotiate synchronous options */ +#define DO_WIDE 0x02 /* (Re)Negotiate wide options */ + u_char period; /* Period suggestion */ + u_char offset; /* Offset suggestion */ + u_char width; /* Width suggestion */ +}; + +struct spc_softc { + struct device sc_dev; + + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + + struct scsi_link sc_link; /* prototype for subdevs */ + + TAILQ_HEAD(, spc_acb) free_list, ready_list, nexus_list; + struct spc_acb *sc_nexus; /* current command */ + struct spc_acb sc_acb[8]; + struct spc_tinfo sc_tinfo[8]; + + /* Data about the current nexus (updated for every cmd switch) */ + u_char *sc_dp; /* Current data pointer */ + size_t sc_dleft; /* Data bytes left to transfer */ + u_char *sc_cp; /* Current command pointer */ + size_t sc_cleft; /* Command bytes left to transfer */ + + /* Adapter state */ + u_char sc_phase; /* Current bus phase */ + u_char sc_prevphase; /* Previous bus phase */ + u_char sc_state; /* State applicable to the adapter */ +#define SPC_INIT 0 +#define SPC_IDLE 1 +#define SPC_SELECTING 2 /* SCSI command is arbiting */ +#define SPC_RESELECTED 3 /* Has been reselected */ +#define SPC_CONNECTED 4 /* Actively using the SCSI bus */ +#define SPC_DISCONNECT 5 /* MSG_DISCONNECT received */ +#define SPC_CMDCOMPLETE 6 /* MSG_CMDCOMPLETE received */ +#define SPC_CLEANING 7 + u_char sc_flags; +#define SPC_DROP_MSGIN 0x01 /* Discard all msgs (parity err detected) */ +#define SPC_ABORTING 0x02 /* Bailing out */ +#define SPC_DOINGDMA 0x04 /* doing DMA */ +#define SPC_INACTIVE 0x80 /* The FIFO data path is active! */ + u_char sc_selid; /* Reselection ID */ + + /* Message stuff */ + u_char sc_msgpriq; /* Messages we want to send */ + u_char sc_msgoutq; /* Messages sent during last MESSAGE OUT */ + u_char sc_lastmsg; /* Message last transmitted */ + u_char sc_currmsg; /* Message currently ready to transmit */ +#define SEND_DEV_RESET 0x01 +#define SEND_PARITY_ERROR 0x02 +#define SEND_INIT_DET_ERR 0x04 +#define SEND_REJECT 0x08 +#define SEND_IDENTIFY 0x10 +#define SEND_ABORT 0x20 +#define SEND_SDTR 0x40 +#define SEND_WDTR 0x80 +#define SPC_MAX_MSG_LEN 8 + u_char sc_omess[SPC_MAX_MSG_LEN]; + u_char *sc_omp; /* Outgoing message pointer */ + u_char sc_imess[SPC_MAX_MSG_LEN]; + u_char *sc_imp; /* Incoming message pointer */ + + /* Hardware stuff */ + int sc_initiator; /* Our scsi id */ + int sc_freq; /* Clock frequency in MHz */ + int sc_minsync; /* Minimum sync period / 4 */ + int sc_maxsync; /* Maximum sync period / 4 */ + + /* DMA function set from MD code */ + void (*sc_dma_start)(struct spc_softc *, void *, size_t, int); + void (*sc_dma_done)(struct spc_softc *); +}; + +#if SPC_DEBUG +#define SPC_SHOWACBS 0x01 +#define SPC_SHOWINTS 0x02 +#define SPC_SHOWCMDS 0x04 +#define SPC_SHOWMISC 0x08 +#define SPC_SHOWTRACE 0x10 +#define SPC_SHOWSTART 0x20 +#define SPC_DOBREAK 0x40 +extern int spc_debug; /* SPC_SHOWSTART|SPC_SHOWMISC|SPC_SHOWTRACE; */ +#define SPC_PRINT(b, s) do {if ((spc_debug & (b)) != 0) printf s;} while (0) +#define SPC_BREAK() do {if ((spc_debug & SPC_DOBREAK) != 0) Debugger();} while (0) +#define SPC_ASSERT(x) do {if (x) {} else {printf("%s at line %d: assertion failed\n", sc->sc_dev.dv_xname, __LINE__); Debugger();}} while (0) +#else +#define SPC_PRINT(b, s) +#define SPC_BREAK() +#define SPC_ASSERT(x) +#endif + +#define SPC_ACBS(s) SPC_PRINT(SPC_SHOWACBS, s) +#define SPC_INTS(s) SPC_PRINT(SPC_SHOWINTS, s) +#define SPC_CMDS(s) SPC_PRINT(SPC_SHOWCMDS, s) +#define SPC_MISC(s) SPC_PRINT(SPC_SHOWMISC, s) +#define SPC_TRACE(s) SPC_PRINT(SPC_SHOWTRACE, s) +#define SPC_START(s) SPC_PRINT(SPC_SHOWSTART, s) + +void spc_attach(struct spc_softc *, struct scsi_adapter *); +int spc_intr(void *); +int spc_find(bus_space_tag_t, bus_space_handle_t, int); +void spc_init(struct spc_softc *); +void spc_sched(struct spc_softc *); +int spc_scsi_cmd(struct scsi_xfer *); +void spc_minphys(struct buf *); +#endif /* _MB89352VAR_H_ */ diff --git a/sys/arch/luna88k/dev/omrasops.c b/sys/arch/luna88k/dev/omrasops.c new file mode 100644 index 00000000000..f9777c933dc --- /dev/null +++ b/sys/arch/luna88k/dev/omrasops.c @@ -0,0 +1,475 @@ +/* $OpenBSD: omrasops.c,v 1.1 2004/04/21 15:23:55 aoyama Exp $ */ +/* $NetBSD: omrasops.c,v 1.1 2000/01/05 08:48:56 nisimura Exp $ */ + +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Tohru Nishimura. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Designed speficically for 'm68k bitorder'; + * - most significant byte is stored at lower address, + * - most significant bit is displayed at left most on screen. + * Implementation relys on; + * - every memory references is done in aligned 32bit chunk, + * - font glyphs are stored in 32bit padded. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> + +#include <dev/rcons/raster.h> +#include <dev/wscons/wscons_raster.h> +#include <dev/wscons/wscons_rfont.h> +#include <dev/wscons/wsdisplayvar.h> + +/* wscons emulator operations */ +void om_cursor(void *, int, int, int); +int om_mapchar(void *, int, unsigned int *); +void om_putchar(void *, int, int, u_int, long); +void om_copycols(void *, int, int, int, int); +void om_copyrows(void *, int, int, int num); +void om_erasecols(void *, int, int, int, long); +void om_eraserows(void *, int, int, long); +int om_alloc_attr(void *, int, int, int, long *); + +struct wsdisplay_emulops omfb_emulops = { + om_cursor, + om_mapchar, + om_putchar, + om_copycols, + om_erasecols, + om_copyrows, + om_eraserows, + om_alloc_attr +}; + +#define ALL1BITS (~0U) +#define ALL0BITS (0U) +#define BLITWIDTH (32) +#define ALIGNMASK (0x1f) +#define BYTESDONE (4) + +#define W(p) (*(u_int32_t *)(p)) +#define R(p) (*(u_int32_t *)((caddr_t)(p) + 0x40000)) + +/* + * Blit a character at the specified co-ordinates. + */ +void +om_putchar(cookie, row, startcol, uc, attr) + void *cookie; + int row, startcol; + u_int uc; + long attr; +{ + struct rcons *rc = cookie; + struct raster *rap = rc->rc_sp; + caddr_t p; + int scanspan, startx, height, width, align, y; + u_int32_t lmask, rmask, glyph, inverse; + u_int32_t *g; + + scanspan = rap->linelongs * 4; + y = rc->rc_yorigin + rc->rc_font->height * row; + startx = rc->rc_xorigin + rc->rc_font->width * startcol; + height = rc->rc_font->height; + g = rc->rc_font->chars[uc].r->pixels; + inverse = (attr != 0) ? ALL1BITS : ALL0BITS; + + p = (caddr_t)rap->pixels + y * scanspan + ((startx / 32) * 4); + align = startx & ALIGNMASK; + width = rc->rc_font->width + align; + lmask = ALL1BITS >> align; + rmask = ALL1BITS << (-width & ALIGNMASK); + if (width <= BLITWIDTH) { + lmask &= rmask; + while (height > 0) { + glyph = *g; + glyph = (glyph >> align) ^ inverse; + W(p) = (R(p) & ~lmask) | (glyph & lmask); + p += scanspan; + g += 1; + height--; + } + } + else { + caddr_t q = p; + u_int32_t lhalf, rhalf; + + while (height > 0) { + glyph = *g; + lhalf = (glyph >> align) ^ inverse; + W(p) = (R(p) & ~lmask) | (lhalf & lmask); + p += BYTESDONE; + rhalf = (glyph << (BLITWIDTH - align)) ^ inverse; + W(p) = (rhalf & rmask) | (R(p) & ~rmask); + + p = (q += scanspan); + g += 1; + height--; + } + } +} + +void +om_erasecols(cookie, row, startcol, ncols, attr) + void *cookie; + int row, startcol, ncols; + long attr; +{ + struct rcons *rc = cookie; + struct raster *rap = rc->rc_sp; + caddr_t p; + int scanspan, startx, height, width, align, w, y; + u_int32_t lmask, rmask, fill; + + scanspan = rap->linelongs * 4; + y = rc->rc_yorigin + rc->rc_font->height * row; + startx = rc->rc_xorigin + rc->rc_font->width * startcol; + height = rc->rc_font->height; + w = rc->rc_font->width * ncols; + fill = (attr != 0) ? ALL1BITS : ALL0BITS; + + p = (caddr_t)rap->pixels + y * scanspan + ((startx / 32) * 4); + align = startx & ALIGNMASK; + width = w + align; + lmask = ALL1BITS >> align; + rmask = ALL1BITS << (-width & ALIGNMASK); + if (width <= BLITWIDTH) { + lmask &= rmask; + fill &= lmask; + while (height > 0) { + W(p) = (R(p) & ~lmask) | fill; + p += scanspan; + height--; + } + } + else { + caddr_t q = p; + while (height > 0) { + W(p) = (R(p) & ~lmask) | (fill & lmask); + width -= 2 * BLITWIDTH; + while (width > 0) { + p += BYTESDONE; + W(p) = fill; + width -= BLITWIDTH; + } + p += BYTESDONE; + W(p) = (fill & rmask) | (R(p) & ~rmask); + + p = (q += scanspan); + width = w + align; + height--; + } + } +} + +void +om_eraserows(cookie, startrow, nrows, attr) + void *cookie; + int startrow, nrows; + long attr; +{ + struct rcons *rc = cookie; + struct raster *rap = rc->rc_sp; + caddr_t p, q; + int scanspan, starty, height, width, w; + u_int32_t rmask, fill; + + scanspan = rap->linelongs * 4; + starty = rc->rc_yorigin + rc->rc_font->height * startrow; + height = rc->rc_font->height * nrows; + w = rc->rc_font->width * rc->rc_maxcol; + fill = (attr == 1) ? ALL1BITS : ALL0BITS; + + p = (caddr_t)rap->pixels + starty * scanspan; + p += (rc->rc_xorigin / 32) * 4; + width = w; + rmask = ALL1BITS << (-width & ALIGNMASK); + q = p; + while (height > 0) { + W(p) = fill; /* always aligned */ + width -= 2 * BLITWIDTH; + while (width > 0) { + p += BYTESDONE; + W(p) = fill; + width -= BLITWIDTH; + } + p += BYTESDONE; + W(p) = (fill & rmask) | (R(p) & ~rmask); + p = (q += scanspan); + width = w; + height--; + } +} + +void +om_copyrows(cookie, srcrow, dstrow, nrows) + void *cookie; + int srcrow, dstrow, nrows; +{ + struct rcons *rc = cookie; + struct raster *rap = rc->rc_sp; + caddr_t p, q; + int scanspan, offset, srcy, height, width, w; + u_int32_t rmask; + + scanspan = rap->linelongs * 4; + height = rc->rc_font->height * nrows; + offset = (dstrow - srcrow) * scanspan * rc->rc_font->height; + srcy = rc->rc_yorigin + rc->rc_font->height * srcrow; + if (srcrow < dstrow && srcrow + nrows > dstrow) { + scanspan = -scanspan; + srcy += height; + } + + p = (caddr_t)rap->pixels + srcy * (rap->linelongs * 4); + p += (rc->rc_xorigin / 32) * 4; + w = rc->rc_font->width * rc->rc_maxcol; + width = w; + rmask = ALL1BITS << (-width & ALIGNMASK); + q = p; + while (height > 0) { + W(p + offset) = R(p); /* always aligned */ + width -= 2 * BLITWIDTH; + while (width > 0) { + p += BYTESDONE; + W(p + offset) = R(p); + width -= BLITWIDTH; + } + p += BYTESDONE; + W(p + offset) = (R(p) & rmask) | (R(p + offset) & ~rmask); + + p = (q += scanspan); + width = w; + height--; + } +} + +void +om_copycols(cookie, startrow, srccol, dstcol, ncols) + void *cookie; + int startrow, srccol, dstcol, ncols; +{ + struct rcons *rc = cookie; + struct raster *rap = rc->rc_sp; + caddr_t sp, dp, basep; + int scanspan, height, width, align, shift, w, y, srcx, dstx; + u_int32_t lmask, rmask; + + scanspan = rap->linelongs * 4; + y = rc->rc_yorigin + rc->rc_font->height * startrow; + srcx = rc->rc_xorigin + rc->rc_font->width * srccol; + dstx = rc->rc_xorigin + rc->rc_font->width * dstcol; + height = rc->rc_font->height; + w = rc->rc_font->width * ncols; + basep = (caddr_t)rap->pixels + y * scanspan; + + align = shift = srcx & ALIGNMASK; + width = w + align; + align = dstx & ALIGNMASK; + lmask = ALL1BITS >> align; + rmask = ALL1BITS << (-(w + align) & ALIGNMASK); + shift = align - shift; + sp = basep + (srcx / 32) * 4; + dp = basep + (dstx / 32) * 4; + + if (shift != 0) + goto hardluckalignment; + + /* alignments comfortably match */ + if (width <= BLITWIDTH) { + lmask &= rmask; + while (height > 0) { + W(dp) = (R(dp) & ~lmask) | (R(sp) & lmask); + dp += scanspan; + sp += scanspan; + height--; + } + } + /* copy forward (left-to-right) */ + else if (dstcol < srccol || srccol + ncols < dstcol) { + caddr_t sq = sp, dq = dp; + + w = width; + while (height > 0) { + W(dp) = (R(dp) & ~lmask) | (R(sp) & lmask); + width -= 2 * BLITWIDTH; + while (width > 0) { + sp += BYTESDONE; + dp += BYTESDONE; + W(dp) = R(sp); + width -= BLITWIDTH; + } + sp += BYTESDONE; + dp += BYTESDONE; + W(dp) = (R(sp) & rmask) | (R(dp) & ~rmask); + sp = (sq += scanspan); + dp = (dq += scanspan); + width = w; + height--; + } + } + /* copy backward (right-to-left) */ + else { + caddr_t sq, dq; + + sq = (sp += width / 32 * 4); + dq = (dp += width / 32 * 4); + w = width; + while (height > 0) { + W(dp) = (R(sp) & rmask) | (R(dp) & ~rmask); + width -= 2 * BLITWIDTH; + while (width > 0) { + sp -= BYTESDONE; + dp -= BYTESDONE; + W(dp) = R(sp); + width -= BLITWIDTH; + } + sp -= BYTESDONE; + dp -= BYTESDONE; + W(dp) = (R(dp) & ~lmask) | (R(sp) & lmask); + + sp = (sq += scanspan); + dp = (dq += scanspan); + width = w; + height--; + } + } + return; + + hardluckalignment: + /* alignments painfully disagree */ +} + +/* + * Map a character. + */ +int +om_mapchar(cookie, c, cp) + void *cookie; + int c; + u_int *cp; +{ + if (c < 128) { + *cp = c; + return (5); + } + *cp = ' '; + return (0); +} + +/* + * Position|{enable|disable} the cursor at the specified location. + */ +void +om_cursor(cookie, on, row, col) + void *cookie; + int on, row, col; +{ + struct rcons *rc = cookie; + struct raster *rap = rc->rc_sp; + caddr_t p; + int scanspan, startx, height, width, align, y; + u_int32_t lmask, rmask, image; + + if (!on) { + /* make sure it's on */ + if ((rc->rc_bits & RC_CURSOR) == 0) + return; + + row = *rc->rc_crowp; + col = *rc->rc_ccolp; + } else { + /* unpaint the old copy. */ + *rc->rc_crowp = row; + *rc->rc_ccolp = col; + } + + scanspan = rap->linelongs * 4; + y = rc->rc_yorigin + rc->rc_font->height * row; + startx = rc->rc_xorigin + rc->rc_font->width * col; + height = rc->rc_font->height; + + p = (caddr_t)rap->pixels + y * scanspan + ((startx / 32) * 4); + align = startx & ALIGNMASK; + width = rc->rc_font->width + align; + lmask = ALL1BITS >> align; + rmask = ALL1BITS << (-width & ALIGNMASK); + if (width <= BLITWIDTH) { + lmask &= rmask; + while (height > 0) { + image = R(p); + W(p) = (image & ~lmask) | ((image ^ ALL1BITS) & lmask); + p += scanspan; + height--; + } + } + else { + caddr_t q = p; + + while (height > 0) { + image = R(p); + W(p) = (image & ~lmask) | ((image ^ ALL1BITS) & lmask); + p += BYTESDONE; + image = R(p); + W(p) = ((image ^ ALL1BITS) & rmask) | (image & ~rmask); + + p = (q += scanspan); + height--; + } + } + rc->rc_bits ^= RC_CURSOR; +} + +/* + * Allocate attribute. We just pack these into an integer. + */ +int +om_alloc_attr(id, fg, bg, flags, attrp) + void *id; + int fg, bg, flags; + long *attrp; +{ + if (flags & (WSATTR_HILIT | WSATTR_BLINK | + WSATTR_UNDERLINE | WSATTR_WSCOLORS)) + return (EINVAL); + if (flags & WSATTR_REVERSE) + *attrp = 1; + else + *attrp = 0; + return (0); +} diff --git a/sys/arch/luna88k/dev/sio.c b/sys/arch/luna88k/dev/sio.c new file mode 100644 index 00000000000..f593dc8e95c --- /dev/null +++ b/sys/arch/luna88k/dev/sio.c @@ -0,0 +1,136 @@ +/* $OpenBSD: sio.c,v 1.1 2004/04/21 15:23:55 aoyama Exp $ */ +/* $NetBSD: sio.c,v 1.1 2000/01/05 08:48:55 nisimura Exp $ */ + +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Tohru Nishimura. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* from NetBSD/luna68k dev/sio.c */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> + +#include <machine/cpu.h> +#include <machine/autoconf.h> +#include <machine/locore.h> /* badaddr() */ + +#include <luna88k/luna88k/isr.h> +#include <luna88k/dev/siovar.h> + +int sio_match(struct device *, void *, void *); +void sio_attach(struct device *, struct device *, void *); +int sio_print(void *, const char *); + +const struct cfattach sio_ca = { + sizeof(struct sio_softc), sio_match, sio_attach +}; + +struct cfdriver sio_cd = { + NULL, "sio", DV_DULL, 0 +}; + +void nullintr(int); +int xsiointr(void *); + +int +sio_match(parent, cf, aux) + struct device *parent; + void *cf, *aux; +{ + struct mainbus_attach_args *ma = aux; + + if (strcmp(ma->ma_name, sio_cd.cd_name)) + return 0; + if (badaddr((vaddr_t)ma->ma_addr, 4)) + return 0; + return 1; +} + +void +sio_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct sio_softc *sc = (void *)self; + struct mainbus_attach_args *ma = aux; + struct sio_attach_args sio_args; + int channel; + extern int sysconsole; /* console: 0 for ttya, 1 for desktop */ + + printf(": 7201a\n"); + + sc->scp_ctl = (caddr_t)ma->ma_addr; + sc->scp_intr[0] = sc->scp_intr[1] = nullintr; + for (channel = 0; channel < 2; channel++) { + sio_args.channel = channel; + sio_args.hwflags = (channel == sysconsole); + config_found(self, (void *)&sio_args, sio_print); + } + + isrlink_autovec(xsiointr, sc, ma->ma_ilvl, ISRPRI_TTYNOBUF); +} + +int +sio_print(aux, name) + void *aux; + const char *name; +{ + struct sio_attach_args *args = aux; + + if (name != NULL) + printf("%s: ", name); + + if (args->channel != -1) + printf(" channel %d", args->channel); + + return UNCONF; +} + +int +xsiointr(arg) + void *arg; +{ + struct sio_softc *sc = arg; + + (*sc->scp_intr[0])(0); /* 0: ttya system serial port */ + (*sc->scp_intr[1])(1); /* 1: keyboard and mouse */ + return 1; +} + +void nullintr(v) + int v; +{ +} diff --git a/sys/arch/luna88k/dev/sioreg.h b/sys/arch/luna88k/dev/sioreg.h new file mode 100644 index 00000000000..7ec6b04b6a4 --- /dev/null +++ b/sys/arch/luna88k/dev/sioreg.h @@ -0,0 +1,137 @@ +/* $OpenBSD: sioreg.h,v 1.1 2004/04/21 15:23:55 aoyama Exp $ */ +/* $NetBSD: sioreg.h,v 1.1 2000/01/05 08:48:55 nisimura Exp $ */ +/* + * Copyright (c) 1992 OMRON Corporation. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * OMRON Corporation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)sioreg.h 8.1 (Berkeley) 6/10/93 + */ + +#define WR0 0x00 +#define WR1 0x01 +#define WR2 0x02 +#define WR3 0x03 +#define WR4 0x04 +#define WR5 0x05 +#define WR6 0x06 +#define WR7 0x07 + +#define WR2A WR2 +#define WR2B (WR2|0x10) + +#define RR0 0x08 +#define RR1 0x09 +#define RR2 0x0A +#define RR3 0x0B +#define RR4 0x0C + +#define RR2A RR2 +#define RR2B (RR2|0x10) + +#define WR0_NOP 0x00 /* No Operation */ +#define WR0_SNDABRT 0x08 /* Send Abort (HDLC) */ +#define WR0_RSTINT 0x10 /* Reset External/Status Interrupt */ +#define WR0_CHANRST 0x18 /* Channel Reset */ +#define WR0_INTNXT 0x20 /* Enable Interrupt on Next Receive Character */ +#define WR0_RSTPEND 0x28 /* Reset Transmitter Interrput/DMA Pending */ +#define WR0_ERRRST 0x30 /* Error Reset */ +#define WR0_ENDINTR 0x38 /* End of Interrupt */ + +#define WR1_ESENBL 0x01 /* External/Status Interrupt Enable */ +#define WR1_TXENBL 0x02 /* Tx Interrupt/DMA Enable */ +#define WR1_STATVEC 0x04 /* Status Affects Vector (Only Chan-B) */ +#define WR1_RXDSEBL 0x00 /* Rx Interrupt/DMA Disable */ +#define WR1_RXFIRST 0x08 /* Interrupt only First Character Received */ +#define WR1_RXALLS 0x10 /* Interrupt Every Characters Received (with Special Char.) */ +#define WR1_RXALL 0x18 /* Interrupt Every Characters Received (without Special Char.) */ + +#define WR2_INTR_0 0x00 /* Interrupt Priority: RxA TxA RxB TxB E/SA E/SA */ +#define WR2_INTR_1 0x04 /* Interrupt Priority: RxA RxB TxA TxB E/SA E/SA */ +#define WR2_VEC85_1 0x00 /* 8085 Vectored Mode - 1 */ +#define WR2_VEC85_2 0x08 /* 8085 Vectored Mode - 2 */ +#define WR2_VEC86 0x10 /* 8086 Vectored */ +#define WR2_VEC85_3 0x18 /* 8085 Vectored Mode - 3 */ + +#define WR3_RXENBL 0x01 /* Rx Enable */ +#define WR3_RXCRC 0x08 /* Rx CRC Check */ +#define WR3_AUTOEBL 0x20 /* Auto Enable (flow control for MODEM) */ +#define WR3_RX5BIT 0x00 /* Rx Bits/Character: 5 Bits */ +#define WR3_RX7BIT 0x40 /* Rx Bits/Character: 7 Bits */ +#define WR3_RX6BIT 0x80 /* Rx Bits/Character: 6 Bits */ +#define WR3_RX8BIT 0xc0 /* Rx Bits/Character: 8 Bits */ + +#define WR4_NPARITY 0x00 /* No Parity */ +#define WR4_PARENAB 0x01 /* Parity Enable */ +#define WR4_OPARITY 0x01 /* Parity Odd */ +#define WR4_EPARITY 0x02 /* Parity Even */ +#define WR4_STOP1 0x04 /* Stop Bits (1bit) */ +#define WR4_STOP15 0x08 /* Stop Bits (1.5bit) */ +#define WR4_STOP2 0x0c /* Stop Bits (2bit) */ +#define WR4_BAUD96 0x40 /* Clock Rate (9600 BAUD) */ +#define WR4_BAUD48 0x80 /* Clock Rate (4800 BAUD) */ +#define WR4_BAUD24 0xc0 /* Clock Rate (2400 BAUD) */ + +#define WR5_TXCRC 0x01 /* Tx CRC Check */ +#define WR5_RTS 0x02 /* Request To Send [RTS] */ +#define WR5_TXENBL 0x08 /* Transmit Enable */ +#define WR5_BREAK 0x10 /* Send Break [BRK] */ +#define WR5_TX5BIT 0x00 /* Tx Bits/Character: 5 Bits */ +#define WR5_TX7BIT 0x20 /* Tx Bits/Character: 7 Bits */ +#define WR5_TX6BIT 0x40 /* Tx Bits/Character: 6 Bits */ +#define WR5_TX8BIT 0x60 /* Tx Bits/Character: 8 Bits */ +#define WR5_DTR 0x80 /* Data Terminal Ready [DTR] */ + +#define RR0_RXAVAIL 0x01 /* Rx Character Available */ +#define RR0_INTRPEND 0x02 /* Interrupt Pending (Channel-A Only) */ +#define RR0_TXEMPTY 0x04 /* Tx Buffer Empty */ +#define RR0_DCD 0x08 /* Data Carrier Detect [DCD] */ +#define RR0_SYNC 0x10 /* Synchronization */ +#define RR0_CTS 0x20 /* Clear To Send [CTS] */ +#define RR0_BREAK 0x80 /* Break Detected [BRK] */ + +#define RR1_PARITY 0x10 /* Parity Error */ +#define RR1_OVERRUN 0x20 /* Data Over Run */ +#define RR1_FRAMING 0x40 /* Framing Error */ + +#define RR_RXRDY 0x0100 /* Rx Character Available */ +#define RR_INTRPEND 0x0200 /* Interrupt Pending (Channel-A Only) */ +#define RR_TXRDY 0x0400 /* Tx Buffer Empty */ +#define RR_DCD 0x0800 /* Data Carrier Detect [DCD] */ +#define RR_SYNC 0x1000 /* Synchronization */ +#define RR_CTS 0x2000 /* Clear To Send [CTS] */ +#define RR_BREAK 0x8000 /* Break Detected */ +#define RR_PARITY 0x0010 /* Parity Error */ +#define RR_OVERRUN 0x0020 /* Data Over Run */ +#define RR_FRAMING 0x0040 /* Framing Error */ diff --git a/sys/arch/luna88k/dev/siotty.c b/sys/arch/luna88k/dev/siotty.c new file mode 100644 index 00000000000..bb8f5fabeea --- /dev/null +++ b/sys/arch/luna88k/dev/siotty.c @@ -0,0 +1,664 @@ +/* $OpenBSD: siotty.c,v 1.1 2004/04/21 15:23:55 aoyama Exp $ */ +/* $NetBSD: siotty.c,v 1.9 2002/03/17 19:40:43 atatat Exp $ */ + +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Tohru Nishimura. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/conf.h> +#include <sys/ioctl.h> +#include <sys/proc.h> +#include <sys/user.h> +#include <sys/tty.h> +#include <sys/uio.h> +#include <sys/fcntl.h> +#include <dev/cons.h> + +#include <machine/cpu.h> + +#include <luna88k/dev/sioreg.h> +#include <luna88k/dev/siovar.h> + +#define TIOCM_BREAK 01000 /* non standard use */ + +static const u_int8_t ch0_regs[6] = { + WR0_RSTINT, /* reset E/S interrupt */ + WR1_RXALLS | WR1_TXENBL, /* Rx per char, Tx */ + 0, /* */ + WR3_RX8BIT | WR3_RXENBL, /* Rx */ + WR4_BAUD96 | WR4_STOP1, /* Tx/Rx */ + WR5_TX8BIT | WR5_TXENBL | WR5_DTR | WR5_RTS, /* Tx */ +}; + +static struct speedtab siospeedtab[] = { + { 2400, WR4_BAUD24, }, + { 4800, WR4_BAUD48, }, + { 9600, WR4_BAUD96, }, + { -1, 0, }, +}; + +struct siotty_softc { + struct device sc_dev; + struct tty *sc_tty; + struct sioreg *sc_ctl; + u_int sc_flags; + u_int8_t sc_wr[6]; +}; + +cdev_decl(sio); +void siostart(struct tty *); +int sioparam(struct tty *, struct termios *); +void siottyintr(int); +int siomctl(struct siotty_softc *, int, int); + +int siotty_match(struct device *, void *, void *); +void siotty_attach(struct device *, struct device *, void *); + +const struct cfattach siotty_ca = { + sizeof(struct siotty_softc), siotty_match, siotty_attach +}; + +const struct cfdriver siotty_cd = { + NULL, "siotty", DV_TTY +}; + +int +siotty_match(parent, cf, aux) + struct device *parent; + void *cf, *aux; +{ + struct sio_attach_args *args = aux; + + if (args->channel != 0) /* XXX allow tty on Ch.B XXX */ + return 0; + return 1; +} + +void +siotty_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct sio_softc *scp = (void *)parent; + struct siotty_softc *sc = (void *)self; + struct sio_attach_args *args = aux; + + sc->sc_ctl = (struct sioreg *)scp->scp_ctl + args->channel; + bcopy(ch0_regs, sc->sc_wr, sizeof(ch0_regs)); + scp->scp_intr[args->channel] = siottyintr; + + if (args->hwflags == 1) { + printf(" (console)"); + sc->sc_flags = TIOCFLAG_SOFTCAR; + } + else { + setsioreg(sc->sc_ctl, WR0, WR0_CHANRST); + setsioreg(sc->sc_ctl, WR2A, WR2_VEC86 | WR2_INTR_1); + setsioreg(sc->sc_ctl, WR2B, 0); + setsioreg(sc->sc_ctl, WR0, sc->sc_wr[WR0]); + setsioreg(sc->sc_ctl, WR4, sc->sc_wr[WR4]); + setsioreg(sc->sc_ctl, WR3, sc->sc_wr[WR3]); + setsioreg(sc->sc_ctl, WR5, sc->sc_wr[WR5]); + setsioreg(sc->sc_ctl, WR0, sc->sc_wr[WR0]); + } + setsioreg(sc->sc_ctl, WR1, sc->sc_wr[WR1]); /* now interrupt driven */ + + printf("\n"); +} + +/*-------------------- low level routine --------------------*/ + +void +siottyintr(chan) + int chan; +{ + struct siotty_softc *sc; + struct sioreg *sio; + struct tty *tp; + unsigned int code; + int rr; + + if (chan >= siotty_cd.cd_ndevs) + return; + sc = siotty_cd.cd_devs[chan]; + tp = sc->sc_tty; + sio = sc->sc_ctl; + rr = getsiocsr(sio); + if (rr & RR_RXRDY) { + do { + code = sio->sio_data; + if (rr & (RR_FRAMING | RR_OVERRUN | RR_PARITY)) { + sio->sio_cmd = WR0_ERRRST; + if (sio->sio_stat & RR_FRAMING) + code |= TTY_FE; + else if (sio->sio_stat & RR_PARITY) + code |= TTY_PE; + } + if (tp == NULL || (tp->t_state & TS_ISOPEN) == 0) + continue; +#if 0 && defined(DDB) /* ?!?! fails to resume ?!?! */ + if ((rr & RR_BREAK) && tp->t_dev == cn_tab->cn_dev) { + if (db_console) + Debugger(); + return; + } +#endif +/* + (*tp->t_linesw->l_rint)(code, tp); +*/ + (*linesw[tp->t_line].l_rint)(code, tp); + } while ((rr = getsiocsr(sio)) & RR_RXRDY); + } + if (rr & RR_TXRDY) { + sio->sio_cmd = WR0_RSTPEND; + if (tp != NULL) { + tp->t_state &= ~(TS_BUSY|TS_FLUSH); +/* + (*tp->t_linesw->l_start)(tp); +*/ + (*linesw[tp->t_line].l_start)(tp); + } + } +} + +void +siostart(tp) + struct tty *tp; +{ + struct siotty_softc *sc = siotty_cd.cd_devs[minor(tp->t_dev)]; + int s, c; + + s = spltty(); + if (tp->t_state & (TS_BUSY|TS_TIMEOUT|TS_TTSTOP)) + goto out; + if (tp->t_outq.c_cc <= tp->t_lowat) { + if (tp->t_state & TS_ASLEEP) { + tp->t_state &= ~TS_ASLEEP; + wakeup((caddr_t)&tp->t_outq); + } + selwakeup(&tp->t_wsel); + } + if (tp->t_outq.c_cc == 0) + goto out; + + tp->t_state |= TS_BUSY; + while (getsiocsr(sc->sc_ctl) & RR_TXRDY) { + if ((c = getc(&tp->t_outq)) == -1) + break; + sc->sc_ctl->sio_data = c; + } +out: + splx(s); +} + +int +siostop(tp, flag) + struct tty *tp; + int flag; +{ + int s; + + s = spltty(); + if (TS_BUSY == (tp->t_state & (TS_BUSY|TS_TTSTOP))) { + /* + * Device is transmitting; must stop it. + */ + tp->t_state |= TS_FLUSH; + } + splx(s); + return (0); +} + +int +sioparam(tp, t) + struct tty *tp; + struct termios *t; +{ + struct siotty_softc *sc = siotty_cd.cd_devs[minor(tp->t_dev)]; + int wr4, s; + + if (t->c_ispeed && t->c_ispeed != t->c_ospeed) + return EINVAL; + wr4 = ttspeedtab(t->c_ospeed, siospeedtab); + if (wr4 < 0) + return EINVAL; + + if (sc->sc_flags & TIOCFLAG_SOFTCAR) { + t->c_cflag |= CLOCAL; + t->c_cflag &= ~HUPCL; + } + if (sc->sc_flags & TIOCFLAG_CLOCAL) + t->c_cflag |= CLOCAL; + + /* + * If there were no changes, don't do anything. This avoids dropping + * input and improves performance when all we did was frob things like + * VMIN and VTIME. + */ + if (tp->t_ospeed == t->c_ospeed && tp->t_cflag == t->c_cflag) + return 0; + + tp->t_ispeed = t->c_ispeed; + tp->t_ospeed = t->c_ospeed; + tp->t_cflag = t->c_cflag; + + sc->sc_wr[WR3] &= 0x3f; + sc->sc_wr[WR5] &= 0x9f; + switch (tp->t_cflag & CSIZE) { + case CS7: + sc->sc_wr[WR3] |= WR3_RX7BIT; sc->sc_wr[WR5] |= WR5_TX7BIT; + break; + case CS8: + sc->sc_wr[WR3] |= WR3_RX8BIT; sc->sc_wr[WR5] |= WR5_TX8BIT; + break; + } + if (tp->t_cflag & PARENB) { + wr4 |= WR4_PARENAB; + if ((tp->t_cflag & PARODD) == 0) + wr4 |= WR4_EPARITY; + } + wr4 |= (tp->t_cflag & CSTOPB) ? WR4_STOP2 : WR4_STOP1; + sc->sc_wr[WR4] = wr4; + + s = spltty(); + setsioreg(sc->sc_ctl, WR4, sc->sc_wr[WR4]); + setsioreg(sc->sc_ctl, WR3, sc->sc_wr[WR3]); + setsioreg(sc->sc_ctl, WR5, sc->sc_wr[WR5]); + splx(s); + + return 0; +} + +int +siomctl(sc, control, op) + struct siotty_softc *sc; + int control, op; +{ + int val, s, wr5, rr; + + val = 0; + if (control & TIOCM_BREAK) + val |= WR5_BREAK; + if (control & TIOCM_DTR) + val |= WR5_DTR; + if (control & TIOCM_RTS) + val |= WR5_RTS; + s = spltty(); + wr5 = sc->sc_wr[WR5]; + switch (op) { + case DMSET: + wr5 &= ~(WR5_BREAK|WR5_DTR|WR5_RTS); + /* FALLTHRU */ + case DMBIS: + wr5 |= val; + break; + case DMBIC: + wr5 &= ~val; + break; + case DMGET: + val = 0; + rr = getsiocsr(sc->sc_ctl); + if (wr5 & WR5_DTR) + val |= TIOCM_DTR; + if (wr5 & WR5_RTS) + val |= TIOCM_RTS; + if (rr & RR_CTS) + val |= TIOCM_CTS; + if (rr & RR_DCD) + val |= TIOCM_CD; + goto done; + } + sc->sc_wr[WR5] = wr5; + setsioreg(sc->sc_ctl, WR5, wr5); + val = 0; + done: + splx(s); + return val; +} + +/*-------------------- cdevsw[] interface --------------------*/ + +int +sioopen(dev, flag, mode, p) + dev_t dev; + int flag, mode; + struct proc *p; +{ + struct siotty_softc *sc; + struct tty *tp; + int error; + + if ((sc = siotty_cd.cd_devs[minor(dev)]) == NULL) + return ENXIO; + if ((tp = sc->sc_tty) == NULL) { + tp = sc->sc_tty = ttymalloc(); + } + else if ((tp->t_state & TS_ISOPEN) && (tp->t_state & TS_XCLUDE) + && p->p_ucred->cr_uid != 0) + return EBUSY; + + tp->t_oproc = siostart; + tp->t_param = sioparam; + tp->t_hwiflow = NULL /* XXX siohwiflow XXX */; + tp->t_dev = dev; + if ((tp->t_state & TS_ISOPEN) == 0) { + struct termios t; + + t.c_ispeed = t.c_ospeed = TTYDEF_SPEED; + t.c_cflag = TTYDEF_CFLAG; + tp->t_ospeed = 0; /* force register update */ + (void)sioparam(tp, &t); + tp->t_iflag = TTYDEF_IFLAG; + tp->t_oflag = TTYDEF_OFLAG; + tp->t_lflag = TTYDEF_LFLAG; + ttychars(tp); + ttsetwater(tp); + /* raise RTS and DTR here; but, DTR lead is not wired */ + /* then check DCD condition; but, DCD lead is not wired */ + tp->t_state |= TS_CARR_ON; /* assume detected all the time */ +#if 0 + if ((sc->sc_flags & TIOCFLAG_SOFTCAR) + || (tp->t_cflag & MDMBUF) + || (getsiocsr(sc->sc_ctl) & RR_DCD)) + tp->t_state |= TS_CARR_ON; + else + tp->t_state &= ~TS_CARR_ON; +#endif + } + + error = ttyopen(dev, tp); + if (error > 0) + return error; +/* + return (*tp->t_linesw->l_open)(dev, tp); +*/ + return (*linesw[tp->t_line].l_open)(dev, tp); +} + +int +sioclose(dev, flag, mode, p) + dev_t dev; + int flag, mode; + struct proc *p; +{ + struct siotty_softc *sc = siotty_cd.cd_devs[minor(dev)]; + struct tty *tp = sc->sc_tty; + int s; + +/* + (*tp->t_linesw->l_close)(tp, flag); +*/ + (*linesw[tp->t_line].l_close)(tp, flag); + + s = spltty(); + siomctl(sc, TIOCM_BREAK, DMBIC); +#if 0 /* because unable to feed DTR signal */ + if ((tp->t_cflag & HUPCL) + || tp->t_wopen || (tp->t_state & TS_ISOPEN) == 0) { + siomctl(sc, TIOCM_DTR, DMBIC); + /* Yield CPU time to others for 1 second, then ... */ + siomctl(sc, TIOCM_DTR, DMBIS); + } +#endif + splx(s); + return ttyclose(tp); +} + +int +sioread(dev, uio, flag) + dev_t dev; + struct uio *uio; + int flag; +{ + struct siotty_softc *sc = siotty_cd.cd_devs[minor(dev)]; + struct tty *tp = sc->sc_tty; + +/* + return (*tp->t_linesw->l_read)(tp, uio, flag); +*/ + return (*linesw[tp->t_line].l_read)(tp, uio, flag); +} + +int +siowrite(dev, uio, flag) + dev_t dev; + struct uio *uio; + int flag; +{ + struct siotty_softc *sc = siotty_cd.cd_devs[minor(dev)]; + struct tty *tp = sc->sc_tty; + +/* + return (*tp->t_linesw->l_write)(tp, uio, flag); +*/ + return (*linesw[tp->t_line].l_write)(tp, uio, flag); +} + +#if 0 +int +sioselect(dev, events, p) + dev_t dev; + int events; + struct proc *p; +{ + struct siotty_softc *sc = siotty_cd.cd_devs[minor(dev)]; + struct tty *tp = sc->sc_tty; + +/* + return ((*tp->t_linesw->l_poll)(tp, events, p)); +*/ + return ((*linesw[tp->t_line].l_select)(tp, events, p)); + +} +#endif + +int +sioioctl(dev, cmd, data, flag, p) + dev_t dev; + u_long cmd; + caddr_t data; + int flag; + struct proc *p; +{ + struct siotty_softc *sc = siotty_cd.cd_devs[minor(dev)]; + struct tty *tp = sc->sc_tty; + int error; + +/* + error = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, p); +*/ + error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); + if (error >= 0) + return error; + + error = ttioctl(tp, cmd, data, flag, p); + if (error >= 0) + return error; + + /* the last resort for TIOC ioctl tranversing */ + switch (cmd) { + case TIOCSBRK: /* Set the hardware into BREAK condition */ + siomctl(sc, TIOCM_BREAK, DMBIS); + break; + case TIOCCBRK: /* Clear the hardware BREAK condition */ + siomctl(sc, TIOCM_BREAK, DMBIC); + break; + case TIOCSDTR: /* Assert DTR signal */ + siomctl(sc, TIOCM_DTR|TIOCM_RTS, DMBIS); + break; + case TIOCCDTR: /* Clear DTR signal */ + siomctl(sc, TIOCM_DTR|TIOCM_RTS, DMBIC); + break; + case TIOCMSET: /* Set modem state replacing current one */ + siomctl(sc, *(int *)data, DMSET); + break; + case TIOCMGET: /* Return current modem state */ + *(int *)data = siomctl(sc, 0, DMGET); + break; + case TIOCMBIS: /* Set individual bits of modem state */ + siomctl(sc, *(int *)data, DMBIS); + break; + case TIOCMBIC: /* Clear individual bits of modem state */ + siomctl(sc, *(int *)data, DMBIC); + break; + case TIOCSFLAGS: /* Instruct how serial port behaves */ + sc->sc_flags = *(int *)data; + break; + case TIOCGFLAGS: /* Return current serial port state */ + *(int *)data = sc->sc_flags; + break; + default: +/* + return EPASSTHROUGH; +*/ + return ENOTTY; + } + return 0; +} + +/* ARSGUSED */ +struct tty * +siotty(dev) + dev_t dev; +{ + struct siotty_softc *sc = siotty_cd.cd_devs[minor(dev)]; + + return sc->sc_tty; +} + +/*-------------------- miscellaneous routines --------------------*/ + +/* EXPORT */ void +setsioreg(sio, regno, val) + struct sioreg *sio; + int regno, val; +{ + if (regno != 0) + sio->sio_cmd = regno; /* DELAY(); */ + sio->sio_cmd = val; /* DELAY(); */ +} + +/* EXPORT */ int +getsiocsr(sio) + struct sioreg *sio; +{ + int val; + + val = sio->sio_stat << 8; /* DELAY(); */ + sio->sio_cmd = 1; /* DELAY(); */ + val |= sio->sio_stat; /* DELAY(); */ + return val; +} + +/*--------------------- console interface ----------------------*/ + +void syscnattach(int); +int syscngetc(dev_t); +void syscnputc(dev_t, int); + +struct consdev syscons = { + NULL, + NULL, + syscngetc, + syscnputc, + nullcnpollc, + NULL, + NODEV, + CN_REMOTE, +}; + +/* EXPORT */ void +syscnattach(channel) + int channel; +{ +/* + * Channel A is immediately initialized with 9600N1 right after cold + * boot/reset/poweron. ROM monitor emits one line message on CH.A. + */ + struct sioreg *sio; + sio = (struct sioreg *)0x51000000 + channel; + +/* syscons.cn_dev = makedev(7, channel); */ + syscons.cn_dev = makedev(12, channel); + cn_tab = &syscons; + +#if 0 + setsioreg(sio, WR0, WR0_CHANRST); + setsioreg(sio, WR2A, WR2_VEC86 | WR2_INTR_1); + setsioreg(sio, WR2B, 0); + setsioreg(sio, WR0, ch0_regs[WR0]); + setsioreg(sio, WR4, ch0_regs[WR4]); + setsioreg(sio, WR3, ch0_regs[WR3]); + setsioreg(sio, WR5, ch0_regs[WR5]); + setsioreg(sio, WR0, ch0_regs[WR0]); +#endif +} + +/* EXPORT */ int +syscngetc(dev) + dev_t dev; +{ + struct sioreg *sio; + int s, c; + + sio = (struct sioreg *)0x51000000 + ((int)dev & 0x1); + s = splhigh(); + while ((getsiocsr(sio) & RR_RXRDY) == 0) + ; + c = sio->sio_data; + splx(s); + + return c; +} + +/* EXPORT */ void +syscnputc(dev, c) + dev_t dev; + int c; +{ + struct sioreg *sio; + int s; + + sio = (struct sioreg *)0x51000000 + ((int)dev & 0x1); + s = splhigh(); + while ((getsiocsr(sio) & RR_TXRDY) == 0) + ; + sio->sio_cmd = WR0_RSTPEND; + sio->sio_data = c; + splx(s); +} diff --git a/sys/arch/luna88k/dev/siovar.h b/sys/arch/luna88k/dev/siovar.h new file mode 100644 index 00000000000..442b295a243 --- /dev/null +++ b/sys/arch/luna88k/dev/siovar.h @@ -0,0 +1,60 @@ +/* $OpenBSD: siovar.h,v 1.1 2004/04/21 15:23:55 aoyama Exp $ */ +/* $NetBSD: siovar.h,v 1.1 2000/01/05 08:48:55 nisimura Exp $ */ + +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Tohru Nishimura. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +struct sio_softc { + struct device scp_dev; + caddr_t scp_ctl; + void (*scp_intr[2])(int); +}; + +struct sio_attach_args { + int channel; + int hwflags; +}; + +struct sioreg { + volatile u_int8_t sio_data; + volatile unsigned : 24; + volatile u_int8_t sio_cmd; + volatile unsigned : 24; +#define sio_stat sio_cmd +}; + +int getsiocsr(struct sioreg *); +void setsioreg(struct sioreg *, int, int); diff --git a/sys/arch/luna88k/dev/spc.c b/sys/arch/luna88k/dev/spc.c new file mode 100644 index 00000000000..bbf1044de9d --- /dev/null +++ b/sys/arch/luna88k/dev/spc.c @@ -0,0 +1,111 @@ +/* $OpenBSD: spc.c,v 1.1 2004/04/21 15:23:55 aoyama Exp $ */ +/* $NetBSD: spc.c,v 1.4 2003/07/05 19:00:17 tsutsui Exp $ */ + +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Tohru Nishimura. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> + +#include <machine/bus.h> +#include <machine/cpu.h> +#include <machine/autoconf.h> + +#include <scsi/scsi_all.h> +#include <scsi/scsi_message.h> +#include <scsi/scsiconf.h> + +#include <luna88k/dev/mb89352reg.h> +#include <luna88k/dev/mb89352var.h> + +#include <luna88k/luna88k/isr.h> + +int spc_mainbus_match(struct device *, void *, void *); +void spc_mainbus_attach(struct device *, struct device *, void *); + +struct cfattach spc_ca = { + sizeof(struct spc_softc), spc_mainbus_match, spc_mainbus_attach +}; + +struct cfdriver spc_cd = { + NULL, "spc", DV_DULL +}; + +struct scsi_adapter spc_switch = { + spc_scsi_cmd, + spc_minphys, /* no max at this level; handled by DMA code */ + NULL, + NULL, +}; + +int +spc_mainbus_match(parent, cf, aux) + struct device *parent; + void *cf, *aux; +{ + struct mainbus_attach_args *ma = aux; + + if (strcmp(ma->ma_name, spc_cd.cd_name)) + return 0; +#if 0 + if (badaddr((caddr_t)ma->ma_addr, 4)) + return 0; + /* Experiments proved 2nd SPC address does NOT make a buserror. */ +#endif + return 1; +} + +void +spc_mainbus_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct spc_softc *sc = (void *)self; + struct mainbus_attach_args *ma = aux; + + printf ("\n"); + + sc->sc_iot = LUNA88K_BUS_SPACE_MEM; + sc->sc_ioh = ma->ma_addr; + sc->sc_initiator = 7; + sc->sc_dma_start = NULL; + sc->sc_dma_done = NULL; + + isrlink_autovec(spc_intr, (void *)sc, ma->ma_ilvl, ISRPRI_BIO); + + spc_attach(sc, &spc_switch); +} diff --git a/sys/arch/luna88k/dev/timekeeper.c b/sys/arch/luna88k/dev/timekeeper.c new file mode 100644 index 00000000000..b154c1fd104 --- /dev/null +++ b/sys/arch/luna88k/dev/timekeeper.c @@ -0,0 +1,303 @@ +/* $OpenBSD: timekeeper.c,v 1.1 2004/04/21 15:23:56 aoyama Exp $ */ +/* $NetBSD: timekeeper.c,v 1.1 2000/01/05 08:48:56 nisimura Exp $ */ + +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Tohru Nishimura. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/kernel.h> + +#include <machine/board.h> /* machtype value */ +#include <machine/cpu.h> + +#include <dev/clock_subr.h> +#include <luna88k/luna88k/clockvar.h> +#include <luna88k/dev/timekeeper.h> +#include <machine/autoconf.h> + +#define MK_YEAR0 1970 /* year offset of MK*/ +#define DS_YEAR0 1990 /* year offset of DS*/ + +struct timekeeper_softc { + struct device sc_dev; + void *sc_clock, *sc_nvram; + int sc_nvramsize; + u_int8_t sc_image[2040]; +}; + +/* + * BCD to decimal and decimal to BCD. + */ +#define FROMBCD(x) (((x) >> 4) * 10 + ((x) & 0xf)) +#define TOBCD(x) (((x) / 10 * 16) + ((x) % 10)) + +int clock_match(struct device *, void *, void *); +void clock_attach(struct device *, struct device *, void *); + +struct cfattach clock_ca = { + sizeof (struct timekeeper_softc), clock_match, clock_attach +}; + +struct cfdriver clock_cd = { + NULL, "clock", DV_DULL +}; + +void mkclock_get(struct device *, time_t, struct clock_ymdhms *); +void mkclock_set(struct device *, struct clock_ymdhms *); +void dsclock_get(struct device *, time_t, struct clock_ymdhms *); +void dsclock_set(struct device *, struct clock_ymdhms *); + +const struct clockfns mkclock_clockfns = { + NULL /* never used */, mkclock_get, mkclock_set, +}; + +const struct clockfns dsclock_clockfns = { + NULL /* never used */, dsclock_get, dsclock_set, +}; + +int +clock_match(parent, match, aux) + struct device *parent; + void *match, *aux; +{ + struct mainbus_attach_args *ma = aux; + + if (strcmp(ma->ma_name, clock_cd.cd_name)) + return 0; + return 1; +} + +extern int machtype; /* in machdep.c */ + +void +clock_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct timekeeper_softc *sc = (void *)self; + struct mainbus_attach_args *ma = aux; + const struct clockfns *clockwork; + + switch (machtype) { + default: + case LUNA_88K: /* Mostek MK48T02 */ + sc->sc_clock = (void *)(ma->ma_addr + 2040); + sc->sc_nvram = (void *)ma->ma_addr; + sc->sc_nvramsize = 2040; + clockwork = &mkclock_clockfns; + printf(": MK48T02\n"); + break; + case LUNA_88K2: /* Dallas DS1397 */ + sc->sc_clock = (void *)ma->ma_addr; + sc->sc_nvram = (void *)(ma->ma_addr + 50); + sc->sc_nvramsize = 50; + clockwork = &dsclock_clockfns; + printf(": DS1397\n"); + break; + } + clockattach(&sc->sc_dev, clockwork); + memcpy(sc->sc_image, sc->sc_nvram, sc->sc_nvramsize); +} + +/* + * Get the time of day, based on the clock's value and/or the base value. + */ +void +mkclock_get(dev, base, dt) + struct device *dev; + time_t base; + struct clock_ymdhms *dt; +{ + struct timekeeper_softc *sc = (void *)dev; + volatile u_int8_t *chiptime = (void *)sc->sc_clock; + int s; + + s = splclock(); + chiptime[MK_CSR] |= MK_CSR_READ; /* enable read (stop time) */ + dt->dt_sec = FROMBCD(chiptime[MK_SEC]); + dt->dt_min = FROMBCD(chiptime[MK_MIN]); + dt->dt_hour = FROMBCD(chiptime[MK_HOUR]); + dt->dt_wday = FROMBCD(chiptime[MK_DOW]); + dt->dt_day = FROMBCD(chiptime[MK_DOM]); + dt->dt_mon = FROMBCD(chiptime[MK_MONTH]); + dt->dt_year = FROMBCD(chiptime[MK_YEAR]) + MK_YEAR0; + chiptime[MK_CSR] &= ~MK_CSR_READ; /* time wears on */ + splx(s); +#ifdef TIMEKEEPER_DEBUG + printf("get %d/%d/%d %d:%d:%d\n", + dt->dt_year, dt->dt_mon, dt->dt_day, + dt->dt_hour, dt->dt_min, dt->dt_sec); +#endif +} + +/* + * Reset the TODR based on the time value. + */ +void +mkclock_set(dev, dt) + struct device *dev; + struct clock_ymdhms *dt; +{ + struct timekeeper_softc *sc = (void *)dev; + volatile u_int8_t *chiptime = (void *)sc->sc_clock; + volatile u_int8_t *stamp = (u_int8_t *)sc->sc_nvram + 0x10; + int s; + + s = splclock(); + chiptime[MK_CSR] |= MK_CSR_WRITE; /* enable write */ + chiptime[MK_SEC] = TOBCD(dt->dt_sec); + chiptime[MK_MIN] = TOBCD(dt->dt_min); + chiptime[MK_HOUR] = TOBCD(dt->dt_hour); + chiptime[MK_DOW] = TOBCD(dt->dt_wday); + chiptime[MK_DOM] = TOBCD(dt->dt_day); + chiptime[MK_MONTH] = TOBCD(dt->dt_mon); + chiptime[MK_YEAR] = TOBCD(dt->dt_year - MK_YEAR0); + chiptime[MK_CSR] &= ~MK_CSR_WRITE; /* load them up */ + splx(s); +#ifdef TIMEKEEPER_DEBUG + printf("set %d/%d/%d %d:%d:%d\n", + dt->dt_year, dt->dt_mon, dt->dt_day, + dt->dt_hour, dt->dt_min, dt->dt_sec); +#endif + + stamp[0] = 'R'; stamp[1] = 'T'; stamp[2] = 'C'; stamp[3] = '\0'; +} + +#define _DS_GET(off, data) \ + do { *chiptime = (off); (u_int8_t)(data) = (*chipdata); } while (0) +#define _DS_SET(off, data) \ + do { *chiptime = (off); *chipdata = (u_int8_t)(data); } while (0) +#define _DS_GET_BCD(off, data) \ + do { \ + u_int8_t c; \ + *chiptime = (off); \ + c = *chipdata; (u_int8_t)(data) = FROMBCD(c); \ + } while (0) +#define _DS_SET_BCD(off, data) \ + do { \ + *chiptime = (off); \ + *chipdata = TOBCD((u_int8_t)(data)); \ + } while (0) + +/* + * Get the time of day, based on the clock's value and/or the base value. + */ +void +dsclock_get(dev, base, dt) + struct device *dev; + time_t base; + struct clock_ymdhms *dt; +{ + struct timekeeper_softc *sc = (void *)dev; + volatile u_int8_t *chiptime = (void *)sc->sc_clock; + volatile u_int8_t *chipdata = (void *)(sc->sc_clock + 1); + int s; + u_int8_t c; + + s = splclock(); + + /* specify 24hr and BCD mode */ + _DS_GET(DS_REGB, c); + c |= DS_REGB_24HR; + c &= ~DS_REGB_BINARY; + _DS_SET(DS_REGB, c); + + /* update in progress; spin loop */ + *chiptime = DS_REGA; + while (*chipdata & DS_REGA_UIP) + ; + + _DS_GET_BCD(DS_SEC, dt->dt_sec); + _DS_GET_BCD(DS_MIN, dt->dt_min); + _DS_GET_BCD(DS_HOUR, dt->dt_hour); + _DS_GET_BCD(DS_DOW, dt->dt_wday); + _DS_GET_BCD(DS_DOM, dt->dt_day); + _DS_GET_BCD(DS_MONTH, dt->dt_mon); + _DS_GET_BCD(DS_YEAR, dt->dt_year); + dt->dt_year += DS_YEAR0; + + splx(s); + +#ifdef TIMEKEEPER_DEBUG + printf("get %d/%d/%d %d:%d:%d\n", + dt->dt_year, dt->dt_mon, dt->dt_day, + dt->dt_hour, dt->dt_min, dt->dt_sec); +#endif +} + +/* + * Reset the TODR based on the time value. + */ +void +dsclock_set(dev, dt) + struct device *dev; + struct clock_ymdhms *dt; +{ + struct timekeeper_softc *sc = (void *)dev; + volatile u_int8_t *chiptime = (void *)sc->sc_clock; + volatile u_int8_t *chipdata = (void *)(sc->sc_clock + 1); + int s; + u_int8_t c; + + s = splclock(); + + /* enable write */ + _DS_GET(DS_REGB, c); + c |= DS_REGB_SET; + _DS_SET(DS_REGB, c); + + _DS_SET_BCD(DS_SEC, dt->dt_sec); + _DS_SET_BCD(DS_MIN, dt->dt_min); + _DS_SET_BCD(DS_HOUR, dt->dt_hour); + _DS_SET_BCD(DS_DOW, dt->dt_wday); + _DS_SET_BCD(DS_DOM, dt->dt_day); + _DS_SET_BCD(DS_MONTH, dt->dt_mon); + _DS_SET_BCD(DS_YEAR, dt->dt_year - DS_YEAR0); + + _DS_GET(DS_REGB, c); + c &= ~DS_REGB_SET; + _DS_SET(DS_REGB, c); + + splx(s); + +#ifdef TIMEKEEPER_DEBUG + printf("set %d/%d/%d %d:%d:%d\n", + dt->dt_year, dt->dt_mon, dt->dt_day, + dt->dt_hour, dt->dt_min, dt->dt_sec); +#endif +} diff --git a/sys/arch/luna88k/dev/timekeeper.h b/sys/arch/luna88k/dev/timekeeper.h new file mode 100644 index 00000000000..d25f15edd0b --- /dev/null +++ b/sys/arch/luna88k/dev/timekeeper.h @@ -0,0 +1,93 @@ +/* $OpenBSD: timekeeper.h,v 1.1 2004/04/21 15:23:56 aoyama Exp $ */ +/* $NetBSD: timekeeper.h,v 1.1 2000/01/05 08:48:56 nisimura Exp $ */ + +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Tohru Nishimura. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Mostek MK48T02 for LUNA-88K + */ +#define MK_CSR 0 /* control register */ +#define MK_SEC 1 /* seconds (0..59; BCD) */ +#define MK_MIN 2 /* minutes (0..59; BCD) */ +#define MK_HOUR 3 /* hour (0..23; BCD) */ +#define MK_DOW 4 /* weekday (1..7) */ +#define MK_DOM 5 /* day in month (1..31; BCD) */ +#define MK_MONTH 6 /* month (1..12; BCD) */ +#define MK_YEAR 7 /* year (0..99; BCD) */ +/* bits in cl_csr */ +#define MK_CSR_WRITE 0x80 /* want to write */ +#define MK_CSR_READ 0x40 /* want to read (freeze clock) */ + +/* + * Dallas Semiconductor DS1397 for LUNA-88K2 + */ +#define DS_SEC 0x0 /* Time of year: seconds (0-59) */ +#define DS_MIN 0x2 /* Time of year: minutes (0-59) */ +#define DS_HOUR 0x4 /* Time of year: hour (see above) */ +#define DS_DOW 0x6 /* Time of year: day of week (1-7) */ +#define DS_DOM 0x7 /* Time of year: day of month (1-31) */ +#define DS_MONTH 0x8 /* Time of year: month (1-12) */ +#define DS_YEAR 0x9 /* Time of year: year in century (0-99) */ + +#define DS_REGA 0xa /* Control register A */ +#define DS_REGA_RSMASK 0x0f /* Interrupt rate select mask (see below) */ +#define DS_REGA_DVMASK 0x70 /* Divisor select mask (see below) */ +#define DS_REGA_UIP 0x80 /* Update in progress; read only. */ + +#define DS_REGB 0xb /* Control register B */ +#define DS_REGB_DSE 0x01 /* Daylight Savings Enable */ +#define DS_REGB_24HR 0x02 /* 24-hour mode (AM/PM mode when clear) */ +#define DS_REGB_BINARY 0x04 /* Binary mode (BCD mode when clear) */ +#define DS_REGB_SQWE 0x08 /* Square Wave Enable */ +#define DS_REGB_UIE 0x10 /* Update End interrupt enable */ +#define DS_REGB_AIE 0x20 /* Alarm interrupt enable */ +#define DS_REGB_PIE 0x40 /* Periodic interrupt enable */ +#define DS_REGB_SET 0x80 /* Allow time to be set; stops updates */ + +#define DS_REGC 0xc /* Control register C */ +/* DS_REGC_UNUSED 0x0f UNUSED */ +#define DS_REGC_UF 0x10 /* Update End interrupt flag */ +#define DS_REGC_AF 0x20 /* Alarm interrupt flag */ +#define DS_REGC_PF 0x40 /* Periodic interrupt flag */ +#define DS_REGC_IRQF 0x80 /* Interrupt request pending flag */ + +#define DS_REGD 0xd /* Control register D */ +/* DS_REGD_UNUSED 0x7f UNUSED */ +#define DS_REGD_VRT 0x80 /* Valid RAM and Time bit */ + +#define DS_NREGS 0xe /* 14 registers; CMOS follows */ +#define DS_NTODREGS 0xa /* 10 of those regs are for TOD and alarm */ diff --git a/sys/arch/luna88k/include/ansi.h b/sys/arch/luna88k/include/ansi.h new file mode 100644 index 00000000000..f720f0420e4 --- /dev/null +++ b/sys/arch/luna88k/include/ansi.h @@ -0,0 +1,83 @@ +/* $OpenBSD: ansi.h,v 1.1 2004/04/21 15:23:56 aoyama Exp $ */ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)ansi.h 8.2 (Berkeley) 1/4/94 + */ + +#ifndef __MACHINE_ANSI_H__ +#define __MACHINE_ANSI_H__ + +/* + * Types which are fundamental to the implementation and may appear in + * more than one standard header are defined here. Standard headers + * then use: + * #ifdef _BSD_SIZE_T_ + * typedef _BSD_SIZE_T_ size_t; + * #undef _BSD_SIZE_T_ + * #endif + */ +#define _BSD_CLOCK_T_ unsigned long /* clock() */ +#define _BSD_PTRDIFF_T_ int /* ptr1 - ptr2 */ +#define _BSD_SIZE_T_ unsigned int /* sizeof() */ +#define _BSD_SSIZE_T_ int /* byte count or error */ +#define _BSD_TIME_T_ int /* time() */ +struct __va_list_tag; +#if defined(__GNUC__) && __GNUC__ >= 3 +#define _BSD_VA_LIST_ __builtin_va_list +#else +#define _BSD_VA_LIST_ struct __va_list_tag * /* va_list */ +#endif +#define _BSD_CLOCKID_T_ int +#define _BSD_TIMER_T_ int + +/* + * Runes (wchar_t) is declared to be an ``int'' instead of the more natural + * ``unsigned long'' or ``long''. Two things are happening here. It is not + * unsigned so that EOF (-1) can be naturally assigned to it and used. Also, + * it looks like 10646 will be a 31 bit standard. This means that if your + * ints cannot hold 32 bits, you will be in trouble. The reason an int was + * chosen over a long is that the is*() and to*() routines take ints (says + * ANSI C), but they use _RUNE_T_ instead of int. By changing it here, you + * lose a bit of ANSI conformance, but your programs will still work. + * + * Note that _WCHAR_T_ and _RUNE_T_ must be of the same type. When wchar_t + * and rune_t are typedef'd, _WCHAR_T_ will be undef'd, but _RUNE_T remains + * defined for ctype.h. + */ +#define _BSD_WCHAR_T_ int /* wchar_t */ +#define _BSD_WINT_T_ int /* wint_t */ +#define _BSD_RUNE_T_ int /* rune_t */ + +/* + * We describe off_t here so its declaration can be visible to + * stdio without pulling in all of <sys/type.h>, thus appeasing ANSI. + */ +#define _BSD_OFF_T_ long long /* file offset */ + +#endif /* __MACHINE_ANSI_H__ */ diff --git a/sys/arch/luna88k/include/asm.h b/sys/arch/luna88k/include/asm.h new file mode 100644 index 00000000000..37e414359c7 --- /dev/null +++ b/sys/arch/luna88k/include/asm.h @@ -0,0 +1,310 @@ +/* $OpenBSD: asm.h,v 1.1 2004/04/21 15:23:56 aoyama Exp $ */ + +/* + * Mach Operating System + * Copyright (c) 1993-1992 Carnegie Mellon University + * Copyright (c) 1991 OMRON Corporation + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON AND OMRON ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON AND OMRON DISCLAIM ANY LIABILITY OF ANY KIND + * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie the + * rights to redistribute these changes. + */ + +#ifndef __MACHINE_M88K_ASM_H__ +#define __MACHINE_M88K_ASM_H__ + +#ifdef __STDC__ +#define _C_LABEL(name) _ ## name +#else +#define _C_LABEL(name) _/**/name +#endif + +#define _ASM_LABEL(name) name + +#define _ENTRY(name) \ + .text; .align 8; .globl name; name: + +#define ENTRY(name) _ENTRY(_C_LABEL(name)) +#define ASENTRY(name) _ENTRY(_ASM_LABEL(name)) + +#define GLOBAL(name) \ + .globl _C_LABEL(name); _C_LABEL(name): + +#define ASGLOBAL(name) \ + .globl _ASM_LABEL(name); _ASM_LABEL(name): + +#define LOCAL(name) \ + _C_LABEL(name): + +#define ASLOCAL(name) \ + _ASM_LABEL(name): + +#define BSS(name, size) \ + .comm _C_LABEL(name), size + +#define ASBSS(name, size) \ + .comm _ASM_LABEL(name), size + +#ifdef __ELF__ +#define WEAK_ALIAS(alias,sym) \ + .weak alias; \ + alias = sym +#else +#ifdef __STDC__ +#define WEAK_ALIAS(alias,sym) \ + .weak _##alias; \ + _##alias = _##sym +#else +#define WEAK_ALIAS(alias,sym) \ + .weak _/**/alias; \ + _/**/alias = _/**/sym +#endif +#endif + +#ifdef _KERNEL + +/* + * Control register symbolic names + */ + +#define PID cr0 +#define PSR cr1 +#define EPSR cr2 +#define SSBR cr3 +#define SXIP cr4 +#define SNIP cr5 +#define SFIP cr6 +#define VBR cr7 +#define DMT0 cr8 +#define DMD0 cr9 +#define DMA0 cr10 +#define DMT1 cr11 +#define DMD1 cr12 +#define DMA1 cr13 +#define DMT2 cr14 +#define DMD2 cr15 +#define DMA2 cr16 +#define SR0 cr17 +#define SR1 cr18 +#define SR2 cr19 +#define SR3 cr20 + +/* MVME197 only */ +#define SRX cr16 +#define EXIP cr4 +#define ENIP cr5 +#define ICMD cr25 +#define ICTL cr26 +#define ISAR cr27 +#define ISAP cr28 +#define IUAP cr29 +#define IIR cr30 +#define IBP cr31 +#define IPPU cr32 +#define IPPL cr33 +#define ISR cr34 +#define ILAR cr35 +#define IPAR cr36 +#define DCMD cr40 +#define DCTL cr41 +#define DSAR cr42 +#define DSAP cr43 +#define DUAP cr44 +#define DIR cr45 +#define DBP cr46 +#define DPPU cr47 +#define DPPL cr48 +#define DSR cr49 +#define DLAR cr50 +#define DPAR cr51 +/* end MVME197 only */ + +#define FPECR fcr0 +#define FPHS1 fcr1 +#define FPLS1 fcr2 +#define FPHS2 fcr3 +#define FPLS2 fcr4 +#define FPPT fcr5 +#define FPRH fcr6 +#define FPRL fcr7 +#define FPIT fcr8 +#define FPSR fcr62 +#define FPCR fcr63 + +/* + * At various times, there is the need to clear the pipeline (i.e. + * synchronize). A "tb1 0, r0, foo" will do that (because a trap + * instruction always synchronizes, and this particular instruction + * will never actually take the trap). + */ +#if 0 +#define FLUSH_PIPELINE tcnd ne0, r0, 0 +#define FLUSH_PIPELINE_STRING "tcnd ne0, r0, 0" +#else +#define FLUSH_PIPELINE tb1 0, r0, 0 +#define FLUSH_PIPELINE_STRING "tb1 0, r0, 0" +#endif +#define NOP or r0, r0, r0 +#define NOP_STRING "or r0, r0, r0" + +#define RTE NOP ; rte + +/* + * Useful in some situations. + */ +#define CALL(NAME, ARG1, ARG2) \ + subu r31, r31, 32; \ + or r2, r0, ARG1; \ + bsr.n NAME; \ + or r3, r0, ARG2; \ + addu r31, r31, 32 + +/* This define is similar to CALL, but accepts a function pointer XXX smurph */ +#define CALLP(NAME, ARG1, ARG2) \ + subu r31, r31, 32; \ + or.u r5, r0, hi16(NAME); \ + ld r4, r5, lo16(NAME); \ + or r2, r0, ARG1; \ + jsr.n r4; \ + or r3, r0, ARG2; \ + addu r31, r31, 32 + +/* + * SR1 - CPU FLAGS REGISTER + * XXX clean this when the trap handler is reworked. Among the things + * I like to see is having the trap frame on the kernel stack instead + * of putting in the PCB. If done properly, we don't need SR1 for doing + * anything special. nivas + * + * SR1 contains flags about the current CPU status. + * + * The bit FLAG_IGNORE_DATA_EXCEPTION indicates that any data exceptions + * should be ignored (well, at least treated in a special way). + * The bit FLAG_ENABLING_FPU indicates that the exception handler is + * in the process of enabling the FPU (so that an exception can + * be serviced). This is needed because enabling the FPU can + * cause other exceptions to happen, and the whole system is + * in a rather precarious state and so special cautions must + * be taken. + */ +#define FLAG_CPU_FIELD_WIDTH 2 /* must be <= 12 */ +#define FLAG_IGNORE_DATA_EXCEPTION 5 +#define FLAG_ENABLING_FPU 7 +#define FLAG_FROM_KERNEL 8 + +/* REGister OFFset into the E.F. (exception frame) */ +#define REG_OFF(reg_num) ((reg_num) * 4) /* (num * sizeof(register_t)) */ +#define GENREG_OFF(num) (REG_OFF(EF_R0 + (num))) /* GENeral REGister OFFset */ + +/* + * Some registers used during the setting up of the new exception frame. + * Don't choose r1, r30, or r31 for any of them. + * + * Also, if any are 'r2' or 'r3', be careful using with CALL above! + */ +#define FLAGS r2 +#define TMP r3 +#define TMP2 r10 +#define TMP3 r11 +#define SAVE_TMP2 st r10, r31, GENREG_OFF(10) +#define SAVE_TMP3 st r11, r31, GENREG_OFF(11) +#define RESTORE_TMP2 ld r10, r31, GENREG_OFF(10) +#define RESTORE_TMP3 ld r11, r31, GENREG_OFF(11) + +/* + * Info about the PSR + */ +#define PSR_SHADOW_FREEZE_BIT 0 +#define PSR_INTERRUPT_DISABLE_BIT 1 +#define PSR_FPU_DISABLE_BIT 3 +#define PSR_BIG_ENDIAN_MODE 30 +#define PSR_SUPERVISOR_MODE_BIT 31 +/* + * mc88110 PSR bit definitions (MVME197) + */ +#define PSR_GRAPHICS_DISABLE_BIT 4 +#define PSR_SERIAL_MODE_BIT 29 +#define PSR_CARRY_BIT 28 +#define PSR_SERIALIZE_BIT 25 + +/* + * Status bits for an SXIP/SNIP/SFIP address. + */ +#define RTE_VALID_BIT 1 +#define RTE_ERROR_BIT 0 + +/* + * Info about DMT0/DMT1/DMT2 + */ +#define DMT_VALID_BIT 0 +#define DMT_WRITE_BIT 1 +#define DMT_LOCK_BIT 12 +#define DMT_DOUBLE_BIT 13 +#define DMT_DAS_BIT 14 +#define DMT_DREG_OFFSET 7 +#define DMT_DREG_WIDTH 5 + +/* + * Bits for eh_debug. + */ +#define DEBUG_INTERRUPT_BIT 0 +#define DEBUG_DATA_BIT 1 +#define DEBUG_INSTRUCTION_BIT 2 +#define DEBUG_MISALIGN_BIT 3 +#define DEBUG_UNIMP_BIT 4 +#define DEBUG_DIVIDE_BIT 5 +#define DEBUG_OF_BIT 6 +#define DEBUG_FPp_BIT 7 +#define DEBUG_FPi_BIT 8 +#define DEBUG_SYSCALL_BIT 9 +#define DEBUG_MACHSYSCALL_BIT 10 +#define DEBUG_UNIMPLEMENTED_BIT 11 +#define DEBUG_PRIVILEGE_BIT 12 +#define DEBUG_BOUNDS_BIT 13 +#define DEBUG_OVERFLOW_BIT 14 +#define DEBUG_ERROR_BIT 15 +#define DEBUG_SIGSYS_BIT 16 +#define DEBUG_SIGTRAP_BIT 17 +#define DEBUG_BREAK_BIT 18 +#define DEBUG_TRACE_BIT 19 +#define DEBUG_KDB_BIT 20 +#define DEBUG_JKDB_BIT 21 +#define DEBUG_BUGCALL_BIT 22 +/* MVME197 Non-Maskable Interrupt */ +#define DEBUG_NON_MASK_BIT 23 +/* MVME197 Data Read Miss (Software Table Searches) */ +#define DEBUG_197_READ_BIT 25 +/* MVME197 Data Write Miss (Software Table Searches) */ +#define DEBUG_197_WRITE_BIT 26 +/* MVME197 Inst ATC Miss (Software Table Searches) */ +#define DEBUG_197_INST_BIT 27 + +#define DEBUG_UNKNOWN_BIT 31 + +/* exception vector marker */ +#define UNKNOWN_HANDLER 0xffffffff +#define END_OF_VECTOR_LIST 0xfffffffe + +#define VECTOR(x) \ + word _C_LABEL(x) + +#endif /* _KERNEL */ + +#endif /* __MACHINE_M88K_ASM_H__ */ diff --git a/sys/arch/luna88k/include/asm_macro.h b/sys/arch/luna88k/include/asm_macro.h new file mode 100644 index 00000000000..62e5b2465c9 --- /dev/null +++ b/sys/arch/luna88k/include/asm_macro.h @@ -0,0 +1,123 @@ +/* $OpenBSD: asm_macro.h,v 1.1 2004/04/21 15:23:56 aoyama Exp $ */ +/* + * Mach Operating System + * Copyright (c) 1993-1991 Carnegie Mellon University + * Copyright (c) 1991 OMRON Corporation + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON AND OMRON ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON AND OMRON DISCLAIM ANY LIABILITY OF ANY KIND + * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie the + * rights to redistribute these changes. + */ + +#ifndef __MACHINE_M88K_ASM_MACRO_H__ +#define __MACHINE_M88K_ASM_MACRO_H__ + +#include <machine/asm.h> +/* + * Various compiler macros used for speed and efficiency. + * Anyone can include. + */ + +/* + * Flushes the data pipeline. + */ +#define flush_pipeline() \ + __asm__ __volatile__ (FLUSH_PIPELINE_STRING) + +/* + * PSR_TYPE is the type of the Process Status Register. + */ +typedef unsigned long m88k_psr_type; + +/* + * disable_interrupts_return_psr() + * + * The INTERRUPT_DISABLE bit is set in the PSR and the *PREVIOUS* + * PSR is returned. Intended to be used with set_psr() [below] as in: + * + * { + * m88k_psr_type psr; + * . + * . + * psr = disable_interrupts_return_psr(); + * . + * SHORT [time-wise] CRITICAL SECTION HERE + * . + * set_psr(psr); + * . + * . + */ +static __inline__ m88k_psr_type disable_interrupts_return_psr(void) +{ + m88k_psr_type temp, oldpsr; + __asm__ __volatile__ ("ldcr %0, cr1" : "=r" (oldpsr)); + __asm__ __volatile__ ("set %1, %0, 1<1>" : "=r" (oldpsr), "=r" (temp)); + __asm__ __volatile__ ("stcr %0, cr1" : "=r" (temp)); + __asm__ __volatile__ (FLUSH_PIPELINE_STRING); + return oldpsr; +} +#define disable_interrupt() (void)disable_interrupts_return_psr() + +/* + * Sets the PSR. See comments above. + */ +static __inline__ void set_psr(m88k_psr_type psr) +{ + __asm__ __volatile__ ("stcr %0, cr1" :: "r" (psr)); + __asm__ __volatile__ (FLUSH_PIPELINE_STRING); +} + +/* + * Gets the PSR. See comments above. + */ +static __inline__ m88k_psr_type get_psr(void) +{ + m88k_psr_type psr; + __asm__ __volatile__ ("ldcr %0, cr1" : "=r" (psr)); + return psr; +} + +/* + * Enables interrupts. + */ +static __inline__ m88k_psr_type enable_interrupts_return_psr(void) +{ + m88k_psr_type temp, oldpsr; /* need a temporary register */ + __asm__ __volatile__ ("ldcr %0, cr1" : "=r" (oldpsr)); + __asm__ __volatile__ ("clr %1, %0, 1<1>" : "=r" (oldpsr), "=r" (temp)); + __asm__ __volatile__ ("stcr %0, cr1" : "=r" (temp)); + __asm__ __volatile__ (FLUSH_PIPELINE_STRING); + return oldpsr; +} +#define enable_interrupt() (void)enable_interrupts_return_psr() + +#define db_enable_interrupt enable_interrupt +#define db_disable_interrupt disable_interrupt + +/* + * Provide access from C code to the assembly instruction ff1 + */ +static __inline__ unsigned ff1(unsigned val) +{ + __asm__ __volatile__ ("ff1 %0, %0" : "=r" (val) : "0" (val)); + return val; +} + +#endif /* __MACHINE_M88K_ASM_MACRO_H__ */ diff --git a/sys/arch/luna88k/include/autoconf.h b/sys/arch/luna88k/include/autoconf.h new file mode 100644 index 00000000000..c5844f078ac --- /dev/null +++ b/sys/arch/luna88k/include/autoconf.h @@ -0,0 +1,79 @@ +/* $OpenBSD: autoconf.h,v 1.1 2004/04/21 15:23:56 aoyama Exp $ */ +/* + * Copyright (c) 1999, Steve Murphree, Jr. + * Copyright (c) 1996 Nivas Madhur + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Nivas Madhur. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/* + * Autoconfiguration information. + */ + +#ifndef _MVME88K_AUTOCONF_H_ +#define _MVME88K_AUTOCONF_H_ + +struct confargs { + int ca_bustype; + void *ca_vaddr; + void *ca_paddr; + int ca_offset; + int ca_len; + int ca_ipl; + int ca_vec; + char *ca_name; + void *ca_master; /* points to bus-dependent data */ +}; + +#define BUS_MAIN 0 +#define BUS_MC 1 +#define BUS_PCC 2 +#define BUS_PCCTWO 3 +#define BUS_VMES 4 +#define BUS_VMEL 5 +#define BUS_SYSCON 6 +#define BUS_BUSSWITCH 7 + +/* the following are from the prom/bootblocks */ +extern void *bootaddr; /* PA of boot device */ +extern int bootpart; /* boot partition (disk) */ + +extern struct device *bootdv; /* boot device */ + +void *mapiodev(void *pa, int size); +void unmapiodev(void *kva, int size); + +struct device *getdevunit(char *name, int unit); + +/* taken from NetBSD/luna68k */ + +struct mainbus_attach_args { + const char *ma_name; + paddr_t ma_addr; + int ma_ilvl; +}; +#endif diff --git a/sys/arch/luna88k/include/board.h b/sys/arch/luna88k/include/board.h new file mode 100644 index 00000000000..7ed3f905fdc --- /dev/null +++ b/sys/arch/luna88k/include/board.h @@ -0,0 +1,214 @@ +/* $OpenBSD: board.h,v 1.1 2004/04/21 15:23:56 aoyama Exp $ */ +/* + * Mach Operating System + * Copyright (c) 1993-1991 Carnegie Mellon University + * Copyright (c) 1991 OMRON Corporation + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON AND OMRON ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON AND OMRON DISCLAIM ANY LIABILITY OF ANY KIND + * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie the + * rights to redistribute these changes. + */ + +#ifndef __LUNA88K_BOARD_H__ +#define __LUNA88K_BOARD_H__ + +/* + * OMRON SX9100DT CPU board constants + */ + +/* + * Something to put append a 'U' to a long constant if it's C so that + * it'll be unsigned in both ANSI and traditional. + */ +#if defined(_LOCORE) +#define U(num) num +#elif defined(__STDC__) +#define U(num) num ## U +#else +#define U(num) num/**/U +#endif +#define UDEFINED + +#define MAX_CPUS 4 /* maximum cpus on the board */ +#define MAX_CMMUS 8 /* maximum cmmus on the board */ + +/* machtype values */ +#define LUNA_88K 1 +#define LUNA_88K2 2 + +#define SYSV_BASE U(0x00000000) /* system virtual base */ +#define VEQR_ADDR U(0x00000000) + +#define OBIO_START U(0x41000000) +#define OBIO_SIZE U(0xBf000000) + +#define OBIO1_BASE U(0x41000000) /* on board i/o 1 base */ +#define OBIO1_SPACE U(0x1f000000) /* on board i/o 1 space */ +#define OBIO2_BASE U(0x61000000) /* on board i/o 2 base */ +#define OBIO2_SPACE U(0x1f000000) /* on board i/o 2 space */ +#define OBIO3_BASE U(0x80000000) /* on board i/o 3 base */ +#define OBIO3_SPACE U(0x80000000) /* on board i/o 3 space */ + +#define MAXU_ADDR U(0x40000000) /* size of user virtual space */ +#define MAXPHYSMEM U(0x10000000) /* max physical memory */ + +/* #define ILLADDRESS U(0x0F000000) */ /* any faulty address */ +/* #define ILLADDRESS U(0x3FFFFFF0) */ /* any faulty address for LUNA-88K2 */ + +#define PROM_ADDR U(0x41000000) /* PROM */ +#define PROM_SPACE U(0x00040000) +#define NVRAM_ADDR U(0x45000000) /* Non Volatile */ +#define NVRAM_SPACE U(0x00001FDC) +#define FUSE_ROM_ADDR U(0x43000000) /* FUSE_ROM */ +#define FUSE_ROM_SPACE 1024 +#define OBIO_CAL_CTL U(0x45001FE0) /* calendar control register */ +#define OBIO_CAL_SEC U(0x45001FE4) /* seconds */ +#define OBIO_CAL_MIN U(0x45001FE8) /* minutes */ +#define OBIO_CAL_HOUR U(0x45001FEC) /* hours */ +#define OBIO_CAL_DOW U(0x45001FF0) /* Day Of the Week */ +#define OBIO_CAL_DAY U(0x45001FF4) /* days */ +#define OBIO_CAL_MON U(0x45001FF8) /* months */ +#define OBIO_CAL_YEAR U(0x45001FFC) /* years */ +#define OBIO_PIO0_BASE U(0x49000000) /* PIO-0 */ +#define OBIO_PIO0_SPACE U(0x0000000C) +#define OBIO_PIO0A U(0x49000000) /* PIO-0 port A */ +#define OBIO_PIO0B U(0x49000004) /* PIO-0 port B */ +#define OBIO_PIO0C U(0x49000008) /* PIO-0 port C*/ +#define OBIO_PIO0 U(0x4900000C) /* PIO-0 control */ +#define OBIO_PIO1_BASE U(0x4D000000) /* PIO-1 */ +#define OBIO_PIO1_SPACE U(0x0000000C) +#define OBIO_PIO1A U(0x4D000000) /* PIO-1 port A */ +#define OBIO_PIO1B U(0x4D000004) /* PIO-1 port B */ +#define OBIO_PIO1C U(0x4D000008) /* PIO-1 port C*/ +#define OBIO_PIO1 U(0x4D00000C) /* PIO-1 control */ +#define OBIO_SIO U(0x51000000) /* SIO */ +#define OBIO_TAS U(0x61000000) /* TAS register */ +#define OBIO_CLOCK0 U(0x63000000) /* system clock CPU 0 */ +#define OBIO_CLOCK1 U(0x63000004) /* system clock CPU 1 */ +#define OBIO_CLOCK2 U(0x63000008) /* system clock CPU 2 */ +#define OBIO_CLOCK3 U(0x6300000C) /* system clock CPU 3 */ +#define OBIO_CLK_INTR 31 /* system clock interrupt flag */ +#define INT_ST_MASK0 U(0x65000000) /* interrupt status register CPU 0 */ +#define INT_ST_MASK1 U(0x65000004) /* interrupt status register CPU 1 */ +#define INT_ST_MASK2 U(0x65000008) /* interrupt status register CPU 2 */ +#define INT_ST_MASK3 U(0x6500000C) /* interrupt status register CPU 3 */ +#define INT_LEVEL 8 /* # of intrrupt level + 1 */ +#define INT_SET_LV7 U(0x00000000) /* disable interrupts */ +#define INT_SET_LV6 U(0x00000000) /* enable level 7 */ +#define INT_SET_LV5 U(0x80000000) /* enable level 7-6 */ +#define INT_SET_LV4 U(0xC0000000) /* enable level 7-5 */ +#define INT_SET_LV3 U(0xE0000000) /* enable level 7-4 */ +#define INT_SET_LV2 U(0xF0000000) /* enable level 7-3 */ +#define INT_SET_LV1 U(0xF8000000) /* enable level 7-2 */ +#define INT_SET_LV0 U(0xFC000000) /* enable interrupts */ +#define INT_SLAVE_MASK U(0x84000000) /* slave can only enable 6 and 1 */ +#define INT_CLOCK_MASK 0xBFFFFFFF /* mask clock */ +#define INT_LV6 U(0x00800000) /* level 6 enebled */ +#define INT_LV5 U(0x00400000) /* level 5 enebled */ +#define INT_LV4 U(0x00200000) /* level 4 enebled */ +#define INT_LV3 U(0x00100000) /* level 3 enebled */ +#define INT_LV2 U(0x00080000) /* level 2 enebled */ +#define INT_LV1 U(0x00040000) /* level 1 enebled */ +#define INT_ST U(0xE0000000) /* interrupt status */ +#define INT_MASK_LV0 INT_LV6 | INT_LV5 | INT_LV4 | INT_LV3 | INT_LV2 | INT_LV1 +#define INT_MASK_LV1 INT_LV6 | INT_LV5 | INT_LV4 | INT_LV3 | INT_LV2 +#define INT_MASK_LV2 INT_LV6 | INT_LV5 | INT_LV4 | INT_LV3 +#define INT_MASK_LV3 INT_LV6 | INT_LV5 | INT_LV4 +#define INT_MASK_LV4 INT_LV6 | INT_LV5 +#define INT_MASK_LV5 INT_LV6 +#define INT_MASK_LV6 0 +#define INT_MASK_LV7 0 +#define SLAVE_MASK INT_LV6 | INT_LV1 + +#define NON_MASKABLE_LEVEL 7 /* non-maskable-interrupt (abort) */ +#define CLOCK_INT_LEVEL 6 /* clock interrupt level */ +#define SOFT_INT_LEVEL 1 /* software interrupt level */ +#define SOFT_INT0 U(0x69000000) /* software interrupt CPU 0 */ +#define SOFT_INT1 U(0x69000004) /* software interrupt CPU 1 */ +#define SOFT_INT2 U(0x69000008) /* software interrupt CPU 2 */ +#define SOFT_INT3 U(0x6900000C) /* software interrupt CPU 3 */ +#define SOFT_INT_FLAG0 U(0x6B000000) /* sfotware interrupt flag CPU 0 */ +#define SOFT_INT_FLAG1 U(0x6B000000) /* sfotware interrupt flag CPU 1 */ +#define SOFT_INT_FLAG2 U(0x6B000000) /* sfotware interrupt flag CPU 2 */ +#define SOFT_INT_FLAG3 U(0x6B000000) /* sfotware interrupt flag CPU 3 */ +#define SOFT_INT_BIT 31 /* software interrupt flag bit */ +#define RESET_CPU0 U(0x6D000000) /* reset CPU 0 */ +#define RESET_CPU1 U(0x6D000004) /* reset CPU 1 */ +#define RESET_CPU2 U(0x6D000008) /* reset CPU 2 */ +#define RESET_CPU3 U(0x6D00000C) /* reset CPU 3 */ +#define RESET_CPU_ALL U(0x6D000010) /* reset ALL CPUs */ +#define TRI_PORT_RAM U(0x71000000) /* 3 port RAM */ +#define TRI_PORT_RAM_SPACE 0x20000 +#define EXT_A_ADDR U(0x81000000) /* extension board A */ +#define EXT_A_SPACE U(0x02000000) +#define EU_BASE U(0x81fe0000) /* VME expand board */ +#define EXT_B_ADDR U(0x83000000) /* extension board B */ +#define EXT_B_SPACE U(0x01000000) +#define PC_BASE U(0x90000000) /* pc-98 extension board */ +#define PC_SPACE U(0x02000000) + +#define MROM_ADDR U(0xA1000000) /* Mask ROM address */ +#define MROM_SPACE 0x400000 +#define BMAP_START U(0xB1000000) /* Bitmap start address */ +#define BMAP_SPACE (BMAP_END - BMAP_START) +#define BMAP_RFCNT U(0xB1000000) /* RFCNT register */ +#define BMAP_BMSEL U(0xB1040000) /* BMSEL register */ +#define BMAP_BMP U(0xB1080000) /* common bitmap plane */ +#define BMAP_BMAP0 U(0xB10C0000) /* bitmap plane 0 */ +#define BMAP_BMAP1 U(0xB1100000) /* bitmap plane 1 */ +#define BMAP_BMAP2 U(0xB1140000) /* bitmap plane 2 */ +#define BMAP_BMAP3 U(0xB1180000) /* bitmap plane 3 */ +#define BMAP_BMAP4 U(0xB11C0000) /* bitmap plane 4 */ +#define BMAP_BMAP5 U(0xB1200000) /* bitmap plane 5 */ +#define BMAP_BMAP6 U(0xB1240000) /* bitmap plane 6 */ +#define BMAP_BMAP7 U(0xB1280000) /* bitmap plane 7 */ +#define BMAP_FN U(0xB12C0000) /* common bitmap function */ +#define BMAP_FN0 U(0xB1300000) /* bitmap function 0 */ +#define BMAP_FN1 U(0xB1340000) /* bitmap function 1 */ +#define BMAP_FN2 U(0xB1380000) /* bitmap function 2 */ +#define BMAP_FN3 U(0xB13C0000) /* bitmap function 3 */ +#define BMAP_FN4 U(0xB1400000) /* bitmap function 4 */ +#define BMAP_FN5 U(0xB1440000) /* bitmap function 5 */ +#define BMAP_FN6 U(0xB1480000) /* bitmap function 6 */ +#define BMAP_FN7 U(0xB14C0000) /* bitmap function 7 */ +#define BMAP_END U(0xB1500000) +#define BMAP_END24P U(0xB1800000) /* end of 24p framemem */ +#define BMAP_PALLET0 U(0xC0000000) /* color pallet */ +#define BMAP_PALLET1 U(0xC1000000) /* color pallet */ +#define BMAP_PALLET2 U(0xC1100000) /* color pallet */ +#define BOARD_CHECK_REG U(0xD0000000) /* board check register */ +#define BMAP_CRTC U(0xD1000000) /* CTRC-II */ +#define BMAP_IDENTROM U(0xD1800000) /* bitmap-boad identify ROM */ +#define SCSI_ADDR U(0xE1000000) /* SCSI address */ +#define LANCE_ADDR U(0xF1000000) /* LANCE */ +#define EXT_IACK_ADDR 0xFFFFFFF7 /* IACK Space for Extended Board */ + +#define VDMA_STD(x) ((int)(x)) + +#define CMMU_I0 U(0xFFF07000) /* CMMU instruction cpu 0 */ +#define CMMU_D0 U(0xFFF06000) /* CMMU data cpu 0 */ +#define CMMU_I1 U(0xFFF05000) /* CMMU instruction cpu 1 */ +#define CMMU_D1 U(0xFFF04000) /* CMMU data cpu 1 */ +#define CMMU_I2 U(0xFFF03000) /* CMMU instruction cpu 2 */ +#define CMMU_D2 U(0xFFF02000) /* CMMU data cpu 2 */ +#define CMMU_I3 U(0xFFF01000) /* CMMU instruction cpu 3 */ +#define CMMU_D3 U(0xFFF00000) /* CMMU data cpu 3 */ + +#endif /* __LUNA88K_BOARD_H__ */ diff --git a/sys/arch/luna88k/include/bus.h b/sys/arch/luna88k/include/bus.h new file mode 100644 index 00000000000..a0debb18345 --- /dev/null +++ b/sys/arch/luna88k/include/bus.h @@ -0,0 +1,632 @@ +/* $OpenBSD: bus.h,v 1.1 2004/04/21 15:23:56 aoyama Exp $ */ +/* $NetBSD: bus.h,v 1.9 1998/01/13 18:32:15 scottr Exp $ */ + +/*- + * Copyright (c) 1996, 1997, 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, + * NASA Ames Research Center. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Copyright (C) 1997 Scott Reynolds. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* almost same as OpenBSD/mac68k */ + +#ifndef _LUNA88K_BUS_H_ +#define _LUNA88K_BUS_H_ + +/* + * Value for the luna88k bus space tag, not to be used directly by MI code. + */ +#define LUNA88K_BUS_SPACE_MEM 0 /* space is mem space */ + +/* + * Bus address and size types + */ +typedef u_long bus_addr_t; +typedef u_long bus_size_t; + +/* + * Access methods for bus resources and address space. + */ +typedef int bus_space_tag_t; +typedef u_long bus_space_handle_t; + +/* + * int bus_space_map(bus_space_tag_t t, bus_addr_t addr, + * bus_size_t size, int flags, bus_space_handle_t *bshp); + * + * Map a region of bus space. + */ + +#define BUS_SPACE_MAP_CACHEABLE 0x01 +#define BUS_SPACE_MAP_LINEAR 0x02 + +int bus_space_map(bus_space_tag_t, bus_addr_t, bus_size_t, + int, bus_space_handle_t *); + +/* + * void bus_space_unmap(bus_space_tag_t t, + * bus_space_handle_t bsh, bus_size_t size); + * + * Unmap a region of bus space. + */ + +void bus_space_unmap(bus_space_tag_t, bus_space_handle_t, bus_size_t); + +/* + * int bus_space_subregion(bus_space_tag_t t, + * bus_space_handle_t bsh, bus_size_t offset, bus_size_t size, + * bus_space_handle_t *nbshp); + * + * Get a new handle for a subregion of an already-mapped area of bus space. + */ + +int bus_space_subregion(bus_space_tag_t t, bus_space_handle_t bsh, + bus_size_t offset, bus_size_t size, bus_space_handle_t *nbshp); + +/* + * int bus_space_alloc(bus_space_tag_t t, bus_addr_t, rstart, + * bus_addr_t rend, bus_size_t size, bus_size_t align, + * bus_size_t boundary, int flags, bus_addr_t *addrp, + * bus_space_handle_t *bshp); + * + * Allocate a region of bus space. + */ + +int bus_space_alloc(bus_space_tag_t t, bus_addr_t rstart, + bus_addr_t rend, bus_size_t size, bus_size_t align, + bus_size_t boundary, int cacheable, bus_addr_t *addrp, + bus_space_handle_t *bshp); + +/* + * int bus_space_free(bus_space_tag_t t, + * bus_space_handle_t bsh, bus_size_t size); + * + * Free a region of bus space. + */ + +void bus_space_free(bus_space_tag_t t, bus_space_handle_t bsh, + bus_size_t size); + +/* + * int luna88k_bus_space_probe(bus_space_tag_t t, + * bus_space_handle_t bsh, bus_size_t offset, int sz); + * + * Probe the bus at t/bsh/offset, using sz as the size of the load. + * + * This is a machine-dependent extension, and is not to be used by + * machine-independent code. + */ + +int luna88k_bus_space_probe(bus_space_tag_t t, + bus_space_handle_t bsh, bus_size_t offset, int sz); + +/* + * u_intN_t bus_space_read_N(bus_space_tag_t tag, + * bus_space_handle_t bsh, bus_size_t offset); + * + * Read a 1, 2, 4, or 8 byte quantity from bus space + * described by tag/handle/offset. + */ + +#define bus_space_read_1(t, h, o) \ + ((void) t, (*(volatile u_int8_t *)((h) + 4 * (o)))) + +#define bus_space_read_2(t, h, o) \ + ((void) t, (*(volatile u_int16_t *)((h) + 4 * (o)))) + +#define bus_space_read_4(t, h, o) \ + ((void) t, (*(volatile u_int32_t *)((h) + 4 * (o)))) + +#if 0 /* Cause a link error for bus_space_read_8 */ +#define bus_space_read_8(t, h, o) !!! bus_space_read_8 unimplemented !!! +#endif + +/* + * void bus_space_read_multi_N(bus_space_tag_t tag, + * bus_space_handle_t bsh, bus_size_t offset, + * u_intN_t *addr, size_t count); + * + * Read `count' 1, 2, 4, or 8 byte quantities from bus space + * described by tag/handle/offset and copy into buffer provided. + */ + +#define bus_space_read_multi_1(t, h, o, a, c) do { \ + (void) t; \ + __asm __volatile (" \ + or r10, r0, %0 ; \ + or r11, r0, %1 ; \ + or r12, r0, %2 ; \ + 1: ld.bu r13, r10, 0 ; \ + st.b r13, r11, 0 ; \ + add r11, r11, 1 ; \ + bcnd.n ne0, r12, 1b ; \ + sub r12, r12, 1" : \ + : \ + "r" ((h) + 4 * (o)), "r" (a), "r" (c) : \ + "r10", "r11", "r12", "r13"); \ +} while (0); + +#define bus_space_read_multi_2(t, h, o, a, c) do { \ + (void) t; \ + __asm __volatile (" \ + or r10, r0, %0 ; \ + or r11, r0, %1 ; \ + or r12, r0, %2 ; \ + 1: ld.hu r13, r10, 0 ; \ + st.hu r13, r11, 0 ; \ + add r11, r11, 2 ; \ + bcnd.n ne0, r12, 1b ; \ + sub r12, r12, 1" : \ + : \ + "r" ((h) + 4 * (o)), "r" (a), "r" (c) : \ + "r10", "r11", "r12", "r13"); \ +} while (0); + +#define bus_space_read_multi_4(t, h, o, a, c) do { \ + (void) t; \ + __asm __volatile (" \ + or r10, r0, %0 ; \ + or r11, r0, %1 ; \ + or r12, r0, %2 ; \ + 1: ld r13, r10, 0 ; \ + st r13, r11, 0 ; \ + add r11, r11, 4 ; \ + bcnd.n ne0, r12, 1b ; \ + sub r12, r12, 1" : \ + : \ + "r" ((h) + 4 * (o)), "r" (a), "r" (c) : \ + "r10", "r11", "r12", "r13"); \ +} while (0); + +#if 0 /* Cause a link error for bus_space_read_multi_8 */ +#define bus_space_read_multi_8 !!! bus_space_read_multi_8 unimplemented !!! +#endif + +/* + * void bus_space_read_region_N(bus_space_tag_t tag, + * bus_space_handle_t bsh, bus_size_t offset, + * u_intN_t *addr, size_t count); + * + * Read `count' 1, 2, 4, or 8 byte quantities from bus space + * described by tag/handle and starting at `offset' and copy into + * buffer provided. + */ + +#define bus_space_read_region_1(t, h, o, a, c) do { \ + (void) t; \ + __asm __volatile (" \ + or r10, r0, %0 ; \ + or r11, r0, %1 ; \ + or r12, r0, %2 ; \ + 1: ld.bu r13, r10, 0 ; \ + st.b r13, r11, 0 ; \ + add r10, r10, 4 ; \ + add r11, r11, 1 ; \ + bcnd.n ne0, r12, 1b ; \ + sub r12, r12, 1" : \ + : \ + "r" ((h) + 4 * (o)), "r" (a), "r" (c) : \ + "r10", "r11", "r12", "r13"); \ +} while (0); + +#define bus_space_read_region_2(t, h, o, a, c) do { \ + (void) t; \ + __asm __volatile (" \ + or r10, r0, %0 ; \ + or r11, r0, %1 ; \ + or r12, r0, %2 ; \ + 1: ld.hu r13, r10, 0 ; \ + st.hu r13, r11, 0 ; \ + add r10, r10, 4 ; \ + add r11, r11, 2 ; \ + bcnd.n ne0, r12, 1b ; \ + sub r12, r12, 1" : \ + : \ + "r" ((h) + 4 * (o)), "r" (a), "r" (c) : \ + "r10", "r11", "r12", "r13"); \ +} while (0); + +#define bus_space_read_region_4(t, h, o, a, c) do { \ + (void) t; \ + __asm __volatile (" \ + or r10, r0, %0 ; \ + or r11, r0, %1 ; \ + or r12, r0, %2 ; \ + 1: ld r13, r10, 0 ; \ + st r13, r11, 0 ; \ + add r10, r10, 4 ; \ + add r11, r11, 4 ; \ + bcnd.n ne0, r12, 1b ; \ + sub r12, r12, 1" : \ + : \ + "r" ((h) + 4 * (o)), "r" (a), "r" (c) : \ + "r10", "r11", "r12", "r13"); \ +} while (0); + +#if 0 /* Cause a link error for bus_space_read_region_8 */ +#define bus_space_read_region_8 !!! bus_space_read_region_8 unimplemented !!! +#endif + +/* + * void bus_space_write_N(bus_space_tag_t tag, + * bus_space_handle_t bsh, bus_size_t offset, + * u_intN_t value); + * + * Write the 1, 2, 4, or 8 byte value `value' to bus space + * described by tag/handle/offset. + */ + +#define bus_space_write_1(t, h, o, v) \ + ((void) t, ((void)(*(volatile u_int8_t *)((h) + 4 * (o)) = (v)))) + +#define bus_space_write_2(t, h, o, v) \ + ((void) t, ((void)(*(volatile u_int16_t *)((h) + 4 * (o)) = (v)))) + +#define bus_space_write_4(t, h, o, v) \ + ((void) t, ((void)(*(volatile u_int32_t *)((h) + 4 * (o)) = (v)))) + +#if 0 /* Cause a link error for bus_space_write_8 */ +#define bus_space_write_8 !!! bus_space_write_8 not implemented !!! +#endif + +/* + * void bus_space_write_multi_N(bus_space_tag_t tag, + * bus_space_handle_t bsh, bus_size_t offset, + * const u_intN_t *addr, size_t count); + * + * Write `count' 1, 2, 4, or 8 byte quantities from the buffer + * provided to bus space described by tag/handle/offset. + */ + +#define bus_space_write_multi_1(t, h, o, a, c) do { \ + (void) t; \ + __asm __volatile (" \ + or r10, r0, %0 ; \ + or r11, r0, %1 ; \ + or r12, r0, %2 ; \ + 1: ld.bu r13, r11, 0 ; \ + st.b r13, r10, 0 ; \ + add r11, r11, 1 ; \ + bcnd.n ne0, r12, 1b ; \ + sub r12, r12, 1" : \ + : \ + "r" ((h) + 4 * (o)), "r" (a), "r" (c) : \ + "r10", "r11", "r12", "r13"); \ +} while (0); + +#define bus_space_write_multi_2(t, h, o, a, c) do { \ + (void) t; \ + __asm __volatile (" \ + or r10, r0, %0 ; \ + or r11, r0, %1 ; \ + or r12, r0, %2 ; \ + 1: ld.hu r13, r11, 0 ; \ + st.hu r13, r10, 0 ; \ + add r11, r11, 2 ; \ + bcnd.n ne0, r12, 1b ; \ + sub r12, r12, 1" : \ + : \ + "r" ((h) + 4 * (o)), "r" (a), "r" (c) : \ + "r10", "r11", "r12", "r13"); \ +} while (0); + +#define bus_space_write_multi_4(t, h, o, a, c) do { \ + (void) t; \ + __asm __volatile (" \ + or r10, r0, %0 ; \ + or r11, r0, %1 ; \ + or r12, r0, %2 ; \ + 1: ld r13, r11, 0 ; \ + st r13, r10, 0 ; \ + add r11, r11, 4 ; \ + bcnd.n ne0, r12, 1b ; \ + sub r12, r12, 1" : \ + : \ + "r" ((h) + 4 * (o)), "r" (a), "r" (c) : \ + "r10", "r11", "r12", "r13"); \ +} while (0); + +#if 0 /* Cause a link error for bus_space_write_8 */ +#define bus_space_write_multi_8(t, h, o, a, c) \ + !!! bus_space_write_multi_8 unimplemented !!! +#endif + +/* + * void bus_space_write_region_N(bus_space_tag_t tag, + * bus_space_handle_t bsh, bus_size_t offset, + * const u_intN_t *addr, size_t count); + * + * Write `count' 1, 2, 4, or 8 byte quantities from the buffer provided + * to bus space described by tag/handle starting at `offset'. + */ + +#define bus_space_write_region_1(t, h, o, a, c) do { \ + (void) t; \ + __asm __volatile (" \ + or r10, r0, %0 ; \ + or r11, r0, %1 ; \ + or r12, r0, %2 ; \ + 1: ld.bu r13, r11, 0 ; \ + st.b r13, r10, 0 ; \ + add r10, r10, 4 ; \ + add r11, r11, 1 ; \ + bcnd.n ne0, r12, 1b ; \ + sub r12, r12, 1" : \ + : \ + "r" ((h) + 4 * (o)), "r" (a), "r" (c) : \ + "r10", "r11", "r12", "r13"); \ +} while (0); + +#define bus_space_write_region_2(t, h, o, a, c) do { \ + (void) t; \ + __asm __volatile (" \ + or r10, r0, %0 ; \ + or r11, r0, %1 ; \ + or r12, r0, %2 ; \ + 1: ld.hu r13, r11, 0 ; \ + st.hu r13, r10, 0 ; \ + add r10, r10, 4 ; \ + add r11, r11, 2 ; \ + bcnd.n ne0, r12, 1b ; \ + sub r12, r12, 1" : \ + : \ + "r" ((h) + 4 * (o)), "r" (a), "r" (c) : \ + "r10", "r11", "r12", "r13"); \ +} while (0); + +#define bus_space_write_region_4(t, h, o, a, c) do { \ + (void) t; \ + __asm __volatile (" \ + or r10, r0, %0 ; \ + or r11, r0, %1 ; \ + or r12, r0, %2 ; \ + 1: ld r13, r11, 0 ; \ + st r13, r10, 0 ; \ + add r10, r10, 4 ; \ + add r11, r11, 4 ; \ + bcnd.n ne0, r12, 1b ; \ + sub r12, r12, 1" : \ + : \ + "r" ((h) + 4 * (o)), "r" (a), "r" (c) : \ + "r10", "r11", "r12", "r13"); \ +} while (0); + +#if 0 /* Cause a link error for bus_space_write_region_8 */ +#define bus_space_write_region_8 \ + !!! bus_space_write_region_8 unimplemented !!! +#endif + +/* + * void bus_space_set_multi_N(bus_space_tag_t tag, + * bus_space_handle_t bsh, bus_size_t offset, u_intN_t val, + * size_t count); + * + * Write the 1, 2, 4, or 8 byte value `val' to bus space described + * by tag/handle/offset `count' times. + */ + +#define bus_space_set_multi_1(t, h, o, val, c) do { \ + (void) t; \ + __asm __volatile (" \ + or r10, r0, %0 ; \ + or r11, r0, %1 ; \ + or r12, r0, %2 ; \ + 1: st.b r11, r10, 0 ; \ + bcnd.n ne0, r12, 1b ; \ + sub r12, r12, 1" : \ + : \ + "r" ((h) + 4 * (o)), "r" (val), "r" (c) : \ + "r10", "r11", "r12"); \ +} while (0); + +#define bus_space_set_multi_2(t, h, o, val, c) do { \ + (void) t; \ + __asm __volatile (" \ + or r10, r0, %0 ; \ + or r11, r0, %1 ; \ + or r12, r0, %2 ; \ + 1: st.hu r11, r10, 0 ; \ + bcnd.n ne0, r12, 1b ; \ + sub r12, r12, 1" : \ + : \ + "r" ((h) + 4 * (o)), "r" (val), "r" (c) : \ + "r10", "r11", "r12"); \ +} while (0); + +#define bus_space_set_multi_4(t, h, o, val, c) do { \ + (void) t; \ + __asm __volatile (" \ + or r10, r0, %0 ; \ + or r11, r0, %1 ; \ + or r12, r0, %2 ; \ + 1: st r11, r10, 0 ; \ + bcnd.n ne0, r12, 1b ; \ + sub r12, r12, 1" : \ + : \ + "r" ((h) + 4 * (o)), "r" (val), "r" (c) : \ + "r10", "r11", "r12"); \ +} while (0); + +#if 0 /* Cause a link error for bus_space_set_multi_8 */ +#define bus_space_set_multi_8 \ + !!! bus_space_set_multi_8 unimplemented !!! +#endif + +/* + * void bus_space_set_region_N(bus_space_tag_t tag, + * bus_space_handle_t bsh, bus_size_t offset, u_intN_t val, + * size_t count); + * + * Write `count' 1, 2, 4, or 8 byte value `val' to bus space described + * by tag/handle starting at `offset'. + */ + +#define bus_space_set_region_1(t, h, o, val, c) do { \ + (void) t; \ + __asm __volatile (" \ + or r10, r0, %0 ; \ + or r11, r0, %1 ; \ + or r12, r0, %2 ; \ + 1: st.b r11, r10, 0 ; \ + add r10, r10, 4 + bcnd.n ne0, r12, 1b ; \ + sub r12, r12, 1" : \ + : \ + "r" ((h) + 4 * (o)), "r" (val), "r" (c) : \ + "r10", "r11", "r12"); \ +} while (0); + +#define bus_space_set_region_2(t, h, o, val, c) do { \ + (void) t; \ + __asm __volatile (" \ + or r10, r0, %0 ; \ + or r11, r0, %1 ; \ + or r12, r0, %2 ; \ + 1: st.hu r11, r10, 0 ; \ + add r10, r10, 4 + bcnd.n ne0, r12, 1b ; \ + sub r12, r12, 1" : \ + : \ + "r" ((h) + 4 * (o)), "r" (val), "r" (c) : \ + "r10", "r11", "r12"); \ +} while (0); + +#define bus_space_set_region_4(t, h, o, val, c) do { \ + (void) t; \ + __asm __volatile (" \ + or r10, r0, %0 ; \ + or r11, r0, %1 ; \ + or r12, r0, %2 ; \ + 1: st r11, r10, 0 ; \ + add r10, r10, 4 + bcnd.n ne0, r12, 1b ; \ + sub r12, r12, 1" : \ + : \ + "r" ((h) + 4 * (o)), "r" (val), "r" (c) : \ + "r10", "r11", "r12"); \ +} while (0); + +#if 0 /* Cause a link error for bus_space_set_region_8 */ +#define bus_space_set_region_8 \ + !!! bus_space_set_region_8 unimplemented !!! +#endif + +/* + * void bus_space_copy_N(bus_space_tag_t tag, + * bus_space_handle_t bsh1, bus_size_t off1, + * bus_space_handle_t bsh2, bus_size_t off2, + * size_t count); + * + * Copy `count' 1, 2, 4, or 8 byte values from bus space starting + * at tag/bsh1/off1 to bus space starting at tag/bsh2/off2. + */ + +#define __LUNA88K_copy_region_N(BYTES) \ +static __inline void __CONCAT(bus_space_copy_region_,BYTES) \ + (bus_space_tag_t, \ + bus_space_handle_t bsh1, bus_size_t off1, \ + bus_space_handle_t bsh2, bus_size_t off2, \ + bus_size_t count); \ + \ +static __inline void \ +__CONCAT(bus_space_copy_region_,BYTES)(t, h1, o1, h2, o2, c) \ + bus_space_tag_t t; \ + bus_space_handle_t h1, h2; \ + bus_size_t o1, o2, c; \ +{ \ + bus_size_t o; \ + \ + if ((h1 + o1) >= (h2 + o2)) { \ + /* src after dest: copy forward */ \ + for (o = 0; c != 0; c--, o += BYTES) \ + __CONCAT(bus_space_write_,BYTES)(t, h2, o2 + o, \ + __CONCAT(bus_space_read_,BYTES)(t, h1, o1 + o)); \ + } else { \ + /* dest after src: copy backwards */ \ + for (o = (c - 1) * BYTES; c != 0; c--, o -= BYTES) \ + __CONCAT(bus_space_write_,BYTES)(t, h2, o2 + o, \ + __CONCAT(bus_space_read_,BYTES)(t, h1, o1 + o)); \ + } \ +} +__LUNA88K_copy_region_N(1) +__LUNA88K_copy_region_N(2) +__LUNA88K_copy_region_N(4) +#if 0 /* Cause a link error for bus_space_copy_8 */ +#define bus_space_copy_8 \ + !!! bus_space_copy_8 unimplemented !!! +#endif + +#undef __LUNA88K_copy_region_N + +/* + * Bus read/write barrier methods. + * + * void bus_space_barrier(bus_space_tag_t tag, + * bus_space_handle_t bsh, bus_size_t offset, + * bus_size_t len, int flags); + * + * Note: the 680x0 does not currently require barriers, but we must + * provide the flags to MI code. + */ +#define bus_space_barrier(t, h, o, l, f) \ + ((void)((void)(t), (void)(h), (void)(o), (void)(l), (void)(f))) +#define BUS_SPACE_BARRIER_READ 0x01 /* force read barrier */ +#define BUS_SPACE_BARRIER_WRITE 0x02 /* force write barrier */ + +#endif /* _LUNA88K_BUS_H_ */ diff --git a/sys/arch/luna88k/include/cdefs.h b/sys/arch/luna88k/include/cdefs.h new file mode 100644 index 00000000000..3eb37b6b943 --- /dev/null +++ b/sys/arch/luna88k/include/cdefs.h @@ -0,0 +1,40 @@ +/* $OpenBSD: cdefs.h,v 1.1 2004/04/21 15:23:56 aoyama Exp $ */ +/* $NetBSD: cdefs.h,v 1.2 1995/03/23 20:10:48 jtc Exp $ */ + +/* + * Written by J.T. Conklin <jtc@wimsey.com> 01/17/95. + * Public domain. + */ + +#ifndef __MACHINE_CDEFS_H__ +#define __MACHINE_CDEFS_H__ + +#ifdef __STDC__ +#define _C_LABEL(name) _ ## name +#else +#define _C_LABEL(name) _/**/name +#endif + +#ifdef __GNUC__ +#ifdef __STDC__ +#define __indr_reference(sym,alias) \ + __asm__(".stabs \"_" #alias "\",11,0,0,0"); \ + __asm__(".stabs \"_" #sym "\",1,0,0,0") +#define __warn_references(sym,msg) \ + __asm__(".stabs \"" msg "\",30,0,0,0"); \ + __asm__(".stabs \"_" #sym "\",1,0,0,0") +#define __weak_alias(alias,sym) \ + __asm__(".weak _" #alias "; _" #alias "= _" __STRING(sym)) +#else +#define __indr_reference(sym,alias) \ + __asm__(".stabs \"_/**/alias\",11,0,0,0"); \ + __asm__(".stabs \"_/**/sym\",1,0,0,0") +#define __warn_references(sym,msg) \ + __asm__(".stabs msg,30,0,0,0"); \ + __asm__(".stabs \"_/**/sym\",1,0,0,0") +#define __weak_alias(alias,sym) \ + __asm__(".weak _/**/alias; _/**/alias = _/**/sym") +#endif +#endif + +#endif /* __MACHINE_CDEFS_H__ */ diff --git a/sys/arch/luna88k/include/cmmu.h b/sys/arch/luna88k/include/cmmu.h new file mode 100644 index 00000000000..95cc42031a2 --- /dev/null +++ b/sys/arch/luna88k/include/cmmu.h @@ -0,0 +1,103 @@ +/* $OpenBSD: cmmu.h,v 1.1 2004/04/21 15:23:56 aoyama Exp $ */ +/* + * Mach Operating System + * Copyright (c) 1993-1992 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +#ifndef _MACHINE_CMMU_H_ +#define _MACHINE_CMMU_H_ + +#include <machine/mmu.h> + +#ifndef _LOCORE +/* + * Prototypes and stuff for cmmu.c. + */ +extern unsigned cpu_sets[MAX_CPUS]; +extern unsigned master_cpu; +extern int max_cpus, max_cmmus; + +/* + * This lock protects the cmmu SAR and SCR's; other ports + * can be accessed without locking it + * + * May be used from "db_interface.c". + */ +extern struct simplelock cmmu_cpu_lock; + +#define CMMU_LOCK simple_lock(&cmmu_cpu_lock) +#define CMMU_UNLOCK simple_unlock(&cmmu_cpu_lock) + +/* machine dependent cmmu function pointer structure */ +struct cmmu_p { + void (*cmmu_init_func)(void); + void (*setup_board_config_func)(void); + void (*cpu_configuration_print_func)(int); + void (*cmmu_shutdown_now_func)(void); + void (*cmmu_parity_enable_func)(void); + unsigned (*cmmu_cpu_number_func)(void); + void (*cmmu_set_sapr_func)(unsigned, unsigned); + void (*cmmu_set_uapr_func)(unsigned); + void (*cmmu_set_pair_batc_entry_func)(unsigned, unsigned, unsigned); + void (*cmmu_flush_tlb_func)(unsigned, unsigned, vaddr_t, vsize_t); + void (*cmmu_pmap_activate_func)(unsigned, unsigned, + u_int32_t i_batc[BATC_MAX], u_int32_t d_batc[BATC_MAX]); + void (*cmmu_flush_cache_func)(int, paddr_t, psize_t); + void (*cmmu_flush_inst_cache_func)(int, paddr_t, psize_t); + void (*cmmu_flush_data_cache_func)(int, paddr_t, psize_t); + void (*dma_cachectl_func)(vaddr_t, vsize_t, int); + /* DDB only */ + void (*cmmu_dump_config_func)(void); + void (*cmmu_show_translation_func)(unsigned, unsigned, unsigned, int); + /* DEBUG only */ + void (*show_apr_func)(unsigned); +}; + +/* THE pointer! */ +extern struct cmmu_p *cmmu; + +/* The macros... */ +#define cmmu_init (cmmu->cmmu_init_func) +#define setup_board_config (cmmu->setup_board_config_func) +#define cpu_configuration_print(a) (cmmu->cpu_configuration_print_func)(a) +#define cmmu_shutdown_now (cmmu->cmmu_shutdown_now_func) +#define cmmu_parity_enable (cmmu->cmmu_parity_enable_func) +#define cmmu_cpu_number (cmmu->cmmu_cpu_number_func) +#define cmmu_set_sapr(a, b) (cmmu->cmmu_set_sapr_func)(a, b) +#define cmmu_set_uapr(a) (cmmu->cmmu_set_uapr_func)(a) +#define cmmu_set_pair_batc_entry(a, b, c) (cmmu->cmmu_set_pair_batc_entry_func)(a, b, c) +#define cmmu_flush_tlb(a, b, c, d) (cmmu->cmmu_flush_tlb_func)(a, b, c, d) +#define cmmu_pmap_activate(a, b, c, d) (cmmu->cmmu_pmap_activate_func)(a, b, c, d) +#define cmmu_flush_cache(a, b, c) (cmmu->cmmu_flush_cache_func)(a, b, c) +#define cmmu_flush_inst_cache(a, b, c) (cmmu->cmmu_flush_inst_cache_func)(a, b, c) +#define cmmu_flush_data_cache(a, b, c) (cmmu->cmmu_flush_data_cache_func)(a, b, c) +#define dma_cachectl(a, b, c) (cmmu->dma_cachectl_func)(a, b, c) +#define cmmu_dump_config (cmmu->cmmu_dump_config_func) +#define cmmu_show_translation(a, b, c, d) (cmmu->cmmu_show_translation_func)(a, b, c, d) +#define show_apr(ap) (cmmu->show_apr_func)(ap) + +#endif /* _LOCORE */ + +#endif /* _MACHINE_CMMU_H_ */ + diff --git a/sys/arch/luna88k/include/cpu.h b/sys/arch/luna88k/include/cpu.h new file mode 100644 index 00000000000..ef11194964a --- /dev/null +++ b/sys/arch/luna88k/include/cpu.h @@ -0,0 +1,198 @@ +/* $OpenBSD: cpu.h,v 1.1 2004/04/21 15:23:57 aoyama Exp $ */ +/* + * Copyright (c) 1996 Nivas Madhur + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Lawrence Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef __MACHINE_CPU_H__ +#define __MACHINE_CPU_H__ + +/* + * CTL_MACHDEP definitinos. + */ +#define CPU_CONSDEV 1 /* dev_t: console terminal device */ +#define CPU_MAXID 2 /* number of valid machdep ids */ + +#define CTL_MACHDEP_NAMES { \ + { 0, 0 }, \ + { "console_device", CTLTYPE_STRUCT }, \ +} + +#ifdef _KERNEL + +#include <machine/psl.h> +#include <machine/pcb.h> +#include <machine/board.h> + +/* + * definitions of cpu-dependent requirements + * referenced in generic code + */ +#define cpu_exec(p) /* nothing */ +#define cpu_wait(p) /* nothing */ +#define cpu_swapout(p) /* nothing */ + +/* + * Arguments to hardclock and gatherstats encapsulate the previous + * machine state in an opaque clockframe. CLKF_INTR is only valid + * if the process is in kernel mode. Clockframe is really trapframe, + * so pointer to clockframe can be safely cast into a pointer to + * trapframe. + */ +struct clockframe { + struct trapframe tf; +}; + +#define CLKF_USERMODE(framep) ((((struct trapframe *)(framep))->tf_epsr & PSR_MODE) == 0) +#define CLKF_PC(framep) (((struct trapframe *)(framep))->tf_sxip & XIP_ADDR) +#define CLKF_INTR(framep) (((struct trapframe *)(framep))->tf_r[31] >= UADDR) + +/* + * Get interrupt glue. + */ +#include <machine/intr.h> + +/* + * Internal IO space (iiomapsize). + * + * Internal IO space is mapped in the kernel from ``OBIO_START'' to + * ``intiolimit'' (defined in locore.s). Since it is always mapped, + * conversion between physical and kernel virtual addresses is easy. + */ + +#ifdef VIRTMAP +/* This will do non 1:1 phys/virt memory mapping in the future - SPM */ +#define ISIIOVA(va) \ + ((char *)(va) >= intiobase && (char *)(va) < intiolimit) +#define IIOV(pa) ((int)(pa)-(int)iiomapbase+(int)intiobase) +#define IIOP(va) ((int)(va)-(int)intiobase+(int)iiomapbase) +#define IIOPOFF(pa) ((int)(pa)-(int)iiomapbase) + +#else + +#define ISIIOVA(va) 1 +#define IIOV(pa) ((pa)) +#define IIOP(va) ((va)) +#define IIOPOFF(pa) ((int)(pa)-(int)OBIO_START) +#endif + +#define SIR_NET 1 +#define SIR_CLOCK 2 + +#define setsoftint(x) (ssir |= (x)) +#define setsoftnet() (ssir |= SIR_NET) +#define setsoftclock() (ssir |= SIR_CLOCK) + +#define siroff(x) (ssir &= ~x) + +extern int ssir; +extern int want_ast; + +/* + * Preempt the current process if in interrupt from user mode, + * or after the current trap/syscall if in system mode. + */ +extern int want_resched; /* resched() was called */ +#define need_resched() (want_resched = 1, want_ast = 1) + +/* + * Give a profiling tick to the current process when the user profiling + * buffer pages are invalid. On the sparc, request an ast to send us + * through trap(), marking the proc as needing a profiling tick. + */ +#define need_proftick(p) ((p)->p_flag |= P_OWEUPC, want_ast = 1) + +/* + * Notify the current process (p) that it has a signal pending, + * process as soon as possible. + */ +#define signotify(p) (want_ast = 1) + +struct intrhand { + int (*ih_fn)(void *); + void *ih_arg; + int ih_ipl; + int ih_wantframe; + struct intrhand *ih_next; +}; + +int intr_establish(int vec, struct intrhand *); + +/* + * return values for intr_establish() + */ + +#define INTR_EST_SUCC 0 +#define INTR_EST_BADVEC 1 +#define INTR_EST_BADIPL 2 + + +/* + * There are 256 possible vectors on a MVME1x7 platform (including + * onboard and VME vectors. Use intr_establish() to register a + * handler for the given vector. vector number is used to index + * into the intr_handlers[] table. + */ +extern struct intrhand *intr_handlers[256]; + +/* + * switchframe - should be double word aligned. + */ +struct switchframe { + u_int sf_pc; /* pc */ + void *sf_proc; /* proc pointer */ +}; + +/* This struct defines the machine dependent pointers */ +struct md_p { + void (*clock_init_func)(void); /* interval clock init function */ + void (*statclock_init_func)(void); /* statistics clock init function */ + void (*delayclock_init_func)(void); /* delay clock init function */ + void (*delay_func)(void); /* delay clock function */ + void (*interrupt_func)(u_int, struct trapframe *); /* interrupt func */ + u_char *volatile intr_mask; + u_char *volatile intr_ipl; + u_char *volatile intr_src; +}; + +extern struct md_p md; + +int badvaddr(vaddr_t, int); +void nmihand(void *); + +#endif /* _KERNEL */ +#endif /* __MACHINE_CPU_H__ */ diff --git a/sys/arch/luna88k/include/cpu_number.h b/sys/arch/luna88k/include/cpu_number.h new file mode 100644 index 00000000000..c8d50b3ab6b --- /dev/null +++ b/sys/arch/luna88k/include/cpu_number.h @@ -0,0 +1,47 @@ +/* $OpenBSD: cpu_number.h,v 1.1 2004/04/21 15:23:57 aoyama Exp $ */ + +/* + * Mach Operating System + * Copyright (c) 1993-1992 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +#ifndef _M88K_CPU_NUMBER_ +#define _M88K_CPU_NUMBER_ + +#ifdef _KERNEL +#ifndef _LOCORE +#include <machine/param.h> + +static unsigned cpu_number(void); + +static __inline__ unsigned cpu_number(void) +{ + unsigned cpu; + + __asm__ ("ldcr %0, cr18" : "=r" (cpu)); + return (cpu & 3); +} +#endif /* _LOCORE */ +#endif /* _KERNEL */ +#endif /* _M88K_CPU_NUMBER_ */ diff --git a/sys/arch/luna88k/include/cpus.h b/sys/arch/luna88k/include/cpus.h new file mode 100644 index 00000000000..6270f10b20a --- /dev/null +++ b/sys/arch/luna88k/include/cpus.h @@ -0,0 +1,65 @@ +/* $OpenBSD: cpus.h,v 1.1 2004/04/21 15:23:57 aoyama Exp $ */ +/* + * Mach Operating System + * Copyright (c) 1993-1992 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * + * HISTORY + */ +/* + Versions Idents for 88k family chips + */ + +#ifndef __MACHINE_CPUS_H__ +#define __MACHINE_CPUS_H__ + +/* + * cpu Processor Identification Register (PID). + */ +#ifndef _LOCORE +union cpupid { + unsigned cpupid; + struct { + unsigned + /*empty*/:16, + arc:8, + version:7, + master:1; + } m88100; + struct { + unsigned + id:8, + type:3, + version:5, + /*empty*/:16; + } m88200; +}; +#endif /* _LOCORE */ + +#define M88100_ID 0 +#define M88200_ID 5 +#define M88204_ID 6 + +#endif /* __MACHINE_CPUS_H__ */ diff --git a/sys/arch/luna88k/include/db_machdep.h b/sys/arch/luna88k/include/db_machdep.h new file mode 100644 index 00000000000..ab6e904a394 --- /dev/null +++ b/sys/arch/luna88k/include/db_machdep.h @@ -0,0 +1,167 @@ +/* $OpenBSD: db_machdep.h,v 1.1 2004/04/21 15:23:57 aoyama Exp $ */ +/* + * Mach Operating System + * Copyright (c) 1993-1991 Carnegie Mellon University + * Copyright (c) 1991 OMRON Corporation + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON AND OMRON ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON AND OMRON DISCLAIM ANY LIABILITY OF ANY KIND + * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie the + * rights to redistribute these changes. + */ + +/* + * Machine-dependent defined for the new kernel debugger + */ + +#ifndef _M88K_DB_MACHDEP_H_ +#define _M88K_DB_MACHDEP_H_ + +/* trap numbers used by ddb */ +#define DDB_ENTRY_BKPT_NO 130 +#define DDB_ENTRY_TRACE_NO 131 +#define DDB_ENTRY_TRAP_NO 132 + +#ifndef _LOCORE + +#include <machine/pcb.h> +#include <machine/trap.h> + +#include <uvm/uvm_param.h> + +/* + * The low two bits of sxip, snip, sfip have valid bits + * in them that need to masked to get the correct addresses + */ +#define PC_REGS(regs) \ + cputyp == CPU_88110 ? ((regs)->exip & ~3) : \ + (((regs)->sxip & 2) ? (regs)->sxip & ~3 : \ + ((regs)->snip & 2 ? (regs)->snip & ~3 : (regs)->sfip & ~3)) + +/* inst_return(ins) - is the instruction a function call return. + * Not mutually exclusive with inst_branch. Should be a jmp r1. */ +#define inst_return(I) (((I)&0xfffffbffU) == 0xf400c001U ? TRUE : FALSE) + +/* + * inst_call - function call predicate: is the instruction a function call. + * Could be either bsr or jsr + */ +#define inst_call(I) ({ unsigned i = (I); \ + ((((i) & 0xf8000000U) == 0xc8000000U || /*bsr*/ \ + ((i) & 0xfffffbe0U) == 0xf400c800U) /*jsr*/ \ + ? TRUE : FALSE) \ +;}) + +#ifdef DDB + +/* + * This is a hack so that mc88100 can use software single step + * and mc88110 can use the wonderful hardware single step + * feature. XXX smurph + */ +#define INTERNAL_SSTEP /* Use local Single Step routines */ + +#define BKPT_SIZE (4) /* number of bytes in bkpt inst. */ +#define BKPT_INST (0xF000D000 | DDB_ENTRY_BKPT_NO) /* tb0, 0,r0, vector 130 */ +#define BKPT_SET(inst) (BKPT_INST) + +/* Entry trap for the debugger - used for inline assembly breaks*/ +#define ENTRY_ASM "tb0 0, r0, 132" + +typedef vaddr_t db_addr_t; +typedef long db_expr_t; +typedef struct reg db_regs_t; +extern db_regs_t ddb_regs; /* register state */ +#define DDB_REGS (&ddb_regs) + +extern int db_noisy; + +unsigned inst_load(unsigned); +unsigned inst_store(unsigned); +boolean_t inst_branch(unsigned); +db_addr_t next_instr_address(db_addr_t, unsigned); +db_addr_t branch_taken(u_int, db_addr_t, db_expr_t (*)(db_regs_t *, int), + db_regs_t *); +int ddb_break_trap(int type, db_regs_t *eframe); +int ddb_entry_trap(int level, db_regs_t *eframe); + +/* breakpoint/watchpoint foo */ +#define IS_BREAKPOINT_TRAP(type,code) ((type)==T_KDB_BREAK) +#if defined(T_WATCHPOINT) +#define IS_WATCHPOINT_TRAP(type,code) ((type)==T_KDB_WATCH) +#else +#define IS_WATCHPOINT_TRAP(type,code) 0 +#endif /* T_WATCHPOINT */ + +/* we don't want coff support */ +#define DB_NO_COFF 1 + +#ifdef INTERNAL_SSTEP +db_expr_t getreg_val(db_regs_t *, int); +void db_set_single_step(db_regs_t *); +void db_clear_single_step(db_regs_t *); +#else +/* need software single step */ +#define SOFTWARE_SSTEP 1 /* we need this for mc88100 */ +#endif + +/* + * Debugger can get to any address space + */ + +#define DB_ACCESS_LEVEL DB_ACCESS_ANY + +#define DB_VALID_KERN_ADDR(addr) (!badaddr((void *)(addr), 1)) +#define DB_VALID_ADDRESS(addr,user) \ + (user ? db_check_user_addr(addr) : DB_VALID_KERN_ADDR(addr)) + +/* instruction type checking - others are implemented in db_sstep.c */ + +#define inst_trap_return(ins) ((ins) == 0xf400fc00U) + +/* don't need to load symbols */ +#define DB_SYMBOLS_PRELOADED 1 + +/* machine specific commands have been added to ddb */ +#define DB_MACHINE_COMMANDS 1 + +/* + * This routine should return true for instructions that result in unconditonal + * transfers of the flow of control. (Unconditional Jumps, subroutine calls, + * subroutine returns, etc). + * + * Trap and return from trap should not be listed here. + */ +#define inst_unconditional_flow_transfer(I) ({ unsigned i = (I); \ + ((((i) & 0xf0000000U) == 0xc0000000U || /* br, bsr */ \ + ((i) & 0xfffff3e0U) == 0xf400c000U) /* jmp, jsr */ \ + ? TRUE: FALSE) \ +;}) + +/* Return true if the instruction has a delay slot. */ +#define db_branch_is_delayed(I) inst_delayed(I) + +#define db_printf_enter db_printing + +int m88k_print_instruction(unsigned iadr, long inst); + +#endif /* DDB */ +#endif /* _LOCORE */ + +#endif /* _M88K_DB_MACHDEP_H_ */ diff --git a/sys/arch/luna88k/include/disklabel.h b/sys/arch/luna88k/include/disklabel.h new file mode 100644 index 00000000000..4ca457a73cd --- /dev/null +++ b/sys/arch/luna88k/include/disklabel.h @@ -0,0 +1,52 @@ +/* $OpenBSD: disklabel.h,v 1.1 2004/04/21 15:23:57 aoyama Exp $ */ +/* $NetBSD: disklabel.h,v 1.2 2000/01/28 02:25:24 nisimura Exp $ */ + +/* + * Copyright (c) 1994 Christopher G. Demetriou + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Christopher G. Demetriou. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _MACHINE_DISKLABEL_H_ +#define _MACHINE_DISKLABEL_H_ + +#define LABELSECTOR 0 /* sector containing label */ +#define LABELOFFSET 64 /* offset of label in sector */ +#define MAXPARTITIONS 16 /* number of partitions */ +#define RAW_PART 2 /* raw partition: xx?c */ + +/* + * This holds a copy of the whole label block, saved in here by + * readdisklabel() so that writedisklabel() can preserve the + * parts of the label block outside of the actual label. + * (i.e. Sun label info, bad block table, etc.) + */ +struct cpu_disklabel { + char cd_block[512]; +}; + +#endif /* _MACHINE_DISKLABEL_H_ */ diff --git a/sys/arch/luna88k/include/endian.h b/sys/arch/luna88k/include/endian.h new file mode 100644 index 00000000000..a42313b07e3 --- /dev/null +++ b/sys/arch/luna88k/include/endian.h @@ -0,0 +1,36 @@ +/* $OpenBSD: endian.h,v 1.1 2004/04/21 15:23:57 aoyama Exp $ */ + +/*- + * Copyright (c) 1997 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __MVME88K_ENDIAN_H__ +#define __MVME88K_ENDIAN_H__ + +#define BYTE_ORDER BIG_ENDIAN +#include <sys/endian.h> + +#define __STRICT_ALIGNMENT + +#endif /* __MVME88K_ENDIAN_H__ */ + diff --git a/sys/arch/luna88k/include/exec.h b/sys/arch/luna88k/include/exec.h new file mode 100644 index 00000000000..0ad476ff297 --- /dev/null +++ b/sys/arch/luna88k/include/exec.h @@ -0,0 +1,32 @@ +/* $OpenBSD: exec.h,v 1.1 2004/04/21 15:23:57 aoyama Exp $ */ +#ifndef __MACHINE_EXEC_H__ +#define __MACHINE_EXEC_H__ + +#define __LDPGSZ 4096 + +struct relocation_info_m88k { + unsigned int r_address; /* offset in text or data segment */ + unsigned int r_symbolnum : 24, /* ordinal number of add symbol */ + r_extern : 1, /* 1 if need to add symbol to value */ + r_baserel : 1, + r_pcrel : 1, + r_jmptable : 1, + r_type : 4; + + int r_addend; +}; +#define relocation_info relocation_info_m88k + +#define ARCH_ELFSIZE 32 + +#define ELF_TARG_CLASS ELFCLASS32 +#define ELF_TARG_DATA ELFDATA2MSB +#define ELF_TARG_MACH EM_88K + +#define _NLIST_DO_AOUT +#define _NLIST_DO_ELF + +#define _KERN_DO_AOUT +#define _KERN_DO_ELF + +#endif /* __MACHINE_EXEC_H__ */ diff --git a/sys/arch/luna88k/include/float.h b/sys/arch/luna88k/include/float.h new file mode 100644 index 00000000000..8abc3de9f17 --- /dev/null +++ b/sys/arch/luna88k/include/float.h @@ -0,0 +1,76 @@ +/* $OpenBSD: float.h,v 1.1 2004/04/21 15:23:57 aoyama Exp $ */ + +/* + * Copyright (c) 1989 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)float.h 7.1 (Berkeley) 5/8/90 + */ + +#ifndef __M88K_FLOAT_H__ +#define __M88K_FLOAT_H__ + +#include <sys/cdefs.h> + +__BEGIN_DECLS +int __flt_rounds(void); +__END_DECLS + +#define FLT_RADIX 2 /* b */ +#define FLT_ROUNDS __flt_rounds() + +#define FLT_MANT_DIG 24 /* p */ +#define FLT_EPSILON 1.19209290E-07F /* b**(1-p) */ +#define FLT_DIG 6 /* floor((p-1)*log10(b))+(b == 10) */ +#define FLT_MIN_EXP -125 /* emin */ +#define FLT_MIN 1.17549435E-38F /* b**(emin-1) */ +#define FLT_MIN_10_EXP -37 /* ceil(log10(b**(emin-1))) */ +#define FLT_MAX_EXP 128 /* emax */ +#define FLT_MAX 3.40282347E+38F /* (1-b**(-p))*b**emax */ +#define FLT_MAX_10_EXP 38 /* floor(log10((1-b**(-p))*b**emax)) */ + +#define DBL_MANT_DIG 53 +#define DBL_EPSILON 2.2204460492503131E-16 +#define DBL_DIG 15 +#define DBL_MIN_EXP -1021 +#define DBL_MIN 2.2250738585072014E-308 +#define DBL_MIN_10_EXP -307 +#define DBL_MAX_EXP 1024 +#define DBL_MAX 1.7976931348623157E+308 +#define DBL_MAX_10_EXP 308 + +#define LDBL_MANT_DIG DBL_MANT_DIG +#define LDBL_EPSILON DBL_EPSILON +#define LDBL_DIG DBL_DIG +#define LDBL_MIN_EXP DBL_MIN_EXP +#define LDBL_MIN DBL_MIN +#define LDBL_MIN_10_EXP DBL_MIN_10_EXP +#define LDBL_MAX_EXP DBL_MAX_EXP +#define LDBL_MAX DBL_MAX +#define LDBL_MAX_10_EXP DBL_MAX_10_EXP + +#endif /* __M88K_FLOAT_H__ */ diff --git a/sys/arch/luna88k/include/frame.h b/sys/arch/luna88k/include/frame.h new file mode 100644 index 00000000000..ab28bb5cf49 --- /dev/null +++ b/sys/arch/luna88k/include/frame.h @@ -0,0 +1,49 @@ +/* $OpenBSD: frame.h,v 1.1 2004/04/21 15:23:57 aoyama Exp $ */ +/* $NetBSD: frame.h,v 1.15 1997/05/03 12:49:05 mycroft Exp $ */ + +/* + * Copyright (c) 1988 University of Utah. + * Copyright (c) 1982, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * the Systems Programming Group of the University of Utah Computer + * Science Department. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: Utah $Hdr: frame.h 1.8 92/12/20$ + * + * @(#)frame.h 8.1 (Berkeley) 6/10/93 + */ + +#ifndef _M88K_FRAME_H_ +#define _M88K_FRAME_H_ + +struct frame { + struct trapframe __packed F_t; +}; + +#endif /* _M88K_FRAME_H_ */ diff --git a/sys/arch/luna88k/include/ieee.h b/sys/arch/luna88k/include/ieee.h new file mode 100644 index 00000000000..2d769d2a8a3 --- /dev/null +++ b/sys/arch/luna88k/include/ieee.h @@ -0,0 +1,142 @@ +/* $OpenBSD: ieee.h,v 1.1 2004/04/21 15:23:57 aoyama Exp $ */ +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Lawrence Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ieee.h 8.1 (Berkeley) 6/11/93 + */ +/* + * Stolen from sparc port + */ + +/* + * ieee.h defines the machine-dependent layout of the machine's IEEE + * floating point. It does *not* define (yet?) any of the rounding + * mode bits, exceptions, and so forth. + */ + +/* + * Define the number of bits in each fraction and exponent. + * + * k k+1 + * Note that 1.0 x 2 == 0.1 x 2 and that denorms are represented + * + * (-exp_bias+1) + * as fractions that look like 0.fffff x 2 . This means that + * + * -126 + * the number 0.10000 x 2 , for instance, is the same as the normalized + * + * -127 -128 + * float 1.0 x 2 . Thus, to represent 2 , we need one leading zero + * + * -129 + * in the fraction; to represent 2 , we need two, and so on. This + * + * (-exp_bias-fracbits+1) + * implies that the smallest denormalized number is 2 + * + * for whichever format we are talking about: for single precision, for + * + * -126 -149 + * instance, we get .00000000000000000000001 x 2 , or 1.0 x 2 , and + * + * -149 == -127 - 23 + 1. + */ +#ifndef __MACHINE_IEEE_H__ +#define __MACHINE_IEEE_H__ +#define SNG_EXPBITS 8 +#define SNG_FRACBITS 23 + +#define DBL_EXPBITS 11 +#define DBL_FRACBITS 52 + +#ifdef notyet +#define E80_EXPBITS 15 +#define E80_FRACBITS 64 +#endif + +#define EXT_EXPBITS 15 +#define EXT_FRACBITS 112 + +struct ieee_single { + u_int sng_sign:1; + u_int sng_exp:8; + u_int sng_frac:23; +}; + +struct ieee_double { + u_int dbl_sign:1; + u_int dbl_exp:11; + u_int dbl_frach:20; + u_int dbl_fracl; +}; + +struct ieee_ext { + u_int ext_sign:1; + u_int ext_exp:15; + u_int ext_frach:16; + u_int ext_frachm; + u_int ext_fraclm; + u_int ext_fracl; +}; + +/* + * Floats whose exponent is in [1..INFNAN) (of whatever type) are + * `normal'. Floats whose exponent is INFNAN are either Inf or NaN. + * Floats whose exponent is zero are either zero (iff all fraction + * bits are zero) or subnormal values. + * + * A NaN is a `signalling NaN' if its QUIETNAN bit is clear in its + * high fraction; if the bit is set, it is a `quiet NaN'. + */ +#define SNG_EXP_INFNAN 255 +#define DBL_EXP_INFNAN 2047 +#define EXT_EXP_INFNAN 32767 + +#if 0 +#define SNG_QUIETNAN (1 << 22) +#define DBL_QUIETNAN (1 << 19) +#define EXT_QUIETNAN (1 << 15) +#endif + +/* + * Exponent biases. + */ +#define SNG_EXP_BIAS 127 +#define DBL_EXP_BIAS 1023 +#define EXT_EXP_BIAS 16383 +#endif /* __MACHINE_IEEE_H__ */ diff --git a/sys/arch/luna88k/include/ieeefp.h b/sys/arch/luna88k/include/ieeefp.h new file mode 100644 index 00000000000..f09891419a0 --- /dev/null +++ b/sys/arch/luna88k/include/ieeefp.h @@ -0,0 +1,56 @@ +/* $OpenBSD: ieeefp.h,v 1.1 2004/04/21 15:23:57 aoyama Exp $ */ +/* + * Copyright (c) 1996 Nivas Madhur + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Nivas Madhur. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/* + * Values for fp_except are selected to match the bits in FPSR (see + * m88100 user's manual page 6-33). This file is derived from the + * definitions in the ABI/88k manual and sparc port. + * -- Nivas + */ + +#ifndef _M88K_IEEEFP_H_ +#define _M88K_IEEEFP_H_ + +typedef int fp_except; +#define FP_X_INV 0x10 /* invalid operation exception */ +#define FP_X_DZ 0x08 /* divide-by-zero exception */ +#define FP_X_UFL 0x04 /* underflow exception */ +#define FP_X_OFL 0x02 /* overflow exception */ +#define FP_X_IMP 0x01 /* imprecise (loss of precision) */ + +typedef enum { + FP_RN=0, /* round to nearest representable number */ + FP_RZ=1, /* round to zero (truncate) */ + FP_RM=2, /* round toward negative infinity */ + FP_RP=3 /* round toward positive infinity */ +} fp_rnd; + +#endif /* _M88K_IEEEFP_H_ */ diff --git a/sys/arch/luna88k/include/internal_types.h b/sys/arch/luna88k/include/internal_types.h new file mode 100644 index 00000000000..c54d7069649 --- /dev/null +++ b/sys/arch/luna88k/include/internal_types.h @@ -0,0 +1,6 @@ +/* $OpenBSD: internal_types.h,v 1.1 2004/04/21 15:23:57 aoyama Exp $ */ +/* Public domain */ +#ifndef _MACHINE_INTERNAL_TYPES_H_ +#define _MACHINE_INTERNAL_TYPES_H_ + +#endif diff --git a/sys/arch/luna88k/include/intr.h b/sys/arch/luna88k/include/intr.h new file mode 100644 index 00000000000..b7855664edc --- /dev/null +++ b/sys/arch/luna88k/include/intr.h @@ -0,0 +1,132 @@ +/* $OpenBSD: intr.h,v 1.1 2004/04/21 15:23:57 aoyama Exp $ */ +/* + * Copyright (C) 2000 Steve Murphree, Jr. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _LUNA88K_INTR_H_ +#define _LUNA88K_INTR_H_ +/* + * INTERRUPT STAT levels. for 'systat vmstat' + * intrcnt and friends are defined in locore.S + * XXX smurph + */ + +#ifndef _LOCORE + +#define M88K_NIRQ 12 + +#define M88K_SPUR_IRQ 0 +#define M88K_LEVEL1_IRQ 1 +#define M88K_LEVEL2_IRQ 2 +#define M88K_LEVEL3_IRQ 3 +#define M88K_LEVEL4_IRQ 4 +#define M88K_LEVEL5_IRQ 5 +#define M88K_LEVEL6_IRQ 6 +#define M88K_LEVEL7_IRQ 7 +/* + * We keep track of these separately, but + * they will be reflected with the above also. + */ +#define M88K_CLK_IRQ 8 +#define M88K_SCLK_IRQ 9 +#define M88K_PCLK_IRQ 10 +#define M88K_NMI_IRQ 11 + +extern int intrcnt[M88K_NIRQ]; + +#endif + +/* + * IPL levels. + */ + +#define IPL_NONE 0 +#define IPL_SOFTCLOCK 1 +#define IPL_SOFTNET 1 +#define IPL_BIO 3 +#define IPL_NET 4 +#define IPL_IMP 4 +#define IPL_TTY 5 +#define IPL_VM 5 +#define IPL_CLOCK 6 +#define IPL_STATCLOCK 6 +#define IPL_HIGH 7 +#define IPL_NMI 7 +#define IPL_ABORT 7 + +#ifdef _KERNEL +#ifndef _LOCORE +unsigned setipl(unsigned level); +unsigned raiseipl(unsigned level); +int spl0(void); + +/* needs major cleanup - XXX nivas */ + +/* SPL asserts */ +#ifdef DIAGNOSTIC +/* + * Although this function is implemented in MI code, it must be in this MD + * header because we don't want this header to include MI includes. + */ +void splassert_fail(int, int, const char *); +extern int splassert_ctl; +void splassert_check(int, const char *); +#define splassert(__wantipl) do { \ + if (__predict_false(splassert_ctl > 0)) { \ + splassert_check(__wantipl, __func__); \ + } \ +} while (0) +#else +#define splassert(wantipl) do { /* nothing */ } while (0) +#endif + +#endif /* _LOCORE */ + +#define spl1() setipl(1) +#define spl2() setipl(2) +#define spl3() setipl(3) +#define spl4() setipl(4) +#define spl5() setipl(5) +#define spl6() setipl(6) +#define spl7() setipl(7) + +#define splnone spl0 +#define spllowersoftclock() setipl(IPL_SOFTCLOCK) +#define splsoftclock() setipl(IPL_SOFTCLOCK) +#define splsoftnet() setipl(IPL_SOFTNET) +#define splbio() raiseipl(IPL_BIO) +#define splnet() raiseipl(IPL_NET) +#define spltty() raiseipl(IPL_TTY) +#define splclock() raiseipl(IPL_CLOCK) +#define splstatclock() raiseipl(IPL_STATCLOCK) +#define splimp() raiseipl(IPL_IMP) +#define splvm() raiseipl(IPL_VM) +#define splhigh() setipl(IPL_HIGH) + +#define splx(x) ((x) ? setipl((x)) : spl0()) + +#endif /* _KERNEL */ +#endif /* _LUNA88K_INTR_H_ */ diff --git a/sys/arch/luna88k/include/kcore.h b/sys/arch/luna88k/include/kcore.h new file mode 100644 index 00000000000..c2c6af6c2c6 --- /dev/null +++ b/sys/arch/luna88k/include/kcore.h @@ -0,0 +1,39 @@ +/* $OpenBSD: kcore.h,v 1.1 2004/04/21 15:23:57 aoyama Exp $ */ + +/* + * Copyright (c) 2001 Miodrag Vallat. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _MVME88K_KCORE_H_ +#define _MVME88K_KCORE_H_ + +/* Keep this define consistent with VM_PHYSSEG_MAX in <machine/vmparam.h> */ +#define NPHYS_RAM_SEGS 1 + +typedef struct cpu_kcore_hdr { + int cputype; /* board type: 187, 188, 197 */ + phys_ram_seg_t ram_segs[NPHYS_RAM_SEGS]; +} cpu_kcore_hdr_t; + +#endif /* _MVME88K_KCORE_H_ */ diff --git a/sys/arch/luna88k/include/limits.h b/sys/arch/luna88k/include/limits.h new file mode 100644 index 00000000000..1ad4a47fc77 --- /dev/null +++ b/sys/arch/luna88k/include/limits.h @@ -0,0 +1,53 @@ +/* $OpenBSD: limits.h,v 1.1 2004/04/21 15:23:57 aoyama Exp $ */ +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)limits.h 8.3 (Berkeley) 1/4/94 + */ + +#ifndef _MACHINE_LIMITS_H_ +#define _MACHINE_LIMITS_H_ + +#define MB_LEN_MAX 6 /* Allow 31 bit UTF2 */ + +#if !defined(_ANSI_SOURCE) +#define SIZE_MAX UINT_MAX /* max value for a size_t */ +#define SSIZE_MAX INT_MAX /* max value for a ssize_t */ + +#if !defined(_POSIX_SOURCE) +#define SIZE_T_MAX UINT_MAX /* max value for a size_t */ + +/* GCC requires that quad constants be written as expressions. */ +#define UQUAD_MAX ((u_quad_t)0-1) /* max value for a uquad_t */ + /* max value for a quad_t */ +#define QUAD_MAX ((quad_t)(UQUAD_MAX >> 1)) +#define QUAD_MIN (-QUAD_MAX-1) /* min value for a quad_t */ +#endif /* !_POSIX_SOURCE */ +#endif /* !_ANSI_SOURCE */ + +#endif /* _MACHINE_LIMITS_H_ */ diff --git a/sys/arch/luna88k/include/locore.h b/sys/arch/luna88k/include/locore.h new file mode 100644 index 00000000000..ab5b2368a77 --- /dev/null +++ b/sys/arch/luna88k/include/locore.h @@ -0,0 +1,84 @@ +/* $OpenBSD: locore.h,v 1.1 2004/04/21 15:23:57 aoyama Exp $ */ + +#ifndef _MACHINE_LOCORE_H_ +#define _MACHINE_LOCORE_H_ + +#include <uvm/uvm_param.h> + +/* + * C prototypes for various routines defined in locore_* and friends + */ + +/* locore_asm_routines.S */ + +unsigned int do_load_word(vaddr_t address, + boolean_t supervisor_mode); +unsigned int do_load_half(vaddr_t address, + boolean_t supervisor_mode); +unsigned int do_load_byte(vaddr_t address, + boolean_t supervisor_mode); + +void do_store_word(vaddr_t address, unsigned int data, + boolean_t supervisor_mode); +void do_store_half(vaddr_t address, unsigned int data, + boolean_t supervisor_mode); +void do_store_byte(vaddr_t address, unsigned int data, + boolean_t supervisor_mode); + +unsigned do_xmem_word(vaddr_t address, unsigned int data, + boolean_t supervisor_mode); +unsigned do_xmem_byte(vaddr_t address, unsigned int data, + boolean_t supervisor_mode); + +unsigned read_processor_identification_register(void); +int badaddr(vaddr_t addr, int size); +#define badwordaddr(x) badaddr(x, 4) +void set_cpu_number(unsigned number); +void doboot(void); + +int guarded_access(unsigned char *volatile address, + unsigned len, u_char *vec); + +/* locore_c_routines.c */ + +void dae_print(unsigned *eframe); +void data_access_emulation(unsigned *eframe); + +unsigned getipl(void); + +/* machdep.c */ + +void _doboot(void); +vaddr_t get_slave_stack(void); +void slave_pre_main(void); +int slave_main(void); +int intr_findvec(int start, int end); +void bugsyscall(void); +void myetheraddr(u_char *cp); +void dosoftint(void); +void MY_info(struct trapframe *f, caddr_t p, int flags, char *s); +void MY_info_done(struct trapframe *f, int flags); +void luna88k_bootstrap(void); +void luna88k_ext_int(u_int v, struct trapframe *eframe); +unsigned int safe_level(unsigned mask, unsigned curlevel); + +/* eh.S */ + +struct proc; +void proc_do_uret(struct proc *); +void sigsys(void); +void sigtrap(void); +void stepbpt(void); +void userbpt(void); +void syscall_handler(void); +void m88110_sigsys(void); +void m88110_sigtrap(void); +void m88110_stepbpt(void); +void m88110_userbpt(void); +void m88110_syscall_handler(void); + +/* process.S */ +void savectx(struct pcb *); +void switch_exit(struct proc *); + +#endif /* _MACHINE_LOCORE_H_ */ diff --git a/sys/arch/luna88k/include/m88100.h b/sys/arch/luna88k/include/m88100.h new file mode 100644 index 00000000000..9609924e648 --- /dev/null +++ b/sys/arch/luna88k/include/m88100.h @@ -0,0 +1,59 @@ +/* $OpenBSD: m88100.h,v 1.1 2004/04/21 15:23:57 aoyama Exp $ */ +/* + * Mach Operating System + * Copyright (c) 1993-1992 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +#ifndef __MACHINE_M88100_H__ +#define __MACHINE_M88100_H__ + +/* + * 88100 RISC definitions + */ + +/* + * DMT0, DMT1, DMT2 layout + * + * The DMT_SKIP bit is never set by the cpu. It is used to mark 'known' + * transactions so that they don't get processed a second time by + * data_access_emulation(). + */ +#define DMT_SKIP 0x00010000 /* skip this dmt */ +#define DMT_BO 0x00008000 /* Byte-Ordering */ +#define DMT_DAS 0x00004000 /* Data Access Space */ +#define DMT_DOUB1 0x00002000 /* Double Word */ +#define DMT_LOCKBAR 0x00001000 /* Bud Lock */ +#define DMT_DREG 0x00000F80 /* Destination Registers 5bits */ +#define DMT_SIGNED 0x00000040 /* Sign-Extended Bit */ +#define DMT_EN 0x0000003C /* Byte Enable Bit */ +#define DMT_WRITE 0x00000002 /* Read/Write Transaction Bit */ +#define DMT_VALID 0x00000001 /* Valid Transaction Bit */ + +#define DMT_DREGSHIFT 7 +#define DMT_ENSHIFT 2 + +#define DMT_DREGBITS(x) (((x) & DMT_DREG) >> DMT_DREGSHIFT) +#define DMT_ENBITS(x) (((x) & DMT_EN) >> DMT_ENSHIFT) + +#endif /* __MACHINE_M88100_H__ */ diff --git a/sys/arch/luna88k/include/m8820x.h b/sys/arch/luna88k/include/m8820x.h new file mode 100644 index 00000000000..94454ec7b81 --- /dev/null +++ b/sys/arch/luna88k/include/m8820x.h @@ -0,0 +1,169 @@ +/* $OpenBSD: m8820x.h,v 1.1 2004/04/21 15:23:57 aoyama Exp $ */ +/* + * Copyright (c) 2004, Miodrag Vallat. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Mach Operating System + * Copyright (c) 1993-1992 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +#ifndef __MACHINE_M8820X_H__ +#define __MACHINE_M8820X_H__ + +/* + * 8820x CMMU definitions + */ + +/* CMMU registers */ +#define CMMU_IDR (0x000 / 4) /* CMMU id register */ +#define CMMU_SCR (0x004 / 4) /* system command register */ +#define CMMU_SSR (0x008 / 4) /* system status register */ +#define CMMU_SAR (0x00c / 4) /* system address register */ +#define CMMU_SCTR (0x104 / 4) /* system control register */ +#define CMMU_PFSR (0x108 / 4) /* P bus fault status register */ +#define CMMU_PFAR (0x10c / 4) /* P bus fault address register */ +#define CMMU_SAPR (0x200 / 4) /* supervisor area pointer register */ +#define CMMU_UAPR (0x204 / 4) /* user area pointer register */ +#define CMMU_BWP0 (0x400 / 4) /* block ATC writer port 0 */ +#define CMMU_BWP1 (0x404 / 4) /* block ATC writer port 1 */ +#define CMMU_BWP2 (0x408 / 4) /* block ATC writer port 2 */ +#define CMMU_BWP3 (0x40c / 4) /* block ATC writer port 3 */ +#define CMMU_BWP4 (0x410 / 4) /* block ATC writer port 4 */ +#define CMMU_BWP5 (0x414 / 4) /* block ATC writer port 5 */ +#define CMMU_BWP6 (0x418 / 4) /* block ATC writer port 6 */ +#define CMMU_BWP7 (0x41c / 4) /* block ATC writer port 7 */ +#define CMMU_BWP(n) (CMMU_BWP0 + (n)) +#define CMMU_CDP0 (0x800 / 4) /* cache data port 0 */ +#define CMMU_CDP1 (0x804 / 4) /* cache data port 1 */ +#define CMMU_CDP2 (0x808 / 4) /* cache data port 2 */ +#define CMMU_CDP3 (0x80c / 4) /* cache data port 3 */ +#define CMMU_CTP0 (0x840 / 4) /* cache tag port 0 */ +#define CMMU_CTP1 (0x844 / 4) /* cache tag port 1 */ +#define CMMU_CTP2 (0x848 / 4) /* cache tag port 2 */ +#define CMMU_CTP3 (0x84c / 4) /* cache tag port 3 */ +#define CMMU_CSSP0 (0x880 / 4) /* cache set status register */ +#define CMMU_CSSP(n) (CMMU_CSSP0 + (n)) +/* the following only exist on 88204 */ +#define CMMU_CSSP1 (0x890 / 4) /* cache set status register */ +#define CMMU_CSSP2 (0x8a0 / 4) /* cache set status register */ +#define CMMU_CSSP3 (0x8b0 / 4) /* cache set status register */ + +/* system commands */ +#define CMMU_FLUSH_CACHE_INV_LINE 0x14 /* data cache invalidate */ +#define CMMU_FLUSH_CACHE_INV_PAGE 0x15 +#define CMMU_FLUSH_CACHE_INV_SEGMENT 0x16 +#define CMMU_FLUSH_CACHE_INV_ALL 0x17 +#define CMMU_FLUSH_CACHE_CB_LINE 0x18 /* data cache copyback */ +#define CMMU_FLUSH_CACHE_CB_PAGE 0x19 +#define CMMU_FLUSH_CACHE_CB_SEGMENT 0x1a +#define CMMU_FLUSH_CACHE_CB_ALL 0x1b +#define CMMU_FLUSH_CACHE_CBI_LINE 0x1c /* copyback and invalidate */ +#define CMMU_FLUSH_CACHE_CBI_PAGE 0x1d +#define CMMU_FLUSH_CACHE_CBI_SEGMENT 0x1e +#define CMMU_FLUSH_CACHE_CBI_ALL 0x1f +#define CMMU_PROBE_USER 0x20 /* probe user address */ +#define CMMU_PROBE_SUPER 0x24 /* probe supervisor address */ +#define CMMU_FLUSH_USER_LINE 0x30 /* flush PATC */ +#define CMMU_FLUSH_USER_PAGE 0x31 +#define CMMU_FLUSH_USER_SEGMENT 0x32 +#define CMMU_FLUSH_USER_ALL 0x33 +#define CMMU_FLUSH_SUPER_LINE 0x34 +#define CMMU_FLUSH_SUPER_PAGE 0x35 +#define CMMU_FLUSH_SUPER_SEGMENT 0x36 +#define CMMU_FLUSH_SUPER_ALL 0x37 + +/* system control values */ +#define CMMU_SCTR_PE 0x00008000 /* parity enable */ +#define CMMU_SCTR_SE 0x00004000 /* snoop enable */ +#define CMMU_SCTR_PR 0x00002000 /* priority arbitration */ + +/* P bus fault status */ +#define CMMU_PFSR_FAULT(pfsr) (((pfsr) >> 16) & 0x07) +#define CMMU_PFSR_SUCCESS 0 /* no fault */ +#define CMMU_PFSR_BERROR 3 /* bus error */ +#define CMMU_PFSR_SFAULT 4 /* segment fault */ +#define CMMU_PFSR_PFAULT 5 /* page fault */ +#define CMMU_PFSR_SUPER 6 /* supervisor violation */ +#define CMMU_PFSR_WRITE 7 /* writer violation */ + +/* CSSP values */ +#define CMMU_CSSP_L5 0x20000000 +#define CMMU_CSSP_L4 0x10000000 +#define CMMU_CSSP_L3 0x08000000 +#define CMMU_CSSP_L2 0x04000000 +#define CMMU_CSSP_L1 0x02000000 +#define CMMU_CSSP_L0 0x01000000 +#define CMMU_CSSP_D3 0x00800000 +#define CMMU_CSSP_D2 0x00400000 +#define CMMU_CSSP_D1 0x00200000 +#define CMMU_CSSP_D0 0x00100000 +#define CMMU_CSSP_VV(n,v) (((v) & 0x03) << (12 + 2 * (n))) +#define CMMU_VV_EXCLUSIVE 0x00 +#define CMMU_VV_MODIFIED 0x01 +#define CMMU_VV_SHARED 0x02 +#define CMMU_VV_INVALID 0x03 + +/* IDR values */ +#define CMMU_ID(idr) ((idr) >> 24) +#define CMMU_TYPE(idr) (((idr) >> 21) & 0x07) +#define CMMU_VERSION(idr) (((idr) >> 16) & 0x1f) +#define M88200_ID 5 +#define M88204_ID 6 + +/* SSR values */ +#define CMMU_SSR_CE 0x00008000 /* copyback error */ +#define CMMU_SSR_BE 0x00004000 /* bus error */ +#define CMMU_SSR_BH 0x00000002 /* probe BATC hit */ + +/* + * Cache line information + */ + +#define MC88200_CACHE_SHIFT 4 +#define MC88200_CACHE_LINE (1 << MC88200_CACHE_SHIFT) + +#define NBSG (1 << (PDT_BITS + PG_BITS)) /* segment size */ + +#endif /* __MACHINE_M8820X_H__ */ diff --git a/sys/arch/luna88k/include/mmu.h b/sys/arch/luna88k/include/mmu.h new file mode 100644 index 00000000000..4d35be5be6d --- /dev/null +++ b/sys/arch/luna88k/include/mmu.h @@ -0,0 +1,252 @@ +/* $OpenBSD: mmu.h,v 1.1 2004/04/21 15:23:57 aoyama Exp $ */ + +/* + * This file bears almost no resemblance to the original m68k file, + * so the following copyright notice is questionable, but we are + * nice people. + */ + +/* + * Copyright (c) 1988 University of Utah. + * Copyright (c) 1982, 1986, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * the Systems Programming Group of the University of Utah Computer + * Science Department. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: Utah $Hdr: pte.h 1.13 92/01/20$ + * + * @(#)pte.h 8.1 (Berkeley) 6/10/93 + */ + +#ifndef _MACHINE_MMU_H_ +#define _MACHINE_MMU_H_ + +/* + * Parameters which determine the 'geometry' of the m88K page tables in memory. + */ + +#define SDT_BITS 10 /* M88K segment table size bits */ +#define PDT_BITS 10 /* M88K page table size bits */ +#define PG_BITS PAGE_SHIFT /* M88K hardware page size bits */ + +/* + * Common fields for APR, SDT and PTE + */ + +/* address frame */ +#define PG_FRAME 0xfffff000 +#define PG_SHIFT PG_BITS +#define PG_PFNUM(x) (((x) & PG_FRAME) >> PG_SHIFT) + +/* cache control bits */ +#define CACHE_DFL 0x00000000 +#define CACHE_INH 0x00000040 /* cache inhibit */ +#define CACHE_GLOBAL 0x00000080 /* global scope */ +#define CACHE_WT 0x00000200 /* write through */ + +#define CACHE_MASK (CACHE_INH | CACHE_GLOBAL | CACHE_WT) + +/* + * Area descriptors + */ + +#define APR_V 0x00000001 /* valid bit */ + +/* + * 88200 PATC (TLB) + */ + +#define PATC_ENTRIES 56 + +/* + * BATC entries + */ + +#define BATC_V 0x00000001 +#define BATC_PROT 0x00000002 +#define BATC_INH 0x00000004 +#define BATC_GLOBAL 0x00000008 +#define BATC_WT 0x00000010 +#define BATC_SO 0x00000020 + + +/* + * Segment table entries + */ + +typedef u_int32_t sdt_entry_t; + +#define SDT_ENTRY_NULL ((sdt_entry_t *) 0) + +#define SG_V 0x00000001 +#define SG_NV 0x00000000 +#define SG_PROT 0x00000004 +#define SG_RO 0x00000004 +#define SG_RW 0x00000000 +#define SG_SO 0x00000100 + +#define SDT_VALID(sdt) (*(sdt) & SG_V) +#define SDT_SUP(sdt) (*(sdt) & SG_SO) +#define SDT_WP(sdt) (*(sdt) & SG_PROT) + +/* + * Page table entries + */ + +typedef u_int32_t pt_entry_t; + +#define PT_ENTRY_NULL ((pt_entry_t *) 0) + +#define PG_V 0x00000001 +#define PG_NV 0x00000000 +#define PG_PROT 0x00000004 +#define PG_U 0x00000008 +#define PG_M 0x00000010 +#define PG_RO 0x00000004 +#define PG_RW 0x00000000 +#define PG_SO 0x00000100 +#define PG_W 0x00000020 /* XXX unused but reserved field */ +#define PG_U0 0x00000400 /* U0 bit for M88110 */ +#define PG_U1 0x00000800 /* U1 bit for M88110 */ + +#define PDT_VALID(pte) (*(pte) & PG_V) +#define PDT_SUP(pte) (*(pte) & PG_SO) +#define PDT_WP(pte) (*(pte) & PG_PROT) + +/* + * Indirect descriptors (mc81110) + */ + +typedef u_int32_t pt_ind_entry_t; + +/* validity bits */ +#define IND_V 0x00000001 +#define IND_NV 0x00000000 +#define IND_MASKED 0x00000002 +#define IND_UNMASKED 0x00000003 +#define IND_MASK 0x00000003 + +#define IND_FRAME 0xfffffffc +#define IND_SHIFT 2 + +#define IND_PDA(x) ((x) & IND_FRAME >> IND_SHIFT) + +/* + * Number of entries in a page table. + */ + +#define SDT_ENTRIES (1<<(SDT_BITS)) +#define PDT_ENTRIES (1<<(PDT_BITS)) + +/* + * Size in bytes of a single page table. + */ + +#define SDT_SIZE (sizeof(sdt_entry_t) * SDT_ENTRIES) +#define PDT_SIZE (sizeof(pt_entry_t) * PDT_ENTRIES) + +/* + * Shifts and masks + */ + +#define SDT_SHIFT (PDT_BITS + PG_BITS) +#define PDT_SHIFT (PG_BITS) + +#define SDT_MASK (((1 << SDT_BITS) - 1) << SDT_SHIFT) +#define PDT_MASK (((1 << PDT_BITS) - 1) << PDT_SHIFT) + +#define SDTIDX(va) (((va) & SDT_MASK) >> SDT_SHIFT) +#define PDTIDX(va) (((va) & PDT_MASK) >> PDT_SHIFT) + +/* XXX uses knowledge of pmap structure */ +#define SDTENT(map, va) ((sdt_entry_t *)((map)->pm_stab + SDTIDX(va))) + +/* + * Va spaces mapped by tables and PDT table group. + */ + +#define PDT_VA_SPACE (PDT_ENTRIES * PAGE_SIZE) + +/* + * Number of sdt entries used to map user and kernel space. + */ + +#define USER_SDT_ENTRIES SDTIDX(VM_MIN_KERNEL_ADDRESS) +#define KERNEL_SDT_ENTRIES (SDT_ENTRIES - USER_SDT_ENTRIES) + +/* + * Parameters and macros for BATC + */ + +/* number of bits to BATC shift (log2(BATC_BLKBYTES)) */ +#define BATC_BLKSHIFT 19 +/* 'block' size of a BATC entry mapping */ +#define BATC_BLKBYTES (1 << BATC_BLKSHIFT) +/* BATC block mask */ +#define BATC_BLKMASK (BATC_BLKBYTES-1) +/* number of BATC entries */ +#define BATC_MAX 8 + +/* physical and logical block address */ +#define BATC_PSHIFT 6 +#define BATC_VSHIFT (BATC_PSHIFT + (32 - BATC_BLKSHIFT)) + +#define BATC_BLK_ALIGNED(x) ((x & BATC_BLKMASK) == 0) + +#define M88K_BTOBLK(x) (x >> BATC_BLKSHIFT) + +/* + * DMA and caching control + */ +#define DMA_CACHE_SYNC 0x1 +#define DMA_CACHE_SYNC_INVAL 0x2 +#define DMA_CACHE_INV 0x3 + +static pt_entry_t invalidate_pte(pt_entry_t *); +static __inline__ pt_entry_t +invalidate_pte(pt_entry_t *pte) +{ + pt_entry_t oldpte; + + oldpte = PG_NV; + __asm__ __volatile__ + ("xmem %0, %2, r0" : "=r"(oldpte) : "0"(oldpte), "r"(pte)); + __asm__ __volatile__ ("tb1 0, r0, 0"); + return oldpte; +} + +extern vaddr_t kmapva; + +#define kvtopte(va) \ + ((pt_entry_t *)(PG_PFNUM(*((sdt_entry_t *)kmapva + \ + SDTIDX(va) + SDT_ENTRIES)) << PDT_SHIFT) + PDTIDX(va)) + +u_int kvtop(vaddr_t); + +#endif /* __MACHINE_MMU_H__ */ diff --git a/sys/arch/luna88k/include/param.h b/sys/arch/luna88k/include/param.h new file mode 100644 index 00000000000..b4d43f2595b --- /dev/null +++ b/sys/arch/luna88k/include/param.h @@ -0,0 +1,183 @@ +/* $OpenBSD: param.h,v 1.1 2004/04/21 15:23:57 aoyama Exp $ */ +/* + * Copyright (c) 1999 Steve Murphree, Jr. + * Copyright (c) 1988 University of Utah. + * Copyright (c) 1982, 1986, 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * the Systems Programming Group of the University of Utah Computer + * Science Department. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: Utah $Hdr: machparam.h 1.11 89/08/14$ + * + * @(#)param.h 7.8 (Berkeley) 6/28/91 + */ +#ifndef _MACHINE_PARAM_H_ +#define _MACHINE_PARAM_H_ + +#ifdef _KERNEL +#ifndef _LOCORE +#include <machine/cpu.h> +#endif /* _LOCORE */ +#endif + +#define _MACHINE luna88k +#define MACHINE "luna88k" +#define _MACHINE_ARCH m88k +#define MACHINE_ARCH "m88k" +#define MID_MACHINE MID_M88K + +/* + * Round p (pointer or byte index) down to a correctly-aligned value + * for all data types (int, long, ...). The result is u_int and + * must be cast to any desired pointer type. ALIGN() is used for + * aligning stack, which needs to be on a double word boundary for + * 88k. + */ + +#define ALIGNBYTES 15 /* 64 bit alignment */ +#define ALIGN(p) (((u_int)(p) + ALIGNBYTES) & ~ALIGNBYTES) +#define ALIGNED_POINTER(p,t) ((((u_long)(p)) & (sizeof(t)-1)) == 0) + +#define NBPG (1 << PGSHIFT) /* bytes/page */ +#define PGOFSET (NBPG-1) /* byte offset into page */ +#define PGSHIFT 12 /* LOG2(NBPG) */ + +#define PAGE_SHIFT 12 +#define PAGE_SIZE (1 << PAGE_SHIFT) +#define PAGE_MASK (PAGE_SIZE - 1) + +#define NPTEPG (PAGE_SIZE / (sizeof(pt_entry_t))) + +/* + * The ROM monitor uses the bottom 128KB. The kernel will allocate PTEs to map this + * space, but the kernel must be linked with a start address past these 128KB. + */ +#define KERNBASE 0x00000000 /* start of kernel virtual */ +#define KERNTEXTOFF 0x00020000 /* start of kernel text */ + +#define DEV_BSHIFT 9 /* log2(DEV_BSIZE) */ +#define DEV_BSIZE (1 << DEV_BSHIFT) +#define BLKDEV_IOSIZE 2048 +#define MAXPHYS (64 * 1024) /* max raw I/O transfer size */ + +#define SSIZE 1 /* initial stack size/NBPG */ +#define SINCR 1 /* increment of stack/NBPG */ + +#define UPAGES 3 /* pages of u-area */ +#define USPACE (UPAGES * NBPG) + +#define UADDR 0xeee00000 /* address of u */ + +/* + * Constants related to network buffer management. + * MCLBYTES must be no larger than the software page size, and, + * on machines that exchange pages of input or output buffers with mbuf + * clusters (MAPPED_MBUFS), MCLBYTES must also be an integral multiple + * of the hardware page size. + */ +#define MSIZE 256 /* size of an mbuf */ +#define MCLSHIFT 11 /* convert bytes to m_buf clusters */ +#define MCLBYTES (1 << MCLSHIFT) /* size of a m_buf cluster */ +#define MCLOFSET (MCLBYTES - 1) /* offset within a m_buf cluster */ + +#ifndef NMBCLUSTERS +#ifdef GATEWAY +#define NMBCLUSTERS 1024 /* map size, max cluster allocation */ +#else +#define NMBCLUSTERS 512 /* map size, max cluster allocation */ +#endif +#endif + +/* + * Minimum and maximum sizes of the kernel malloc arena in PAGE_SIZE-sized + * logical pages. + */ +#define NKMEMPAGES_MIN_DEFAULT ((4 * 1024 * 1024) >> PAGE_SHIFT) +#define NKMEMPAGES_MAX_DEFAULT ((4 * 1024 * 1024) >> PAGE_SHIFT) + +#define MSGBUFSIZE PAGE_SIZE + +/* pages ("clicks") to disk blocks */ +#define ctod(x) ((x) << (PGSHIFT - DEV_BSHIFT)) +#define dtoc(x) ((x) >> (PGSHIFT - DEV_BSHIFT)) + +/* pages to bytes */ +#define ctob(x) ((x) << PGSHIFT) +#define btoc(x) (((x) + PGOFSET) >> PGSHIFT) + +/* bytes to disk blocks */ +#define btodb(x) ((x) >> DEV_BSHIFT) +#define dbtob(x) ((x) << DEV_BSHIFT) + +/* + * Map a ``block device block'' to a file system block. + * This should be device dependent, and should use the bsize + * field from the disk label. + * For now though just use DEV_BSIZE. + */ +#define bdbtofsb(bn) ((bn) / (BLKDEV_IOSIZE/DEV_BSIZE)) + +/* + * Get interrupt glue. + */ +#include <machine/intr.h> + +#ifdef _KERNEL +extern void delay(int); +#define DELAY(x) delay(x) + +extern int cputyp; +extern int brdtyp; +extern int cpumod; +#endif + +/* + * Values for the brdtyp variable. + */ +#define BRD_187 0x187 +#define BRD_188 0x188 +#define BRD_197 0x197 +#define BRD_8120 0x8120 + +/* + * Values for the cputyp variable. + */ +#define CPU_88100 0x100 +#define CPU_88110 0x110 + +/* + * Values for the cpumod variable. + */ +#define MOD_LE 0x01 +#define MOD_SP 0x02 +#define MOD_DP 0x03 + +#endif /* !_MACHINE_PARAM_H_ */ + + diff --git a/sys/arch/luna88k/include/pcb.h b/sys/arch/luna88k/include/pcb.h new file mode 100644 index 00000000000..3df7ed511e6 --- /dev/null +++ b/sys/arch/luna88k/include/pcb.h @@ -0,0 +1,149 @@ +/* $OpenBSD: pcb.h,v 1.1 2004/04/21 15:23:57 aoyama Exp $ */ +/* + * Copyright (c) 1996 Nivas Madhur + * Mach Operating System + * Copyright (c) 1993-1992 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON AND OMRON ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON AND OMRON DISCLAIM ANY LIABILITY OF ANY KIND + * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * Motorola 88100 pcb definitions + * + */ +/* + */ +#ifndef _M88K_PCB_H_ +#define _M88K_PCB_H_ + +#include <machine/reg.h> + +/* + * Our PCB is the regular PCB+Save area for kernel frame. + * Upon entering kernel mode from user land, save the user context + * in the saved_state area - this is passed as the exception frame. + * On a context switch, only registers that need to be saved by the + * C calling convention and few other regs (pc, psr etc) are saved + * in the kernel_state part of the PCB. Typically, trap frames are + * saved on the stack (by low level handlers or by hardware) but, + * we just decided to do it in the PCB. + */ + +struct m88100_pcb { + unsigned pcb_pc; /* address to return */ + unsigned pcb_ipl; + unsigned pcb_r14; + unsigned pcb_r15; + unsigned pcb_r16; + unsigned pcb_r17; + unsigned pcb_r18; + unsigned pcb_r19; + unsigned pcb_r20; + unsigned pcb_r21; + unsigned pcb_r22; + unsigned pcb_r23; + unsigned pcb_r24; + unsigned pcb_r25; + unsigned pcb_r26; + unsigned pcb_r27; + unsigned pcb_r28; + unsigned pcb_r29; + unsigned pcb_r30; + unsigned pcb_sp; /* kernel stack pointer */ + /* floating-point state */ + unsigned pcb_fcr62; + unsigned pcb_fcr63; +}; + +struct trapframe { + struct reg tf_regs; + register_t tf_vector; /* exception vector number */ + register_t tf_mask; /* interrupt mask level */ + register_t tf_mode; /* interrupt mode */ + register_t tf_scratch1; /* reserved for use by locore */ + register_t tf_ipfsr; /* P BUS status */ + register_t tf_dpfsr; /* P BUS status */ + register_t tf_cpu; /* cpu number */ +}; + +#define tf_r tf_regs.r +#define tf_sp tf_regs.r[31] +#define tf_epsr tf_regs.epsr +#define tf_fpsr tf_regs.fpsr +#define tf_fpcr tf_regs.fpcr +#define tf_sxip tf_regs.sxip +#define tf_snip tf_regs.snip +#define tf_sfip tf_regs.sfip +#define tf_exip tf_regs.sxip +#define tf_enip tf_regs.snip +#define tf_ssbr tf_regs.ssbr +#define tf_dmt0 tf_regs.dmt0 +#define tf_dmd0 tf_regs.dmd0 +#define tf_dma0 tf_regs.dma0 +#define tf_dmt1 tf_regs.dmt1 +#define tf_dmd1 tf_regs.dmd1 +#define tf_dma1 tf_regs.dma1 +#define tf_dmt2 tf_regs.dmt2 +#define tf_dmd2 tf_regs.dmd2 +#define tf_dma2 tf_regs.dma2 +#define tf_duap tf_regs.ssbr +#define tf_dsr tf_regs.dmt0 +#define tf_dlar tf_regs.dmd0 +#define tf_dpar tf_regs.dma0 +#define tf_isr tf_regs.dmt1 +#define tf_ilar tf_regs.dmd1 +#define tf_ipar tf_regs.dma1 +#define tf_isap tf_regs.dmt2 +#define tf_dsap tf_regs.dmd2 +#define tf_iuap tf_regs.dma2 +#define tf_fpecr tf_regs.fpecr +#define tf_fphs1 tf_regs.fphs1 +#define tf_fpls1 tf_regs.fpls1 +#define tf_fphs2 tf_regs.fphs2 +#define tf_fpls2 tf_regs.fpls2 +#define tf_fppt tf_regs.fppt +#define tf_fprh tf_regs.fprh +#define tf_fprl tf_regs.fprl +#define tf_fpit tf_regs.fpit + +struct pcb +{ + struct m88100_pcb kernel_state; + struct trapframe user_state; + int pcb_onfault; +}; + +/* + * Location of saved user registers for the proc. + */ +#define USER_REGS(p) \ + (((struct reg *)(&((p)->p_addr->u_pcb.user_state)))) +/* + * The pcb is augmented with machine-dependent additional data for + * core dumps. Note that the trapframe here is a copy of the one + * from the top of the kernel stack (included here so that the kernel + * stack itself need not be dumped). + */ +struct md_coredump { + struct trapframe md_tf; +}; + +#endif /* _M88K_PCB_H_ */ diff --git a/sys/arch/luna88k/include/pmap.h b/sys/arch/luna88k/include/pmap.h new file mode 100644 index 00000000000..27ba17dd1b1 --- /dev/null +++ b/sys/arch/luna88k/include/pmap.h @@ -0,0 +1,77 @@ +/* $OpenBSD: pmap.h,v 1.1 2004/04/21 15:23:57 aoyama Exp $ */ +/* + * Mach Operating System + * Copyright (c) 1991 Carnegie Mellon University + * Copyright (c) 1991 OMRON Corporation + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + */ +#ifndef _MACHINE_PMAP_H_ +#define _MACHINE_PMAP_H_ + +#include <machine/mmu.h> +#include <machine/pcb.h> + +/* + * PMAP structure + */ + +/* #define PMAP_USE_BATC */ +struct pmap { + sdt_entry_t *pm_stab; /* virtual pointer to sdt */ + u_int32_t pm_apr; + int pm_count; /* reference count */ + /* cpus using of this pmap; NCPU must be <= 32 */ + u_int32_t pm_cpus; + struct simplelock pm_lock; + struct pmap_statistics pm_stats; /* pmap statistics */ +#ifdef PMAP_USE_BATC + u_int32_t pm_ibatc[BATC_MAX]; /* instruction BATCs */ + u_int32_t pm_dbatc[BATC_MAX]; /* data BATCs */ +#endif +}; + +#define PMAP_NULL ((pmap_t) 0) + +/* The PV (Physical to virtual) List. + * + * For each vm_page_t, pmap keeps a list of all currently valid virtual + * mappings of that page. An entry is a pv_entry_t; the list is the + * pv_head_table. This is used by things like pmap_remove, when we must + * find and remove all mappings for a particular physical page. + */ +/* XXX - struct pv_entry moved to vmparam.h because of include ordering issues */ + +typedef struct pmap *pmap_t; +typedef struct pv_entry *pv_entry_t; + +#ifdef _KERNEL + +extern pmap_t kernel_pmap; +extern struct pmap kernel_pmap_store; +extern caddr_t vmmap; + +#define pmap_kernel() (&kernel_pmap_store) +#define pmap_resident_count(pmap) ((pmap)->pm_stats.resident_count) +#define pmap_wired_count(pmap) ((pmap)->pm_stats.wired_count) +#define pmap_phys_address(frame) ((paddr_t)(ptoa(frame))) + +#define pmap_copy(dp,sp,d,l,s) do { /* nothing */ } while (0) +#define pmap_update(pmap) do { /* nothing (yet) */ } while (0) + +#define pmap_clear_modify(pg) pmap_unsetbit(pg, PG_M) +#define pmap_clear_reference(pg) pmap_unsetbit(pg, PG_U) + +void pmap_bootstrap(vaddr_t, paddr_t *, paddr_t *, vaddr_t *, vaddr_t *); +void pmap_cache_ctrl(pmap_t, vaddr_t, vaddr_t, u_int); +boolean_t pmap_unsetbit(struct vm_page *, int); + +#endif /* _KERNEL */ + +#endif /* _MACHINE_PMAP_H_ */ diff --git a/sys/arch/luna88k/include/pmap_table.h b/sys/arch/luna88k/include/pmap_table.h new file mode 100644 index 00000000000..be2a91cb8b1 --- /dev/null +++ b/sys/arch/luna88k/include/pmap_table.h @@ -0,0 +1,49 @@ +/* $OpenBSD: pmap_table.h,v 1.1 2004/04/21 15:23:58 aoyama Exp $ */ +/* + * Mach Operating System + * Copyright (c) 1992 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +#ifndef __MACHINE_PMAP_TABLE_H__ +#define __MACHINE_PMAP_TABLE_H__ + +/* + * Built-in mappings list. + * An entry is considered invalid if pm_size = 0, and + * end of list is indicated by pm_size 0xffffffff + */ +typedef struct { + vaddr_t phys_start; /* in bytes */ + vaddr_t virt_start; /* in bytes */ + vsize_t size; /* in bytes */ + unsigned int prot; /* vm_prot_read, vm_prot_write */ + unsigned int cacheability; /* none, writeback, normal */ +} pmap_table_entry; + +typedef const pmap_table_entry *pmap_table_t; + +pmap_table_t pmap_table_build(void); + +#endif /* __MACHINE_PMAP_TABLE_H__ */ + diff --git a/sys/arch/luna88k/include/proc.h b/sys/arch/luna88k/include/proc.h new file mode 100644 index 00000000000..f5e679e8957 --- /dev/null +++ b/sys/arch/luna88k/include/proc.h @@ -0,0 +1,64 @@ +/* $OpenBSD: proc.h,v 1.1 2004/04/21 15:23:58 aoyama Exp $ */ +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Lawrence Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)proc.h 8.1 (Berkeley) 6/11/93 + * + * from: Header: proc.h,v 1.6 92/11/26 02:04:41 torek Exp (LBL) + */ + +#ifndef __MACHINE_PROC_H__ +#define __MACHINE_PROC_H__ + +#include <machine/pcb.h> + +/* + * Machine-dependent part of the proc structure for mvme88k. + */ +struct mdproc { + struct trapframe *md_tf; /* trap/syscall registers */ + struct fpstate *md_fpstate; /* fpu state, if any; always resident */ + int md_upte[UPAGES]; /* ptes for mapping u page */ + + unsigned md_ss_addr; /* single step address for ptrace */ + unsigned md_ss_instr; /* single step instruction for ptrace */ + unsigned md_ss_taken_addr; /* single step address for ptrace */ + unsigned md_ss_taken_instr; /* single step instruction for ptrace */ +}; + +#endif /* __MACHINE_PROC_H__ */ + diff --git a/sys/arch/luna88k/include/psl.h b/sys/arch/luna88k/include/psl.h new file mode 100644 index 00000000000..82afeafef2a --- /dev/null +++ b/sys/arch/luna88k/include/psl.h @@ -0,0 +1,93 @@ +/* $OpenBSD: psl.h,v 1.1 2004/04/21 15:23:58 aoyama Exp $ */ +/* + * Copyright (c) 1996 Nivas Madhur + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Nivas Madhur. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/* + * Mach Operating System + * Copyright (c) 1991 Carnegie Mellon University + * Copyright (c) 1991 OMRON Corporation + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + */ + +#ifndef __M88K_PSL_H__ +#define __M88K_PSL_H__ + +/* + * 88100 control registers + */ + +/* + * processor identification register (PID) + */ +#define PID_ARN 0x0000FF00U /* architectural revision number */ +#define PID_VN 0x000000FEU /* version number */ +#define PID_MC 0x00000001U /* master/checker */ + +/* + * processor status register + */ + +#define PSR_MODE 0x80000000U /* supervisor/user mode */ +#define PSR_BO 0x40000000U /* byte-ordering 0:big 1:little */ +#define PSR_SER 0x20000000U /* 88110 serial mode */ +#define PSR_C 0x10000000U /* carry */ +#define PSR_SGN 0x04000000U /* 88110 Signed Immediate mode */ +#define PSR_SRM 0x02000000U /* 88110 Serialize Memory */ +#define PSR_TRACE 0x00800000U /* 88110 hardware trace */ +#define PSR_SFD 0x000003E0U /* SFU disable */ +#define PSR_SFD2 0x00000010U /* 88110 SFU2 (Graphics) disable */ +#define PSR_SFD1 0x00000008U /* SFU1 (FPU) disable */ +#define PSR_MXM 0x00000004U /* misaligned access enable */ +#define PSR_IND 0x00000002U /* interrupt disable */ +#define PSR_SFRZ 0x00000001U /* shadow freeze */ + +#define PSR_SUPERVISOR (PSR_MODE | PSR_SFD) +#define PSR_USER (PSR_SFD) +#define PSR_SET_BY_USER (PSR_BO | PSR_SER | PSR_C | PSR_MXM) + +#define FIP_V 0x00000002U /* valid */ +#define FIP_E 0x00000001U /* exception */ +#define FIP_ADDR 0xFFFFFFFCU /* address mask */ +#define NIP_V 0x00000002U /* valid */ +#define NIP_E 0x00000001U /* exception */ +#define NIP_ADDR 0xFFFFFFFCU /* address mask */ +#define XIP_V 0x00000002U /* valid */ +#define XIP_E 0x00000001U /* exception */ +#define XIP_ADDR 0xFFFFFFFCU /* address mask */ + +#endif /* __M88K_PSL_H__ */ + diff --git a/sys/arch/luna88k/include/ptrace.h b/sys/arch/luna88k/include/ptrace.h new file mode 100644 index 00000000000..d375507d13b --- /dev/null +++ b/sys/arch/luna88k/include/ptrace.h @@ -0,0 +1,62 @@ +/* $OpenBSD: ptrace.h,v 1.1 2004/04/21 15:23:58 aoyama Exp $ */ +/* + * Copyright (c) 1999, Steve Murphree, Jr. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Lawrence Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ptrace.h 8.1 (Berkeley) 6/11/93 + * + * from: Header: ptrace.h,v 1.6 92/11/26 02:04:43 torek Exp (LBL) + */ + +/* + * m88k-dependent ptrace definitions. + */ + +#ifndef __MACHINE_PTRACE_H__ +#define __MACHINE_PTRACE_H__ + +#define PT_STEP (PT_FIRSTMACH + 0) +#define PT_GETREGS (PT_FIRSTMACH + 1) +#define PT_SETREGS (PT_FIRSTMACH + 2) +#define PT_GETFPREGS (PT_FIRSTMACH + 3) +#define PT_SETFPREGS (PT_FIRSTMACH + 4) + +#ifdef _KERNEL +int cpu_singlestep(struct proc *); +#endif + +#endif /* __MACHINE_PTRACE_H__ */ diff --git a/sys/arch/luna88k/include/reg.h b/sys/arch/luna88k/include/reg.h new file mode 100644 index 00000000000..893a32042ea --- /dev/null +++ b/sys/arch/luna88k/include/reg.h @@ -0,0 +1,90 @@ +/* $OpenBSD: reg.h,v 1.1 2004/04/21 15:23:58 aoyama Exp $ */ +/* + * Copyright (c) 1999 Steve Murphree, Jr. + * Copyright (c) 1996 Nivas Madhur + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Nivas Madhur. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _M88K_REG_H_ +#define _M88K_REG_H_ + +struct reg { + unsigned int r[32]; /* 0 - 31 */ + unsigned int epsr; /* 32 */ + unsigned int fpsr; + unsigned int fpcr; + unsigned int sxip; +#define exip sxip /* mc88110 */ + unsigned int snip; +#define enip snip /* mc88110 */ + unsigned int sfip; + unsigned int ssbr; +#define duap ssbr /* mc88110 */ + unsigned int dmt0; +#define dsr dmt0 /* mc88110 */ + unsigned int dmd0; +#define dlar dmd0 /* mc88110 */ + unsigned int dma0; +#define dpar dma0 /* mc88110 */ + unsigned int dmt1; +#define isr dmt1 /* mc88110 */ + unsigned int dmd1; +#define ilar dmd1 /* mc88110 */ + unsigned int dma1; +#define ipar dma1 /* mc88110 */ + unsigned int dmt2; +#define isap dmt2 /* mc88110 */ + unsigned int dmd2; +#define dsap dmd2 /* mc88110 */ + unsigned int dma2; +#define iuap dma2 /* mc88110 */ + unsigned int fpecr; + unsigned int fphs1; + unsigned int fpls1; + unsigned int fphs2; + unsigned int fpls2; + unsigned int fppt; + unsigned int fprh; + unsigned int fprl; + unsigned int fpit; +}; + +struct fpreg { + unsigned int fp_fpecr; + unsigned int fp_fphs1; + unsigned int fp_fpls1; + unsigned int fp_fphs2; + unsigned int fp_fpls2; + unsigned int fp_fppt; + unsigned int fp_fprh; + unsigned int fp_fprl; + unsigned int fp_fpit; +}; + +#endif /* _M88K_REG_H_ */ diff --git a/sys/arch/luna88k/include/setjmp.h b/sys/arch/luna88k/include/setjmp.h new file mode 100644 index 00000000000..aa7ab9e888a --- /dev/null +++ b/sys/arch/luna88k/include/setjmp.h @@ -0,0 +1,9 @@ +/* $OpenBSD: setjmp.h,v 1.1 2004/04/21 15:23:58 aoyama Exp $ */ + +/* + * machine/setjmp.h: machine dependent setjmp-related information. + */ +#ifndef __MACHINE_SETJMP_H__ +#define __MACHINE_SETJMP_H__ +#define _JBLEN 21 /* size, in longs, of a jmp_buf */ +#endif /* __MACHINE_SETJMP_H__ */ diff --git a/sys/arch/luna88k/include/signal.h b/sys/arch/luna88k/include/signal.h new file mode 100644 index 00000000000..abda50594f8 --- /dev/null +++ b/sys/arch/luna88k/include/signal.h @@ -0,0 +1,56 @@ +/* $OpenBSD: signal.h,v 1.1 2004/04/21 15:23:58 aoyama Exp $ */ +/* + * Copyright (c) 1996 Nivas Madhur + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Nivas Madhur. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __MACHINE_SIGNAL_H__ +#define __MACHINE_SIGNAL_H__ + +#include <machine/reg.h> + +typedef int sig_atomic_t; + +/* + * Information pushed on stack when a signal is delivered. + * This is used by the kernel to restore state following + * execution of the signal handler. It is also made available + * to the handler to allow it to restore state properly if + * a non-standard exit is performed. + * + * All machines must have an sc_onstack and sc_mask. + */ +struct sigcontext { + int sc_onstack; /* sigstack state to restore */ + int sc_mask; /* signal mask to restore */ + /* begin machine dependent portion */ + struct reg sc_regs; +}; + +#endif /* __MACHINE_SIGNAL_H__ */ diff --git a/sys/arch/luna88k/include/spinlock.h b/sys/arch/luna88k/include/spinlock.h new file mode 100644 index 00000000000..c9a2369f12f --- /dev/null +++ b/sys/arch/luna88k/include/spinlock.h @@ -0,0 +1,10 @@ +/* $OpenBSD: spinlock.h,v 1.1 2004/04/21 15:23:58 aoyama Exp $ */ + +#ifndef _M88K_SPINLOCK_H_ +#define _M88K_SPINLOCK_H_ + +#define _SPINLOCK_UNLOCKED (0) +#define _SPINLOCK_LOCKED (1) +typedef int _spinlock_lock_t; + +#endif diff --git a/sys/arch/luna88k/include/stdarg.h b/sys/arch/luna88k/include/stdarg.h new file mode 100644 index 00000000000..95f5e13ab06 --- /dev/null +++ b/sys/arch/luna88k/include/stdarg.h @@ -0,0 +1,16 @@ +/* $OpenBSD: stdarg.h,v 1.1 2004/04/21 15:23:58 aoyama Exp $ */ + +#ifndef _M88K_STDARGS_H_ +#define _M88K_STDARGS_H_ + +#include <machine/ansi.h> + +#ifndef _STDARG_H +#define _STDARG_H +#endif + +#include <machine/va-m88k.h> + +typedef _BSD_VA_LIST_ va_list; + +#endif /* _M88K_STDARGS_H_ */ diff --git a/sys/arch/luna88k/include/trap.h b/sys/arch/luna88k/include/trap.h new file mode 100644 index 00000000000..d328d37464e --- /dev/null +++ b/sys/arch/luna88k/include/trap.h @@ -0,0 +1,78 @@ +/* $OpenBSD: trap.h,v 1.1 2004/04/21 15:23:58 aoyama Exp $ */ +/* + * Mach Operating System + * Copyright (c) 1992 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * Trap codes + */ +#ifndef __MACHINE_TRAP_H__ +#define __MACHINE_TRAP_H__ + +/* + * Trap type values + */ + +#define T_RESADFLT 0 /* reserved addressing fault */ +#define T_PRIVINFLT 1 /* privileged instruction fault */ +#define T_RESOPFLT 2 /* reserved operand fault */ +#define T_INSTFLT 3 /* instruction access exception */ +#define T_DATAFLT 4 /* data access exception */ +#define T_MISALGNFLT 5 /* misaligned access exception */ +#define T_ILLFLT 6 /* unimplemented opcode exception */ +#define T_BNDFLT 7 /* bounds check violation exception */ +#define T_ZERODIV 8 /* illegal divide exception */ +#define T_OVFFLT 9 /* integer overflow exception */ +#define T_ERRORFLT 10 /* error exception */ +#define T_FPEPFLT 11 /* floating point precise exception */ +#define T_FPEIFLT 12 /* floating point imprecise exception */ +#define T_ASTFLT 13 /* software trap */ +#define T_KDB_ENTRY 14 /* force entry to kernel debugger */ +#define T_KDB_BREAK 15 /* break point hit */ +#define T_KDB_TRACE 16 /* trace */ +#define T_UNKNOWNFLT 17 /* unknown exception */ +#define T_SIGTRAP 18 /* generate SIGTRAP */ +#define T_SIGSYS 19 /* generate SIGSYS */ +#define T_STEPBPT 20 /* special breakpoint for single step */ +#define T_USERBPT 21 /* user set breakpoint (for debugger) */ +#define T_SYSCALL 22 /* Syscall */ +#define T_NON_MASK 23 /* MVME197 Non-Maskable Interrupt */ +#define T_KDB_WATCH 24 /* watchpoint hit */ +#define T_197_READ 25 /* MVME197 Data Read Miss (Software Table Searches) */ +#define T_197_WRITE 26 /* MVME197 Data Write Miss (Software Table Searches) */ +#define T_197_INST 27 /* MVME197 Inst ATC Miss (Software Table Searches) */ +#define T_INT 28 /* interrupt exception */ +#define T_USER 29 /* user mode fault */ + +#ifndef _LOCORE + +void m88100_trap(unsigned, struct trapframe *); +void m88100_syscall(register_t, struct trapframe *); + +void m88110_trap(unsigned, struct trapframe *); +void m88110_syscall(register_t, struct trapframe *); + +#endif /* _LOCORE */ + +#endif /* __MACHINE_TRAP_H__ */ diff --git a/sys/arch/luna88k/include/types.h b/sys/arch/luna88k/include/types.h new file mode 100644 index 00000000000..562e969a0ad --- /dev/null +++ b/sys/arch/luna88k/include/types.h @@ -0,0 +1,80 @@ +/* $NetBSD: types.h,v 1.7 1995/07/05 17:46:11 pk Exp $ */ +/* $OpenBSD: types.h,v 1.1 2004/04/21 15:23:58 aoyama Exp $ */ + +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Lawrence Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)types.h 8.1 (Berkeley) 6/11/93 + */ + +#ifndef _MACHTYPES_H_ +#define _MACHTYPES_H_ + +#include <sys/cdefs.h> + +#if !defined(_ANSI_SOURCE) && !defined(_POSIX_SOURCE) +typedef struct label_t { + int val[19]; +} label_t; +#endif + +typedef unsigned long vaddr_t; +typedef unsigned long paddr_t; +typedef unsigned long vsize_t; +typedef unsigned long psize_t; + +/* + * Basic integral types. Omit the typedef if + * not possible for a machine/compiler combination. + */ +#define __BIT_TYPES_DEFINED__ +typedef __signed char int8_t; +typedef unsigned char u_int8_t; +typedef unsigned char uint8_t; +typedef short int16_t; +typedef unsigned short u_int16_t; +typedef unsigned short uint16_t; +typedef int int32_t; +typedef unsigned int u_int32_t; +typedef unsigned int uint32_t; +typedef long long int64_t; +typedef unsigned long long u_int64_t; +typedef unsigned long long uint64_t; + +typedef int32_t register_t; + +#endif /* _MACHTYPES_H_ */ diff --git a/sys/arch/luna88k/include/va-m88k.h b/sys/arch/luna88k/include/va-m88k.h new file mode 100644 index 00000000000..4e1e6b58ea1 --- /dev/null +++ b/sys/arch/luna88k/include/va-m88k.h @@ -0,0 +1,82 @@ +/* $OpenBSD: va-m88k.h,v 1.1 2004/04/21 15:23:58 aoyama Exp $ */ + +/* This file has local changes by MOTOROLA +Thu Sep 9 09:06:29 CDT 1993 Dale Rahn (drahn@pacific) + * Due to C-Front's usage of __alignof__ builtin the + usage of it must be changed to have an object of that type + as the argument not just the type. + */ +/* GNU C varargs support for the Motorola 88100 */ + +/* Define __gnuc_va_list. */ + +#ifndef __GNUC_VA_LIST +#define __GNUC_VA_LIST + +typedef struct __va_list_tag { + unsigned int __va_arg; /* argument number */ + unsigned int *__va_stk; /* start of args passed on stack */ + unsigned int *__va_reg; /* start of args passed in regs */ +} __va_list[1], __gnuc_va_list[1]; + +#endif /* not __GNUC_VA_LIST */ + +/* If this is for internal libc use, don't define anything but + __gnuc_va_list. */ +#if defined (_STDARG_H) || defined (_VARARGS_H) + +#define __va_start_common(AP,FAKE) \ +__extension__ ({ \ + (AP) = (struct __va_list_tag *)__builtin_alloca(sizeof(__gnuc_va_list)); \ + __builtin_memcpy ((AP), __builtin_saveregs (), sizeof(__gnuc_va_list)); \ + }) + +#ifdef _STDARG_H /* stdarg.h support */ + +/* Calling __builtin_next_arg gives the proper error message if LASTARG is + not indeed the last argument. */ +#define va_start(AP,LASTARG) \ + (__builtin_next_arg (LASTARG), __va_start_common (AP, 0)) + +#else /* varargs.h support */ + +#define va_start(AP) __va_start_common (AP, 1) +#define va_alist __va_1st_arg +#define va_dcl register int va_alist; ... + +#endif /* _STDARG_H */ + +#define __va_reg_p(TYPE) \ + (__builtin_classify_type(*(TYPE *)0) < 12 \ + ? sizeof(TYPE) <= 8 : sizeof(TYPE) == 4 && __alignof__(*(TYPE *)0) == 4) + +#define __va_size(TYPE) ((sizeof(TYPE) + 3) >> 2) + +/* We cast to void * and then to TYPE * because this avoids + a warning about increasing the alignment requirement. */ +#define va_arg(AP,TYPE) \ + ( (AP)->__va_arg = (((AP)->__va_arg + (1 << (__alignof__(*(TYPE *)0) >> 3)) - 1) \ + & ~((1 << (__alignof__(*(TYPE *)0) >> 3)) - 1)) \ + + __va_size(TYPE), \ + *((TYPE *) (void *) ((__va_reg_p(TYPE) \ + && (AP)->__va_arg < 8 + __va_size(TYPE) \ + ? (AP)->__va_reg : (AP)->__va_stk) \ + + ((AP)->__va_arg - __va_size(TYPE))))) + +#define va_end(AP) + +/* Copy __gnuc_va_list into another variable of this type. */ +#define __va_copy(dest, src) \ +__extension__ ({ \ + (dest) = \ + (struct __va_list_tag *)__builtin_alloca(sizeof(__gnuc_va_list)); \ + *(dest) = *(src);\ + }) + +#if !defined(_ANSI_SOURCE) && \ + (!defined(_POSIX_C_SOURCE) && !defined(_XOPEN_SOURCE) || \ + defined(_ISOC99_SOURCE) || (__STDC_VERSION__ - 0) >= 199901L) +#define va_copy(dest, src) __va_copy(dest, src) +#endif + +#endif /* defined (_STDARG_H) || defined (_VARARGS_H) */ diff --git a/sys/arch/luna88k/include/varargs.h b/sys/arch/luna88k/include/varargs.h new file mode 100644 index 00000000000..f85d4737ace --- /dev/null +++ b/sys/arch/luna88k/include/varargs.h @@ -0,0 +1,13 @@ +/* $OpenBSD: varargs.h,v 1.1 2004/04/21 15:23:58 aoyama Exp $ */ + +#ifndef _M88K_VARARGS_H_ +#define _M88K_VARARGS_H_ + +#define _VARARGS_H + +#include <machine/ansi.h> +#include <machine/va-m88k.h> + +typedef _BSD_VA_LIST_ va_list; + +#endif /* _M88K_VARARGS_H_ */ diff --git a/sys/arch/luna88k/include/vmparam.h b/sys/arch/luna88k/include/vmparam.h new file mode 100644 index 00000000000..129ef221832 --- /dev/null +++ b/sys/arch/luna88k/include/vmparam.h @@ -0,0 +1,133 @@ +/* $OpenBSD: vmparam.h,v 1.1 2004/04/21 15:23:58 aoyama Exp $ */ +/* + * Mach Operating System + * Copyright (c) 1992 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +/* + * machine dependent virtual memory parameters. + */ + + +#ifndef _MACHINE_VM_PARAM_ +#define _MACHINE_VM_PARAM_ + +/* + * USRTEXT is the start of the user text/data space, while USRSTACK + * is the top (end) of the user stack. + */ +#define USRTEXT 0x1000 /* Start of user text */ +#define USRSTACK 0x80000000 /* Start of user stack */ + +/* + * Virtual memory related constants, all in bytes + */ +#ifndef MAXTSIZ +#define MAXTSIZ (8*1024*1024) /* max text size */ +#endif +#ifndef DFLDSIZ +#define DFLDSIZ (32*1024*1024) /* initial data size limit */ +#endif +#ifndef MAXDSIZ +#define MAXDSIZ (64*1024*1024) /* max data size */ +#endif +#ifndef DFLSSIZ +#define DFLSSIZ (2*1024*1024) /* initial stack size limit */ +#endif +#ifndef MAXSSIZ +#define MAXSSIZ MAXDSIZ /* max stack size */ +#endif + +/* + * Size of shared memory map + */ +#ifndef SHMMAXPGS +#define SHMMAXPGS 1024 +#endif + +/* + * The time for a process to be blocked before being very swappable. + * This is a number of seconds which the system takes as being a non-trivial + * amount of real time. You probably shouldn't change this; + * it is used in subtle ways (fractions and multiples of it are, that is, like + * half of a ``long time'', almost a long time, etc.) + * It is related to human patience and other factors which don't really + * change over time. + */ +#define MAXSLP 20 + +#define VM_MIN_ADDRESS ((vaddr_t) 0) +#define VM_MAX_ADDRESS ((vaddr_t) 0xffc00000) +#define VM_MAXUSER_ADDRESS VM_MAX_ADDRESS + +/* on vme188, max = 0xf0000000 */ + +#define VM_MIN_KERNEL_ADDRESS ((vaddr_t) 0) +#define VM_MAX_KERNEL_ADDRESS ((vaddr_t) 0x20000000) + +#define KERNEL_STACK_SIZE (3 * PAGE_SIZE) /* kernel stack size */ +#define INTSTACK_SIZE (4 * PAGE_SIZE) /* interrupt stack size */ + +/* virtual sizes (bytes) for various kernel submaps */ +#define VM_PHYS_SIZE (1 * NPTEPG * PAGE_SIZE) + +/* + * Constants which control the way the VM system deals with memory segments. + * The mvme88k only has one physical memory segment. + */ +#define VM_PHYSSEG_MAX 1 +#define VM_PHYSSEG_STRAT VM_PSTRAT_BSEARCH +#define VM_PHYSSEG_NOADD + +#define VM_NFREELIST 1 +#define VM_FREELIST_DEFAULT 0 + +#ifndef _LOCORE +/* + * pmap-specific data stored in the vm_physmem[] array. + */ + +/* XXX - belongs in pmap.h, but put here because of ordering issues */ +struct pv_entry { + struct pv_entry *pv_next; /* next pv_entry */ + struct pmap *pv_pmap; /* pmap where mapping lies */ + vaddr_t pv_va; /* virtual address for mapping */ + int pv_flags; +}; + +#define __HAVE_VM_PAGE_MD +struct vm_page_md { + struct pv_entry pvent; +}; + +#define VM_MDPAGE_INIT(pg) do { \ + (pg)->mdpage.pvent.pv_next = NULL; \ + (pg)->mdpage.pvent.pv_pmap = PMAP_NULL; \ + (pg)->mdpage.pvent.pv_va = 0; \ + (pg)->mdpage.pvent.pv_flags = 0; \ +} while (0) + +#endif /* _LOCORE */ + +#endif /* _MACHINE_VM_PARAM_ */ diff --git a/sys/arch/luna88k/luna88k/autoconf.c b/sys/arch/luna88k/luna88k/autoconf.c new file mode 100644 index 00000000000..39e645d0596 --- /dev/null +++ b/sys/arch/luna88k/luna88k/autoconf.c @@ -0,0 +1,415 @@ +/* $OpenBSD: autoconf.c,v 1.1 2004/04/21 15:23:58 aoyama Exp $ */ +/* + * Copyright (c) 1998 Steve Murphree, Jr. + * Copyright (c) 1996 Nivas Madhur + * Copyright (c) 1994 Christian E. Hopps + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Christian E. Hopps. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/buf.h> +#include <sys/dkstat.h> +#include <sys/reboot.h> +#include <sys/conf.h> +#include <sys/device.h> +#include <sys/disklabel.h> +#include <sys/kernel.h> + +#include <machine/asm_macro.h> /* enable/disable interrupts */ +#include <machine/autoconf.h> +#include <machine/cpu.h> +#include <machine/disklabel.h> +#include <machine/vmparam.h> + +#include <dev/cons.h> + +/* + * The following several variables are related to + * the configuration process, and are used in initializing + * the machine. + */ + +struct device *parsedisk(char *, int, int, dev_t *); +void setroot(void); +void swapconf(void); +void dumpconf(void); +int findblkmajor(struct device *); +struct device *getdisk(char *, int, int, dev_t *); +struct device *getdevunit(char *name, int unit); + +int cold = 1; /* 1 if still booting */ + +void *bootaddr; +int bootpart; +struct device *bootdv; /* set by device drivers (if found) */ + +/* + * called at boot time, configure all devices on the system. + */ +void +cpu_configure() +{ + + if (config_rootfound("mainbus", "mainbus") == 0) + panic("no mainbus found"); + + /* + * Turn external interrupts on. + * + * XXX We have a race here. If we enable interrupts after setroot(), + * the kernel dies. + */ + enable_interrupt(); + spl0(); + setroot(); + swapconf(); + + cold = 0; +} + +/* + * Configure swap space and related parameters. + */ +void +swapconf() +{ + struct swdevt *swp; + int nblks; + + for (swp = swdevt; swp->sw_dev != NODEV; swp++) + if (bdevsw[major(swp->sw_dev)].d_psize) { + nblks = + (*bdevsw[major(swp->sw_dev)].d_psize)(swp->sw_dev); + if (nblks != -1 && + (swp->sw_nblks == 0 || swp->sw_nblks > nblks)) + swp->sw_nblks = nblks; + swp->sw_nblks = ctod(dtoc(swp->sw_nblks)); + } + + dumpconf(); +} + +/* + * the rest of this file was adapted from Theo de Raadt's code in the + * sparc port to nuke the "options GENERIC" stuff. + */ + +struct nam2blk { + char *name; + int maj; +} nam2blk[] = { + { "sd", 4 }, + { "st", 5 }, + { "rd", 7 }, +}; + +int +findblkmajor(dv) + struct device *dv; +{ + char *name = dv->dv_xname; + int i; + + for (i = 0; i < sizeof(nam2blk)/sizeof(nam2blk[0]); ++i) + if (strncmp(name, nam2blk[i].name, strlen(nam2blk[0].name)) == 0) + return (nam2blk[i].maj); + return (-1); +} + +struct device * +getdisk(str, len, defpart, devp) + char *str; + int len, defpart; + dev_t *devp; +{ + struct device *dv; + + if ((dv = parsedisk(str, len, defpart, devp)) == NULL) { + printf("use one of:"); + for (dv = alldevs.tqh_first; dv != NULL; + dv = dv->dv_list.tqe_next) { + if (dv->dv_class == DV_DISK) + printf(" %s[a-p]", dv->dv_xname); +#ifdef NFSCLIENT + if (dv->dv_class == DV_IFNET) + printf(" %s", dv->dv_xname); +#endif + } + printf("\n"); + } + return (dv); +} + +struct device * +parsedisk(str, len, defpart, devp) + char *str; + int len, defpart; + dev_t *devp; +{ + struct device *dv; + char *cp, c; + int majdev, unit, part; + + if (len == 0) + return (NULL); + cp = str + len - 1; + c = *cp; + if (c >= 'a' && (c - 'a') < MAXPARTITIONS) { + part = c - 'a'; + *cp = '\0'; + } else + part = defpart; + + for (dv = alldevs.tqh_first; dv != NULL; dv = dv->dv_list.tqe_next) { + if (dv->dv_class == DV_DISK && + strcmp(str, dv->dv_xname) == 0) { + majdev = findblkmajor(dv); + unit = dv->dv_unit; + if (majdev < 0) + panic("parsedisk"); + *devp = MAKEDISKDEV(majdev, unit, part); + break; + } +#ifdef NFSCLIENT + if (dv->dv_class == DV_IFNET && + strcmp(str, dv->dv_xname) == 0) { + *devp = NODEV; + break; + } +#endif + } + + *cp = c; + return (dv); +} + +/* + * Attempt to find the device from which we were booted. + * If we can do so, and not instructed not to do so, + * change rootdev to correspond to the load device. + * + * XXX Actually, swap and root must be on the same type of device, + * (ie. DV_DISK or DV_IFNET) because of how (*mountroot) is written. + * That should be fixed. + */ +void +setroot() +{ + struct swdevt *swp; + struct device *dv; + int len, majdev, unit; + dev_t nrootdev, nswapdev = NODEV; + char buf[128]; + dev_t temp; +#if defined(NFSCLIENT) + extern char *nfsbootdevname; +#endif + + printf("boot device: %s\n", + (bootdv) ? bootdv->dv_xname : "<unknown>"); + + if (boothowto & RB_ASKNAME) { + for (;;) { + printf("root device "); + if (bootdv != NULL) + printf("(default %s%c)", + bootdv->dv_xname, + bootdv->dv_class == DV_DISK ? 'a' : ' '); + printf(": "); + len = getsn(buf, sizeof(buf)); + if (len == 0 && bootdv != NULL) { + strlcpy(buf, bootdv->dv_xname, sizeof buf); + len = strlen(buf); + } + if (len > 0 && buf[len - 1] == '*') { + buf[--len] = '\0'; + dv = getdisk(buf, len, 1, &nrootdev); + if (dv) { + bootdv = dv; + nswapdev = nrootdev; + goto gotswap; + } + } + dv = getdisk(buf, len, 0, &nrootdev); + if (dv) { + bootdv = dv; + break; + } + } + + /* + * because swap must be on same device as root, for + * network devices this is easy. + */ + if (bootdv->dv_class == DV_IFNET) { + goto gotswap; + } + for (;;) { + printf("swap device "); + if (bootdv != NULL) + printf("(default %s%c)", + bootdv->dv_xname, + bootdv->dv_class == DV_DISK ? 'b' : ' '); + printf(": "); + len = getsn(buf, sizeof(buf)); + if (len == 0 && bootdv != NULL) { + switch (bootdv->dv_class) { + case DV_IFNET: + nswapdev = NODEV; + break; + case DV_DISK: + nswapdev = MAKEDISKDEV(major(nrootdev), + DISKUNIT(nrootdev), 1); + break; + case DV_TAPE: + case DV_TTY: + case DV_DULL: + case DV_CPU: + break; + } + break; + } + dv = getdisk(buf, len, 1, &nswapdev); + if (dv) { + if (dv->dv_class == DV_IFNET) + nswapdev = NODEV; + break; + } + } +gotswap: + rootdev = nrootdev; + dumpdev = nswapdev; + swdevt[0].sw_dev = nswapdev; + swdevt[1].sw_dev = NODEV; + } else if (mountroot == NULL) { + /* + * `swap generic': Use the device the ROM told us to use. + */ + if (bootdv == NULL) + panic("boot device not known"); + + majdev = findblkmajor(bootdv); + if (majdev >= 0) { + /* + * Root and swap are on a disk. + * val[2] of the boot device is the partition number. + * Assume swap is on partition b. + */ + unit = bootdv->dv_unit; + rootdev = MAKEDISKDEV(majdev, unit, bootpart); + nswapdev = dumpdev = MAKEDISKDEV(major(rootdev), + DISKUNIT(rootdev), 1); + } else { + /* + * Root and swap are on a net. + */ + nswapdev = dumpdev = NODEV; + } + swdevt[0].sw_dev = nswapdev; + swdevt[1].sw_dev = NODEV; + } else { + /* + * `root DEV swap DEV': honour rootdev/swdevt. + * rootdev/swdevt/mountroot already properly set. + */ + return; + } + + switch (bootdv->dv_class) { +#if defined(NFSCLIENT) + case DV_IFNET: + mountroot = nfs_mountroot; + nfsbootdevname = bootdv->dv_xname; + return; +#endif +#if defined(FFS) + case DV_DISK: + mountroot = dk_mountroot; + majdev = major(rootdev); + unit = DISKUNIT(rootdev); + printf("root on %s%c\n", bootdv->dv_xname, + DISKPART(rootdev) + 'a'); + break; +#endif + default: + printf("can't figure root, hope your kernel is right\n"); + return; + } + + /* + * Make the swap partition on the root drive the primary swap. + */ + temp = NODEV; + for (swp = swdevt; swp->sw_dev != NODEV; swp++) { + if (majdev == major(swp->sw_dev) && + unit == DISKUNIT(swp->sw_dev)) { + temp = swdevt[0].sw_dev; + swdevt[0].sw_dev = swp->sw_dev; + swp->sw_dev = temp; + break; + } + } + if (swp->sw_dev == NODEV) + return; + + /* + * If dumpdev was the same as the old primary swap device, move + * it to the new primary swap device. + */ + if (temp == dumpdev) + dumpdev = swdevt[0].sw_dev; +} + +/* + * find a device matching "name" and unit number + */ +struct device * +getdevunit(name, unit) + char *name; + int unit; +{ + struct device *dev = alldevs.tqh_first; + char num[10], fullname[16]; + int lunit; + + /* compute length of name and decimal expansion of unit number */ + snprintf(num, sizeof num, "%d", unit); + lunit = strlen(num); + if (strlen(name) + lunit >= sizeof(fullname) - 1) + panic("config_attach: device name too long"); + + strlcpy(fullname, name, sizeof fullname); + strlcat(fullname, num, sizeof fullname); + + while (strcmp(dev->dv_xname, fullname) != 0) { + if ((dev = dev->dv_list.tqe_next) == NULL) + return NULL; + } + return dev; +} diff --git a/sys/arch/luna88k/luna88k/clock.c b/sys/arch/luna88k/luna88k/clock.c new file mode 100644 index 00000000000..475d0865b99 --- /dev/null +++ b/sys/arch/luna88k/luna88k/clock.c @@ -0,0 +1,218 @@ +/* $OpenBSD: clock.c,v 1.1 2004/04/21 15:23:58 aoyama Exp $ */ +/* $NetBSD: clock.c,v 1.2 2000/01/11 10:29:35 nisimura Exp $ */ + +/* + * Copyright (c) 1988 University of Utah. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * the Systems Programming Group of the University of Utah Computer + * Science Department and Ralph Campbell. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: Utah Hdr: clock.c 1.18 91/01/21 + * + * @(#)clock.c 8.1 (Berkeley) 6/10/93 + */ + +/* from NetBSD/luna68k sys/arch/luna68k/luna68k/clock.c */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/kernel.h> + +#include <machine/cpu.h> + +#include <dev/clock_subr.h> +#include <luna88k/luna88k/clockvar.h> + +#if 0 /* aoyama */ +#define CLOCK_LEVEL 5 +#include <luna68k/luna68k/isr.h> +#endif /* aoyama */ + +struct device *clockdev; +const struct clockfns *clockfns; +int clockinitted; + +void +clockattach(dev, fns) + struct device *dev; + const struct clockfns *fns; +{ + /* + * Just bookkeeping. + */ + if (clockfns != NULL) + panic("clockattach: multiple clocks"); + clockdev = dev; + clockfns = fns; +} + +/* + * Machine-dependent clock routines. + * + * Startrtclock restarts the real-time clock, which provides + * hardclock interrupts to kern_clock.c. + * + * Inittodr initializes the time of day hardware which provides + * date functions. Its primary function is to use some file + * system information in case the hardare clock lost state. + * + * Resettodr restores the time of day hardware after a time change. + */ + +int clock_enable; /* XXX to be removed XXX */ + +/* + * Start the real-time and statistics clocks. Leave stathz 0 since there + * are no other timers available. + */ +void +cpu_initclocks() +{ + int s; + + if (clockfns == NULL) + panic("cpu_initclocks: no clock attached"); + + tick = 1000000 / hz; /* number of microseconds between interrupts */ + tickfix = 1000000 - (hz * tick); + if (tickfix) { + int ftp; + + ftp = min(ffs(tickfix), ffs(hz)); + tickfix >>= (ftp - 1); + tickfixinterval = hz >> (ftp - 1); + } + /* + * Get the clock started. + */ + s = splhigh(); + /* + * XXX + * I guess it's necessary to program clock source with + * approprivate mode/value. + * XXX + */ + clock_enable = 1; + splx(s); +} + +/* + * We assume newhz is either stathz or profhz, and that neither will + * change after being set up above. Could recalculate intervals here + * but that would be a drag. + */ +void +setstatclockrate(newhz) + int newhz; +{ + /* nothing we can do */ +} + +/* + * Initialze the time of day register, based on the time base which is, e.g. + * from a filesystem. Base provides the time to within six months, + * and the time of year clock (if any) provides the rest. + */ +void +inittodr(base) + time_t base; +{ + struct clock_ymdhms dt; + time_t deltat; + int badbase; + + if (base < 5*SECYR) { + printf("WARNING: preposterous time in file system"); + /* read the system clock anyway */ + base = 6*SECYR + 186*SECDAY + SECDAY/2; + badbase = 1; + } else + badbase = 0; + + (*clockfns->cf_get)(clockdev, base, &dt); + clockinitted = 1; + /* simple sanity checks */ + if (dt.dt_year < 1970 || dt.dt_mon < 1 || dt.dt_mon > 12 + || dt.dt_day < 1 || dt.dt_day > 31 + || dt.dt_hour > 23 || dt.dt_min > 59 || dt.dt_sec > 59) { + /* + * Believe the time in the file system for lack of + * anything better, resetting the TODR. + */ + time.tv_sec = base; + if (!badbase) { + printf("WARNING: preposterous clock chip time"); + resettodr(); + } + goto bad; + } + /* now have days since Jan 1, 1970; the rest is easy... */ + time.tv_sec = clock_ymdhms_to_secs(&dt); + + if (!badbase) { + /* + * See if we gained/lost two or more days; + * if so, assume something is amiss. + */ + deltat = time.tv_sec - base; + if (deltat < 0) + deltat = -deltat; + if (deltat < 2 * SECDAY) + return; + printf("WARNING: clock %s %d days", + time.tv_sec < base ? "lost" : "gained", + (int) (deltat / SECDAY)); + } +bad: + printf(" -- CHECK AND RESET THE DATE!\n"); +} + +/* + * Reset the TODR based on the time value; used when the TODR + * has a preposterous value and also when the time is reset + * by the stime system call. Also called when the TODR goes past + * TODRZERO + 100*(SECYEAR+2*SECDAY) (e.g. on Jan 2 just after midnight) + * to wrap the TODR around. + */ +void +resettodr() +{ + struct clock_ymdhms dt; + + if (!clockinitted) + return; + clock_secs_to_ymdhms(time.tv_sec, &dt); + (*clockfns->cf_set)(clockdev, &dt); +} diff --git a/sys/arch/luna88k/luna88k/clockvar.h b/sys/arch/luna88k/luna88k/clockvar.h new file mode 100644 index 00000000000..128d809d4af --- /dev/null +++ b/sys/arch/luna88k/luna88k/clockvar.h @@ -0,0 +1,54 @@ +/* $OpenBSD: clockvar.h,v 1.1 2004/04/21 15:23:58 aoyama Exp $ */ +/* $NetBSD: clockvar.h,v 1.1 2000/01/05 08:49:02 nisimura Exp $ */ + +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Tohru Nishimura. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +struct clock_ymdhms; + +/* + * clockfns structure: + * + * function switch used by chip-independent clock code, to access + * chip-dependent routines. + */ +struct clockfns { + void (*cf_init)(struct device *); + void (*cf_get)(struct device *, time_t, struct clock_ymdhms *); + void (*cf_set)(struct device *, struct clock_ymdhms *); +}; + +void clockattach(struct device *, const struct clockfns *); diff --git a/sys/arch/luna88k/luna88k/cmmu.c b/sys/arch/luna88k/luna88k/cmmu.c new file mode 100644 index 00000000000..375a8241e99 --- /dev/null +++ b/sys/arch/luna88k/luna88k/cmmu.c @@ -0,0 +1,78 @@ +/* $OpenBSD: cmmu.c,v 1.1 2004/04/21 15:23:58 aoyama Exp $ */ +/* + * Copyright (c) 1998 Steve Murphree, Jr. + * Copyright (c) 1996 Nivas Madhur + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Nivas Madhur. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/* + * Mach Operating System + * Copyright (c) 1993-1991 Carnegie Mellon University + * Copyright (c) 1991 OMRON Corporation + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON AND OMRON ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON AND OMRON DISCLAIM ANY LIABILITY OF ANY KIND + * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie the + * rights to redistribute these changes. + */ + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/simplelock.h> +#include <machine/cmmu.h> +#include <machine/cpu_number.h> + +/* + * This lock protects the cmmu SAR and SCR's; other ports + * can be accessed without locking it. + * + * May be used from "db_interface.c". + */ +struct simplelock cmmu_cpu_lock; + +unsigned cpu_sets[MAX_CPUS]; +unsigned master_cpu = 0; +int max_cpus, max_cmmus; + +struct cmmu_p *cmmu; diff --git a/sys/arch/luna88k/luna88k/conf.c b/sys/arch/luna88k/luna88k/conf.c new file mode 100644 index 00000000000..4d998d97f18 --- /dev/null +++ b/sys/arch/luna88k/luna88k/conf.c @@ -0,0 +1,274 @@ +/* $OpenBSD: conf.c,v 1.1 2004/04/21 15:23:58 aoyama Exp $ */ + +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)conf.c 7.9 (Berkeley) 5/28/91 + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/buf.h> +#include <sys/ioctl.h> +#include <sys/tty.h> +#include <sys/vnode.h> +#include <sys/conf.h> + +#define mmread mmrw +#define mmwrite mmrw +cdev_decl(mm); + +#include "pty.h" +#include "bpfilter.h" +#include "tun.h" +#include "vnd.h" +#include "ccd.h" +#include "rd.h" +#include "cd.h" +#include "ch.h" +#include "sd.h" +#include "ss.h" +#include "st.h" +#include "uk.h" + +#ifdef XFS +#include <xfs/nxfs.h> +cdev_decl(xfs_dev); +#endif +#include "ksyms.h" + +#ifdef notyet +#include "xd.h" +bdev_decl(xd); +cdev_decl(xd); +#endif /* notyet */ + +#include "siotty.h" +cdev_decl(sio); + +#include "wsdisplay.h" +#include "wskbd.h" +#include "wsmouse.h" +#include "wsmux.h" + +/* open, close, write, ioctl */ +#define cdev_lp_init(c,n) { \ + dev_init(c,n,open), dev_init(c,n,close), (dev_type_read((*))) enodev, \ + dev_init(c,n,write), dev_init(c,n,ioctl), (dev_type_stop((*))) enodev, \ + 0, seltrue, (dev_type_mmap((*))) enodev } + +/* open, close, ioctl, mmap, ioctl */ +#define cdev_mdev_init(c,n) { \ + dev_init(c,n,open), dev_init(c,n,close), dev_init(c,n,read), \ + dev_init(c,n,write), dev_init(c,n,ioctl), \ + (dev_type_stop((*))) enodev, 0, (dev_type_poll((*))) enodev, \ + dev_init(c,n,mmap) } + +#if notyet +#include "lp.h" +cdev_decl(lp); +#include "lptwo.h" +cdev_decl(lptwo); +#endif /* notyet */ + +#include "pf.h" + +#include "systrace.h" + +struct bdevsw bdevsw[] = +{ + bdev_notdef(), /* 0 */ + bdev_notdef(), /* 1 */ + bdev_notdef(), /* 2 */ + bdev_swap_init(1,sw), /* 3: swap pseudo-device */ + bdev_disk_init(NSD,sd), /* 4: SCSI disk */ + bdev_tape_init(NST,st), /* 5: SCSI tape */ + bdev_disk_init(NCD,cd), /* 6: SCSI CD-ROM */ + bdev_disk_init(NRD,rd), /* 7: ramdisk */ + bdev_disk_init(NVND,vnd), /* 8: vnode disk driver */ + bdev_disk_init(NCCD,ccd), /* 9: concatenated disk driver */ + bdev_notdef(), /* 10 */ + bdev_notdef(), /* 11 */ + bdev_notdef(), /* 12 */ + bdev_lkm_dummy(), /* 13 */ + bdev_lkm_dummy(), /* 14 */ + bdev_lkm_dummy(), /* 15 */ + bdev_lkm_dummy(), /* 16 */ + bdev_lkm_dummy(), /* 17 */ + bdev_lkm_dummy(), /* 18 */ +}; +int nblkdev = sizeof(bdevsw) / sizeof(bdevsw[0]); + +struct cdevsw cdevsw[] = +{ + cdev_cn_init(1,cn), /* 0: virtual console */ + cdev_ctty_init(1,ctty), /* 1: controlling terminal */ + cdev_mm_init(1,mm), /* 2: /dev/{null,mem,kmem,...} */ + cdev_swap_init(1,sw), /* 3: /dev/drum (swap pseudo-device) */ + cdev_tty_init(NPTY,pts), /* 4: pseudo-tty slave */ + cdev_ptc_init(NPTY,ptc), /* 5: pseudo-tty master */ + cdev_log_init(1,log), /* 6: /dev/klog */ + cdev_notdef(), /* 7: */ + cdev_disk_init(NSD,sd), /* 8: SCSI disk */ + cdev_disk_init(NCD,cd), /* 9: SCSI CD-ROM */ + cdev_notdef(), /* 10 */ + cdev_notdef(), /* 11 */ + cdev_tty_init(NSIOTTY,sio), /* 12: on-board UART (ttya) */ + cdev_wsdisplay_init(NWSDISPLAY, /* 13: frame buffers, etc. */ + wsdisplay), + cdev_mouse_init(NWSKBD,wskbd), /* 14: keyboard */ + cdev_mouse_init(NWSMOUSE, /* 15: mouse */ + wsmouse), + cdev_mouse_init(NWSMUX,wsmux), /* 16: ws multiplexor */ + cdev_disk_init(NCCD,ccd), /* 17: concatenated disk */ + cdev_disk_init(NRD,rd), /* 18: ramdisk disk */ + cdev_disk_init(NVND,vnd), /* 19: vnode disk */ + cdev_tape_init(NST,st), /* 20: SCSI tape */ + cdev_fd_init(1,filedesc), /* 21: file descriptor pseudo-dev */ + cdev_bpftun_init(NBPFILTER,bpf),/* 22: berkeley packet filter */ + cdev_bpftun_init(NTUN,tun), /* 23: network tunnel */ + cdev_lkm_init(NLKM,lkm), /* 24: loadable module driver */ + cdev_notdef(), /* 25 */ + cdev_notdef(), /* 26 */ + cdev_notdef(), /* 27 */ + cdev_notdef(), /* 28 */ + cdev_notdef(), /* 29 */ + cdev_notdef(), /* 30 */ + cdev_notdef(), /* 31 */ + cdev_notdef(), /* 32 */ + cdev_lkm_dummy(), /* 33 */ + cdev_lkm_dummy(), /* 34 */ + cdev_lkm_dummy(), /* 35 */ + cdev_lkm_dummy(), /* 36 */ + cdev_lkm_dummy(), /* 37 */ + cdev_lkm_dummy(), /* 38 */ + cdev_pf_init(NPF,pf), /* 39: packet filter */ + cdev_random_init(1,random), /* 40: random data source */ + cdev_uk_init(NUK,uk), /* 41 */ + cdev_ss_init(NSS,ss), /* 42 */ + cdev_ksyms_init(NKSYMS,ksyms), /* 43: Kernel symbols device */ + cdev_ch_init(NCH,ch), /* 44: SCSI autochanger */ + cdev_notdef(), /* 45 */ + cdev_notdef(), /* 46 */ + cdev_notdef(), /* 47 */ + cdev_notdef(), /* 48 */ + cdev_notdef(), /* 49 */ + cdev_systrace_init(NSYSTRACE,systrace), /* 50 system call tracing */ +#ifdef XFS + cdev_xfs_init(NXFS,xfs_dev), /* 51: xfs communication device */ +#else + cdev_notdef(), /* 51 */ +#endif + cdev_ptm_init(NPTY,ptm), /* 52: pseudo-tty ptm device */ +}; +int nchrdev = sizeof(cdevsw) / sizeof(cdevsw[0]); + +int mem_no = 2; /* major device number of memory special file */ + +/* + * Swapdev is a fake device implemented + * in sw.c used only internally to get to swstrategy. + * It cannot be provided to the users, because the + * swstrategy routine munches the b_dev and b_blkno entries + * before calling the appropriate driver. This would horribly + * confuse, e.g. the hashing routines. Instead, /dev/drum is + * provided as a character (raw) device. + */ +dev_t swapdev = makedev(3, 0); + +/* + * Returns true if dev is /dev/mem or /dev/kmem. + */ +int +iskmemdev(dev) + dev_t dev; +{ + + return (major(dev) == mem_no && minor(dev) < 2); +} + +/* + * Returns true if dev is /dev/zero. + */ +int +iszerodev(dev) + dev_t dev; +{ + + return (major(dev) == mem_no && minor(dev) == 12); +} + +dev_t +getnulldev() +{ + return makedev(mem_no, 2); +} + +int chrtoblktbl[] = { + /* XXXX This needs to be dynamic for LKMs. */ + /*VCHR*/ /*VBLK*/ + /* 0 */ NODEV, + /* 1 */ NODEV, + /* 2 */ NODEV, + /* 3 */ NODEV, + /* 4 */ NODEV, + /* 5 */ NODEV, + /* 6 */ NODEV, + /* 7 */ NODEV, + /* 8 */ 4, /* SCSI disk */ + /* 9 */ 6, /* SCSI CD-ROM */ + /* 10 */ NODEV, + /* 11 */ NODEV, + /* 12 */ NODEV, + /* 13 */ NODEV, + /* 14 */ NODEV, + /* 15 */ NODEV, + /* 16 */ NODEV, + /* 17 */ NODEV, + /* 18 */ 7, /* ram disk */ + /* 19 */ 8, /* vnode disk */ +}; +int nchrtoblktbl = sizeof(chrtoblktbl) / sizeof(chrtoblktbl[0]); + +/* + * This entire table could be autoconfig()ed but that would mean that + * the kernel's idea of the console would be out of sync with that of + * the standalone boot. I think it best that they both use the same + * known algorithm unless we see a pressing need otherwise. + */ +#include <dev/cons.h> + +#define romttycnpollc nullcnpollc +cons_decl(romtty); + +struct consdev constab[] = { +#if 0 + cons_init(romtty), +#endif + { 0 }, +}; diff --git a/sys/arch/luna88k/luna88k/disksubr.c b/sys/arch/luna88k/luna88k/disksubr.c new file mode 100644 index 00000000000..7e13d9f0fba --- /dev/null +++ b/sys/arch/luna88k/luna88k/disksubr.c @@ -0,0 +1,526 @@ +/* $OpenBSD: disksubr.c,v 1.1 2004/04/21 15:23:59 aoyama Exp $ */ +/* $NetBSD: disksubr.c,v 1.12 2002/02/19 17:09:44 wiz Exp $ */ + +/* + * Copyright (c) 1994, 1995 Gordon W. Ross + * Copyright (c) 1994 Theo de Raadt + * Copyright (c) 1982, 1986, 1988 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Credits: + * This file was based mostly on the i386/disksubr.c file: + * @(#)ufs_disksubr.c 7.16 (Berkeley) 5/4/91 + * The functions: disklabel_sun_to_bsd, disklabel_bsd_to_sun + * were originally taken from arch/sparc/scsi/sun_disklabel.c + * (which was written by Theo de Raadt) and then substantially + * rewritten by Gordon W. Ross. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/buf.h> +#include <sys/device.h> +#include <sys/disklabel.h> +#include <sys/disk.h> +#include <sys/dkbad.h> + +#include <dev/sun/disklabel.h> + +/* + * UniOS disklabel (== ISI disklabel) is very similar to SunOS. + * SunOS UniOS/ISI + * text 128 128 + * (pad) 292 294 + * rpm 2 - + * pcyl 2 badchk 2 + * sparecyl 2 maxblk 4 + * (pad) 4 dtype 2 + * interleave 2 ndisk 2 + * ncyl 2 2 + * acyl 2 2 + * ntrack 2 2 + * nsect 2 2 + * (pad) 4 bhead 2 + * - ppart 2 + * dkpart[8] 64 64 + * magic 2 2 + * cksum 2 2 + * + * Magic number value and checksum calculation are identical. Subtle + * difference is partition start address; UniOS/ISI maintains sector + * numbers while SunOS label has cylinder number. + * + * It is found that LUNA Mach2.5 has BSD label embedded at offset 64 + * retaining UniOS/ISI label at the end of label block. LUNA Mach + * manipulates BSD disklabel in the same manner as 4.4BSD. It's + * uncertain LUNA Mach can create a disklabel on fresh disks since + * Mach writedisklabel logic seems to fail when no BSD label is found. + * + * Kernel handles disklabel in this way; + * - searchs BSD label at offset 64 + * - if not found, searchs UniOS/ISI label at the end of block + * - kernel can distinguish whether it was SunOS label or UniOS/ISI + * label and understand both + * - kernel writes UniOS/ISI label combined with BSD label to update + * the label block + */ + +#if LABELSECTOR != 0 +#error "Default value of LABELSECTOR no longer zero?" +#endif + +char *disklabel_om_to_bsd(char *, struct disklabel *); +int disklabel_bsd_to_om(struct disklabel *, char *); + +/* + * Attempt to read a disk label from a device + * using the indicated strategy routine. + * The label must be partly set up before this: + * secpercyl, secsize and anything required for a block i/o read + * operation in the driver's strategy/start routines + * must be filled in before calling us. + * + * Return buffer for use in signalling errors if requested. + * + * Returns null on success and an error string on failure. + */ +char * +readdisklabel(dev, strat, lp, clp, spoofonly) + dev_t dev; + void (*strat)(struct buf *); + struct disklabel *lp; + struct cpu_disklabel *clp; + int spoofonly; +{ + struct buf *bp; + struct disklabel *dlp; + struct sun_disklabel *slp; + int error; + + /* minimal requirements for archtypal disk label */ + if (lp->d_secperunit == 0) + lp->d_secperunit = 0x1fffffff; + lp->d_npartitions = 1; + if (lp->d_partitions[0].p_size == 0) + lp->d_partitions[0].p_size = 0x1fffffff; + lp->d_partitions[0].p_offset = 0; + + /* don't read the on-disk label if we are in spoofed-only mode */ + if (spoofonly) + return (NULL); + + /* obtain buffer to probe drive with */ + bp = geteblk((int)lp->d_secsize); + + /* next, dig out disk label */ + bp->b_dev = dev; + bp->b_blkno = LABELSECTOR; + bp->b_cylinder = 0; + bp->b_bcount = lp->d_secsize; + bp->b_flags |= B_READ; + (*strat)(bp); + + /* if successful, locate disk label within block and validate */ + error = biowait(bp); + if (!error) { + /* Save the whole block in case it has info we need. */ + bcopy(bp->b_data, clp->cd_block, sizeof(clp->cd_block)); + } + brelse(bp); + if (error) + return ("disk label read error"); + + /* Check for a BSD disk label first. */ + dlp = (struct disklabel *)(clp->cd_block + LABELOFFSET); + if (dlp->d_magic == DISKMAGIC && dlp->d_magic2 == DISKMAGIC) { + if (dkcksum(dlp) == 0) { + *lp = *dlp; /* struct assignment */ + return (NULL); + } + printf("BSD disk label corrupted"); + } + + /* Check for a UniOS/ISI disk label. */ + slp = (struct sun_disklabel *)clp->cd_block; + if (slp->sl_magic == SUN_DKMAGIC) { + return (disklabel_om_to_bsd(clp->cd_block, lp)); + } + + memset(clp->cd_block, 0, sizeof(clp->cd_block)); + return ("no disk label"); +} + +/* + * Check new disk label for sensibility + * before setting it. + */ +int +setdisklabel(olp, nlp, openmask, clp) + struct disklabel *olp, *nlp; + u_long openmask; + struct cpu_disklabel *clp; +{ + struct partition *opp, *npp; + int i; + + /* sanity clause */ + if ((nlp->d_secpercyl == 0) || (nlp->d_secsize == 0) || + (nlp->d_secsize % DEV_BSIZE) != 0) + return (EINVAL); + + /* special case to allow disklabel to be invalidated */ + if (nlp->d_magic == 0xffffffff) { + *olp = *nlp; + return (0); + } + + if (nlp->d_magic != DISKMAGIC || + nlp->d_magic2 != DISKMAGIC || + dkcksum(nlp) != 0) + return (EINVAL); + + while (openmask != 0) { + i = ffs(openmask) - 1; + openmask &= ~(1 << i); + if (nlp->d_npartitions <= i) + return (EBUSY); + opp = &olp->d_partitions[i]; + npp = &nlp->d_partitions[i]; + if (npp->p_offset != opp->p_offset || + npp->p_size < opp->p_size) + return (EBUSY); + } + + /* We did not modify the new label, so the checksum is OK. */ + *olp = *nlp; + return (0); +} + + +/* + * Write disk label back to device after modification. + * Current label is already in clp->cd_block[] + */ +int +writedisklabel(dev, strat, lp, clp) + dev_t dev; + void (*strat)(struct buf *); + struct disklabel *lp; + struct cpu_disklabel *clp; +{ + struct buf *bp; + struct disklabel *dlp; + int error; + + /* implant NetBSD disklabel at LABELOFFSET. */ + dlp = (struct disklabel *)(clp->cd_block + LABELOFFSET); + *dlp = *lp; /* struct assignment */ + + error = disklabel_bsd_to_om(lp, clp->cd_block); + if (error) + return (error); + + /* Get a buffer and copy the new label into it. */ + bp = geteblk((int)lp->d_secsize); + bcopy(clp->cd_block, bp->b_data, sizeof(clp->cd_block)); + + /* Write out the updated label. */ + bp->b_dev = dev; + bp->b_blkno = LABELSECTOR; + bp->b_cylinder = 0; + bp->b_bcount = lp->d_secsize; + bp->b_flags |= B_WRITE; + (*strat)(bp); + error = biowait(bp); + brelse(bp); + + return (error); +} + +/* + * Determine the size of the transfer, and make sure it is + * within the boundaries of the partition. Adjust transfer + * if needed, and signal errors or early completion. + */ +int +bounds_check_with_label(bp, lp, osdep, wlabel) + struct buf *bp; + struct disklabel *lp; + struct cpu_disklabel *osdep; + int wlabel; +{ + struct partition *p; + int sz, maxsz; + + p = lp->d_partitions + DISKPART(bp->b_dev); + maxsz = p->p_size; + sz = (bp->b_bcount + DEV_BSIZE - 1) >> DEV_BSHIFT; + + /* overwriting disk label ? */ + /* XXX should also protect bootstrap in first 8K */ + /* XXX PR#2598: labelsect is always sector zero. */ + if (((bp->b_blkno + p->p_offset) <= LABELSECTOR) && + ((bp->b_flags & B_READ) == 0) && (wlabel == 0)) + { + bp->b_error = EROFS; + goto bad; + } + + /* beyond partition? */ + if (bp->b_blkno < 0 || bp->b_blkno + sz > maxsz) { + /* if exactly at end of disk, return an EOF */ + if (bp->b_blkno == maxsz) { + bp->b_resid = bp->b_bcount; + return (0); + } + /* or truncate if part of it fits */ + sz = maxsz - bp->b_blkno; + if (sz <= 0) { + bp->b_error = EINVAL; + goto bad; + } + bp->b_bcount = sz << DEV_BSHIFT; + } + + /* calculate cylinder for disksort to order transfers with */ + bp->b_cylinder = (bp->b_blkno + p->p_offset) / lp->d_secpercyl; + return (1); + +bad: + bp->b_flags |= B_ERROR; + return (-1); +} + +void +dk_establish(dk, dev) + struct disk *dk; + struct device *dev; +{ +#if 0 /* taken from OpenBSD/mvme88k */ + struct scsibus_softc *sbsc; + int target, lun; + + if (bootpart == -1) /* ignore flag from controller driver? */ + return; + + /* + * scsi: sd,cd + */ + + if (strncmp("sd", dev->dv_xname, 2) == 0 || + strncmp("cd", dev->dv_xname, 2) == 0) { + + sbsc = (struct scsibus_softc *)dev->dv_parent; + target = get_target(); /* Work the Motorola Magic */ + lun = 0; + + if (sbsc->sc_link[target][lun] != NULL && + sbsc->sc_link[target][lun]->device_softc == (void *)dev) { + bootdv = dev; + return; + } + } +#endif +} + +/************************************************************************ + * + * The rest of this was taken from arch/sparc/scsi/sun_disklabel.c + * and then substantially rewritten by Gordon W. Ross + * + ************************************************************************/ + +/* What partition types to assume for Sun disklabels: */ +static const u_char +sun_fstypes[8] = { + FS_BSDFFS, /* a */ + FS_SWAP, /* b */ + FS_OTHER, /* c - whole disk */ + FS_BSDFFS, /* d */ + FS_BSDFFS, /* e */ + FS_BSDFFS, /* f */ + FS_BSDFFS, /* g */ + FS_BSDFFS, /* h */ +}; + +/* + * Given a UniOS/ISI disk label, set lp to a BSD disk label. + * Returns NULL on success, else an error string. + * + * The BSD label is cleared out before this is called. + */ +char * +disklabel_om_to_bsd(cp, lp) + char *cp; + struct disklabel *lp; +{ + struct sun_disklabel *sl; + struct partition *npp; + struct sun_dkpart *spp; + int i, secpercyl; + u_short cksum, *sp1, *sp2; + + sl = (struct sun_disklabel *)cp; + + /* Verify the XOR check. */ + sp1 = (u_short *)sl; + sp2 = (u_short *)(sl + 1); + cksum = 0; + while (sp1 < sp2) + cksum ^= *sp1++; + if (cksum != 0) + return ("UniOS disk label, bad checksum"); + + memset((caddr_t)lp, 0, sizeof(struct disklabel)); + /* Format conversion. */ + lp->d_magic = DISKMAGIC; + lp->d_magic2 = DISKMAGIC; + memcpy(lp->d_packname, sl->sl_text, sizeof(lp->d_packname)); + + lp->d_type = DTYPE_SCSI; + lp->d_secsize = 512; + lp->d_nsectors = sl->sl_nsectors; + lp->d_ntracks = sl->sl_ntracks; + lp->d_ncylinders = sl->sl_ncylinders; + + secpercyl = sl->sl_nsectors * sl->sl_ntracks; + lp->d_secpercyl = secpercyl; + lp->d_secperunit = secpercyl * sl->sl_ncylinders; + + lp->d_sparespercyl = 0; /* no way to know */ + lp->d_acylinders = sl->sl_acylinders; + lp->d_rpm = sl->sl_rpm; /* UniOS - (empty) */ + lp->d_interleave = sl->sl_interleave; /* UniOS - ndisk */ + + if (sl->sl_rpm == 0) { + /* UniOS label has blkoffset, not cyloffset */ + secpercyl = 1; + } + + lp->d_npartitions = 8; + /* These are as defined in <ufs/ffs/fs.h> */ + lp->d_bbsize = 8192; /* XXX */ + lp->d_sbsize = 8192; /* XXX */ + for (i = 0; i < 8; i++) { + spp = &sl->sl_part[i]; + npp = &lp->d_partitions[i]; + npp->p_offset = spp->sdkp_cyloffset * secpercyl; + npp->p_size = spp->sdkp_nsectors; + if (npp->p_size == 0) + npp->p_fstype = FS_UNUSED; + else { + /* Partition has non-zero size. Set type, etc. */ + npp->p_fstype = sun_fstypes[i]; + + /* + * The sun label does not store the FFS fields, + * so just set them with default values here. + * XXX: This keeps newfs from trying to rewrite + * XXX: the disk label in the most common case. + * XXX: (Should remove that code from newfs...) + */ + if (npp->p_fstype == FS_BSDFFS) { + npp->p_fsize = 1024; + npp->p_frag = 8; + npp->p_cpg = 16; + } + } + } + + /* + * XXX BandAid XXX + * UniOS rootfs sits on part c which don't begin at sect 0, + * and impossible to mount. Thus, make it usable as part b. + */ + if (sl->sl_rpm == 0 && lp->d_partitions[2].p_offset != 0) { + lp->d_partitions[1] = lp->d_partitions[2]; + lp->d_partitions[1].p_fstype = FS_BSDFFS; + } + + lp->d_checksum = dkcksum(lp); + + return (NULL); +} + +/* + * Given a BSD disk label, update the UniOS disklabel + * pointed to by cp with the new info. Note that the + * UniOS disklabel may have other info we need to keep. + * Returns zero or error code. + */ +int +disklabel_bsd_to_om(lp, cp) + struct disklabel *lp; + char *cp; +{ + struct sun_disklabel *sl; + struct partition *npp; + struct sun_dkpart *spp; + int i; + u_short cksum, *sp1, *sp2; + + if (lp->d_secsize != 512) + return (EINVAL); + + sl = (struct sun_disklabel *)cp; + + /* Format conversion. */ + memcpy(sl->sl_text, lp->d_packname, sizeof(lp->d_packname)); + sl->sl_rpm = 0; /* UniOS */ +#if 0 /* leave as was */ + sl->sl_pcyl = lp->d_ncylinders + lp->d_acylinders; /* XXX */ + sl->sl_sparespercyl = lp->d_sparespercyl; +#endif + sl->sl_interleave = lp->d_interleave; + sl->sl_ncylinders = lp->d_ncylinders; + sl->sl_acylinders = lp->d_acylinders; + sl->sl_ntracks = lp->d_ntracks; + sl->sl_nsectors = lp->d_nsectors; + + for (i = 0; i < 8; i++) { + spp = &sl->sl_part[i]; + npp = &lp->d_partitions[i]; + + spp->sdkp_cyloffset = npp->p_offset; /* UniOS */ + spp->sdkp_nsectors = npp->p_size; + } + sl->sl_magic = SUN_DKMAGIC; + + /* Correct the XOR check. */ + sp1 = (u_short *)sl; + sp2 = (u_short *)(sl + 1); + sl->sl_cksum = cksum = 0; + while (sp1 < sp2) + cksum ^= *sp1++; + sl->sl_cksum = cksum; + + return (0); +} diff --git a/sys/arch/luna88k/luna88k/eh.S b/sys/arch/luna88k/luna88k/eh.S new file mode 100644 index 00000000000..2b205580d3c --- /dev/null +++ b/sys/arch/luna88k/luna88k/eh.S @@ -0,0 +1,2709 @@ +/* $OpenBSD: eh.S,v 1.1 2004/04/21 15:24:00 aoyama Exp $ */ +/* + * Mach Operating System + * Copyright (c) 1993-1991 Carnegie Mellon University + * Copyright (c) 1991 OMRON Corporation + * Copyright (c) 1996 Nivas Madhur + * Copyright (c) 1998 Steve Murphree, Jr. + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON AND OMRON ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON AND OMRON DISCLAIM ANY LIABILITY OF ANY KIND + * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie the + * rights to redistribute these changes. + */ + +/* + * In the following discussion, references are made to: + * MC88100 - RISC MICROPROCESSOR USER'S MANUAL + * (second edition). Reference in []s refer to section numbers. + * + * This discussion assumes that you are at least vaguely familiar with 88100 + * exception handling (chapter 6), the MACH kernel, and that you have a brain + * (and use it while reading this). + * + * I also assume (and hope) that you're not offended by frequent misspellings. + * + * Jeffrey Friedl + * jfriedl@rna.ncl.omron.co.jp + * December, 1989 + * ------------------------------------------------------------------- + * + * EXCEPTIONS, INTERRUPTS, and TRAPS + * --------------------------------- + * This is the machine exception handler. + * In the MC88100, various "conditions" cause an exception, where + * processing momentarily jumps here to "service" the exception, + * and then continues where it left off. + * + * There are a number of different types of exceptions. + * For example, exception #6 is the privilege violation exception which + * is raised when the user tries to execute a supervisor-only instruction. + * + * Exception #1 is the interrupt exception, and is raised when an + * outside device raises the INT line on the CPU. This happens, + * for example, when the clock signals that it is time for a context + * switch, or perhaps the disk drive signaling that some operation + * is complete. + * + * Traps are also exceptions. Traps are ways for user programs to request + * kernel operations. For example, "tcnd eq0, r0, 128" will raise + * exception 128, the system call exception. + * + * + * SERVICING AN EXCEPTION + * ----------------------- + * When an exception occurs, each control register is saved in its + * respective shadow register and execution continues from the + * appropriate exception handler. The exception handler must + * - save the context from the time of the exception + * - service the exception + * - restore the context (registers, etc) + * - pick up from where the exception occurred. + * + * The context is saved on a stack. Actually, in the user_state area + * in the PCB if the exception happens in user mode. + * + * Servicing the exception is usually straightforward and in fact not dealt + * with very much here. Usually a C routine is called to handle it. + * For example, when a privilege exception is raised, the routine that sends + * an "illegal instruction" signal to the offending process is called. + * + * When the exception has been serviced, the context is restored from the + * stack and execution resumes from where it left off. + * + * In more detail: + * + * Saving the exception-time context. + * --------------------------------- + * In saving the exception-time context, we copy the shadow and general + * purpose registers to memory. Since one exception may occur while + * servicing another, the memory used to save the exception-time context may + * not be static (i.e. the same every time). Thus, memory on a stack is set + * aside for the exception frame (area where the exception-time context is + * saved). The same stack is also used when C routines are called (to + * service the exception). + * + * Each process has a stack in kernel space (called the "kernel stack", + * short for "process's kernel stack) as well as the user space stack. When + * entering the kernel from user space, the kernel stack is unused. On this + * stack we save the exception state and (most likely call a C routine to) + * service the exception. + * + * Before servicing an exception, several issues must be addressed. + * + * 1) When an interrupt is recognized by the hardware, the data pipeline is + * allowed to clear. However, if one of these data accesses faults (bad + * reference, or a reference to a page which needs to be swapped in), that + * reference, as well as any others in the pipeline at the time (at most + * three total) are left there, to be taken care of by the exception + * handler [6.4.1]. This involves swapping in the proper page and + * manually doing the appropriate load or store. + * + * The other (at most, two other) data accesses that might have been in + * the pipeline must also be manually completed (even though they may not + * be at fault [yes, that's a bad pun, thank you]). + * + * 2) If any of the (at most three) uncompleted data access in the pipeline + * are loads (from memory to a register), then the bit for the destination + * register is set in the SSBR. Since the hardware will never complete + * that load (since we do it manually), the hardware will never clear that + * SSBR bit. Thus, we must clear it manually. If this isn't done, the + * system will hang waiting for a bit to clear that will never. + * + * 3) If the exception is the privilege violation exception, the bounds + * violation exception, or the misaligned access exception, the + * destination register bit in the SSBR may need to be cleared. + * + * 4) If the exception is one of the floating exceptions, then the + * destination register for that floating process won't be written, + * and the SSBR must be cleared explicitly. + * + * 5) The FPU must be enabled (as it is disabled by the exception processing + * hardware) and allowed to complete actions in progress. This is so + * so that it may be used in the servicing of any instruction. + * When the FPU is being restarted, operations attempting to complete + * may themselves fault (raising another exception). + * + * More on Restarting the FPU + * -------------------------- + * The manual [section 6.4.3.4] gives only minor mention to this + * rather complex task. Before the FPU is restarted all SSBR bits are + * cleared for actions that the exception handler completes (as mentioned + * above) so that the SSBR is clear unless there are FPU operations that + * have not actually been completed (and hence not written to the registers). + * Also, all control registers (at least all those that we care about) are + * saved to the stack exception frame before the FPU is restarted (this + * is important... the reason comes later). + * + * The FPU is restarted by doing an rte to a trap-not-taken (the rte + * actually enables the fpu because we ensure that the EPSR has the + * FPU-enable bit on; the trap-not-taken ensures anything in the FPU + * completes by waiting until scoreboard register is clear). + * + * At the time the FPU is restarted (the rte to the trap-not-taken) the FPU + * can write to ANY of the general registers. Thus, we must make sure that + * all general registers (r1..r31) are in their pre-exception state so that + * when saved to the exception frame after the FPU is enabled, they properly + * reflect any changes made by the FPU in being restarted. + * + * Because we can't save the pointer to the exception frame in a general + * register during the FPU restart (it could get overwritten by the FPU!), + * we save it in a control register, SR3, during the restart. + * + * + * HOWEVER ..... + * + * Because other uncompleted actions in the FPU may fault when the FPU is + * restarted, a new exception may be raised during the restart. This may + * happen recursively a number of times. Thus, during a restart, ANY register + * whatsoever may be modified, including control registers. Because of this + * we must make sure that the exception handler preserves SR3 throughout + * servicing an exception so that, if the exception had been raised during + * an FPU restart, it is returned unmolested when control returns to the FPU + * restart. + * + * Thus: if an exception is from kernel space, we MUST preserve SR3. + * (if it from user space, no FPU-enable can be in progress and SR3 is + * unimportant). + * + * Now is a good time to recap SR1..SR3 usage: + * SR1 - CPU flags (exception handler flags) + * SR2 - generally free + * SR3 - free only if the exception is from user mode + * + * Once the FPU has been restarted, the general registers are saved to the + * exception frame. If the exception is not the interrupt exception, + * interrupts are enabled and any faulted data accesses (see above) are + * serviced. In either case, the exception is then serviced (usually by + * calling a C routine). After servicing, any faulted data accesses are + * serviced (if it had been the interrupt exception). The context is then + * restored and control returns to where the exception occurred. + * + */ + +#include "assym.h" + +#include <machine/param.h> +#include <machine/asm.h> +#include <machine/board.h> +#include <machine/m8820x.h> +#include <machine/trap.h> + +/* + * The exception frame as defined in "machine/pcb.h" (among other places) is + * a bit outdated and needs to be changed. Until then, we'll define some + * pseudo-fields there for our needs. + * + * EF_SR3 + * A place to save the exception-time SR3 from just after the + * time when an exception is raised until just after the FPU + * has been restarted. This does not necessarly conflict with + * the general registers (though it can if you're not careful) + * and so we can use a spot later used to save a general register. + * + * EF_FLAGS + * This is just the old EF_MODE. "EF_MODE" isn't a very good name. + */ +#define EF_SR3 (EF_R0 + 5) +#define EF_FLAGS EF_MODE + + data + align 4 +ASLOCAL(sbadcpupanic) + string "eh.S: bad cpu number in FLAGS\000" + + text + align 8 + +ASLOCAL(Lbadcpupanic) + subu r31, r31, 32 + or.u r2, r0, hi16(_ASM_LABEL(sbadcpupanic)) + bsr.n _C_LABEL(panic) + or r2, r2, lo16(_ASM_LABEL(sbadcpupanic)) + addu r31, r31, 32 + + align 8 + +#ifdef M88110 +#define SAVE_CTX \ + stcr r31, SRX ; \ + or.u r31, r0, hi16(_ASM_LABEL(save_frame)) ; \ + or r31, r31, lo16(_ASM_LABEL(save_frame)) ; \ + /* save old R31 and other R registers */; \ + st.d r0 , r31, GENREG_OFF(0) ; \ + st.d r2 , r31, GENREG_OFF(2) ; \ + st.d r4 , r31, GENREG_OFF(4) ; \ + st.d r6 , r31, GENREG_OFF(6) ; \ + st.d r8 , r31, GENREG_OFF(8) ; \ + st.d r10, r31, GENREG_OFF(10) ; \ + st.d r12, r31, GENREG_OFF(12) ; \ + st.d r14, r31, GENREG_OFF(14) ; \ + st.d r16, r31, GENREG_OFF(16) ; \ + st.d r18, r31, GENREG_OFF(18) ; \ + st.d r20, r31, GENREG_OFF(20) ; \ + st.d r22, r31, GENREG_OFF(22) ; \ + st.d r24, r31, GENREG_OFF(24) ; \ + st.d r26, r31, GENREG_OFF(26) ; \ + st.d r28, r31, GENREG_OFF(28) ; \ + st r30, r31, GENREG_OFF(30) ; \ + ldcr r1, SRX ; \ + st r1, r31, GENREG_OFF(31) ; \ + ldcr r1, EPSR ; \ + ldcr r2, EXIP ; \ + ldcr r3, ENIP ; \ + st r1, r31, REG_OFF(EF_EPSR) ; \ + st r2, r31, REG_OFF(EF_EXIP) ; \ + st r3, r31, REG_OFF(EF_ENIP) ; \ + ldcr r1, DSR ; \ + ldcr r2, DLAR ; \ + ldcr r3, DPAR ; \ + st r1, r31, REG_OFF(EF_DSR) ; \ + st r2, r31, REG_OFF(EF_DLAR) ; \ + st r3, r31, REG_OFF(EF_DPAR) ; \ + ldcr r1, ISR ; \ + ldcr r2, ILAR ; \ + ldcr r3, IPAR ; \ + st r1, r31, REG_OFF(EF_ISR) ; \ + st r2, r31, REG_OFF(EF_ILAR) ; \ + st r3, r31, REG_OFF(EF_IPAR) ; \ + ldcr r1, DSAP ; \ + ldcr r2, DUAP ; \ + st r1, r31, REG_OFF(EF_DSAP) ; \ + st r2, r31, REG_OFF(EF_DUAP) ; \ + ldcr r1, ISAP ; \ + ldcr r2, IUAP ; \ + st r1, r31, REG_OFF(EF_ISAP) ; \ + st r2, r31, REG_OFF(EF_IUAP) ; \ + /* Restore r1, r2, r3, and r31 */ ; \ + ld r1 , r31, GENREG_OFF(1) ; \ + ld r2 , r31, GENREG_OFF(2) ; \ + ld r3 , r31, GENREG_OFF(3) ; \ + ld r31, r31, GENREG_OFF(31) +#endif + +/* + * + * #define PREP(NAME, NUM, BIT, SSBR_STUFF, FLAG_CHECK) + * + * This is the "exception processing preparaton" common to all exception + * processing. It is used in the following manner: + * + * ASGLOBAL(foo_handler) + * PREP("foo", 11, DEBUG_FOO_BIT, SSBR_Stuff, Precheck_Stuff) + * CALL(_C_LABEL(trapXXX), T_FOO_FAULT, r31) + * DONE(DEBUG_FOO_BIT) + * + * This defines the exception handler for the "foo" exception. + * The arguments ro PREP(): + * NAME + * String for debugging (more info later) + * NUM + * The exception number [see the manual, Table 6-1] + * BIT + * Bit to check in eh_debug for debugging (more info later) + * SSBR_STUFF + * If the exception might leave some bits in the SSBR set, + * this should indicate how they are cleared. + * FLAG_PRECHECK + * This is for the data access exception only. See it for + * more info. + * + * What's in between PREP() and DONE() (usually a CALL) is the actual + * servicing of the interrupt. During this time, any register may + * be used freely as they've all been saved in the exception frame + * (which is pointed-to by r31). + */ + +#ifdef M88100 +#define PREP(NAME, NUM, BIT, SSBR_STUFF, FLAG_PRECHECK) \ + xcr FLAGS, FLAGS, SR1 ; \ + FLAG_PRECHECK ; \ + /* the bsr later clobbers r1, so save now */ \ + stcr r1, SR2 /* r1 now free */ ; \ + /* set or clear the FLAG_FROM_KERNEL bit */ \ + ldcr r1, EPSR ; \ + bb0.n PSR_SUPERVISOR_MODE_BIT, r1, 1f ; \ + clr FLAGS, FLAGS, 1<FLAG_FROM_KERNEL> ; \ + set FLAGS, FLAGS, 1<FLAG_FROM_KERNEL> ; \ + /* get a stack (exception frame) */ \ +1: bsr _ASM_LABEL(setup_phase_one) ; \ + /* TMP2 now free -- use to set EF_VECTOR */ \ + or TMP2, r0, NUM ; \ + st TMP2, r31, REG_OFF(EF_VECTOR) ; \ + /* Clear any bits in the SSBR (held in TMP) */ \ + /* SSBR_STUFF may be empty, though. */ \ + SSBR_STUFF ; \ + /* call setup_phase_two to restart the FPU */ \ + /* and to save all general registers. */ \ + bsr _ASM_LABEL(setup_phase_two) ; \ + /* All general regs free -- do any debugging */ \ + PREP_DEBUG(BIT, NAME) +#endif + +#ifdef M88110 +#define PREP2(NAME, NUM, BIT, FLAG_PRECHECK) \ + SAVE_CTX ; \ + xcr FLAGS, FLAGS, SR1 ; \ + FLAG_PRECHECK ; \ + /* the bsr later clobbers r1, so save now */ ; \ + stcr r1, SR2 /* r1 now free */ ; \ + /* set or clear the FLAG_FROM_KERNEL bit */ ; \ + ldcr r1, EPSR ; \ + bb0.n PSR_SUPERVISOR_MODE_BIT, r1, 1f ; \ + clr FLAGS, FLAGS, 1<FLAG_FROM_KERNEL> ; \ + set FLAGS, FLAGS, 1<FLAG_FROM_KERNEL> ; \ + /* get a stack (exception frame) */ ; \ +1: bsr _ASM_LABEL(m88110_setup_phase_one) ; \ + /* TMP2 now free -- use to set EF_VECTOR */ ; \ + or TMP2, r0, NUM ; \ + st TMP2, r31, REG_OFF(EF_VECTOR) ; \ + /* call setup_phase_two to restart the FPU */ ; \ + /* and to save all general registers. */ ; \ + bsr _ASM_LABEL(m88110_setup_phase_two) ; \ + /* All general regs free -- do any debugging */ ; \ + PREP_DEBUG(BIT, NAME) +#endif + +/* Some defines for use with PREP() */ +#define Clear_SSBR_Dest \ + bsr _ASM_LABEL(clear_dest_ssbr_bit) +#define Data_Precheck \ + bb1.n FLAG_IGNORE_DATA_EXCEPTION, FLAGS, \ + _ASM_LABEL(ignore_data_exception) +#define M88110_Data_Precheck \ + bb1.n FLAG_IGNORE_DATA_EXCEPTION, FLAGS, \ + _ASM_LABEL(m88110_ignore_data_exception) + +#ifdef EH_DEBUG +/* + * If we allow debugging, there is a variable "eh_debug" + * in which there is a bit for each exception. If the bit + * is set for an exception, debugging information is printed + * about that exception whenever it occurs. + * + * The bits are defined in "asm.h" + */ + +ASGLOBAL(eh_debug) + word 0x00000000 + +/* + * additional pre-servicing preparation to be done when + * debugging... check eh_debug and make the call if + * need be. + */ +#define PREP_DEBUG(DebugNumber, Name) \ + or.u r2, r0, hi16(_ASM_LABEL(eh_debug)) ; \ + ld r3, r2, lo16(_ASM_LABEL(eh_debug)) ; \ + bb0 DebugNumber, r3, 4f ; \ + /* call MY_info(ef,SR0,flags,kind)*/ \ + or r2, r30, r0 ; \ + ldcr r3, SR0 ; \ + ldcr r4, SR1 ; \ + or.u r5, r0, hi16(2f) ; \ + or r5, r5, lo16(2f) ; \ + bsr.n _C_LABEL(MY_info) ; \ + subu r31, r31, 32 ; \ + br.n 4f ; \ + addu r31, r31, 32 ; \ + data ; \ +2: string Name ; \ + byte 0 ; \ + align 4 ; \ + text ; \ +4: + + +/* + * Post-servicing work to be done. + * When debugging, check "eh_debug" and call the + * debug routined if neeed be. + * + * Then, return from the interrupt handler. + */ +#define DONE(DebugNumber) \ + or.u r2, r0, hi16(_ASM_LABEL(eh_debug)) ; \ + ld r3, r2, lo16(_ASM_LABEL(eh_debug)) ; \ + bb0 DebugNumber, r3, 2f ; \ + ldcr r4, SR1 ; \ + CALL(_C_LABEL(MY_info_done), r31, r4) ; \ +2: br _ASM_LABEL(return_from_exception_handler) +#define DONE2(DebugNumber) \ + or.u r2, r0, hi16(_ASM_LABEL(eh_debug)) ; \ + ld r3, r2, lo16(_ASM_LABEL(eh_debug)) ; \ + bb0 DebugNumber, r3, 2f ; \ + ldcr r4, SR1 ; \ + CALL(_C_LABEL(MY_info_done), r31, r4) ; \ +2: br _ASM_LABEL(m88110_return_code) +#else +/* + * If not debugging, then no debug-prep to do. + * Also, when you're done, you're done! (no debug check). + */ +#define PREP_DEBUG(bit, name) +#define DONE(num) \ + br _ASM_LABEL(return_from_exception_handler) +#define DONE2(num) \ + br _ASM_LABEL(m88110_return_code) +#endif /* EH_DEBUG */ + +#ifdef M88100 +/* + * MVME18x exception handlers + */ + +/* unknown exception handler */ +GLOBAL(unknown_handler) + PREP("unknown", 0, DEBUG_UNKNOWN_BIT,,) + CALL(_C_LABEL(m88100_trap), T_UNKNOWNFLT, r30) + DONE(DEBUG_UNKNOWN_BIT) + +/* interrupt exception handler */ +GLOBAL(interrupt_handler) + PREP("interrupt", 1, DEBUG_INTERRUPT_BIT,,) + CALL(_C_LABEL(m88100_trap), T_INT, r30) + DONE(DEBUG_INTERRUPT_BIT) + +/* instruction access exception handler */ +GLOBAL(instruction_access_handler) + PREP("inst", 2, DEBUG_INSTRUCTION_BIT,,) + CALL(_C_LABEL(m88100_trap), T_INSTFLT, r30) + DONE(DEBUG_INSTRUCTION_BIT) + +/* + * data access exception handler -- + * See badaddr() below for info about Data_Precheck. + */ +GLOBAL(data_exception_handler) + PREP("data", 3, DEBUG_DATA_BIT,, Data_Precheck) + /* No need to call m88100_trap(T_DATAFLT) as PREP will do this for us */ + DONE(DEBUG_DATA_BIT) + +/* misaligned access exception handler */ +GLOBAL(misaligned_handler) + PREP("misalign", 4, DEBUG_MISALIGN_BIT, Clear_SSBR_Dest,) + CALL(_C_LABEL(m88100_trap), T_MISALGNFLT, r30) + DONE(DEBUG_MISALIGN_BIT) + +/* unimplemented opcode exception handler */ +GLOBAL(unimplemented_handler) + PREP("unimp", 5, DEBUG_UNIMPLEMENTED_BIT,,) + CALL(_C_LABEL(m88100_trap), T_ILLFLT, r30) + DONE(DEBUG_UNIMPLEMENTED_BIT) + +/* + * Some versions of the chip have a bug whereby false privilege + * violation exceptions are raised. If the valid bit in the SXIP is clear, + * it is false. If so, just return. The code before PREP handles this.... + */ +GLOBAL(privilege_handler) + stcr r1, SR2 /* hold r1 for a moment */ + ldcr r1, SXIP /* look at the sxip... valid bit set? */ + bb1.n RTE_VALID_BIT, r1, 1f /* skip over if a valid exception */ + ldcr r1, SR2 /* restore r1 */ + RTE +1: PREP("privilege", 6, DEBUG_PRIVILEGE_BIT, Clear_SSBR_Dest,) + CALL(_C_LABEL(m88100_trap), T_PRIVINFLT, r30) + DONE(DEBUG_PRIVILEGE_BIT) + +/* bounds checking exception handler */ +GLOBAL(bounds_handler) + PREP("bounds", 7, DEBUG_BOUNDS_BIT, Clear_SSBR_Dest,) + CALL(_C_LABEL(m88100_trap), T_BNDFLT, r30) + DONE(DEBUG_BOUNDS_BIT) + +/* integer divide-by-zero exception handler */ +GLOBAL(divide_handler) + PREP("divide", 8, DEBUG_DIVIDE_BIT, Clear_SSBR_Dest,) + CALL(_C_LABEL(m88100_trap), T_ZERODIV, r30) + DONE(DEBUG_DIVIDE_BIT) + +/* integer overflow exception handler */ +GLOBAL(overflow_handler) + PREP("overflow", 9, DEBUG_OVERFLOW_BIT,,) + CALL(_C_LABEL(m88100_trap), T_OVFFLT, r30) + DONE(DEBUG_OVERFLOW_BIT) + +/* Floating-point precise handler */ +#define FPp_SSBR_STUFF \ + bsr _ASM_LABEL(clear_FPp_ssbr_bit) +GLOBAL(fp_precise_handler) + PREP("FPU precise", 114, DEBUG_FPp_BIT, FPp_SSBR_STUFF,) + CALL(_ASM_LABEL(m88100_Xfp_precise), r0, r30) + DONE(DEBUG_FPp_BIT) + +/* Floating-point imprecise handler */ +#define FPi_SSBR_STUFF \ + bsr _ASM_LABEL(clear_FPi_ssbr_bit) +GLOBAL(fp_imprecise_handler) + PREP("FPU imprecise", 115, DEBUG_FPi_BIT, FPi_SSBR_STUFF,) + CALL(_ASM_LABEL(Xfp_imprecise), r0, r30) + DONE(DEBUG_FPi_BIT) + +/* All standard system calls. */ +GLOBAL(syscall_handler) + PREP("syscall", 128, DEBUG_SYSCALL_BIT,,) + ld r13, r30, GENREG_OFF(13) + CALL(_C_LABEL(m88100_syscall), r13, r30) + DONE(DEBUG_SYSCALL_BIT) + +/* trap 496 comes here */ +GLOBAL(bugtrap) + PREP("bugsyscall", 496, DEBUG_BUGCALL_BIT,,) + ld r9, r30, GENREG_OFF(9) + CALL(_C_LABEL(bugsyscall), r9, r30) + DONE(DEBUG_BUGCALL_BIT) + +GLOBAL(sigsys) + PREP("sigsys", 501, DEBUG_SIGSYS_BIT,,) + CALL(_C_LABEL(m88100_trap), T_SIGSYS, r30) + DONE(DEBUG_SIGSYS_BIT) + +GLOBAL(sigtrap) + PREP("sigtrap", 510, DEBUG_SIGTRAP_BIT,,) + CALL(_C_LABEL(m88100_trap), T_SIGTRAP, r30) + DONE(DEBUG_SIGTRAP_BIT) + +GLOBAL(stepbpt) + PREP("stepbpt", 504, DEBUG_SIGTRAP_BIT,,) + CALL(_C_LABEL(m88100_trap), T_STEPBPT, r30) + DONE(DEBUG_SIGTRAP_BIT) + +GLOBAL(userbpt) + PREP("userbpt", 511, DEBUG_SIGTRAP_BIT,,) + CALL(_C_LABEL(m88100_trap), T_USERBPT, r30) + DONE(DEBUG_SIGTRAP_BIT) + +#ifdef DDB +GLOBAL(break) + PREP("break", 130, DEBUG_BREAK_BIT,,) + CALL(_C_LABEL(m88100_trap), T_KDB_BREAK, r30) + DONE(DEBUG_BREAK_BIT) + +GLOBAL(trace) + PREP("trace", 131, DEBUG_TRACE_BIT,,) + CALL(_C_LABEL(m88100_trap), T_KDB_TRACE, r30) + DONE(DEBUG_TRACE_BIT) + +GLOBAL(entry) + PREP("kdb", 132, DEBUG_KDB_BIT,,) + CALL(_C_LABEL(m88100_trap), T_KDB_ENTRY, r30) + DONE(DEBUG_KDB_BIT) +#else +GLOBAL(break) + PREP("break", 130, DEBUG_BREAK_BIT,,) + CALL(_C_LABEL(m88100_trap), T_UNKNOWNFLT, r30) + DONE(DEBUG_BREAK_BIT) + +GLOBAL(trace) + PREP("trace", 131, DEBUG_TRACE_BIT,,) + CALL(_C_LABEL(m88100_trap), T_UNKNOWNFLT, r30) + DONE(DEBUG_TRACE_BIT) + +GLOBAL(entry) + PREP("unknown", 132, DEBUG_KDB_BIT,,) + CALL(_C_LABEL(m88100_trap), T_UNKNOWNFLT, r30) + DONE(DEBUG_KDB_BIT) +#endif + +/* + * The error exception and reset exception handler. + * + * The error exception is raised when any other non-trap exception is raised + * while shadowing is off. This is Bad News. + * + * The reset exception is raised when the RST signal is asserted (machine + * is reset), the value of VBR is changed after exceptions are enabled, + * or when a jmp, br/bsr to addr 0 (accidents do happen :-) + * To tell the difference, you should check the value of r1 and the valid + * bit of SXIP. + * Upon a real reset, VBR is set to zero (0), so code must be at addr 0 + * to handle it!!! + * + * The shadow registers are not valid in this case (shadowing was off, if this + * was an error exception, and may not be on, if this was a reset exception). + * R1-R31 may be interesting though, so we'll save them. + * + * We'll not worry about trashing r26-29 here, + * since they aren't generally used. + */ +GLOBAL(error_handler) + br.n 1f + or r29, r0, 10 +GLOBAL(reset_handler) + or r29, r0, 0 +1: + /* pick up the slavestack */ + or r26, r0, r31 /* save old stack */ + or.u r31, r0, hi16(_ASM_LABEL(intstack_end)) + or r31, r31, lo16(_ASM_LABEL(intstack_end)) + +#ifdef DEBUG + /* zero the stack, so we'll know what we're lookin' at */ + or.u r27, r0, hi16(_C_LABEL(intstack)) + or r27, r27, lo16(_C_LABEL(intstack)) +1: cmp r28, r27, r31 + bb1 ge, r28, 2f /* branch if at the end of the stack */ + st r0, r0, r27 + br.n 1b + addu r27, r27, 4 /* bump up */ +2: /* stack has been cleared */ +#endif + + /* ensure that stack is 8-byte aligned */ + clr r31, r31, 3<0> /* round down to 8-byte boundary */ + + /* create exception frame on stack */ + subu r31, r31, SIZEOF_EF /* r31 now our E.F. */ + + /* save old R31 and other R registers */ + st.d r0 , r31, GENREG_OFF(0) + st.d r2 , r31, GENREG_OFF(2) + st.d r4 , r31, GENREG_OFF(4) + st.d r6 , r31, GENREG_OFF(6) + st.d r8 , r31, GENREG_OFF(8) + st.d r10, r31, GENREG_OFF(10) + st.d r12, r31, GENREG_OFF(12) + st.d r14, r31, GENREG_OFF(14) + st.d r16, r31, GENREG_OFF(16) + st.d r18, r31, GENREG_OFF(18) + st.d r20, r31, GENREG_OFF(20) + st.d r22, r31, GENREG_OFF(22) + st.d r24, r31, GENREG_OFF(24) + st r30, r31, GENREG_OFF(30) + st r26, r31, GENREG_OFF(31) + + /* save shadow registers (are OLD if error_handler, though) */ + ldcr r10, EPSR + st r10, r31, REG_OFF(EF_EPSR) + ldcr r10, SXIP + st r10, r31, REG_OFF(EF_SXIP) + ldcr r10, SNIP + st r10, r31, REG_OFF(EF_SNIP) + ldcr r10, SR1 + st r10, r31, REG_OFF(EF_MODE) + ldcr r10, SFIP + st r10, r31, REG_OFF(EF_SFIP) + ldcr r10, SSBR + st r10, r31, REG_OFF(EF_SSBR) + stcr r0, SSBR /* won't want shadow bits bothering us later */ + + ldcr r10, DMT0 + st r10, r31, REG_OFF(EF_DMT0) + ldcr r11, DMD0 + st r11, r31, REG_OFF(EF_DMD0) + ldcr r12, DMA0 + + st r12, r31, REG_OFF(EF_DMA0) + ldcr r10, DMT1 + st r10, r31, REG_OFF(EF_DMT1) + FLUSH_PIPELINE + ldcr r11, DMD1 + st r11, r31, REG_OFF(EF_DMD1) + ldcr r12, DMA1 + st r12, r31, REG_OFF(EF_DMA1) + + ldcr r10, DMT2 + st r10, r31, REG_OFF(EF_DMT2) + ldcr r11, DMD2 + st r11, r31, REG_OFF(EF_DMD2) + ldcr r12, DMA2 + st r12, r31, REG_OFF(EF_DMA2) + + /* shove sr2 into EF_FPLS1 */ + ldcr r10, SR2 + st r10, r31, REG_OFF(EF_FPLS1) + + /* shove sr3 into EF_FPHS2 */ + ldcr r10, SR3 + st r10, r31, REG_OFF(EF_FPHS2) + + /* save error vector */ + st r29, r31, REG_OFF(EF_VECTOR) + + /* + * Cheap way to enable FPU and start shadowing again. + */ + ldcr r10, PSR + clr r10, r10, 1<PSR_FPU_DISABLE_BIT> /* enable the FPU */ + clr r10, r10, 1<PSR_SHADOW_FREEZE_BIT> /* and shadowing */ + stcr r10, PSR + FLUSH_PIPELINE + + /* put pointer to regs into r30... r31 will become a simple stack */ + or r30, r31, r0 + + subu r31, r31, 0x10 /* make some breathing space */ + st r30, r31, 0x0c /* store frame pointer on the st */ + st r30, r31, 0x08 /* store again for the debugger to recognize */ + or.u r20, r0, hi16(0x87654321) + or r20, r20, lo16(0x87654321) + st r20, r31, 0x04 + st r20, r31, 0x00 + + CALL(_C_LABEL(error_fatal), r30, r30) + + /* turn interrupts back on */ + ldcr r1, PSR + clr r1, r1, 1<PSR_INTERRUPT_DISABLE_BIT> + stcr r1, PSR + FLUSH_PIPELINE + +1: + br 1b + /* NOTREACHED */ +#endif /* M88100 */ + +/* + * This is part of baddadr (below). + */ +#ifdef M88100 +ASLOCAL(ignore_data_exception) + /* + * SR1: previous FLAGS reg + * SR2: free + * SR3: must presere + * FLAGS: CPU status flags + */ + xcr FLAGS, FLAGS, SR1 /* replace SR1, FLAGS */ + + /* + * For more info, see badaddr() below. + * + * We just want to jump to "badaddr__return_nonzero" below. + * + * We don't worry about trashing R2 here because we're + * jumping back to the function badaddr() where we're allowed + * to blast r2..r9 as we see fit. + */ + + /* the "+2" below is to set the VALID bit. */ + or.u r2, r0, hi16(_ASM_LABEL(badaddr__return_nonzero) + 2) + or r2, r2, lo16(_ASM_LABEL(badaddr__return_nonzero) + 2) + stcr r2, SNIP /* Make it the next instruction to execute */ + addu r2, r2, 4 + stcr r2, SFIP /* and the next one after that, too. */ + stcr r0, SSBR /* make the scoreboard happy. */ + RTE +#endif /* M88100 */ + +#ifdef M88110 +/* + * This is part of baddadr (below). + */ +ASLOCAL(m88110_ignore_data_exception) + /* + * SR1: previous FLAGS reg + * SR2: free + * SR3: must preserve + * FLAGS: CPU status flags + */ + xcr FLAGS, FLAGS, SR1 /* replace SR1, FLAGS */ + + /* + * For more info, see badaddr() below. + * + * We just want to jump to "m88110_badaddr__return_nonzero" below. + * + * We don't worry about trashing R2 here because we're + * jumping back to the function badaddr() where we're allowd + * to blast r2..r9 as we see fit. + */ + + or.u r2, r0, hi16(_ASM_LABEL(m88110_badaddr__return_nonzero)) + or r2, r2, lo16(_ASM_LABEL(m88110_badaddr__return_nonzero)) + stcr r2, EXIP /* Make it the next instruction to execute */ + RTE +#endif /* M88110 */ + +/* + * extern boolean_t badaddr(unsigned addr, unsigned len) + * + * Returns true (non-zero) if the given LEN bytes starting at ADDR are + * not all currently accessible by the kernel. + * + * If all LEN bytes starting at ADDR are accessible, zero is returned. + * + * Len may be be 1, 2, or 4. + * + * This is implemented by setting a special flag in SR1 before trying to access + * the given address. If a data access exception is raised, the address + * is inaccessible. The exception handler will notice the special CPU flag + * and not try to swap the address in. Rather, it will return to + * "badaddr__return_nonzero" in this routine so that we may return non-zero + * to the calling routine. + * + * If no fault is raised, we continue to where we return zero to the calling + * routine (after removing the special CPU flag). + */ + +GLOBAL(badaddr) + /* + * Disable interrupts ... don't want a context switch while we're + * doing this! Also, save the old PSR in R8 to restore later. + */ + ldcr r8, PSR + set r4, r8, 1<PSR_INTERRUPT_DISABLE_BIT> + stcr r4, PSR + FLUSH_PIPELINE + + ldcr r5, SR1 + set r5, r5, 1<FLAG_IGNORE_DATA_EXCEPTION> + /* resetting r5 to SR1 done in the delay slot below. */ + + /* + * If it's a word we're doing, do that here. Otherwise, + * see if it's a halfword..... + */ + sub r6, r3, 4 + bcnd.n ne0, r6, _ASM_LABEL(badaddr__maybe_halfword) + stcr r5, SR1 + FLUSH_PIPELINE + + /* + * It's a bad address if it's misaligned. + */ + bb1 0, r2, _ASM_LABEL(badaddr__return_nonzero) + bb1 1, r2, _ASM_LABEL(badaddr__return_nonzero) + /* + * The next line will either fault or not. If it faults, execution + * will go to: data_access_handler (see above) + * and then to: ignore_data_exception (see above) + * and then to: badaddr__return_nonzero (see below) + * which will return to the calling function. + * + * If there is no fault, execution just continues as normal. + */ + ld r5, r2, 0 + FLUSH_PIPELINE + br.n _ASM_LABEL(badaddr__return) + or r2, r0, r0 /* indicate a zero (address not bad) return.*/ + +ASLOCAL(badaddr__maybe_halfword) + /* More or less like the code for checking a word above */ + sub r6, r3, 2 + bcnd ne0, r6, _ASM_LABEL(badaddr__maybe_byte) + + /* it's bad if it's misaligned */ + bb1 0, r2, _ASM_LABEL(badaddr__return_nonzero) + + FLUSH_PIPELINE + ld.h r5, r2, 0 + FLUSH_PIPELINE + br.n _ASM_LABEL(badaddr__return) + or r2, r0, r0 + +ASLOCAL(badaddr__maybe_byte) +#ifdef DEBUG + /* More or less like the code for checking a word above */ + sub r6, r3, 1 + bcnd ne0, r6, _ASM_LABEL(badaddr__unknown_size) +#endif + FLUSH_PIPELINE + ld.b r5, r2, 0 + FLUSH_PIPELINE + br.n _ASM_LABEL(badaddr__return) + or r2, r0, r0 +ASLOCAL(badaddr__unknown_size) +#ifdef DEBUG + data +1: string "bad length (%d) to badaddr() from 0x%x\000" + text + subu r31, r31, 32 + or.u r2, r0, hi16(1b) + or r2, r2, lo16(1b) + bsr.n _C_LABEL(panic) + or r4, r0, r1 + addu r31, r31, 32 + /*NOTREACHED*/ +#endif + +#ifdef M88110 +ASLOCAL(m88110_badaddr__return_nonzero) + /* + * On mc88110, we possibly took an exception and we have to clear + * DSR before the rte instruction clears the EFRZ bit in the PSR. + */ + stcr r0, DSR + stcr r0, DLAR + stcr r0, DPAR + /* FALLTHROUGH */ +#endif +ASLOCAL(badaddr__return_nonzero) + or r2, r0, 1 + /* FALLTHROUGH */ + +ASLOCAL(badaddr__return) + ldcr r4, SR1 + clr r4, r4, 1<FLAG_IGNORE_DATA_EXCEPTION> + stcr r4, SR1 + + /* + * Restore the PSR to what it was before. + * The only difference is that we might be enabling interrupts + * (which we turned off above). If interrupts were already off, + * we do not want to turn them on now, so we just restore from + * where we saved it. + */ + stcr r8, PSR + FLUSH_PIPELINE + jmp r1 + +#ifdef M88100 +ASLOCAL(setup_phase_one) + /* + * SR1: saved copy of exception-time register now holding FLAGS + * SR2: saved copy of exception-time r1 + * SR3: must be preserved .. may be the exception-time stack + * r1: return address to calling exception handler + * FLAGS: CPU status flags + * + * immediate goal: + * Decide where we're going to put the exception frame. + * Might be at the end of R31, SR3, or the thread's pcb. + */ + + /* Check if we are coming in from a FPU restart exception. + If so, the pcb will be in SR3 */ + NOP + xcr r1, r1, SR2 + NOP + NOP + NOP + + bb1 FLAG_ENABLING_FPU, FLAGS, _ASM_LABEL(use_SR3_pcb) + /* are we coming in from user mode? If so, pick up thread pcb */ + bb0 FLAG_FROM_KERNEL, FLAGS, _ASM_LABEL(pickup_stack) + + /* Interrupt in kernel mode, not FPU restart */ + /* + * SR1: saved copy of exception-time register now holding FLAGS + * SR2: return address to the calling exception handler + * SR3: must be preserved; may be important for other exceptions + * FLAGS: CPU status flags + * + * immediate goal: + * We're already on the kernel stack, but not having + * needed to use SR3. We can just make room on the + * stack (r31) for our exception frame. + */ + subu r31, r31, SIZEOF_EF /* r31 now our E.F. */ + st FLAGS,r31, REG_OFF(EF_FLAGS) /* save flags */ + st r1, r31, GENREG_OFF(1) /* save prev. r1 (now r1 free)*/ + + ldcr r1, SR3 /* save previous SR3 */ + st r1, r31, REG_OFF(EF_SR3) + + addu r1, r31, SIZEOF_EF /* save previous r31 */ + br.n _ASM_LABEL(have_pcb) + st r1, r31, GENREG_OFF(31) + +ASLOCAL(use_SR3_pcb) + /* + * SR1: saved copy of exception-time register now holding FLAGS + * SR2: return address to the calling exception handler + * SR3: must be preserved; exception-time stack pointer + * FLAGS: CPU status flags + * + * immediate goal: + * An exception occurred while enabling the FPU. Since r31 + * is the user's r31 while enabling the FPU, we had put + * our pcb pointer into SR3, so make room from + * there for our stack pointer. + * We need to check if SR3 is the old stack pointer or the + * pointer off to the user pcb. If it pointing to the user + * pcb, we need to pick up the kernel stack. Otherwise + * we need to allocate a frame upon it. + * We look at the EPSR to see if it was from user mode + * Unfortunately, we have no registers free at the moment + * But we know register 0 in the pcb frame will always be + * zero, so we can use it as scratch storage. + */ + xcr r30, r30, SR3 /* r30 = old exception frame */ + st r1, r30, GENREG_OFF(0) /* free up r1 */ + ld r1, r30, REG_OFF(EF_EPSR) /* get back the epsr */ + bb0.n PSR_SUPERVISOR_MODE_BIT, r1, 1f /* if user mode */ + ld r1, r30, GENREG_OFF(0) /* restore r1 */ + /* we were in kernel mode - dump frame upon the stack */ + st r0, r30, GENREG_OFF(0) /* repair old frame */ + subu r30, r30, SIZEOF_EF /* r30 now our E.F. */ + st FLAGS,r30, REG_OFF(EF_FLAGS) /* save flags */ + st r1, r30, GENREG_OFF(1) /* save prev r1 (now free) */ + + st r31, r30, GENREG_OFF(31) /* save previous r31 */ + or r31, r0, r30 /* make r31 our pointer. */ + addu r30, r30, SIZEOF_EF /* r30 now has previous SR3 */ + st r30, r31, REG_OFF(EF_SR3) /* save previous SR3 */ + br.n _ASM_LABEL(have_pcb) + xcr r30, r30, SR3 /* restore r30 */ +1: + /* we took an exception while restarting the FPU from user space. + * Consequently, we never picked up a stack. Do so now. + * R1 is currently free (saved in the exception frame pointed at by + * r30) */ + or.u r1, r0, hi16(_ASM_LABEL(kstack)) + ld r1, r1, lo16(_ASM_LABEL(kstack)) + addu r1, r1, USIZE-SIZEOF_EF + st FLAGS,r1, REG_OFF(EF_FLAGS) /* store flags */ + st r31, r1, GENREG_OFF(31) /* store r31 - now free */ + st r30, r1, REG_OFF(EF_SR3) /* store old SR3 (pcb) */ + or r31, r1, r0 /* make r31 our exception fp */ + ld r1, r30, GENREG_OFF(0) /* restore old r1 */ + st r0, r30, GENREG_OFF(0) /* repair that frame */ + st r1, r31, GENREG_OFF(1) /* store r1 */ + br.n _ASM_LABEL(have_pcb) + xcr r30, r30, SR3 /* restore r30 */ + +ASLOCAL(pickup_stack) + /* + * SR1: saved copy of exception-time register now holding FLAGS + * SR2: return address to the calling exception handler + * SR3: free + * FLAGS: CPU status flags + * + * immediate goal: + * Since we're servicing an exception from user mode, we + * know that SR3 is free. We use it to free up a temp. + * register to be used in getting the thread's pcb + */ + stcr r31, SR3 /* save previous r31 */ + + /* switch to the thread's kernel stack. */ + or.u r31, r0, hi16(_C_LABEL(curpcb)) + ld r31, r31, lo16(_C_LABEL(curpcb)) + addu r31, r31, PCB_USER_STATE /* point to user save area */ + st FLAGS,r31, REG_OFF(EF_FLAGS) /* save flags */ + st r1, r31, GENREG_OFF(1) /* save prev. r1 (now free) */ + ldcr r1, SR3 /* save previous r31 */ + st r1, r31, GENREG_OFF(31) + /* FALLTHROUGH */ + +ASLOCAL(have_pcb) + /* + * SR1: saved copy of exception-time register now holding FLAGS + * SR2: return address to the calling exception handler + * SR3: free + * r1: free + * FLAGS: CPU status flags + * r31: our exception frame + * Valid in the exception frame: + * Exception-time r1, r31, FLAGS. + * Exception SR3, if appropriate. + * + * immediate goal: + * Save the shadow registers that need to be saved to + * the exception frame. + */ + stcr TMP, SR3 /* free up TMP, TMP2, TMP3 */ + SAVE_TMP2 + SAVE_TMP3 + + /* save some exception-time registers to the exception frame */ + ldcr TMP, EPSR + st TMP, r31, REG_OFF(EF_EPSR) + ldcr TMP3, SNIP + st TMP3, r31, REG_OFF(EF_SNIP) + ldcr TMP2, SFIP + st TMP2, r31, REG_OFF(EF_SFIP) + /* get and store the cpu number */ + extu TMP, FLAGS, FLAG_CPU_FIELD_WIDTH<0> /* TMP = cpu# */ + st TMP, r31, REG_OFF(EF_CPU) + + /* + * Save Pbus fault status register from data and inst CMMU. + */ + extu TMP, FLAGS, FLAG_CPU_FIELD_WIDTH<0> /* TMP = cpu# */ + cmp TMP2, TMP, 0x0 /* CPU0 ? */ + bb1 eq, TMP2, 1f + cmp TMP2, TMP, 0x1 /* CPU1 ? */ + bb1 eq, TMP2, 2f + cmp TMP2, TMP, 0x2 /* CPU2 ? */ + bb1 eq, TMP2, 3f + cmp TMP2, TMP, 0x3 /* CPU3 ? */ + bb1 eq, TMP2, 4f + /* Arrrrg! bad cpu# */ + br _ASM_LABEL(Lbadcpupanic) + /* XXX WHAT ABOUT MODULES WITH SPLIT U/S CMMUS ??? */ +1: + /* must be CPU0 */ + or.u TMP, r0, hi16(CMMU_I0) + ld TMP2, TMP, lo16(CMMU_I0) + 0x108 + st TMP2, r31, REG_OFF(EF_IPFSR) + or.u TMP, r0, hi16(CMMU_D0) + ld TMP2, TMP, lo16(CMMU_D0) + 0x108 + st TMP2, r31, REG_OFF(EF_DPFSR) + br _ASM_LABEL(pfsr_done) +2: + /* must be CPU1 */ + or.u TMP, r0, hi16(CMMU_I1) + ld TMP2, TMP, lo16(CMMU_I1) + 0x108 + st TMP2, r31, REG_OFF(EF_IPFSR) + or.u TMP, r0, hi16(CMMU_D1) + ld TMP2, TMP, lo16(CMMU_D1) + 0x108 + st TMP2, r31, REG_OFF(EF_DPFSR) + br _ASM_LABEL(pfsr_done) +3: + /* must be CPU2 */ + or.u TMP, r0, hi16(CMMU_I2) + ld TMP2, TMP, lo16(CMMU_I2) + 0x108 + st TMP2, r31, REG_OFF(EF_IPFSR) + or.u TMP, r0, hi16(CMMU_D2) + ld TMP2, TMP, lo16(CMMU_D2) + 0x108 + st TMP2, r31, REG_OFF(EF_DPFSR) + br _ASM_LABEL(pfsr_done) +4: + /* must be CPU3 */ + or.u TMP, r0, hi16(CMMU_I3) + ld TMP2, TMP, lo16(CMMU_I3) + 0x108 + st TMP2, r31, REG_OFF(EF_IPFSR) + or.u TMP, r0, hi16(CMMU_D3) + ld TMP2, TMP, lo16(CMMU_D3) + 0x108 + st TMP2, r31, REG_OFF(EF_DPFSR) + br _ASM_LABEL(pfsr_done) +5: + +ASLOCAL(pfsr_done) + ldcr TMP, SSBR + ldcr TMP2, SXIP + ldcr TMP3, DMT0 + st TMP2, r31, REG_OFF(EF_SXIP) + +/* + * The above shadow registers are obligatory for any and all + * exceptions. Now, if the data access pipeline is not clear, + * we must save the DMx shadow registers, as well as clear + * the appropriate SSBR bits for the destination registers of + * loads or xmems. + */ + bb0.n DMT_VALID_BIT, TMP3, 8f + st TMP3, r31, REG_OFF(EF_DMT0) + + ldcr TMP2, DMT1 + ldcr TMP3, DMT2 + st TMP2, r31, REG_OFF(EF_DMT1) + st TMP3, r31, REG_OFF(EF_DMT2) + + ldcr TMP2, DMA0 + ldcr TMP3, DMA1 + st TMP2, r31, REG_OFF(EF_DMA0) + st TMP3, r31, REG_OFF(EF_DMA1) + + ldcr TMP2, DMA2 + ldcr TMP3, DMD0 + st TMP2, r31, REG_OFF(EF_DMA2) + st TMP3, r31, REG_OFF(EF_DMD0) + + FLUSH_PIPELINE + ldcr TMP2, DMD1 + ldcr TMP3, DMD2 + st TMP2, r31, REG_OFF(EF_DMD1) + st TMP3, r31, REG_OFF(EF_DMD2) + +/* + * need to clear "appropriate" bits in the SSBR before + * we restart the FPU + */ + + ldcr TMP2, DMT0 + bb0.n DMT_VALID_BIT, TMP2, 8f + /* make sure an exception in fpu_enable will not see our DMT0 */ + stcr r0, DMT0 + bb1 DMT_LOCK_BIT, TMP2, 1f + bb1 DMT_WRITE_BIT, TMP2, 2f +1: + extu TMP2, TMP2, DMT_DREG_WIDTH <DMT_DREG_OFFSET> + set TMP2, TMP2, 1<5> + clr TMP, TMP, TMP2 +2: + ldcr TMP2, DMT1 + bb0 DMT_VALID_BIT, TMP2, 4f + bb1 DMT_LOCK_BIT, TMP2, 3f + bb1 DMT_WRITE_BIT, TMP2, 4f +3: + extu TMP2, TMP2, DMT_DREG_WIDTH <DMT_DREG_OFFSET> + set TMP2, TMP2, 1<5> + clr TMP, TMP, TMP2 +4: + ldcr TMP2, DMT2 + bb0 DMT_VALID_BIT, TMP2, 8f + bb1 DMT_LOCK_BIT, TMP2, 5f + bb1 DMT_WRITE_BIT, TMP2, 8f + bb1 DMT_DOUBLE_BIT,TMP2, 6f +5: + extu TMP2, TMP2, DMT_DREG_WIDTH <DMT_DREG_OFFSET> + br.n 7f + set TMP2, TMP2, 1<5> /* single */ +6: + extu TMP2, TMP2, DMT_DREG_WIDTH <DMT_DREG_OFFSET> + set TMP2, TMP2, 1<6> /* double */ +7: + clr TMP, TMP, TMP2 +8: + /* + * SR1: saved copy of exception-time register now holding FLAGS + * SR2: return address to the calling exception handler + * SR3: saved TMP + * r1: free + * TMP: possibly revised SSBR + * TMP2: free + * TMP3: free + * FLAGS: CPU status flags + * r31: exception frame + * Valid in the exception frame: + * Exception-time r1, r31, FLAGS. + * Exception-time TMP2, TMP3. + * Exception-time espr, sfip, snip, sxip. + * Dmt0. + * Other data pipeline control registers, if appropriate. + * Exception SR3, if appropriate. + */ + ldcr r1, SR2 + jmp r1 /* allow the handler to clear more SSBR bits */ + +#endif /* M88100 */ + +ASLOCAL(clear_FPi_ssbr_bit) + /* + * Clear floatingpoint-imprecise ssbr bits. + * Also, save appropriate FPU control registers to the E.F. + * + * r1: return address to calling exception handler + * TMP: (possibly) revised ssbr + * TMP2: free + * TMP3: free + */ + fldcr TMP2, FPSR + fldcr TMP3, FPCR + st TMP2, r31, REG_OFF(EF_FPSR) + st TMP3, r31, REG_OFF(EF_FPCR) + + fldcr TMP2, FPECR + fldcr TMP3, FPRH + st TMP2, r31, REG_OFF(EF_FPECR) + st TMP3, r31, REG_OFF(EF_FPRH) + + fldcr TMP2, FPIT + fldcr TMP3, FPRL + st TMP2, r31, REG_OFF(EF_FPIT) + st TMP3, r31, REG_OFF(EF_FPRL) + + /* + * We only need clear the bit in the SSBR for the + * 2nd reg of a double result [see section 6.8.5] + */ +#define FPIT_SIZE_BIT 10 + bb0 FPIT_SIZE_BIT, TMP2, 1f + extu TMP2, TMP2, 5<0> /* get the reg. */ + set TMP2, TMP2, 1<6> /* set width */ + clr TMP, TMP, TMP2 +1: + jmp r1 + + +ASLOCAL(clear_FPp_ssbr_bit) + /* + * Clear floating pont precise ssbr bits. + * Also, save appropriate FPU control registers to the E.F. + * + * r1: return address to calling exception handler + * TMP: (possibly) revised ssbr + * TMP2: free + * TMP3: free + */ + fldcr TMP2, FPSR + fldcr TMP3, FPCR + st TMP2, r31, REG_OFF(EF_FPSR) + st TMP3, r31, REG_OFF(EF_FPCR) + + fldcr TMP3, FPECR + st TMP3, r31, REG_OFF(EF_FPECR) + fldcr TMP2, FPHS1 + fldcr TMP3, FPHS2 + st TMP2, r31, REG_OFF(EF_FPHS1) + st TMP3, r31, REG_OFF(EF_FPHS2) + + fldcr TMP2, FPLS1 + fldcr TMP3, FPLS2 + st TMP2, r31, REG_OFF(EF_FPLS1) + st TMP3, r31, REG_OFF(EF_FPLS2) + + fldcr TMP2, FPPT + st TMP2, r31, REG_OFF(EF_FPPT) +1: + +#define FPPT_SIZE_BIT 5 + bb1.n FPPT_SIZE_BIT, TMP2, 2f + extu TMP3, TMP2, 5<0> /* get FP operation dest reg */ + br.n 3f + set TMP3, TMP3, 1<5> /* size=1 - clear one bit for float */ +2: + set TMP3, TMP3, 1<6> /* size=2 - clear two bit for double */ +3: + clr TMP, TMP, TMP3 /* clear bit(s) in ssbr. */ + jmp r1 + + +ASLOCAL(clear_dest_ssbr_bit) + /* + * There are various cases where an exception can leave the + * destination register's bit in the SB set. + * Examples: + * misaligned or privilege exception on a LD or XMEM + * DIV or DIVU by zero. + * + * I think that if the instruction is LD.D, then two bits must + * be cleared. + * + * Even though there are a number of instructions/exception + * combinations that could fire this code up, it's only required + * to be run for the above cases. However, I don't think it'll + * ever be a problem to run this in other cases (ST instructions, + * for example), so I don't bother checking. If we had to check + * for every possible instruction, this code would be much larger. + * + * The only checking, then, is to see if it's a LD.D or not. + * + * At the moment.... + * r1: return address to calling exception handler + * TMP: (possibly) revised ssbr + * TMP2: free + * TMP3: free + */ + + ldcr TMP3, EPSR /* going to check: user or system memory? */ + ldcr TMP2, SXIP /* get the instruction's address */ + bb1.n PSR_SUPERVISOR_MODE_BIT, TMP3, 2f + clr TMP2, TMP2, 2<0> /* get rid of valid and error bits. */ + + /* user space load here */ +#if ERRATA__XXX_USR + NOP + ld.usr TMP2,TMP2, r0 /* get the instruction itself */ + NOP + NOP + NOP + br 3f +#else + br.n 3f + ld.usr TMP2,TMP2, r0 /* get the instruction itself */ +#endif + +2: /* system space load here */ + ld TMP2, TMP2, r0 /* get the instruction itself */ + +3: /* now we have the instruction..... */ + /* + * Now see if it's a double load + * There are three forms of double load [IMM16, scaled, unscaled], + * which can be checked by matching against two templates: + * -- 77776666555544443333222211110000 -- + * if (((instruction & 11111100000000000000000000000000) == + * 00010000000000000000000000000000) ;; + * ((instruction & 11111100000000001111110011100000) == + * 11110100000000000001000000000000)) + * { + * It's a load double, so + * clear two SSBR bits. + * } else { + * It's not a load double. + * Must be a load single, xmem, or st + * Thus, clear one SSBR bit. + * } + */ + /* check the first pattern for ld.d */ + extu TMP3, TMP2, 16<16> /* get the upper 16 bits */ + mask TMP3, TMP3, 0xFC00 /* apply the mask */ + cmp TMP3, TMP3, 0x1000 /* if equal, it's a load double */ + bb1 eq, TMP3, 2f + + /* still could be -- check the second pattern for ld.d */ + /* look at the upper 16 bits first */ + extu TMP3, TMP2, 16<16> /* get the upper 16 bits */ + mask TMP3, TMP3, 0xFC00 /* apply the mask */ + cmp TMP3, TMP3, 0xF400 /* if equal, might be a load double */ + bb1 ne, TMP3, 1f /* not equal, must be single */ + + /* now look at the lower 16 bits */ + extu TMP3, TMP2, 16<0> /* get the lower 16 bits */ + mask TMP3, TMP3, 0xFCE0 /* apply the mask */ + cmp TMP3, TMP3, 0x1000 /* if equal, it's a load double */ + bb1 eq, TMP3, 2f + +1: /* misaligned single */ + extu TMP2, TMP2, 5<21> /* get the destination register */ + br.n 3f + set TMP2, TMP2, 1<5> /* set size=1 */ + +2: /* misaligned double */ + extu TMP2, TMP2, 5<21> /* get the destination register */ + set TMP2, TMP2, 1<6> /* set size=2 -- clear two bits */ +3: + jmp.n r1 + clr TMP, TMP, TMP2 /* clear bit(s) in ssbr. */ + +#ifdef M88100 + +ASLOCAL(setup_phase_two) + /* + * SR1: saved copy of exception-time register now holding FLAGS + * SR2: free + * SR3: saved TMP + * r1: return address to calling exception handler + * TMP: possibly revised SSBR + * TMP2: free + * TMP3: free + * FLAGS: CPU status flags + * r31: our exception frame + * Valid in the exception frame: + * Exception-time r1, r31, FLAGS. + * Exception-time TMP2, TMP3. + * Exception-time espr, sfip, snip, sxip. + * Exception number (EF_VECTOR). + * Dmt0 + * Other data pipeline control registers, if appropriate. + * FPU control registers, if appropriate. + * Exception SR3, if appropriate. + * + * immediate goal: + * restore the system to the exception-time state (except + * SR3 will be OUR stack pointer) so that we may resart the FPU. + */ + + stcr TMP, SSBR /* done with SSBR, TMP now free */ + RESTORE_TMP2 /* done with extra temp regs */ + RESTORE_TMP3 /* done with extra temp regs */ + + /* Get the current PSR and modify for the rte to enable the FPU */ + ldcr TMP, PSR + clr TMP, TMP, 1<PSR_FPU_DISABLE_BIT> /* enable the FPU */ + clr TMP, TMP, 1<PSR_SHADOW_FREEZE_BIT> /* and shadowing */ + stcr TMP, EPSR + + /* the "+2" below is to set the VALID_BIT */ + or.u TMP, r0, hi16(_ASM_LABEL(fpu_enable) + 2) + or TMP, TMP, lo16(_ASM_LABEL(fpu_enable) + 2) + stcr TMP, SNIP /* jump to here fpu_enable */ + addu TMP, TMP, 4 + stcr TMP, SFIP /* and then continue after that */ + + set FLAGS, FLAGS, 1<FLAG_ENABLING_FPU> + xcr FLAGS, FLAGS, SR1 + st r1, r31, REG_OFF(EF_RET) /* save the return address */ + ld r1, r31, GENREG_OFF(1) /* get original r1 */ + + xcr TMP, r31, SR3 /* TMP now restored. R31 now saved in SR3 */ + ld r31, r31, GENREG_OFF(31) /* get original r31 */ + + /* + * SR1: CPU flags + * SR2: free + * SR3: pointer to our exception frame (our stack pointer) + * r1 through r31: original exception-time values + * + * Valid in the exception frame: + * Exception-time FLAGS. + * Exception-time espr, sfip, snip, sxip. + * Exception number (EF_VECTOR). + * Dmt0 + * Other data pipeline control registers, if appropriate. + * FPU control registers, if appropriate. + * Exception SR3, if appropriate. + * Held temporarly in the exception frame: + * Return address to the calling exception handler. + * + * immediate goal: + * Do an RTE to restart the fpu and jump to "fpu_enable" + * Another exception (or exceptions) may be raised in + * this, which is why FLAG_ENABLING_FPU is set in SR1. + */ + + RTE /* jumps to "fpu_enable" on the next line to enable the FPU. */ + +ASLOCAL(fpu_enable) + FLUSH_PIPELINE + xcr TMP, TMP, SR3 /* get E.F. pointer */ + st r30, TMP, GENREG_OFF(30) /* save previous r30, r31 */ + st r31, TMP, GENREG_OFF(31) /* save previous r30, r31 */ + or r31, TMP, r0 /* transfer E.F. pointer to r31 */ + ld TMP, r31, REG_OFF(EF_SR3) /* get previous SR3 */ + + /* make sure that the FLAG_ENABLING_FPU bit is off */ + xcr FLAGS,FLAGS,SR1 + clr FLAGS,FLAGS,1<FLAG_ENABLING_FPU> + xcr FLAGS,FLAGS,SR1 + + xcr TMP, TMP, SR3 /* replace TMP, SR3 */ + + /* now save all regs to the exception frame. */ + st r0 , r31, GENREG_OFF(0) + st r1 , r31, GENREG_OFF(1) + st r2 , r31, GENREG_OFF(2) + st r3 , r31, GENREG_OFF(3) + st r4 , r31, GENREG_OFF(4) + st r5 , r31, GENREG_OFF(5) + st r6 , r31, GENREG_OFF(6) + st r7 , r31, GENREG_OFF(7) + st r8 , r31, GENREG_OFF(8) + st r9 , r31, GENREG_OFF(9) + st r10, r31, GENREG_OFF(10) + st r11, r31, GENREG_OFF(11) + st r12, r31, GENREG_OFF(12) + st r13, r31, GENREG_OFF(13) + st r14, r31, GENREG_OFF(14) + st r15, r31, GENREG_OFF(15) + st r16, r31, GENREG_OFF(16) + st r17, r31, GENREG_OFF(17) + st r18, r31, GENREG_OFF(18) + st r19, r31, GENREG_OFF(19) + st r20, r31, GENREG_OFF(20) + st r21, r31, GENREG_OFF(21) + st r22, r31, GENREG_OFF(22) + st r23, r31, GENREG_OFF(23) + st r24, r31, GENREG_OFF(24) + st r25, r31, GENREG_OFF(25) + st r26, r31, GENREG_OFF(26) + st r27, r31, GENREG_OFF(27) + st r28, r31, GENREG_OFF(28) + st r29, r31, GENREG_OFF(29) +#ifdef JEFF_DEBUG + /* mark beginning of frame with notable value */ + or.u r20, r0, hi16(0x12345678) + or r20, r20, lo16(0x12345678) + st r20, r31, GENREG_OFF(0) +#endif + + /* get and save IPL */ + bsr.n _C_LABEL(getipl) + subu r31, r31, 32 + addu r31, r31, 32 + st r2, r31, REG_OFF(EF_MASK) + + /* + * SR1: free + * SR2: free + * SR3: previous exception-time SR3 + * r1: return address to the calling exception handler + * r2 through r30: free + * r31: our exception frame + * + * Valid in the exception frame: + * Exception-time r0 through r31. + * Exception-time FLAGS. + * Exception-time espr, sfip, snip, sxip. + * Exception number (EF_VECTOR). + * Dmt0 + * Other data pipeline control registers, if appropriate. + * FPU control registers, if appropriate. + * Exception SR3, if appropriate. + * + * immediate goal: + * Pick up a stack if we came in from user mode. + * Put a copy of the exception frame pointer into r30 + * Bump the stack a doubleword and write the exception frame pointer. + * If not an interrupt exception, turn on interrupts and service any + * outstanding data access exceptions. + * Return to calling exception handler to service the exception. + */ + + /* + * If it's not the interrupt exception, enable interrupts and + * take care of any data access exceptions...... + */ + or r30, r0, r31 /* get a copy of the e.f. pointer */ + ld r2, r31, REG_OFF(EF_EPSR) + bb1 PSR_SUPERVISOR_MODE_BIT, r2, 1f /* if in kernel mode */ + + or.u r31, r0, hi16(_ASM_LABEL(kstack)) + ld r31, r31, lo16(_ASM_LABEL(kstack)) + addu r31, r31, USIZE /* point at proper end */ +1: + + /* + * here - r30 holds a pointer to the exception frame. + * r31 is a pointer to the kernel stack/interrupt stack. + */ + subu r31, r31, 8 /* make some breathing space */ + st r30, r31, 0 /* store frame pointer on the stack */ +#ifdef DDB + st r30, r31, 4 /* store it for the debugger to recognize */ +#endif /* DDB */ + + ld r2, r30, REG_OFF(EF_VECTOR) + bcnd.n eq0, r2, 8f /* error exception */ + ld r14, r30, REG_OFF(EF_RET) + + /* + * Do not process possible data exceptions here if this is an interrupt. + * Instead, the interrupt handler will take care of this by itself. + */ + cmp r3, r2, 1 /* is an interrupt? */ + bb1.n eq, r3, 8f /* skip if so */ + +#ifdef DDB + cmp r3, r2, 130 /* DDB break exception */ + bb1.n eq, r3, 8f + cmp r3, r2, 132 /* DDB entry exception */ + bb1.n eq, r3, 8f +#endif + + /* turn interrupts back on */ + ldcr r2, PSR + clr r2, r2, 1<PSR_INTERRUPT_DISABLE_BIT> + stcr r2, PSR + FLUSH_PIPELINE + + /* service any outstanding data pipeline stuff */ + ld r3, r30, REG_OFF(EF_DMT0) + bb0 DMT_VALID_BIT, r3, 8f + + /* + * r30 can be clobbered by calls. So stuff its value into a preserved + * register, say r15. R14 is in use (see return_to_... below). + */ + or r15, r0, r30 + CALL(_C_LABEL(m88100_trap), T_DATAFLT, r15) + or r30, r0, r15 + +8: + jmp r14 /* loaded above */ +#endif /* M88100 */ + +/* + * proc_trampoline. + * When a process setup by cpu_set_kpc() resumes, it will find itself in + * proc_trampoline, with r31 pointing to a ksigframe. proc_trampoline will + * load func and proc values from ksigframe, call the function, and on return + * pop off the ksigframe. Then, it will load pc from the switchframe and + * jump there. + */ + +ENTRY(proc_trampoline) + ld r1, r31, 0 /* load func */ + ld r2, r31, 4 /* load proc pointer */ + jsr.n r1 + subu r31, r31, 32 /* create stack space for function */ + addu r31, r31, 32 + 8 /* stack space above + ksigframe */ + ld r1, r31, 0 /* load pc */ + ld r2, r31, 4 /* & proc pointer from switch frame */ + jsr.n r1 + addu r31, r31, 8 + +/* + * proc_do_uret + * this is called as proc_do_uret(proc) from proc_trampoline(). This function + * loads r31 with a pointer to the trap frame for the given proc and calls + * return_from_exception_handler which loads all the registers and does an + * rte. + */ + +ENTRY(proc_do_uret) + ld r3,r2,P_ADDR /* p->p_addr */ + addu r3,r3,PCB_USER_STATE /* p->p_addr.u_pcb.user_state */ + st r3,r31,0 /* put it on the stack */ +#if defined(M88100) && defined(M88110) +#ifdef M88110 + or.u r2, r0, hi16(_C_LABEL(cputyp)) + ld r3, r2, lo16(_C_LABEL(cputyp)) + cmp r2, r3, CPU_88110 + bb1 eq, r2, _ASM_LABEL(m88110_return_code) + /* br _ASM_LABEL(return_from_exception_handler) */ +#endif +#endif + + /* + * Regs r1-r30 are free. R31 is pointing at the word + * on the kernel stack where our pointer to the exception frame + * it stored. Reload it now. + * + * At this point, if EF_DMT0 is not zero on MC88100, then + * this must have been an interrupt where the fault didn't + * get corrected above. We'll do that now. + * + * We load it into r14 since it is preserved across function + * calls, and we may have to call some routines from within here. + * + * Control is transferred here from obvious places in this file. + */ + +#ifdef M88100 +ASLOCAL(return_from_exception_handler) + /* + * if there happens to be a data fault that hasn't been serviced yet, + * go off and service that... + */ + +#define FPTR r14 + ld FPTR, r31, 0 /* grab exception frame pointer */ + ld r3, FPTR, REG_OFF(EF_DMT0) + bb0 DMT_VALID_BIT, r3, _ASM_LABEL(check_ast) + + /* + * If it's the interrupt exception, and interrupts were + * initially disabled, enable interrupts again... + */ + ld r2, FPTR, REG_OFF(EF_VECTOR) + cmp r2, r2, 1 /* is an interrupt? */ + bb1.n ne, r2, 1f /* if not so, skip */ + + /* ...unless they were already disabled */ + ld r2, FPTR, REG_OFF(EF_EPSR) + bb1.n PSR_INTERRUPT_DISABLE_BIT, r2, 1f + + ldcr r2, PSR + clr r2, r2, 1<PSR_INTERRUPT_DISABLE_BIT> + stcr r2, PSR + FLUSH_PIPELINE +1: + + CALL(_C_LABEL(m88100_trap), T_DATAFLT, r30) + br _ASM_LABEL(check_ast) +#endif /* M88100 */ + +#ifdef M88110 +ASLOCAL(m88110_return_code) +#define FPTR r14 + ld FPTR, r31, 0 /* grab exception frame pointer */ + +#if 0 + /* + * If it's the interrupt exception, enable interrupt. + */ + + /* + * Is it ever possible to have interrupt exception while EPSR has + * it disabled? I don't think so.. XXX nivas + * + * On mc88110, you can. The NMI interrupt. aka ABORT. XXX smurph + */ + ld r2, FPTR, REG_OFF(EF_VECTOR) + cmp r2, r2, 1 /* Is it an interrupt? */ + bb1.n ne, r2, 1f /* If not, skip */ + + /* ...unless they were already disabled */ + ld r2, FPTR, REG_OFF(EF_EPSR) + bb1.n PSR_INTERRUPT_DISABLE_BIT, r2, 1f + + ldcr r2, PSR + clr r2, r2, 1<PSR_INTERRUPT_DISABLE_BIT> /* enable interrupts */ + stcr r2, PSR + FLUSH_PIPELINE +1: +#endif +#endif /* M88110 */ + +/* + * If the saved ipl is 0, then call dosoftint() to process soft + * interrupts. + * If returning to user land, look for ASTs + */ +ASLOCAL(check_ast) + ld r2, FPTR, REG_OFF(EF_EPSR) /* get pre-exception PSR */ + bb1 PSR_INTERRUPT_DISABLE_BIT, r2, 1f /* skip if ints off */ + ld r2, FPTR, REG_OFF(EF_MASK) /* get pre-exception ipl */ + bcnd ne0, r2, 1f /* can't do softint's */ + + subu r31, r31, 32 + bsr.n _C_LABEL(setipl) + or r2, r0, IPL_SOFTCLOCK + /* at ipl 1 now */ + bsr _C_LABEL(dosoftint) + /* is this needed? we are going to restore the ipl below XXX nivas */ + bsr.n _C_LABEL(setipl) + or r2, r0, IPL_NONE /* ints are enabled */ + addu r31, r31, 32 + /* at ipl 0 now */ +1: + ld r2, FPTR, REG_OFF(EF_EPSR) /* get pre-exception PSR */ + bb1 PSR_SUPERVISOR_MODE_BIT, r2, no_ast /*skip if system mode */ + + /* should assert here - not in user mode with ints off XXX nivas */ + /* get and check want_ast */ + or.u r2, r0, hi16(_C_LABEL(want_ast)) + ld r3, r2, lo16(_C_LABEL(want_ast)) + bcnd eq0, r3, no_ast + + /* + * trap(AST,...) will service ast's. + */ +#if defined(M88110) && defined(M88100) + or.u r2, r0, hi16(_C_LABEL(cputyp)) + ld r3, r2, lo16(_C_LABEL(cputyp)) + cmp r2, r3, CPU_88110 + bb0 eq, r2, 2f +#endif +#if defined(M88110) + CALL(_C_LABEL(m88110_trap), T_ASTFLT, FPTR) +#endif +#if defined(M88110) && defined(M88100) + br no_ast +2: +#endif +#ifdef M88100 + CALL(_C_LABEL(m88100_trap), T_ASTFLT, FPTR) +#endif + +ASLOCAL(no_ast) + /* disable interrupts */ + ldcr r1, PSR + set r1, r1, 1<PSR_INTERRUPT_DISABLE_BIT> + stcr r1, PSR + FLUSH_PIPELINE + + /* now ready to return....*/ + subu r31, r31, 32 + bsr.n _C_LABEL(setipl) + ld r2, FPTR, REG_OFF(EF_MASK) /* get pre-exception ipl */ + addu r31, r31, 32 + + /* + * Transfer the frame pointer to r31, since we no longer need a stack. + * No page faults here, and interrupts are disabled. + */ + or r31, r0, FPTR + /* restore r1 later */ + ld r2 , r31, GENREG_OFF(2) + ld r3 , r31, GENREG_OFF(3) + ld r4 , r31, GENREG_OFF(4) + ld r5 , r31, GENREG_OFF(5) + ld r6 , r31, GENREG_OFF(6) + ld r7 , r31, GENREG_OFF(7) + ld r8 , r31, GENREG_OFF(8) + ld r9 , r31, GENREG_OFF(9) + ld r10, r31, GENREG_OFF(10) + ld r11, r31, GENREG_OFF(11) + ld r12, r31, GENREG_OFF(12) + ld r13, r31, GENREG_OFF(13) + ld r14, r31, GENREG_OFF(14) + ld r15, r31, GENREG_OFF(15) + ld r16, r31, GENREG_OFF(16) + ld r17, r31, GENREG_OFF(17) + ld r18, r31, GENREG_OFF(18) + ld r19, r31, GENREG_OFF(19) + ld r20, r31, GENREG_OFF(20) + ld r21, r31, GENREG_OFF(21) + ld r22, r31, GENREG_OFF(22) + ld r23, r31, GENREG_OFF(23) + ld r24, r31, GENREG_OFF(24) + ld r25, r31, GENREG_OFF(25) + ld r26, r31, GENREG_OFF(26) + ld r27, r31, GENREG_OFF(27) + ld r28, r31, GENREG_OFF(28) + ld r29, r31, GENREG_OFF(29) + /* restore r1, r30, r31 later */ + + /* disable shadowing */ + ldcr r1, PSR + set r1, r1, 1<PSR_SHADOW_FREEZE_BIT> + stcr r1, PSR + FLUSH_PIPELINE + + /* reload the control regs*/ +#ifdef M88110 +#ifdef M88100 + or.u r1, r0, hi16(_C_LABEL(cputyp)) + ld r30, r1, lo16(_C_LABEL(cputyp)) + cmp r1, r30, CPU_88110 + bb1 ne, r1, 1f +#endif + /* mc88110 needs the EXIP */ + ld r30, r31, REG_OFF(EF_ENIP) + ld r1, r31, REG_OFF(EF_EXIP) + stcr r30, ENIP + stcr r1, EXIP +#ifdef M88100 + br 2f +1: +#endif +#endif +#ifdef M88100 + st r0, r31, REG_OFF(EF_IPFSR) + st r0, r31, REG_OFF(EF_DPFSR) + + /* + * Note: no need to restore the SXIP. + * When the "rte" causes execution to continue + * first with the instruction pointed to by the NIP + * and then the FIP. + * + * See MC88100 Risc Processor User's Manual, 2nd Edition, + * section 6.4.3.1.2-4 + */ + ld r30, r31, REG_OFF(EF_SNIP) + ld r1, r31, REG_OFF(EF_SFIP) + stcr r0, SSBR + stcr r30, SNIP + stcr r1, SFIP +2: +#endif + ld r30, r31, REG_OFF(EF_EPSR) + ld r1, r31, REG_OFF(EF_MODE) + stcr r30, EPSR + + /* Now restore r1, r30, and r31 */ + ld r1, r31, GENREG_OFF(1) + ld r30, r31, GENREG_OFF(30) + ld r31, r31, GENREG_OFF(31) + + RTE + +#ifdef M88110 +/* + * MVME197 exception handlers + */ + +/* unknown exception handler */ +GLOBAL(m88110_unknown_handler) + PREP2("unknown", 0, DEBUG_UNKNOWN_BIT,) + CALL(_C_LABEL(m88110_trap), T_UNKNOWNFLT, r30) + DONE2(DEBUG_UNKNOWN_BIT) + +/* interrupt exception handler */ +GLOBAL(m88110_interrupt_handler) + PREP2("interrupt", 1, DEBUG_INTERRUPT_BIT,) + CALL(_C_LABEL(m88110_trap), T_INT, r30) + DONE2(DEBUG_INTERRUPT_BIT) + +/* instruction access exception handler */ +GLOBAL(m88110_instruction_access_handler) + PREP2("inst", 2, DEBUG_INSTRUCTION_BIT,) + CALL(_C_LABEL(m88110_trap), T_INSTFLT, r30) + DONE2(DEBUG_INSTRUCTION_BIT) +/* + * data access exception handler -- + * See badaddr() below for info about Data_Precheck. + */ +GLOBAL(m88110_data_exception_handler) + PREP2("data", 3, DEBUG_DATA_BIT, M88110_Data_Precheck) + CALL(_C_LABEL(m88110_trap), T_DATAFLT, r30) + DONE2(DEBUG_DATA_BIT) + +/* misaligned access exception handler */ +GLOBAL(m88110_misaligned_handler) + PREP2("misalign", 4, DEBUG_MISALIGN_BIT,) + CALL(_C_LABEL(m88110_trap), T_MISALGNFLT, r30) + DONE2(DEBUG_MISALIGN_BIT) + +/* unimplemented opcode exception handler */ +GLOBAL(m88110_unimplemented_handler) + PREP2("unimp", 5, DEBUG_UNIMPLEMENTED_BIT,) + CALL(_C_LABEL(m88110_trap), T_ILLFLT, r30) + DONE2(DEBUG_UNIMPLEMENTED_BIT) + +/* privilege exception handler */ +GLOBAL(m88110_privilege_handler) + PREP2("privilege", 6, DEBUG_PRIVILEGE_BIT,) + CALL(_C_LABEL(m88110_trap), T_PRIVINFLT, r30) + DONE2(DEBUG_PRIVILEGE_BIT) + +/* + * I'm not sure what the trap(T_BNDFLT,...) does, but it doesn't send + * a signal to the process... + */ +GLOBAL(m88110_bounds_handler) + PREP2("bounds", 7, DEBUG_BOUNDS_BIT,) + CALL(_C_LABEL(m88110_trap), T_BNDFLT, r30) + DONE2(DEBUG_BOUNDS_BIT) + +/* integer divide-by-zero exception handler */ +GLOBAL(m88110_divide_handler) + PREP2("divide", 8, DEBUG_DIVIDE_BIT,) + CALL(_C_LABEL(m88110_trap), T_ZERODIV, r30) + DONE2(DEBUG_DIVIDE_BIT) + +/* integer overflow exception handler */ +GLOBAL(m88110_overflow_handler) + PREP2("overflow", 9, DEBUG_OVERFLOW_BIT,) + CALL(_C_LABEL(m88110_trap), T_OVFFLT, r30) + DONE2(DEBUG_OVERFLOW_BIT) + +/* Floating-point precise handler */ +GLOBAL(m88110_fp_precise_handler) + PREP2("FPU precise", 114, DEBUG_FPp_BIT,) + CALL(_ASM_LABEL(m88110_Xfp_precise), r0, r30) + DONE2(DEBUG_FPp_BIT) + +/* MVME197 non-maskable interrupt handler (ABORT button) */ +GLOBAL(m88110_nonmaskable) + PREP2("MVME197 non-mask", 11, DEBUG_NON_MASK_BIT,) + CALL(_C_LABEL(m88110_trap), T_NON_MASK, r30) + DONE2(DEBUG_NON_MASK_BIT) + +/* MVME197 data MMU read miss handler */ +GLOBAL(m88110_data_read_miss) + PREP2("MVME197 read miss", 12, DEBUG_197_READ_BIT,) + CALL(_C_LABEL(m88110_trap), T_197_READ, r30) + DONE2(DEBUG_197_READ_BIT) + +/* MVME197 data MMU write miss handler */ +GLOBAL(m88110_data_write_miss) + PREP2("MVME197 write miss", 13, DEBUG_197_WRITE_BIT,) + CALL(_C_LABEL(m88110_trap), T_197_WRITE, r30) + DONE2(DEBUG_197_WRITE_BIT) + +/* MVME197 inst MMU ATC miss handler */ +GLOBAL(m88110_inst_atc_miss) + PREP2("MVME197 inst miss", 14, DEBUG_197_INST_BIT,) + CALL(_C_LABEL(m88110_trap), T_197_INST, r30) + DONE2(DEBUG_197_INST_BIT) + +/* All standard system calls. */ +GLOBAL(m88110_syscall_handler) + PREP2("syscall", 128, DEBUG_SYSCALL_BIT,) + ld r13, r30, GENREG_OFF(13) + CALL(_C_LABEL(m88110_syscall), r13, r30) + DONE2(DEBUG_SYSCALL_BIT) + +/* trap 496 comes here */ +GLOBAL(m88110_bugtrap) + PREP2("bugsyscall", 496, DEBUG_BUGCALL_BIT,) + ld r9, r30, GENREG_OFF(9) + CALL(_C_LABEL(bugsyscall), r9, r30) + DONE2(DEBUG_BUGCALL_BIT) + +GLOBAL(m88110_sigsys) + PREP2("sigsys", 501, DEBUG_SIGSYS_BIT,) + CALL(_C_LABEL(m88110_trap), T_SIGSYS, r30) + DONE2(DEBUG_SIGSYS_BIT) + +GLOBAL(m88110_sigtrap) + PREP2("sigtrap", 510, DEBUG_SIGTRAP_BIT,) + CALL(_C_LABEL(m88110_trap), T_SIGTRAP, r30) + DONE2(DEBUG_SIGTRAP_BIT) + +GLOBAL(m88110_stepbpt) + PREP2("stepbpt", 504, DEBUG_SIGTRAP_BIT,) + CALL(_C_LABEL(m88110_trap), T_STEPBPT, r30) + DONE2(DEBUG_SIGTRAP_BIT) + +GLOBAL(m88110_userbpt) + PREP2("userbpt", 511, DEBUG_SIGTRAP_BIT,) + CALL(_C_LABEL(m88110_trap), T_USERBPT, r30) + DONE2(DEBUG_SIGTRAP_BIT) + +#ifdef DDB +GLOBAL(m88110_break) + PREP2("break", 130, DEBUG_BREAK_BIT,) + CALL(_C_LABEL(m88110_trap), T_KDB_BREAK, r30) + DONE2(DEBUG_BREAK_BIT) + +GLOBAL(m88110_trace) + PREP2("trace", 131, DEBUG_TRACE_BIT,) + CALL(_C_LABEL(m88110_trap), T_KDB_TRACE, r30) + DONE2(DEBUG_TRACE_BIT) + +GLOBAL(m88110_entry) + PREP2("kdb", 132, DEBUG_KDB_BIT,) + CALL(_C_LABEL(m88110_trap), T_KDB_ENTRY, r30) + DONE2(DEBUG_KDB_BIT) +#else +GLOBAL(m88110_break) + PREP2("break", 130, DEBUG_BREAK_BIT,) + CALL(_C_LABEL(m88110_trap), T_UNKNOWNFLT, r30) + DONE2(DEBUG_BREAK_BIT) + +GLOBAL(m88110_trace) + PREP2("trace", 131, DEBUG_TRACE_BIT,) + CALL(_C_LABEL(m88110_trap), T_UNKNOWNFLT, r30) + DONE2(DEBUG_TRACE_BIT) + +GLOBAL(m88110_entry) + PREP2("unknown", 132, DEBUG_KDB_BIT,) + CALL(_C_LABEL(m88110_trap), T_UNKNOWNFLT, r30) + DONE2(DEBUG_KDB_BIT) +#endif + +/* + * The error exception and reset exception handler. + * + * The error exception is raised when any other non-trap exception is raised + * while shadowing is off. This is Bad News. + * + * The reset exception is raised when the RST signal is asserted (machine + * is reset), the value of VBR is changed after exceptions are enabled, + * or when a jmp, br/bsr to addr 0 (accidents do happen :-) + * Upon a real reset, VBR is set to zero (0), so code must be at addr 0 + * to handle it!!! + * + * The shadow registers are not valid in this case (shadowing was off, if this + * was an error exception, and may not be on, if this was a reset exception). + * R1-R31 may be interesting though, so we'll save them. + * + * We'll not worry about trashing r26-29 here, + * since they aren't generally used. + */ +GLOBAL(m88110_error_handler) + br.n 1f + or r29, r0, 10 +GLOBAL(m88110_reset_handler) + or r29, r0, 0 +1: + /* pick up the slavestack */ + or r26, r0, r31 /* save old stack */ + or.u r31, r0, hi16(_ASM_LABEL(intstack_end)) + or r31, r31, lo16(_ASM_LABEL(intstack_end)) + +#ifdef DEBUG + /* zero the stack, so we'll know what we're lookin' at */ + or.u r27, r0, hi16(_C_LABEL(intstack)) + or r27, r27, lo16(_C_LABEL(intstack)) +1: cmp r28, r27, r31 + bb1 ge, r28, 2f /* branch if at the end of the stack */ + st r0, r0, r27 + br.n 1b + addu r27, r27, 4 /* bump up */ +2: /* stack has been cleared */ +#endif + + /* ensure that stack is 8-byte aligned */ + clr r31, r31, 3<0> /* round down to 8-byte boundary */ + + /* create exception frame on stack */ + subu r31, r31, SIZEOF_EF /* r31 now our E.F. */ + + /* save old R31 and other R registers */ + st.d r0 , r31, GENREG_OFF(0) + st.d r2 , r31, GENREG_OFF(2) + st.d r4 , r31, GENREG_OFF(4) + st.d r6 , r31, GENREG_OFF(6) + st.d r8 , r31, GENREG_OFF(8) + st.d r10, r31, GENREG_OFF(10) + st.d r12, r31, GENREG_OFF(12) + st.d r14, r31, GENREG_OFF(14) + st.d r16, r31, GENREG_OFF(16) + st.d r18, r31, GENREG_OFF(18) + st.d r20, r31, GENREG_OFF(20) + st.d r22, r31, GENREG_OFF(22) + st.d r24, r31, GENREG_OFF(24) + st r30, r31, GENREG_OFF(30) + st r26, r31, GENREG_OFF(31) + + /* vector is put in SRO (either 0 or 10 at this point) */ + st r29, r31, REG_OFF(EF_VECTOR) + cmp r29, r29, 0 /* is it the reset exception? */ + bb1.n ne, r29, 1f /* if not, skip */ + + /* save shadow registers (are OLD if error_handler, though) */ + ldcr r10, EPSR + st r10, r31, REG_OFF(EF_EPSR) + ldcr r10, EXIP + st r10, r31, REG_OFF(EF_EXIP) + ldcr r10, ENIP + st r10, r31, REG_OFF(EF_ENIP) + ldcr r10, DSR + st r10, r31, REG_OFF(EF_DSR) + ldcr r10, DLAR + st r10, r31, REG_OFF(EF_DLAR) + ldcr r10, DPAR + st r10, r31, REG_OFF(EF_DPAR) + ldcr r10, ISR + st r10, r31, REG_OFF(EF_ISR) + ldcr r10, ILAR + st r10, r31, REG_OFF(EF_ILAR) + ldcr r10, IPAR + st r10, r31, REG_OFF(EF_IPAR) + ldcr r10, SR1 + br.n 2f + st r10, r31, REG_OFF(EF_MODE) + +1: + /* retrieve saved shadow registers for error_handler, though) */ + or.u r30, r0, hi16(_ASM_LABEL(save_frame)) + or r30, r30, lo16(_ASM_LABEL(save_frame)) + ld r10, r30, REG_OFF(EF_EPSR) + st r10, r31, REG_OFF(EF_EPSR) + ld r10, r30, REG_OFF(EF_EXIP) + st r10, r31, REG_OFF(EF_ENIP) + ld r10, r30, REG_OFF(EF_DSR) + st r10, r31, REG_OFF(EF_DSR) + ld r10, r30, REG_OFF(EF_DLAR) + st r10, r31, REG_OFF(EF_DLAR) + ld r10, r30, REG_OFF(EF_DPAR) + st r10, r31, REG_OFF(EF_DPAR) + ld r10, r30, REG_OFF(EF_ISR) + st r10, r31, REG_OFF(EF_ISR) + ld r10, r30, REG_OFF(EF_ILAR) + st r10, r31, REG_OFF(EF_ILAR) + ld r10, r30, REG_OFF(EF_IPAR) + st r10, r31, REG_OFF(EF_IPAR) + ld r10, r30, REG_OFF(EF_ISAP) + st r10, r31, REG_OFF(EF_ISAP) + ld r10, r30, REG_OFF(EF_DSAP) + st r10, r31, REG_OFF(EF_DSAP) + ld r10, r30, REG_OFF(EF_IUAP) + st r10, r31, REG_OFF(EF_IUAP) + ld r10, r30, REG_OFF(EF_DUAP) + st r10, r31, REG_OFF(EF_DUAP) + ldcr r10, SR1 + st r10, r31, REG_OFF(EF_MODE) +2: + /* shove sr2 into EF_FPLS1 */ + ldcr r10, SR2 + st r10, r31, REG_OFF(EF_FPLS1) + + /* shove sr3 into EF_FPHS2 */ + ldcr r10, SR3 + st r10, r31, REG_OFF(EF_FPHS2) + + /* + * Cheap way to enable FPU and start shadowing again. + */ + ldcr r10, PSR + clr r10, r10, 1<PSR_FPU_DISABLE_BIT> /* enable the FPU */ + clr r10, r10, 1<PSR_SHADOW_FREEZE_BIT> /* and shadowing */ + stcr r10, PSR + FLUSH_PIPELINE + + /* put pointer to regs into r30... r31 will become a simple stack */ + or r30, r31, r0 + + subu r31, r31, 0x10 /* make some breathing space */ + st r30, r31, 0x0c /* store frame pointer on the st */ + st r30, r31, 0x08 /* store again for the debugger to recognize */ + or.u r20, r0, hi16(0x87654321) + or r20, r20, lo16(0x87654321) + st r20, r31, 0x04 + st r20, r31, 0x00 + + CALL(_C_LABEL(error_fatal), r30, r30) + + /* turn interrupts back on */ + ldcr r1, PSR + clr r1, r1, 1<PSR_INTERRUPT_DISABLE_BIT> + stcr r1, PSR + FLUSH_PIPELINE + +1: + br 1b + /* NOTREACHED */ + +ASLOCAL(m88110_setup_phase_one) + /* + * SR1: saved copy of exception-time register now holding FLAGS + * SR2: saved copy of exception-time r1 + * SR3: must be preserved .. may be the exception-time stack + * r1: return address to calling exception handler + * FLAGS: CPU status flags + * + * immediate goal: + * Decide where we're going to put the exception frame. + * Might be at the end of R31, SR3, or the thread's pcb. + */ + + /* Check if we are coming in from a FPU restart exception. + If so, the pcb will be in SR3 */ + NOP + xcr r1, r1, SR2 + NOP + NOP + NOP + + bb1 FLAG_ENABLING_FPU, FLAGS, _ASM_LABEL(m88110_use_SR3_pcb) + /* are we coming in from user mode? If so, pick up process pcb */ + bb0 FLAG_FROM_KERNEL, FLAGS, _ASM_LABEL(m88110_pickup_stack) + + /* Interrupt in kernel mode, not FPU restart */ + /* + * SR1: saved copy of exception-time register now holding FLAGS + * SR2: return address to the calling exception handler + * SR3: must be preserved; may be important for other exceptions + * FLAGS: CPU status flags + * + * immediate goal: + * We're already on the kernel stack, but not having + * needed to use SR3. We can just make room on the + * stack (r31) for our exception frame. + */ + subu r31, r31, SIZEOF_EF /* r31 now our E.F. */ + st FLAGS,r31, REG_OFF(EF_FLAGS) /* save flags */ + st r1, r31, GENREG_OFF(1) /* save prev. r1 (now free) */ + + ldcr r1, SR3 /* save previous SR3 */ + st r1, r31, REG_OFF(EF_SR3) + + addu r1, r31, SIZEOF_EF /* save previous r31 */ + br.n _ASM_LABEL(m88110_have_pcb) + st r1, r31, GENREG_OFF(31) + +ASLOCAL(m88110_use_SR3_pcb) + /* + * SR1: saved copy of exception-time register now holding FLAGS + * SR2: return address to the calling exception handler + * SR3: must be preserved; exception-time stack pointer + * FLAGS: CPU status flags + * + * immediate goal: + * An exception occurred while enabling the FPU. Since r31 is the + * user's r31 while enabling the FPU, we had put our pcb pointer + * into SR3, so make room from there for our stack pointer. + * We need to check if SR3 is the old stack pointer or the pointer + * off to the user pcb. If it pointing to the user pcb, we need to + * pick up the kernel stack. Otherwise we need to allocate a frame + * upon it. + * We look at the EPSR to see if it was from user mode + * Unfortunately, we have no registers free at the moment, but we + * know register 0 in the pcb frame will always be zero, so we can + * use it as scratch storage. + */ +#if 1 + br _C_LABEL(m88110_error_handler) +#else + /* Testing!!! */ + xcr r30, r30, SR3 /* r30 = old exception frame */ + st r1, r30, GENREG_OFF(0) /* free up r1 */ + ld r1, r30, REG_OFF(EF_EPSR) /* get back the epsr */ + bb0.n PSR_SUPERVISOR_MODE_BIT, r1, 1f /* if user mode */ + ld r1, r30, GENREG_OFF(0) /* restore r1 */ + /* we were in kernel mode - dump frame upon the stack */ + st r0, r30, GENREG_OFF(0) /* repair old frame */ + subu r30, r30, SIZEOF_EF /* r30 now our E.F. */ + st FLAGS,r30, REG_OFF(EF_FLAGS) /* save flags */ + st r1, r30, GENREG_OFF(1) /* save prev. r1 (now free) */ + + st r31, r30, GENREG_OFF(31) /* save previous r31 */ + or r31, r0, r30 /* make r31 our pointer. */ + addu r30, r30, SIZEOF_EF /* r30 now has previous SR3 */ + st r30, r31, REG_OFF(EF_SR3) /* save previous SR3 */ + br.n _ASM_LABEL(m88110_have_pcb) + xcr r30, r30, SR3 /* restore r30 */ +1: + /* + * We took an exception while restarting the FPU from user space. + * Consequently, we never picked up a stack. Do so now. + * R1 is currently free (saved in the exception frame pointed at by + * r30) + */ + or.u r1, r0, hi16(_ASM_LABEL(kstack)) + ld r1, r1, lo16(_ASM_LABEL(kstack)) + addu r1, r1, USIZE-SIZEOF_EF + st FLAGS,r1, REG_OFF(EF_FLAGS) /* store flags */ + st r31, r1, GENREG_OFF(31) /* store r31 - now free */ + st r30, r1, REG_OFF(EF_SR3) /* store old SR3 (pcb) */ + or r31, r1, r0 /* make r31 our exception fp */ + ld r1, r30, GENREG_OFF(0) /* restore old r1 */ + st r0, r30, GENREG_OFF(0) /* repair that frame */ + st r1, r31, GENREG_OFF(1) /* store r1 */ + br.n _ASM_LABEL(m88110_have_pcb) + xcr r30, r30, SR3 /* restore r30 */ +#endif + +ASLOCAL(m88110_pickup_stack) + /* + * SR1: saved copy of exception-time register now holding FLAGS + * SR2: return address to the calling exception handler + * SR3: free + * FLAGS: CPU status flags + * + * immediate goal: + * Since we're servicing an exception from user mode, we + * know that SR3 is free. We use it to free up a temporary + * register to be used in getting the thread's pcb. + */ + stcr r31, SR3 /* save previous r31 */ + + /* switch to the thread's kernel stack. */ + or.u r31, r0, hi16(_C_LABEL(curpcb)) + ld r31, r31, lo16(_C_LABEL(curpcb)) + addu r31, r31, PCB_USER_STATE /* point to user save area */ + st FLAGS,r31, REG_OFF(EF_FLAGS) /* save flags */ + st r1, r31, GENREG_OFF(1) /* save prev. r1 (now free)*/ + ldcr r1, SR3 /* save previous r31 */ + st r1, r31, GENREG_OFF(31) + /* FALLTHROUGH */ + +ASLOCAL(m88110_have_pcb) + /* + * SR1: saved copy of exception-time register now holding FLAGS + * SR2: return address to the calling exception handler + * SR3: free + * r1: free + * FLAGS: CPU status flags + * r31: our exception frame + * Valid in the exception frame: + * Exception-time r1, r31, FLAGS. + * Exception SR3, if appropriate. + * + * immediate goal: + * Save the shadow registers that need to be saved to + * the exception frame. + */ + stcr TMP, SR3 /* free up TMP, TMP2, TMP3 */ + SAVE_TMP2 + SAVE_TMP3 + + /* save some exception-time registers to the exception frame */ + ldcr TMP, EPSR + st TMP, r31, REG_OFF(EF_EPSR) + ldcr TMP2, EXIP + /* if the instruction was NOT in the delay slot, ignore ENIP */ + bb0.n 0, TMP2, 1f + st TMP2, r31, REG_OFF(EF_EXIP) + ldcr TMP3, ENIP + st TMP3, r31, REG_OFF(EF_ENIP) +1: + + /* get and store the cpu number */ + extu TMP, FLAGS, FLAG_CPU_FIELD_WIDTH<0> /* TMP = cpu# */ + st TMP, r31, REG_OFF(EF_CPU) + + /* + * Save Pbus fault status register from data and inst CMMU. + */ + ldcr TMP, ISR + ldcr TMP2, ILAR + ldcr TMP3, IPAR + st TMP, r31, REG_OFF(EF_ISR) + st TMP2, r31, REG_OFF(EF_ILAR) + st TMP3, r31, REG_OFF(EF_IPAR) + ldcr TMP, ISAP + ldcr TMP2, IUAP + st TMP, r31, REG_OFF(EF_ISAP) + st TMP2, r31, REG_OFF(EF_IUAP) + ldcr TMP, DSR + ldcr TMP2, DLAR + ldcr TMP3, DPAR + st TMP, r31, REG_OFF(EF_DSR) + st TMP2, r31, REG_OFF(EF_DLAR) + st TMP3, r31, REG_OFF(EF_DPAR) + ldcr TMP, DSAP + ldcr TMP2, DUAP + st TMP, r31, REG_OFF(EF_DSAP) + st TMP2, r31, REG_OFF(EF_DUAP) + + ldcr r1, SR2 + jmp r1 + +ASLOCAL(m88110_setup_phase_two) + /* + * SR1: saved copy of exception-time register now holding FLAGS + * SR2: free + * SR3: saved TMP + * r1: return address to calling exception handler + * TMP: possibly revised SSBR + * TMP2: free + * TMP3: free + * FLAGS: CPU status flags + * r31: our exception frame + * Valid in the exception frame: + * Exception-time r1, r31, FLAGS. + * Exception-time TMP2, TMP3. + * Exception-time espr, enip, exip. + * Exception number (EF_VECTOR). + * Dmt0 + * Other data pipeline control registers, if appropriate. + * FPU control registers, if appropriate. + * Exception SR3, if appropriate. + * + * immediate goal: + * restore the system to the exception-time state (except SR3 will + * be OUR stack pointer) so that we may resart the FPU. + */ + + RESTORE_TMP2 /* done with extra temp regs */ + RESTORE_TMP3 /* done with extra temp regs */ + + ldcr TMP, PSR + clr TMP, TMP, 1<PSR_FPU_DISABLE_BIT> /* enable the FPU */ + clr TMP, TMP, 1<PSR_SHADOW_FREEZE_BIT> /* and shadowing */ + stcr TMP, EPSR + + or.u TMP, r0, hi16(_ASM_LABEL(m88110_fpu_enable)) + or TMP, TMP, lo16(_ASM_LABEL(m88110_fpu_enable)) + stcr TMP, EXIP /* jump to here m88110_fpu_enable */ + addu TMP, TMP, 4 + stcr TMP, ENIP /* and then continue after that */ + + set FLAGS, FLAGS, 1<FLAG_ENABLING_FPU> + xcr FLAGS, FLAGS, SR1 + st r1, r31, REG_OFF(EF_RET) /* save the return address */ + ld r1, r31, GENREG_OFF(1) /* get original r1 */ + + ldcr TMP, SR3 + stcr r31, SR3 /* TMP now restored. R31 now saved in SR3 */ + ld r31, r31, GENREG_OFF(31) /* get original r31 */ + + /* + * SR1: CPU flags + * SR2: free + * SR3: pointer to our exception frame (our stack pointer) + * r1 through r31: original exception-time values + * + * Valid in the exception frame: + * Exception-time FLAGS. + * Exception-time espr, sfip, enip, exip. + * Exception number (EF_VECTOR). + * Dmt0 + * Other data pipeline control registers, if appropriate. + * FPU control registers, if appropriate. + * Exception SR3, if appropriate. + * Held temporarily in the exception frame: + * Return address to the calling exception handler. + * + * immediate goal: + * Do an RTE to restart the fpu and jump to "m88110_fpu_enable" + * Another exception (or exceptions) may be raised in + * this, which is why FLAG_ENABLING_FPU is set in SR1. + */ + NOP + RTE /* jumps to "m88110_fpu_enable" on the next line to enable the FPU. */ + +ASLOCAL(m88110_fpu_enable) + FLUSH_PIPELINE + /* Now we can handle another exception!!! */ + /* Now that EFRZ is cleared, we can clear these */ + stcr r0, ISR /* Clear ISR */ + stcr r0, ILAR /* Clear ILAR */ + stcr r0, IPAR /* Clear IPAR */ + stcr r0, DSR /* Clear DSR */ + stcr r0, DLAR /* Clear DLAR */ + stcr r0, DPAR /* Clear DPAR */ + xcr TMP, TMP, SR3 /* get E.F. pointer */ + st r30, TMP, GENREG_OFF(30) /* save previous r30, r31 */ + st r31, TMP, GENREG_OFF(31) /* save previous r30, r31 */ + or r31, TMP, r0 /* transfer E.F. pointer */ + ld TMP, r31, REG_OFF(EF_SR3) /* get previous SR3 */ + + /* make sure that the FLAG_ENABLING_FPU bit is off */ + xcr FLAGS,FLAGS,SR1 + clr FLAGS,FLAGS,1<FLAG_ENABLING_FPU> + xcr FLAGS,FLAGS,SR1 + + xcr TMP, TMP, SR3 /* replace TMP, SR3 */ + + /* now save all regs to the exception frame. */ + st r0 , r31, GENREG_OFF(0) + st r1 , r31, GENREG_OFF(1) + st r2 , r31, GENREG_OFF(2) + st r3 , r31, GENREG_OFF(3) + st r4 , r31, GENREG_OFF(4) + st r5 , r31, GENREG_OFF(5) + st r6 , r31, GENREG_OFF(6) + st r7 , r31, GENREG_OFF(7) + st r8 , r31, GENREG_OFF(8) + st r9 , r31, GENREG_OFF(9) + st r10, r31, GENREG_OFF(10) + st r11, r31, GENREG_OFF(11) + st r12, r31, GENREG_OFF(12) + st r13, r31, GENREG_OFF(13) + st r14, r31, GENREG_OFF(14) + st r15, r31, GENREG_OFF(15) + st r16, r31, GENREG_OFF(16) + st r17, r31, GENREG_OFF(17) + st r18, r31, GENREG_OFF(18) + st r19, r31, GENREG_OFF(19) + st r20, r31, GENREG_OFF(20) + st r21, r31, GENREG_OFF(21) + st r22, r31, GENREG_OFF(22) + st r23, r31, GENREG_OFF(23) + st r24, r31, GENREG_OFF(24) + st r25, r31, GENREG_OFF(25) + st r26, r31, GENREG_OFF(26) + st r27, r31, GENREG_OFF(27) + st r28, r31, GENREG_OFF(28) + st r29, r31, GENREG_OFF(29) +#ifdef JEFF_DEBUG + /* mark beginning of frame with notable value */ + or.u r20, r0, hi16(0x12345678) + or r20, r20, lo16(0x12345678) + st r20, r31, GENREG_OFF(0) +#endif + + /* get and save IPL */ + bsr.n _C_LABEL(getipl) + subu r31, r31, 32 + addu r31, r31, 32 + st r2, r31, REG_OFF(EF_MASK) + + /* + * SR1: free + * SR2: free + * SR3: previous exception-time SR3 + * r1: return address to the calling exception handler + * r2 through r30: free + * r31: our exception frame + * + * Valid in the exception frame: + * Exception-time r0 through r31. + * Exception-time FLAGS. + * Exception-time espr, enip, exip. + * Exception number (EF_VECTOR). + * DSR + * Other data pipeline control registers, if appropriate. + * FPU control registers, if appropriate. + * Exception SR3, if appropriate. + * + * immediate goal: + * Pick up a stack if we came in from user mode. + * Put a copy of the exception frame pointer into r30 + * Bump the stack a doubleword and write the exception frame pointer. + * If not an interrupt exception, turn on interrupts and service any + * outstanding data access exceptions. + * Return to calling exception handler to service the exception. + */ + + /* + * If it's not the interrupt exception, enable interrupts and + * take care of any data access exceptions...... + */ + or r30, r0, r31 /* get a copy of the e.f. pointer */ + ld r2, r31, REG_OFF(EF_EPSR) + bb1 PSR_SUPERVISOR_MODE_BIT, r2, 1f /* if in kernel mode */ + + or.u r31, r0, hi16(_ASM_LABEL(kstack)) + ld r31, r31, lo16(_ASM_LABEL(kstack)) + addu r31, r31, USIZE /* point at proper end */ +1: + + /* + * here - r30 holds a pointer to the exception frame. + * r31 is a pointer to the kernel stack/interrupt stack. + */ + subu r31, r31, 8 /* make some breathing space */ + st r30, r31, 0 /* store frame pointer on the stack */ +#ifdef DDB + st r30, r31, 4 /* store it again for the debugger */ +#endif /* DDB */ + + ld r2, r30, REG_OFF(EF_VECTOR) + bcnd.n eq0, r2, 8f + ld r14, r30, REG_OFF(EF_RET) /* load return value XXX!!! */ + cmp r3, r2, 1 /* is an interrupt? */ + bb1.n eq, r3, 8f + +#ifdef DDB + cmp r3, r2, 130 /* DDB break exception */ + bb1.n eq, r3, 8f + + cmp r3, r2, 132 /* DDB entry exception */ + bb1.n eq, r3, 8f +#endif + + /* turn interrupts back on */ + ldcr r2, PSR + clr r2, r2, 1<PSR_INTERRUPT_DISABLE_BIT> + stcr r2, PSR + FLUSH_PIPELINE + +8: + jmp r14 /* loaded above */ + + data + .align 8 /* needs align 8 for ld.d/st.d */ +ASLOCAL(save_frame) + space SIZEOF_EF +#endif /* M88110 */ diff --git a/sys/arch/luna88k/luna88k/genassym.cf b/sys/arch/luna88k/luna88k/genassym.cf new file mode 100644 index 00000000000..dfe4af42192 --- /dev/null +++ b/sys/arch/luna88k/luna88k/genassym.cf @@ -0,0 +1,144 @@ +# $OpenBSD: genassym.cf,v 1.1 2004/04/21 15:24:00 aoyama Exp $ +# +# Copyright (c) 1982, 1990 The Regents of the University of California. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)genassym.c 7.8 (Berkeley) 5/7/91 +# $Id: genassym.cf,v 1.1 2004/04/21 15:24:00 aoyama Exp $ +# + +include <sys/param.h> +include <sys/buf.h> +include <sys/time.h> +include <sys/proc.h> +include <sys/mbuf.h> +include <sys/msgbuf.h> +include <machine/cpu.h> +include <machine/trap.h> +include <machine/psl.h> +include <machine/vmparam.h> +include <sys/syscall.h> +include <sys/user.h> + +# proc fields and values +struct proc +member p_forw +member p_back +member p_addr +member p_stat +member p_wchan + +export SRUN + +# general constants +export UPAGES +define USIZE USPACE + +# pcb fields +struct pcb +member pcb_onfault +member PCB_USER_STATE user_state + +# system calls +export SYS_exit +export SYS_sigreturn + +# trapframe element indexes +define EF_R0 offsetof(struct trapframe, tf_r[0]) / sizeof(int) +define EF_FPSR offsetof(struct trapframe, tf_fpsr) / sizeof(int) +define EF_FPCR offsetof(struct trapframe, tf_fpcr) / sizeof(int) +define EF_EPSR offsetof(struct trapframe, tf_epsr) / sizeof(int) +define EF_SXIP offsetof(struct trapframe, tf_sxip) / sizeof(int) +define EF_SFIP offsetof(struct trapframe, tf_sfip) / sizeof(int) +define EF_SNIP offsetof(struct trapframe, tf_snip) / sizeof(int) +define EF_SSBR offsetof(struct trapframe, tf_ssbr) / sizeof(int) +define EF_DMT0 offsetof(struct trapframe, tf_dmt0) / sizeof(int) +define EF_DMD0 offsetof(struct trapframe, tf_dmd0) / sizeof(int) +define EF_DMA0 offsetof(struct trapframe, tf_dma0) / sizeof(int) +define EF_DMT1 offsetof(struct trapframe, tf_dmt1) / sizeof(int) +define EF_DMD1 offsetof(struct trapframe, tf_dmd1) / sizeof(int) +define EF_DMA1 offsetof(struct trapframe, tf_dma1) / sizeof(int) +define EF_DMT2 offsetof(struct trapframe, tf_dmt2) / sizeof(int) +define EF_DMD2 offsetof(struct trapframe, tf_dmd2) / sizeof(int) +define EF_DMA2 offsetof(struct trapframe, tf_dma2) / sizeof(int) +define EF_FPECR offsetof(struct trapframe, tf_fpecr) / sizeof(int) +define EF_FPHS1 offsetof(struct trapframe, tf_fphs1) / sizeof(int) +define EF_FPLS1 offsetof(struct trapframe, tf_fpls1) / sizeof(int) +define EF_FPHS2 offsetof(struct trapframe, tf_fphs2) / sizeof(int) +define EF_FPLS2 offsetof(struct trapframe, tf_fpls2) / sizeof(int) +define EF_FPPT offsetof(struct trapframe, tf_fppt) / sizeof(int) +define EF_FPRH offsetof(struct trapframe, tf_fprh) / sizeof(int) +define EF_FPRL offsetof(struct trapframe, tf_fprl) / sizeof(int) +define EF_FPIT offsetof(struct trapframe, tf_fpit) / sizeof(int) +define EF_VECTOR offsetof(struct trapframe, tf_vector) / sizeof(int) +define EF_MASK offsetof(struct trapframe, tf_mask) / sizeof(int) +define EF_MODE offsetof(struct trapframe, tf_mode) / sizeof(int) +define EF_RET offsetof(struct trapframe, tf_scratch1) / sizeof(int) +define EF_IPFSR offsetof(struct trapframe, tf_ipfsr) / sizeof(int) +define EF_DPFSR offsetof(struct trapframe, tf_dpfsr) / sizeof(int) +define EF_CPU offsetof(struct trapframe, tf_cpu) / sizeof(int) + +# m88110 trapframe element indexes +define EF_EXIP offsetof(struct trapframe, tf_exip) / sizeof(int) +define EF_ENIP offsetof(struct trapframe, tf_enip) / sizeof(int) +define EF_DSR offsetof(struct trapframe, tf_dsr) / sizeof(int) +define EF_DLAR offsetof(struct trapframe, tf_dlar) / sizeof(int) +define EF_DPAR offsetof(struct trapframe, tf_dpar) / sizeof(int) +define EF_ISR offsetof(struct trapframe, tf_isr) / sizeof(int) +define EF_ILAR offsetof(struct trapframe, tf_ilar) / sizeof(int) +define EF_IPAR offsetof(struct trapframe, tf_ipar) / sizeof(int) +define EF_ISAP offsetof(struct trapframe, tf_isap) / sizeof(int) +define EF_DSAP offsetof(struct trapframe, tf_dsap) / sizeof(int) +define EF_IUAP offsetof(struct trapframe, tf_iuap) / sizeof(int) +define EF_DUAP offsetof(struct trapframe, tf_duap) / sizeof(int) + +define SIZEOF_EF sizeof(struct trapframe) + +# more (machine-dependent) pcb fields +struct m88100_pcb +member pcb_pc +member pcb_ipl +member pcb_r14 +member pcb_r15 +member pcb_r16 +member pcb_r17 +member pcb_r18 +member pcb_r19 +member pcb_r20 +member pcb_r21 +member pcb_r22 +member pcb_r23 +member pcb_r24 +member pcb_r25 +member pcb_r26 +member pcb_r27 +member pcb_r28 +member pcb_r29 +member pcb_r30 +member pcb_sp +member pcb_fcr62 +member pcb_fcr63 diff --git a/sys/arch/luna88k/luna88k/isr.c b/sys/arch/luna88k/luna88k/isr.c new file mode 100644 index 00000000000..0ecaa9ba9b9 --- /dev/null +++ b/sys/arch/luna88k/luna88k/isr.c @@ -0,0 +1,204 @@ +/* $OpenBSD: isr.c,v 1.1 2004/04/21 15:24:00 aoyama Exp $ */ +/* $NetBSD: isr.c,v 1.5 2000/07/09 08:08:20 nisimura Exp $ */ + +/*- + * Copyright (c) 1996 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Adam Glass, Gordon W. Ross, and Jason R. Thorpe. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Link and dispatch interrupts. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/malloc.h> +#include <sys/vmmeter.h> + +#include <uvm/uvm_extern.h> + +#include <net/netisr.h> + +#include <machine/cpu.h> + +#include <luna88k/luna88k/isr.h> + +isr_autovec_list_t isr_autovec[NISRAUTOVEC]; + +extern int intrcnt[]; /* from locore.s */ + +void +isrinit() +{ + int i; + + /* Initialize the autovector lists. */ + for (i = 0; i < NISRAUTOVEC; ++i) { + LIST_INIT(&isr_autovec[i]); + } +} + +/* + * Establish an autovectored interrupt handler. + * Called by driver attach functions. + */ +void +isrlink_autovec(func, arg, ipl, priority) + int (*func)(void *); + void *arg; + int ipl; + int priority; +{ + struct isr_autovec *newisr, *curisr; + isr_autovec_list_t *list; + + if ((ipl < 0) || (ipl >= NISRAUTOVEC)) + panic("isrlink_autovec: bad ipl %d", ipl); + + newisr = (struct isr_autovec *)malloc(sizeof(struct isr_autovec), + M_DEVBUF, M_NOWAIT); + if (newisr == NULL) + panic("isrlink_autovec: can't allocate space for isr"); + + /* Fill in the new entry. */ + newisr->isr_func = func; + newisr->isr_arg = arg; + newisr->isr_ipl = ipl; + newisr->isr_priority = priority; + + /* + * Some devices are particularly sensitive to interrupt + * handling latency. The SCC, for example, can lose many + * characters if its interrupt isn't handled with reasonable + * speed. + * + * To work around this problem, each device can give itself a + * "priority". An unbuffered SCC would give itself a higher + * priority than a SCSI device, for example. + * + * This solution was originally developed for the hp300, which + * has a flat spl scheme (by necessity). Thankfully, the + * MVME systems don't have this problem, though this may serve + * a useful purpose in any case. + */ + + /* + * Get the appropriate ISR list. If the list is empty, no + * additional work is necessary; we simply insert ourselves + * at the head of the list. + */ + list = &isr_autovec[ipl]; + if (list->lh_first == NULL) { + LIST_INSERT_HEAD(list, newisr, isr_link); + return; + } + + /* + * A little extra work is required. We traverse the list + * and place ourselves after any ISRs with our current (or + * higher) priority. + */ + for (curisr = list->lh_first; curisr->isr_link.le_next != NULL; + curisr = curisr->isr_link.le_next) { + if (newisr->isr_priority > curisr->isr_priority) { + LIST_INSERT_BEFORE(curisr, newisr, isr_link); + return; + } + } + + /* + * We're the least important entry, it seems. We just go + * on the end. + */ + LIST_INSERT_AFTER(curisr, newisr, isr_link); +} + +/* + * This is the dispatcher called by the low-level + * assembly language autovectored interrupt routine. + */ +void +isrdispatch_autovec(ipl) + int ipl; +{ + struct isr_autovec *isr; + isr_autovec_list_t *list; + int handled = 0; + static int straycount, unexpected; + + if ((ipl < 0) || (ipl >= NISRAUTOVEC)) + panic("isrdispatch_autovec: bad ipl 0x%d\n", ipl); + + intrcnt[ipl]++; +#if 0 /* XXX: already counted in machdep.c */ + uvmexp.intrs++; +#endif + + list = &isr_autovec[ipl]; + if (list->lh_first == NULL) { + printf("isrdispatch_autovec: ipl %d unexpected\n", ipl); + if (++unexpected > 10) + panic("too many unexpected interrupts"); + return; + } + + /* Give all the handlers a chance. */ + for (isr = list->lh_first ; isr != NULL; isr = isr->isr_link.le_next) + handled |= (*isr->isr_func)(isr->isr_arg); + + if (handled) + straycount = 0; + else if (++straycount > 50) + panic("isr_dispatch_autovec: too many stray interrupts"); + else + printf("isrdispatch_autovec: stray level %d interrupt\n", ipl); +} + +void netintr(void); + +void +netintr() +{ +#define DONETISR(bit, fn) do { \ + if (netisr & (1 << bit)) { \ + netisr &= ~(1 << bit); \ + fn(); \ + } \ +} while (0) + +#include <net/netisr_dispatch.h> + +#undef DONETISR + +} diff --git a/sys/arch/luna88k/luna88k/isr.h b/sys/arch/luna88k/luna88k/isr.h new file mode 100644 index 00000000000..dafe618da6e --- /dev/null +++ b/sys/arch/luna88k/luna88k/isr.h @@ -0,0 +1,70 @@ +/* $OpenBSD: isr.h,v 1.1 2004/04/21 15:24:00 aoyama Exp $ */ +/* $NetBSD: isr.h,v 1.1 2000/01/05 08:49:04 nisimura Exp $ */ + +/*- + * Copyright (c) 1996 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/queue.h> + +/* + * The max size of the autovectored interrupt. + */ +#define NISRAUTOVEC 8 + +/* + * Autovectored interupt handler cookie. + */ +struct isr_autovec { + LIST_ENTRY(isr_autovec) isr_link; + int (*isr_func)(void *); + void *isr_arg; + int isr_ipl; + int isr_priority; +}; + +typedef LIST_HEAD(, isr_autovec) isr_autovec_list_t; + +/* + * Autovectored ISR priorities. These are not the same as interrupt levels. + */ +#define ISRPRI_BIO 0 +#define ISRPRI_NET 1 +#define ISRPRI_TTY 2 +#define ISRPRI_TTYNOBUF 3 + +void isrinit(void); +void isrlink_autovec(int (*)(void *), void *, int, int); +void isrdispatch_autovec(int); diff --git a/sys/arch/luna88k/luna88k/locore.S b/sys/arch/luna88k/luna88k/locore.S new file mode 100644 index 00000000000..6016368af90 --- /dev/null +++ b/sys/arch/luna88k/luna88k/locore.S @@ -0,0 +1,545 @@ +/* $OpenBSD: locore.S,v 1.1 2004/04/21 15:24:00 aoyama Exp $ */ +/* + * Copyright (c) 1998 Steve Murphree, Jr. + * Copyright (c) 1996 Nivas Madhur + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Nivas Madhur. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/* + * Mach Operating System + * Copyright (c) 1993-1991 Carnegie Mellon University + * Copyright (c) 1991 OMRON Corporation + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON AND OMRON ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON AND OMRON DISCLAIM ANY LIABILITY OF ANY KIND + * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie the + * rights to redistribute these changes. + */ + +#include "assym.h" +#include "ksyms.h" + +#include <machine/asm.h> +#include <machine/trap.h> +#include <machine/m88100.h> +#include <machine/psl.h> +#include <machine/param.h> +#include <machine/vmparam.h> + +/* + * The memory looks like: + * 0x00000 - 0x01000 trap vectors + * 0x10000 - 0x20000 ROM monitor work area + * 0x20000 == start Boot loader jumps here. + */ + text + +GLOBAL(kernelstart) +GLOBAL(kernel_text) +GLOBAL(start) +ASGLOBAL(start) + br _C_LABEL(start_text) + br _C_LABEL(start_text) + br _C_LABEL(start_text) + br _C_LABEL(start_text) + + /* This is the *real* start upon poweron or reset */ +GLOBAL(start_text) + /* + * XXX: OpenBSD/luna88k does not have its native boot + * loader, so no args are passed ... + * (below is the comment for mvme88k.) + * + * Args passed by boot loader + * r2 howto + * r3 boot controller address + * r4 esym + * r5 start of mini + * r6 end miniroot + * r7 ((Clun << 8) ; Dlun & FF) -> bootdev + * r8 cpu type (0x187, 0x188, 0x197) + */ +#if 0 /* not yet */ +/* + * (*entry)(flag, bugargs.ctrl_addr, cp, kernel.smini,kernel.emini, + * bootdev, brdtyp); + */ + or.u r13, r0, hi16(_C_LABEL(boothowto)) + st r2, r13, lo16(_C_LABEL(boothowto)) + or.u r13, r0, hi16(_C_LABEL(bootaddr)) + st r3, r13, lo16(_C_LABEL(bootaddr)) + or.u r13, r0, hi16(_C_LABEL(first_addr)) + st r4, r13, lo16(_C_LABEL(first_addr)) +#if defined(DDB) || NKSYMS > 0 + or.u r13, r0, hi16(_C_LABEL(esym)) + st r4, r13, lo16(_C_LABEL(esym)) +#endif + or.u r13, r0, hi16(_C_LABEL(bootdev)) + st r7, r13, lo16(_C_LABEL(bootdev)) + or.u r13, r0, hi16(_C_LABEL(brdtyp)) + st r8, r13, lo16(_C_LABEL(brdtyp)) + + /* set _cputyp */ + cmp r2, r8, BRD_197 /* r8 contains brdtyp */ + bb1 ne, r2, 1f /* if it's a '197, CPU is 88110 */ + or.u r13, r0, hi16(CPU_88110) + or r8, r13, lo16(CPU_88110) + br 2f +1: + or.u r13, r0, hi16(CPU_88100) + or r8, r13, lo16(CPU_88100) +2: + or.u r13, r0, hi16(_C_LABEL(cputyp)) + st r8, r13, lo16(_C_LABEL(cputyp)) +#endif /* not yet */ + + /* + * CPU Initialization + * + * Every CPU starts from here.. + * (well, from 'start' above, which just jumps here). + * + * I use r11 and r22 here 'cause they're easy to not + * get mixed up -- r10, for example, looks too similar + * to r0 when not being careful.... + * + * Ensure that the PSR is as we like: + * supervisor mode + * big-endian byte ordering + * concurrent operation allowed + * carry bit clear (I don't think we really care about this) + * FPU enabled + * misaligned access raises an exception + * interrupts disabled + * shadow registers frozen + * + * The manual says not to disable interrupts and freeze shadowing + * at the same time because interrupts are not actually disabled + * until after the next instruction. Well, if an interrupt + * occurs now, we're in deep anyway, so I'm going to do + * the two together. + * + * Upon a reset (or poweron, I guess), the PSR indicates: + * supervisor mode + * interrupts, shadowing, FPU, missaligned exception: all disabled + * + * We'll just construct our own turning on what we want. + * + * jfriedl@omron.co.jp + */ + + stcr r0, SSBR /* clear this for later */ + stcr r0, SR0 /* clear "current thread" */ + stcr r0, SR1 /* clear the CPU flags */ + + set r11, r0, 1<PSR_SUPERVISOR_MODE_BIT> + set r11, r11, 1<PSR_INTERRUPT_DISABLE_BIT> + set r11, r11, 1<PSR_GRAPHICS_DISABLE_BIT> + + stcr r11, PSR + FLUSH_PIPELINE + stcr r0, VBR /* set Vector Base Register to 0, ALWAYS! */ + FLUSH_PIPELINE + + /* clear BSS. Boot loader might have already done this... */ + or.u r2, r0, hi16(_C_LABEL(edata)) + or r2, r2, lo16(_C_LABEL(edata)) + or.u r4, r0, hi16(_C_LABEL(end)) + or r4, r4, lo16(_C_LABEL(end)) + bsr.n _bzero /* bzero(edata, end-edata) */ + subu r3, r4, r2 + + /* + * First time to count how many CPUs to attach + */ + or.u r11, r0, hi16(_ASM_LABEL(initialized_cpu_lock)) + or r11, r11, lo16(_ASM_LABEL(initialized_cpu_lock)) +1: + FLUSH_PIPELINE + or r22, r0, 1 + xmem r22, r11, r0 /* If r22 gets 0, we have the lock.. */ + bcnd eq0, r22, 3f /* ..but if not, we must wait */ +2: + /* just watch the lock until it looks clear */ + ld r22, r11, r0 + bcnd eq0, r22, 1b + br 2b /* looks clear -- try to grab */ +3: + FLUSH_PIPELINE + or.u r11, r0, hi16(_ASM_LABEL(initialized_cpus)) + ld r22, r11, lo16(_ASM_LABEL(initialized_cpus)) + add r23, r22, 1 + st r23, r11, lo16(_ASM_LABEL(initialized_cpus)) + + or.u r11, r0, hi16(_ASM_LABEL(initialized_cpu_lock)) + st r0, r11, lo16(_ASM_LABEL(initialized_cpu_lock)) + /* + * Now we view with any other processors to see who's the master. + * We first try to obtain a lock to see who's allowed + * to check/set the master lock. + */ + or.u r11, r0, hi16(_ASM_LABEL(inter_processor_lock)) + or r11, r11, lo16(_ASM_LABEL(inter_processor_lock)) +1: + FLUSH_PIPELINE + or r22, r0, 1 + xmem r22, r11, r0 /* If r22 gets 0, we have the lock.. */ + bcnd eq0, r22, 4f /* ..but if not, we must wait */ +2: + /* just watch the lock until it looks clear */ + ld r22, r11, r0 + bcnd ne0, r22, 2b + /* since we can be here with caches off, add a few nops to + keep the bus from getting overloaded */ + or r2, r0, lo16(1000) +3: + subu r2, r2, 1 + bcnd eq0, r2, 3b + br 1b /* looks clear -- try to grab */ +4: + /* now try to grab the master_processor_chosen prize */ + FLUSH_PIPELINE + or.u r11, r0, hi16(_ASM_LABEL(master_processor_chosen)) + or r11, r11, lo16(_ASM_LABEL(master_processor_chosen)) + or r22, r0, 1 + xmem r22, r11, r0 + + /* + * If r22 is not clear we're a slave, + * otherwise we're first and the master. + * + * Note that we haven't released the interprocessor lock.... + * We'll do that when we're ready for another CPU to go. + * (if we're the master, we'll do that in master_start below. + * if we're a slave, we'll do it in slave_start below). + */ + bcnd ne0, r22, _ASM_LABEL(slave_start) + /* fall through to master start if that's appropriate */ + +ASLOCAL(master_start) + /* + * Switch to interrupt stack + * Use idle_u's stack instead? + */ + or.u r31, r0, hi16(_ASM_LABEL(intstack_end)) + or r31, r31, lo16(_ASM_LABEL(intstack_end)) + clr r31, r31, 3<0> /* round down to 8-byte boundary */ + + or.u r3, r0, hi16(_C_LABEL(vector_list)) + or r3, r3, lo16(_C_LABEL(vector_list)) + + bsr.n _C_LABEL(vector_init) + ldcr r2, VBR + + /* PIO stuff */ + or r10, r0, 0xb6 /* initialize pio 0 */ + or.u r11, r0, hi16(0x4900000c) /* 0x4900000c: PIO0 ctrl */ + st.b r10, r11, lo16(0x4900000c) + + /* read dispswitch setting */ + ld.bu r10, r11, lo16(0x49000000) /* dipsw-1 (from portA) */ + mak r10, r10, 0<8> /* shift left 8 bit */ + ld.bu r12, r11, lo16(0x49000004) /* dipsw-2 (from portB) */ + or r10, r10, r12 + + or.u r11, r0, hi16(_dipswitch) + st.h r10, r11, lo16(_dipswitch) + + bb1 14, r10, 1f /* XXX: if dipsw-1:2 is on, */ + or r10, r0, r0 /* XXX: console is ttya */ + or.u r11, r0, hi16(_sysconsole) + st r10, r11, lo16(_sysconsole) + +1: + or r10, r0, 0x84 /* initialize pio1 */ + or.u r11, r0, hi16(0x4d00000c) + st.b r10, r11, lo16(0x4d00000c) + or r10, r0, 0x9 /* port c bit 1 on */ + st.b r10, r11, lo16(0x4d00000c) + + or.u r10, r0, hi16(0xe1000010) /* clear scsi int */ + ld.b r11, r10, lo16(0xe1000010) + st.b r11, r10, lo16(0xe1000010) + + /* write 0x41000000 to escape rom */ + or.u r2, r0, hi16(0x41000000) + st r0, r2, lo16(0x41000000) + + /* still on int stack */ + bsr.n _C_LABEL(luna88k_bootstrap) + subu r31, r31, 40 + addu r31, r31, 40 + + /* + * we now know our cpu number, so we + * can set interrupt_stack[cpu_number()] = intstack + */ + ldcr r10, SR1 + extu r10, r10, FLAG_CPU_FIELD_WIDTH<0> /* r10 <-- CPU# */ + + /* figure interrupt_stack[cpu_number()] */ + or.u r11, r0, hi16(_C_LABEL(interrupt_stack)) + or r11, r11, lo16(_C_LABEL(interrupt_stack)) + or.u r12, r0, hi16(_C_LABEL(intstack)) + or r12, r12, lo16(_C_LABEL(intstack)) + st r12, r11 [r10] + + /* switch to proc0 uarea */ + or.u r10, r0, hi16(UADDR) + or r31, r10,lo16(UADDR) + addu r31, r31, USIZE - 8 + + /* make the call: main() */ + or.u r2, r0, hi16(UADDR) + or r2, r2,lo16(UADDR) + addu r2, r2, USIZE - 8 + subu r31, r31, 40 + bsr _C_LABEL(main) + addu r31, r31, 40 + bsr _C_LABEL(panic) + +/* + * slave CPUs starts here + */ +ASLOCAL(slave_start) + /* + * While holding the inter_processor_lock, the slave cpu can use + * the slavestack to call slave_pre_main and determine its cpu number. + * After that, however, it should switch over to the interrupt stack + * associated with its cpu. + */ + + /* r31 <-- slavestack */ + or.u r31, r0, hi16(_ASM_LABEL(slavestack_end)) + or r31, r31, lo16(_ASM_LABEL(slavestack_end)) + clr r31, r31, 3<0> /* round down to 8-byte boundary */ + + bsr.n _C_LABEL(slave_pre_main) /* set cpu number */ + subu r31, r31, 48 /* allocate frame */ + + bsr _C_LABEL(get_slave_stack) + addu r31, r2, INTSTACK_SIZE + + /* + * SR1 now contains our cpu number. We can now release the + * inter_processor_lock, as we are done with the slavestack. + * We also have an interrupt stack + */ + + or.u r10, r0, hi16(_ASM_LABEL(inter_processor_lock)) + st r0, r10, lo16(_ASM_LABEL(inter_processor_lock)) + + br.n _C_LABEL(slave_main) /* does not return */ + subu r31, r31, 40 /* allocate frame */ + +GLOBAL(spin_cpu) + or.u r3, r0, hi16(_C_LABEL(start_text)) + or r3, r3, lo16(_C_LABEL(start_text)) + or r9, r0, 0x100 /* .FORKMPU */ + tb0 0, r0, 0x200-16 /* call 188Bug */ + jmp r1 + +/* + * void delay(int count) + * + * The processor loops (busy waits) for the given number of microseconds: + * Thus, delay(1000000) will delay for one second. + * (originally from Mach 2.5) + * + * REGISTER USAGE: + * IN r1 - return address + * IN r2 - (signed int) number of microseconds + * r3 - (float) number of microseconds + * r4/5 - (double) number of cycles per microsecond + * r6 - (float) number of cycles to delay + * r7 - (signed) number of cycles to delay + */ + +GLOBAL(delay) + flt.ss r3, r2 /* convert microseconds from signed int to float */ + or.u r4, r0, hi16(_cycles_per_microsecond) + ld.d r4, r4, lo16(_cycles_per_microsecond) + fmul.ssd r6, r3, r4 /* convert microseconds to cycles */ + int.ss r7, r6 /* convert cycles from float to signed int */ + subu r7, r7, 25 /* subtract for overhead of above instruction */ + + /* now loop for the given number of cycles */ +1: + bcnd.n gt0, r7, 1b + subu r7, r7, 2 /* two cycles per iteration */ + + jmp r1 /* return */ + +/*****************************************************************************/ + + data + .align 4096 /* SDT (segment descriptor table */ +GLOBAL(kernel_sdt) + space (0x2000) /* 8K - 4K phys, 4K virt*/ +GLOBAL(ret_addr) + word 0 +ASLOCAL(initialized_cpu_lock) + /* XMEM spin lock -- to count CPUs */ + word 0 +ASLOCAL(initialized_cpus) + /* CPU counter to initialize */ + word 0 +ASLOCAL(master_processor_chosen) + /* The first processor that XMEMs this becomes the master */ + word 0 +ASLOCAL(inter_processor_lock) + /* XMEM spin lock -- controls access to master_processor_chosen */ + word 0 + + .align 4096 +GLOBAL(intstack) + space (INTSTACK_SIZE) /* 16K, just to be safe */ +ASGLOBAL(intstack_end) +ASGLOBAL(slavestack) + space (NBPG) /* 4K, small, interim stack */ +ASGLOBAL(slavestack_end) + +/* + * When a process exits and its u. area goes away, we set curpcb to point + * to this `u.', leaving us with something to use for an interrupt stack, + * and letting all the register save code have a pcb_uw to examine. + * This is also carefully arranged (to come just before u0, so that + * process 0's kernel stack can quietly overrun into it during bootup, if + * we feel like doing that). + * Should be page aligned. + */ + .align 4096 +GLOBAL(idle_u) + space UPAGES * NBPG + +/* + * Process 0's u. + * + * This must be page aligned + */ + .align 4096 +ASLOCAL(u0) + space UPAGES * NBPG + +/* + * UPAGES get mapped to kstack + */ + +ASGLOBAL(kstack) + word UADDR + +#if defined(DDB) || NKSYMS > 0 +GLOBAL(esym) + word 0 +#endif /* DDB || NKSYMS > 0 */ + +GLOBAL(intiobase) + word 0 | KVA of base of internal IO space +GLOBAL(intiolimit) + word 0 | KVA of end of internal IO space + +GLOBAL(proc0paddr) + word _ASM_LABEL(u0) /* KVA of proc0 uarea */ + +/* + * curpcb points to the current pcb (and hence u. area). + * Initially this is the special one. + */ +/* + * pcb is composed of kernel state + user state + * I may have to change curpcb to u0 + PCB_USER based on what + * other parts expect XXX nivas + */ +GLOBAL(curpcb) + word _ASM_LABEL(u0) /* curpcb = &u0 */ + +/* + * Trampoline code. Gets copied to the top of + * user stack in exec. + */ +GLOBAL(sigcode) /* r31 points to sigframe */ + ld r2, r31, 0 /* signo */ + ld r3, r31, 4 /* siginfo_t* */ + ld r4, r31, 8 /* sigcontext* */ + ld r5, r31, 12 /* handler */ + jsr.n r5 + subu r31, r31, 40 /* give some stack space */ + addu r31, r31, 40 /* restore old sp value */ + ld r2, r31, 8 /* sigcontext* */ + or r13, r0, SYS_sigreturn + tb0 0, r0, 128 /* syscall trap, calling sigreturn */ + NOP | failure return +#if 0 + NOP | success return +#endif + /* sigreturn will not return unless it fails */ + or r13, r0, SYS_exit + tb0 0, r0, 128 /* syscall trap, exit */ + /* + * this never returns, but we need to provide fetchable instructions + * for the 88100 pipeline. + */ + NOP + NOP +GLOBAL(esigcode) + +/* interrupt counters */ +GLOBAL(intrnames) + string "spur\0" + string "lev1\0" + string "lev2\0" + string "lev3\0" + string "lev4\0" + string "lev5\0" + string "lev6\0" + string "lev7\0" + string "clk\0" + string "sclk\0" + string "pclk\0" + string "nmi\0" +GLOBAL(eintrnames) + .align 8 +GLOBAL(intrcnt) + word 0,0,0,0,0,0,0,0,0,0,0,0 +GLOBAL(eintrcnt) diff --git a/sys/arch/luna88k/luna88k/locore_asm_routines.S b/sys/arch/luna88k/luna88k/locore_asm_routines.S new file mode 100644 index 00000000000..d8b52dce159 --- /dev/null +++ b/sys/arch/luna88k/luna88k/locore_asm_routines.S @@ -0,0 +1,1621 @@ +/* $OpenBSD: locore_asm_routines.S,v 1.1 2004/04/21 15:24:01 aoyama Exp $ */ +/* + * Mach Operating System + * Copyright (c) 1993-1992 Carnegie Mellon University + * Copyright (c) 1991 OMRON Corporation + * Copyright (c) 1996 Nivas Madhur + * Copyright (c) 1998 Steve Murphree, Jr. + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON AND OMRON ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON AND OMRON DISCLAIM ANY LIABILITY OF ANY KIND + * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie the + * rights to redistribute these changes. + */ + +#include "assym.h" + +#include <sys/errno.h> + +#include <machine/asm.h> +#include <machine/board.h> +#include <machine/cpu_number.h> +#include <machine/trap.h> + +/***************************************************************************** + * DO_LOAD_ADDRESS + * + * unsigned int do_load_word(address, supervisor_mode) + * vaddr_t address; \\ in r2 + * boolean_t supervisor_mode; \\ in r3 + * + * Return the word at ADDRESS (from user space if SUPERVISOR_MODE is zero, + * supervisor space if non-zero). + * + */ + +ENTRY(do_load_word) /* do_load_word(address, supervisor) */ + bcnd ne0,r3,1f +#ifdef ERRATA__XXX_USR + NOP + ld.usr r2,r2,r0 + NOP + NOP + NOP +#else + ld.usr r2,r2,r0 +#endif + br 2f +1: ld r2,r2,r0 +2: jmp r1 + +ENTRY(do_load_half) /* do_load_half(address, supervisor) */ + bcnd ne0,r3,1f +#ifdef ERRATA__XXX_USR + NOP + ld.h.usr r2,r2,r0 + NOP + NOP + NOP +#else + ld.h.usr r2,r2,r0 +#endif + br 2f +1: ld.h r2,r2,r0 +2: jmp r1 + +ENTRY(do_load_byte) /* do_load_byte(address, supervisor) */ + bcnd ne0,r3,1f +#ifdef ERRATA__XXX_USR + NOP + ld.b.usr r2,r2,r0 + NOP + NOP + NOP +#else + ld.b.usr r2,r2,r0 +#endif + br 2f +1: ld.b r2,r2,r0 +2: jmp r1 + +ENTRY(do_store_word) /* do_store_word(address, data, supervisor) */ + bcnd ne0,r4,1f +#ifdef ERRATA__XXX_USR + NOP + st.usr r3,r2,r0 + NOP + NOP + NOP +#else + st.usr r3,r2,r0 +#endif + br 2f +1: st r3,r2,r0 +2: jmp r1 + +ENTRY(do_store_half) /* do_store_half(address, data, supervisor) */ + bcnd ne0,r4,1f +#ifdef ERRATA__XXX_USR + NOP + st.h.usr r3,r2,r0 + NOP + NOP + NOP +#else + st.h.usr r3,r2,r0 +#endif + br 2f +1: st.h r3,r2,r0 +2: jmp r1 + +ENTRY(do_store_byte) /* do_store_byte(address, data, supervisor) */ + bcnd ne0,r4,1f +#ifdef ERRATA__XXX_USR + NOP + st.b.usr r3,r2,r0 + NOP + NOP + NOP +#else + st.b.usr r3,r2,r0 +#endif + br 2f +1: st.b r3,r2,r0 +2: jmp r1 + +ENTRY(do_xmem_word) /* do_xmem_word(address, data, supervisor) */ + bcnd ne0,r4,1f +#ifdef ERRATA__XXX_USR + NOP + xmem.usr r3,r2,r0 + NOP + NOP + NOP +#else + xmem.usr r3,r2,r0 +#endif + br 2f +1: xmem r3,r2,r0 +2: jmp r1 + +ENTRY(do_xmem_byte) /* do_xmem_byte(address, data, supervisor) */ + bcnd ne0,r4,1f +#ifdef ERRATA__XXX_USR + NOP + xmem.bu.usr r3,r2,r0 + NOP + NOP + NOP +#else + xmem.bu.usr r3,r2,r0 +#endif + br 2f +1: xmem.bu r3,r2,r0 +2: jmp r1 + +/* + * Copy specified amount of data from user space into the kernel + * copyin(from, to, len) + * r2 == from (user source address) + * r3 == to (kernel destination address) + * r4 == length + * (r1=return addr) + */ + +#define SRC r2 +#define DEST r3 +#define LEN r4 + +ENTRY(copyin) + /* set up fault handler */ + or.u r5, r0, hi16(_C_LABEL(curpcb)) + ld r6, r5, lo16(_C_LABEL(curpcb)) + or.u r5, r0, hi16(_ASM_LABEL(Lciflt)) + or r5, r5, lo16(_ASM_LABEL(Lciflt)) + st r5, r6, PCB_ONFAULT /* pcb_onfault = Lciflt */ + +#if 0 + bcnd ne0, LEN, 1f /* XXX optimize len = 0 case */ + or r2, r0, 0 + br _ASM_LABEL(Lcidone) +1: bcnd lt0, LEN, _ASM_LABEL(Lciflt) /* EFAULT if len < 0 */ +#endif + + /* If it's a small length (less than 8), then do byte-by-byte */ + cmp r9, LEN, 8 + bb1 lt, r9, _ASM_LABEL(copyin_byte_only) + + /* If they're not aligned similarly, use byte only... */ + xor r9, SRC, DEST + mask r8, r9, 0x3 + bcnd ne0, r8, _ASM_LABEL(copyin_byte_only) + + /* + * At this point, we don't know if they're word aligned or not, + * but we know that what needs to be done to one to align + * it is what's needed for the other. + */ + bb1 0, SRC, _ASM_LABEL(copyin_left_align_to_halfword) +ASLOCAL(copyin_left_aligned_to_halfword) + bb1 1, SRC, _ASM_LABEL(copyin_left_align_to_word) +ASLOCAL(copyin_left_aligned_to_word) + bb1 0, LEN, _ASM_LABEL(copyin_right_align_to_halfword) +ASLOCAL(copyin_right_aligned_to_halfword) + bb1 1, LEN, _ASM_LABEL(copyin_right_align_to_word) +ASLOCAL(copyin_right_aligned_to_word) + + /* At this point, both SRC and DEST are aligned to a word */ + /* boundry, and LEN is an even multiple of 4. */ + bb1.n 2, LEN, _ASM_LABEL(copyin_right_align_to_doubleword) + or r7, r0, 4 + +ASLOCAL(copyin_right_aligned_to_doubleword) +#ifdef ERRATA__XXX_USR + NOP + ld.usr r5, SRC, r0 + NOP + NOP + NOP + ld.usr r6, SRC, r7 + NOP + NOP + NOP +#else + ld.usr r5, SRC, r0 + ld.usr r6, SRC, r7 +#endif + subu LEN, LEN, 8 + st r5, DEST, r0 + addu SRC, SRC, 8 + st r6, DEST, r7 + bcnd.n ne0, LEN, _ASM_LABEL(copyin_right_aligned_to_doubleword) + addu DEST, DEST, 8 + br.n _ASM_LABEL(Lcidone) + or r2, r0, r0 /* successful return */ + + /***************************************************/ + +ASLOCAL(copyin_left_align_to_halfword) +#ifdef ERRATA__XXX_USR + NOP + ld.b.usr r5, SRC, r0 + NOP + NOP + NOP +#else + ld.b.usr r5, SRC, r0 +#endif + subu LEN, LEN, 1 + st.b r5, DEST, r0 + addu SRC, SRC, 1 + br.n _ASM_LABEL(copyin_left_aligned_to_halfword) + addu DEST, DEST, 1 + +ASLOCAL(copyin_left_align_to_word) +#ifdef ERRATA__XXX_USR + NOP + ld.h.usr r5, SRC, r0 + NOP + NOP + NOP +#else + ld.h.usr r5, SRC, r0 +#endif + subu LEN, LEN, 2 + st.h r5, DEST, r0 + addu SRC, SRC, 2 + br.n _ASM_LABEL(copyin_left_aligned_to_word) + addu DEST, DEST, 2 + +ASLOCAL(copyin_right_align_to_halfword) + subu LEN, LEN, 1 +#ifdef ERRATA__XXX_USR + NOP + ld.b.usr r5, SRC, LEN + NOP + NOP + NOP +#else + ld.b.usr r5, SRC, LEN +#endif + br.n _ASM_LABEL(copyin_right_aligned_to_halfword) + st.b r5, DEST, LEN + +ASLOCAL(copyin_right_align_to_word) + subu LEN, LEN, 2 +#ifdef ERRATA__XXX_USR + NOP + ld.h.usr r5, SRC, LEN + NOP + NOP + NOP +#else + ld.h.usr r5, SRC, LEN +#endif + br.n _ASM_LABEL(copyin_right_aligned_to_word) + st.h r5, DEST, LEN + +ASLOCAL(copyin_right_align_to_doubleword) + subu LEN, LEN, 4 +#ifdef ERRATA__XXX_USR + NOP + ld.usr r5, SRC, LEN + NOP + NOP + NOP +#else + ld.usr r5, SRC, LEN +#endif + bcnd.n ne0, LEN, _ASM_LABEL(copyin_right_aligned_to_doubleword) + st r5, DEST, LEN + br.n _ASM_LABEL(Lcidone) + or r2, r0, r0 /* successful return */ + +ASLOCAL(copyin_byte_only) + bcnd eq0, LEN, 2f +1: + subu LEN, LEN, 1 +#ifdef ERRATA__XXX_USR + NOP + ld.b.usr r5, SRC, LEN + NOP + NOP + NOP +#else + ld.b.usr r5, SRC, LEN +#endif + bcnd.n ne0, LEN, 1b + st.b r5, DEST, LEN +2: + br.n _ASM_LABEL(Lcidone) + or r2, r0, r0 /* successful return */ + +ASLOCAL(Lcidone) + or.u r5,r0,hi16(_C_LABEL(curpcb)) + ld r6,r5,lo16(_C_LABEL(curpcb)) + jmp.n r1 + st r0,r6,PCB_ONFAULT + +ASLOCAL(Lciflt) + br.n _ASM_LABEL(Lcidone) + or r2, r0, EFAULT /* return fault */ + +#undef SRC +#undef DEST +#undef LEN +/*######################################################################*/ +/*######################################################################*/ + +/* + * Copy a null terminated string from the user space to the kernel + * address space. + * + * copyinstr(from, to, maxlen, &lencopied) + * r2 == from + * r3 == to + * r4 == maxlen + * r5 == len actually transferred (includes the terminating NULL!!!) + * r6 & r7 - used as temporaries + */ +#define SRC r2 +#define DEST r3 +#define CNT r4 +#define LEN r5 + +ENTRY(copyinstr) + + /* setup fault handler */ + or.u r6, r0, hi16(_C_LABEL(curpcb)) + ld r7, r6, lo16(_C_LABEL(curpcb)) + or.u r6, r0, hi16(_ASM_LABEL(Lcisflt)) + or r6, r6, lo16(_ASM_LABEL(Lcisflt)) + st r6, r7, PCB_ONFAULT + or r6, r0, 0 + bcnd lt0, CNT, _ASM_LABEL(Lcisflt) + bcnd eq0, CNT, _ASM_LABEL(Lcistoolong) +1: +#ifdef ERRATA__XXX_USR + NOP + ld.bu.usr r7, SRC, r6 + NOP + NOP + NOP +#else + ld.bu.usr r7, SRC, r6 +#endif + st.b r7, DEST, r6 + bcnd.n eq0, r7, 2f /* all done */ + addu r6, r6, 1 + cmp r7, r6, CNT + bb1 lt, r7, 1b + +ASLOCAL(Lcistoolong) + or r2, r0, ENAMETOOLONG /* overflow */ + +ASLOCAL(Lcisnull) + bcnd eq0,r6, _ASM_LABEL(Lcisdone) /* do not attempt to clear last byte */ + /* if we did not write to the string */ + subu r6, r6, 1 + st.b r0, DEST, r6 /* clear last byte */ + br.n _ASM_LABEL(Lcisdone) + addu r6, r6, 1 +2: /* all done */ + or r2, r0, 0 + +ASLOCAL(Lcisdone) + bcnd eq0, LEN, 3f + st r6, r0, LEN +3: + or.u r5,r0,hi16(_C_LABEL(curpcb)) + ld r6,r5,lo16(_C_LABEL(curpcb)) + jmp.n r1 + st r0,r6,PCB_ONFAULT /* clear the handler */ + +ASLOCAL(Lcisflt) + br.n _ASM_LABEL(Lcisnull) + or r2, r0, EFAULT /* return fault */ + +#undef SRC +#undef DEST +#undef CNT +#undef LEN + +/* + * Copy specified amount of data from kernel to the user space + * Copyout(from, to, len) + * r2 == from (kernel source address) + * r3 == to (user destination address) + * r4 == length + */ + +#define SRC r2 +#define DEST r3 +#define LEN r4 + +ENTRY(copyout) + /* setup fault handler */ + or.u r5, r0, hi16(_C_LABEL(curpcb)) + ld r6, r5, lo16(_C_LABEL(curpcb)) + or.u r5, r0, hi16(_ASM_LABEL(Lcoflt)) + or r5, r5, lo16(_ASM_LABEL(Lcoflt)) + st r5, r6, PCB_ONFAULT /* pcb_onfault = Lcoflt */ +#if 0 + bcnd ne0, LEN, 1f /* XXX optimize len = 0 case */ + or r2, r0, 0 + br _ASM_LABEL(Lcodone) +1: bcnd lt0, LEN, _ASM_LABEL(Lcoflt) /* EFAULT if len < 0 */ +#endif + /* If it's a small length (less than 8), then do byte-by-byte */ + cmp r9, LEN, 8 + bb1 lt, r9, _ASM_LABEL(copyout_byte_only) + + /* If they're not aligned similarly, use byte only... */ + xor r9, SRC, DEST + mask r8, r9, 0x3 + bcnd ne0, r8, _ASM_LABEL(copyout_byte_only) + + /* + * At this point, we don't know if they're word aligned or not, + * but we know that what needs to be done to one to align + * it is what's needed for the other. + */ + bb1 0, SRC, _ASM_LABEL(copyout_left_align_to_halfword) +ASLOCAL(copyout_left_aligned_to_halfword) + bb1 1, SRC, _ASM_LABEL(copyout_left_align_to_word) +ASLOCAL(copyout_left_aligned_to_word) + bb1 0, LEN, _ASM_LABEL(copyout_right_align_to_halfword) +ASLOCAL(copyout_right_aligned_to_halfword) + bb1 1, LEN, _ASM_LABEL(copyout_right_align_to_word) +ASLOCAL(copyout_right_aligned_to_word) + + /* + * At this point, both SRC and DEST are aligned to a word + * boundry, and LEN is an even multiple of 4. + */ + bb1.n 2, LEN, _ASM_LABEL(copyout_right_align_to_doubleword) + or r7, r0, 4 + +ASLOCAL(copyout_right_aligned_to_doubleword) + ld r5, SRC, r0 + ld r6, SRC, r7 + subu LEN, LEN, 8 +#ifdef ERRATA__XXX_USR + NOP + st.usr r5, DEST, r0 + NOP + NOP + NOP +#else + st.usr r5, DEST, r0 +#endif + addu SRC, SRC, 8 +#ifdef ERRATA__XXX_USR + NOP + st.usr r6, DEST, r7 + NOP + NOP + NOP +#else + st.usr r6, DEST, r7 +#endif + bcnd.n ne0, LEN, _ASM_LABEL(copyout_right_aligned_to_doubleword) + addu DEST, DEST, 8 + or r2, r0, r0 /* successful return */ + br _ASM_LABEL(Lcodone) + + /***************************************************/ +ASLOCAL(copyout_left_align_to_halfword) + ld.b r5, SRC, r0 + subu LEN, LEN, 1 +#ifdef ERRATA__XXX_USR + NOP + st.b.usr r5, DEST, r0 + NOP + NOP + NOP +#else + st.b.usr r5, DEST, r0 +#endif + addu SRC, SRC, 1 + br.n _ASM_LABEL(copyout_left_aligned_to_halfword) + addu DEST, DEST, 1 + +ASLOCAL(copyout_left_align_to_word) + ld.h r5, SRC, r0 + subu LEN, LEN, 2 +#ifdef ERRATA__XXX_USR + NOP + st.h.usr r5, DEST, r0 + NOP + NOP + NOP +#else + st.h.usr r5, DEST, r0 +#endif + addu SRC, SRC, 2 + br.n _ASM_LABEL(copyout_left_aligned_to_word) + addu DEST, DEST, 2 + +ASLOCAL(copyout_right_align_to_halfword) + subu LEN, LEN, 1 + ld.b r5, SRC, LEN +#ifdef ERRATA__XXX_USR + NOP + st.b.usr r5, DEST, LEN + NOP + NOP + NOP + br _ASM_LABEL(copyout_right_aligned_to_halfword) +#else + br.n _ASM_LABEL(copyout_right_aligned_to_halfword) + st.b.usr r5, DEST, LEN +#endif + +ASLOCAL(copyout_right_align_to_word) + subu LEN, LEN, 2 + ld.h r5, SRC, LEN +#ifdef ERRATA__XXX_USR + NOP + st.h.usr r5, DEST, LEN + NOP + NOP + NOP + br _ASM_LABEL(copyout_right_aligned_to_word) +#else + br.n _ASM_LABEL(copyout_right_aligned_to_word) + st.h.usr r5, DEST, LEN +#endif + +ASLOCAL(copyout_right_align_to_doubleword) + subu LEN, LEN, 4 + ld r5, SRC, LEN +#ifdef ERRATA__XXX_USR + NOP + st.usr r5, DEST, LEN + NOP + NOP + NOP + bcnd ne0, LEN, _ASM_LABEL(copyout_right_aligned_to_doubleword) +#else + bcnd.n ne0, LEN, _ASM_LABEL(copyout_right_aligned_to_doubleword) + st.usr r5, DEST, LEN +#endif + br.n _ASM_LABEL(Lcodone) + or r2, r0, r0 /* successful return */ + +ASLOCAL(copyout_byte_only) + bcnd eq0, LEN, 2f +1: + subu LEN, LEN, 1 + ld.b r5, SRC, LEN +#ifdef ERRATA__XXX_USR + NOP + st.b.usr r5, DEST, LEN + NOP + NOP + NOP + bcnd ne0, LEN, 1b +#else + bcnd.n ne0, LEN, 1b + st.b.usr r5, DEST, LEN +#endif + +2: + br.n _ASM_LABEL(Lcodone) + or r2, r0, r0 /* successful return */ + +ASLOCAL(Lcodone) + or.u r5,r0,hi16(_C_LABEL(curpcb)) + ld r6,r5,lo16(_C_LABEL(curpcb)) + jmp.n r1 + st r0,r6,PCB_ONFAULT /* clear the handler */ + +ASLOCAL(Lcoflt) + br.n _ASM_LABEL(Lcodone) + or r2, r0, EFAULT /* return fault */ + +#undef SRC +#undef DEST +#undef LEN + +/* + * Copy a null terminated string from the kernel space to the user + * address space. + * + * copyoutstr(from, to, maxlen, &lencopied) + * r2 == from + * r3 == to + * r4 == maxlen that can be copied + * r5 == len actually copied (including the terminating NULL!!!) + */ + +#define SRC r2 +#define DEST r3 +#define CNT r4 +#define LEN r5 + +ENTRY(copyoutstr) + /* setup fault handler */ + or.u r6, r0, hi16(_C_LABEL(curpcb)) + ld r7, r6, lo16(_C_LABEL(curpcb)) + or.u r6, r0, hi16(_ASM_LABEL(Lcosflt)) + or r6, r6, lo16(_ASM_LABEL(Lcosflt)) + st r6, r7, PCB_ONFAULT + bcnd lt0, CNT, _ASM_LABEL(Lcosflt) + bcnd eq0, CNT, _ASM_LABEL(Lcosdone) + or r6, r0, 0 +1: + ld.bu r7, SRC, r6 +#ifdef ERRATA__XXX_USR + NOP + st.b.usr r7, DEST, r6 + NOP + NOP + NOP +#else + st.b.usr r7, DEST, r6 +#endif + bcnd.n eq0, r7, 2f /* all done */ + addu r6, r6, 1 + cmp r7, r6, CNT + bb1 lt, r7, 1b + br.n _ASM_LABEL(Lcosdone) + or r2, r0, ENAMETOOLONG +2: + br.n _ASM_LABEL(Lcosdone) + or r2, r0, 0 + +ASLOCAL(Lcosflt) + br.n _ASM_LABEL(Lcosdone) + or r2, r0, EFAULT + +ASLOCAL(Lcosdone) + bcnd eq0, LEN, 3f + st r6, r0, LEN +3: or.u r5,r0,hi16(_C_LABEL(curpcb)) + ld r6,r5,lo16(_C_LABEL(curpcb)) + jmp.n r1 + st r0,r6,PCB_ONFAULT /* clear the handler */ + +#undef SRC +#undef DEST +#undef CNT +#undef LEN + +/*######################################################################*/ + +/* + * kcopy(const void *src, void *dst, size_t len); + * + * Copy len bytes from src to dst, aborting if we encounter a page fault. + */ +ENTRY(kcopy) + or.u r5, r0, hi16(_C_LABEL(curpcb)) + ld r6, r5, lo16(_C_LABEL(curpcb)) + or.u r5, r0, hi16(_ASM_LABEL(kcopy_fault)) + or r5, r5, lo16(_ASM_LABEL(kcopy_fault)) + st r5, r6, PCB_ONFAULT /* pcb_onfault = kcopy_fault */ + bcnd le0,r4,_ASM_LABEL(kcopy_out) /* nothing to do if <= 0 */ +/* + * check position of source and destination data + */ + cmp r9,r2,r3 /* compare source address to destination */ + bb1 eq,r9,_ASM_LABEL(kcopy_out) /* nothing to do if equal */ + bb1 lo,r9,_ASM_LABEL(kcopy_revers)e /* reverse copy if src < dest */ +/* + * source address is greater than destination address, copy forward + */ + cmp r9,r4,16 /* see if we have at least 16 bytes */ + bb1 lt,r9,_ASM_LABEL(kf_byte_copy) /* copy bytes for small length */ +/* + * determine copy strategy based on alignment of source and destination + */ + mask r6,r2,3 /* get 2 low order bits of source address */ + mask r7,r3,3 /* get 2 low order bits of destintation addr */ + mak r6,r6,0<4> /* convert source bits to table offset */ + mak r7,r7,0<2> /* convert destination bits to table offset */ + or.u r12,r0,hi16(_ASM_LABEL(kf_strat)) + or r12,r12,lo16(_ASM_LABEL(kf_strat)) + addu r6,r6,r7 /* compute final table offset for strategy */ + ld r12,r12,r6 /* load the strategy routine */ + jmp r12 /* branch to strategy routine */ + +/* + * Copy three bytes from src to destination then copy words + */ +ASLOCAL(kf_3byte_word_copy) + ld.bu r6,r2,0 /* load byte from source */ + ld.bu r7,r2,1 /* load byte from source */ + ld.bu r8,r2,2 /* load byte from source */ + st.b r6,r3,0 /* store byte to destination */ + st.b r7,r3,1 /* store byte to destination */ + st.b r8,r3,2 /* store byte to destination */ + addu r2,r2,3 /* increment source pointer */ + addu r3,r3,3 /* increment destination pointer */ + br.n _ASM_LABEL(kf_word_copy) /* copy full words */ + subu r4,r4,3 /* decrement length */ + +/* + * Copy 1 halfword from src to destination then copy words + */ +ASLOCAL(kf_1half_word_copy) + ld.hu r6,r2,0 /* load half-word from source */ + st.h r6,r3,0 /* store half-word to destination */ + addu r2,r2,2 /* increment source pointer */ + addu r3,r3,2 /* increment destination pointer */ + br.n _ASM_LABEL(kf_word_copy) /* copy full words */ + subu r4,r4,2 /* decrement remaining length */ + +/* + * Copy 1 byte from src to destination then copy words + */ +ASLOCAL(kf_1byte_word_copy) + ld.bu r6,r2,0 /* load 1 byte from source */ + st.b r6,r3,0 /* store 1 byte to destination */ + addu r2,r2,1 /* increment source pointer */ + addu r3,r3,1 /* increment destination pointer */ + subu r4,r4,1 /* decrement remaining length */ + /* fall through to word copy */ +/* + * Copy as many full words as possible, 4 words per loop + */ +ASLOCAL(kf_word_copy) + cmp r10,r4,16 /* see if we have 16 bytes remaining */ + bb1 lo,r10,_ASM_LABEL(kf_byte_copy) /* not enough left, copy bytes */ + ld r6,r2,0 /* load first word */ + ld r7,r2,4 /* load second word */ + ld r8,r2,8 /* load third word */ + ld r9,r2,12 /* load fourth word */ + st r6,r3,0 /* store first word */ + st r7,r3,4 /* store second word */ + st r8,r3,8 /* store third word */ + st r9,r3,12 /* store fourth word */ + addu r2,r2,16 /* increment source pointer */ + addu r3,r3,16 /* increment destination pointer */ + br.n _ASM_LABEL(kf_word_copy) /* copy another block */ + subu r4,r4,16 /* decrement remaining length */ + +ASLOCAL(kf_1byte_half_copy) + ld.bu r6,r2,0 /* load 1 byte from source */ + st.b r6,r3,0 /* store 1 byte to destination */ + addu r2,r2,1 /* increment source pointer */ + addu r3,r3,1 /* increment destination pointer */ + subu r4,r4,1 /* decrement remaining length */ + /* fall through to half copy */ + +ASLOCAL(kf_half_copy) + cmp r10,r4,16 /* see if we have 16 bytes remaining */ + bb1 lo,r10,_ASM_LABEL(kf_byte_copy) /* not enough left, copy bytes */ + ld.hu r6,r2,0 /* load first half-word */ + ld.hu r7,r2,2 /* load second half-word */ + ld.hu r8,r2,4 /* load third half-word */ + ld.hu r9,r2,6 /* load fourth half-word */ + ld.hu r10,r2,8 /* load fifth half-word */ + ld.hu r11,r2,10 /* load sixth half-word */ + ld.hu r12,r2,12 /* load seventh half-word */ + ld.hu r13,r2,14 /* load eighth half-word */ + st.h r6,r3,0 /* store first half-word */ + st.h r7,r3,2 /* store second half-word */ + st.h r8,r3,4 /* store third half-word */ + st.h r9,r3,6 /* store fourth half-word */ + st.h r10,r3,8 /* store fifth half-word */ + st.h r11,r3,10 /* store sixth half-word */ + st.h r12,r3,12 /* store seventh half-word */ + st.h r13,r3,14 /* store eighth half-word */ + addu r2,r2,16 /* increment source pointer */ + addu r3,r3,16 /* increment destination pointer */ + br.n _ASM_LABEL(kf_half_copy) /* copy another block */ + subu r4,r4,16 /* decrement remaining length */ + +ASLOCAL(kf_byte_copy) + bcnd eq0,r4,_ASM_LABEL(kcopy_out) /* branch if nothing left to copy */ + ld.bu r6,r2,0 /* load byte from source */ + st.b r6,r3,0 /* store byte in destination */ + addu r2,r2,1 /* increment source pointer */ + addu r3,r3,1 /* increment destination pointer */ + br.n _ASM_LABEL(kf_byte_copy) /* branch for next byte */ + subu r4,r4,1 /* decrement remaining length */ + +/* + * source address is less than destination address, copy in reverse + */ +ASLOCAL(kcopy_reverse) +/* + * start copy pointers at end of data + */ + addu r2,r2,r4 /* start source at end of data */ + addu r3,r3,r4 /* start destination at end of data */ +/* + * check for short data + */ + cmp r9,r4,16 /* see if we have at least 16 bytes */ + bb1 lt,r9,_ASM_LABEL(kr_byte_copy) /* copy bytes for small data length */ +/* + * determine copy strategy based on alignment of source and destination + */ + mask r6,r2,3 /* get 2 low order bits of source address */ + mask r7,r3,3 /* get 2 low order bits of destintation addr */ + mak r6,r6,0<4> /* convert source bits to table offset */ + mak r7,r7,0<2> /* convert destination bits to table offset */ + or.u r12,r0,hi16(_ASM_LABEL(kr_strat)) + or r12,r12,lo16(_ASM_LABEL(kr_strat)) + addu r6,r6,r7 /* compute final table offset for strategy */ + ld r12,r12,r6 /* load the strategy routine */ + jmp r12 /* branch to strategy routine */ + +/* + * Copy three bytes from src to destination then copy words + */ +ASLOCAL(kr_3byte_word_copy) + subu r2,r2,3 /* decrement source pointer */ + subu r3,r3,3 /* decrement destination pointer */ + ld.bu r6,r2,0 /* load byte from source */ + ld.bu r7,r2,1 /* load byte from source */ + ld.bu r8,r2,2 /* load byte from source */ + st.b r6,r3,0 /* store byte to destination */ + st.b r7,r3,1 /* store byte to destination */ + st.b r8,r3,2 /* store byte to destination */ + br.n _ASM_LABEL(kr_word_copy) /* copy full words */ + subu r4,r4,3 /* decrement length */ + +/* + * Copy 1 halfword from src to destination then copy words + */ +ASLOCAL(kr_1half_word_copy) + subu r2,r2,2 /* decrement source pointer */ + subu r3,r3,2 /* decrement destination pointer */ + ld.hu r6,r2,0 /* load half-word from source */ + st.h r6,r3,0 /* store half-word to destination */ + br.n _ASM_LABEL(kr_word_copy) /* copy full words */ + subu r4,r4,2 /* decrement remaining length */ + +/* + * Copy 1 byte from src to destination then copy words + */ +ASLOCAL(kr_1byte_word_copy) + subu r2,r2,1 /* decrement source pointer */ + subu r3,r3,1 /* decrement destination pointer */ + ld.bu r6,r2,0 /* load 1 byte from source */ + st.b r6,r3,0 /* store 1 byte to destination */ + subu r4,r4,1 /* decrement remaining length */ + /* fall through to word copy */ +/* + * Copy as many full words as possible, 4 words per loop + */ +ASLOCAL(kr_word_copy) + cmp r10,r4,16 /* see if we have 16 bytes remaining */ + bb1 lo,r10,_ASM_LABEL(kr_byte_copy) /* not enough left, copy bytes */ + subu r2,r2,16 /* decrement source pointer */ + subu r3,r3,16 /* decrement destination pointer */ + ld r6,r2,0 /* load first word */ + ld r7,r2,4 /* load second word */ + ld r8,r2,8 /* load third word */ + ld r9,r2,12 /* load fourth word */ + st r6,r3,0 /* store first word */ + st r7,r3,4 /* store second word */ + st r8,r3,8 /* store third word */ + st r9,r3,12 /* store fourth word */ + br.n _ASM_LABEL(kr_word_copy) /* copy another block */ + subu r4,r4,16 /* decrement remaining length */ + +ASLOCAL(kr_1byte_half_copy) + subu r2,r2,1 /* decrement source pointer */ + subu r3,r3,1 /* decrement destination pointer */ + ld.bu r6,r2,0 /* load 1 byte from source */ + st.b r6,r3,0 /* store 1 byte to destination */ + subu r4,r4,1 /* decrement remaining length */ + /* fall through to half copy */ + +ASLOCAL(kr_half_copy) + cmp r10,r4,16 /* see if we have 16 bytes remaining */ + bb1 lo,r10,_ASM_LABEL(kr_byte_copy) /* not enough left, copy bytes */ + subu r2,r2,16 /* decrement source pointer */ + subu r3,r3,16 /* decrement destination pointer */ + ld.hu r6,r2,0 /* load first half-word */ + ld.hu r7,r2,2 /* load second half-word */ + ld.hu r8,r2,4 /* load third half-word */ + ld.hu r9,r2,6 /* load fourth half-word */ + ld.hu r10,r2,8 /* load fifth half-word */ + ld.hu r11,r2,10 /* load sixth half-word */ + ld.hu r12,r2,12 /* load seventh half-word */ + ld.hu r13,r2,14 /* load eighth half-word */ + st.h r6,r3,0 /* store first half-word */ + st.h r7,r3,2 /* store second half-word */ + st.h r8,r3,4 /* store third half-word */ + st.h r9,r3,6 /* store fourth half-word */ + st.h r10,r3,8 /* store fifth half-word */ + st.h r11,r3,10 /* store sixth half-word */ + st.h r12,r3,12 /* store seventh half-word */ + st.h r13,r3,14 /* store eighth half-word */ + br.n _ASM_LABEL(kr_half_copy) /* copy another block */ + subu r4,r4,16 /* decrement remaining length */ + +ASLOCAL(kr_byte_copy) + bcnd eq0,r4,_ASM_LABEL(kcopy_out) /* branch if nothing left to copy */ + subu r2,r2,1 /* decrement source pointer */ + subu r3,r3,1 /* decrement destination pointer */ + ld.bu r6,r2,0 /* load byte from source */ + st.b r6,r3,0 /* store byte in destination */ + br.n _ASM_LABEL(kr_byte_copy) /* branch for next byte */ + subu r4,r4,1 /* decrement remaining length */ + +ASLOCAL(kcopy_out) + or r2, r0, 0 /* return success */ +ASLOCAL(kcopy_out_fault) + or.u r5,r0,hi16(_C_LABEL(curpcb)) + ld r6,r5,lo16(_C_LABEL(curpcb)) + st r0,r6,PCB_ONFAULT /* clear the handler */ + jmp r1 /* all done, return to caller */ + +ASLOCAL(kcopy_fault) + or r2, r0, EFAULT /* return fault */ + br _ASM_LABEL(kcopy_out_fault) + + data + align 4 +ASLOCAL(kf_strat) + word _ASM_LABEL(kf_word_copy) + word _ASM_LABEL(kf_byte_copy) + word _ASM_LABEL(kf_half_copy) + word _ASM_LABEL(kf_byte_copy) + word _ASM_LABEL(kf_byte_copy) + word _ASM_LABEL(kf_3byte_word_copy) + word _ASM_LABEL(kf_byte_copy) + word _ASM_LABEL(kf_1byte_half_copy) + word _ASM_LABEL(kf_half_copy) + word _ASM_LABEL(kf_byte_copy) + word _ASM_LABEL(kf_1half_word_copy) + word _ASM_LABEL(kf_byte_copy) + word _ASM_LABEL(kf_byte_copy) + word _ASM_LABEL(kf_1byte_half_copy) + word _ASM_LABEL(kf_byte_copy) + word _ASM_LABEL(kf_1byte_word_copy) + +ASLOCAL(kr_strat) + word _ASM_LABEL(kr_word_copy) + word _ASM_LABEL(kr_byte_copy) + word _ASM_LABEL(kr_half_copy) + word _ASM_LABEL(kr_byte_copy) + word _ASM_LABEL(kr_byte_copy) + word _ASM_LABEL(kr_1byte_word_copy) + word _ASM_LABEL(kr_byte_copy) + word _ASM_LABEL(kr_1byte_half_copy) + word _ASM_LABEL(kr_half_copy) + word _ASM_LABEL(kr_byte_copy) + word _ASM_LABEL(kr_1half_word_copy) + word _ASM_LABEL(kr_byte_copy) + word _ASM_LABEL(kr_byte_copy) + word _ASM_LABEL(kr_1byte_half_copy) + word _ASM_LABEL(kr_byte_copy) + word _ASM_LABEL(kr_3byte_word_copy) + +/* + * Gcc 2 generates calls to memcpy for bcopies of unknown size. memcpy can + * simply be implemented as ovbcopy but the src (r2, r3) and dst args need to + * be switched. + */ +/* + * void memcpy(dest, source, count) + * + */ +ENTRY(memcpy) + or r5, r0, r2 /* dst -> tmp */ + or r2, r0, r3 /* src -> 1st arg */ + br.n _C_LABEL(ovbcopy) + or r3, r0, r5 /* dst -> 2nd arg */ + +/* + * void bcopy(source, destination, count) + * + * copy count bytes of data from source to destination + * Don Harper (don@omron.co.jp), Omron Corporation. + * + */ + +ENTRY(bcopy) +ENTRY(ovbcopy) + bcnd le0,r4,_ASM_LABEL(bcopy_out) /* nothing to do if <= 0 */ +/* + * check position of source and destination data + */ + cmp r9,r2,r3 /* compare source address to destination */ + bb1 eq,r9,_ASM_LABEL(bcopy_out) /* nothing to do if equal */ + bb1 lo,r9,_ASM_LABEL(bcopy_reverse) /* reverse copy if src < dest */ +/* + * source address is greater than destination address, copy forward + */ + cmp r9,r4,16 /* see if we have at least 16 bytes */ + bb1 lt,r9,_ASM_LABEL(f_byte_copy) /* copy bytes for small data length */ +/* + * determine copy strategy based on alignment of source and destination + */ + mask r6,r2,3 /* get 2 low order bits of source address */ + mask r7,r3,3 /* get 2 low order bits of destintation addr */ + mak r6,r6,0<4> /* convert source bits to table offset */ + mak r7,r7,0<2> /* convert destination bits to table offset */ + or.u r12,r0,hi16(_ASM_LABEL(f_strat)) + or r12,r12,lo16(_ASM_LABEL(f_strat)) + addu r6,r6,r7 /* compute final table offset for strategy */ + ld r12,r12,r6 /* load the strategy routine */ + jmp r12 /* branch to strategy routine */ + + +/* + * Copy three bytes from src to destination then copy words + */ +ASLOCAL(f_3byte_word_copy) + ld.bu r6,r2,0 /* load byte from source */ + ld.bu r7,r2,1 /* load byte from source */ + ld.bu r8,r2,2 /* load byte from source */ + st.b r6,r3,0 /* store byte to destination */ + st.b r7,r3,1 /* store byte to destination */ + st.b r8,r3,2 /* store byte to destination */ + addu r2,r2,3 /* increment source pointer */ + addu r3,r3,3 /* increment destination pointer */ + br.n _ASM_LABEL(f_word_copy) /* copy full words */ + subu r4,r4,3 /* decrement length */ + +/* + * Copy 1 halfword from src to destination then copy words + */ +ASLOCAL(f_1half_word_copy) + ld.hu r6,r2,0 /* load half-word from source */ + st.h r6,r3,0 /* store half-word to destination */ + addu r2,r2,2 /* increment source pointer */ + addu r3,r3,2 /* increment destination pointer */ + br.n _ASM_LABEL(f_word_copy) /* copy full words */ + subu r4,r4,2 /* decrement remaining length */ + +/* + * Copy 1 byte from src to destination then copy words + */ +ASLOCAL(f_1byte_word_copy) + ld.bu r6,r2,0 /* load 1 byte from source */ + st.b r6,r3,0 /* store 1 byte to destination */ + addu r2,r2,1 /* increment source pointer */ + addu r3,r3,1 /* increment destination pointer */ + subu r4,r4,1 /* decrement remaining length */ + /* fall through to word copy */ +/* + * Copy as many full words as possible, 4 words per loop + */ +ASLOCAL(f_word_copy) + cmp r10,r4,16 /* see if we have 16 bytes remaining */ + bb1 lo,r10,_ASM_LABEL(f_byte_copy) /* not enough left, copy bytes */ + ld r6,r2,0 /* load first word */ + ld r7,r2,4 /* load second word */ + ld r8,r2,8 /* load third word */ + ld r9,r2,12 /* load fourth word */ + st r6,r3,0 /* store first word */ + st r7,r3,4 /* store second word */ + st r8,r3,8 /* store third word */ + st r9,r3,12 /* store fourth word */ + addu r2,r2,16 /* increment source pointer */ + addu r3,r3,16 /* increment destination pointer */ + br.n _ASM_LABEL(f_word_copy) /* branch to copy another block */ + subu r4,r4,16 /* decrement remaining length */ + +ASLOCAL(f_1byte_half_copy) + ld.bu r6,r2,0 /* load 1 byte from source */ + st.b r6,r3,0 /* store 1 byte to destination */ + addu r2,r2,1 /* increment source pointer */ + addu r3,r3,1 /* increment destination pointer */ + subu r4,r4,1 /* decrement remaining length */ + /* fall through to half copy */ + +ASLOCAL(f_half_copy) + cmp r10,r4,16 /* see if we have 16 bytes remaining */ + bb1 lo,r10,_ASM_LABEL(f_byte_copy) /* not enough left, copy bytes */ + ld.hu r6,r2,0 /* load first half-word */ + ld.hu r7,r2,2 /* load second half-word */ + ld.hu r8,r2,4 /* load third half-word */ + ld.hu r9,r2,6 /* load fourth half-word */ + ld.hu r10,r2,8 /* load fifth half-word */ + ld.hu r11,r2,10 /* load sixth half-word */ + ld.hu r12,r2,12 /* load seventh half-word */ + ld.hu r13,r2,14 /* load eighth half-word */ + st.h r6,r3,0 /* store first half-word */ + st.h r7,r3,2 /* store second half-word */ + st.h r8,r3,4 /* store third half-word */ + st.h r9,r3,6 /* store fourth half-word */ + st.h r10,r3,8 /* store fifth half-word */ + st.h r11,r3,10 /* store sixth half-word */ + st.h r12,r3,12 /* store seventh half-word */ + st.h r13,r3,14 /* store eighth half-word */ + addu r2,r2,16 /* increment source pointer */ + addu r3,r3,16 /* increment destination pointer */ + br.n _ASM_LABEL(f_half_copy) /* branch to copy another block */ + subu r4,r4,16 /* decrement remaining length */ + +ASLOCAL(f_byte_copy) + bcnd eq0,r4,_ASM_LABEL(bcopy_out) /* branch if nothing left to copy */ + ld.bu r6,r2,0 /* load byte from source */ + st.b r6,r3,0 /* store byte in destination */ + addu r2,r2,1 /* increment source pointer */ + addu r3,r3,1 /* increment destination pointer */ + br.n _ASM_LABEL(f_byte_copy) /* branch for next byte */ + subu r4,r4,1 /* decrement remaining length */ + +/* + * source address is less than destination address, copy in reverse + */ +ASLOCAL(bcopy_reverse) +/* + * start copy pointers at end of data + */ + addu r2,r2,r4 /* start source at end of data */ + addu r3,r3,r4 /* start destination at end of data */ +/* + * check for short data + */ + cmp r9,r4,16 /* see if we have at least 16 bytes */ + bb1 lt,r9,_ASM_LABEL(r_byte_copy) /* copy bytes for small data length */ +/* + * determine copy strategy based on alignment of source and destination + */ + mask r6,r2,3 /* get 2 low order bits of source address */ + mask r7,r3,3 /* get 2 low order bits of destintation addr */ + mak r6,r6,0<4> /* convert source bits to table offset */ + mak r7,r7,0<2> /* convert destination bits to table offset */ + or.u r12,r0,hi16(_ASM_LABEL(r_strat)) + or r12,r12,lo16(_ASM_LABEL(r_strat)) + addu r6,r6,r7 /* compute final table offset for strategy */ + ld r12,r12,r6 /* load the strategy routine */ + jmp r12 /* branch to strategy routine */ + +/* + * Copy three bytes from src to destination then copy words + */ +ASLOCAL(r_3byte_word_copy) + subu r2,r2,3 /* decrement source pointer */ + subu r3,r3,3 /* decrement destination pointer */ + ld.bu r6,r2,0 /* load byte from source */ + ld.bu r7,r2,1 /* load byte from source */ + ld.bu r8,r2,2 /* load byte from source */ + st.b r6,r3,0 /* store byte to destination */ + st.b r7,r3,1 /* store byte to destination */ + st.b r8,r3,2 /* store byte to destination */ + br.n _ASM_LABEL(r_word_copy) /* copy full words */ + subu r4,r4,3 /* decrement length */ + +/* + * Copy 1 halfword from src to destination then copy words + */ +ASLOCAL(r_1half_word_copy) + subu r2,r2,2 /* decrement source pointer */ + subu r3,r3,2 /* decrement destination pointer */ + ld.hu r6,r2,0 /* load half-word from source */ + st.h r6,r3,0 /* store half-word to destination */ + br.n _ASM_LABEL(r_word_copy) /* copy full words */ + subu r4,r4,2 /* decrement remaining length */ + +/* + * Copy 1 byte from src to destination then copy words + */ +ASLOCAL(r_1byte_word_copy) + subu r2,r2,1 /* decrement source pointer */ + subu r3,r3,1 /* decrement destination pointer */ + ld.bu r6,r2,0 /* load 1 byte from source */ + st.b r6,r3,0 /* store 1 byte to destination */ + subu r4,r4,1 /* decrement remaining length */ + /* fall through to word copy */ +/* + * Copy as many full words as possible, 4 words per loop + */ +ASLOCAL(r_word_copy) + cmp r10,r4,16 /* see if we have 16 bytes remaining */ + bb1 lo,r10,_ASM_LABEL(r_byte_copy) /* not enough left, copy bytes */ + subu r2,r2,16 /* decrement source pointer */ + subu r3,r3,16 /* decrement destination pointer */ + ld r6,r2,0 /* load first word */ + ld r7,r2,4 /* load second word */ + ld r8,r2,8 /* load third word */ + ld r9,r2,12 /* load fourth word */ + st r6,r3,0 /* store first word */ + st r7,r3,4 /* store second word */ + st r8,r3,8 /* store third word */ + st r9,r3,12 /* store fourth word */ + br.n _ASM_LABEL(r_word_copy) /* branch to copy another block */ + subu r4,r4,16 /* decrement remaining length */ + +ASLOCAL(r_1byte_half_copy) + subu r2,r2,1 /* decrement source pointer */ + subu r3,r3,1 /* decrement destination pointer */ + ld.bu r6,r2,0 /* load 1 byte from source */ + st.b r6,r3,0 /* store 1 byte to destination */ + subu r4,r4,1 /* decrement remaining length */ + /* fall through to half copy */ + +ASLOCAL(r_half_copy) + cmp r10,r4,16 /* see if we have 16 bytes remaining */ + bb1 lo,r10,_ASM_LABEL(r_byte_copy) /* not enough left, copy bytes */ + subu r2,r2,16 /* decrement source pointer */ + subu r3,r3,16 /* decrement destination pointer */ + ld.hu r6,r2,0 /* load first half-word */ + ld.hu r7,r2,2 /* load second half-word */ + ld.hu r8,r2,4 /* load third half-word */ + ld.hu r9,r2,6 /* load fourth half-word */ + ld.hu r10,r2,8 /* load fifth half-word */ + ld.hu r11,r2,10 /* load sixth half-word */ + ld.hu r12,r2,12 /* load seventh half-word */ + ld.hu r13,r2,14 /* load eighth half-word */ + st.h r6,r3,0 /* store first half-word */ + st.h r7,r3,2 /* store second half-word */ + st.h r8,r3,4 /* store third half-word */ + st.h r9,r3,6 /* store fourth half-word */ + st.h r10,r3,8 /* store fifth half-word */ + st.h r11,r3,10 /* store sixth half-word */ + st.h r12,r3,12 /* store seventh half-word */ + st.h r13,r3,14 /* store eighth half-word */ + br.n _ASM_LABEL(r_half_copy) /* branch to copy another block */ + subu r4,r4,16 /* decrement remaining length */ + +ASLOCAL(r_byte_copy) + bcnd eq0,r4,_ASM_LABEL(bcopy_out) /* branch if nothing left to copy */ + subu r2,r2,1 /* decrement source pointer */ + subu r3,r3,1 /* decrement destination pointer */ + ld.bu r6,r2,0 /* load byte from source */ + st.b r6,r3,0 /* store byte in destination */ + br.n _ASM_LABEL(r_byte_copy) /* branch for next byte */ + subu r4,r4,1 /* decrement remaining length */ + +ASLOCAL(bcopy_out) + jmp r1 /* all done, return to caller */ + + data + align 4 +ASLOCAL(f_strat) + word _ASM_LABEL(f_word_copy) + word _ASM_LABEL(f_byte_copy) + word _ASM_LABEL(f_half_copy) + word _ASM_LABEL(f_byte_copy) + word _ASM_LABEL(f_byte_copy) + word _ASM_LABEL(f_3byte_word_copy) + word _ASM_LABEL(f_byte_copy) + word _ASM_LABEL(f_1byte_half_copy) + word _ASM_LABEL(f_half_copy) + word _ASM_LABEL(f_byte_copy) + word _ASM_LABEL(f_1half_word_copy) + word _ASM_LABEL(f_byte_copy) + word _ASM_LABEL(f_byte_copy) + word _ASM_LABEL(f_1byte_half_copy) + word _ASM_LABEL(f_byte_copy) + word _ASM_LABEL(f_1byte_word_copy) + +ASLOCAL(r_strat) + word _ASM_LABEL(r_word_copy) + word _ASM_LABEL(r_byte_copy) + word _ASM_LABEL(r_half_copy) + word _ASM_LABEL(r_byte_copy) + word _ASM_LABEL(r_byte_copy) + word _ASM_LABEL(r_1byte_word_copy) + word _ASM_LABEL(r_byte_copy) + word _ASM_LABEL(r_1byte_half_copy) + word _ASM_LABEL(r_half_copy) + word _ASM_LABEL(r_byte_copy) + word _ASM_LABEL(r_1half_word_copy) + word _ASM_LABEL(r_byte_copy) + word _ASM_LABEL(r_byte_copy) + word _ASM_LABEL(r_1byte_half_copy) + word _ASM_LABEL(r_byte_copy) + word _ASM_LABEL(r_3byte_word_copy) + + text + +/*######################################################################*/ + +/* + * April 1990, Omron Corporation + * jfriedl@nff.ncl.omron.co.jp + * + * void bzero(destination, length) + * + * Clear (set to zero) LENGTH bytes of memory starting at DESTINATION. + * Note that there is no return value. + * + * This is fast. Really fast. Especially for long lengths. + */ +#define R_dest r2 +#define R_len r3 + +#define R_bytes r4 +#define R_mark_address r5 +#define R_addr r6 /* R_addr && R_temp SHARE */ +#define R_temp r6 /* R_addr && R_temp SHARE */ + +ENTRY(bzero) + /* + * If the destination is not word aligned, we'll word align + * it first to make things easier. + * + * We'll check to see first if bit #0 is set and then bit #1 + * (of the destination address). If either are set, it's + * not word aligned. + */ + bb1 0, R_dest, _ASM_LABEL(not_initially_word_aligned) + bb1 1, R_dest, _ASM_LABEL(not_initially_word_aligned) + +ASLOCAL(now_word_aligned) + /* + * before we get into the main loop, grab the + * address of the label "mark" below. + */ + or.u R_mark_address, r0, hi16(_ASM_LABEL(mark)) + or R_mark_address, R_mark_address, lo16(_ASM_LABEL(mark)) + +ASLOCAL(top_of_main_loop) +#define MAX_AT_ONE_TIME 128 + /* + * Now we find out how many words we can zero-fill in a row. + * We do this by doing something like: + * + * bytes &= 0xfffffffc; + * if (bytes > MAX_AT_ONE_TIME) + * bytes = MAX_AT_ONE_TIME; + */ + + /* + * Clear lower two bits of length to give us the number of bytes + * ALIGNED TO THE WORD LENGTH remaining to move. + */ + clr R_bytes, R_len, 2<0> + + /* if we're done clearing WORDS, jump out */ + bcnd eq0, R_bytes, _ASM_LABEL(done_doing_words) + + /* if the number of bytes > MAX_AT_ONE_TIME, do only the max */ + cmp R_temp, R_bytes, MAX_AT_ONE_TIME + bb1 lt, R_temp, 1f + + /* + * Since we're doing the max, we know exactly where we're + * jumping (the first one in the list!), so we can jump + * right there. However, we've still got to adjust + * the length, so we'll jump to where we ajust the length + * which just happens to fall through to the first store zero + * in the list. + * + * Note, however, that we're jumping to an instruction that + * would be in the delay slot for the jump in front of it, + * so if you change things here, WATCH OUT. + */ + br.n do_max + or R_bytes, r0, MAX_AT_ONE_TIME + +1: + /* + * Now we have the number of bytes to zero during this iteration, + * (which, as it happens, is the last iteration if we're here). + * We'll calculate the proper place to jump and then jump there, + * after adjusting the length. NOTE that there is a label between + * the "jmp.n" and the "subu" below... the "subu" is NOT always + * executed in the delay slot of the "jmp.n". + */ + subu R_addr, R_mark_address, R_bytes + + /* and go there (after adjusting the length via ".n") */ + jmp.n R_addr +ASLOCAL(do_max) + subu R_len, R_len, R_bytes /* NOTE: this is in the delay slot! */ + + st r0, R_dest, 0x7c /* 128 */ + st r0, R_dest, 0x78 /* 124 */ + st r0, R_dest, 0x74 /* 120 */ + st r0, R_dest, 0x70 /* 116 */ + st r0, R_dest, 0x6c /* 112 */ + st r0, R_dest, 0x68 /* 108 */ + st r0, R_dest, 0x64 /* 104 */ + st r0, R_dest, 0x60 /* 100 */ + st r0, R_dest, 0x5c /* 96 */ + st r0, R_dest, 0x58 /* 92 */ + st r0, R_dest, 0x54 /* 88 */ + st r0, R_dest, 0x50 /* 84 */ + st r0, R_dest, 0x4c /* 80 */ + st r0, R_dest, 0x48 /* 76 */ + st r0, R_dest, 0x44 /* 72 */ + st r0, R_dest, 0x40 /* 68 */ + st r0, R_dest, 0x3c /* 64 */ + st r0, R_dest, 0x38 /* 60 */ + st r0, R_dest, 0x34 /* 56 */ + st r0, R_dest, 0x30 /* 52 */ + st r0, R_dest, 0x2c /* 44 */ + st r0, R_dest, 0x28 /* 40 */ + st r0, R_dest, 0x24 /* 36 */ + st r0, R_dest, 0x20 /* 32 */ + st r0, R_dest, 0x1c /* 28 */ + st r0, R_dest, 0x18 /* 24 */ + st r0, R_dest, 0x14 /* 20 */ + st r0, R_dest, 0x10 /* 16 */ + st r0, R_dest, 0x0c /* 12 */ + st r0, R_dest, 0x08 /* 8 */ + st r0, R_dest, 0x04 /* 4 */ + st r0, R_dest, 0x00 /* 0 */ + +ASLOCAL(mark) + br.n _ASM_LABEL(top_of_main_loop) + addu R_dest, R_dest, R_bytes /* bump up the dest address */ + +ASLOCAL(done_doing_words) + bcnd ne0, R_len, 1f + jmp r1 + +1: + subu R_len, R_len, 1 + bcnd.n ne0, R_len, 1b + st.b r0, R_dest, R_len +1: + jmp r1 + +ASLOCAL(not_initially_word_aligned) + /* + * Bzero to word-align the address (at least if the length allows it). + */ + bcnd eq0, R_len, 1b + st.b r0, R_dest, 0 + addu R_dest, R_dest, 1 + mask R_temp, R_dest, 0x3 + bcnd.n eq0, R_temp, _ASM_LABEL(now_word_aligned) + subu R_len, R_len, 1 + br _ASM_LABEL(not_initially_word_aligned) + +#undef R_dest +#undef R_len +#undef R_bytes +#undef R_mark_address +#undef R_addr +#undef R_temp +#undef MAX_AT_ONE_TIME + +/* + * non-local goto + * int setjmp(label_t *); + * void longjmp(label_t*); + */ +ENTRY(setjmp) + st r1,r2,0 + st r14,r2,4 + st r15,r2,2*4 + st r16,r2,3*4 + st r17,r2,4*4 + st r18,r2,5*4 + st r19,r2,6*4 + st r20,r2,7*4 + st r21,r2,8*4 + st r22,r2,9*4 + st r23,r2,10*4 + st r24,r2,11*4 + st r25,r2,12*4 + st r26,r2,13*4 + st r27,r2,14*4 + st r28,r2,15*4 + st r29,r2,16*4 + st r30,r2,17*4 + st r31,r2,18*4 + jmp.n r1 + or r2,r0,r0 + +ENTRY(longjmp) + ld r1,r2,0 + ld r14,r2,4 + ld r15,r2,2*4 + ld r16,r2,3*4 + ld r17,r2,4*4 + ld r18,r2,5*4 + ld r19,r2,6*4 + ld r20,r2,7*4 + ld r21,r2,8*4 + ld r22,r2,9*4 + ld r23,r2,10*4 + ld r24,r2,11*4 + ld r25,r2,12*4 + ld r26,r2,13*4 + ld r27,r2,14*4 + ld r28,r2,15*4 + ld r29,r2,16*4 + ld r30,r2,17*4 + ld r31,r2,18*4 + jmp.n r1 + or r2,r0,1 + +ENTRY(read_processor_identification_register) + jmp.n r1 + ldcr r2, PID + +GLOBAL(guarded_access_start) +ENTRY(guarded_access) + cmp r9,r3,4 + bb1 eq,r9,@L145 + cmp r9,r3,2 + bb1 eq,r9,@L144 + cmp r9,r3,1 + bb1 eq,r9,@L143 + br _C_LABEL(guarded_access_bad) +@L143: + ld.b r9,r0,r2 + tb1 0, r0, 0 + st.b r9,r0,r4 + br @L142 +@L144: + ld.h r9,r0,r2 + tb1 0, r0, 0 + st.h r9,r0,r4 + br @L142 +@L145: + ld r9,r0,r2 + tb1 0, r0, 0 + st r9,r0,r4 + br @L142 + +GLOBAL(guarded_access_bad) + jmp.n r1 + or r2,r0,EFAULT + +@L142: + jmp.n r1 + or r2,r0,0 + +GLOBAL(guarded_access_end) + +/* + * void set_cpu_number(unsigned number); + * + * Sets the kernel cpu number for this cpu to the given value. + * + * Input: + * r1 return address + * r2 the number (should be 0, 1, 2, or 3). + * + * Other registers used: + * r3 temp + * r4 original PSR + * r5 temporary new PSR + */ +ENTRY(set_cpu_number) +#ifdef DEBUG + /* make sure the CPU number is valid */ + clr r3, r2, FLAG_CPU_FIELD_WIDTH<0> + bcnd ne0, r3, 1f /* bad cpu number */ +#endif + + /* going to change a control register -- disable interrupts */ + ldcr r4, PSR + set r5, r4, 1<PSR_INTERRUPT_DISABLE_BIT> + stcr r5, PSR + FLUSH_PIPELINE + + /* put in the cpu number */ + ldcr r3, SR1 /* get the flags */ + clr r3, r3, FLAG_CPU_FIELD_WIDTH<0> /* clean the slate */ + or r3, r3, r2 /* add the cpu number */ + stcr r3, SR1 /* put back */ + + /* put back the PSR to what it was before and return */ + stcr r4, PSR + FLUSH_PIPELINE + jmp r1 + +#ifdef DEBUG +1: /* bad cpu number*/ + or.u r2, r0, hi16(9f) + bsr.n _C_LABEL(panic) + or r2, r2, lo16(9f) + + data +9: string "set_cpu_number: bad CPU number %x\0" +#endif diff --git a/sys/arch/luna88k/luna88k/locore_c_routines.c b/sys/arch/luna88k/luna88k/locore_c_routines.c new file mode 100644 index 00000000000..432f9d11f45 --- /dev/null +++ b/sys/arch/luna88k/luna88k/locore_c_routines.c @@ -0,0 +1,512 @@ +/* $OpenBSD: locore_c_routines.c,v 1.1 2004/04/21 15:24:01 aoyama Exp $ */ +/* + * Mach Operating System + * Copyright (c) 1993-1991 Carnegie Mellon University + * Copyright (c) 1991 OMRON Corporation + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON AND OMRON ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON AND OMRON DISCLAIM ANY LIABILITY OF ANY KIND + * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie the + * rights to redistribute these changes. + */ + +#include "assym.h" + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/systm.h> + +#include <machine/board.h> /* m188 bit defines */ +#include <machine/cmmu.h> /* DMT_VALID */ +#include <machine/asm.h> /* END_OF_VECTOR_LIST, etc. */ +#include <machine/asm_macro.h> /* enable/disable interrupts */ +#include <machine/cpu_number.h> /* cpu_number() */ +#include <machine/locore.h> +#include <machine/trap.h> +#ifdef M88100 +#include <machine/m88100.h> +#endif + +#ifdef DDB +#include <ddb/db_output.h> +#endif /* DDB */ + +#if defined(DDB) && defined(JEFF_DEBUG) +#define DATA_DEBUG +#endif + +#if DDB +#define DEBUG_MSG db_printf +#else +#define DEBUG_MSG printf +#endif /* DDB */ + +typedef struct { + unsigned word_one, word_two; +} m88k_exception_vector_area; + +extern unsigned int *volatile int_mask_reg[MAX_CPUS]; /* in machdep.c */ +extern unsigned master_cpu; /* in cmmu.c */ + +/* FORWARDS */ +void setlevel(unsigned int); +void vector_init(m88k_exception_vector_area *, unsigned *); + +#ifdef M88100 + +/* + * data access emulation for M88100 exceptions + */ + +#define DMT_BYTE 1 +#define DMT_HALF 2 +#define DMT_WORD 4 + +const struct { + unsigned char offset; + unsigned char size; +} dmt_en_info[16] = { + {0, 0}, {3, DMT_BYTE}, {2, DMT_BYTE}, {2, DMT_HALF}, + {1, DMT_BYTE}, {0, 0}, {0, 0}, {0, 0}, + {0, DMT_BYTE}, {0, 0}, {0, 0}, {0, 0}, + {0, DMT_HALF}, {0, 0}, {0, 0}, {0, DMT_WORD} +}; + +#ifdef DATA_DEBUG +int data_access_emulation_debug = 0; +#define DAE_DEBUG(stuff) \ + do { \ + if (data_access_emulation_debug != 0) { \ + stuff; \ + } \ + } while (0) +#else +#define DAE_DEBUG(stuff) +#endif + +void +dae_print(unsigned *eframe) +{ + int x; + unsigned dmax, dmdx, dmtx; + + if (!ISSET(eframe[EF_DMT0], DMT_VALID)) + return; + + for (x = 0; x < 3; x++) { + dmtx = eframe[EF_DMT0 + x * 3]; + if (!ISSET(dmtx, DMT_VALID)) + continue; + + dmdx = eframe[EF_DMD0 + x * 3]; + dmax = eframe[EF_DMA0 + x * 3]; + + if (ISSET(dmtx, DMT_WRITE)) + printf("[DMT%d=%x: st.%c %x to %x as %d %s %s]\n", + x, dmtx, dmtx & DMT_DAS ? 's' : 'u', dmdx, dmax, + DMT_ENBITS(dmtx), + dmtx & DMT_DOUB1 ? "double": "not double", + dmtx & DMT_LOCKBAR ? "xmem": "not xmem"); + else + printf("[DMT%d=%x: ld.%c r%d <- %x as %d %s %s]\n", + x, dmtx, dmtx & DMT_DAS ? 's' : 'u', + DMT_DREGBITS(dmtx), dmax, DMT_ENBITS(dmtx), + dmtx & DMT_DOUB1 ? "double": "not double", + dmtx & DMT_LOCKBAR ? "xmem": "not xmem"); + } +} + +void +data_access_emulation(unsigned *eframe) +{ + int x; + unsigned dmax, dmdx, dmtx; + unsigned v, reg; + + dmtx = eframe[EF_DMT0]; + if (!ISSET(dmtx, DMT_VALID)) + return; + + for (x = 0; x < 3; x++) { + dmtx = eframe[EF_DMT0 + x * 3]; + if (!ISSET(dmtx, DMT_VALID) || ISSET(dmtx, DMT_SKIP)) + continue; + + dmdx = eframe[EF_DMD0 + x * 3]; + dmax = eframe[EF_DMA0 + x * 3]; + + DAE_DEBUG( + if (ISSET(dmtx, DMT_WRITE)) + printf("[DMT%d=%x: st.%c %x to %x as %d %s %s]\n", + x, dmtx, dmtx & DMT_DAS ? 's' : 'u', dmdx, dmax, + DMT_ENBITS(dmtx), + dmtx & DMT_DOUB1 ? "double": "not double", + dmtx & DMT_LOCKBAR ? "xmem": "not xmem"); + else + printf("[DMT%d=%x: ld.%c r%d <- %x as %d %s %s]\n", + x, dmtx, dmtx & DMT_DAS ? 's' : 'u', + DMT_DREGBITS(dmtx), dmax, DMT_ENBITS(dmtx), + dmtx & DMT_DOUB1 ? "double": "not double", + dmtx & DMT_LOCKBAR ? "xmem": "not xmem") + ); + + dmax += dmt_en_info[DMT_ENBITS(dmtx)].offset; + reg = DMT_DREGBITS(dmtx); + + if (!ISSET(dmtx, DMT_LOCKBAR)) { + /* the fault is not during an XMEM */ + + if (x == 2 && ISSET(dmtx, DMT_DOUB1)) { + /* pipeline 2 (earliest stage) for a double */ + + if (ISSET(dmtx, DMT_WRITE)) { + /* + * STORE DOUBLE WILL BE REINITIATED + * BY rte + */ + } else { + /* EMULATE ld.d INSTRUCTION */ + v = do_load_word(dmax, dmtx & DMT_DAS); + if (reg != 0) + eframe[EF_R0 + reg] = v; + v = do_load_word(dmax ^ 4, + dmtx & DMT_DAS); + if (reg != 31) + eframe[EF_R0 + reg + 1] = v; + } + } else { + /* not pipeline #2 with a double */ + if (dmtx & DMT_WRITE) { + switch (dmt_en_info[DMT_ENBITS(dmtx)].size) { + case DMT_BYTE: + DAE_DEBUG( + DEBUG_MSG("[byte %x -> [%x(%c)]\n", + dmdx & 0xff, dmax, + ISSET(dmtx, DMT_DAS) ? 's' : 'u') + ); + do_store_byte(dmax, dmdx, + dmtx & DMT_DAS); + break; + case DMT_HALF: + DAE_DEBUG( + DEBUG_MSG("[half %x -> [%x(%c)]\n", + dmdx & 0xffff, dmax, + ISSET(dmtx, DMT_DAS) ? 's' : 'u') + ); + do_store_half(dmax, dmdx, + dmtx & DMT_DAS); + break; + case DMT_WORD: + DAE_DEBUG( + DEBUG_MSG("[word %x -> [%x(%c)]\n", + dmdx, dmax, + ISSET(dmtx, DMT_DAS) ? 's' : 'u') + ); + do_store_word(dmax, dmdx, + dmtx & DMT_DAS); + break; + } + } else { + /* else it's a read */ + switch (dmt_en_info[DMT_ENBITS(dmtx)].size) { + case DMT_BYTE: + v = do_load_byte(dmax, + dmtx & DMT_DAS); + if (!ISSET(dmtx, DMT_SIGNED)) + v &= 0x000000ff; + break; + case DMT_HALF: + v = do_load_half(dmax, + dmtx & DMT_DAS); + if (!ISSET(dmtx, DMT_SIGNED)) + v &= 0x0000ffff; + break; + case DMT_WORD: + v = do_load_word(dmax, + dmtx & DMT_DAS); + break; + } + DAE_DEBUG( + if (reg == 0) + DEBUG_MSG("[no write to r0 done]\n"); + else + DEBUG_MSG("[r%d <- %x]\n", reg, v); + ); + if (reg != 0) + eframe[EF_R0 + reg] = v; + } + } + } else { + /* if lockbar is set... it's part of an XMEM */ + /* + * According to Motorola's "General Information", + * the DMT_DOUB1 bit is never set in this case, as it + * should be. + * If lockbar is set (as it is if we're here) and if + * the write is not set, then it's the same as if DOUB1 + * was set... + */ + if (!ISSET(dmtx, DMT_WRITE)) { + if (x != 2) { + /* RERUN xmem WITH DMD(x+1) */ + x++; + dmdx = eframe[EF_DMD0 + x * 3]; + } else { + /* RERUN xmem WITH DMD2 */ + } + + if (dmt_en_info[DMT_ENBITS(dmtx)].size == + DMT_WORD) { + v = do_xmem_word(dmax, dmdx, + dmtx & DMT_DAS); + } else { + v = do_xmem_byte(dmax, dmdx, + dmtx & DMT_DAS); + } + if (reg != 0) + eframe[EF_R0 + reg] = v; + } else { + if (x == 0) { + if (reg != 0) + eframe[EF_R0 + reg] = dmdx; + eframe[EF_SFIP] = eframe[EF_SNIP]; + eframe[EF_SNIP] = eframe[EF_SXIP]; + eframe[EF_SXIP] = 0; + /* xmem RERUN ON rte */ + eframe[EF_DMT0] = 0; + return; + } + } + } + } + eframe[EF_DMT0] = 0; +} +#endif /* M88100 */ + +#define SIGSYS_MAX 501 +#define SIGTRAP_MAX 510 + +#define EMPTY_BR 0xc0000000 /* empty "br" instruction */ +#define NO_OP 0xf4005800 /* "or r0, r0, r0" */ + +#define BRANCH(FROM, TO) \ + (EMPTY_BR | ((unsigned)(TO) - (unsigned)(FROM)) >> 2) + +#define SET_VECTOR(NUM, VALUE) \ + do { \ + vector[NUM].word_one = NO_OP; \ + vector[NUM].word_two = BRANCH(&vector[NUM].word_two, VALUE); \ + } while (0) + +/* + * vector_init(vector, vector_init_list) + * + * This routine sets up the m88k vector table for the running processor. + * It is called with a very little stack, and interrupts disabled, + * so don't call any other functions! + * XXX clean this - nivas + */ +void +vector_init(m88k_exception_vector_area *vector, unsigned *vector_init_list) +{ + unsigned num; + unsigned vec; + extern void bugtrap(void); + extern void m88110_bugtrap(void); + + for (num = 0; (vec = vector_init_list[num]) != END_OF_VECTOR_LIST; + num++) { + if (vec != UNKNOWN_HANDLER) + SET_VECTOR(num, vec); + } + + for (; num <= SIGSYS_MAX; num++) + SET_VECTOR(num, sigsys); + + for (; num <= SIGTRAP_MAX; num++) + SET_VECTOR(num, sigtrap); + + SET_VECTOR(450, syscall_handler); + SET_VECTOR(504, stepbpt); + SET_VECTOR(511, userbpt); + + /* GCC will by default produce explicit trap 503 for division by zero */ + SET_VECTOR(503, vector_init_list[T_ZERODIV]); +} + +unsigned int luna88k_curspl[MAX_CPUS] = {0, 0, 0, 0}; + +unsigned int int_mask_val[INT_LEVEL] = { + INT_MASK_LV0, + INT_MASK_LV1, + INT_MASK_LV2, + INT_MASK_LV3, + INT_MASK_LV4, + INT_MASK_LV5, + INT_MASK_LV6, + INT_MASK_LV7 +}; + +unsigned int int_set_val[INT_LEVEL] = { + INT_SET_LV0, + INT_SET_LV1, + INT_SET_LV2, + INT_SET_LV3, + INT_SET_LV4, + INT_SET_LV5, + INT_SET_LV6, + INT_SET_LV7 +}; + +/* + * return next safe spl to reenable interrupts. + */ +unsigned int +safe_level(mask, curlevel) + unsigned mask; + unsigned curlevel; +{ + int i; + + for (i = curlevel; i < 8; i++) + if (!(int_mask_val[i] & mask)) + return i; + + panic("safe_level: no safe level for mask 0x%08x level %d found", + mask, curlevel); + /* NOTREACHED */ +} + +void +setlevel(unsigned int level) +{ + unsigned int set_value; + int cpu = cpu_number(); + + set_value = int_set_val[level]; + + if (cpu != master_cpu) + set_value &= INT_SLAVE_MASK; + +#if 0 + set_value &= ISR_SOFTINT_EXCEPT_MASK(cpu); + set_value &= ~blocked_interrupts_mask; +#endif + + *int_mask_reg[cpu] = set_value; +#if 0 + int_mask_shadow[cpu] = set_value; +#endif + luna88k_curspl[cpu] = level; +} + +unsigned +getipl(void) +{ + unsigned curspl; + m88k_psr_type psr; /* processor status register */ + + psr = disable_interrupts_return_psr(); + curspl = luna88k_curspl[cpu_number()]; + set_psr(psr); + return curspl; +} + +unsigned +setipl(unsigned level) +{ + unsigned curspl; + m88k_psr_type psr; /* processor status register */ + +#ifdef DEBUG + if (level > 7) { + printf("setipl: invoked with invalid level %x\n", level); + level &= 0x07; /* and pray it will work */ + } +#endif + + psr = disable_interrupts_return_psr(); + curspl = luna88k_curspl[cpu_number()]; + setlevel(level); + + flush_pipeline(); + + /* The flush pipeline is required to make sure the above write gets + * through the data pipe and to the hardware; otherwise, the next + * bunch of instructions could execute at the wrong spl protection + */ + set_psr(psr); + return curspl; +} + +unsigned +raiseipl(unsigned level) +{ + unsigned curspl; + m88k_psr_type psr; /* processor status register */ + +#ifdef DEBUG + if (level > 7) { + printf("raiseipl: invoked with invalid level %x\n", level); + level &= 0x07; /* and pray it will work */ + } +#endif + + psr = disable_interrupts_return_psr(); + curspl = luna88k_curspl[cpu_number()]; + if (curspl < level) + setlevel(level); + + flush_pipeline(); + + /* The flush pipeline is required to make sure the above write gets + * through the data pipe and to the hardware; otherwise, the next + * bunch of instructions could execute at the wrong spl protection + */ + set_psr(psr); + return curspl; +} + +/* XXX Utterly bogus */ +#if NCPUS > 1 +#include <sys/simplelock.h> +void +simple_lock_init(lkp) + struct simplelock *volatile lkp; +{ + lkp->lock_data = 0; +} + +int +test_and_set(lock) + int *volatile lock; +{ +#if 0 + int oldlock = *lock; + if (*lock == 0) { + *lock = 1; + return 0; + } +#else + return *lock; + *lock = 1; + return 0; +#endif +} +#endif diff --git a/sys/arch/luna88k/luna88k/m88100_fp.S b/sys/arch/luna88k/luna88k/m88100_fp.S new file mode 100644 index 00000000000..c3959e4879f --- /dev/null +++ b/sys/arch/luna88k/luna88k/m88100_fp.S @@ -0,0 +1,2516 @@ +/* $OpenBSD: m88100_fp.S,v 1.1 2004/04/21 15:24:02 aoyama Exp $ */ +/* + * Mach Operating System + * Copyright (c) 1991 Carnegie Mellon University + * Copyright (c) 1991 OMRON Corporation + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON AND OMRON ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON AND OMRON DISCLAIM ANY LIABILITY OF ANY KIND + * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie the + * rights to redistribute these changes. + */ + +/* Floating point trouble routines */ +#include "assym.h" +#include <machine/trap.h> +#include <machine/asm.h> + +#define destsize 10 + +/* Floating-Point Status Register bits */ +#define inexact 0 +#define overflow 1 +#define underflow 2 +#define divzero 3 +#define oper 4 + +#define sign 31 +#define s1size 9 +#define s2size 7 +#define dsize 5 + +#define FADDop 0x05 +#define FSUBop 0x06 +#define FCMPop 0x07 +#define FMULop 0x00 +#define FDIVop 0x0e +#define FSQRTop 0x0f +#define INTop 0x09 +#define NINTop 0x0a +#define TRNCop 0x0b + +#define s1nan 7 +#define s2nan 6 +#define s1inf 5 +#define s2inf 4 +#define s1zero 3 +#define s2zero 2 +#define sigbit 19 + +#define modehi 30 +#define modelo 29 +#define rndhi 15 +#define rndlo 14 +#define efunf 7 +#define efovf 6 +#define efinx 5 + +ASENTRY(m88100_Xfp_precise) + or r29, r3, r0 /* r29 is now the E.F. */ + subu r31, r31, 40 + st r1, r31, 32 + st r29, r31, 36 + + ld r2, r29, EF_FPSR * 4 + ld r3, r29, EF_FPCR * 4 + ld r4, r29, EF_FPECR * 4 + ld r5, r29, EF_FPHS1 * 4 + ld r6, r29, EF_FPLS1 * 4 + ld r7, r29, EF_FPHS2 * 4 + ld r8, r29, EF_FPLS2 * 4 + ld r9, r29, EF_FPPT * 4 + + + /* + * Load into r1 the return address for the 0 handlers. Looking at + * FPECR, branch to the appropriate 0 handler. However, if none of the + * 0 bits are enabled, then a floating point instruction was issued + * with the floating point unit disabled. This will cause an + * unimplemented opcode 0. + */ + + or.u r1,r0,hi16(wrapup) /* load return address of function */ + or r1,r1,lo16(wrapup) + + bb0 6,r4, 3f /* branch to FPunimp if bit set */ + br FPuimp +3: + bb0 7,r4, 4f /* branch to FPintover if bit set */ + br FPintover +4: +#if 0 + bb0 5,r4, 5f /* branch to FPpriviol if bit set */ + br FPpriviol +#endif +5: + bb0 4,r4, 6f /* branch to FPresoper if bit set */ + br FPresoper +6: + bb0 3,r4, 7f /* branch to FPdivzero if bit set */ + br FPdivzero +7: + or.u r4, r4, 0xffff + +ASLOCAL(FPuimp) + subu r31,r31,40 /* allocate stack */ + st r1,r31,36 /* save return address */ + st r3,r31,32 /* save exception frame */ + or r2,r0,T_FPEPFLT /* load trap type */ + or r3, r29, r0 + bsr _C_LABEL(m88100_trap) + ld r1,r31,36 /* recover return address */ + addu r31,r31,40 /* deallocate stack */ + jmp r1 + + /* + * To write back the results to the user registers, disable exceptions + * and the floating point unit. Write FPSR and FPCR and load the SNIP + * and SFIP. + * r5 will contain the upper word of the result + * r6 will contain the lower word of the result + */ + +ASLOCAL(wrapup) + tb1 0,r0,0 /* make sure all floating point operations */ + /* have finished */ + ldcr r10, cr1 /* load the PSR */ +#if 0 + set r10, r10, 1<PSR_FPU_DISABLE_BIT> +#endif + set r10, r10, 1<PSR_INTERRUPT_DISABLE_BIT> + stcr r10, cr1 + + ld r1, r31, 32 + ld r29, r31, 36 + addu r31, r31, 40 + + fstcr r2, FPSR /* write revised value of FPSR */ + fstcr r3, FPCR /* write revised value of FPCR */ + + /* result writeback routine */ + addu r3, r29, EF_R0 * 4 + extu r2, r9, 5<0> /* get 5 bits of destination register */ + bb0 5, r9, writesingle /* branch if destination is single */ + +/* writedouble here */ + st r5, r3 [r2] /* write high word */ + add r2, r2, 1 /* for double, the low word is the */ + /* unspecified register */ + clr r2, r2, 27<5> /* perform equivalent of mod 32 */ +ASLOCAL(writesingle) + st r6, r3 [r2] /* write low word into memory */ + jmp r1 + +/* + * Check if the numerator is zero. If the numerator is zero, then handle + * this instruction as you would a 0/0 invalid operation. + */ + +ASLOCAL(FPdivzero) + st r1,r31,0 /* save return address */ + bb1 s1size,r9,1f /* branch if numerator double */ +/* single number */ + clr r10,r5,1<sign> /* clear sign bit */ + extu r11,r6,3<29> /* grab upper bits of lower word */ + or r10,r10,r11 /* combine ones of mantissa */ + bcnd eq0,r10,resoper /* numerator is zero, handle reserved operand */ + br setbit /* set divzero bit */ +1: +/* double number */ + clr r10,r5,1<sign> /* clear sign bit */ + or r10,r10,r6 /* or high and low words */ + bcnd ne0,r10,setbit /* set divzero bit */ + +/* + * The numerator is zero, so handle the invalid operation by setting the + * invalid operation bit and branching to the user handler if there is one + * or writing a quiet NaN to the destination. + */ + +ASLOCAL(resoper) + set r2,r2,1<oper> +#ifdef HANDLER + bb0 oper,r3,noreshand /* branch to execute default handling */ + /* for reserved operands */ + bsr _handler /* branch to user handler */ + br FP_div_return +#endif + +noreshand: + set r5,r0,0<0> /* put a NaN in high word */ + set r6,r0,0<0> /* put a NaN in low word */ + br FP_div_return + /* writing to a word which may be ignored */ + /* is just as quick as checking the precision */ + /* of the destination */ + +/* + * The operation is divide by zero, so set the divide by zero bit in the + * FPSR. If the user handler is set, then go to the user handler, else + * go to the default mode. + */ + +setbit: + set r2,r2,1<divzero> +#ifdef HANDLER + bb0 divzero,r3,default /* go to default routine if no hdlr */ + bsr _handler /* execute handler routine */ + br FP_div_return +#endif + +/* + * Considering the sign of the numerator and zero, write a correctly + * signed infinity of the proper precision into the destination. + */ + +default: + bb1 dsize,r9,FPzero_double /* branch to handle double result */ +FPzero_single: + clr r10,r5,31<0> /* clear all of S1HI except sign bit */ + xor r10,r7,r10 /* xor the sign bits of the operands */ + or.u r6,r0,0x7f80 /* load single precision infinity */ + br.n FP_div_return + or r6,r6,r10 /* load correctly signed infinity */ + +FPzero_double: + clr r10,r5,31<0> /* clear all of S1HI except sign bit */ + xor r10,r7,r10 /* xor the sign bits of the operands */ + or.u r5,r0,0x7ff0 /* load double precision infinity */ + or r5,r5,r10 /* load correctly signed infinity */ + or r6,r0,r0 /* clear lower word of double */ + +FP_div_return: + ld r1,r31,0 /* load return address */ + jmp r1 + +/* + * Both NINT and TRNC require a certain rounding mode, so check which + * instruction caused the integer conversion overflow. Use a substitute + * FPCR in r1, and modify the rounding mode if the instruction is NINT + * or TRNC. + */ +ASLOCAL(FPintover) + extu r10,r9,5<11> /* extract opcode */ + cmp r11,r10,INTop /* see if instruction is INT */ + st r1,r31,0 /* save return address */ + bb1.n eq,r11,checksize /* instruction is INT, do not modify */ + /* rounding mode */ + or r1,r0,r3 /* load FPCR into r1 */ + cmp r11,r10,NINTop /* see if instruction is NINT */ + bb1 eq,r11,NINT /* instruction is NINT */ +TRNC: + clr r1,r1,2<rndlo> /* clear rounding mode bits, */ + /* instruction is TRNC */ + br.n checksize /* branch to check size */ + set r1,r1,1<rndlo> /* make rounding mode round towards */ + /* zero */ +NINT: + clr r1,r1,2<rndlo> /* make rounding mode round to */ + /* nearest */ + +/* See whether the source is single or double precision. */ + +checksize: + bb1 s2size,r9,checkdoub /* S2 is double, branch to see if */ + /* there is a false alarm */ + +/* + * An integer has more bits than the mantissa of a single precision floating + * point number, so to check for false alarms (i.e. valid conversion), simply + * check the exponents. False alarms are detected for 2**30 to (2**30) - 1 + * and -2**30 to -2**31. Only seven bits need to be looked at since an + * exception will not occur for the other half of the numbering system. + * To speed up the processing, first check to see if the exponent is 32 or + * greater. + * + * This code was originally written for the exponent in the control + * register to have the most significant bit (8 - single, 11 - double) + * flipped and sign extended. For precise exceptions, however, the most + * significant bit is only sign extended. Therefore, the code was chopped + * up so that it would work for positive values of real exponent which were + * only sign extended. + */ + +checksing: + extu r10,r7,7<20> /* internal representation for single */ + /* precision is IEEE 8 bits sign extended */ + /* to 11 bits; for real exp. = 30, the */ + /* above instruction gives a result exp. */ + /* that has the MSB flipped and sign */ + /* extended like in the IMPCR */ + cmp r11,r10,31 /* compare to 32,but exp. off by 1 */ + /* these 2 instructions to speed up valid */ + /* execution of valid cases */ + bb1 ge,r11,overflw /* valid case, perform overflow routine */ + bb1 sign,r7,checksingn /* source operand is negative */ + +/* + * If the number is positve and the exponent is greater than 30, than it is + * overflow. + */ +checksingp: + cmp r10,r10,29 /* compare to 30, but exp. off by 1 */ + bb1 gt,r10,overflw /* no false alarm, its overflow */ + br conversionsp /* finish single precision conversion */ + +/* + * If the number is negative, and the exponent is 30, or 31 with a mantissa + * of 0, then it is a false alarm. + */ +checksingn: + cmp r11,r10,30 /* compare to 31,but exp. off by 1 */ + bb1 lt,r11,conversionsn /* exp. less than 31, so convert */ + extu r10,r8,3<29> /* get upper three bits of lower */ + /* mantissa */ + mak r12,r7,20<3> /* get upper 20 bits of mantissa */ + or r10,r10,r12 /* form complete mantissa */ + bcnd eq0,r10,conversionsn /* complete conversion if mantissa */ + /* is 0 */ + br overflw /* no false alarm, its overflow */ + +/* + * False alarms are detected for 2**30 to (2**30) - 1 and -2**30 to -2**31. + * Only seven bits need to be looked at since an exception will not occur + * for the other half of the numbering system. + * To speed up the processing, first check to see if the exponent is 32 or + * greater. Since there are more mantissa bits than integer bits, rounding + * could cause overflow. (2**31) - 1 needs to be checked so that it does + * not round to 2**31, and -2**31 needs to be checked in case it rounds to + * -((2**31) + 1). + */ +checkdoub: + extu r10,r7,10<20> /* internal representation for double */ + /* precision is the same IEEE 11 bits */ + /* for real exp. = 30, the */ + /* above instruction gives a result exp. */ + /* that has the MSB flipped and sign */ + /* extended like in the IMPCR */ + cmp r11,r10,31 /* compare to 32,but exp. off by 1 */ + /* these 2 instructions to speed up valid */ + /* execution of valid cases */ + bb1 ge,r11,overflw /* valid case, perform overflow routine */ + bb1 sign,r7,checkdoubn /* source operand is negative */ + +/* + * If the exponent is not 31, then the floating point number will be rounded + * before the conversion is done. A branch table is set up with bits 4 and 3 + * being the rounding mode, and bits 2, 1, and 0 are the guard, round, and + * sticky bits. + */ +checkdoubp: + cmp r11,r10,30 /* compare to 31, but exponent off by 1 */ + bb1 eq,r11,overflw /* no false alarm, its overflow */ + extu r12,r8,1<22> /* get LSB for integer with exp. = 30 */ + mak r12,r12,1<2> /* start to set up field for branch table */ + extu r11,r8,1<21> /* get guard bit */ + mak r11,r11,1<1> /* set up field for branch table */ + or r12,r11,r12 /* set up field for branch table */ + extu r11,r8,21<0> /* get bits for sticky bit */ + bcnd eq0,r11,nostickyp /* do not set sticky */ + set r12,r12,1<0> /* set sticky bit */ +nostickyp: + rot r11,r1,0<rndlo> /* shift rounding mode to 2 LSB''s */ + mak r11,r11,2<3> /* set up field, clear other bits */ + or r12,r11,r12 /* set up field for branch table */ + lda r12,r0[r12] /* scale r12 */ + or.u r12,r12,hi16(ptable) /* load pointer into table */ + addu r12,r12,lo16(ptable) + jmp r12 + +ptable: + br conversiondp + br conversiondp + br conversiondp + br paddone + br conversiondp + br conversiondp + br paddone + br paddone + br conversiondp + br conversiondp + br conversiondp + br conversiondp + br conversiondp + br conversiondp + br conversiondp + br conversiondp + br conversiondp + br conversiondp + br conversiondp + br conversiondp + br conversiondp + br conversiondp + br conversiondp + br conversiondp + br conversiondp + br paddone + br paddone + br paddone + br conversiondp + br paddone + br paddone + br paddone + +/* + * Add one to the bit of the mantissa which corresponds to the LSB of an + * integer. If the mantissa overflows, then there is a valid integer + * overflow conversion; otherwise, the mantissa can be converted to the + * integer. + */ +paddone: + or r10,r0,r0 /* clear r10 */ + set r10,r10,1<22> /* set LSB bit to 1 for adding */ + addu.co r8,r8,r10 /* add the 1 obtained from rounding */ + clr r11,r7,12<20> /* clear exponent and sign */ + addu.ci r11,r0,r11 /* add carry */ + bb1 20,r11,overflw /* overflow to 2**31, abort the rest */ + br.n conversiondp /* since the exp. was 30, and the exp. */ + /* did not round up to 31, the largest */ + /* number that S2 could become is 2**31-1 */ + or r7,r0,r11 /* store r11 into r7 for conversion */ + +/* + * Now check for negative double precision sources. If the exponent is 30, + * then convert the false alarm. If the exponent is 31, then check the + * mantissa bits which correspond to integer bits. If any of them are a one, + * then there is overflow. If they are zero, then check the guard, round, + * and sticky bits. + * Round toward zero and positive will not cause a roundup, but round toward + * nearest and negative may, so perform those roundings. If there is no + * overflow, then convert and return. + */ +checkdoubn: + cmp r11,r10,29 /* compare to 30, but exp. off by 1 */ + bb1 eq,r11,conversiondn /* false alarm if exp. = 30 */ + extu r10,r8,11<21> /* check upper bits of lower mantissa */ + bcnd ne0,r10,overflw /* one of the bits is a 1, so oflow */ + extu r10,r7,20<0> /* check upper bits of upper mantissa */ + bcnd ne0,r10,overflw /* one of the bits is a 1, so oflow */ + bb0 rndlo,r1,possround /* rounding mode is either round near */ + /* or round negative, which may cause */ + /* a round */ + br.n FPintov_return /* round positive, which will not */ + /* cause a round */ + set r6,r0,1<sign> +possround: + extu r12,r8,1<20> /* get guard bit */ + extu r11,r8,20<0> /* get bits for sticky bit */ + bcnd.n eq0,r11,nostickyn /* do not set sticky */ + mak r12,r12,1<1> /* set up field for branch table */ + set r12,r12,1<0> /* set sticky bit */ +nostickyn: + bb1 rndhi,r1,negative /* rounding mode is negative */ +nearest: + cmp r12,r12,3 /* are both guard and sticky set */ + bb1 eq,r12,overflw /* both guard and sticky are set, */ + /* so signal overflow */ + or r6,r0,r0 /* clear destination register r6 */ + br.n FPintov_return + set r6,r6,1<sign> /* set the sign bit and take care of */ + /* this special case */ +negative: + bcnd ne0,r12,overflw /* -2**31 will be rounded to */ + /* -(2**31+1), so signal overflow */ + or r6,r0,r0 /* clear destination register r6 */ + br.n FPintov_return + set r6,r6,1<sign> /* set the sign bit and take care of */ + /* this special case */ + + /* + * Since the exp. was 30, and there was no round-up, the largest + * number that S2 could have been was 2**31 - 1 + */ + + + /* Convert the single precision positive floating point number. */ + +conversionsp: + extu r6,r8,3<29> /* extract lower bits of integer */ + mak r6,r6,3<7> /* shift left to correct place in integer */ + mak r10,r7,20<10> /* shift left upper bits of integer */ + or r6,r6,r10 /* form most of integer */ + br.n FPintov_return + set r6,r6,1<30> /* set hidden one */ + + /* Convert the single precision negative floating point number. */ + +conversionsn: + bb1 eq,r11,exp31s /* use old r11 to see if exp. is 31 */ + extu r6,r8,3<29> /* extract lower bits of mantissa */ + mak r6,r6,3<7> /* shift left to correct place in integer */ + mak r10,r7,20<10> /* shift left upper bits of integer */ + or r6,r6,r10 /* form most of integer */ + set r6,r6,1<30> /* set hidden one */ + or.c r6,r0,r6 /* negate result */ + br.n FPintov_return + addu r6,r6,1 /* add 1 to get 2''s complement */ +exp31s: + or r6,r0,r0 /* clear r6 */ + br.n FPintov_return + set r6,r6,1<sign> /* set sign bit */ + + /* Convert the double precision positive floating point number. */ + +conversiondp: + extu r6,r8,10<22> /* extract lower bits of integer */ + mak r10,r7,20<10> /* shift left upper bits of integer */ + or r6,r6,r10 /* form most of integer */ + br.n FPintov_return + set r6,r6,1<30> /* set hidden one */ + + /* + * Convert the double precision negative floating point number. + * The number, whose exponent is 30, must be rounded before converting. + * Bits 4 and 3 are the rounding mode, and bits 2, 1, and 0 are the + * guard, round, and sticky bits for the branch table. + */ + +conversiondn: + extu r12,r8,1<22> /* get LSB for integer with exp. = 30 */ + mak r12,r12,1<2> /* start to set up field for branch table */ + extu r11,r8,1<21> /* get guard bit */ + mak r11,r11,1<1> /* set up field for branch table */ + or r12,r11,r12 /* set up field for branch table */ + extu r11,r8,21<0> /* get bits for sticky bit */ + bcnd eq0,r11,nostkyn /* do not set sticky */ + set r12,r12,1<0> /* set sticky bit */ +nostkyn: + rot r11,r1,0<rndlo> /* shift rounding mode to 2 LSB''s */ + mak r11,r11,2<3> /* set up field, clear other bits */ + or r12,r11,r12 /* set up field for branch table */ + lda r12,r0[r12] /* scale r12 */ + or.u r12,r12,hi16(ntable) /* load pointer into table */ + addu r12,r12,lo16(ntable) + jmp r12 + +ntable: + br nnoaddone + br nnoaddone + br nnoaddone + br naddone + br nnoaddone + br nnoaddone + br naddone + br naddone + br nnoaddone + br nnoaddone + br nnoaddone + br nnoaddone + br nnoaddone + br nnoaddone + br nnoaddone + br nnoaddone + br nnoaddone + br naddone + br naddone + br naddone + br nnoaddone + br naddone + br naddone + br naddone + br nnoaddone + br nnoaddone + br nnoaddone + br nnoaddone + br nnoaddone + br nnoaddone + br nnoaddone + br nnoaddone + +/* + * Add one to the mantissa, and check to see if it overflows to -2**31. + * The conversion is done in nnoaddone. + */ + +naddone: + or r10,r0,r0 /* clear r10 */ + set r10,r10,1<22> /* set LSB bit to 1 for adding */ + add.co r8,r8,r10 /* add the 1 obtained from rounding */ + clr r7,r7,12<20> /* clear exponent and sign */ + add.ci r7,r0,r7 /* add carry */ + bb1 20,r7,maxneg /* rounded to -2**31,handle separately */ + /* the exponent was originally 30 */ +nnoaddone: + extu r6,r8,11<22> /* extract lower bits of integer */ + mak r10,r7,20<10> /* shift left upper bits of integer */ + or r6,r6,r10 /* form most of integer */ + set r6,r6,1<30> /* set hidden one */ + or.c r6,r0,r6 /* negate integer */ + br.n FPintov_return + addu r6,r6,1 /* add 1 to get 2''s complement */ + +maxneg: + or r6,r0,r0 /* clear integer */ + br.n FPintov_return + set r6,r6,1<sign> /* set sign bit */ + + /* + * For valid overflows, check to see if the integer overflow user + * handler is set. If it is set, then go to user handler, else write + * the correctly signed largest integer. + */ + +overflw: + set r2,r2,1<oper> +#ifdef HANDLER + bb0 oper,r3,nohandler /* do not go to user handler routine */ + bsr _handler /* go to user handler routine */ + br FPintov_return +nohandler: +#endif + bb0.n sign,r7,FPintov_return /* if positive then return */ + set r6,r6,31<0> /* set result to largest positive int */ + or.c r6,r0,r6 /* negate r6, giving largest negative */ + /* integer */ + +FPintov_return: + ld r1,r31,0 /* load return address from memory */ + jmp r1 + +/* + * Some instructions only have the S2 operations, so clear S1HI and S1LO + * for those instructions so that the previous contents of S1HI and S1LO + * do not influence this instruction. + */ + +ASLOCAL(FPresoper) + st r1, r31, 0 + extu r10,r9,5<11> /* extract opcode */ +#if 0 + cmp r11,r10,FSQRTop /* compare to FSQRT */ + bb1 eq,r11,S1clear /* clear S1 if instruction only had S2 operand */ +#endif + cmp r11,r10,INTop /* compare to INT */ + bb1 eq,r11,S1clear /* clear S1 if instruction only had S2 operand */ + cmp r11,r10,NINTop /* compare to NINT */ + bb1 eq,r11,S1clear /* clear S1 if instruction only had S2 operand */ + cmp r11,r10,TRNCop /* compare to TRNC */ + bb0 eq,r11,opercheck /* check for reserved operands */ + +ASLOCAL(S1clear) + or r5,r0,r0 /* clear any NaN''s, denorms, or infinities */ + or r6,r0,r0 /* that may be left in S1HI,S1LO from a */ + /* previous instruction */ + +/* + * r12 contains the following flags: + * bit 9 -- s1sign + * bit 8 -- s2sign + * bit 7 -- s1nan + * bit 6 -- s2nan + * bit 5 -- s1inf + * bit 4 -- s2inf + * bit 3 -- s1zero + * bit 2 -- s2zero + * bit 1 -- s1denorm + * bit 0 -- s2denorm + */ + +/* + * Using code for both single and double precision, check if S1 is either + * a NaN or infinity and set the appropriate flags in r12. Then check if + * S2 is a NaN or infinity. If it is a NaN, then branch to the NaN routine. + */ + +ASLOCAL(opercheck) + extu r10,r5,11<20> /* internal representation for double */ + bb1.n s1size,r9,S1NaNdoub /* S1 is double precision */ + or r12,r0,r0 /* clear operand flag register */ +ASLOCAL(S1NaNsing) + xor r10,r10,0x0080 /* internal representation for single */ + ext r10,r10,8<0> /* precision is IEEE 8 bits sign extended */ + /* to 11 bits; for real exp. > 0, the */ + /* above instructions gives a result exp. */ + /* that has the MSB flipped and sign */ + /* extended like in the IMPCR */ + cmp r11,r10,127 /* Is exponent equal to IEEE 255 (here 127) */ + bb1 ne,r11,S2NaN /* source 1 is not a NaN or infinity */ + mak r10,r5,20<0> /* load r10 with upper bits of S1 mantissa */ + extu r11,r6,3<29> /* get 3 upper bits of lower word */ + or r11,r10,r11 /* combine any existing 1 */ + bcnd eq0,r11,noS1NaNs /* since r11 can only hold 0 or a */ + /* > 0 number, branch to noS1NaN when eq0 */ + br.n S2NaN /* see if S2 has a NaN */ + set r12,r12,1<s1nan> /* indicate that S1 has a NaN */ +ASLOCAL(noS1NaNs) + br.n S2NaN /* check contents of S2 */ + set r12,r0,1<s1inf> /* indicate that S1 has an infinity */ + +ASLOCAL(S1NaNdoub) + xor r10,r10,0x0400 /* precision is the same IEEE 11 bits */ + /* The above instructions gives a result exp. */ + /* that has the MSB flipped and sign */ + /* extended like in the IMPCR */ + cmp r11,r10,1023 /* Is exp. equal to IEEE 2047 (internal 1023) */ + bb1 ne,r11,S2NaN /* source 1 is not a NaN or infinity */ + mak r10,r5,20<0> /* load r10 with upper bits of S1 mantissa */ + or r11,r6,r10 /* combine existing 1''s of mantissa */ + bcnd eq0,r11,noS1NaNd /* since r11 can only hold 0 or a > 0 */ + /* number, branch to noS1NaN when eq0 */ + br.n S2NaN /* see if S2 has a NaN */ + set r12,r12,1<s1nan> /* indicate that S1 has a NaN */ +ASLOCAL(noS1NaNd) + set r12,r0,1<s1inf> /* indicate that S1 has an infinity */ + +ASLOCAL(S2NaN) + bb1.n s2size,r9,S2NaNdoub /* S1 is double precision */ + extu r10,r7,11<20> /* internal representation for double */ +ASLOCAL(S2NaNsing) + xor r10,r10,0x0080 /* internal representation for single */ + ext r10,r10,8<0> /* precision is IEEE 8 bits sign extended */ + /* to 11 bits; for real exp. > 0, the */ + /* above instruction gives a result exp. */ + /* that has the MSB flipped and sign */ + /* extended like in the IMPCR */ + cmp r11,r10,127 /* Is exponent equal to IEEE 255 (here 127) */ + bb1 ne,r11,inf /* source 2 is not a NaN or infinity */ + mak r10,r7,20<0> /* load r10 with upper bits of S1 mantissa */ + extu r11,r8,3<29> /* get 3 upper bits of lower word */ + or r11,r10,r11 /* combine any existing 1''s */ + bcnd eq0,r11,noS2NaNs /* since r11 can only hold 0 or a > 0 */ + /* number, branch to noS2NaNs when eq0 */ + br.n _ASM_LABEL(NaN) /* branch to NaN routine */ + set r12,r12,1<s2nan> /* indicate that s2 has a NaN */ +ASLOCAL(noS2NaNs) + bb0 s1nan,r12, 1f /* branch to NaN if S1 is a NaN */ + br _ASM_LABEL(NaN) +1: + br.n _ASM_LABEL(infinity) /* If S1 had a NaN we would have */ + /* already branched, and S2 does not have a */ + /* NaN, but it does have an infinity, so */ + /* branch to handle the finity */ + set r12,r12,1<s2inf> /* indicate that S2 has an infinity */ + +ASLOCAL(S2NaNdoub) + xor r10,r10,0x0400 /* precision is the same IEEE 11 bits */ + /* The above instruction gives a result exp. */ + /* that has the MSB flipped and sign */ + /* extended like in the IMPCR */ + cmp r11,r10,1023 /* Is exp. equal to IEEE 2047 (internal 1023) */ + bb1 ne,r11,inf /* source 2 is not a NaN or infinity */ + mak r10,r7,20<0> /* load r10 with upper bits of S2 mantissa */ + or r11,r8,r10 /* combine existing 1''s of mantissa */ + bcnd eq0,r11,noS2NaNd /* since r11 can only hold 0 or a > 0 */ + /* number, branch to noS2NaNd when eq0 */ + br.n _ASM_LABEL(NaN) /* branch to NaN routine */ + set r12,r12,1<s2nan> /* indicate that s2 has a NaN */ +ASLOCAL(noS2NaNd) + bb0 s1nan,r12,1f /* branch to NaN if S1 is a NaN */ + br _ASM_LABEL(NaN) +1: + br.n _ASM_LABEL(infinity) /* If S1 had a NaN we would have */ + /* already branched, and S2 does not have a */ + /* NaN, but it does have an infinity, so */ + /* branch to handle the finity */ + set r12,r12,1<s2inf> /* indicate that S2 has an infinity */ + +/* + * If S2 was a NaN, the routine would have already branched to NaN. If S1 + * is a NaN, then branch to NaN. If S1 is not a NaN and S2 is infinity, then + * we would have already branched to infinity. If S1 is infinity, then branch. + * If the routine still has not branched, then branch to denorm, the only + * reserved operand left. + */ + +ASLOCAL(inf) + bb0 s1nan,r12,1f /* branch if S1 has a NaN and S2 does not */ + br _ASM_LABEL(NaN) +1: + bb0 s1inf,r12,2f /* Neither S1 or S2 has a NaN, and we would */ + /* have branched already if S2 had an */ + /* infinity, so branch if S1 is infinity */ + br _ASM_LABEL(infinity) +2: + br _ASM_LABEL(denorm) /* branch to denorm, the only */ + /* remaining alternative */ + +/* + * First check for an underflow user handler. If there is not one, then + * branch to the routine to make a denormalized number. Before branching + * to the underflow user handler, add 192 to a single precision exponent + * and 1536 to a double precision exponent. + */ + +ASLOCAL(FPunderflow) + st r1,r31,0 /* save return address */ + set r2,r2,1<underflow> + set r2,r2,1<inexact> +#ifdef HANDLER + bb0 efunf,r12,FPU_denorm /* jump to default procedure */ + bb1 destsize,r12,doubleprec /* double precision destination */ +singleprec: + or.u r6,r0,0x0c00 /* load exponent adjust 192 */ + br.n callundhand /* branch to call handler for user handler */ + add r12,r6,r12 /* adjust single precision exponent */ +doubleprec: + or.u r6,r0,0x6000 /* load exponent adjust 1536 */ + add r12,r6,r12 /* adjust double precision exponent */ +callundhand: + bsr _handler /* call handler for user handler */ + br Ureturn +#endif + +/* + * Now the floating point number, which has an exponent smaller than what + * IEEE allows, must be denormalized. Denormalization is done by calculating + * the difference between a denormalized exponent and an underflow exponent + * and shifting the mantissa by that amount. A one may need to be subtracted + * from the LSB if a one was added during rounding. + * r9 is used to contain the guard, round, sticky, and an inaccuracy bit in + * case some bits were shifted off the mantissa during denormalization. + * r9 will contain: + * bit 4 -- new addone if one added during rounding after denormalization + * bit 3 -- inaccuracy flag caused by denormalization or pre-denormalization + * inexactness + * bit 2 -- guard bit of result + * bit 1 -- round bit of result + * bit 0 -- sticky bit of result + */ + +FPU_denorm: + bb1.n destsize,r12,Udouble /* denorm for double */ + extu r9,r10,3<26> /* load r9 with grs */ +Usingle: + mak r5,r10,21<3> /* extract high 21 bits of mantissa */ + extu r6,r11,3<29> /* extract low 3 bits of mantissa */ + or r11,r5,r6 /* form 24 bits of mantissa */ + +/* See if the addone bit is set and unround if it is. */ + bb0.n 25,r10,nounrounds /* do not unround if addone bit clear */ + extu r6,r12,12<20> /* extract signed exponent from IMPCR */ +unrounds: + subu r11,r11,1 /* subtract 1 from mantissa */ + +/* + * If the hidden bit is cleared after subtracting the one, then the one added + * during the rounding must have propagated through the mantissa. The exponent + * will need to be decremented. + */ + bb1 23,r11,nounrounds /* if hidden bit is set,then exponent */ + /* does not need to be decremented */ +decexps: + sub r6,r6,1 /* decrement exponent 1 */ + set r11,r11,1<23> /* set the hidden bit */ + +/* + * For both single and double precision, there are cases where it is easier + * and quicker to make a special case. Examples of this are if the shift + * amount is only 1 or 2, or all the mantissa is shifted off, or all the + * mantissa is shifted off and it is still shifting, or, in the case of + * doubles, if the shift amount is around the boundary of MANTLO and MANTHI. + */ + +nounrounds: + or r8,r0,lo16(0x00000f81) /* load r8 with -127 in decimal */ + /* for lowest 12 bits */ + sub r7,r8,r6 /* find difference between two exponents, */ + /* this amount is the shift amount */ + cmp r6,r7,3 /* check to see if r7 contains 3 or more */ + bb1 ge,r6,threesing /* br to code that handles shifts of >=3 */ + cmp r6,r7,2 /* check to see if r7 contains 2 */ + bb1 eq,r6,twosing /* br to code that handles shifts of 2 */ +one: + rot r9,r9,0<1> /* rotate roundoff register once, this places */ + /* guard in round and round in sticky */ + bb0 31,r9,nosticky1s /* do not or round and sticky if sticky is */ + /* 0, this lost bit will be cleared later */ + set r9,r9,1<0> /* or round and sticky */ +nosticky1s: + bb0 0,r11,guardclr1s /* do not set guard bit if LSB = 0 */ + set r9,r9,1<2> /* set guard bit */ +guardclr1s: + extu r11,r11,31<1> /* shift mantissa right 1 */ + br.n round /* round result */ + mak r9,r9,3<0> /* clear bits lost during rotation */ + +twosing: + rot r9,r9,0<2> /* rotate roundff register twice, this places */ + /* guard in sticky */ + bb0 30,r9,nosticky2s /* do not or guard and sticky if stick is 0 */ + /* this lost bit will be cleared later */ + br.n noround2s /* skip or old guard and old round if old */ + /* sticky set */ + set r9,r9,1<0> /* or guard and sticky */ +nosticky2s: + bb0 31,r9,noround2s /* do not or guard and round if round is 0 */ + /* this lost bit will be cleared later */ + set r9,r9,1<0> /* or guard and round */ +noround2s: + bb0 0,r11,roundclr2s /* do not set round bit if LSB = 0 */ + set r9,r9,1<1> /* set round bit */ +roundclr2s: + bb0 1,r11,guardclr2s /* do not set guard bit if LSB + 1 = 0 */ + set r9,r9,1<2> /* set guard bit */ +guardclr2s: + extu r11,r11,30<2> /* shift mantissa right 2 */ + br.n round /* round result */ + mak r9,r9,3<0> /* clear bits lost during rotation */ + +threesing: + bb1 0,r9,noguard3s /* check sticky initially */ + /* sticky is set, forget most of the oring */ +nosticky3s: + bb0 1,r9,noround3s /* check round initially, do not set sticky */ + br.n noguard3s /* forget most of the rest of oring */ + set r9,r9,1<0> /* if round is clear,set sticky if round set */ +noround3s: + bb0.n 2,r9,noguard3s /* check guard initially, do not set sticky */ + clr r9,r9,2<1> /* clear the original guard and round for when */ + /* you get to round section */ + set r9,r9,1<0> /* if guard is clear,set sticky if guard set */ +noguard3s: + cmp r6,r7,23 /* check if # of shifts is <=23 */ + bb1 gt,r6,s24 /* branch to see if shifts = 24 */ + sub r6,r7,2 /* get number of bits to check for sticky */ + mak r6,r6,5<5> /* shift width into width field */ + mak r8,r11,r6 /* mask off shifted bits -2 */ + ff1 r8,r8 /* see if r8 has any ones */ + bb1 5,r8,nostky23 /* do not set sticky if no ones found */ + set r9,r9,1<0> /* set sticky bit */ +nostky23: + or r8,r0,34 /* start code to get new mantissa plus two */ + /* extra bits for new round and new guard */ + /* bits */ + subu r8,r8,r7 + mak r8,r8,5<5> /* shift field width into second five bits */ + extu r6,r6,5<5> /* shift previous shifted -2 into offset field */ + or r6,r6,r8 /* complete field */ + extu r11,r11,r6 /* form new mantissa with two extra bits */ + + bb0 0,r11,nornd3s /* do not set new round bit */ + set r9,r9,1<1> /* set new round bit */ +nornd3s: + bb0 1,r11,nogrd3s /* do not set new guard bit */ + set r9,r9,1<2> /* set new guard bit */ +nogrd3s: + br.n round /* round mantissa */ + extu r11,r11,30<2> /* shift off remaining two bits */ + +s24: + cmp r6,r7,24 /* check to see if # of shifts is 24 */ + bb1 gt,r6,s25 /* branch to see if shifts = 25 */ + bb1 0,r9,nostky24 /* skip checking if old sticky set */ + extu r8,r11,22<0> /* prepare to check bits that will be shifted */ + /* into the sticky */ + ff1 r8,r8 /* see if there are any 1''s */ + bb1 5,r8,nostky24 /* do not set sticky if no ones found */ + set r9,r9,1<0> /* set sticky bit */ +nostky24: + bb0 22,r11,nornd24 /* do not set new round bit */ + set r9,r9,1<1> /* set new round bit */ +nornd24: + set r9,r9,1<2> /* set new guard bit,this is hidden bit */ + br.n round /* round mantissa */ + or r11,r0,r0 /* clear r11, all of mantissa shifted off */ + +s25: + cmp r6,r7,25 /* check to see if # of shifts is 25 */ + bb1 gt,r6,s26 /* branch to execute for shifts => 26 */ + bb1 0,r9,nostky25 /* skip checking if old sticky set */ + extu r8,r11,23<0> /* prepare to check bits that will be shifted */ + /* into the sticky */ + ff1 r8,r8 /* see if there are any 1''s */ + bb1 5,r8,nostky25 /* do not set sticky if no ones found */ + set r9,r9,1<0> /* set sticky bit */ +nostky25: + set r9,r9,1<1> /* set new round bit,this is hidden bit */ + clr r9,r9,1<2> /* clear guard bit since nothing shifted in */ + br.n round /* round and assemble result */ + or r11,r0,r0 /* clear r11, all of mantissa shifted off */ + +s26: + set r9,r9,1<0> /* set sticky bit,this contains hidden bit */ + clr r9,r9,2<1> /* clear guard and round bits since nothing */ + /* shifted in */ + br.n round /* round and assemble result */ + or r11,r0,r0 /* clear mantissa */ + +Udouble: + mak r5,r10,21<0> /* extract upper bits of mantissa */ + bb0.n 25,r10,nounroundd /* do not unround if addone bit clear */ + extu r6,r12,12<20> /* extract signed exponenet from IMPCR */ +unroundd: + or r8,r0,1 + subu.co r11,r11,r8 /* subtract 1 from mantissa */ + subu.ci r5,r5,r0 /* subtract borrow from upper word */ + bb1 20,r5,nounroundd /* if hidden bit is set, then exponent does */ + /* not need to be decremented */ +decexpd: + sub r6,r6,1 /* decrement exponent 1 */ + set r5,r5,1<20> /* set the hidden bit */ + +nounroundd: + or r8,r0,lo16(0x00000c01) /* load r8 with -1023 in decimal */ + /* for lowest 12 bits */ + sub r7,r8,r6 /* find difference between two exponents, */ + /* this amount is the shift amount */ + cmp r6,r7,3 /* check to see if r7 contains 3 or more */ + bb1 ge,r6,threedoub /* br to code that handles shifts of >=3 */ + cmp r6,r7,2 /* check to see if r7 contains 2 */ + bb1 eq,r6,twodoub /* br to code that handles shifts of 2 */ + +onedoub: + rot r9,r9,0<1> /* rotate roundoff register once, this places */ + /* guard in round and round in sticky */ + bb0 31,r9,nosticky1d/* do not or round and sticky if sticky is 0 */ + /* this lost bit will be cleared later */ + set r9,r9,1<0> /* or old round and old sticky into new sticky */ +nosticky1d: + bb0 0,r11,guardclr1d /* do not set new guard bit if old LSB = 0 */ + set r9,r9,1<2> /* set new guard bit */ +guardclr1d: + extu r11,r11,31<1> /* shift lower mantissa over 1 */ + mak r6,r5,1<31> /* shift off low bit of high mantissa */ + or r11,r6,r11 /* load high bit onto lower mantissa */ + extu r5,r5,20<1> /* shift right once upper 20 bits of mantissa */ + br.n round /* round mantissa and assemble result */ + mak r9,r9,3<0> /* clear bits lost during rotation */ + +twodoub: + rot r9,r9,0<2> /* rotate roundoff register twice, this places */ + /* old guard into sticky */ + bb0 30,r9,nosticky2d /* do not or old guard and old sticky if */ + /* old sticky is 0 */ + br.n noround2d /* skip or of old guard and old round if old */ + /* sticky set */ + set r9,r9,1<0> /* or old guard and old sticky into new sticky */ +nosticky2d: + bb0 31,r9,noround2d /* do not or old guard and old round if */ + /* old round is 0 */ + set r9,r9,1<0> /* or old guard and old round into new sticky */ +noround2d: + bb0 0,r11,roundclr2d /* do not set round bit if old LSB = 0 */ + set r9,r9,1<1> /* set new round bit */ +roundclr2d: + bb0 1,r11,guardclr2d /* do not set guard bit if old LSB + 1 = 0 */ + set r9,r9,1<2> /* set new guard bit */ +guardclr2d: + extu r11,r11,30<2> /* shift lower mantissa over 2 */ + mak r6,r5,2<30> /* shift off low bits of high mantissa */ + or r11,r6,r11 /* load high bit onto lower mantissa */ + extu r5,r5,19<2> /* shift right twice upper 19 bits of mantissa */ + br.n round /* round mantissa and assemble result */ + mak r9,r9,3<0> /* clear bits lost during rotation */ + +threedoub: + bb1 0,r9,noguard3d /* checky sticky initially */ + /* sticky is set, forget most of rest of oring */ +nosticky3d: + bb0 1,r9,noround3d /* check old round, do not set sticky if */ + /* old round is clear, set otherwise */ + br.n noguard3d /* sticky is set, forget most of rest of oring */ + set r9,r9,1<0> /* set sticky if old round is set */ +noround3d: + bb0 2,r9,noguard3d /* check old guard, do not set sticky if 0 */ + clr r9,r9,2<1> /* clear the original guard and round for when */ + /* you get to round section */ + set r9,r9,1<0> /* set sticky if old guard is set */ +noguard3d: + cmp r6,r7,32 /* do I need to work with a 1 or 2 word mant. */ + /* when forming sticky, round and guard */ + bb1 gt,r6,d33 /* jump to code that handles 2 word mantissas */ + sub r6,r7,2 /* get number of bits to check for sticky */ + mak r6,r6,5<5> /* shift width into width field */ + mak r8,r11,r6 /* mask off shifted bits -2 */ + ff1 r8,r8 /* see if r8 has any ones */ + bb1 5,r8,nostky32 /* do not set sticky if no ones found */ + set r9,r9,1<0> /* set sticky bit */ +nostky32: + or r8,r0,34 /* start code to get new mantissa plus two */ + /* extra bits for new round and new guard bits, */ + /* the upper word bits will be shifted after */ + /* the round and guard bits are handled */ + subu r8,r8,r7 + mak r8,r8,5<5> /* shift field width into second five bits */ + extu r6,r6,5<5> /* shift previous shifted -2 into offset field */ + or r6,r6,r8 /* complete bit field */ + extu r11,r11,r6 /* partially form new low mantissa with 2 more */ + /* bits */ + bb0 0,r11,nornd32d /* do not set new round bit */ + set r9,r9,1<1> /* set new round bit */ +nornd32d: + bb0 1,r11,nogrd32d /* do not set new guard bit */ + set r9,r9,1<2> /* set new guard bit */ +nogrd32d: + extu r11,r11,30<2> /* shift off remaining two bits */ + mak r6,r7,5<5> /* shift field width into second 5 bits, if the */ + /* width is 32, then these bits will be 0 */ + or r8,r0,32 /* load word length into r8 */ + sub r8,r8,r7 /* form offset for high bits moved to low word */ + or r6,r6,r8 /* form complete bit field */ + mak r6,r5,r6 /* get shifted bits of high word */ + or r11,r6,r11 /* form new low word of mantissa */ + bcnd ne0,r8,regular33 /* do not adjust for special case of r8 */ + br.n round /* containing zeros, which would cause */ + or r5,r0,r0 /* all of the bits to be extracted under */ + /* the regular method */ +regular33: + mak r6,r7,5<0> /* place lower 5 bits of shift into r6 */ + mak r8,r8,5<5> /* shift r8 into width field */ + or r6,r6,r8 /* form field for shifting of upper bits */ + br.n round /* round and assemble result */ + extu r5,r5,r6 /* form new high word mantissa */ + +d33: + cmp r6,r7,33 /* is the number of bits to be shifted is 33? */ + bb1 gt,r6,d34 /* check to see if # of bits is 34 */ + bb1 0,r9,nostky33 /* skip checking if old sticky set */ + mak r6,r11,31<0> /* check bits that will be shifted into sticky */ + ff1 r8,r8 /* check for ones */ + bb1 5,r8,nostky33 /* do not set sticky if there are no ones */ + set r9,r9,1<0> /* set new sticky bit */ +nostky33: + bb0 31,r11,nornd33 /* do not set round if bit is not a 1 */ + set r9,r9,1<1> /* set new round bit */ +nornd33: + bb0 0,r5,nogrd33 /* do not set guard bit if bit is not a 1 */ + set r9,r9,1<2> /* set new guard bit */ +nogrd33: + extu r11,r5,31<1> /* shift high bits into low word */ + br.n round /* round and assemble result */ + or r5,r0,r0 /* clear high word */ + +d34: + cmp r6,r7,34 /* is the number of bits to be shifted 34? */ + bb1 gt,r6,d35 /* check to see if # of bits is >= 35 */ + bb1 0,r9,nostky34 /* skip checking if old sticky set */ + ff1 r8,r11 /* check bits that will be shifted into sticky */ + bb1 5,r8,nostky34 /* do not set sticky if there are no ones */ + set r9,r9,1<0> /* set new sticky bit */ +nostky34: + bb0 0,r5,nornd34 /* do not set round if bit is not a 1 */ + set r9,r9,1<1> /* set new round bit */ +nornd34: + bb0 1,r5,nogrd34 /* do not set guard bit if bit is not a 1 */ + set r9,r9,1<2> /* set new guard bit */ +nogrd34: + extu r11,r5,30<2> /* shift high bits into low word */ + br.n round /* round and assemble result */ + or r5,r0,r0 /* clear high word */ + +d35: + cmp r6,r7,52 /* see if # of shifts is 35 <= X <= 52 */ + bb1 gt,r6,d53 /* check to see if # of shifts is 52 */ + bb1.n 0,r9,nostky35 /* skip checking if old sticky set */ + sub r7,r7,34 /* subtract 32 from # of shifts so that opera- */ + /* tions can be done on the upper word, and */ + /* then subtract two more checking guard and */ + /* sticky bits */ + ff1 r8,r11 /* see if lower word has a bit for sticky */ + bb1 5,r8,stkycheck35 /* see if upper word has any sticky bits */ + br.n nostky35 /* quit checking for sticky */ + set r9,r9,1<0> /* set sticky bit */ +stkycheck35: + mak r6,r7,5<5> /* place width into width field */ + mak r8,r5,r6 /* mask off shifted bits - 2 */ + ff1 r8,r8 /* see if r8 has any ones */ + bb1 5,r8,nostky35 /* do not set sticky if no ones found */ + set r9,r9,1<0> /* set sticky bit */ +nostky35: + or r8,r0,32 /* look at what does not get shifted off plus */ + /* round and sticky, remember that the r7 value */ + /* was adjusted so that it did not include */ + /* new round or new sticky in shifted off bits */ + subu r8,r8,r7 /* complement width */ + mak r8,r8,5<5> /* shift width into width field */ + or r8,r7,r8 /* add offset field */ + extu r11,r5,r8 /* extract upper bits into low word */ + bb0 0,r11,nornd35 /* do not set new round bit */ + set r9,r9,1<1> /* set new round bit */ +nornd35: + bb0 1,r11,nogrd35 /* do not set new guard bit */ + set r9,r9,1<2> /* set new guard bit */ +nogrd35: + extu r11,r11,30<2> /* shift off remaining guard and round bits */ + br.n round /* round and assemble result */ + or r5,r0,r0 /* clear high word */ + +d53: + cmp r6,r7,53 /* check to see if # of shifts is 53 */ + bb1 gt,r6,d54 /* branch to see if shifts = 54 */ + bb1 0,r9,nostky53 /* skip checking if old sticky set */ + ff1 r8,r11 /* see if lower word has a bit for sticky */ + bb1 5,r8,stkycheck53 /* see if upper word has any sticky bits */ + br.n nostky53 /* quit checking for sticky */ + set r9,r9,1<0> /* set sticky bit */ +stkycheck53: + mak r6,r5,19<0> /* check bits that are shifted into sticky */ + ff1 r8,r6 /* see if r6 has any ones */ + bb1 5,r8,nostky53 /* do not set sticky if no ones found */ + set r9,r9,1<0> /* set sticky bit */ +nostky53: + bb0 19,r5,nornd53 /* do not set new round bit */ + set r9,r9,1<1> /* set new round bit */ +nornd53: + set r9,r9,1<2> /* set new guard bit,this is hidden bit */ + or r5,r0,r0 /* clear high word */ + br.n round /* round and assemble result */ + or r11,r0,r0 /* clear low word */ + +d54: + cmp r6,r7,54 /* check to see if # of shifts is 54 */ + bb1 gt,r6,d55 /* branch to execute for shifts =>55 */ + bb1 0,r9,nostky54 /* skip checking if old sticky set */ + ff1 r8,r11 /* see if lower word has a bit for sticky */ + bb1 5,r8,stkycheck54 /* see if upper word has any sticky bits */ + br.n nostky54 /* quit checking for sticky */ + set r9,r9,1<0> /* set sticky bit */ +stkycheck54: + mak r6,r5,20<0> /* check bits that are shifted into sticky */ + ff1 r8,r6 /* see if r6 has any ones */ + bb1 5,r8,nostky54 /* do not set sticky if no ones found */ + set r9,r9,1<0> /* set sticky bit */ +nostky54: + set r9,r9,1<1> /* set new round bit,this is hidden bit */ + clr r9,r9,1<2> /* clear guard bit since nothing shifted in */ + or r5,r0,r0 /* clear high word */ + br.n round /* round and assemble result */ + or r11,r0,r0 /* clear low word */ + +d55: + set r9,r9,1<0> /* set new sticky bit,this contains hidden bit */ + clr r9,r9,2<1> /* clear guard and round bits since nothing */ + /* shifted in */ + or r5,r0,r0 /* clear high word */ + or r11,r0,r0 /* clear low word */ + + +/* The first item that the rounding code does is see if either guard, round, */ +/* or sticky is set. If all are clear, then there is no denormalization loss */ +/* and no need to round, then branch to assemble answer. */ +/* For rounding, a branch table is set up. The left two most bits are the */ +/* rounding mode. The third bit is either the LSB of the mantissa or the */ +/* sign bit, depending on the rounding mode. The three LSB''s are the guard, */ +/* round and sticky bits. */ + +round: + ff1 r8,r9 /* see if there is denormalization loss */ + bb1 5,r8,assemble /* no denormalization loss or inexactness */ + extu r6,r10,2<modelo> /* extract rounding mode */ + bb1.n modehi,r10,signext /* use sign bit instead of LSB */ + mak r6,r6,2<4> /* shift over rounding mode */ + extu r7,r11,1<0> /* extract LSB */ + br.n grs /* skip sign extraction */ + mak r7,r7,1<3> /* shift over LSB */ +signext: + extu r7,r10,1<31> /* extract sign bit */ + mak r7,r7,1<3> /* shift sign bit over */ +grs: + or r6,r6,r7 + or r6,r6,r9 /* or in guard, round, and sticky */ + or.u r1,r0,hi16(roundtable) /* form address of branch table */ + or r1,r1,lo16(roundtable) + lda r6,r1[r6] /* scale offset into branch table */ + jmp.n r6 /* jump to branch table */ + set r9,r9,1<3> /* set inexact flag in r9 */ + +roundtable: + br noaddone + br noaddone + br noaddone + br noaddone + br noaddone + br addone + br addone + br addone + br noaddone + br noaddone + br noaddone + br noaddone + br addone + br addone + br addone + br addone + br noaddone + br noaddone + br noaddone + br noaddone + br noaddone + br noaddone + br noaddone + br noaddone + br noaddone + br noaddone + br noaddone + br noaddone + br noaddone + br noaddone + br noaddone + br noaddone + br noaddone + br noaddone + br noaddone + br noaddone + br noaddone + br noaddone + br noaddone + br noaddone + br noaddone + br addone + br addone + br addone + br addone + br addone + br addone + br addone + br noaddone + br addone + br addone + br addone + br addone + br addone + br addone + br addone + br noaddone + br noaddone + br noaddone + br noaddone + br noaddone + br noaddone + br noaddone + br noaddone + +/* Round by adding a one to the LSB of the mantissa. */ +addone: + or r6,r0,1 /* load a 1 into r6 so that add.co can be used */ + add.co r11,r11,r6 /* add a one to the lower word of result */ + bb0.n destsize,r12,noaddone /* single result,forget carry */ + set r9,r9,1<4> /* indicate that a 1 has been added */ + add.ci r5,r5,r0 /* propagate carry into high word */ + + +/* Branch to inexact user handler if there is one. */ + +noaddone: + set r2,r2,1<inexact> + set r2,r2,1<underflow> +#ifdef HANDLER + bb1 efinx,r12,modformdef /* branch to modify form for user */ + /* handler */ +#endif + + +/* Assemble the result of the denormalization routine for writeback to the */ +/* destination register. The exponent of a denormalized number is zero, */ +/* so simply assemble the sign and the new mantissa. */ + +assemble: + bb1 destsize,r12,doubassem /* assemble double result */ + bb0 sign,r10,exassems /* exit assemble if sign is zero */ + set r11,r11,1<sign> /* make result negative */ +exassems: + br Ureturn + +doubassem: + bb0.n sign,r10,signclr /* do not set sign in r10 */ + or r10,r5,r0 /* load high word from r5 into r10 */ + set r10,r10,1<sign> /* high word with sign loaded */ +signclr: + br Ureturn + + +/* modfordef modifies the result of denormalization to the input format of */ +/* the inexact user handler. This input format is the same format that */ +/* MANTHI, MANTLO, and IMPCR were initially loaded with. */ + +#ifdef HANDLER +modformdef: + clr r12,r12,12<20> /* clear result exponent,IMPCR complete */ + clr r10,r10,4<25> /* clear old guard,round,sticky,and addone */ + mak r5,r9,3<26> /* make grs field */ + bb0.n 4,r9,newaddone /* do not set new addone in MANTHI */ + or r10,r5,r10 /* or in new grs field */ + set r10,r10,1<25> /* set new addone */ +newaddone: + bb1.n destsize,r12,moddefd /* branch to handle double precision */ + clr r10,r10,21<0> /* clear upper bits of old mantissa */ +moddefs: + extu r5,r11,20<3> /* extract upper bits */ + or r10,r5,r10 /* MANTHI complete */ + bsr.n _handler /* execute user handler for inexact */ + rot r11,r11,0<3> /* MANTLO complete */ + br Ureturn +moddefd: + bsr.n _handler /* execute user handler for inexact */ + or r10,r5,r10 /* MANTHI complete,r5 should be set to OR */ +#endif + +/* Return to fpui. */ + +Ureturn: + ld r1,r31,0 /* load return address */ + jmp r1 + +/* + * FPoverflow + */ +/* If the overflow user handler bit is not set, then the inexact bit in the */ +/* FPSR is set, and the inexact user handler bit is checked. If it is set, */ +/* then the inexact user handler is executed, else the default routine for */ +/* overflow is executed. */ + +ASLOCAL(FPoverflow) + st r1,r31,0 /* save return address */ + set r2,r2,1<overflow> + set r2,r2,1<inexact> +#ifdef HANDLER + bb1 efovf,r12,hand /* go to user handler if bit set for overflow */ + bb0 efinx,r12,nohandler/* if userhandler for inexact not set,then */ + /* round result */ + br callhandler /* branch to user handler for inexact */ + +/* Before the overflow user handler is executed, the exponent is modified */ +/* by subtracting 192 for single precision and 1536 for double precision. */ + +hand: + bb1 10,r12,doubleprec /* double precision result */ +singleprec: + or.u r5,r0,0x0c00 /* load exponent adjust */ + br.n callhandler /* prepare to call user handler */ + subu r12,r12,r5 /* adjust single precision exponent */ +doubleprec: + or.u r5,r0,0x6000 /* load exponent adjust */ + subu r12,r12,r5 /* adjust double precision exponent */ +callhandler: + bsr _handler /* branch to common handler routine */ + br return +#endif + +/* Determine which rounding mode to use for the default procedure. */ + +nohandler: + bb1 modehi,r10,signed /* mode is either round toward pos. or neg. */ + bb0 modelo,r10,OFnearest /* rounding mode is round nearest */ + br OFzero /* rounding mode is round zero */ +signed: + bb0 modelo,r10,OFnegative /* rounding mode is round negative */ + br positive /* rounding mode is round positive */ + + +/* In the round toward nearest mode, positive values are rounded to */ +/* positive infinity and negative values are loaded toward negative infinity. */ +/* The value for single or double precision is loaded from a data table. */ + +OFnearest: + bb1.n destsize,r12,neardouble /* branch to neardouble of */ + /* double result */ + mask.u r5,r10,0x8000 /* mask off sign bit from MANTHI */ + or.u r11,r0,hi16(0x7f800000) /* load single infinity constant */ + or r11,r11,lo16(0x7f800000) + br.n return /* return with result */ + or r11,r5,r11 /* adjust sign */ +neardouble: + or r11,r0,r0 /* load lower word of infinity */ + or.u r10,r0,hi16(0x7ff00000) /* load upper word of infinity */ + or r10,r10,lo16(0x7ff00000) + br.n return /* return with result */ + or r10,r5,r10 /* adjust sign */ + + +/* In the round toward zero mode, positive values are rounded to the largest */ +/* postive finite number and negative values are rounded toward the largest */ +/* negative finite number. */ +/* The value for single or double precision is loaded from a data table. */ + +OFzero: + bb1.n destsize,r12,zerodouble /* branch to zerodouble of */ + /* double result */ + mask.u r5,r10,0x8000 /* mask off sign bit from MANTHI */ + or.u r11,r0,hi16(0x7f7fffff) /* load single finite number constant */ + or r11,r11,lo16(0x7f7fffff) + br.n return /* return with result */ + or r11,r5,r11 /* adjust sign */ +zerodouble: + set r11,r0,0<0> /* load lower word of finite number */ + or.u r10,r0,hi16(0x7fefffff) /* load upper word of finite number */ + or r10,r10,lo16(0x7fefffff) + br.n return /* return with result */ + or r10,r5,r10 /* adjust sign */ + + +/* In the round toward positve mode, positive values are rounded to */ +/* postive infinity and negative values are loaded toward the largest */ +/* negative finite number. */ +/* The value for single or double precision is loaded from a data table. */ + +positive: + bb1 destsize,r12,posdouble /* branch to section for double result */ +possingle: + bb1 sign,r10,possingleneg /* branch to section for negatives */ +possinglepos: + or.u r11,r0,hi16(0x7f800000) /* load single infinity constant */ + br.n return /* return with result */ + or r11,r11,lo16(0x7f800000) +possingleneg: + or.u r11,r0,hi16(0x7f7fffff) /* load single finite number constant */ + or r11,r11,lo16(0x7f7fffff) + br.n return /* return with result */ + set r11,r11,1<sign> /* set sign for negative */ +posdouble: + bb1 sign,r10,posdoubleneg /* branch to negative double results */ +posdoublepos: + or r11,r0,r0 /* load lower word of double infinity */ + or.u r10,r0,hi16(0x7ff00000) /* load upper word of infinity */ + br.n return /* return with result */ + or r10,r10,lo16(0x7ff00000) +posdoubleneg: + set r11,r0,0<0> /* load lower word of finite number */ + or.u r10,r0,hi16(0x7fefffff) /* load upper word of finite number */ + or r10,r10,lo16(0x7fefffff) + br.n return /* return with result */ + set r10,r10,1<sign> /* set sign for negative */ + + +/* In the round toward negative mode, positive values are rounded to the largest */ +/* postive finite number and negative values are rounded to negative infinity. */ +/* The value for single or double precision is loaded from a data table. */ + +OFnegative: + bb1 destsize,r12,negdouble /* branch to section for double result */ +negsingle: + bb1 sign,r10,negsingleneg /* branch to section for negatives */ +negsinglepos: + or.u r11,r0,hi16(0x7f7fffff) /* load single finite number constant */ + br.n return /* return with result */ + or r11,r11,lo16(0x7f7fffff) +negsingleneg: + or.u r11,r0,hi16(0x7f800000) /* load single infinity constant */ + or r11,r11,lo16(0x7f800000) + br.n return /* return with result */ + set r11,r11,1<sign> /* set sign for negative */ +negdouble: + bb1 sign,r10,negdoubleneg /* branch to negative double results */ +negdoublepos: + set r11,r0,0<0> /* load lower word of finite number */ + or.u r10,r0,hi16(0x7fefffff) /* load upper word of finite number */ + br.n return /* return with result */ + or r10,r10,lo16(0x7fefffff) +negdoubleneg: + or r11,r0,r0 /* load lower word of double infinity */ + or.u r10,r0,hi16(0x7ff00000) /* load upper word of infinity */ + or r10,r10,lo16(0x7ff00000) + set r10,r10,1<sign> /* set sign for negative */ + +return: + ld r1,r31,0 /* ld return address */ + jmp r1 + + data + + +/* If either S1 or S2 is a signalling NaN, then set the invalid operation */ +/* bit of the FPSR. If the invalid operation user handler flag is set and */ +/* then NaN is signalling, then branch to the handler routine to go to the */ +/* user handler. */ +/* If S1 is the only NaN or one of two NaN''s, then write */ +/* a quiet S1 to the result. A signalling NaN must be made quiet before */ +/* it can be written, but a signalling S2 is not modified in this routine */ +/* if S1 is a NaN. */ + text +ASLOCAL(NaN) + bb0.n s1nan,r12,S2sigcheck /* S1 is not a NaN */ + st r1,r31,0 /* save return address */ + bb1 sigbit,r5,S2sigcheck /* S1 is not a signaling NaN */ + set r2,r2,1<oper> +#ifdef HANDLER + bb0 oper,r3,S1nohandler /* branch if no user handler */ + bsr _handler /* branch to handler */ + br FPnan_return +ASLOCAL(S1nohandler) +#endif + br.n S1write /* FPSR bit already set, S1 is made quiet, */ + /* and since we always write S1 if it is a */ + /* NaN, write S1 and skip rest of routine */ + set r5,r5,1<sigbit> /* make S1 a quiet NaN */ + +ASLOCAL(S2sigcheck) + bb0 s2nan,r12,S1write /* S2 is not a NaN */ + bb1 sigbit,r7,S1write /* S2 is not a signaling NaN */ + set r2,r2,1<oper> +#ifdef HANDLER + bb0 oper,r3,S2nohandler /* branch if no user handler */ + bsr _handler /* branch to handler */ + br FPnan_return +#endif + +ASLOCAL(S2nohandler) + set r7,r7,1<sigbit> /* make S2 a quiet NaN */ + + +/* Write a single or double precision quiet NaN unless the opeation is FCMP. */ +/* If the operation is FCMP, then set the not comparable bit in the result. */ + +ASLOCAL(S1write) + bb0 s1nan,r12,S2write /* do not write S1 if it is not a NaN */ + extu r10,r9,5<11> /* extract opcode */ + cmp r11,r10,FCMPop /* compare to FCMP */ + bb1 ne,r11,S1noFCMP /* operation is not FCMP */ + set r6,r0,1<nc> /* set the not comparable bit */ + br.n FPnan_return + set r6,r6,1<ne> /* set the not equal bit */ +ASLOCAL(S1noFCMP) + bb1.n dsize,r9,wrdoubS1 /* double destination */ + set r5,r5,11<20> /* set all exponent bits to 1 */ +/* The single result will be formed the same way whether S1 is a single or double */ +ASLOCAL(wrsingS1) + mak r10,r5,28<3> /* wipe out extra exponent bits */ + extu r11,r6,3<29> /* get lower three bits of mantissa */ + or r10,r10,r11 /* combine all of result except sign */ + clr r6,r5,31<0> /* clear all but sign */ + br.n FPnan_return + or r6,r6,r10 /* form result */ + +ASLOCAL(wrdoubS1) + set r6,r6,29<0> /* set extra bits of lower word */ + br FPnan_return /* no modification necessary for writing */ + /* double to double, so return */ + +ASLOCAL(S2write) + extu r10,r9,5<11> /* extract opcode */ + cmp r11,r10,FCMPop /* compare to FCMP */ + bb1.n ne,r11,S2noFCMP /* operation is not FCMP */ + set r7,r7,11<20> /* set all exponent bits to 1 */ + set r6,r0,1<nc> /* set the not comparable bit */ + br.n FPnan_return + set r6,r6,1<ne> /* set the not equal bit */ +ASLOCAL(S2noFCMP) + bb1.n dsize,r9,wrdoubS2 /* double destination */ + set r5,r5,11<20> /* set all exponent bits to 1 */ +/* The single result will be formed the same way whether S1 is a single or double */ +ASLOCAL(wrsingS2) + mak r10,r7,28<3> /* wipe out extra exponent bits */ + extu r11,r8,3<29> /* get lower three bits of mantissa */ + or r10,r10,r11 /* combine all of result except sign */ + clr r6,r7,31<0> /* clear all but sign */ + br.n FPnan_return + or r6,r6,r10 /* form result */ + +ASLOCAL(wrdoubS2) + set r6,r8,29<0> /* set extra bits of lower word */ + +/* Return from this subroutine with the result. */ + +ASLOCAL(FPnan_return) + /* no modification necessary for writing */ + /* double to double, so return */ + ld r1,r31, 0 /* retrieve return address */ + jmp r1 + + data + +/* + * infinity + */ + +/* Extract the opcode, compare to a constant, and branch to the code */ +/* for the instruction. */ + +ASLOCAL(infinity) + extu r10,r9,5<11> /* extract opcode */ + cmp r11,r10,FADDop /* compare to FADD */ + bb1.n eq,r11,FADD /* operation is FADD */ + st r1,r31,0 /* save return address */ + cmp r11,r10,FSUBop /* compare to FSUB */ + bb1 eq,r11,FSUB /* operation is FSUB */ + cmp r11,r10,FCMPop /* compare to FCMP */ + bb1 eq,r11,FCMP /* operation is FCMP */ + cmp r11,r10,FMULop /* compare to FMUL */ + bb1 eq,r11,FMUL /* operation is FMUL */ + cmp r11,r10,FDIVop /* compare to FDIV */ + bb1 eq,r11,FDIV /* operation is FDIV */ +#if 0 + cmp r11,r10,FSQRTop /* compare to FSQRT */ + bb1 eq,r11,FSQRT /* operation is FSQRT */ +#endif + cmp r11,r10,INTop /* compare to INT */ + bb1 eq,r11,FP_inf_overflw /* operation is INT */ + cmp r11,r10,NINTop /* compare to NINT */ + bb1 eq,r11,FP_inf_overflw /* operation is NINT */ + cmp r11,r10,TRNCop /* compare to TRNC */ + bb1 eq,r11,FP_inf_overflw /* operation is TRNC */ + + +/* Adding infinities of opposite signs will cause an exception, */ +/* but all other operands will result in a correctly signed infinity. */ + +FADD: + bb0 s1inf,r12,addS2write /* branch if S1 not infinity */ + bb0 s2inf,r12,addS1write /* S2 is not inf., so branch to write S1 */ + bb1 sign,r5,addS1neg /* handle case of S1 negative */ +addS1pos: + bb1 sign,r7,excpt /* adding infinities of different */ + /* signs causes an exception */ + br poswrinf /* branch to write positive infinity */ +addS1neg: + bb0 sign,r7,excpt /* adding infinities of different */ + /* signs causes an exception */ + br negwrinf /* branch to write negative infinity */ +addS1write: + bb0 sign,r5,poswrinf /* branch to write positive infinity */ + br negwrinf /* branch to write negative infinity */ +addS2write: + bb0 sign,r7,poswrinf /* branch to write positive infinity */ + br negwrinf /* branch to write negative infinity */ + + +/* Subtracting infinities of the same sign will cause an exception, */ +/* but all other operands will result in a correctly signed infinity. */ + +FSUB: + bb0 s1inf,r12,subS2write /* branch if S1 not infinity */ + bb0 s2inf,r12,subS1write /* S2 is not inf., so branch to write S1 */ + bb1 sign,r5,subS1neg /* handle case of S1 negative */ +subS1pos: + bb0 sign,r7,excpt /* subtracting infinities of the same */ + /* sign causes an exception */ + br poswrinf /* branch to write positive infinity */ +subS1neg: + bb1 sign,r7,excpt /* subtracting infinities of the same */ + /* sign causes an exception */ + br negwrinf /* branch to write negative infinity */ +subS1write: + bb0 sign,r5,poswrinf /* branch to write positive infinity */ + br negwrinf /* branch to write negative infinity */ +subS2write: + bb1 sign,r7,poswrinf /* branch to write positive infinity */ + br negwrinf /* branch to write negative infinity */ + + +/* Compare the operands, at least one of which is infinity, and set the */ +/* correct bits in the destination register. */ + +FCMP: + bb0.n s1inf,r12,FCMPS1f /* branch for finite S1 */ + set r4,r0,1<cp> /* since neither S1 or S2 is a NaN, */ + /* set cp */ +FCMPS1i: + bb1 sign,r5,FCMPS1ni /* branch to negative S1i */ +FCMPS1pi: + bb0 s2inf,r12,FCMPS1piS2f /* branch to finite S2 with S1pi */ +FCMPS1piS2i: + bb1 sign,r7,FCMPS1piS2ni /* branch to negative S2i with S1pi */ +FCMPS1piS2pi: + set r4,r4,1<eq> /* set eq bit */ + set r4,r4,1<le> /* set le bit */ + set r4,r4,1<ge> /* set ge bit */ + set r4,r4,1<ib> /* set ib bit */ + br.n move + set r4,r4,1<ob> /* set ob bit */ +FCMPS1piS2ni: + set r4,r4,1<ne> /* set ne bit */ + set r4,r4,1<gt> /* set gt bit */ + br.n move + set r4,r4,1<ge> /* set ge bit */ +FCMPS1piS2f: + set r4,r4,1<ne> /* set ne bit */ + set r4,r4,1<gt> /* set gt bit */ + bsr.n _ASM_LABEL(zero) /* see if any of the operands are zero */ + set r4,r4,1<ge> /* set ge bit */ + bb0 s2zero,r12,FCMPS1piS2nz /* check for negative if s2 not zero */ + set r4,r4,1<ou> /* set ou bit */ + br.n move + set r4,r4,1<ob> /* set ob bit */ +FCMPS1piS2nz: + bb1 sign,r7,move /* return if s2 is negative */ +FCMPS1piS2pf: + set r4,r4,1<ou> /* set ou bit */ + br.n move + set r4,r4,1<ob> /* set ob bit */ +FCMPS1ni: + bb0 s2inf,r12,FCMPS1niS2f /* branch to finite S2 with S1ni */ +FCMPS1niS2i: + bb1 sign,r7,FCMPS1niS2ni /* branch to negative S2i with S1ni */ +FCMPS1niS2pi: + set r4,r4,1<ne> /* set eq bit */ + set r4,r4,1<le> /* set le bit */ + set r4,r4,1<lt> /* set lt bit */ + set r4,r4,1<ou> /* set ou bit */ + br.n move + set r4,r4,1<ob> /* set ob bit */ +FCMPS1niS2ni: + set r4,r4,1<eq> /* set eq bit */ + set r4,r4,1<le> /* set le bit */ + br.n move + set r4,r4,1<ge> /* set ge bit */ +FCMPS1niS2f: + set r4,r4,1<ne> /* set eq bit */ + set r4,r4,1<le> /* set le bit */ + bsr.n _ASM_LABEL(zero) /* see if any of the operands are zero */ + set r4,r4,1<lt> /* set lt bit */ + bb0 s2zero,r12,FCMPS1niS2nz /* branch if s2 is not zero */ + set r4,r4,1<ou> /* set ou bit */ + br.n move + set r4,r4,1<ob> /* set ob bit */ +FCMPS1niS2nz: + bb1 sign,r7,move /* return if s2 is negative */ + set r4,r4,1<ou> /* set ou bit */ + br.n move + set r4,r4,1<ob> /* set ob bit */ +FCMPS1f: + bb1 sign,r5,FCMPS1nf /* branch to negative S1f */ +FCMPS1pf: + bb1.n sign,r7,FCMPS1pfS2ni /* branch to negative S2i with S1pf */ + set r4,r4,1<ne> /* set ne bit */ +FCMPS1pfS2pi: + set r4,r4,1<le> /* set le bit */ + set r4,r4,1<lt> /* set lt bit */ + bsr.n _ASM_LABEL(zero) + set r4,r4,1<ib> /* set ib bit */ + bb0 s1zero,r12,FCMPS1pfS2pinozero +FCMPS1pfS2pizero: + br.n move + set r4,r4,1<ob> /* set ob bit */ +FCMPS1pfS2pinozero: + br.n move + set r4,r4,1<in> /* set in bit */ +FCMPS1pfS2ni: + set r4,r4,1<gt> /* set gt bit */ + br.n move + set r4,r4,1<ge> /* set ge bit */ +FCMPS1nf: + bb1.n sign,r7,FCMPS1nfS2ni /* branch to negative S2i with S1nf */ + set r4,r4,1<ne> /* set ne bit */ + set r4,r4,1<le> /* set gt bit */ + set r4,r4,1<lt> /* set ge bit */ + bsr.n _ASM_LABEL(zero) /* see which of the operands are zero */ + set r4,r4,1<ob> /* set ob bit */ + bb0 s1zero,r12,FCMPS1nfS2pinozero /* no ls and lo */ +FCMPS1nfS2pizero: + br.n move + set r4,r4,1<ib> /* set ib bit */ +FCMPS1nfS2pinozero: + br.n move + set r4,r4,1<ou> /* set ou bit */ +FCMPS1nfS2ni: + set r4,r4,1<gt> /* set gt bit */ + set r4,r4,1<ge> /* set ge bit */ + +move: + br.n inf_return + or r6,r0,r4 /* transfer answer to r6 */ + + +/* Multiplying infinity and zero causes an exception, but all other */ +/* operations produce a correctly signed infinity. */ + +FMUL: + bsr _ASM_LABEL(zero) /* see if any of the operands are zero */ + bb1 s1zero,r12,excpt /* infinity X 0 causes an exception */ + bb1 s2zero,r12,excpt /* infinity X 0 causes an exception */ + bb1 sign,r5,FMULS1neg /* handle negative cases of S1 */ + bb0 sign,r7,poswrinf /* + X + = + */ + br negwrinf /* + X - = - */ +FMULS1neg: + bb1 sign,r7,poswrinf /* - X - = + */ + br negwrinf /* - X + = - */ + + +/* Dividing infinity by infinity causes an exception, but dividing */ +/* infinity by a finite yields a correctly signed infinity, and */ +/* dividing a finite by an infinity produces a correctly signed zero. */ + +FDIV: + bb1 s1inf,r12,FDIVS1inf /* handle case of S1 being infinity */ + bb1 sign,r5,FDIVS1nf /* handle cases of S1 being neg. non-inf. */ + bb1 sign,r7,FDIVS1pfS2mi /* handle case of negative S2 */ +FDIVS1pfS2pi: + br poswrzero /* +f / +inf = +0 */ +FDIVS1pfS2mi: + br negwrzero /* +f / -inf = -0 */ +FDIVS1nf: + bb1 sign,r7,FDIVS1nfS2mi /* handle case of negative S2 */ +FDIVS1nfS2pi: + br negwrzero /* -f / +inf = -0 */ +FDIVS1nfS2mi: + br poswrzero /* -f / -inf = +0 */ +FDIVS1inf: + bb1 s2inf,r12,excpt /* inf / inf = exception */ + bb1 sign,r5,FDIVS1mi /* handle cases of S1 being neg. inf. */ + bb1 sign,r7,FDIVS1piS2nf /* handle case of negative S2 */ +FDIVS1piS2pf: + br poswrinf /* +inf / +f = +inf */ +FDIVS1piS2nf: + br negwrinf /* +inf / -f = -inf */ +FDIVS1mi: + bb1 sign,r7,FDIVS1miS2nf /* handle case of negative S2 */ +FDIVS1miS2pf: + br negwrinf /* -inf / +f = -inf */ +FDIVS1miS2nf: + br poswrinf /* -inf / -f = +inf */ + + +/* The square root of positive infinity is positive infinity, */ +/* but the square root of negative infinity is a NaN */ + +#if 0 +FSQRT: + bb0 sign,r7,poswrinf /* write sqrt(inf) = inf */ + br excpt /* write sqrt(-inf) = NaN */ +#endif + +excpt: + set r2,r2,1<oper> +#ifdef HANDLER + bb0 oper,r3,nohandler /* branch if no user handler */ + bsr _handler /* branch to interface with user handler */ + br inf_return +nohandler: +#endif + set r5,r0,0<0> /* write NaN into r5 */ + br.n inf_return + set r6,r0,0<0> /* write NaN into r6, writing NaN''s into */ + /* both of these registers is quicker than */ + /* checking for single or double precision */ + + +/* Write positive infinity of the correct precision */ + +poswrinf: + bb1 dsize,r9,poswrinfd /* branch to write double precision inf. */ + br.n inf_return + or.u r6,r0,0x7f80 /* load r6 with single precision pos inf. */ +poswrinfd: + or.u r5,r0,0x7ff0 /* load double precision pos inf. */ + br.n inf_return + or r6,r0,r0 + + +/* Write negative infinity of the correct precision */ + +negwrinf: + bb1 dsize,r9,negwrinfd /* branch to write double precision inf. */ + br.n inf_return + or.u r6,r0,0xff80 /* load r6 with single precision pos inf. */ +negwrinfd: + or.u r5,r0,0xfff0 /* load double precision pos inf. */ + br.n inf_return + or r6,r0,r0 + + +/* Write a positive zero disregarding precision. */ + +poswrzero: + or r5,r0,r0 /* write to both high word and low word now */ + br.n inf_return /* it does not matter that both are written */ + or r6,r0,r0 + + +/* Write a negative zero of the correct precision. */ + +negwrzero: + or r6,r0,r0 /* clear low word */ + bb1 dsize,r9,negwrzerod /* branch to write double precision zero */ + br.n inf_return + set r6,r6,1<31> /* set sign bit */ +negwrzerod: + or r5,r0,r0 /* clear high word */ + br.n inf_return + set r5,r5,1<31> /* set sign bit */ + +FP_inf_overflw: + set r2,r2,1<oper> + set r2,r2,1<overflow> + set r2,r2,1<inexact> +#ifdef HANDLER + bb0 oper,r3,nohandlero /* do not go to user handler routine */ + bsr _handler /* go to user handler routine */ + br inf_return +#endif + +nohandlero: + bb0.n sign,r7,inf_return /* if positive then return */ + + set r6,r6,31<0> /* set result to largest positive integer */ + or.c r6,r0,r6 /* negate r6,giving largest negative int. */ + +inf_return: + ld r1,r31,0 /* load return address */ + jmp r1 + + data + +#define FADD denorm_FADD +#define FSUB denorm_FSUB +#define FCMP denorm_FCMP +#define FMUL denorm_FMUL +#define FDIV denorm_FDIV +#define NINT denorm_NINT +#define TRNC denorm_TRNC +#define return denorm_return + +/* + * denorm + */ + +/* Check to see if either S1 or S2 is a denormalized number. First */ +/* extract the exponent to see if it is zero, and then check to see if */ +/* the mantissa is not zero. If the number is denormalized, then set the */ +/* 1 or 0 bit 10 r12. */ + +ASLOCAL(denorm) + st r1,r31,0 /* save return address */ +dnmcheckS1: + extu r10,r5,11<20> /* extract exponent */ + bcnd ne0,r10,dnmsetS2 /* S1 is not a denorm, so S2 must be */ + bb1.n 9,r9,dnmcheckS1d /* S1 is double precision */ + mak r10,r5,20<3> /* mak field with only mantissa bits */ + /* into final result */ +dnmcheckS1s: + extu r11,r6,3<29> /* get three low bits of mantissa */ + or r10,r10,r11 /* assemble all of the mantissa bits */ + bcnd eq0,r10,dnmsetS2 /* S1 is not a denorm, so S2 must be */ + br dnmsetS1 /* S1 is a denorm */ + +dnmcheckS1d: + or r10,r6,r10 /* or all of mantissa bits */ + bcnd eq0,r10,dnmsetS2 /* S1 is not a denorm, so S2 must be */ +dnmsetS1: + set r12,r12,1<1> /* S1 is a denorm */ + +dnmcheckS2: + extu r10,r7,11<20> /* extract exponent */ + bcnd ne0,r10,S1form /* S2 is not a denorm */ + bb1.n 7,r9,dnmcheckS2d /* S2 is double precision */ + mak r10,r7,20<3> /* mak field with only mantissa bits */ +dnmcheckS2s: + extu r11,r8,3<29> /* get three low bits of mantissa */ + or r10,r10,r11 /* assemble all of the mantissa bits */ + bcnd eq0,r10,S1form /* S2 is not a denorm */ + br dnmsetS2 /* S1 is a denorm */ +dnmcheckS2d: + or r10,r8,r10 /* or all or mantissa bits */ + bcnd eq0,r10,S1form /* S2 is not a denorm */ +dnmsetS2: + set r12,r12,1<0> /* S2 is a denorm */ + + +/* Since the operations are going to be reperformed with modified denorms, */ +/* the operands which were initially single precision need to be modified */ +/* back to single precision. */ + +S1form: + bb1 9,r9,S2form /* S1 is double precision, so do not */ + /* modify S1 into single format */ + mak r11,r5,28<3> /* over final exponent and mantissa */ + /* eliminating extra 3 bits of exponent */ + extu r6,r6,3<29> /* get low 3 bits of mantissa */ + or r11,r6,r11 /* form complete mantissa and exponent */ + extu r10,r5,1<31> /* get the 31 bit */ + mak r10,r10,1<31> /* place 31 bit 10 correct position */ + or r6,r10,r11 /* or 31, exponent, and all of mantissa */ + +S2form: + bb1 7,r9,checkop /* S2 is double precision, so do not */ + /* modify S2 into single format */ + mak r11,r7,28<3> /* over final exponent and mantissa */ + /* eliminating extra 3 bits of exponent */ + extu r8,r8,3<29> /* get low 3 bits of mantissa */ + or r11,r8,r11 /* form complete mantissa and exponent */ + extu r10,r7,1<31> /* get the 31 bit */ + mak r10,r10,1<31> /* place 31 bit 10 correct position */ + or r8,r10,r11 /* or 31, exponent, and all of mantissa */ + + +/* Extract the opcode, compare to a constant, and branch to the code that */ +/* deals with that opcode. */ + +checkop: + extu r10,r9,5<11> /* extract opcode */ + cmp r11,r10,0x05 /* compare to FADD */ + bb1 2,r11,FADD /* operation is FADD */ + cmp r11,r10,0x06 /* compare to FSUB */ + bb1 2,r11,FSUB /* operation is FSUB */ + cmp r11,r10,0x07 /* compare to FCMP */ + bb1 2,r11,FCMP /* operation is FCMP */ + cmp r11,r10,0x00 /* compare to FMUL */ + bb1 2,r11,FMUL /* operation is FMUL */ + cmp r11,r10,0x0e /* compare to FDIV */ + bb1 2,r11,FDIV /* operation is FDIV */ +#if 0 + cmp r11,r10,0x0f /* compare to FSQRT */ + bb1 2,r11,FSQRT /* operation is FSQRT */ +#endif + cmp r11,r10,0x09 /* compare to INT */ + bb1 2,r11,INT /* operation is INT */ + cmp r11,r10,0x0a /* compare to NINT */ + bb1 2,r11,NINT /* operation is NINT */ + cmp r11,r10,0x0b /* compare to TRNC */ + bb1 2,r11,TRNC /* operation is TRNC */ + + +/* For all the following operations, the denormalized number is set to */ +/* zero and the operation is reperformed the correct destination and source */ +/* sizes. */ + +FADD: + bb0 1,r12,FADDS2dnm /* S1 is not denorm, so S2 must be */ + or r5,r0,r0 /* set S1 to zero */ + or r6,r0,r0 +FADDS2chk: + bb0 0,r12,FADDcalc /* S2 is not a denorm */ +FADDS2dnm: + or r7,r0,r0 /* set S2 to zero */ + or r8,r0,r0 +FADDcalc: + bb1 5,r9,FADDdD /* branch for double precision destination */ +FADDsD: + bb1 9,r9,FADDsDdS1 /* branch for double precision S1 */ +FADDsDsS1: + bb1 7,r9,FADDsDsS1dS2 /* branch for double precision S2 */ +FADDsDsS1sS2: + br.n return + fadd.sss r6,r6,r8 /* add the two sources and place result 10 S1 */ +FADDsDsS1dS2: + br.n return + fadd.ssd r6,r6,r7 /* add the two sources and place result 10 S1 */ +FADDsDdS1: + bb1 7,r9,FADDsDdS1dS2 /* branch for double precision S2 */ +FADDsDdS1sS2: + br.n return + fadd.sds r6,r5,r8 /* add the two sources and place result 10 S1 */ +FADDsDdS1dS2: + br.n return + fadd.sdd r6,r5,r7 /* add the two sources and place result 10 S1 */ +FADDdD: + bb1 9,r9,FADDdDdS1 /* branch for double precision S1 */ +FADDdDsS1: + bb1 7,r9,FADDdDsS1dS2 /* branch for double precision S2 */ +FADDdDsS1sS2: + br.n return + fadd.dss r5,r6,r8 /* add the two sources and place result 10 S1 */ +FADDdDsS1dS2: + br.n return + fadd.dsd r5,r6,r7 /* add the two sources and place result 10 S1 */ +FADDdDdS1: + bb1 7,r9,FADDdDdS1dS2 /* branch for double precision S2 */ +FADDdDdS1sS2: + br.n return + fadd.dds r5,r5,r8 /* add the two sources and place result 10 S1 */ +FADDdDdS1dS2: + br.n return + fadd.ddd r5,r5,r7 /* add the two sources and place result 10 S1 */ + +FSUB: + bb0 1,r12,FSUBS2dnm /* S1 is not denorm, so S2 must be */ + or r5,r0,r0 /* set S1 to zero */ + or r6,r0,r0 +FSUBS2chk: + bb0 0,r12,FSUBcalc /* S2 is not a denorm */ +FSUBS2dnm: + or r7,r0,r0 /* set S2 to zero */ + or r8,r0,r0 +FSUBcalc: + bb1 5,r9,FSUBdD /* branch for double precision destination */ +FSUBsD: + bb1 9,r9,FSUBsDdS1 /* branch for double precision S1 */ +FSUBsDsS1: + bb1 7,r9,FSUBsDsS1dS2 /* branch for double precision S2 */ +FSUBsDsS1sS2: + br.n return + fsub.sss r6,r6,r8 /* add the two sources and place result 10 S1 */ +FSUBsDsS1dS2: + br.n return + fsub.ssd r6,r6,r7 /* add the two sources and place result 10 S1 */ +FSUBsDdS1: + bb1 7,r9,FSUBsDdS1dS2 /* branch for double precision S2 */ +FSUBsDdS1sS2: + br.n return + fsub.sds r6,r5,r8 /* add the two sources and place result 10 S1 */ +FSUBsDdS1dS2: + br.n return + fsub.sdd r6,r5,r7 /* add the two sources and place result 10 S1 */ +FSUBdD: + bb1 9,r9,FSUBdDdS1 /* branch for double precision S1 */ +FSUBdDsS1: + bb1 7,r9,FSUBdDsS1dS2 /* branch for double precision S2 */ +FSUBdDsS1sS2: + br.n return + fsub.dss r5,r6,r8 /* add the two sources and place result 10 S1 */ +FSUBdDsS1dS2: + br.n return + fsub.dsd r5,r6,r7 /* add the two sources and place result 10 S1 */ +FSUBdDdS1: + bb1 7,r9,FSUBdDdS1dS2 /* branch for double precision S2 */ +FSUBdDdS1sS2: + br.n return + fsub.dds r5,r5,r8 /* add the two sources and place result 10 S1 */ +FSUBdDdS1dS2: + br.n return + fsub.ddd r5,r5,r7 /* add the two sources and place result 10 S1 */ + +FCMP: + bb0 1,r12,FCMPS2dnm /* S1 is not denorm, so S2 must be */ + or r5,r0,r0 /* set S1 to zero */ + or r6,r0,r0 +FCMPS2chk: + bb0 0,r12,FCMPcalc /* S2 is not a denorm */ +FCMPS2dnm: + or r7,r0,r0 /* set S2 to zero */ + or r8,r0,r0 +FCMPcalc: + bb1 9,r9,FCMPdS1 /* branch for double precision S1 */ +FCMPsS1: + bb1 7,r9,FCMPsS1dS2 /* branch for double precision S2 */ +FCMPsS1sS2: + br.n return + fcmp.sss r6,r6,r8 /* add the two sources and place result 10 S1 */ +FCMPsS1dS2: + br.n return + fcmp.ssd r6,r6,r7 /* add the two sources and place result 10 S1 */ +FCMPdS1: + bb1 7,r9,FCMPdS1dS2 /* branch for double precision S2 */ +FCMPdS1sS2: + br.n return + fcmp.sds r6,r5,r8 /* add the two sources and place result 10 S1 */ +FCMPdS1dS2: + br.n return + fcmp.sdd r6,r5,r7 /* add the two sources and place result 10 S1 */ + +FMUL: + bb0 1,r12,FMULS2dnm /* S1 is not denorm, so S2 must be */ + or r5,r0,r0 /* set S1 to zero */ + or r6,r0,r0 +FMULS2chk: + bb0 0,r12,FMULcalc /* S2 is not a denorm */ +FMULS2dnm: + or r7,r0,r0 /* set S2 to zero */ + or r8,r0,r0 +FMULcalc: + bb1 5,r9,FMULdD /* branch for double precision destination */ +FMULsD: + bb1 9,r9,FMULsDdS1 /* branch for double precision S1 */ +FMULsDsS1: + bb1 7,r9,FMULsDsS1dS2 /* branch for double precision S2 */ +FMULsDsS1sS2: + br.n return + fmul.sss r6,r6,r8 /* add the two sources and place result 10 S1 */ +FMULsDsS1dS2: + br.n return + fmul.ssd r6,r6,r7 /* add the two sources and place result 10 S1 */ +FMULsDdS1: + bb1 7,r9,FMULsDdS1dS2 /* branch for double precision S2 */ +FMULsDdS1sS2: + br.n return + fmul.sds r6,r5,r8 /* add the two sources and place result 10 S1 */ +FMULsDdS1dS2: + br.n return + fmul.sdd r6,r5,r7 /* add the two sources and place result 10 S1 */ +FMULdD: + bb1 9,r9,FMULdDdS1 /* branch for double precision S1 */ +FMULdDsS1: + bb1 7,r9,FMULdDsS1dS2 /* branch for double precision S2 */ +FMULdDsS1sS2: + br.n return + fmul.dss r5,r6,r8 /* add the two sources and place result 10 S1 */ +FMULdDsS1dS2: + br.n return + fmul.dsd r5,r6,r7 /* add the two sources and place result 10 S1 */ +FMULdDdS1: + bb1 7,r9,FMULdDdS1dS2 /* branch for double precision S2 */ +FMULdDdS1sS2: + br.n return + fmul.dds r5,r5,r8 /* add the two sources and place result 10 S1 */ +FMULdDdS1dS2: + br.n return + fmul.ddd r5,r5,r7 /* add the two sources and place result 10 S1 */ + +FDIV: + bb0 1,r12,FDIVS2dnm /* S1 is not denorm, so S2 must be */ + or r5,r0,r0 /* set S1 to zero */ + or r6,r0,r0 +FDIVS2chk: + bb0 0,r12,FDIVcalc /* S2 is not a denorm */ +FDIVS2dnm: + or r7,r0,r0 /* set S2 to zero */ + or r8,r0,r0 +FDIVcalc: + bb1 5,r9,FDIVdD /* branch for double precision destination */ +FDIVsD: + bb1 9,r9,FDIVsDdS1 /* branch for double precision S1 */ +FDIVsDsS1: + bb1 7,r9,FDIVsDsS1dS2 /* branch for double precision S2 */ +FDIVsDsS1sS2: + fdiv.sss r6,r6,r8 /* add the two sources and place result 10 S1 */ + br return +FDIVsDsS1dS2: + fdiv.ssd r6,r6,r7 /* add the two sources and place result 10 S1 */ + br return +FDIVsDdS1: + bb1 7,r9,FDIVsDdS1dS2 /* branch for double precision S2 */ +FDIVsDdS1sS2: + fdiv.sds r6,r5,r8 /* add the two sources and place result 10 S1 */ + br return +FDIVsDdS1dS2: + fdiv.sdd r6,r5,r7 /* add the two sources and place result 10 S1 */ + br return +FDIVdD: + bb1 9,r9,FDIVdDdS1 /* branch for double precision S1 */ +FDIVdDsS1: + bb1 7,r9,FDIVdDsS1dS2 /* branch for double precision S2 */ +FDIVdDsS1sS2: + fdiv.dss r5,r6,r8 /* add the two sources and place result 10 S1 */ + br return +FDIVdDsS1dS2: + fdiv.dsd r5,r6,r7 /* add the two sources and place result 10 S1 */ + br return +FDIVdDdS1: + bb1 7,r9,FDIVdDdS1dS2 /* branch for double precision S2 */ +FDIVdDdS1sS2: + fdiv.dds r5,r5,r8 /* add the two sources and place result 10 S1 */ + br return +FDIVdDdS1dS2: + fdiv.ddd r5,r5,r7 /* add the two sources and place result 10 S1 */ + br return + +#if 0 +FSQRT: + or r7,r0,r0 /* set S2 to zero */ + or r8,r0,r0 +FSQRTcalc: + bb1 5,r9,FSQRTdD /* branch for double precision destination */ +FSQRTsD: + bb1 7,r9,FSQRTsDdS2 /* branch for double precision S2 */ +FSQRTsDsS2: + br.n return + fsqrt.ss r6,r8 /* add the two sources and place result 10 S1 */ +FSQRTsDdS2: + br.n return + fsqrt.sd r6,r7 /* add the two sources and place result 10 S1 */ +FSQRTdD: + bb1 7,r9,FSQRTdDdS2 /* branch for double precision S2 */ +FSQRTdDsS2: + br.n return + fsqrt.ds r5,r8 /* add the two sources and place result 10 S1 */ +FSQRTdDdS2: + br.n return + fsqrt.dd r5,r7 /* add the two sources and place result 10 S1 */ +#endif + +INT: + or r7,r0,r0 /* set S2 to zero */ + or r8,r0,r0 +INTcalc: + bb1 7,r9,INTdS2 /* branch for double precision S2 */ +INTsS2: + br.n return + int.ss r6,r8 /* add the two sources and place result 10 S1 */ +INTdS2: + br.n return + int.sd r6,r7 /* add the two sources and place result 10 S1 */ + +NINT: + or r7,r0,r0 /* set S2 to zero */ + or r8,r0,r0 +NINTcalc: + bb1 7,r9,NINTdS2 /* branch for double precision S2 */ +NINTsS2: + br.n return + nint.ss r6,r8 /* add the two sources and place result 10 S1 */ +NINTdS2: + br.n return + nint.sd r6,r7 /* add the two sources and place result 10 S1 */ + +TRNC: + or r7,r0,r0 /* set S2 to zero */ + or r8,r0,r0 +TRNCcalc: + bb1 7,r9,TRNCdS2 /* branch for double precision S2 */ +TRNCsS2: + br.n return + trnc.ss r6,r8 /* add the two sources and place result 10 S1 */ +TRNCdS2: + trnc.sd r6,r7 /* add the two sources and place result 10 S1 */ + + +/* Return to the routine that detected the reserved operand. */ + +return: + ld r1,r31,0 /* load return address */ + jmp r1 + + data + + +/* S1 and/or S2 is an infinity, and the other operand may be a zero. */ +/* Knowing which operands are infinity, check the remaining operands for zeros. */ + +ASLOCAL(zero) + bb0 s1inf,r12,S1noinf /* see if S1 is zero */ + bb0 s2inf,r12,S2noinf /* see if S2 is zero */ + jmp r1 + +/* See if S1 is zero. Whether or not S1 is a zero, being in this routine */ +/* implies that S2 is infinity, so return to subroutine infinity after */ +/* completing this code. Set the s1zero flag in r12 if S1 is zero. */ + +S1noinf: + bb1 s1size,r9,S1noinfd /* work with double precision operand */ +S1noinfs: + or r10,r0,r5 /* load high word into r10 */ + clr r10,r10,1<sign> /* clear the sign bit */ + extu r11,r6,3<29> /* extract lower 3 bits of mantissa */ + or r10,r10,r11 /* or these 3 bits with high word */ + bcnd ne0,r10,operation /* do not set zero flag */ + jmp.n r1 /* since this operand was not */ + /* infinity, S2 must have been, */ + /* so return */ + set r12,r12,1<s1zero> /* set zeroflag */ +S1noinfd: + clr r10,r5,1<sign> /* clear the sign bit */ + or r10,r6,r10 /* or high and low word */ + bcnd ne0,r10,operation /* do not set zero flag */ + jmp.n r1 /* since this operand was not */ + /* infinity, S2 must have been, */ + /* so return */ + set r12,r12,1<s1zero> /* set zeroflag */ + + +/* Check S2 for zero. If it is zero, then set the s2zero flag in r12. */ + +S2noinf: + bb1 s2size,r9,S2noinfd /* work with double precision operand */ +S2noinfs: + or r10,r0,r7 /* load high word into r10 */ + clr r10,r10,1<sign> /* clear the sign bit */ + extu r11,r8,3<29> /* extract lower 3 bits of mantissa */ + or r10,r10,r11 /* or these 3 bits with high word */ + bcnd ne0,r10,operation /* do not set zero flag */ + jmp.n r1 /* since this operand was not */ + /* infinity, S1 must have been, */ + /* so return */ + set r12,r12,1<s2zero> /* set zeroflag */ +S2noinfd: + clr r10,r7,1<sign> /* clear the sign bit */ + or r10,r8,r10 /* or high and low word */ + bcnd ne0,r10,operation /* do not set zero flag */ + set r12,r12,1<s2zero> /* set zeroflag */ + /* since this operand was not */ + /* infinity, S1 must have been, */ + /* so return */ +operation: + jmp r1 + +ASENTRY(Xfp_imprecise) +/* input: r3 is the exception frame */ + or r29, r3, r0 /* r29 is now the E.F. */ + subu r31, r31, 40 + st r1, r31, 32 + st r29, r31, 36 + + ld r2 , r29, EF_FPSR * 4 + ld r3 , r29, EF_FPCR * 4 + ld r4 , r29, EF_FPECR * 4 + ld r10, r29, EF_FPRH * 4 + ld r11, r29, EF_FPRL * 4 + ld r12, r29, EF_FPIT * 4 + +/* Load into r1 the return address for the exception handlers. Looking */ +/* at FPECR, branch to the appropriate exception handler. */ + + or.u r1,r0,hi16(fpui_wrapup)/* load return address of functions */ + or r1,r1,lo16(fpui_wrapup) + + bb0 2,r4,2f /* branch to FPunderflow if bit set */ + br _ASM_LABEL(FPunderflow) +2: + bb0 1,r4,3f /* branch to FPoverflow if bit set */ + br _ASM_LABEL(FPoverflow) +3: + /* XXX handle inexact!!! */ +#ifdef HANDLER + br _handler /* branch to handler since bit will */ + /* be set for inexact */ +#endif + +fpui_wrapup: + tb1 0,r0,0 /* make sure all floating point operations */ + /* have finished */ + ldcr r4, cr1 /* load the PSR */ +#if 0 + set r4, r4, 1<PSR_FPU_DISABLE_BIT> +#endif + set r4, r4, 1<PSR_INTERRUPT_DISABLE_BIT> + stcr r4, cr1 + ld r1, r31, 32 + ld r29,r31, 36 + addu r31, r31, 40 + + fstcr r2, FPSR /* write revised value of FPSR */ + fstcr r3, FPCR /* write revised value of FPCR */ + + /* write back the results */ + extu r2, r12, 5<0> + addu r3, r29, EF_R0*4 + bb0 destsize, r12, Iwritesingle + st r10, r3 [r2] + addu r2, r2, 1 + clr r2, r2, 27<5> +Iwritesingle: + st r11, r3 [r2] + jmp r1 diff --git a/sys/arch/luna88k/luna88k/m8820x.c b/sys/arch/luna88k/luna88k/m8820x.c new file mode 100644 index 00000000000..2c7255f71d1 --- /dev/null +++ b/sys/arch/luna88k/luna88k/m8820x.c @@ -0,0 +1,1595 @@ +/* $OpenBSD: m8820x.c,v 1.1 2004/04/21 15:24:03 aoyama Exp $ */ +/* + * Copyright (c) 2004, Miodrag Vallat. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Copyright (c) 2001 Steve Murphree, Jr. + * Copyright (c) 1996 Nivas Madhur + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Nivas Madhur. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/* + * Mach Operating System + * Copyright (c) 1993-1991 Carnegie Mellon University + * Copyright (c) 1991 OMRON Corporation + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON AND OMRON ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON AND OMRON DISCLAIM ANY LIABILITY OF ANY KIND + * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie the + * rights to redistribute these changes. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/simplelock.h> + +#include <machine/asm_macro.h> +#include <machine/board.h> +#include <machine/cpu_number.h> +#include <machine/locore.h> + +#include <machine/cmmu.h> +#include <machine/m8820x.h> + +#include <uvm/uvm_extern.h> + +#ifdef DDB +#include <ddb/db_output.h> /* db_printf() */ +#endif /* DDB */ + +/* + * On some versions of the 88200, page size flushes don't work. I am using + * sledge hammer approach till I find for sure which ones are bad XXX nivas + */ +#define BROKEN_MMU_MASK + +#undef SHADOW_BATC /* don't use BATCs for now XXX nivas */ + +#ifdef DEBUG +unsigned int m8820x_debuglevel; +#define dprintf(_X_) \ + do { \ + if (m8820x_debuglevel != 0) { \ + unsigned int psr = disable_interrupts_return_psr(); \ + printf("%d: ", cpu_number()); \ + printf _X_; \ + set_psr(psr); \ + } \ + } while (0) +#else +#define dprintf(_X_) do { } while (0) +#endif + +void m8820x_cmmu_init(void); +void m8820x_setup_board_config(void); +void m8820x_cpu_configuration_print(int); +void m8820x_cmmu_shutdown_now(void); +void m8820x_cmmu_parity_enable(void); +unsigned m8820x_cmmu_cpu_number(void); +void m8820x_cmmu_set_sapr(unsigned, unsigned); +void m8820x_cmmu_set_uapr(unsigned); +void m8820x_cmmu_set_pair_batc_entry(unsigned, unsigned, unsigned); +void m8820x_cmmu_flush_tlb(unsigned, unsigned, vaddr_t, vsize_t); +void m8820x_cmmu_pmap_activate(unsigned, unsigned, + u_int32_t i_batc[BATC_MAX], u_int32_t d_batc[BATC_MAX]); +void m8820x_cmmu_flush_cache(int, paddr_t, psize_t); +void m8820x_cmmu_flush_inst_cache(int, paddr_t, psize_t); +void m8820x_cmmu_flush_data_cache(int, paddr_t, psize_t); +void m8820x_dma_cachectl(vaddr_t, vsize_t, int); +void m8820x_cmmu_dump_config(void); +void m8820x_cmmu_show_translation(unsigned, unsigned, unsigned, int); +void m8820x_show_apr(unsigned); + +/* This is the function table for the mc8820x CMMUs */ +struct cmmu_p cmmu8820x = { + m8820x_cmmu_init, + m8820x_setup_board_config, + m8820x_cpu_configuration_print, + m8820x_cmmu_shutdown_now, + m8820x_cmmu_parity_enable, + m8820x_cmmu_cpu_number, + m8820x_cmmu_set_sapr, + m8820x_cmmu_set_uapr, + m8820x_cmmu_set_pair_batc_entry, + m8820x_cmmu_flush_tlb, + m8820x_cmmu_pmap_activate, + m8820x_cmmu_flush_cache, + m8820x_cmmu_flush_inst_cache, + m8820x_cmmu_flush_data_cache, + m8820x_dma_cachectl, +#ifdef DDB + m8820x_cmmu_dump_config, + m8820x_cmmu_show_translation, +#else + NULL, + NULL, +#endif +#ifdef DEBUG + m8820x_show_apr, +#else + NULL, +#endif +}; + +/* + * CMMU kernel information + */ +struct m8820x_cmmu { + unsigned *volatile cmmu_regs; /* CMMU "base" area */ + unsigned int cmmu_cpu; /* cpu number it is attached to */ + unsigned int cmmu_type; +#define INST_CMMU 0 +#define DATA_CMMU 1 + unsigned int cmmu_access; +#define CMMU_ACS_USER 0 +#define CMMU_ACS_SUPER 1 +#define CMMU_ACS_BOTH 2 + unsigned int cmmu_alive; +#define CMMU_DEAD 0 /* This cmmu is not there */ +#define CMMU_AVAILABLE 1 /* It's there, but which cpu's? */ +#define CMMU_MARRIED 2 /* Know which cpu it belongs to. */ + vaddr_t cmmu_addr; /* address range */ + vaddr_t cmmu_addr_mask; /* address mask */ + int cmmu_addr_match;/* return value of address comparison */ +#ifdef SHADOW_BATC + unsigned batc[BATC_MAX]; +#endif +}; + +#ifdef SHADOW_BATC +/* CMMU(cpu,data) is the cmmu struct for the named cpu's indicated cmmu. */ +#define CMMU(cpu, data) cpu_cmmu[(cpu)].pair[(data) ? DATA_CMMU : INST_CMMU] +#endif + +/* + * Structure for accessing MMUS properly + */ + +struct m8820x_cmmu m8820x_cmmu[MAX_CMMUS] = +{ + /* address, cpu, mode, access, alive, addr, mask */ + {(unsigned *volatile)CMMU_I0, -1, INST_CMMU, CMMU_ACS_BOTH, CMMU_DEAD, 0, 0}, + {(unsigned *volatile)CMMU_D0, -1, DATA_CMMU, CMMU_ACS_BOTH, CMMU_DEAD, 0, 0}, + {(unsigned *volatile)CMMU_I1, -1, INST_CMMU, CMMU_ACS_BOTH, CMMU_DEAD, 0, 0}, + {(unsigned *volatile)CMMU_D1, -1, DATA_CMMU, CMMU_ACS_BOTH, CMMU_DEAD, 0, 0}, + {(unsigned *volatile)CMMU_I2, -1, INST_CMMU, CMMU_ACS_BOTH, CMMU_DEAD, 0, 0}, + {(unsigned *volatile)CMMU_D2, -1, DATA_CMMU, CMMU_ACS_BOTH, CMMU_DEAD, 0, 0}, + {(unsigned *volatile)CMMU_I3, -1, INST_CMMU, CMMU_ACS_BOTH, CMMU_DEAD, 0, 0}, + {(unsigned *volatile)CMMU_D3, -1, DATA_CMMU, CMMU_ACS_BOTH, CMMU_DEAD, 0, 0} +}; + +struct cpu_cmmu { + struct m8820x_cmmu *pair[2]; +} cpu_cmmu[MAX_CPUS]; + +/* + * CMMU per CPU split strategies + */ + +#define CMMU_SPLIT_ADDRESS 0x00 +#define CMMU_SPLIT_SPV 0x01 +#define CMMU_SPLIT_SRAM_SPV 0x02 +#define CMMU_SPLIT_SRAM_ALL 0x03 + +#define CMMU_SPLIT_MASK 0x03 + +struct cmmu_strategy { + int inst; + int data; +} cpu_cmmu_strategy[] = { + /* inst data */ + { CMMU_SPLIT_ADDRESS, CMMU_SPLIT_ADDRESS}, /* CPU 0 */ + { CMMU_SPLIT_ADDRESS, CMMU_SPLIT_ADDRESS}, /* CPU 1 */ + { CMMU_SPLIT_ADDRESS, CMMU_SPLIT_ADDRESS}, /* CPU 2 */ + { CMMU_SPLIT_ADDRESS, CMMU_SPLIT_ADDRESS} /* CPU 3 */ +}; + +unsigned int cmmu_shift; + +/* local prototypes */ +void m8820x_cmmu_set(int, unsigned, int, int, int, int, vaddr_t); +void m8820x_cmmu_wait(int); +void m8820x_cmmu_sync_cache(paddr_t, psize_t); +void m8820x_cmmu_sync_inval_cache(paddr_t, psize_t); +void m8820x_cmmu_inval_cache(paddr_t, psize_t); + +/* Flags passed to m8820x_cmmu_set() */ +#define MODE_VAL 0x01 +#define ACCESS_VAL 0x02 +#define ADDR_VAL 0x04 + +#ifdef DEBUG +void +m8820x_show_apr(value) + unsigned value; +{ + printf("table @ 0x%x000", PG_PFNUM(value)); + if (value & CACHE_WT) + printf(", writethrough"); + if (value & CACHE_GLOBAL) + printf(", global"); + if (value & CACHE_INH) + printf(", cache inhibit"); + if (value & APR_V) + printf(", valid"); + printf("\n"); +} +#endif + +/* + * This routine sets up the CPU/CMMU configuration. + */ +void +m8820x_setup_board_config() +{ + int num, cmmu_num; + unsigned *volatile cr; + + master_cpu = 0; /* temp to get things going */ + max_cpus = 4; + max_cmmus = 8; + + cmmu_shift = ff1(max_cmmus / max_cpus); + + /* + * Probe for available MMUs + */ + for (cmmu_num = 0; cmmu_num < max_cmmus; cmmu_num++) { + cr = m8820x_cmmu[cmmu_num].cmmu_regs; + if (badwordaddr((vaddr_t)cr) == 0) { + int type; + + type = CMMU_TYPE(cr[CMMU_IDR]); +#ifdef DIAGNOSTIC + if (type != M88200_ID && type != M88204_ID) { + printf("WARNING: non M8820x circuit found " + "at CMMU address %p\n", cr); + continue; /* will probably die quickly */ + } +#endif + m8820x_cmmu[cmmu_num].cmmu_alive = CMMU_AVAILABLE; + dprintf(("m8820x_setup_cmmu_config: CMMU %d found at %p\n", + cmmu_num, cr)); + } + } + + /* + * Now that we know which CMMUs are there, let's report on which + * CPU/CMMU sets seem complete (hopefully all) + */ + for (num = 0; num < max_cpus; num++) { + int i, type; + + for (i = 0; i < (1 << cmmu_shift); i++) { + dprintf(("cmmu_init: testing CMMU %d for CPU %d\n", + (num << cmmu_shift) | i, num)); +#ifdef DIAGNOSTIC + if (m8820x_cmmu[(num << cmmu_shift) | i].cmmu_alive == CMMU_DEAD) { + printf("CMMU %d attached to CPU %d is not working\n", + (num << cmmu_shift) | i, num); + continue; /* will probably die quickly */ + } +#endif + } + cpu_sets[num] = 1; /* This cpu installed... */ + type = CMMU_TYPE(m8820x_cmmu[num << cmmu_shift]. + cmmu_regs[CMMU_IDR]); + + printf("CPU%d is attached with %d MC%x CMMUs\n", + num, 1 << cmmu_shift, type == M88204_ID ? 0x88204 : 0x88200); + } + + /* + * Calculate the CMMU<->CPU connections + */ + for (cmmu_num = 0; cmmu_num < max_cmmus; cmmu_num++) { + m8820x_cmmu[cmmu_num].cmmu_cpu = + (cmmu_num * max_cpus) / max_cmmus; + dprintf(("m8820x_setup_cmmu_config: CMMU %d connected with CPU %d\n", + cmmu_num, m8820x_cmmu[cmmu_num].cmmu_cpu)); + } + + /* + * Now set m8820x_cmmu[].cmmu_access and addr + */ + for (cmmu_num = 0; cmmu_num < max_cmmus; cmmu_num++) { + /* + * We don't set up anything for the hardwired configurations. + */ + m8820x_cmmu[cmmu_num].cmmu_addr = 0; + m8820x_cmmu[cmmu_num].cmmu_addr_mask = 0; + m8820x_cmmu[cmmu_num].cmmu_addr_match = 1; + m8820x_cmmu[cmmu_num].cmmu_access = CMMU_ACS_BOTH; + } +} + +#ifdef DDB + +const char *cmmu_strat_string[] = { + "address split ", + "user/spv split", + "spv SRAM split", + "all SRAM split" +}; + +void +m8820x_cmmu_dump_config() +{ + int cmmu_num; + + db_printf("Current CPU/CMMU configuration:\n"); + + for (cmmu_num = 0; cmmu_num < max_cmmus; cmmu_num++) { + db_printf("CMMU #%d: %s CMMU for CPU %d:\n Strategy: %s\n %s access addr 0x%08lx mask 0x%08lx match %s\n", + cmmu_num, + (m8820x_cmmu[cmmu_num].cmmu_type == INST_CMMU) ? "inst" : "data", + m8820x_cmmu[cmmu_num].cmmu_cpu, + cmmu_strat_string[(m8820x_cmmu[cmmu_num].cmmu_type == INST_CMMU) ? + cpu_cmmu_strategy[m8820x_cmmu[cmmu_num].cmmu_cpu].inst : + cpu_cmmu_strategy[m8820x_cmmu[cmmu_num].cmmu_cpu].data], + (m8820x_cmmu[cmmu_num].cmmu_access == CMMU_ACS_BOTH) ? "User and spv" : + ((m8820x_cmmu[cmmu_num].cmmu_access == CMMU_ACS_USER) ? "User " : + "Supervisor "), + m8820x_cmmu[cmmu_num].cmmu_addr, + m8820x_cmmu[cmmu_num].cmmu_addr_mask, + m8820x_cmmu[cmmu_num].cmmu_addr_match ? "TRUE" : "FALSE"); + } +} +#endif /* DDB */ + +/* + * This function is called by the MMU module and pokes values + * into the CMMU's registers. + */ +void +m8820x_cmmu_set(reg, val, flags, num, mode, access, addr) + int reg; + unsigned val; + int flags, num, mode, access; + vaddr_t addr; +{ + int mmu; + + /* + * We scan all CMMUs to find the matching ones and store the + * values there. + */ + for (mmu = num << cmmu_shift; + mmu < (num + 1) << cmmu_shift; mmu++) { + if (((flags & MODE_VAL)) && + (m8820x_cmmu[mmu].cmmu_type != mode)) + continue; + if (((flags & ACCESS_VAL)) && + (m8820x_cmmu[mmu].cmmu_access != access) && + (m8820x_cmmu[mmu].cmmu_access != CMMU_ACS_BOTH)) + continue; + if (flags & ADDR_VAL) { + if (((addr & m8820x_cmmu[mmu].cmmu_addr_mask) == m8820x_cmmu[mmu].cmmu_addr) + != m8820x_cmmu[mmu].cmmu_addr_match) { + continue; + } + } + m8820x_cmmu[mmu].cmmu_regs[reg] = val; + } +} + +/* + * Force a read from the CMMU status register, thereby forcing execution to + * stop until all pending CMMU operations are finished. + * This is used by the various cache invalidation functions. + */ +void +m8820x_cmmu_wait(int cpu) +{ + int mmu; + + /* + * We scan all related CMMUs and read their status register. + */ + for (mmu = cpu << cmmu_shift; + mmu < (cpu + 1) << cmmu_shift; mmu++) { +#ifdef DEBUG + if (m8820x_cmmu[mmu].cmmu_regs[CMMU_SSR] & CMMU_SSR_BE) { + panic("cache flush failed!"); + } +#else + /* force the read access, but do not issue this statement... */ + __asm__ __volatile__ ("|or r0, r0, %0" :: + "r" (m8820x_cmmu[mmu].cmmu_regs[CMMU_SSR])); +#endif + } +} + +const char *mmutypes[8] = { + "Unknown (0)", + "Unknown (1)", + "Unknown (2)", + "Unknown (3)", + "Unknown (4)", + "M88200 (16K)", + "M88204 (64K)", + "Unknown (7)" +}; + +/* + * Should only be called after the calling cpus knows its cpu + * number and master/slave status . Should be called first + * by the master, before the slaves are started. +*/ +void +m8820x_cpu_configuration_print(master) + int master; +{ + int pid = read_processor_identification_register(); + int proctype = (pid & 0xff00) >> 8; + int procvers = (pid & 0xe) >> 1; + int mmu, cpu = cpu_number(); + struct simplelock print_lock; + + if (master) + simple_lock_init(&print_lock); + + simple_lock(&print_lock); + + printf("cpu%d: ", cpu); + if (proctype != 0) { + printf("unknown model arch 0x%x rev 0x%x\n", + proctype, procvers); + simple_unlock(&print_lock); + return; + } + + printf("M88100 rev 0x%x", procvers); +#if 0 /* not useful yet */ +#ifdef MVME188 + if (brdtyp == BRD_188) + printf(", %s", master ? "master" : "slave"); +#endif +#endif + printf(", %d CMMU", 1 << cmmu_shift); + + for (mmu = cpu << cmmu_shift; mmu < (cpu + 1) << cmmu_shift; + mmu++) { + int idr = m8820x_cmmu[mmu].cmmu_regs[CMMU_IDR]; + int mmuid = CMMU_TYPE(idr); + int access = m8820x_cmmu[mmu].cmmu_access; + + if (mmu % 2 == 0) + printf("\ncpu%d: ", cpu); + else + printf(", "); + + if (mmutypes[mmuid][0] == 'U') + printf("unknown model id 0x%x", mmuid); + else + printf("%s", mmutypes[mmuid]); + printf(" rev 0x%x, %s %scache", + CMMU_VERSION(idr), + access == CMMU_ACS_BOTH ? "global" : + (access == CMMU_ACS_USER ? "user" : "sup"), + m8820x_cmmu[mmu].cmmu_type == INST_CMMU ? "I" : "D"); + } + printf("\n"); + +#ifndef ERRATA__XXX_USR + { + static int errata_warn = 0; + + if (proctype != 0 && procvers < 2) { + if (!errata_warn++) + printf("WARNING: M88100 bug workaround code " + "not enabled.\nPlease recompile the kernel " + "with option ERRATA__XXX_USR !\n"); + } + } +#endif + + simple_unlock(&print_lock); +} + +/* + * CMMU initialization routine + */ +void +m8820x_cmmu_init() +{ + unsigned int line, cmmu_num; + int cssp, cpu, type; + u_int32_t apr; + unsigned *volatile cr; + + for (cpu = 0; cpu < max_cpus; cpu++) { + cpu_cmmu[cpu].pair[INST_CMMU] = 0; + cpu_cmmu[cpu].pair[DATA_CMMU] = 0; + } + + for (cmmu_num = 0; cmmu_num < max_cmmus; cmmu_num++) { + if (m8820x_cmmu[cmmu_num].cmmu_alive != CMMU_DEAD) { + cr = m8820x_cmmu[cmmu_num].cmmu_regs; + type = CMMU_TYPE(cr[CMMU_IDR]); + + cpu_cmmu[m8820x_cmmu[cmmu_num].cmmu_cpu]. + pair[m8820x_cmmu[cmmu_num].cmmu_type] = + &m8820x_cmmu[cmmu_num]; + + /* + * Reset cache + */ + for (cssp = type == M88204_ID ? 3 : 0; + cssp >= 0; cssp--) + for (line = 0; line <= 255; line++) { + cr[CMMU_SAR] = + line << MC88200_CACHE_SHIFT; + cr[CMMU_CSSP(cssp)] = + CMMU_CSSP_L5 | CMMU_CSSP_L4 | + CMMU_CSSP_L3 | CMMU_CSSP_L2 | + CMMU_CSSP_L1 | CMMU_CSSP_L0 | + CMMU_CSSP_VV(3, CMMU_VV_INVALID) | + CMMU_CSSP_VV(2, CMMU_VV_INVALID) | + CMMU_CSSP_VV(1, CMMU_VV_INVALID) | + CMMU_CSSP_VV(0, CMMU_VV_INVALID); + } + + /* + * Set the SCTR, SAPR, and UAPR to some known state + */ + cr[CMMU_SCTR] &= + ~(CMMU_SCTR_PE | CMMU_SCTR_SE | CMMU_SCTR_PR); + cr[CMMU_SAPR] = cr[CMMU_UAPR] = + ((0x00000 << PG_BITS) | CACHE_WT | CACHE_GLOBAL | + CACHE_INH) & ~APR_V; + +#ifdef SHADOW_BATC + m8820x_cmmu[cmmu_num].batc[0] = + m8820x_cmmu[cmmu_num].batc[1] = + m8820x_cmmu[cmmu_num].batc[2] = + m8820x_cmmu[cmmu_num].batc[3] = + m8820x_cmmu[cmmu_num].batc[4] = + m8820x_cmmu[cmmu_num].batc[5] = + m8820x_cmmu[cmmu_num].batc[6] = + m8820x_cmmu[cmmu_num].batc[7] = 0; +#endif + cr[CMMU_BWP0] = cr[CMMU_BWP1] = + cr[CMMU_BWP2] = cr[CMMU_BWP3] = + cr[CMMU_BWP4] = cr[CMMU_BWP5] = + cr[CMMU_BWP6] = cr[CMMU_BWP7] = 0; + cr[CMMU_SCR] = CMMU_FLUSH_CACHE_INV_ALL; + __asm__ __volatile__ ("|or r0, r0, %0" :: + "r" (cr[CMMU_SSR])); + cr[CMMU_SCR] = CMMU_FLUSH_SUPER_ALL; + cr[CMMU_SCR] = CMMU_FLUSH_USER_ALL; + } + } + + /* + * Enable snooping on MVME188 only. + * Snooping is enabled for instruction cmmus as well so that + * we can share breakpoints. + */ + + for (cpu = 0; cpu < max_cpus; cpu++) { + if (cpu_sets[cpu] == 0) + continue; + + m8820x_cmmu_set(CMMU_SCTR, CMMU_SCTR_SE, MODE_VAL, cpu, + DATA_CMMU, 0, 0); + m8820x_cmmu_set(CMMU_SCTR, CMMU_SCTR_SE, MODE_VAL, cpu, + INST_CMMU, 0, 0); + + m8820x_cmmu_set(CMMU_SCR, CMMU_FLUSH_SUPER_ALL, + ACCESS_VAL, cpu, 0, CMMU_ACS_SUPER, 0); + m8820x_cmmu_wait(cpu); + /* Icache gets flushed just below */ + } + + /* + * Enable instruction cache. + * Data cache can not be enabled at this point, because some device + * addresses can never be cached, and the no-caching zones are not + * set up yet. + */ + for (cpu = 0; cpu < max_cpus; cpu++) { + if (cpu_sets[cpu] == 0) + continue; + + apr = ((0x00000 << PG_BITS) | CACHE_WT | CACHE_GLOBAL) + & ~(CACHE_INH | APR_V); + + m8820x_cmmu_set(CMMU_SAPR, apr, MODE_VAL, cpu, INST_CMMU, 0, 0); + m8820x_cmmu_set(CMMU_SCR, CMMU_FLUSH_SUPER_ALL, + ACCESS_VAL, cpu, 0, CMMU_ACS_SUPER, 0); + m8820x_cmmu_wait(cpu); + } +} + +/* + * Just before poweroff or reset.... + */ +void +m8820x_cmmu_shutdown_now() +{ + unsigned cmmu_num; + unsigned *volatile cr; + + CMMU_LOCK; + for (cmmu_num = 0; cmmu_num < MAX_CMMUS; cmmu_num++) + if (m8820x_cmmu[cmmu_num].cmmu_alive != CMMU_DEAD) { + cr = m8820x_cmmu[cmmu_num].cmmu_regs; + + cr[CMMU_SCTR] &= + ~(CMMU_SCTR_PE | CMMU_SCTR_SE | CMMU_SCTR_PR); + cr[CMMU_SAPR] = cr[CMMU_UAPR] = + ((0x00000 << PG_BITS) | CACHE_INH) & + ~(CACHE_WT | CACHE_GLOBAL | APR_V); + } + CMMU_UNLOCK; +} + +/* + * enable parity + */ +void +m8820x_cmmu_parity_enable() +{ + unsigned cmmu_num; + unsigned *volatile cr; + + CMMU_LOCK; + + for (cmmu_num = 0; cmmu_num < max_cmmus; cmmu_num++) + if (m8820x_cmmu[cmmu_num].cmmu_alive != CMMU_DEAD) { + cr = m8820x_cmmu[cmmu_num].cmmu_regs; + cr[CMMU_SCTR] |= CMMU_SCTR_PE; + } + + CMMU_UNLOCK; +} + +/* + * Find out the CPU number from accessing CMMU + * Better be at splhigh, or even better, with interrupts + * disabled. + */ +#define ILLADDRESS 0x3ffffff0 /* any faulty address for luna88k2 */ + +unsigned +m8820x_cmmu_cpu_number() +{ + unsigned cmmu_no; + int i, cpu; + + CMMU_LOCK; + + for (i = 0; i < 10; i++) { + /* clear CMMU p-bus status registers */ + for (cmmu_no = 0; cmmu_no < MAX_CMMUS; cmmu_no++) { + if (m8820x_cmmu[cmmu_no].cmmu_alive == CMMU_AVAILABLE && + m8820x_cmmu[cmmu_no].cmmu_type == DATA_CMMU) + m8820x_cmmu[cmmu_no].cmmu_regs[CMMU_PFSR] = 0; + } + + /* access faulting address */ + badwordaddr((vaddr_t)ILLADDRESS); + + /* check which CMMU reporting the fault */ + for (cmmu_no = 0; cmmu_no < MAX_CMMUS; cmmu_no++) { + if (m8820x_cmmu[cmmu_no].cmmu_alive == CMMU_AVAILABLE && + m8820x_cmmu[cmmu_no].cmmu_type == DATA_CMMU && + CMMU_PFSR_FAULT(m8820x_cmmu[cmmu_no]. + cmmu_regs[CMMU_PFSR]) != CMMU_PFSR_SUCCESS) { + /* clean register, just in case... */ + m8820x_cmmu[cmmu_no].cmmu_regs[CMMU_PFSR] = 0; + m8820x_cmmu[cmmu_no].cmmu_alive = CMMU_MARRIED; + cpu = m8820x_cmmu[cmmu_no].cmmu_cpu; + CMMU_UNLOCK; + return cpu; + } + } + } + CMMU_UNLOCK; + + panic("m8820x_cmmu_cpu_number: could not determine my cpu number"); +} + +void +m8820x_cmmu_set_sapr(cpu, ap) + unsigned cpu, ap; +{ + CMMU_LOCK; + m8820x_cmmu_set(CMMU_SAPR, ap, ACCESS_VAL, cpu, 0, CMMU_ACS_SUPER, 0); + CMMU_UNLOCK; +} + +void +m8820x_cmmu_set_uapr(ap) + unsigned ap; +{ + int s = splhigh(); + int cpu = cpu_number(); + + CMMU_LOCK; + /* this functionality also mimiced in m8820x_cmmu_pmap_activate() */ + m8820x_cmmu_set(CMMU_UAPR, ap, ACCESS_VAL, cpu, 0, CMMU_ACS_USER, 0); + CMMU_UNLOCK; + splx(s); +} + +/* + * Set batc entry number entry_no to value in + * the data and instruction cache for the named CPU. + * + * Except for the cmmu_init, this function and m8820x_cmmu_pmap_activate + * are the only functions which may set the batc values. + */ +void +m8820x_cmmu_set_pair_batc_entry(cpu, entry_no, value) + unsigned cpu, entry_no; + unsigned value; /* the value to stuff into the batc */ +{ + CMMU_LOCK; + + m8820x_cmmu_set(CMMU_BWP(entry_no), value, MODE_VAL | ACCESS_VAL, + cpu, DATA_CMMU, CMMU_ACS_USER, 0); +#ifdef SHADOW_BATC + CMMU(cpu,DATA_CMMU)->batc[entry_no] = value; +#endif + m8820x_cmmu_set(CMMU_BWP(entry_no), value, MODE_VAL | ACCESS_VAL, + cpu, INST_CMMU, CMMU_ACS_USER, 0); +#ifdef SHADOW_BATC + CMMU(cpu,INST_CMMU)->batc[entry_no] = value; +#endif + + CMMU_UNLOCK; +} + +/* + * Functions that invalidate TLB entries. + */ + +/* + * flush any tlb + * Some functionality mimiced in m8820x_cmmu_pmap_activate. + */ +void +m8820x_cmmu_flush_tlb(unsigned cpu, unsigned kernel, vaddr_t vaddr, + vsize_t size) +{ + int s = splhigh(); + + CMMU_LOCK; + + /* + * Since segment operations are horribly expensive, don't + * do any here. Invalidations of up to three pages are performed + * as page invalidations, otherwise the entire tlb is flushed. + * + * Note that this code relies upon size being a multiple of + * a page and vaddr being page-aligned. + */ + if (size == PAGE_SIZE) { /* most frequent situation */ + m8820x_cmmu_set(CMMU_SAR, vaddr, + ADDR_VAL | ACCESS_VAL, cpu, 0, + kernel ? CMMU_ACS_SUPER : CMMU_ACS_USER, vaddr); + m8820x_cmmu_set(CMMU_SCR, + kernel ? CMMU_FLUSH_SUPER_PAGE : CMMU_FLUSH_USER_PAGE, + ADDR_VAL | ACCESS_VAL, cpu, 0, + kernel ? CMMU_ACS_SUPER : CMMU_ACS_USER, vaddr); + } else if (size > 3 * PAGE_SIZE) { + m8820x_cmmu_set(CMMU_SCR, + kernel ? CMMU_FLUSH_SUPER_ALL : CMMU_FLUSH_USER_ALL, + ACCESS_VAL, cpu, 0, + kernel ? CMMU_ACS_SUPER : CMMU_ACS_USER, 0); + } else + while (size != 0) { + m8820x_cmmu_set(CMMU_SAR, vaddr, + ADDR_VAL | ACCESS_VAL, cpu, 0, + kernel ? CMMU_ACS_SUPER : CMMU_ACS_USER, vaddr); + m8820x_cmmu_set(CMMU_SCR, + kernel ? CMMU_FLUSH_SUPER_PAGE : CMMU_FLUSH_USER_PAGE, + ADDR_VAL | ACCESS_VAL, cpu, 0, + kernel ? CMMU_ACS_SUPER : CMMU_ACS_USER, vaddr); + + size -= PAGE_SIZE; + vaddr += PAGE_SIZE; + } + + CMMU_UNLOCK; + splx(s); +} + +/* + * New fast stuff for pmap_activate. + * Does what a few calls used to do. + * Only called from pmap_activate(). + */ +void +m8820x_cmmu_pmap_activate(cpu, uapr, i_batc, d_batc) + unsigned cpu, uapr; + u_int32_t i_batc[BATC_MAX]; + u_int32_t d_batc[BATC_MAX]; +{ + int entry_no; + + CMMU_LOCK; + + /* the following is from m8820x_cmmu_set_uapr */ + m8820x_cmmu_set(CMMU_UAPR, uapr, ACCESS_VAL, + cpu, 0, CMMU_ACS_USER, 0); + + for (entry_no = 0; entry_no < BATC_MAX; entry_no++) { + m8820x_cmmu_set(CMMU_BWP(entry_no), i_batc[entry_no], + MODE_VAL | ACCESS_VAL, cpu, INST_CMMU, CMMU_ACS_USER, 0); + m8820x_cmmu_set(CMMU_BWP(entry_no), d_batc[entry_no], + MODE_VAL | ACCESS_VAL, cpu, DATA_CMMU, CMMU_ACS_USER, 0); +#ifdef SHADOW_BATC + CMMU(cpu,INST_CMMU)->batc[entry_no] = i_batc[entry_no]; + CMMU(cpu,DATA_CMMU)->batc[entry_no] = d_batc[entry_no]; +#endif + } + + /* + * Flush the user TLB. + * IF THE KERNEL WILL EVER CARE ABOUT THE BATC ENTRIES, + * THE SUPERVISOR TLBs SHOULD BE FLUSHED AS WELL. + */ + m8820x_cmmu_set(CMMU_SCR, CMMU_FLUSH_USER_ALL, ACCESS_VAL, + cpu, 0, CMMU_ACS_USER, 0); + + CMMU_UNLOCK; +} + +/* + * Functions that invalidate caches. + * + * Cache invalidates require physical addresses. Care must be exercised when + * using segment invalidates. This implies that the starting physical address + * plus the segment length should be invalidated. A typical mistake is to + * extract the first physical page of a segment from a virtual address, and + * then expecting to invalidate when the pages are not physically contiguous. + * + * We don't push Instruction Caches prior to invalidate because they are not + * snooped and never modified (I guess it doesn't matter then which form + * of the command we use then). + */ + +/* + * flush both Instruction and Data caches + */ +void +m8820x_cmmu_flush_cache(int cpu, paddr_t physaddr, psize_t size) +{ + int s = splhigh(); + CMMU_LOCK; + +#if !defined(BROKEN_MMU_MASK) + if (size > NBSG) { + m8820x_cmmu_set(CMMU_SCR, CMMU_FLUSH_CACHE_CBI_ALL, 0, + cpu, 0, 0, 0); + } else if (size <= MC88200_CACHE_LINE) { + m8820x_cmmu_set(CMMU_SAR, (unsigned)physaddr, ADDR_VAL, + cpu, 0, 0, (unsigned)physaddr); + m8820x_cmmu_set(CMMU_SCR, CMMU_FLUSH_CACHE_CBI_LINE, ADDR_VAL, + cpu, 0, 0, (unsigned)physaddr); + } else if (size <= NBPG) { + m8820x_cmmu_set(CMMU_SAR, (unsigned)physaddr, ADDR_VAL, + cpu, 0, 0, (unsigned)physaddr); + m8820x_cmmu_set(CMMU_SCR, CMMU_FLUSH_CACHE_CBI_PAGE, ADDR_VAL, + cpu, 0, 0, (unsigned)physaddr); + } else { + m8820x_cmmu_set(CMMU_SAR, (unsigned)physaddr, 0, + cpu, 0, 0, 0); + m8820x_cmmu_set(CMMU_SCR, CMMU_FLUSH_CACHE_CBI_SEGMENT, 0, + cpu, 0, 0, 0); + } +#else + m8820x_cmmu_set(CMMU_SCR, CMMU_FLUSH_CACHE_CBI_ALL, 0, + cpu, 0, 0, 0); +#endif /* !BROKEN_MMU_MASK */ + + m8820x_cmmu_wait(cpu); + + CMMU_UNLOCK; + splx(s); +} + +/* + * flush Instruction caches + */ +void +m8820x_cmmu_flush_inst_cache(int cpu, paddr_t physaddr, psize_t size) +{ + int s = splhigh(); + CMMU_LOCK; + +#if !defined(BROKEN_MMU_MASK) + if (size > NBSG) { + m8820x_cmmu_set(CMMU_SCR, CMMU_FLUSH_CACHE_CBI_ALL, MODE_VAL, + cpu, INST_CMMU, 0, 0); + } else if (size <= MC88200_CACHE_LINE) { + m8820x_cmmu_set(CMMU_SAR, (unsigned)physaddr, + MODE_VAL | ADDR_VAL, cpu, INST_CMMU, 0, (unsigned)physaddr); + m8820x_cmmu_set(CMMU_SCR, CMMU_FLUSH_CACHE_CBI_LINE, + MODE_VAL | ADDR_VAL, cpu, INST_CMMU, 0, (unsigned)physaddr); + } else if (size <= NBPG) { + m8820x_cmmu_set(CMMU_SAR, (unsigned)physaddr, + MODE_VAL | ADDR_VAL, cpu, INST_CMMU, 0, (unsigned)physaddr); + m8820x_cmmu_set(CMMU_SCR, CMMU_FLUSH_CACHE_CBI_PAGE, + MODE_VAL | ADDR_VAL, cpu, INST_CMMU, 0, (unsigned)physaddr); + } else { + m8820x_cmmu_set(CMMU_SAR, (unsigned)physaddr, + MODE_VAL, cpu, INST_CMMU, 0, 0); + m8820x_cmmu_set(CMMU_SCR, CMMU_FLUSH_CACHE_CBI_SEGMENT, + MODE_VAL, cpu, INST_CMMU, 0, 0); + } +#else + m8820x_cmmu_set(CMMU_SCR, CMMU_FLUSH_CACHE_CBI_ALL, MODE_VAL, + cpu, INST_CMMU, 0, 0); +#endif /* !BROKEN_MMU_MASK */ + + m8820x_cmmu_wait(cpu); + + CMMU_UNLOCK; + splx(s); +} + +void +m8820x_cmmu_flush_data_cache(int cpu, paddr_t physaddr, psize_t size) +{ + int s = splhigh(); + CMMU_LOCK; + +#if !defined(BROKEN_MMU_MASK) + if (size > NBSG) { + m8820x_cmmu_set(CMMU_SCR, CMMU_FLUSH_CACHE_CBI_ALL, MODE_VAL, + cpu, DATA_CMMU, 0, 0); + } else if (size <= MC88200_CACHE_LINE) { + m8820x_cmmu_set(CMMU_SAR, (unsigned)physaddr, + MODE_VAL | ADDR_VAL, cpu, DATA_CMMU, 0, (unsigned)physaddr); + m8820x_cmmu_set(CMMU_SCR, CMMU_FLUSH_CACHE_CBI_LINE, + MODE_VAL | ADDR_VAL, cpu, DATA_CMMU, 0, (unsigned)physaddr); + } else if (size <= NBPG) { + m8820x_cmmu_set(CMMU_SAR, (unsigned)physaddr, + MODE_VAL | ADDR_VAL, cpu, DATA_CMMU, 0, (unsigned)physaddr); + m8820x_cmmu_set(CMMU_SCR, CMMU_FLUSH_CACHE_CBI_PAGE, + MODE_VAL | ADDR_VAL, cpu, DATA_CMMU, 0, (unsigned)physaddr); + } else { + m8820x_cmmu_set(CMMU_SAR, (unsigned)physaddr, + MODE_VAL, cpu, DATA_CMMU, 0, 0); + m8820x_cmmu_set(CMMU_SCR, CMMU_FLUSH_CACHE_CBI_SEGMENT, + MODE_VAL, cpu, DATA_CMMU, 0, 0); + } +#else + m8820x_cmmu_set(CMMU_SCR, CMMU_FLUSH_CACHE_CBI_ALL, MODE_VAL, + cpu, DATA_CMMU, 0, 0); +#endif /* !BROKEN_MMU_MASK */ + + m8820x_cmmu_wait(cpu); + + CMMU_UNLOCK; + splx(s); +} + +/* + * sync dcache (and icache too) + */ +void +m8820x_cmmu_sync_cache(paddr_t physaddr, psize_t size) +{ + int s = splhigh(); + int cpu = cpu_number(); + + CMMU_LOCK; + +#if !defined(BROKEN_MMU_MASK) + if (size > NBSG) { + m8820x_cmmu_set(CMMU_SCR, CMMU_FLUSH_CACHE_CB_ALL, MODE_VAL, + cpu, DATA_CMMU, 0, 0); + m8820x_cmmu_set(CMMU_SCR, CMMU_FLUSH_CACHE_CB_ALL, MODE_VAL, + cpu, INST_CMMU, 0, 0); + } else if (size <= MC88200_CACHE_LINE) { + m8820x_cmmu_set(CMMU_SAR, (unsigned)physaddr, + MODE_VAL | ADDR_VAL, cpu, INST_CMMU, 0, (unsigned)physaddr); + m8820x_cmmu_set(CMMU_SAR, CMMU_FLUSH_CACHE_CB_LINE, + MODE_VAL, cpu, INST_CMMU, 0, 0); + m8820x_cmmu_set(CMMU_SAR, (unsigned)physaddr, + MODE_VAL | ADDR_VAL, cpu, DATA_CMMU, 0, (unsigned)physaddr); + m8820x_cmmu_set(CMMU_SAR, CMMU_FLUSH_CACHE_CB_LINE, + MODE_VAL, cpu, DATA_CMMU, 0, 0); + } else if (size <= NBPG) { + m8820x_cmmu_set(CMMU_SAR, (unsigned)physaddr, + MODE_VAL | ADDR_VAL, cpu, INST_CMMU, 0, (unsigned)physaddr); + m8820x_cmmu_set(CMMU_SAR, CMMU_FLUSH_CACHE_CB_PAGE, + MODE_VAL, cpu, INST_CMMU, 0, 0); + m8820x_cmmu_set(CMMU_SAR, (unsigned)physaddr, + MODE_VAL | ADDR_VAL, cpu, DATA_CMMU, 0, (unsigned)physaddr); + m8820x_cmmu_set(CMMU_SAR, CMMU_FLUSH_CACHE_CB_PAGE, + MODE_VAL, cpu, DATA_CMMU, 0, 0); + } else { + m8820x_cmmu_set(CMMU_SAR, (unsigned)physaddr, + MODE_VAL | ADDR_VAL, cpu, INST_CMMU, 0, (unsigned)physaddr); + m8820x_cmmu_set(CMMU_SAR, CMMU_FLUSH_CACHE_CB_SEGMENT, + MODE_VAL, cpu, INST_CMMU, 0, 0); + m8820x_cmmu_set(CMMU_SAR, (unsigned)physaddr, + MODE_VAL | ADDR_VAL, cpu, DATA_CMMU, 0, (unsigned)physaddr); + m8820x_cmmu_set(CMMU_SAR, CMMU_FLUSH_CACHE_CB_SEGMENT, + MODE_VAL, cpu, DATA_CMMU, 0, 0); + } +#else + m8820x_cmmu_set(CMMU_SCR, CMMU_FLUSH_CACHE_CB_ALL, MODE_VAL, + cpu, DATA_CMMU, 0, 0); + m8820x_cmmu_set(CMMU_SCR, CMMU_FLUSH_CACHE_CB_ALL, MODE_VAL, + cpu, INST_CMMU, 0, 0); +#endif /* !BROKEN_MMU_MASK */ + + m8820x_cmmu_wait(cpu); + + CMMU_UNLOCK; + splx(s); +} + +void +m8820x_cmmu_sync_inval_cache(paddr_t physaddr, psize_t size) +{ + int s = splhigh(); + int cpu = cpu_number(); + + CMMU_LOCK; + +#if !defined(BROKEN_MMU_MASK) + if (size > NBSG) { + m8820x_cmmu_set(CMMU_SCR, CMMU_FLUSH_CACHE_CBI_ALL, MODE_VAL, + cpu, DATA_CMMU, 0, 0); + m8820x_cmmu_set(CMMU_SCR, CMMU_FLUSH_CACHE_CBI_ALL, MODE_VAL, + cpu, INST_CMMU, 0, 0); + } else if (size <= MC88200_CACHE_LINE) { + m8820x_cmmu_set(CMMU_SAR, (unsigned)physaddr, + MODE_VAL | ADDR_VAL, cpu, INST_CMMU, 0, (unsigned)physaddr); + m8820x_cmmu_set(CMMU_SAR, CMMU_FLUSH_CACHE_CBI_LINE, + MODE_VAL, cpu, INST_CMMU, 0, 0); + m8820x_cmmu_set(CMMU_SAR, (unsigned)physaddr, + MODE_VAL | ADDR_VAL, cpu, DATA_CMMU, 0, (unsigned)physaddr); + m8820x_cmmu_set(CMMU_SAR, CMMU_FLUSH_CACHE_CBI_LINE, + MODE_VAL, cpu, DATA_CMMU, 0, 0); + } else if (size <= NBPG) { + m8820x_cmmu_set(CMMU_SAR, (unsigned)physaddr, + MODE_VAL | ADDR_VAL, cpu, INST_CMMU, 0, (unsigned)physaddr); + m8820x_cmmu_set(CMMU_SAR, CMMU_FLUSH_CACHE_CBI_PAGE, + MODE_VAL, cpu, INST_CMMU, 0, 0); + m8820x_cmmu_set(CMMU_SAR, (unsigned)physaddr, + MODE_VAL | ADDR_VAL, cpu, DATA_CMMU, 0, (unsigned)physaddr); + m8820x_cmmu_set(CMMU_SAR, CMMU_FLUSH_CACHE_CBI_PAGE, + MODE_VAL, cpu, DATA_CMMU, 0, 0); + } else { + m8820x_cmmu_set(CMMU_SAR, (unsigned)physaddr, + MODE_VAL | ADDR_VAL, cpu, INST_CMMU, 0, (unsigned)physaddr); + m8820x_cmmu_set(CMMU_SAR, CMMU_FLUSH_CACHE_CBI_SEGMENT, + MODE_VAL, cpu, INST_CMMU, 0, 0); + m8820x_cmmu_set(CMMU_SAR, (unsigned)physaddr, + MODE_VAL | ADDR_VAL, cpu, DATA_CMMU, 0, (unsigned)physaddr); + m8820x_cmmu_set(CMMU_SAR, CMMU_FLUSH_CACHE_CBI_SEGMENT, + MODE_VAL, cpu, DATA_CMMU, 0, 0); + } +#else + m8820x_cmmu_set(CMMU_SCR, CMMU_FLUSH_CACHE_CBI_ALL, MODE_VAL, + cpu, DATA_CMMU, 0, 0); + m8820x_cmmu_set(CMMU_SCR, CMMU_FLUSH_CACHE_CBI_ALL, MODE_VAL, + cpu, INST_CMMU, 0, 0); +#endif /* !BROKEN_MMU_MASK */ + + m8820x_cmmu_wait(cpu); + + CMMU_UNLOCK; + splx(s); +} + +void +m8820x_cmmu_inval_cache(paddr_t physaddr, psize_t size) +{ + int s = splhigh(); + int cpu = cpu_number(); + + CMMU_LOCK; + +#if !defined(BROKEN_MMU_MASK) + if (size > NBSG) { + m8820x_cmmu_set(CMMU_SCR, CMMU_FLUSH_CACHE_INV_ALL, MODE_VAL, + cpu, DATA_CMMU, 0, 0); + m8820x_cmmu_set(CMMU_SCR, CMMU_FLUSH_CACHE_INV_ALL, MODE_VAL, + cpu, INST_CMMU, 0, 0); + } else if (size <= MC88200_CACHE_LINE) { + m8820x_cmmu_set(CMMU_SAR, (unsigned)physaddr, + MODE_VAL | ADDR_VAL, cpu, INST_CMMU, 0, (unsigned)physaddr); + m8820x_cmmu_set(CMMU_SAR, CMMU_FLUSH_CACHE_INV_LINE, + MODE_VAL, cpu, INST_CMMU, 0, 0); + m8820x_cmmu_set(CMMU_SAR, (unsigned)physaddr, + MODE_VAL | ADDR_VAL, cpu, DATA_CMMU, 0, (unsigned)physaddr); + m8820x_cmmu_set(CMMU_SAR, CMMU_FLUSH_CACHE_INV_LINE, + MODE_VAL, cpu, DATA_CMMU, 0, 0); + } else if (size <= NBPG) { + m8820x_cmmu_set(CMMU_SAR, (unsigned)physaddr, + MODE_VAL | ADDR_VAL, cpu, INST_CMMU, 0, (unsigned)physaddr); + m8820x_cmmu_set(CMMU_SAR, CMMU_FLUSH_CACHE_INV_PAGE, + MODE_VAL, cpu, INST_CMMU, 0, 0); + m8820x_cmmu_set(CMMU_SAR, (unsigned)physaddr, + MODE_VAL | ADDR_VAL, cpu, DATA_CMMU, 0, (unsigned)physaddr); + m8820x_cmmu_set(CMMU_SAR, CMMU_FLUSH_CACHE_INV_PAGE, + MODE_VAL, cpu, DATA_CMMU, 0, 0); + } else { + m8820x_cmmu_set(CMMU_SAR, (unsigned)physaddr, + MODE_VAL | ADDR_VAL, cpu, INST_CMMU, 0, (unsigned)physaddr); + m8820x_cmmu_set(CMMU_SAR, CMMU_FLUSH_CACHE_INV_SEGMENT, + MODE_VAL, cpu, INST_CMMU, 0, 0); + m8820x_cmmu_set(CMMU_SAR, (unsigned)physaddr, + MODE_VAL | ADDR_VAL, cpu, DATA_CMMU, 0, (unsigned)physaddr); + m8820x_cmmu_set(CMMU_SAR, CMMU_FLUSH_CACHE_INV_SEGMENT, + MODE_VAL, cpu, DATA_CMMU, 0, 0); + } +#else + m8820x_cmmu_set(CMMU_SCR, CMMU_FLUSH_CACHE_INV_ALL, MODE_VAL, + cpu, DATA_CMMU, 0, 0); + m8820x_cmmu_set(CMMU_SCR, CMMU_FLUSH_CACHE_INV_ALL, MODE_VAL, + cpu, INST_CMMU, 0, 0); +#endif /* !BROKEN_MMU_MASK */ + + m8820x_cmmu_wait(cpu); + + CMMU_UNLOCK; + splx(s); +} + +void +m8820x_dma_cachectl(vaddr_t va, vsize_t size, int op) +{ + paddr_t pa; +#if !defined(BROKEN_MMU_MASK) + psize_t count; + + while (size != 0) { + count = NBPG - (va & PGOFSET); + + if (size < count) + count = size; + + if (pmap_extract(pmap_kernel(), va, &pa) != FALSE) { + switch (op) { + case DMA_CACHE_SYNC: + m8820x_cmmu_sync_cache(pa, count); + break; + case DMA_CACHE_SYNC_INVAL: + m8820x_cmmu_sync_inval_cache(pa, count); + break; + default: + m8820x_cmmu_inval_cache(pa, count); + break; + } + } + + va += count; + size -= count; + } +#else + /* XXX This assumes the space is also physically contiguous */ + if (pmap_extract(pmap_kernel(), va, &pa) != FALSE) { + switch (op) { + case DMA_CACHE_SYNC: + m8820x_cmmu_sync_cache(pa, size); + break; + case DMA_CACHE_SYNC_INVAL: + m8820x_cmmu_sync_inval_cache(pa, size); + break; + default: + m8820x_cmmu_inval_cache(pa, size); + break; + } + } +#endif /* !BROKEN_MMU_MASK */ +} + +#ifdef DDB +union ssr { + unsigned bits; + struct { + unsigned :16, + ce:1, + be:1, + :4, + wt:1, + sp:1, + g:1, + ci:1, + :1, + m:1, + u:1, + wp:1, + bh:1, + v:1; + } field; +}; + +union cssp { + unsigned bits; + struct { + unsigned : 2, + l: 6, + d3: 1, + d2: 1, + d1: 1, + d0: 1, + vv3: 2, + vv2: 2, + vv1: 2, + vv0: 2, + :12; + } field; +}; + +union batcu { + unsigned bits; + struct { /* block address translation register */ + unsigned int + lba:13, /* logical block address */ + pba:13, /* physical block address */ + s:1, /* supervisor */ + wt:4, /* write through */ + g:1, /* global */ + ci:1, /* cache inhibit */ + wp:1, /* write protect */ + v:1; /* valid */ + } field; +}; + + #define VV_EX_UNMOD 0 + #define VV_EX_MOD 1 + #define VV_SHARED_UNMOD 2 + #define VV_INVALID 3 + + #define D(UNION, LINE) \ + ((LINE) == 3 ? (UNION).field.d3 : \ + ((LINE) == 2 ? (UNION).field.d2 : \ + ((LINE) == 1 ? (UNION).field.d1 : \ + ((LINE) == 0 ? (UNION).field.d0 : ~0)))) + #define VV(UNION, LINE) \ + ((LINE) == 3 ? (UNION).field.vv3 : \ + ((LINE) == 2 ? (UNION).field.vv2 : \ + ((LINE) == 1 ? (UNION).field.vv1 : \ + ((LINE) == 0 ? (UNION).field.vv0 : ~0)))) + + #undef VEQR_ADDR + #define VEQR_ADDR 0 +/* + * Show (for debugging) how the given CMMU translates the given ADDRESS. + * If cmmu == -1, the data cmmu for the current cpu is used. + */ +void +m8820x_cmmu_show_translation(address, supervisor_flag, verbose_flag, cmmu_num) + unsigned address, supervisor_flag, verbose_flag; + int cmmu_num; +{ + /* + * A virtual address is split into three fields. Two are used as + * indicies into tables (segment and page), and one is an offset into + * a page of memory. + */ + union { + unsigned bits; + struct { + unsigned segment_table_index:SDT_BITS, + page_table_index:PDT_BITS, + page_offset:PG_BITS; + } field; + } virtual_address; + u_int32_t value; + + if (verbose_flag) + db_printf("-------------------------------------------\n"); + + + + /****** ACCESS PROPER CMMU or THREAD ***********/ + if (cmmu_num == -1) { + int cpu = cpu_number(); + if (cpu_cmmu[cpu].pair[DATA_CMMU] == 0) { + db_printf("ack! can't figure my own data cmmu number.\n"); + return; + } + cmmu_num = cpu_cmmu[cpu].pair[DATA_CMMU] - m8820x_cmmu; + if (verbose_flag) + db_printf("The data cmmu for cpu#%d is cmmu#%d.\n", + 0, cmmu_num); + } else if (cmmu_num < 0 || cmmu_num >= MAX_CMMUS) { + db_printf("invalid cpu number [%d]... must be in range [0..%d]\n", + cmmu_num, MAX_CMMUS - 1); + + return; + } + + if (m8820x_cmmu[cmmu_num].cmmu_alive == CMMU_DEAD) { + db_printf("warning: cmmu %d is not alive.\n", cmmu_num); +#if 0 + return; +#endif + } + + if (!verbose_flag) { + if (!(m8820x_cmmu[cmmu_num].cmmu_regs[CMMU_SCTR] & CMMU_SCTR_SE)) + db_printf("WARNING: snooping not enabled for CMMU#%d.\n", + cmmu_num); + } else { + int i; + for (i = 0; i < MAX_CMMUS; i++) + if ((i == cmmu_num || m8820x_cmmu[i].cmmu_alive != CMMU_DEAD) && + (verbose_flag > 1 || !(m8820x_cmmu[i].cmmu_regs[CMMU_SCTR] & CMMU_SCTR_SE))) { + db_printf("CMMU#%d (cpu %d %s) snooping %s\n", i, + m8820x_cmmu[i].cmmu_cpu, m8820x_cmmu[i].cmmu_type ? "data" : "inst", + (m8820x_cmmu[i].cmmu_regs[CMMU_SCTR] & CMMU_SCTR_SE) ? "on":"OFF"); + } + } + + if (supervisor_flag) + value = m8820x_cmmu[cmmu_num].cmmu_regs[CMMU_SAPR]; + else + value = m8820x_cmmu[cmmu_num].cmmu_regs[CMMU_UAPR]; + +#ifdef SHADOW_BATC + { + int i; + union batcu batc; + for (i = 0; i < 8; i++) { + batc.bits = m8820x_cmmu[cmmu_num].batc[i]; + if (batc.field.v == 0) { + if (verbose_flag>1) + db_printf("cmmu #%d batc[%d] invalid.\n", cmmu_num, i); + } else { + db_printf("cmmu#%d batc[%d] v%08x p%08x", cmmu_num, i, + batc.field.lba << 18, batc.field.pba); + if (batc.field.s) db_printf(", supervisor"); + if (batc.field.wt) db_printf(", wt.th"); + if (batc.field.g) db_printf(", global"); + if (batc.field.ci) db_printf(", cache inhibit"); + if (batc.field.wp) db_printf(", write protect"); + } + } + } +#endif /* SHADOW_BATC */ + + /******* SEE WHAT A PROBE SAYS (if not a thread) ***********/ + { + union ssr ssr; + unsigned *volatile cmmu_regs = m8820x_cmmu[cmmu_num].cmmu_regs; + cmmu_regs[CMMU_SAR] = address; + cmmu_regs[CMMU_SCR] = supervisor_flag ? CMMU_PROBE_SUPER : CMMU_PROBE_USER; + ssr.bits = cmmu_regs[CMMU_SSR]; + if (verbose_flag > 1) + db_printf("probe of 0x%08x returns ssr=0x%08x\n", + address, ssr.bits); + if (ssr.field.v) + db_printf("PROBE of 0x%08x returns phys=0x%x", + address, cmmu_regs[CMMU_SAR]); + else + db_printf("PROBE fault at 0x%x", cmmu_regs[CMMU_PFAR]); + if (ssr.field.ce) db_printf(", copyback err"); + if (ssr.field.be) db_printf(", bus err"); + if (ssr.field.wt) db_printf(", writethrough"); + if (ssr.field.sp) db_printf(", sup prot"); + if (ssr.field.g) db_printf(", global"); + if (ssr.field.ci) db_printf(", cache inhibit"); + if (ssr.field.m) db_printf(", modified"); + if (ssr.field.u) db_printf(", used"); + if (ssr.field.wp) db_printf(", write prot"); + if (ssr.field.bh) db_printf(", BATC"); + db_printf(".\n"); + } + + /******* INTERPRET AREA DESCRIPTOR *********/ + { + if (verbose_flag > 1) { + db_printf("CMMU#%d", cmmu_num); + db_printf(" %cAPR is 0x%08x\n", + supervisor_flag ? 'S' : 'U', value); + } + db_printf("CMMU#%d", cmmu_num); + db_printf(" %cAPR: SegTbl: 0x%x000p", + supervisor_flag ? 'S' : 'U', PG_PFNUM(value)); + if (value & CACHE_WT) + db_printf(", WTHRU"); + if (value & CACHE_GLOBAL) + db_printf(", GLOBAL"); + if (value & CACHE_INH) + db_printf(", INHIBIT"); + if (value & APR_V) + db_printf(", VALID"); + db_printf("\n"); + + /* if not valid, done now */ + if ((value & APR_V) == 0) { + db_printf("<would report an error, valid bit not set>\n"); + + return; + } + + value &= PG_FRAME; /* now point to seg page */ + } + + /* translate value from physical to virtual */ + if (verbose_flag) + db_printf("[%x physical is %x virtual]\n", value, value + VEQR_ADDR); + value += VEQR_ADDR; + + virtual_address.bits = address; + + /****** ACCESS SEGMENT TABLE AND INTERPRET SEGMENT DESCRIPTOR *******/ + { + sdt_entry_t sdt; + if (verbose_flag) + db_printf("will follow to entry %d of page at 0x%x...\n", + virtual_address.field.segment_table_index, value); + value |= virtual_address.field.segment_table_index * + sizeof(sdt_entry_t); + + if (badwordaddr((vaddr_t)value)) { + db_printf("ERROR: unable to access page at 0x%08x.\n", value); + return; + } + + sdt = *(sdt_entry_t *)value; + if (verbose_flag > 1) + db_printf("SEG DESC @0x%x is 0x%08x\n", value, sdt); + db_printf("SEG DESC @0x%x: PgTbl: 0x%x000", + value, PG_PFNUM(sdt)); + if (sdt & CACHE_WT) db_printf(", WTHRU"); + else db_printf(", !wthru"); + if (sdt & SG_SO) db_printf(", S-PROT"); + else db_printf(", UserOk"); + if (sdt & CACHE_GLOBAL) db_printf(", GLOBAL"); + else db_printf(", !global"); + if (sdt & CACHE_INH) db_printf(", $INHIBIT"); + else db_printf(", $ok"); + if (sdt & SG_PROT) db_printf(", W-PROT"); + else db_printf(", WriteOk"); + if (sdt & SG_V) db_printf(", VALID"); + else db_printf(", !valid"); + db_printf(".\n"); + + /* if not valid, done now */ + if (!(sdt & SG_V)) { + db_printf("<would report an error, STD entry not valid>\n"); + return; + } + + value = ptoa(PG_PFNUM(sdt)); + } + + /* translate value from physical to virtual */ + if (verbose_flag) + db_printf("[%x physical is %x virtual]\n", value, value + VEQR_ADDR); + value += VEQR_ADDR; + + /******* PAGE TABLE *********/ + { + pt_entry_t pte; + if (verbose_flag) + db_printf("will follow to entry %d of page at 0x%x...\n", + virtual_address.field.page_table_index, value); + value |= virtual_address.field.page_table_index * + sizeof(pt_entry_t); + + if (badwordaddr((vaddr_t)value)) { + db_printf("error: unable to access page at 0x%08x.\n", value); + + return; + } + + pte = *(pt_entry_t *)value; + if (verbose_flag > 1) + db_printf("PAGE DESC @0x%x is 0x%08x.\n", value, pte); + db_printf("PAGE DESC @0x%x: page @%x000", + value, PG_PFNUM(pte)); + if (pte & PG_W) db_printf(", WIRE"); + else db_printf(", !wire"); + if (pte & CACHE_WT) db_printf(", WTHRU"); + else db_printf(", !wthru"); + if (pte & PG_SO) db_printf(", S-PROT"); + else db_printf(", UserOk"); + if (pte & CACHE_GLOBAL) db_printf(", GLOBAL"); + else db_printf(", !global"); + if (pte & CACHE_INH) db_printf(", $INHIBIT"); + else db_printf(", $ok"); + if (pte & PG_M) db_printf(", MOD"); + else db_printf(", !mod"); + if (pte & PG_U) db_printf(", USED"); + else db_printf(", !used"); + if (pte & PG_PROT) db_printf(", W-PROT"); + else db_printf(", WriteOk"); + if (pte & PG_V) db_printf(", VALID"); + else db_printf(", !valid"); + db_printf(".\n"); + + /* if not valid, done now */ + if (!(pte & PG_V)) { + db_printf("<would report an error, PTE entry not valid>\n"); + return; + } + + value = ptoa(PG_PFNUM(pte)); + if (verbose_flag) + db_printf("will follow to byte %d of page at 0x%x...\n", + virtual_address.field.page_offset, value); + value |= virtual_address.field.page_offset; + + if (badwordaddr((vaddr_t)value)) { + db_printf("error: unable to access page at 0x%08x.\n", value); + return; + } + } + + /* translate value from physical to virtual */ + if (verbose_flag) + db_printf("[%x physical is %x virtual]\n", value, value + VEQR_ADDR); + value += VEQR_ADDR; + + db_printf("WORD at 0x%x is 0x%08x.\n", value, *(unsigned *)value); + +} +#endif /* DDB */ diff --git a/sys/arch/luna88k/luna88k/machdep.c b/sys/arch/luna88k/luna88k/machdep.c new file mode 100644 index 00000000000..677d1f15a1b --- /dev/null +++ b/sys/arch/luna88k/luna88k/machdep.c @@ -0,0 +1,1840 @@ +/* $OpenBSD: machdep.c,v 1.1 2004/04/21 15:24:08 aoyama Exp $ */ +/* + * Copyright (c) 1998, 1999, 2000, 2001 Steve Murphree, Jr. + * Copyright (c) 1996 Nivas Madhur + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Nivas Madhur. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/* + * Mach Operating System + * Copyright (c) 1993-1991 Carnegie Mellon University + * Copyright (c) 1991 OMRON Corporation + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/signalvar.h> +#include <sys/kernel.h> +#include <sys/proc.h> +#include <sys/user.h> +#include <sys/buf.h> +#include <sys/reboot.h> +#include <sys/conf.h> +#include <sys/malloc.h> +#include <sys/mount.h> +#include <sys/msgbuf.h> +#include <sys/syscallargs.h> +#ifdef SYSVMSG +#include <sys/msg.h> +#endif +#include <sys/exec.h> +#include <sys/sysctl.h> +#include <sys/errno.h> +#include <sys/extent.h> +#include <sys/core.h> +#include <sys/kcore.h> + +#include <net/netisr.h> + +#include <machine/asm_macro.h> /* enable/disable interrupts */ +#include <machine/mmu.h> +#include <machine/board.h> +#include <machine/cmmu.h> /* CMMU stuff */ +#include <machine/cpu.h> +#include <machine/cpu_number.h> +#include <machine/kcore.h> +#include <machine/locore.h> +#include <machine/reg.h> +#include <machine/trap.h> +#include <machine/m88100.h> /* DMT_VALID */ + +#include <luna88k/luna88k/isr.h> + +#include <dev/cons.h> + +#include <uvm/uvm_extern.h> + +#include "ksyms.h" +#if DDB +#include <machine/db_machdep.h> +#include <ddb/db_extern.h> +#include <ddb/db_interface.h> +#include <ddb/db_output.h> /* db_printf() */ +#endif /* DDB */ + +#if DDB +#define DEBUG_MSG db_printf +#else +#define DEBUG_MSG printf +#endif /* DDB */ + +vaddr_t interrupt_stack[MAX_CPUS]; + +/* machine dependent function pointers. */ +struct md_p md; + +/* prototypes */ +void regdump(struct trapframe *f); +void dumpsys(void); +void consinit(void); +vaddr_t size_memory(void); +int getcpuspeed(void); +void identifycpu(void); +void save_u_area(struct proc *, vaddr_t); +void load_u_area(struct proc *); +void dumpconf(void); +void luna88k_ext_int(u_int v, struct trapframe *eframe); +void powerdown(void); + +/* + * *int_mask_reg[CPU] + * Points to the hardware interrupt status register for each CPU. + */ +unsigned int *volatile int_mask_reg[MAX_CPUS] = { + (unsigned int *)INT_ST_MASK0, + (unsigned int *)INT_ST_MASK1, + (unsigned int *)INT_ST_MASK2, + (unsigned int *)INT_ST_MASK3 +}; + +/* + * *clock_reg[CPU] + */ +unsigned int *volatile clock_reg[MAX_CPUS] = { + (unsigned int *)OBIO_CLOCK0, + (unsigned int *)OBIO_CLOCK1, + (unsigned int *)OBIO_CLOCK2, + (unsigned int *)OBIO_CLOCK3 +}; + +volatile vaddr_t obiova; + +int ssir; +int want_ast; +int want_resched; + +int physmem; /* available physical memory, in pages */ +int longformat = 1; /* for regdump() */ +/* + * safepri is a safe priority for sleep to set for a spin-wait + * during autoconfiguration or after a panic. + */ +int safepri = IPL_NONE; + +struct vm_map *exec_map = NULL; +struct vm_map *phys_map = NULL; + +/* + * iomap stuff is for managing chunks of virtual address space that + * can be allocated to IO devices. + * VMEbus drivers use this at this now. Only on-board IO devices' addresses + * are mapped so that pa == va. XXX smurph. + */ + +vaddr_t iomapbase; + +struct extent *iomap_extent; +struct vm_map *iomap_map; + +/* + * Declare these as initialized data so we can patch them. + */ +#ifdef NBUF +int nbuf = NBUF; +#else +int nbuf = 0; +#endif + +#ifndef BUFCACHEPERCENT +#define BUFCACHEPERCENT 5 +#endif + +#ifdef BUFPAGES +int bufpages = BUFPAGES; +#else +int bufpages = 0; +#endif +int bufcachepercent = BUFCACHEPERCENT; + +caddr_t allocsys(caddr_t); + +/* + * Info for CTL_HW + */ +char machine[] = MACHINE; /* cpu "architecture" */ +char cpu_model[120]; + +#if defined(DDB) || NKSYMS > 0 +extern char *esym; +#endif + +int machtype = LUNA_88K2; /* XXX: aoyama */ +int cputyp = CPU_88100; /* XXX: aoyama */ +int boothowto = RB_ASKNAME; /* XXX: should be set in boot loader and locore.S */ +int bootdev; /* XXX: should be set in boot loader and locore.S */ +int cpuspeed; +double cycles_per_microsecond; /* used in locore.S:delay() */ +int sysconsole = 1; /* 0 = ttya, 1 = keyboard/mouse, used in dev/sio.c */ +u_int16_t dipswitch = 0; /* set in locore.S */ +int hwplanemask; /* set in luna88k_bootstrap() */ + +int netisr; + +extern char *etext; +extern char *edata; +extern char *end; +extern struct consdev syscons; /* in dev/siotty.c */ + +extern void greeting(void); /* in dev/lcd.c */ +extern void syscnattach(int); /* in dev/siotty.c */ +extern int omfb_cnattach(void); /* in dev/lunafb.c */ +extern void ws_cnattach(void); /* in dev/lunaws.c */ + +vaddr_t first_addr = 0; +vaddr_t last_addr = 0; + +vaddr_t avail_start, avail_end; +vaddr_t virtual_avail, virtual_end; + +extern struct pcb *curpcb; +extern struct user *proc0paddr; + +/* + * This is to fake out the console routines, while booting. + * We could use directly the romtty console, but we want to be able to + * configure a kernel without romtty since we do not necessarily need a + * full-blown console driver. + */ +void romttycnprobe(struct consdev *); +void romttycninit(struct consdev *); +void romttycnputc(dev_t, int); +int romttycngetc(dev_t); +extern void nullcnpollc(dev_t, int); + +struct consdev romttycons = { + NULL, + NULL, + romttycngetc, + romttycnputc, + nullcnpollc, + NULL, + makedev(14,0), + CN_NORMAL, +}; + +/* + * Early console initialization: called early on from main, before vm init. + */ +void +consinit() +{ +#ifdef ROM_CONSOLE + extern struct consdev *cn_tab; +#endif + /* + * Initialize the console before we print anything out. + */ +#ifdef ROM_CONSOLE + cn_tab = &romttycons; + /* cninit(); */ +#else /* from NetBSD/luna68k */ + if (sysconsole == 0) { + syscnattach(0); + } else { + omfb_cnattach(); + ws_cnattach(); + } + /* cninit(); */ /* XXX: this should be later? */ +#endif + +#if defined(DDB) + db_machine_init(); + ddb_init(); + if (boothowto & RB_KDB) + Debugger(); +#endif +} + +/* + * Figure out how much real memory is available. + * Start looking from the megabyte after the end of the kernel data, + * until we find non-memory. + */ +vaddr_t +size_memory() +{ + unsigned int *volatile look; + unsigned int *max; +#if 0 + extern char *end; +#endif +#define PATTERN 0x5a5a5a5a +#define STRIDE (4*1024) /* 4k at a time */ +#define Roundup(value, stride) (((unsigned)(value) + (stride) - 1) & ~((stride)-1)) + /* + * count it up. + */ + max = (void *)MAXPHYSMEM; +#if 0 + for (look = (void *)Roundup(end, STRIDE); look < max; +#else + for (look = (void *)first_addr; look < max; +#endif + look = (int *)((unsigned)look + STRIDE)) { + unsigned save; + + /* if can't access, we've reached the end */ + if (badwordaddr((vaddr_t)look)) { +#if defined(DEBUG) + printf("%x\n", look); +#endif + look = (int *)((int)look - STRIDE); + break; + } + + /* + * If we write a value, we expect to read the same value back. + * We'll do this twice, the 2nd time with the opposite bit + * pattern from the first, to make sure we check all bits. + */ + save = *look; + if (*look = PATTERN, *look != PATTERN) + break; + if (*look = ~PATTERN, *look != ~PATTERN) + break; + *look = save; + } + + return (trunc_page((unsigned)look)); +} + +int +getcpuspeed() +{ + double clock_mhz; + + switch(machtype) { + case LUNA_88K: + clock_mhz = 25.0; + break; + case LUNA_88K2: + clock_mhz = 33.0; + break; + default: + panic("getcpuspeed: can not determine CPU speed"); + break; + } + + cycles_per_microsecond = clock_mhz; + return (int)clock_mhz; +} + +void +identifycpu() +{ + cpuspeed = getcpuspeed(); + snprintf(cpu_model, sizeof cpu_model, + "OMRON LUNA-88K%s, %dMHz", + machtype == LUNA_88K2 ? "2" : "", cpuspeed); +} + +/* + * Setup u area ptes for u area double mapping. + */ + +void +save_u_area(struct proc *p, vaddr_t va) +{ + int i; + + for (i = 0; i < UPAGES; i++) { + p->p_md.md_upte[i] = *((pt_entry_t *)kvtopte(va)); + va += NBPG; + } +} + +void +load_u_area(struct proc *p) +{ + int i; + vaddr_t va; + pt_entry_t *t; + + for (i = 0, va = UADDR; i < UPAGES; i++) { + t = kvtopte(va); + *t = p->p_md.md_upte[i]; + va += NBPG; + } + cmmu_flush_tlb(cpu_number(), 1, UADDR, USPACE); +} + +void +cpu_startup() +{ + caddr_t v; + int sz, i; + vsize_t size; + int base, residual; + vaddr_t minaddr, maxaddr, uarea_pages; + + /* + * Initialize error message buffer (at end of core). + * avail_end was pre-decremented in mvme_bootstrap() to compensate. + */ + for (i = 0; i < btoc(MSGBUFSIZE); i++) + pmap_kenter_pa((paddr_t)msgbufp + i * NBPG, + avail_end + i * NBPG, VM_PROT_READ | VM_PROT_WRITE); + pmap_update(pmap_kernel()); + initmsgbuf((caddr_t)msgbufp, round_page(MSGBUFSIZE)); + + /* + * Good {morning,afternoon,evening,night}. + */ + printf(version); + identifycpu(); + printf("real mem = %d\n", ctob(physmem)); + + /* + * Check front DIP switch setting + */ + printf("dipsw = 0x%x\n", dipswitch); + + /* Check DIP switch 1 - 1 */ + if ((0x8000 & dipswitch) == 0) { + boothowto |= RB_SINGLE; + } + + /* Check DIP switch 1 - 3 */ + if ((0x2000 & dipswitch) == 0) { + boothowto |= RB_ASKNAME; + } + + /* Check DIP switch 1 - 4 */ + if ((0x1000 & dipswitch) == 0) { + boothowto |= RB_CONFIG; + } + + /* + * Get frame buffer depth from ROM work area. + */ + { + int depth; + + depth = *((volatile int *)0x00001114); + printf("frame buffer depth = %d\n", depth); + switch (depth) { + case 1: + hwplanemask = 0x01; + break; + case 4: + hwplanemask = 0x0f; + break; + case 8: + hwplanemask = 0xff; + break; + default: + hwplanemask = 0; /* No frame buffer */ + break; + } + } + +#if 0 /* just for test */ + /* + * Get boot arguments + */ + { + char buf[256]; + char **p = (volatile char **)0x00001120; + + strncpy(buf, *p, 256); + if (buf[255] != '\0') + buf[255] = '\0'; + + printf("boot arg: (0x%x) %s\n", *p, buf); + } +#endif + + /* + * Find out how much space we need, allocate it, + * and then give everything true virtual addresses. + */ + sz = (int)allocsys((caddr_t)0); + + if ((v = (caddr_t)uvm_km_zalloc(kernel_map, round_page(sz))) == 0) + panic("startup: no room for tables"); + if (allocsys(v) - v != sz) + panic("startup: table size inconsistency"); + + /* + * Grab UADDR virtual address + */ + uarea_pages = UADDR; + uvm_map(kernel_map, (vaddr_t *)&uarea_pages, USPACE, + NULL, UVM_UNKNOWN_OFFSET, 0, + UVM_MAPFLAG(UVM_PROT_NONE, UVM_PROT_NONE, UVM_INH_NONE, + UVM_ADV_NORMAL, 0)); + if (uarea_pages != UADDR) + panic("uarea_pages %lx: UADDR not free", uarea_pages); + + /* + * Grab the OBIO space that we hardwired in pmap_bootstrap + */ + obiova = OBIO_START; + uvm_map(kernel_map, (vaddr_t *)&obiova, OBIO_SIZE, + NULL, UVM_UNKNOWN_OFFSET, 0, + UVM_MAPFLAG(UVM_PROT_NONE, UVM_PROT_NONE, UVM_INH_NONE, + UVM_ADV_NORMAL, 0)); + if (obiova != OBIO_START) + panic("obiova %lx: OBIO not free", obiova); + + /* + * Now allocate buffers proper. They are different than the above + * in that they usually occupy more virtual memory than physical. + */ + size = MAXBSIZE * nbuf; + if (uvm_map(kernel_map, (vaddr_t *) &buffers, round_page(size), + NULL, UVM_UNKNOWN_OFFSET, 0, UVM_MAPFLAG(UVM_PROT_NONE, + UVM_PROT_NONE, UVM_INH_NONE, UVM_ADV_NORMAL, 0))) + panic("cpu_startup: cannot allocate VM for buffers"); + minaddr = (vaddr_t)buffers; + + if ((bufpages / nbuf) >= btoc(MAXBSIZE)) { + /* don't want to alloc more physical mem than needed */ + bufpages = btoc(MAXBSIZE) * nbuf; + } + base = bufpages / nbuf; + residual = bufpages % nbuf; + + for (i = 0; i < nbuf; i++) { + vsize_t curbufsize; + vaddr_t curbuf; + struct vm_page *pg; + + /* + * Each buffer has MAXBSIZE bytes of VM space allocated. Of + * that MAXBSIZE space, we allocate and map (base+1) pages + * for the first "residual" buffers, and then we allocate + * "base" pages for the rest. + */ + curbuf = (vaddr_t)buffers + (i * MAXBSIZE); + curbufsize = PAGE_SIZE * ((i < residual) ? (base+1) : base); + + while (curbufsize) { + pg = uvm_pagealloc(NULL, 0, NULL, 0); + if (pg == NULL) + panic("cpu_startup: not enough memory for " + "buffer cache"); + pmap_kenter_pa(curbuf, VM_PAGE_TO_PHYS(pg), + VM_PROT_READ | VM_PROT_WRITE); + curbuf += PAGE_SIZE; + curbufsize -= PAGE_SIZE; + } + } + pmap_update(pmap_kernel()); + + /* + * Allocate a submap for exec arguments. This map effectively + * limits the number of processes exec'ing at any time. + */ + exec_map = uvm_km_suballoc(kernel_map, &minaddr, &maxaddr, + 16 * NCARGS, VM_MAP_PAGEABLE, FALSE, NULL); + + /* + * Allocate map for physio. + */ + phys_map = uvm_km_suballoc(kernel_map, &minaddr, &maxaddr, + VM_PHYS_SIZE, 0, FALSE, NULL); + + printf("avail mem = %ld (%d pages)\n", ptoa(uvmexp.free), uvmexp.free); + printf("using %d buffers containing %d bytes of memory\n", nbuf, + bufpages * PAGE_SIZE); + + /* + * Set up buffers, so they can be used to read disk labels. + */ + bufinit(); + + /* + * Initialize the autovectored interrupt list. + */ + isrinit(); + + /* + * Configure the system. + */ + if (boothowto & RB_CONFIG) { +#ifdef BOOT_CONFIG + user_config(); +#else + printf("kernel does not support -c; continuing..\n"); +#endif + } + + /* + * Say hello to the world on LCD. + */ + greeting(); +} + +/* + * Allocate space for system data structures. We are given + * a starting virtual address and we return a final virtual + * address; along the way we set each data structure pointer. + * + * We call allocsys() with 0 to find out how much space we want, + * allocate that much and fill it with zeroes, and then call + * allocsys() again with the correct base virtual address. + */ +caddr_t +allocsys(v) + caddr_t v; +{ + +#define valloc(name, type, num) \ + v = (caddr_t)(((name) = (type *)v) + (num)) + +#ifdef SYSVMSG + valloc(msgpool, char, msginfo.msgmax); + valloc(msgmaps, struct msgmap, msginfo.msgseg); + valloc(msghdrs, struct msg, msginfo.msgtql); + valloc(msqids, struct msqid_ds, msginfo.msgmni); +#endif + + /* + * Determine how many buffers to allocate. We use 10% of the + * first 2MB of memory, and 5% of the rest, with a minimum of 16 + * buffers. We allocate 1/2 as many swap buffer headers as file + * i/o buffers. + */ + if (bufpages == 0) { + if (physmem < btoc(2 * 1024 * 1024)) + bufpages = physmem / 10; + else + bufpages = (btoc(2 * 1024 * 1024) + physmem) * + bufcachepercent / 100; + } + if (nbuf == 0) { + nbuf = bufpages; + if (nbuf < 16) + nbuf = 16; + } + + /* Restrict to at most 70% filled kvm */ + if (nbuf > + (VM_MAX_KERNEL_ADDRESS - VM_MIN_KERNEL_ADDRESS) / MAXBSIZE * 7 / 10) + nbuf = (VM_MAX_KERNEL_ADDRESS - VM_MIN_KERNEL_ADDRESS) / + MAXBSIZE * 7 / 10; + + /* More buffer pages than fits into the buffers is senseless. */ + if (bufpages > nbuf * MAXBSIZE / PAGE_SIZE) + bufpages = nbuf * MAXBSIZE / PAGE_SIZE; + + valloc(buf, struct buf, nbuf); + + return v; +} + +/* + * Set registers on exec. + * Clear all except sp and pc. + */ +void +setregs(p, pack, stack, retval) + struct proc *p; + struct exec_package *pack; + u_long stack; + int retval[2]; +{ + struct trapframe *tf = (struct trapframe *)USER_REGS(p); + + /* + * The syscall will ``return'' to snip; set it. + * argc, argv, envp are placed on the stack by copyregs. + * Point r2 to the stack. crt0 should extract envp from + * argc & argv before calling user's main. + */ +#if 0 + /* + * I don't think I need to mess with fpstate on 88k because + * we make sure the floating point pipeline is drained in + * the trap handlers. Should check on this later. XXX Nivas. + */ + + if ((fs = p->p_md.md_fpstate) != NULL) { + /* + * We hold an FPU state. If we own *the* FPU chip state + * we must get rid of it, and the only way to do that is + * to save it. In any case, get rid of our FPU state. + */ + if (p == fpproc) { + savefpstate(fs); + fpproc = NULL; + } + free((void *)fs, M_SUBPROC); + p->p_md.md_fpstate = NULL; + } +#endif /* 0 */ + bzero((caddr_t)tf, sizeof *tf); + + if (cputyp == CPU_88110) { + /* + * user mode, serialize mem, interrupts enabled, + * graphics unit, fp enabled + */ + tf->tf_epsr = PSR_SRM | PSR_SFD; + /* + * XXX disable OoO for now... + */ + tf->tf_epsr |= PSR_SER; + } else { + /* + * user mode, interrupts enabled, + * no graphics unit, fp enabled + */ + tf->tf_epsr = PSR_SFD | PSR_SFD2; + } + + /* + * We want to start executing at pack->ep_entry. The way to + * do this is force the processor to fetch from ep_entry. Set + * NIP to something bogus and invalid so that it will be a NOOP. + * And set sfip to ep_entry with valid bit on so that it will be + * fetched. mc88110 - just set exip to pack->ep_entry. + */ + if (cputyp == CPU_88110) { + tf->tf_exip = pack->ep_entry & ~3; +#ifdef DEBUG + printf("exec @ 0x%x\n", tf->tf_exip); +#endif + } else { + tf->tf_snip = pack->ep_entry & ~3; + tf->tf_sfip = (pack->ep_entry & ~3) | FIP_V; + } + tf->tf_r[2] = stack; + tf->tf_r[31] = stack; + retval[1] = 0; +} + +struct sigstate { + int ss_flags; /* which of the following are valid */ + struct trapframe ss_frame; /* original exception frame */ +}; + +/* + * WARNING: code in locore.s assumes the layout shown for sf_signo + * through sf_handler so... don't screw with them! + */ +struct sigframe { + int sf_signo; /* signo for handler */ + siginfo_t * sf_sip; + struct sigcontext * sf_scp; /* context ptr for handler */ + sig_t sf_handler; /* handler addr for u_sigc */ + struct sigcontext sf_sc; /* actual context */ + siginfo_t sf_si; +}; + +#ifdef DEBUG +int sigdebug = 0; +int sigpid = 0; + #define SDB_FOLLOW 0x01 + #define SDB_KSTACK 0x02 + #define SDB_FPSTATE 0x04 +#endif + +/* + * Send an interrupt to process. + */ +void +sendsig(catcher, sig, mask, code, type, val) + sig_t catcher; + int sig, mask; + unsigned long code; + int type; + union sigval val; +{ + struct proc *p = curproc; + struct trapframe *tf; + struct sigacts *psp = p->p_sigacts; + struct sigframe *fp; + int oonstack, fsize; + struct sigframe sf; + int addr; + + tf = p->p_md.md_tf; + oonstack = psp->ps_sigstk.ss_flags & SS_ONSTACK; + /* + * Allocate and validate space for the signal handler + * context. Note that if the stack is in data space, the + * call to grow() is a nop, and the copyout() + * will fail if the process has not already allocated + * the space with a `brk'. + */ + fsize = sizeof(struct sigframe); + if ((psp->ps_flags & SAS_ALTSTACK) && + (psp->ps_sigstk.ss_flags & SS_ONSTACK) == 0 && + (psp->ps_sigonstack & sigmask(sig))) { + fp = (struct sigframe *)(psp->ps_sigstk.ss_sp + + psp->ps_sigstk.ss_size - fsize); + psp->ps_sigstk.ss_flags |= SS_ONSTACK; + } else + fp = (struct sigframe *)(tf->tf_r[31] - fsize); + + /* make sure the frame is aligned on a 8 byte boundary */ + if (((vaddr_t)fp & 0x07) != 0) + fp = (struct sigframe *)((vaddr_t)fp & ~0x07); + + if ((unsigned)fp <= USRSTACK - ctob(p->p_vmspace->vm_ssize)) + (void)uvm_grow(p, (unsigned)fp); + +#ifdef DEBUG + if ((sigdebug & SDB_FOLLOW) || + ((sigdebug & SDB_KSTACK) && (p->p_pid == sigpid))) + printf("sendsig(%d): sig %d ssp %x usp %x scp %x\n", + p->p_pid, sig, &oonstack, fp, &fp->sf_sc); +#endif + /* + * Build the signal context to be used by sigreturn. + */ + sf.sf_signo = sig; + sf.sf_scp = &fp->sf_sc; + sf.sf_handler = catcher; + sf.sf_sc.sc_onstack = oonstack; + sf.sf_sc.sc_mask = mask; + + if (psp->ps_siginfo & sigmask(sig)) { + sf.sf_sip = &fp->sf_si; + initsiginfo(&sf.sf_si, sig, code, type, val); + } + + /* + * Copy the whole user context into signal context that we + * are building. + */ + bcopy((const void *)&tf->tf_regs, (void *)&sf.sf_sc.sc_regs, + sizeof(sf.sf_sc.sc_regs)); + + if (copyout((caddr_t)&sf, (caddr_t)fp, sizeof sf)) { + /* + * Process has trashed its stack; give it an illegal + * instruction to halt it in its tracks. + */ + sigexit(p, SIGILL); + /* NOTREACHED */ + } + /* + * Build the argument list for the signal handler. + * Signal trampoline code is at base of user stack. + */ + addr = p->p_sigcode; + if (cputyp != CPU_88110) { + /* mc88100 */ + tf->tf_snip = (addr & ~3) | NIP_V; + tf->tf_sfip = (tf->tf_snip + 4) | FIP_V; + } else { + /* mc88110 */ + tf->tf_exip = (addr & ~3); + tf->tf_enip = (tf->tf_exip + 4); + } + tf->tf_r[31] = (unsigned)fp; +#ifdef DEBUG + if ((sigdebug & SDB_FOLLOW) || + ((sigdebug & SDB_KSTACK) && p->p_pid == sigpid)) + printf("sendsig(%d): sig %d returns\n", p->p_pid, sig); +#endif +} + +/* + * System call to cleanup state after a signal + * has been taken. Reset signal mask and + * stack state from context left by sendsig (above). + * Return to previous pc and psl as specified by + * context left by sendsig. Check carefully to + * make sure that the user has not modified the + * psl to gain improper privileges or to cause + * a machine fault. + */ + +/* ARGSUSED */ +int +sys_sigreturn(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + struct sys_sigreturn_args /* { + syscallarg(struct sigcontext *) sigcntxp; + } */ *uap = v; + struct sigcontext *scp; + struct trapframe *tf; + struct sigcontext ksc; + + scp = (struct sigcontext *)SCARG(uap, sigcntxp); +#ifdef DEBUG + if (sigdebug & SDB_FOLLOW) + printf("sigreturn: pid %d, scp %x\n", p->p_pid, scp); +#endif + if (((vaddr_t)scp & 3) != 0 || + copyin((caddr_t)scp, (caddr_t)&ksc, sizeof(struct sigcontext))) + return (EINVAL); + + tf = p->p_md.md_tf; + scp = &ksc; + + /* + * this can be improved by doing + * bcopy(sc_reg to tf, sizeof sigcontext - 2 words) + * XXX nivas + */ + bcopy((const void *)&scp->sc_regs, (caddr_t)&tf->tf_regs, + sizeof(scp->sc_regs)); + + /* + * Restore the user supplied information + */ + if (scp->sc_onstack & SS_ONSTACK) + p->p_sigacts->ps_sigstk.ss_flags |= SS_ONSTACK; + else + p->p_sigacts->ps_sigstk.ss_flags &= ~SS_ONSTACK; + p->p_sigmask = scp->sc_mask & ~sigcantmask; + + return (EJUSTRETURN); +} + +__dead void +boot(howto) + int howto; +{ + /* take a snapshot before clobbering any registers */ + if (curproc && curproc->p_addr) + savectx(curpcb); + + /* If system is cold, just halt. */ + if (cold) { + /* (Unless the user explicitly asked for reboot.) */ + if ((howto & RB_USERREQ) == 0) + howto |= RB_HALT; + goto haltsys; + } + + boothowto = howto; + if ((howto & RB_NOSYNC) == 0) { + vfs_shutdown(); + /* + * If we've been adjusting the clock, the todr + * will be out of synch; adjust it now unless + * the system was sitting in ddb. + */ + if ((howto & RB_TIMEBAD) == 0) + resettodr(); + else + printf("WARNING: not updating battery clock\n"); + } + + /* Disable interrupts. */ + splhigh(); + + /* If rebooting and a dump is requested, do it. */ + if (howto & RB_DUMP) + dumpsys(); + +haltsys: + /* Run any shutdown hooks. */ + doshutdownhooks(); + + /* Luna88k supports automatic powerdown */ + if ((howto & RB_POWERDOWN) == RB_POWERDOWN) { + printf("attempting to power down...\n"); + powerdown(); + /* if failed, fall through. */ + } + + if (howto & RB_HALT) { + printf("halted\n\n"); + } else { + /* Reset all cpus, which causes reboot */ + *((volatile unsigned *)0x6d000010) = 0; + } + + for (;;); /* to keep compiler happy, and me from going crazy */ + /*NOTREACHED*/ +} + +unsigned dumpmag = 0x8fca0101; /* magic number for savecore */ +int dumpsize = 0; /* also for savecore */ +long dumplo = 0; +cpu_kcore_hdr_t cpu_kcore_hdr; + +/* + * This is called by configure to set dumplo and dumpsize. + * Dumps always skip the first PAGE_SIZE of disk space + * in case there might be a disk label stored there. + * If there is extra space, put dump at the end to + * reduce the chance that swapping trashes it. + */ +void +dumpconf() +{ + int nblks; /* size of dump area */ + int maj; + + if (dumpdev == NODEV) + return; + maj = major(dumpdev); + if (maj < 0 || maj >= nblkdev) + panic("dumpconf: bad dumpdev=0x%x", dumpdev); + if (bdevsw[maj].d_psize == NULL) + return; + nblks = (*bdevsw[maj].d_psize)(dumpdev); + if (nblks <= ctod(1)) + return; + + dumpsize = physmem; + + /* luna88k only uses a single segment. */ + cpu_kcore_hdr.ram_segs[0].start = 0; + cpu_kcore_hdr.ram_segs[0].size = ctob(physmem); + cpu_kcore_hdr.cputype = cputyp; + + /* + * Don't dump on the first block + * in case the dump device includes a disk label. + */ + if (dumplo < ctod(1)) + dumplo = ctod(1); + + /* Put dump at end of partition, and make it fit. */ + if (dumpsize + 1 > dtoc(nblks - dumplo)) + dumpsize = dtoc(nblks - dumplo) - 1; + if (dumplo < nblks - ctod(dumpsize) - 1) + dumplo = nblks - ctod(dumpsize) - 1; +} + +/* + * Doadump comes here after turning off memory management and + * getting on the dump stack, either when called above, or by + * the auto-restart code. + */ +void +dumpsys() +{ + int maj; + int psize; + daddr_t blkno; /* current block to write */ + /* dump routine */ + int (*dump)(dev_t, daddr_t, caddr_t, size_t); + int pg; /* page being dumped */ + paddr_t maddr; /* PA being dumped */ + int error; /* error code from (*dump)() */ + kcore_seg_t *kseg_p; + cpu_kcore_hdr_t *chdr_p; + char dump_hdr[dbtob(1)]; /* XXX assume hdr fits in 1 block */ + + extern int msgbufmapped; + + msgbufmapped = 0; + + /* Make sure dump device is valid. */ + if (dumpdev == NODEV) + return; + if (dumpsize == 0) { + dumpconf(); + if (dumpsize == 0) + return; + } + maj = major(dumpdev); + if (dumplo < 0) { + printf("\ndump to dev %u,%u not possible\n", maj, + minor(dumpdev)); + return; + } + dump = bdevsw[maj].d_dump; + blkno = dumplo; + + printf("\ndumping to dev %u,%u offset %ld\n", maj, + minor(dumpdev), dumplo); + + /* Setup the dump header */ + kseg_p = (kcore_seg_t *)dump_hdr; + chdr_p = (cpu_kcore_hdr_t *)&dump_hdr[ALIGN(sizeof(*kseg_p))]; + bzero(dump_hdr, sizeof(dump_hdr)); + + CORE_SETMAGIC(*kseg_p, KCORE_MAGIC, MID_MACHINE, CORE_CPU); + kseg_p->c_size = dbtob(1) - ALIGN(sizeof(*kseg_p)); + *chdr_p = cpu_kcore_hdr; + + printf("dump "); + psize = (*bdevsw[maj].d_psize)(dumpdev); + if (psize == -1) { + printf("area unavailable\n"); + return; + } + + /* Dump the header. */ + error = (*dump)(dumpdev, blkno++, (caddr_t)dump_hdr, dbtob(1)); + if (error != 0) + goto abort; + + maddr = (paddr_t)0; + for (pg = 0; pg < dumpsize; pg++) { +#define NPGMB (1024 * 1024 / PAGE_SIZE) + /* print out how many MBs we have dumped */ + if (pg != 0 && (pg % NPGMB) == 0) + printf("%d ", pg / NPGMB); +#undef NPGMB + pmap_enter(pmap_kernel(), (vaddr_t)vmmap, maddr, + VM_PROT_READ, VM_PROT_READ|PMAP_WIRED); + + error = (*dump)(dumpdev, blkno, vmmap, PAGE_SIZE); + if (error == 0) { + maddr += PAGE_SIZE; + blkno += btodb(PAGE_SIZE); + } else + break; + } +abort: + switch (error) { + case 0: + printf("succeeded\n"); + break; + + case ENXIO: + printf("device bad\n"); + break; + + case EFAULT: + printf("device not ready\n"); + break; + + case EINVAL: + printf("area improper\n"); + break; + + case EIO: + printf("i/o error\n"); + break; + + case EINTR: + printf("aborted from console\n"); + break; + + default: + printf("error %d\n", error); + break; + } +} + +/* gets an interrupt stack for slave processors */ +vaddr_t +get_slave_stack() +{ + vaddr_t addr; + + addr = (vaddr_t)uvm_km_zalloc(kernel_map, INTSTACK_SIZE); + + if (addr == NULL) + panic("Cannot allocate slave stack for cpu %d", + cpu_number()); + + interrupt_stack[cpu_number()] = addr; + return addr; +} + +/* + * Slave CPU pre-main routine. + * Determine CPU number and set it. + * + * Running on an interrupt stack here; do nothing fancy. + * + * Called from "luna88k/locore.S" + */ +void +slave_pre_main() +{ + set_cpu_number(cmmu_cpu_number()); /* Determine cpu number by CMMU */ + splhigh(); + enable_interrupt(); +} + +/* dummy main routine for slave processors */ +int +slave_main() +{ + printf("slave CPU%d started\n", cpu_number()); + while (1); /* spin forever */ + return 0; +} + +/* + * Device interrupt handler for LUNA88K + * + * when we enter, interrupts are disabled; + * when we leave, they should be disabled, + * but they need not be disabled throughout + * the routine. + */ + +#define GET_MASK(cpu, val) *int_mask_reg[cpu] & (val) +extern unsigned int luna88k_curspl[MAX_CPUS]; /* XXX sould be here? */ +extern unsigned int int_mask_val[INT_LEVEL]; /* XXX sould be here? */ + +void +luna88k_ext_int(u_int v, struct trapframe *eframe) +{ + int cpu = cpu_number(); + unsigned int cur_mask, cur_int; + unsigned int level, old_spl; + + cur_mask = *int_mask_reg[cpu]; + old_spl = luna88k_curspl[cpu]; + eframe->tf_mask = old_spl; + + cur_int = cur_mask >> 29; + + if (cur_int == 0) { + /* + * Spurious interrupts - may be caused by debug output clearing + * DUART interrupts. + */ + printf("luna88k_ext_int(): Spurious interrupts?\n"); + flush_pipeline(); + goto out; + } + + uvmexp.intrs++; + + /* + * We want to service all interrupts marked in the IST register + * They are all valid because the mask would have prevented them + * from being generated otherwise. We will service them in order of + * priority. + */ + + /* XXX: This is very rough. Should be considered more. (aoyama) */ + do { + level = (cur_int > old_spl ? cur_int : old_spl); + if (level >= 8) { + register int i; + + printf("safe level %d <= old level %d\n", level, old_spl); + printf("cur_int = 0x%x\n", cur_int); + + for (i = 0; i < 4; i++) + printf("IEN%d = 0x%x ", i, *int_mask_reg[i]); + printf("\nCPU0 spl %d CPU1 spl %d CPU2 spl %d CPU3 spl %d\n", + luna88k_curspl[0], luna88k_curspl[1], + luna88k_curspl[2], luna88k_curspl[3]); + for (i = 0; i < 8; i++) + printf("int_mask[%d] = 0x%08x\n", i, int_mask_val[i]); + printf("--CPU %d halted--\n", cpu_number()); + spl7(); + for(;;) ; + } + + if (level > 7 || (char)level < 0) { + panic("int level (%x) is not between 0 and 7", level); + } + + setipl(level); + + enable_interrupt(); + + switch(cur_int) { + case CLOCK_INT_LEVEL: + /* increment intr counter */ + intrcnt[M88K_CLK_IRQ]++; + + *clock_reg[cpu] = 0xFFFFFFFFU; /* reset clock */ + + /* + if (clock_enabled[cpu]) + sys_clock_interrupt(USERMODE(eframe[EF_EPSR])); + */ + hardclock((void *)eframe); + break; + case 5: + case 4: + case 3: + isrdispatch_autovec(cur_int); + break; + default: + printf("luna88k_ext_int(): level %d interrupt.\n", cur_int); + break; + } + } while ((cur_int = (*int_mask_reg[cpu]) >> 29) != 0); + + /* + * process any remaining data access exceptions before + * returning to assembler + */ + disable_interrupt(); +out: + if (eframe->tf_dmt0 & DMT_VALID) + m88100_trap(T_DATAFLT, eframe); + + /* + * Restore the mask level to what it was when the interrupt + * was taken. + */ + setipl(eframe->tf_mask); + flush_pipeline(); /* XXX: need this? */ +} + +int +cpu_exec_aout_makecmds(p, epp) + struct proc *p; + struct exec_package *epp; +{ + + return (ENOEXEC); +} + +int +sys_sysarch(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ +#if 0 + struct sys_sysarch_args /* { + syscallarg(int) op; + syscallarg(char *) parm; + } */ *uap = v; +#endif + + return (ENOSYS); +} + +/* + * machine dependent system variables. + */ + +int +cpu_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p) + int *name; + u_int namelen; + void *oldp; + size_t *oldlenp; + void *newp; + size_t newlen; + struct proc *p; +{ + dev_t consdev; + + /* all sysctl names are this level are terminal */ + if (namelen != 1) + return (ENOTDIR); /* overloaded */ + + switch (name[0]) { + case CPU_CONSDEV: + if (cn_tab != NULL) + consdev = cn_tab->cn_dev; + else + consdev = NODEV; + return (sysctl_rdstruct(oldp, oldlenp, newp, &consdev, + sizeof consdev)); + default: + return (EOPNOTSUPP); + } + /*NOTREACHED*/ +} + +/* + * insert an element into a queue + */ + +void +_insque(velement, vhead) + void *velement, *vhead; +{ + struct prochd *element, *head; + element = velement; + head = vhead; + element->ph_link = head->ph_link; + head->ph_link = (struct proc *)element; + element->ph_rlink = (struct proc *)head; + ((struct prochd *)(element->ph_link))->ph_rlink=(struct proc *)element; +} + +/* + * remove an element from a queue + */ + +void +_remque(velement) + void *velement; +{ + struct prochd *element; + element = velement; + ((struct prochd *)(element->ph_link))->ph_rlink = element->ph_rlink; + ((struct prochd *)(element->ph_rlink))->ph_link = element->ph_link; + element->ph_rlink = (struct proc *)0; +} + +int +copystr(fromaddr, toaddr, maxlength, lencopied) + const void *fromaddr; + void *toaddr; + size_t maxlength; + size_t *lencopied; +{ + u_int tally; + + tally = 0; + + while (maxlength--) { + *(u_char *)toaddr = *(u_char *)fromaddr++; + tally++; + if (*(u_char *)toaddr++ == 0) { + if (lencopied) *lencopied = tally; + return (0); + } + } + + if (lencopied) + *lencopied = tally; + + return (ENAMETOOLONG); +} + +void +setrunqueue(p) + struct proc *p; +{ + struct prochd *q; + struct proc *oldlast; + int which = p->p_priority >> 2; + + if (p->p_back != NULL) + panic("setrunqueue %p", p); + q = &qs[which]; + whichqs |= 1 << which; + p->p_forw = (struct proc *)q; + p->p_back = oldlast = q->ph_rlink; + q->ph_rlink = p; + oldlast->p_forw = p; +} + +/* + * Remove process p from its run queue, which should be the one + * indicated by its priority. Calls should be made at splstatclock(). + */ +void +remrunqueue(vp) + struct proc *vp; +{ + struct proc *p = vp; + int which = p->p_priority >> 2; + struct prochd *q; + + if ((whichqs & (1 << which)) == 0) + panic("remrq %p", p); + p->p_forw->p_back = p->p_back; + p->p_back->p_forw = p->p_forw; + p->p_back = NULL; + q = &qs[which]; + if (q->ph_link == (struct proc *)q) + whichqs &= ~(1 << which); +} + +/* dummys for now */ + +void +bugsyscall() +{ +} + +void +dosoftint() +{ + if (ssir & SIR_NET) { + siroff(SIR_NET); + uvmexp.softs++; +#define DONETISR(bit, fn) \ + do { \ + if (netisr & (1 << bit)) { \ + netisr &= ~(1 << bit); \ + fn(); \ + } \ + } while (0) +#include <net/netisr_dispatch.h> +#undef DONETISR + } + + if (ssir & SIR_CLOCK) { + siroff(SIR_CLOCK); + uvmexp.softs++; + softclock(); + } +} + +int +spl0() +{ + int x; + x = splsoftclock(); + + if (ssir) { + dosoftint(); + } + + setipl(0); + + return (x); +} + +#ifdef EH_DEBUG + +void +MY_info(f, p, flags, s) + struct trapframe *f; + caddr_t p; + int flags; + char *s; +{ + regdump(f); + printf("proc %x flags %x type %s\n", p, flags, s); +} + +void +MY_info_done(f, flags) + struct trapframe *f; + int flags; +{ + regdump(f); +} + +#endif + +void +nmihand(void *framep) +{ +#if 0 + struct trapframe *frame = framep; +#endif + +#if DDB + DEBUG_MSG("Abort Pressed\n"); + Debugger(); +#else + DEBUG_MSG("Spurious NMI?\n"); +#endif /* DDB */ +} + +void +regdump(struct trapframe *f) +{ +#define R(i) f->tf_r[i] + printf("R00-05: 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", + R(0),R(1),R(2),R(3),R(4),R(5)); + printf("R06-11: 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", + R(6),R(7),R(8),R(9),R(10),R(11)); + printf("R12-17: 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", + R(12),R(13),R(14),R(15),R(16),R(17)); + printf("R18-23: 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", + R(18),R(19),R(20),R(21),R(22),R(23)); + printf("R24-29: 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", + R(24),R(25),R(26),R(27),R(28),R(29)); + printf("R30-31: 0x%08x 0x%08x\n",R(30),R(31)); + if (cputyp == CPU_88110) { + printf("exip %x enip %x\n", f->tf_exip, f->tf_enip); + } else { + printf("sxip %x snip %x sfip %x\n", + f->tf_sxip, f->tf_snip, f->tf_sfip); + } +#ifdef M88100 + if (f->tf_vector == 0x3 && cputyp != CPU_88110) { + /* print dmt stuff for data access fault */ + printf("dmt0 %x dmd0 %x dma0 %x\n", + f->tf_dmt0, f->tf_dmd0, f->tf_dma0); + printf("dmt1 %x dmd1 %x dma1 %x\n", + f->tf_dmt1, f->tf_dmd1, f->tf_dma1); + printf("dmt2 %x dmd2 %x dma2 %x\n", + f->tf_dmt2, f->tf_dmd2, f->tf_dma2); + printf("fault type %d\n", (f->tf_dpfsr >> 16) & 0x7); + dae_print((unsigned *)f); + } + if (longformat && cputyp != CPU_88110) { + printf("fpsr %x fpcr %x epsr %x ssbr %x\n", + f->tf_fpsr, f->tf_fpcr, f->tf_epsr, f->tf_ssbr); + printf("fpecr %x fphs1 %x fpls1 %x fphs2 %x fpls2 %x\n", + f->tf_fpecr, f->tf_fphs1, f->tf_fpls1, + f->tf_fphs2, f->tf_fpls2); + printf("fppt %x fprh %x fprl %x fpit %x\n", + f->tf_fppt, f->tf_fprh, f->tf_fprl, f->tf_fpit); + printf("vector %d mask %x mode %x scratch1 %x cpu %x\n", + f->tf_vector, f->tf_mask, f->tf_mode, + f->tf_scratch1, f->tf_cpu); + } +#endif +#ifdef M88110 + if (longformat && cputyp == CPU_88110) { + printf("fpsr %x fpcr %x fpecr %x epsr %x\n", + f->tf_fpsr, f->tf_fpcr, f->tf_fpecr, f->tf_epsr); + printf("dsap %x duap %x dsr %x dlar %x dpar %x\n", + f->tf_dsap, f->tf_duap, f->tf_dsr, f->tf_dlar, f->tf_dpar); + printf("isap %x iuap %x isr %x ilar %x ipar %x\n", + f->tf_isap, f->tf_iuap, f->tf_isr, f->tf_ilar, f->tf_ipar); + printf("vector %d mask %x mode %x scratch1 %x cpu %x\n", + f->tf_vector, f->tf_mask, f->tf_mode, + f->tf_scratch1, f->tf_cpu); + } +#endif +#ifdef MVME188 + if (brdtyp == BRD_188) { + unsigned int istr, cur_mask; + + istr = *(int *volatile)IST_REG; + cur_mask = GET_MASK(0, istr); + printf("emask = 0x%b\n", f->tf_mask, IST_STRING); + printf("istr = 0x%b\n", istr, IST_STRING); + printf("cmask = 0x%b\n", cur_mask, IST_STRING); + } +#endif +} + +/* + * Called from locore.S during boot, + * this is the first C code that's run. + */ + +void +luna88k_bootstrap() +{ + extern int kernelstart; + extern struct consdev *cn_tab; + extern struct cmmu_p cmmu8820x; + + /* + * Must initialize p_addr before autoconfig or + * the fault handler will get a NULL reference. + * Do this early so that we can take a data or + * instruction fault and survive it. XXX smurph + */ + proc0.p_addr = proc0paddr; + curproc = &proc0; + curpcb = &proc0paddr->u_pcb; + + /* zero out the machine dependant function pointers */ + bzero(&md, sizeof(struct md_p)); + + /* + * set up interrupt and fp exception handlers + * based on the machine. + */ + cmmu = &cmmu8820x; + md.interrupt_func = &luna88k_ext_int; + md.intr_mask = NULL; + md.intr_ipl = NULL; + md.intr_src = NULL; + /* clear and disable all interrupts */ + *int_mask_reg[0] = 0; + *int_mask_reg[1] = 0; + *int_mask_reg[2] = 0; + *int_mask_reg[3] = 0; + + /* startup fake console driver. It will be replaced by consinit() */ + cn_tab = &romttycons; + + uvmexp.pagesize = NBPG; + uvm_setpagesize(); + + first_addr = round_page((vaddr_t) &end); /* XXX: Is this OK? */ + last_addr = size_memory(); + physmem = btoc(last_addr); + + cmmu_parity_enable(); + + setup_board_config(); + cmmu_init(); + master_cpu = cmmu_cpu_number(); + set_cpu_number(master_cpu); + + /* + * We may have more than one CPU, so mention which one is the master. + * We will also want to spin up slave CPUs on the long run... + */ + printf("CPU%d is master CPU\n", master_cpu); + +#if 0 + int i; + for (i = 0; i < MAX_CPUS; i++) { + if (!spin_cpu(i)) + printf("CPU%d started\n", i); + } +#endif + + avail_start = first_addr; + avail_end = last_addr; + /* + * Steal MSGBUFSIZE at the top of physical memory for msgbuf + */ + avail_end -= round_page(MSGBUFSIZE); + +#ifdef DEBUG + printf("LUNA88K boot: memory from 0x%x to 0x%x\n", avail_start, avail_end); +#endif + pmap_bootstrap((vaddr_t)trunc_page((unsigned)&kernelstart) /* = loadpt */, + &avail_start, &avail_end, &virtual_avail, + &virtual_end); + /* + * Tell the VM system about available physical memory. + * luna88k only has one segment. + */ + uvm_page_physload(atop(avail_start), atop(avail_end), + atop(avail_start), atop(avail_end),VM_FREELIST_DEFAULT); + + /* Initialize cached PTEs for u-area mapping. */ + save_u_area(&proc0, (vaddr_t)proc0paddr); + + /* + * Map proc0's u-area at the standard address (UADDR). + */ + load_u_area(&proc0); + + /* Initialize the "u-area" pages. */ + bzero((caddr_t)UADDR, UPAGES*NBPG); +#ifdef DEBUG + printf("leaving luna88k_bootstrap()\n"); +#endif +} + +/* + * Rom console routines: + * Enables printing of boot messages before consinit(). + */ + +#define __ROM_FUNC_TABLE ((int **)0x00001100) +#define ROMGETC() (*(int (*)(void))__ROM_FUNC_TABLE[3])() +#define ROMPUTC(x) (*(void (*)(int))__ROM_FUNC_TABLE[4])(x) + +void +romttycnprobe(cp) + struct consdev *cp; +{ + cp->cn_dev = makedev(14, 0); + cp->cn_pri = CN_NORMAL; +} + +void +romttycninit(cp) + struct consdev *cp; +{ + /* Nothing to do */ +} + +int +romttycngetc(dev) + dev_t dev; +{ + int s, c; + + do { + s = splhigh(); + c = ROMGETC(); + splx(s); + } while (c == -1); + return c; +} + +void +romttycnputc(dev, c) + dev_t dev; + int c; +{ + int s; + +#if 0 + if ((char)c == '\n') + ROMPUTC('\r'); +#endif + s = splhigh(); + ROMPUTC(c); + splx(s); +} + +/* taken from NetBSD/luna68k */ +void +microtime(tvp) + register struct timeval *tvp; +{ + int s = splclock(); + static struct timeval lasttime; + + *tvp = time; +#ifdef notdef + tvp->tv_usec += clkread(); + while (tvp->tv_usec >= 1000000) { + tvp->tv_sec++; + tvp->tv_usec -= 1000000; + } +#endif + if (tvp->tv_sec == lasttime.tv_sec && + tvp->tv_usec <= lasttime.tv_usec && + (tvp->tv_usec = lasttime.tv_usec + 1) >= 1000000) { + tvp->tv_sec++; + tvp->tv_usec -= 1000000; + } + lasttime = *tvp; + splx(s); +} + +/* powerdown */ + +struct pio { + volatile u_int8_t portA; + volatile unsigned : 24; + volatile u_int8_t portB; + volatile unsigned : 24; + volatile u_int8_t portC; + volatile unsigned : 24; + volatile u_int8_t cntrl; + volatile unsigned : 24; +}; + +#define PIO1_POWER 0x04 + +#define PIO1_ENABLE 0x01 +#define PIO1_DISABLE 0x00 + +void +powerdown(void) +{ + struct pio *p1 = (struct pio *)OBIO_PIO1_BASE; + + DELAY(100000); + p1->cntrl = (PIO1_POWER << 1) | PIO1_DISABLE; + *(volatile u_int8_t *)&p1->portC; +} diff --git a/sys/arch/luna88k/luna88k/mainbus.c b/sys/arch/luna88k/luna88k/mainbus.c new file mode 100644 index 00000000000..14a25832c1e --- /dev/null +++ b/sys/arch/luna88k/luna88k/mainbus.c @@ -0,0 +1,107 @@ +/* $OpenBSD: mainbus.c,v 1.1 2004/04/21 15:24:08 aoyama Exp $ */ +/* $NetBSD: mainbus.c,v 1.2 2000/01/07 05:13:08 nisimura Exp $ */ + +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Tohru Nishimura. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> + +#include <machine/cpu.h> +#include <machine/autoconf.h> + +static const struct mainbus_attach_args devs[] = { + { "clock", 0x45000000, -1 }, /* Mostek/Dallas TimeKeeper */ + { "le", 0xf1000000, 4 }, /* Am7990 */ + { "sio", 0x51000000, 5 }, /* uPD7201A */ + { "fb", 0xc1100000, -1 }, /* BrookTree RAMDAC */ + { "spc", 0xe1000000, 3 }, /* MB89352 */ + { "spc", 0xe1000040, 3 }, /* ditto */ +#if NPCM > 0 + { "pcm", 0x91000000, 4 }, /* NEC-9801-86 Sound board (under testing) */ +#endif +}; + +void mainbus_attach(struct device *, struct device *, void *); +int mainbus_match(struct device *, void *, void *); +int mainbus_print(void *, const char *); + +const struct cfattach mainbus_ca = { + sizeof(struct device), mainbus_match, mainbus_attach +}; + +const struct cfdriver mainbus_cd = { + NULL, "mainbus", DV_DULL, 0 +}; + +int +mainbus_match(parent, cf, args) + struct device *parent; + void *cf, *args; +{ + static int mainbus_matched; + + if (mainbus_matched) + return (0); + + return ((mainbus_matched = 1)); +} + +void +mainbus_attach(parent, self, args) + struct device *parent, *self; + void *args; +{ + int i; + + printf("\n"); + for (i = 0; i < sizeof(devs)/sizeof(devs[0]); i++) + config_found(self, (void *)&devs[i], mainbus_print); +} + +int +mainbus_print(aux, pnp) + void *aux; + const char *pnp; +{ + struct mainbus_attach_args *ma = aux; + + if (pnp) + printf("%s at %s", ma->ma_name, pnp); + + return (UNCONF); +} diff --git a/sys/arch/luna88k/luna88k/mem.c b/sys/arch/luna88k/luna88k/mem.c new file mode 100644 index 00000000000..a0553547a0c --- /dev/null +++ b/sys/arch/luna88k/luna88k/mem.c @@ -0,0 +1,237 @@ +/* $OpenBSD: mem.c,v 1.1 2004/04/21 15:24:10 aoyama Exp $ */ + +/* + * Copyright (c) 1988 University of Utah. + * Copyright (c) 1982, 1986, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * the Systems Programming Group of the University of Utah Computer + * Science Department. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)mem.c 8.3 (Berkeley) 1/12/94 + */ + +/* + * Memory special file + */ + +#include <sys/param.h> +#include <sys/conf.h> +#include <sys/buf.h> +#include <sys/systm.h> +#include <sys/uio.h> +#include <sys/malloc.h> + +#include <machine/board.h> + +#include <uvm/uvm_extern.h> + +caddr_t zeropage; + +#define mmread mmrw +#define mmwrite mmrw +cdev_decl(mm); + +/*ARGSUSED*/ +int +mmopen(dev, flag, mode, p) + dev_t dev; + int flag, mode; + struct proc *p; +{ + + switch (minor(dev)) { + case 0: + case 1: + case 2: + case 12: + return (0); + default: + return (ENXIO); + } +} + +/*ARGSUSED*/ +int +mmclose(dev, flag, mode, p) + dev_t dev; + int flag, mode; + struct proc *p; +{ + + return (0); +} + +/*ARGSUSED*/ +int +mmrw(dev, uio, flags) + dev_t dev; + struct uio *uio; + int flags; +{ + vaddr_t o, v; + int c; + struct iovec *iov; + int error = 0; + static int physlock = 0; + extern caddr_t vmmap; + + if (minor(dev) == 0) { + /* lock against other uses of shared vmmap */ + while (physlock > 0) { + physlock++; + error = tsleep((caddr_t)&physlock, PZERO | PCATCH, + "mmrw", 0); + if (error) + return (error); + } + physlock = 1; + } + while (uio->uio_resid > 0 && error == 0) { + iov = uio->uio_iov; + if (iov->iov_len == 0) { + uio->uio_iov++; + uio->uio_iovcnt--; + if (uio->uio_iovcnt < 0) + panic("mmrw"); + continue; + } + switch (minor(dev)) { + +/* minor device 0 is physical memory */ + case 0: + /* move one page at a time */ + v = uio->uio_offset; + if (v > MAXPHYSMEM) { + error = EFAULT; + goto unlock; + } + pmap_enter(pmap_kernel(), (vaddr_t)vmmap, + trunc_page(v), + uio->uio_rw == UIO_READ ? VM_PROT_READ : VM_PROT_WRITE, + (uio->uio_rw == UIO_READ ? VM_PROT_READ : VM_PROT_WRITE) | PMAP_WIRED); + pmap_update(pmap_kernel()); + o = uio->uio_offset & PGOFSET; + c = min(uio->uio_resid, (int)(NBPG - o)); + error = uiomove((caddr_t)vmmap + o, c, uio); + pmap_remove(pmap_kernel(), (vaddr_t)vmmap, + (vaddr_t)vmmap + NBPG); + pmap_update(pmap_kernel()); + continue; + +/* minor device 1 is kernel memory */ + case 1: + v = uio->uio_offset; + c = min(iov->iov_len, MAXPHYS); + if (!uvm_kernacc((caddr_t)v, c, + uio->uio_rw == UIO_READ ? B_READ : B_WRITE)) + return (EFAULT); + if (v < NBPG) { +#ifdef DEBUG + /* + * For now, return zeros on read of page 0 + * and EFAULT for writes. + */ + if (uio->uio_rw == UIO_READ) { + if (zeropage == NULL) { + zeropage = (caddr_t) + malloc(PAGE_SIZE, M_TEMP, + M_WAITOK); + bzero(zeropage, PAGE_SIZE); + } + c = min(c, NBPG - (int)v); + v = (vaddr_t)zeropage; + } else +#endif + return (EFAULT); + } + error = uiomove((caddr_t)v, c, uio); + continue; + +/* minor device 2 is EOF/RATHOLE */ + case 2: + if (uio->uio_rw == UIO_WRITE) + uio->uio_resid = 0; + return (0); + +/* should add vme bus so that we can do user level probes */ + +/* minor device 12 (/dev/zero) is source of nulls on read, rathole on write */ + case 12: + if (uio->uio_rw == UIO_WRITE) { + c = iov->iov_len; + break; + } + if (zeropage == NULL) { + zeropage = (caddr_t) + malloc(PAGE_SIZE, M_TEMP, M_WAITOK); + bzero(zeropage, PAGE_SIZE); + } + c = min(iov->iov_len, PAGE_SIZE); + error = uiomove(zeropage, c, uio); + continue; + + default: + return (ENXIO); + } + if (error) + break; + iov->iov_base += c; + iov->iov_len -= c; + uio->uio_offset += c; + uio->uio_resid -= c; + } + if (minor(dev) == 0) { +unlock: + if (physlock > 1) + wakeup((caddr_t)&physlock); + physlock = 0; + } + return (error); +} + +paddr_t +mmmmap(dev, off, prot) + dev_t dev; + off_t off; + int prot; +{ + return (-1); +} + +/*ARGSUSED*/ +int +mmioctl(dev, cmd, data, flags, p) + dev_t dev; + u_long cmd; + caddr_t data; + int flags; + struct proc *p; +{ + return (EOPNOTSUPP); +} diff --git a/sys/arch/luna88k/luna88k/pmap.c b/sys/arch/luna88k/luna88k/pmap.c new file mode 100644 index 00000000000..48e56127dbf --- /dev/null +++ b/sys/arch/luna88k/luna88k/pmap.c @@ -0,0 +1,2834 @@ +/* $OpenBSD: pmap.c,v 1.1 2004/04/21 15:24:13 aoyama Exp $ */ +/* + * Copyright (c) 2001, 2002, 2003 Miodrag Vallat + * Copyright (c) 1998-2001 Steve Murphree, Jr. + * Copyright (c) 1996 Nivas Madhur + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Nivas Madhur. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/* + * Mach Operating System + * Copyright (c) 1991 Carnegie Mellon University + * Copyright (c) 1991 OMRON Corporation + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/simplelock.h> +#include <sys/proc.h> +#include <sys/malloc.h> +#include <sys/pool.h> +#include <sys/msgbuf.h> +#include <sys/user.h> + +#include <uvm/uvm.h> + +#include <machine/asm_macro.h> +#include <machine/board.h> +#include <machine/cmmu.h> +#include <machine/cpu_number.h> +#include <machine/pmap_table.h> + +/* + * VM externals + */ +extern vaddr_t avail_start, avail_end; +extern vaddr_t virtual_avail, virtual_end; + +/* + * Macros to operate pm_cpus field + */ +#define SETBIT_CPUSET(cpu_number, cpuset) (*(cpuset)) |= (1 << (cpu_number)); +#define CLRBIT_CPUSET(cpu_number, cpuset) (*(cpuset)) &= ~(1 << (cpu_number)); + +#ifdef DEBUG +/* + * Static variables, functions and variables for debugging + */ + +/* + * conditional debugging + */ +#define CD_FULL 0x02 + +#define CD_ACTIVATE 0x0000004 /* pmap_activate */ +#define CD_KMAP 0x0000008 /* pmap_expand_kmap */ +#define CD_MAP 0x0000010 /* pmap_map */ +#define CD_CACHE 0x0000020 /* pmap_cache_ctrl */ +#define CD_BOOT 0x0000040 /* pmap_bootstrap */ +#define CD_INIT 0x0000080 /* pmap_init */ +#define CD_CREAT 0x0000100 /* pmap_create */ +#define CD_FREE 0x0000200 /* pmap_release */ +#define CD_DESTR 0x0000400 /* pmap_destroy */ +#define CD_RM 0x0000800 /* pmap_remove */ +#define CD_RMAL 0x0001000 /* pmap_remove_all */ +#define CD_PROT 0x0002000 /* pmap_protect */ +#define CD_EXP 0x0004000 /* pmap_expand */ +#define CD_ENT 0x0008000 /* pmap_enter */ +#define CD_UPD 0x0010000 /* pmap_update */ +#define CD_COL 0x0020000 /* pmap_collect */ +#define CD_CBIT 0x0040000 /* pmap_changebit */ +#define CD_TBIT 0x0080000 /* pmap_testbit */ +#define CD_USBIT 0x0100000 /* pmap_unsetbit */ +#define CD_PGMV 0x0200000 /* pagemove */ +#define CD_ALL 0x0FFFFFC + +int pmap_con_dbg = 0; + +/* + * Alignment checks for pages (must lie on page boundaries). + */ +#define PAGE_ALIGNED(ad) (((vaddr_t)(ad) & PAGE_MASK) == 0) +#define CHECK_PAGE_ALIGN(ad, who) \ + if (!PAGE_ALIGNED(ad)) \ + printf("%s: addr %x not page aligned.\n", who, ad) + +#else /* DEBUG */ + +#define CHECK_PAGE_ALIGN(ad, who) + +#endif /* DEBUG */ + +struct pool pmappool, pvpool; + +caddr_t vmmap; +pt_entry_t *vmpte, *msgbufmap; + +struct pmap kernel_pmap_store; +pmap_t kernel_pmap = &kernel_pmap_store; + +typedef struct kpdt_entry *kpdt_entry_t; +struct kpdt_entry { + kpdt_entry_t next; + paddr_t phys; +}; +#define KPDT_ENTRY_NULL ((kpdt_entry_t)0) + +kpdt_entry_t kpdt_free; + +/* + * MAX_KERNEL_VA_SIZE must fit into the virtual address space between + * VM_MIN_KERNEL_ADDRESS and VM_MAX_KERNEL_ADDRESS. + */ + +#define MAX_KERNEL_VA_SIZE (256*1024*1024) /* 256 Mb */ + +/* + * Size of kernel page tables, which is enough to map MAX_KERNEL_VA_SIZE + */ +#define KERNEL_PDT_SIZE (atop(MAX_KERNEL_VA_SIZE) * sizeof(pt_entry_t)) + +/* + * Size of kernel page tables for mapping onboard IO space. + */ +#define OBIO_PDT_SIZE (atop(OBIO_SIZE) * sizeof(pt_entry_t)) + +#define MAX_KERNEL_PDT_SIZE (KERNEL_PDT_SIZE + OBIO_PDT_SIZE) + +/* + * Two pages of scratch space per cpu. + * Used in pmap_copy_page() and pmap_zero_page(). + */ +vaddr_t phys_map_vaddr, phys_map_vaddr_end; + +#define PV_ENTRY_NULL ((pv_entry_t) 0) + +static pv_entry_t pg_to_pvh(struct vm_page *); + +static __inline pv_entry_t +pg_to_pvh(struct vm_page *pg) +{ + return &pg->mdpage.pvent; +} + +/* + * Locking primitives + */ + +/* + * We raise the interrupt level to splvm, to block interprocessor + * interrupts during pmap operations. + */ +#define SPLVM(spl) spl = splvm() +#define SPLX(spl) splx(spl) + +#define PMAP_LOCK(pmap,spl) \ + do { \ + SPLVM(spl); \ + simple_lock(&(pmap)->pm_lock); \ + } while (0) +#define PMAP_UNLOCK(pmap, spl) \ + do { \ + simple_unlock(&(pmap)->pm_lock); \ + SPLX(spl); \ + } while (0) + +#define ETHERPAGES 16 +void *etherbuf = NULL; +int etherlen; + +#ifdef PMAP_USE_BATC + +/* + * number of BATC entries used + */ +int batc_used; + +/* + * keep track BATC mapping + */ +batc_entry_t batc_entry[BATC_MAX]; + +#endif /* PMAP_USE_BATC */ + +vaddr_t kmapva = 0; + +/* + * Internal routines + */ +void flush_atc_entry(long, vaddr_t, boolean_t); +pt_entry_t *pmap_expand_kmap(vaddr_t, vm_prot_t); +void pmap_remove_pte(pmap_t, vaddr_t, pt_entry_t *); +void pmap_remove_range(pmap_t, vaddr_t, vaddr_t); +void pmap_expand(pmap_t, vaddr_t); +void pmap_release(pmap_t); +vaddr_t pmap_map(vaddr_t, paddr_t, paddr_t, vm_prot_t, u_int); +pt_entry_t *pmap_pte(pmap_t, vaddr_t); +void pmap_remove_all(struct vm_page *); +void pmap_changebit(struct vm_page *, int, int); +boolean_t pmap_unsetbit(struct vm_page *, int); +boolean_t pmap_testbit(struct vm_page *, int); + +/* + * quick PTE field checking macros + */ +#define pmap_pte_w(pte) (*(pte) & PG_W) +#define pmap_pte_prot(pte) (*(pte) & PG_PROT) + +#define pmap_pte_w_chg(pte, nw) ((nw) ^ pmap_pte_w(pte)) +#define pmap_pte_prot_chg(pte, np) ((np) ^ pmap_pte_prot(pte)) + +/* + * Convert machine-independent protection code to M88K protection bits. + */ +static __inline u_int32_t +m88k_protection(pmap_t pmap, vm_prot_t prot) +{ + pt_entry_t p; + + p = (prot & VM_PROT_WRITE) ? PG_RW : PG_RO; + /* + * XXX this should not be necessary anymore now that pmap_enter + * does the correct thing... -- miod + */ +#ifdef M88110 + if (cputyp == CPU_88110) { + p |= PG_U; + /* if the map is the kernel's map and since this + * is not a paged kernel, we go ahead and mark + * the page as modified to avoid an exception + * upon writing to the page the first time. XXX smurph + */ + if (pmap == kernel_pmap) { + if ((p & PG_RO) == 0) + p |= PG_M; + } + } +#endif + return p; +} + +/* + * Routine: FLUSH_ATC_ENTRY + * + * Function: + * Flush atc(TLB) which maps given virtual address, in the CPUs which + * are specified by 'users', for the operating mode specified by + * 'kernel'. + * + * Parameters: + * users bit patterns of the CPUs which may hold the TLB, and + * should be flushed + * va virtual address that should be flushed + * kernel TRUE if supervisor mode, FALSE if user mode + */ +void +flush_atc_entry(long users, vaddr_t va, boolean_t kernel) +{ + int cpu; + long tusers = users; + +#ifdef DEBUG + if ((tusers != 0) && (ff1(tusers) >= MAX_CPUS)) { + panic("flush_atc_entry: invalid ff1 users = %d", ff1(tusers)); + } +#endif + + while ((cpu = ff1(tusers)) != 32) { + if (cpu_sets[cpu]) { /* just checking to make sure */ + cmmu_flush_tlb(cpu, kernel, va, PAGE_SIZE); + } + tusers &= ~(1 << cpu); + } +} + +/* + * Routine: PMAP_PTE + * + * Function: + * Given a map and a virtual address, compute a (virtual) pointer + * to the page table entry (PTE) which maps the address . + * If the page table associated with the address does not + * exist, PT_ENTRY_NULL is returned (and the map may need to grow). + * + * Parameters: + * pmap pointer to pmap structure + * virt virtual address for which page table entry is desired + * + * Otherwise the page table address is extracted from the segment table, + * the page table index is added, and the result is returned. + */ +pt_entry_t * +pmap_pte(pmap_t pmap, vaddr_t virt) +{ + sdt_entry_t *sdt; + +#ifdef DEBUG + /*XXX will this change if physical memory is not contiguous? */ + /* take a look at PDTIDX XXXnivas */ + if (pmap == PMAP_NULL) + panic("pmap_pte: pmap is NULL"); +#endif + + sdt = SDTENT(pmap, virt); + /* + * Check whether page table exists. + */ + if (!SDT_VALID(sdt)) + return (PT_ENTRY_NULL); + + return (pt_entry_t *)(PG_PFNUM(*(sdt + SDT_ENTRIES)) << PDT_SHIFT) + + PDTIDX(virt); +} + +/* + * Routine: PMAP_EXPAND_KMAP (internal) + * + * Function: + * Allocate a page descriptor table (pte_table) and validate associated + * segment table entry, returning pointer to page table entry. This is + * much like 'pmap_expand', except that table space is acquired + * from an area set up by pmap_bootstrap, instead of through + * uvm_km_zalloc. (Obviously, because uvm_km_zalloc uses the kernel map + * for allocation - which we can't do when trying to expand the + * kernel map!) Note that segment tables for the kernel map were + * all allocated at pmap_bootstrap time, so we only need to worry + * about the page table here. + * + * Parameters: + * virt VA for which translation tables are needed + * prot protection attributes for segment entries + * + * Extern/Global: + * kpdt_free kernel page table free queue + * + * Calls: + * m88k_protection + * + * This routine simply dequeues a table from the kpdt_free list, + * initializes all its entries (invalidates them), and sets the + * corresponding segment table entry to point to it. If the kpdt_free + * list is empty - we panic (no other places to get memory, sorry). (Such + * a panic indicates that pmap_bootstrap is not allocating enough table + * space for the kernel virtual address space). + * + */ +pt_entry_t * +pmap_expand_kmap(vaddr_t virt, vm_prot_t prot) +{ + sdt_entry_t template, *sdt; + kpdt_entry_t kpdt_ent; + +#ifdef DEBUG + if ((pmap_con_dbg & (CD_KMAP | CD_FULL)) == (CD_KMAP | CD_FULL)) + printf("(pmap_expand_kmap: %x) v %x\n", curproc, virt); +#endif + + template = m88k_protection(kernel_pmap, prot) | SG_V; + + /* segment table entry derivate from map and virt. */ + sdt = SDTENT(kernel_pmap, virt); +#ifdef DEBUG + if (SDT_VALID(sdt)) + panic("pmap_expand_kmap: segment table entry VALID"); +#endif + + kpdt_ent = kpdt_free; + if (kpdt_ent == KPDT_ENTRY_NULL) + panic("pmap_expand_kmap: Ran out of kernel pte tables"); + + kpdt_free = kpdt_free->next; + /* physical table */ + *sdt = kpdt_ent->phys | template; + /* virtual table */ + *(sdt + SDT_ENTRIES) = (vaddr_t)kpdt_ent | template; + + /* Reinitialize this kpdt area to zero */ + bzero((void *)kpdt_ent, PDT_SIZE); + + return (pt_entry_t *)(kpdt_ent) + PDTIDX(virt); +} + +/* + * Routine: PMAP_MAP + * + * Function: + * Map memory at initialization. The physical addresses being + * mapped are not managed and are never unmapped. + * + * Parameters: + * virt virtual address of range to map + * start physical address of range to map + * end physical address of end of range + * prot protection attributes + * cmode cache control attributes + * + * Calls: + * pmap_pte + * pmap_expand_kmap + * + * Special Assumptions + * For now, VM is already on, only need to map the specified + * memory. Used only by pmap_bootstrap() and vm_page_startup(). + * + * For each page that needs mapping: + * pmap_pte is called to obtain the address of the page table + * table entry (PTE). If the page table does not exist, + * pmap_expand_kmap is called to allocate it. Finally, the page table + * entry is set to point to the physical page. + * + * initialize template with paddr, prot, dt + * look for number of phys pages in range + * { + * pmap_pte(virt) - expand if necessary + * stuff pte from template + * increment virt one page + * increment template paddr one page + * } + * + */ +vaddr_t +pmap_map(vaddr_t virt, paddr_t start, paddr_t end, vm_prot_t prot, u_int cmode) +{ + u_int npages; + u_int num_phys_pages; + pt_entry_t template, *pte; + paddr_t page; +#ifdef PMAP_USE_BATC + u_int32_t batctmp; + int i; +#endif + +#ifdef DEBUG + if (pmap_con_dbg & CD_MAP) + printf ("(pmap_map: %x) phys address from %x to %x mapped at virtual %x, prot %x cmode %x\n", + curproc, start, end, virt, prot, cmode); +#endif + +#ifdef DEBUG + /* Check for zero if we map the very end of the address space... */ + if (start > end && end != 0) { + panic("pmap_map: start greater than end address"); + } +#endif + + template = m88k_protection(kernel_pmap, prot) | cmode | PG_V; + +#ifdef PMAP_USE_BATC + batctmp = BATC_SO | BATC_V; + if (template & CACHE_WT) + batctmp |= BATC_WT; + if (template & CACHE_GLOBAL) + batctmp |= BATC_GLOBAL; + if (template & CACHE_INH) + batctmp |= BATC_INH; + if (template & PG_PROT) + batctmp |= BATC_PROT; +#endif + + page = trunc_page(start); + npages = atop(round_page(end) - page); + for (num_phys_pages = npages; num_phys_pages != 0; num_phys_pages--) { +#ifdef PMAP_USE_BATC + +#ifdef DEBUG + if ((pmap_con_dbg & (CD_MAP | CD_FULL)) == (CD_MAP | CD_FULL)) + printf("(pmap_map: %x) num_phys_pg=%x, virt=%x, " + "align V=%d, page=%x, align P=%d\n", + curproc, num_phys_pages, virt, + BATC_BLK_ALIGNED(virt), page, + BATC_BLK_ALIGNED(page)); +#endif + + if (BATC_BLK_ALIGNED(virt) && BATC_BLK_ALIGNED(page) && + num_phys_pages >= BATC_BLKBYTES/PAGE_SIZE && + batc_used < BATC_MAX ) { + /* + * map by BATC + */ + batctmp |= M88K_BTOBLK(virt) << BATC_VSHIFT; + batctmp |= M88K_BTOBLK(page) << BATC_PSHIFT; + + for (i = 0; i < MAX_CPUS; i++) + if (cpu_sets[i]) + cmmu_set_pair_batc_entry(i, batc_used, + batctmp); + batc_entry[batc_used] = batctmp; +#ifdef DEBUG + if (pmap_con_dbg & CD_MAP) { + printf("(pmap_map: %x) BATC used=%d, data=%x\n", curproc, batc_used, batctmp); + for (i = 0; i < BATC_BLKBYTES; i += PAGE_SIZE) { + pte = pmap_pte(kernel_pmap, virt + i); + if (PDT_VALID(pte)) + printf("(pmap_map: %x) va %x is already mapped: pte %x\n", + curproc, virt + i, *pte); + } + } +#endif + batc_used++; + virt += BATC_BLKBYTES; + page += BATC_BLKBYTES; + num_phys_pages -= BATC_BLKBYTES/PAGE_SIZE; + continue; + } +#endif /* PMAP_USE_BATC */ + + if ((pte = pmap_pte(kernel_pmap, virt)) == PT_ENTRY_NULL) + pte = pmap_expand_kmap(virt, + VM_PROT_READ | VM_PROT_WRITE); + +#ifdef DEBUG + if ((pmap_con_dbg & (CD_MAP | CD_FULL)) == (CD_MAP | CD_FULL)) + if (PDT_VALID(pte)) + printf("(pmap_map: %x) pte @ 0x%p already valid\n", curproc, pte); +#endif + + *pte = template | page; + virt += PAGE_SIZE; + page += PAGE_SIZE; + } + return virt; +} + +/* + * Routine: PMAP_CACHE_CONTROL + * + * Function: + * Set the cache-control bits in the page table entries(PTE) which maps + * the specified virtual address range. + * + * Parameters: + * pmap_t pmap + * vaddr_t s + * vaddr_t e + * u_int mode + * + * Calls: + * pmap_pte + * invalidate_pte + * flush_atc_entry + * + * This routine sequences through the pages of the specified range. + * For each, it calls pmap_pte to acquire a pointer to the page table + * entry (PTE). If the PTE is invalid, or non-existent, nothing is done. + * Otherwise, the cache-control bits in the PTE's are adjusted as specified. + * + */ +void +pmap_cache_ctrl(pmap_t pmap, vaddr_t s, vaddr_t e, u_int mode) +{ + int spl; + pt_entry_t *pte; + vaddr_t va; + paddr_t pa; + boolean_t kflush; + int cpu; + u_int users; + +#ifdef DEBUG + if ((mode & CACHE_MASK) != mode) { + printf("(cache_ctrl) illegal mode %x\n", mode); + return; + } + if (pmap_con_dbg & CD_CACHE) { + printf("(pmap_cache_ctrl: %x) pmap %x, va %x, mode %x\n", curproc, pmap, s, mode); + } + + if (pmap == PMAP_NULL) + panic("pmap_cache_ctrl: pmap is NULL"); +#endif /* DEBUG */ + + PMAP_LOCK(pmap, spl); + + users = pmap->pm_cpus; + kflush = pmap == kernel_pmap; + + for (va = s; va < e; va += PAGE_SIZE) { + if ((pte = pmap_pte(pmap, va)) == PT_ENTRY_NULL) + continue; +#ifdef DEBUG + if (pmap_con_dbg & CD_CACHE) { + printf("(cache_ctrl) pte@0x%p\n", pte); + } +#endif /* DEBUG */ + /* + * Invalidate pte temporarily to avoid being written back + * the modified bit and/or the reference bit by any other cpu. + * XXX + */ + *pte = (invalidate_pte(pte) & ~CACHE_MASK) | mode; + flush_atc_entry(users, va, kflush); + + /* + * Data cache should be copied back and invalidated. + */ + pa = ptoa(PG_PFNUM(*pte)); + for (cpu = 0; cpu < MAX_CPUS; cpu++) + if (cpu_sets[cpu]) + cmmu_flush_cache(cpu, pa, PAGE_SIZE); + } + PMAP_UNLOCK(pmap, spl); +} + +/* + * Routine: PMAP_BOOTSTRAP + * + * Function: + * Bootstrap the system enough to run with virtual memory. + * Map the kernel's code and data, allocate the kernel + * translation table space, and map control registers + * and other IO addresses. + * + * Parameters: + * load_start PA where kernel was loaded + * &phys_start PA of first available physical page + * &phys_end PA of last available physical page + * &virtual_avail VA of first available page (after kernel bss) + * &virtual_end VA of last available page (end of kernel address space) + * + * Extern/Global: + * + * PAGE_SIZE VM (software) page size + * kernelstart start symbol of kernel text + * etext end of kernel text + * phys_map_vaddr VA of page mapped arbitrarily for debug/IO + * + * Calls: + * simple_lock_init + * pmap_map + * + * The physical address 'load_start' is mapped at + * VM_MIN_KERNEL_ADDRESS, which maps the kernel code and data at the + * virtual address for which it was (presumably) linked. Immediately + * following the end of the kernel code/data, sufficient page of + * physical memory are reserved to hold translation tables for the kernel + * address space. The 'phys_start' parameter is adjusted upward to + * reflect this allocation. This space is mapped in virtual memory + * immediately following the kernel code/data map. + * + * A pair of virtual pages per cpu are reserved for debugging and + * IO purposes. They are arbitrarily mapped when needed. They are used, + * for example, by pmap_copy_page and pmap_zero_page. + * + * For luna88k, we have to map ROM work area also. This is a read only + * mapping for 0x20000 bytes. We will end up having load_start as + * 0 and VM_MIN_KERNEL_ADDRESS as 0 - yes sir, we have one-to-one + * mapping!!! + */ + +void +pmap_bootstrap(vaddr_t load_start, paddr_t *phys_start, paddr_t *phys_end, + vaddr_t *virt_start, vaddr_t *virt_end) +{ + kpdt_entry_t kpdt_virt; + sdt_entry_t *kmap; + vaddr_t vaddr, virt, kernel_pmap_size, pdt_size; + paddr_t s_text, e_text, kpdt_phys; + pt_entry_t *pte; + int i; + pmap_table_t ptable; + extern void *kernelstart, *etext; + +#ifdef DEBUG + if (pmap_con_dbg & CD_BOOT) { + printf("pmap_bootstrap: \"load_start\" 0x%x\n", load_start); + } + if (!PAGE_ALIGNED(load_start)) + panic("pmap_bootstrap: \"load_start\" not on the m88k page boundary: 0x%x", load_start); +#endif + + simple_lock_init(&kernel_pmap->pm_lock); + + /* + * Allocate the kernel page table from the front of available + * physical memory, i.e. just after where the kernel image was loaded. + */ + /* + * The calling sequence is + * ... + * pmap_bootstrap(&kernelstart, ...); + * kernelstart is the first symbol in the load image. + * We link the kernel such that &kernelstart == 0x20000 + * The expression (&kernelstart - load_start) will end up as + * 0, making *virt_start == *phys_start, giving a 1-to-1 map) + */ + + *phys_start = round_page(*phys_start); + *virt_start = *phys_start + + (trunc_page((vaddr_t)&kernelstart) - load_start); + + /* + * Initialize kernel_pmap structure + */ + kernel_pmap->pm_count = 1; + kernel_pmap->pm_cpus = 0; + kmap = (sdt_entry_t *)(*phys_start); + kernel_pmap->pm_stab = (sdt_entry_t *)(*virt_start); + kmapva = *virt_start; + +#ifdef DEBUG + if ((pmap_con_dbg & (CD_BOOT | CD_FULL)) == (CD_BOOT | CD_FULL)) { + printf("kernel_pmap->pm_stab = 0x%x (pa 0x%x)\n", + kernel_pmap->pm_stab, kmap); + } +#endif + + /* + * Reserve space for segment table entries. + * One for the regular segment table and one for the shadow table + * The shadow table keeps track of the virtual address of page + * tables. This is used in virtual-to-physical address translation + * functions. Remember, MMU cares only for physical addresses of + * segment and page table addresses. For kernel page tables, we + * really don't need this virtual stuff (since the kernel will + * be mapped 1-to-1) but for user page tables, this is required. + * Just to be consistent, we will maintain the shadow table for + * kernel pmap also. + */ + kernel_pmap_size = 2 * SDT_SIZE; + +#ifdef DEBUG + if ((pmap_con_dbg & (CD_BOOT | CD_FULL)) == (CD_BOOT | CD_FULL)) { + printf("kernel segment table size = 0x%x\n", kernel_pmap_size); + } +#endif + /* init all segment descriptors to zero */ + bzero(kernel_pmap->pm_stab, kernel_pmap_size); + + *phys_start += kernel_pmap_size; + *virt_start += kernel_pmap_size; + + /* make sure page tables are page aligned!! XXX smurph */ + *phys_start = round_page(*phys_start); + *virt_start = round_page(*virt_start); + + /* save pointers to where page table entries start in physical memory */ + kpdt_phys = *phys_start; + kpdt_virt = (kpdt_entry_t)*virt_start; + + /* might as well round up to a page - XXX smurph */ + pdt_size = round_page(MAX_KERNEL_PDT_SIZE); + kernel_pmap_size += pdt_size; + *phys_start += pdt_size; + *virt_start += pdt_size; + + /* init all page descriptors to zero */ + bzero((void *)kpdt_phys, pdt_size); +#ifdef DEBUG + if ((pmap_con_dbg & (CD_BOOT | CD_FULL)) == (CD_BOOT | CD_FULL)) { + printf("--------------------------------------\n"); + printf(" kernel page start = 0x%x\n", kpdt_phys); + printf(" kernel page table size = 0x%x\n", pdt_size); + printf(" kernel page end = 0x%x\n", *phys_start); + } +#endif + +#ifdef DEBUG + if ((pmap_con_dbg & (CD_BOOT | CD_FULL)) == (CD_BOOT | CD_FULL)) { + printf("kpdt_phys = 0x%x\n", kpdt_phys); + printf("kpdt_virt = 0x%x\n", kpdt_virt); + printf("end of kpdt at (virt)0x%08x, (phys)0x%08x\n", + *virt_start, *phys_start); + } +#endif + /* + * init the kpdt queue + */ + kpdt_free = kpdt_virt; + for (i = pdt_size / PDT_SIZE; i != 0; i--) { + kpdt_virt->next = (kpdt_entry_t)((vaddr_t)kpdt_virt + PDT_SIZE); + kpdt_virt->phys = kpdt_phys; + kpdt_virt = kpdt_virt->next; + kpdt_phys += PDT_SIZE; + } + kpdt_virt->next = KPDT_ENTRY_NULL; /* terminate the list */ + + /* + * Map the kernel image into virtual space + */ + + s_text = load_start; /* paddr of text */ + e_text = load_start + + ((vaddr_t)&etext - trunc_page((vaddr_t)&kernelstart)); + /* paddr of end of text section*/ + e_text = round_page(e_text); + +#if 0 + /* map the first 128k (PROM work) read only, cache inhibited (? XXX) */ + vaddr = pmap_map(0, 0, 0x20000, VM_PROT_WRITE | VM_PROT_READ, + CACHE_INH); +#endif + + /* map the kernel text read only */ + vaddr = pmap_map(trunc_page((vaddr_t)&kernelstart), + s_text, e_text, VM_PROT_WRITE | VM_PROT_READ, + CACHE_GLOBAL); /* shouldn't it be RO? XXX*/ + + vaddr = pmap_map(vaddr, e_text, (paddr_t)kmap, + VM_PROT_WRITE | VM_PROT_READ, CACHE_GLOBAL); + + /* + * Map system segment & page tables - should be cache inhibited? + * 88200 manual says that CI bit is driven on the Mbus while accessing + * the translation tree. I don't think we need to map it CACHE_INH + * here... + */ + if (kmapva != vaddr) { +#ifdef DEBUG + if ((pmap_con_dbg & (CD_BOOT | CD_FULL)) == (CD_BOOT | CD_FULL)) { + printf("(pmap_bootstrap) correcting vaddr\n"); + } +#endif + while (vaddr < (*virt_start - kernel_pmap_size)) + vaddr = round_page(vaddr + 1); + } + vaddr = pmap_map(vaddr, (paddr_t)kmap, *phys_start, + VM_PROT_WRITE | VM_PROT_READ, CACHE_INH); + +#if defined (MVME187) || defined (MVME197) + /* + * Get ethernet buffer - need etherlen bytes physically contiguous. + * 1 to 1 mapped as well???. There is actually a bug in the macros + * used by the 1x7 ethernet driver. Remove this when that is fixed. + * XXX -nivas + */ + if (brdtyp == BRD_187 || brdtyp == BRD_8120 || brdtyp == BRD_197) { + *phys_start = vaddr; + etherlen = ETHERPAGES * PAGE_SIZE; + etherbuf = (void *)vaddr; + + vaddr = pmap_map(vaddr, *phys_start, *phys_start + etherlen, + VM_PROT_WRITE | VM_PROT_READ, CACHE_INH); + + *virt_start += etherlen; + *phys_start += etherlen; + + if (vaddr != *virt_start) { +#ifdef DEBUG + if ((pmap_con_dbg & (CD_BOOT | CD_FULL)) == (CD_BOOT | CD_FULL)) { + printf("2: vaddr %x *virt_start %x *phys_start %x\n", vaddr, + *virt_start, *phys_start); + } +#endif + *virt_start = vaddr; + *phys_start = round_page(*phys_start); + } + } + +#endif /* defined (MVME187) || defined (MVME197) */ + + *virt_start = round_page(*virt_start); + *virt_end = VM_MAX_KERNEL_ADDRESS; + + /* + * Map two pages per cpu for copying/zeroing. + */ + + phys_map_vaddr = *virt_start; + phys_map_vaddr_end = *virt_start + 2 * (max_cpus << PAGE_SHIFT); + *phys_start += 2 * (max_cpus << PAGE_SHIFT); + *virt_start += 2 * (max_cpus << PAGE_SHIFT); + + /* + * Map all IO space 1-to-1. Ideally, I would like to not do this + * but have va for the given IO address dynamically allocated. But + * on the 88200, 2 of the BATCs are hardwired to map the IO space + * 1-to-1; I decided to map the rest of the IO space 1-to-1. + * And bug ROM & the SRAM need to be mapped 1-to-1 if we ever want to + * execute bug system calls after the MMU has been turned on. + * OBIO should be mapped cache inhibited. + */ + + ptable = pmap_table_build(); +#ifdef DEBUG + if ((pmap_con_dbg & (CD_BOOT | CD_FULL)) == (CD_BOOT | CD_FULL)) { + printf("pmap_bootstrap: -> pmap_table_build\n"); + } +#endif + + for (; ptable->size != (vsize_t)(-1); ptable++){ + if (ptable->size) { + pmap_map(ptable->virt_start, ptable->phys_start, + ptable->phys_start + ptable->size, + ptable->prot, ptable->cacheability); + } + } + + /* + * Allocate all the submaps we need. Note that SYSMAP just allocates + * kernel virtual address with no physical backing memory. The idea + * is physical memory will be mapped at this va before using that va. + * This means that if different physical pages are going to be mapped + * at different times, we better do a tlb flush before using it - + * else we will be referencing the wrong page. + */ + +#define SYSMAP(c, p, v, n) \ +({ \ + v = (c)virt; \ + if ((p = pmap_pte(kernel_pmap, virt)) == PT_ENTRY_NULL) \ + pmap_expand_kmap(virt, VM_PROT_READ | VM_PROT_WRITE); \ + virt += ((n) * PAGE_SIZE); \ +}) + + virt = *virt_start; + + SYSMAP(caddr_t, vmpte, vmmap, 1); + invalidate_pte(vmpte); + + SYSMAP(struct msgbuf *, msgbufmap, msgbufp, btoc(MSGBUFSIZE)); + + *virt_start = virt; + + /* + * Set translation for UPAGES at UADDR. The idea is we want to + * have translations set up for UADDR. Later on, the ptes for + * for this address will be set so that kstack will refer + * to the u area. Make sure pmap knows about this virtual + * address by doing vm_findspace on kernel_map. + */ + + for (i = 0, virt = UADDR; i < UPAGES; i++, virt += PAGE_SIZE) { +#ifdef DEBUG + if ((pmap_con_dbg & (CD_BOOT | CD_FULL)) == (CD_BOOT | CD_FULL)) { + printf("setting up mapping for Upage %d @ %x\n", i, virt); + } +#endif + if ((pte = pmap_pte(kernel_pmap, virt)) == PT_ENTRY_NULL) + pmap_expand_kmap(virt, VM_PROT_READ | VM_PROT_WRITE); + } + + /* + * Switch to using new page tables + */ + + kernel_pmap->pm_apr = (atop(kmap) << PG_SHIFT) | + CACHE_GLOBAL | CACHE_WT | APR_V; +#ifdef DEBUG + if ((pmap_con_dbg & (CD_BOOT | CD_FULL)) == (CD_BOOT | CD_FULL)) { + show_apr(kernel_pmap->pm_apr); + } +#endif + /* Invalidate entire kernel TLB and get ready for address translation */ + for (i = 0; i < MAX_CPUS; i++) + if (cpu_sets[i]) { + cmmu_flush_tlb(i, TRUE, VM_MIN_KERNEL_ADDRESS, + VM_MAX_KERNEL_ADDRESS - VM_MIN_KERNEL_ADDRESS); + /* Load supervisor pointer to segment table. */ + cmmu_set_sapr(i, kernel_pmap->pm_apr); +#ifdef DEBUG + if ((pmap_con_dbg & (CD_BOOT | CD_FULL)) == (CD_BOOT | CD_FULL)) { + printf("Processor %d running virtual.\n", i); + } +#endif + SETBIT_CPUSET(i, &kernel_pmap->pm_cpus); + } + +#ifdef DEBUG + if ((pmap_con_dbg & (CD_BOOT | CD_FULL)) == (CD_BOOT | CD_FULL)) { + printf("running virtual - avail_next 0x%x\n", *phys_start); + } +#endif +} + +/* + * Routine: PMAP_INIT + * + * Function: + * Initialize the pmap module. It is called by vm_init, to initialize + * any structures that the pmap system needs to map virtual memory. + * + * Calls: + * pool_init + * + * This routine does not really have much to do. It initializes + * pools for pmap structures and pv_entry structures. + */ +void +pmap_init(void) +{ +#ifdef DEBUG + if (pmap_con_dbg & CD_INIT) + printf("pmap_init()\n"); +#endif + + pool_init(&pmappool, sizeof(struct pmap), 0, 0, 0, "pmappl", + &pool_allocator_nointr); + pool_init(&pvpool, sizeof(pv_entry_t), 0, 0, 0, "pvpl", NULL); +} /* pmap_init() */ + +/* + * Routine: PMAP_ZERO_PAGE + * + * Function: + * Zeroes the specified page. + * + * Parameters: + * pg page to zero + * + * Extern/Global: + * phys_map_vaddr + * + * Calls: + * m88k_protection + * + * Special Assumptions: + * no locking required + * + * This routine maps the physical pages at the 'phys_map' virtual + * address set up in pmap_bootstrap. It flushes the TLB to make the new + * mappings effective, and zeros all the bits. + */ +void +pmap_zero_page(struct vm_page *pg) +{ + paddr_t pa = VM_PAGE_TO_PHYS(pg); + vaddr_t va; + int spl; + int cpu = cpu_number(); + pt_entry_t *pte; + + CHECK_PAGE_ALIGN(pa, "pmap_zero_page"); + + va = (vaddr_t)(phys_map_vaddr + 2 * (cpu << PAGE_SHIFT)); + pte = pmap_pte(kernel_pmap, va); + + SPLVM(spl); + + *pte = m88k_protection(kernel_pmap, VM_PROT_READ | VM_PROT_WRITE) | + CACHE_GLOBAL | PG_V | pa; + + /* + * We don't need the flush_atc_entry() dance, as these pages are + * bound to only one cpu. + */ + cmmu_flush_tlb(cpu, TRUE, va, PAGE_SIZE); + + /* + * The page is likely to be a non-kernel mapping, and as + * such write back. Also, we might have split U/S caches! + * So be sure to have the pa flushed after the filling. + */ + bzero((void *)va, PAGE_SIZE); + cmmu_flush_data_cache(cpu, pa, PAGE_SIZE); + + SPLX(spl); +} + +/* + * Routine: PMAP_CREATE + * + * Function: + * Create and return a physical map. If the size specified for the + * map is zero, the map is an actual physical map, and may be referenced + * by the hardware. If the size specified is non-zero, the map will be + * used in software only, and is bounded by that size. + * + * This routines allocates a pmap structure. + */ +pmap_t +pmap_create(void) +{ + pmap_t pmap; + sdt_entry_t *segdt; + paddr_t stpa; + u_int s; +#ifdef PMAP_USE_BATC + int i; +#endif + + pmap = pool_get(&pmappool, PR_WAITOK); + bzero(pmap, sizeof(*pmap)); + + /* + * Allocate memory for *actual* segment table and *shadow* table. + */ + s = round_page(2 * SDT_SIZE); +#ifdef DEBUG + if (pmap_con_dbg & CD_CREAT) { + printf("(pmap_create: %x) need %d pages for sdt\n", + curproc, atop(s)); + } +#endif + + segdt = (sdt_entry_t *)uvm_km_zalloc(kernel_map, s); + if (segdt == NULL) + panic("pmap_create: uvm_km_zalloc failure"); + + /* + * Initialize pointer to segment table both virtual and physical. + */ + pmap->pm_stab = segdt; + if (pmap_extract(kernel_pmap, (vaddr_t)segdt, + (paddr_t *)&stpa) == FALSE) + panic("pmap_create: pmap_extract failed!"); + pmap->pm_apr = (atop(stpa) << PG_SHIFT) | + CACHE_GLOBAL | APR_V; + +#ifdef DEBUG + if (!PAGE_ALIGNED(stpa)) + panic("pmap_create: sdt_table 0x%x not aligned on page boundary", + (int)stpa); + + if (pmap_con_dbg & CD_CREAT) { + printf("(pmap_create: %x) pmap=0x%p, pm_stab=0x%x (pa 0x%x)\n", + curproc, pmap, pmap->pm_stab, stpa); + } +#endif + + /* memory for page tables should not be writeback or local */ + pmap_cache_ctrl(kernel_pmap, + (vaddr_t)segdt, (vaddr_t)segdt + s, CACHE_GLOBAL | CACHE_WT); + + /* + * Initialize SDT_ENTRIES. + */ + /* + * There is no need to clear segment table, since uvm_km_zalloc + * provides us clean pages. + */ + + /* + * Initialize pmap structure. + */ + pmap->pm_count = 1; + simple_lock_init(&pmap->pm_lock); + pmap->pm_cpus = 0; + +#ifdef PMAP_USE_BATC + /* initialize block address translation cache */ + for (i = 0; i < BATC_MAX; i++) { + pmap->pm_ibatc[i].bits = 0; + pmap->pm_dbatc[i].bits = 0; + } +#endif + + return pmap; +} + +/* + * Routine: PMAP_RELEASE + * + * Internal procedure used by pmap_destroy() to actualy deallocate + * the tables. + * + * Parameters: + * pmap pointer to pmap structure + * + * Calls: + * pmap_pte + * uvm_km_free + * + * Special Assumptions: + * No locking is needed, since this is only called which the + * pm_count field of the pmap structure goes to zero. + * + * This routine sequences of through the user address space, releasing + * all translation table space back to the system using uvm_km_free. + * The loops are indexed by the virtual address space + * ranges represented by the table group sizes(PDT_VA_SPACE). + * + */ +void +pmap_release(pmap_t pmap) +{ + unsigned long sdt_va; /* outer loop index */ + sdt_entry_t *sdttbl; /* ptr to first entry in the segment table */ + pt_entry_t *gdttbl; /* ptr to first entry in a page table */ + +#ifdef DEBUG + if (pmap_con_dbg & CD_FREE) + printf("(pmap_release: %x) pmap %x\n", curproc, pmap); +#endif + + /* Segment table Loop */ + for (sdt_va = VM_MIN_ADDRESS; sdt_va < VM_MAX_ADDRESS; + sdt_va += PDT_VA_SPACE) { + if ((gdttbl = pmap_pte(pmap, (vaddr_t)sdt_va)) != PT_ENTRY_NULL) { +#ifdef DEBUG + if ((pmap_con_dbg & (CD_FREE | CD_FULL)) == (CD_FREE | CD_FULL)) + printf("(pmap_release: %x) free page table = 0x%x\n", + curproc, gdttbl); +#endif + uvm_km_free(kernel_map, (vaddr_t)gdttbl, PAGE_SIZE); + } + } + + /* + * Freeing both *actual* and *shadow* segment tables + */ + sdttbl = pmap->pm_stab; /* addr of segment table */ +#ifdef DEBUG + if ((pmap_con_dbg & (CD_FREE | CD_FULL)) == (CD_FREE | CD_FULL)) + printf("(pmap_release: %x) free segment table = 0x%x\n", + curproc, sdttbl); +#endif + uvm_km_free(kernel_map, (vaddr_t)sdttbl, round_page(2 * SDT_SIZE)); + +#ifdef DEBUG + if (pmap_con_dbg & CD_FREE) + printf("(pmap_release: %x) pm_count = 0\n", curproc); +#endif +} + +/* + * Routine: PMAP_DESTROY + * + * Function: + * Retire the given physical map from service. Should only be called + * if the map contains no valid mappings. + * + * Parameters: + * pmap pointer to pmap structure + * + * Calls: + * pmap_release + * pool_put + * + * Special Assumptions: + * Map contains no valid mappings. + * + * This routine decrements the reference count in the pmap + * structure. If it goes to zero, pmap_release is called to release + * the memory space to the system. Then, call pool_put to free the + * pmap structure. + */ +void +pmap_destroy(pmap_t pmap) +{ + int count; + +#ifdef DEBUG + if (pmap == kernel_pmap) + panic("pmap_destroy: Attempt to destroy kernel pmap"); +#endif + + simple_lock(&pmap->pm_lock); + count = --pmap->pm_count; + simple_unlock(&pmap->pm_lock); + if (count == 0) { + pmap_release(pmap); + pool_put(&pmappool, pmap); + } +} + + +/* + * Routine: PMAP_REFERENCE + * + * Function: + * Add a reference to the specified pmap. + * + * Parameters: + * pmap pointer to pmap structure + * + * Under a pmap read lock, the pm_count field of the pmap structure + * is incremented. The function then returns. + */ +void +pmap_reference(pmap_t pmap) +{ + + simple_lock(&pmap->pm_lock); + pmap->pm_count++; + simple_unlock(&pmap->pm_lock); +} + +/* + * Routine: PMAP_REMOVE_PTE (internal) + * + * Function: + * Invalidate a given page table entry associated with the + * given virtual address. + * + * Parameters: + * pmap pointer to pmap structure + * va virtual address of page to remove + * pte existing pte + * + * External/Global: + * pv lists + * + * Calls: + * pool_put + * invalidate_pte + * flush_atc_entry + * + * Special Assumptions: + * The pmap must be locked. + * + * If the PTE is valid, the routine must invalidate the entry. The + * 'modified' bit, if on, is referenced to the VM, and into the appropriate + * entry in the PV list entry. Next, the function must find the PV + * list entry associated with this pmap/va (if it doesn't exist - the function + * panics). The PV list entry is unlinked from the list, and returned to + * its zone. + */ +void +pmap_remove_pte(pmap_t pmap, vaddr_t va, pt_entry_t *pte) +{ + pt_entry_t opte; + pv_entry_t prev, cur, pvl; + struct vm_page *pg; + paddr_t pa; + u_int users; + boolean_t kflush; + +#ifdef DEBUG + if (pmap_con_dbg & CD_RM) { + if (pmap == kernel_pmap) + printf("(pmap_remove_pte: %x) pmap kernel va %x\n", curproc, va); + else + printf("(pmap_remove: %x) pmap %x va %x\n", curproc, pmap, va); + } +#endif + + if (pte == PT_ENTRY_NULL || !PDT_VALID(pte)) { + return; /* no page mapping, nothing to do! */ + } + + users = pmap->pm_cpus; + kflush = pmap == kernel_pmap; + + /* + * Update statistics. + */ + pmap->pm_stats.resident_count--; + if (pmap_pte_w(pte)) + pmap->pm_stats.wired_count--; + + pa = ptoa(PG_PFNUM(*pte)); + + /* + * Invalidate the pte. + */ + + opte = invalidate_pte(pte) & (PG_U | PG_M); + flush_atc_entry(users, va, kflush); + + pg = PHYS_TO_VM_PAGE(pa); + + /* If this isn't a managed page, just return. */ + if (pg == NULL) + return; + + /* + * Remove the mapping from the pvlist for + * this physical page. + */ + pvl = pg_to_pvh(pg); + +#ifdef DIAGNOSTIC + if (pvl->pv_pmap == PMAP_NULL) + panic("pmap_remove_pte: null pv_list"); +#endif + + prev = PV_ENTRY_NULL; + for (cur = pvl; cur != PV_ENTRY_NULL; cur = cur->pv_next) { + if (cur->pv_va == va && cur->pv_pmap == pmap) + break; + prev = cur; + } + if (cur == PV_ENTRY_NULL) { + panic("pmap_remove_pte: mapping for va " + "0x%lx (pa 0x%lx) not in pv list at 0x%p", + va, pa, pvl); + } + + if (prev == PV_ENTRY_NULL) { + /* + * Hander is the pv_entry. Copy the next one + * to hander and free the next one (we can't + * free the hander) + */ + cur = cur->pv_next; + if (cur != PV_ENTRY_NULL) { + cur->pv_flags = pvl->pv_flags; + *pvl = *cur; + pool_put(&pvpool, cur); + } else { + pvl->pv_pmap = PMAP_NULL; + } + } else { + prev->pv_next = cur->pv_next; + pool_put(&pvpool, cur); + } + + /* Update saved attributes for managed page */ + pvl->pv_flags |= opte; +} + +/* + * Routine: PMAP_REMOVE_RANGE (internal) + * + * Function: + * Invalidate page table entries associated with the + * given virtual address range. The entries given are the first + * (inclusive) and last (exclusive) entries for the VM pages. + * + * Parameters: + * pmap pointer to pmap structure + * s virtual address of start of range to remove + * e virtual address of end of range to remove + * + * External/Global: + * pv lists + * + * Calls: + * pmap_pte + * pmap_remove_pte + * + * Special Assumptions: + * The pmap must be locked. + * + * This routine sequences through the pages defined by the given + * range. For each page, the associated page table entry (PTE) is + * invalidated via pmap_remove_pte(). + * + * Empty segments are skipped for performance. + */ +void +pmap_remove_range(pmap_t pmap, vaddr_t s, vaddr_t e) +{ + vaddr_t va; + +#ifdef DEBUG + if (pmap_con_dbg & CD_RM) { + if (pmap == kernel_pmap) + printf("(pmap_remove: %x) pmap kernel s %x e %x\n", curproc, s, e); + else + printf("(pmap_remove: %x) pmap %x s %x e %x\n", curproc, pmap, s, e); + } +#endif + + /* + * Loop through the range in vm_page_size increments. + */ + for (va = s; va < e; va += PAGE_SIZE) { + sdt_entry_t *sdt; + + sdt = SDTENT(pmap, va); + + /* If no segment table, skip a whole segment */ + if (!SDT_VALID(sdt)) { + va &= SDT_MASK; + va += (1 << SDT_SHIFT) - PAGE_SIZE; + continue; + } + + pmap_remove_pte(pmap, va, pmap_pte(pmap, va)); + } +} + +/* + * Routine: PMAP_REMOVE + * + * Function: + * Remove the given range of addresses from the specified map. + * It is assumed that start and end are properly rounded to the VM page + * size. + * + * Parameters: + * pmap pointer to pmap structure + * s + * e + * + * Special Assumptions: + * Assumes not all entries must be valid in specified range. + * + * Calls: + * pmap_remove_range + * + * After taking pmap read lock, pmap_remove_range is called to do the + * real work. + */ +void +pmap_remove(pmap_t pmap, vaddr_t s, vaddr_t e) +{ + int spl; + + if (pmap == PMAP_NULL) + return; + +#ifdef DEBUG + if (s >= e) + panic("pmap_remove: start greater than end address"); +#endif + + PMAP_LOCK(pmap, spl); + pmap_remove_range(pmap, s, e); + PMAP_UNLOCK(pmap, spl); +} + +/* + * Routine: PMAP_REMOVE_ALL + * + * Function: + * Removes this physical page from all physical maps in which it + * resides. Reflects back modify bits to the pager. + * + * Parameters: + * pg physical pages which is to + * be removed from all maps + * + * Extern/Global: + * pv lists + * + * Calls: + * simple_lock + * pmap_pte + * pool_put + * + * If the page specified by the given address is not a managed page, + * this routine simply returns. Otherwise, the PV list associated with + * that page is traversed. For each pmap/va pair pmap_pte is called to + * obtain a pointer to the page table entry (PTE) associated with the + * va (the PTE must exist and be valid, otherwise the routine panics). + * The hardware 'modified' bit in the PTE is examined. If it is on, the + * corresponding bit in the PV list entry corresponding + * to the physical page is set to 1. + * Then, the PTE is invalidated, and the PV list entry is unlinked and + * freed. + * + * At the end of this function, the PV list for the specified page + * will be null. + */ +void +pmap_remove_all(struct vm_page *pg) +{ + pt_entry_t *pte; + pv_entry_t pvl; + vaddr_t va; + pmap_t pmap; + int spl; + + if (pg == NULL) { + /* not a managed page. */ +#ifdef DEBUG + if (pmap_con_dbg & CD_RMAL) + printf("(pmap_remove_all: %x) vm page 0x%x not a managed page\n", curproc, pg); +#endif + return; + } + +#ifdef DEBUG + if (pmap_con_dbg & CD_RMAL) + printf("(pmap_remove_all: %x) va %x\n", curproc, pg, pg_to_pvh(pg)->pv_va); +#endif + + SPLVM(spl); + /* + * Walk down PV list, removing all mappings. + * We don't have to lock the pv list, since we have the entire pmap + * system. + */ +remove_all_Retry: + + pvl = pg_to_pvh(pg); + + /* + * Loop for each entry on the pv list + */ + while (pvl != PV_ENTRY_NULL && (pmap = pvl->pv_pmap) != PMAP_NULL) { + if (!simple_lock_try(&pmap->pm_lock)) + goto remove_all_Retry; + + va = pvl->pv_va; + pte = pmap_pte(pmap, va); + + if (pte == PT_ENTRY_NULL || !PDT_VALID(pte)) { + pvl = pvl->pv_next; + goto next; /* no page mapping */ + } + if (pmap_pte_w(pte)) { +#ifdef DEBUG + if (pmap_con_dbg & CD_RMAL) + printf("pmap_remove_all: wired mapping for %lx not removed\n", + pg); +#endif + pvl = pvl->pv_next; + goto next; + } + + pmap_remove_pte(pmap, va, pte); + + /* + * Do not free any page tables, + * leaves that for when VM calls pmap_collect(). + */ +next: + simple_unlock(&pmap->pm_lock); + } + SPLX(spl); +} + +/* + * Routine: PMAP_PROTECT + * + * Function: + * Sets the physical protection on the specified range of this map + * as requested. + * + * Parameters: + * pmap pointer to pmap structure + * s start address of start of range + * e end address of end of range + * prot desired protection attributes + * + * Calls: + * PMAP_LOCK, PMAP_UNLOCK + * CHECK_PAGE_ALIGN + * pmap_pte + * PDT_VALID + * + * This routine sequences through the pages of the specified range. + * For each, it calls pmap_pte to acquire a pointer to the page table + * entry (PTE). If the PTE is invalid, or non-existent, nothing is done. + * Otherwise, the PTE's protection attributes are adjusted as specified. + */ +void +pmap_protect(pmap_t pmap, vaddr_t s, vaddr_t e, vm_prot_t prot) +{ + int spl; + pt_entry_t *pte, ap; + vaddr_t va; + u_int users; + boolean_t kflush; + +#ifdef DEBUG + if (s >= e) + panic("pmap_protect: start grater than end address"); +#endif + + if ((prot & VM_PROT_READ) == 0) { + pmap_remove(pmap, s, e); + return; + } + + ap = m88k_protection(pmap, prot) & PG_PROT; + + PMAP_LOCK(pmap, spl); + + users = pmap->pm_cpus; + kflush = pmap == kernel_pmap; + + CHECK_PAGE_ALIGN(s, "pmap_protect"); + + /* + * Loop through the range in vm_page_size increments. + */ + for (va = s; va < e; va += PAGE_SIZE) { + sdt_entry_t *sdt; + + sdt = SDTENT(pmap, va); + + /* If no segment table, skip a whole segment */ + if (!SDT_VALID(sdt)) { + va &= SDT_MASK; + va += (1 << SDT_SHIFT) - PAGE_SIZE; + continue; + } + + pte = pmap_pte(pmap, va); + if (pte == PT_ENTRY_NULL || !PDT_VALID(pte)) { + continue; /* no page mapping */ + } + + /* + * Invalidate pte temporarily to avoid the + * modified bit and/or the reference bit being + * written back by any other cpu. + */ + *pte = (invalidate_pte(pte) & ~PG_PROT) | ap; + flush_atc_entry(users, va, kflush); + pte++; + } + PMAP_UNLOCK(pmap, spl); +} + +/* + * Routine: PMAP_EXPAND + * + * Function: + * Expands a pmap to be able to map the specified virtual address. + * New kernel virtual memory is allocated for a page table. + * + * Must be called with the pmap system and the pmap unlocked, since + * these must be unlocked to use vm_allocate or vm_deallocate (via + * uvm_km_zalloc). Thus it must be called in a unlock/lock loop + * that checks whether the map has been expanded enough. (We won't loop + * forever, since page table aren't shrunk.) + * + * Parameters: + * pmap point to pmap structure + * v VA indicating which tables are needed + * + * Extern/Global: + * user_pt_map + * kernel_pmap + * + * Calls: + * pmap_pte + * uvm_km_free + * uvm_km_zalloc + * pmap_extract + * + * Special Assumptions + * no pmap locks held + * pmap != kernel_pmap + * + * 1: This routine immediately allocates space for a page table. + * + * 2: The page table entries (PTEs) are initialized (set invalid), and + * the corresponding segment table entry is set to point to the new + * page table. + */ +void +pmap_expand(pmap_t pmap, vaddr_t v) +{ + int spl; + vaddr_t pdt_vaddr; + paddr_t pdt_paddr; + sdt_entry_t *sdt; + pt_entry_t *pte; + +#ifdef DEBUG + if (pmap_con_dbg & CD_EXP) + printf ("(pmap_expand: %x) map %x v %x\n", curproc, pmap, v); +#endif + + CHECK_PAGE_ALIGN(v, "pmap_expand"); + + /* XXX */ + pdt_vaddr = uvm_km_zalloc(kernel_map, PAGE_SIZE); + if (pmap_extract(kernel_pmap, pdt_vaddr, &pdt_paddr) == FALSE) + panic("pmap_expand: pmap_extract failed"); + + /* memory for page tables should not be writeback or local */ + pmap_cache_ctrl(kernel_pmap, + pdt_vaddr, pdt_vaddr + PAGE_SIZE, CACHE_GLOBAL | CACHE_WT); + + PMAP_LOCK(pmap, spl); + + if ((pte = pmap_pte(pmap, v)) != PT_ENTRY_NULL) { + /* + * Someone else caused us to expand + * during our vm_allocate. + */ + simple_unlock(&pmap->pm_lock); + uvm_km_free(kernel_map, pdt_vaddr, PAGE_SIZE); + +#ifdef DEBUG + if (pmap_con_dbg & CD_EXP) + printf("(pmap_expand: %x) table has already been allocated\n", curproc); +#endif + splx(spl); + return; + } + /* + * Apply a mask to V to obtain the vaddr of the beginning of + * its containing page 'table group', i.e. the group of + * page tables that fit eithin a single VM page. + * Using that, obtain the segment table pointer that references the + * first page table in the group, and initialize all the + * segment table descriptions for the page 'table group'. + */ + v &= ~((1 << (PDT_BITS + PG_BITS)) - 1); + + sdt = SDTENT(pmap, v); + + /* + * Init each of the segment entries to point the freshly allocated + * page tables. + */ + *((sdt_entry_t *)sdt) = pdt_paddr | SG_RW | SG_V; + *((sdt_entry_t *)(sdt + SDT_ENTRIES)) = pdt_vaddr | SG_RW | SG_V; + + PMAP_UNLOCK(pmap, spl); +} + +/* + * Routine: PMAP_ENTER + * + * Function: + * Insert the given physical page (p) at the specified virtual + * address (v) in the target phisical map with the protecton requested. + * If specified, the page will be wired down, meaning that the + * related pte can not be reclaimed. + * + * N.B.: This is the only routine which MAY NOT lazy-evaluation or lose + * information. That is, this routine must actually insert this page + * into the given map NOW. + * + * Parameters: + * pmap pointer to pmap structure + * va VA of page to be mapped + * pa PA of page to be mapped + * prot protection attributes for page + * wired wired attribute for page + * + * Extern/Global: + * pv lists + * + * Calls: + * m88k_protection + * pmap_pte + * pmap_expand + * pmap_remove_pte + * + * This routine starts off by calling pmap_pte to obtain a (virtual) + * pointer to the page table entry corresponding to given virtual + * address. If the page table itself does not exist, pmap_expand is + * called to allocate it. + * + * If the page table entry (PTE) already maps the given physical page, + * all that is needed is to set the protection and wired attributes as + * given. TLB entries are flushed and pmap_enter returns. + * + * If the page table entry (PTE) maps a different physical page than + * that given, the old mapping is removed by a call to map_remove_range. + * And execution of pmap_enter continues. + * + * To map the new physical page, the routine first inserts a new + * entry in the PV list exhibiting the given pmap and virtual address. + * It then inserts the physical page address, protection attributes, and + * wired attributes into the page table entry (PTE). + * + * + * get machine-dependent prot code + * get the pte for this page + * if necessary pmap_expand(pmap, v) + * if (changing wired attribute or protection) { + * flush entry from TLB + * update template + * for (ptes per vm page) + * stuff pte + * } else if (mapped at wrong addr) + * flush entry from TLB + * pmap_remove_pte + * } else { + * enter mapping in pv_list + * setup template and stuff ptes + * } + * + */ +int +pmap_enter(pmap_t pmap, vaddr_t va, paddr_t pa, vm_prot_t prot, int flags) +{ + int spl; + pt_entry_t *pte, template; + paddr_t old_pa; + pv_entry_t pv_e, pvl; + u_int users; + boolean_t kflush; + boolean_t wired = (flags & PMAP_WIRED) != 0; + struct vm_page *pg; + + CHECK_PAGE_ALIGN(va, "pmap_entry - va"); + CHECK_PAGE_ALIGN(pa, "pmap_entry - pa"); + +#ifdef DEBUG + if (pmap_con_dbg & CD_ENT) { + if (pmap == kernel_pmap) + printf("(pmap_enter: %x) pmap kernel va %x pa %x\n", curproc, va, pa); + else + printf("(pmap_enter: %x) pmap %x va %x pa %x\n", curproc, pmap, va, pa); + } + + /* copying/zeroing pages are magic */ + if (pmap == kernel_pmap && + va >= phys_map_vaddr && va < phys_map_vaddr_end) { + return 0; + } +#endif + + template = m88k_protection(pmap, prot); + + PMAP_LOCK(pmap, spl); + users = pmap->pm_cpus; + kflush = pmap == kernel_pmap; + + /* + * Expand pmap to include this pte. + */ + while ((pte = pmap_pte(pmap, va)) == PT_ENTRY_NULL) { + if (pmap == kernel_pmap) { + pmap_expand_kmap(va, VM_PROT_READ | VM_PROT_WRITE); + } else { + /* + * Must unlock to expand the pmap. + */ + simple_unlock(&pmap->pm_lock); + pmap_expand(pmap, va); + simple_lock(&pmap->pm_lock); + } + } + /* + * Special case if the physical page is already mapped at this address. + */ + old_pa = ptoa(PG_PFNUM(*pte)); +#ifdef DEBUG + if (pmap_con_dbg & CD_ENT) + printf("(pmap_enter) old_pa %x pte %x\n", old_pa, *pte); +#endif + if (old_pa == pa) { + /* May be changing its wired attributes or protection */ + if (wired && !(pmap_pte_w(pte))) + pmap->pm_stats.wired_count++; + else if (!wired && pmap_pte_w(pte)) + pmap->pm_stats.wired_count--; + + pvl = NULL; + } else { /* if (pa == old_pa) */ + /* Remove old mapping from the PV list if necessary. */ + pmap_remove_pte(pmap, va, pte); + + pg = PHYS_TO_VM_PAGE(pa); + if (pg != NULL) { + /* + * Enter the mapping in the PV list for this + * physical page. + */ + pvl = pg_to_pvh(pg); + + if (pvl->pv_pmap == PMAP_NULL) { + /* + * No mappings yet + */ + pvl->pv_va = va; + pvl->pv_pmap = pmap; + pvl->pv_next = PV_ENTRY_NULL; + pvl->pv_flags = 0; + + } else { +#ifdef DEBUG + /* + * Check that this mapping is not already there + */ + for (pv_e = pvl; pv_e; pv_e = pv_e->pv_next) + if (pv_e->pv_pmap == pmap && + pv_e->pv_va == va) + panic("pmap_enter: already in pv_list"); +#endif + /* + * Add new pv_entry after header. + */ + pv_e = pool_get(&pvpool, PR_NOWAIT); + if (pv_e == NULL) { + if (flags & PMAP_CANFAIL) { + PMAP_UNLOCK(pmap, spl); + return (ENOMEM); + } else + panic("pmap_enter: " + "pvpool exhausted"); + } + pv_e->pv_va = va; + pv_e->pv_pmap = pmap; + pv_e->pv_next = pvl->pv_next; + pv_e->pv_flags = 0; + pvl->pv_next = pv_e; + } + } + + /* + * And count the mapping. + */ + pmap->pm_stats.resident_count++; + if (wired) + pmap->pm_stats.wired_count++; + } /* if (pa == old_pa) ... else */ + + template |= PG_V; + if (wired) + template |= PG_W; + + if ((unsigned long)pa >= MAXPHYSMEM) + template |= CACHE_INH; + else + template |= CACHE_GLOBAL; + + if (flags & VM_PROT_WRITE) + template |= PG_U | PG_M; + else if (flags & VM_PROT_ALL) + template |= PG_U; + + /* + * Invalidate pte temporarily to avoid being written + * back the modified bit and/or the reference bit by + * any other cpu. + */ + template |= invalidate_pte(pte) & (PG_U | PG_M); + *pte = template | pa; + flush_atc_entry(users, va, kflush); +#ifdef DEBUG + if (pmap_con_dbg & CD_ENT) + printf("(pmap_enter) set pte to %x\n", *pte); +#endif + + /* + * Cache attribute flags + */ + if (pvl != NULL) + pvl->pv_flags |= (template & (PG_U | PG_M)); + + PMAP_UNLOCK(pmap, spl); + + return 0; +} + +/* + * Routine: pmap_unwire + * + * Function: Change the wiring attributes for a map/virtual-address pair. + * + * Parameters: + * pmap pointer to pmap structure + * v virtual address of page to be unwired + * + * Calls: + * pmap_pte + * + * Special Assumptions: + * The mapping must already exist in the pmap. + */ +void +pmap_unwire(pmap_t pmap, vaddr_t v) +{ + pt_entry_t *pte; + int spl; + + PMAP_LOCK(pmap, spl); + + if ((pte = pmap_pte(pmap, v)) == PT_ENTRY_NULL) + panic("pmap_unwire: pte missing"); + + if (pmap_pte_w(pte)) { + /* unwired mapping */ + pmap->pm_stats.wired_count--; + *pte &= ~PG_W; + } + + PMAP_UNLOCK(pmap, spl); +} + +/* + * Routine: PMAP_EXTRACT + * + * Function: + * Extract the physical page address associoated + * with the given map/virtual_address pair. + * + * Parameters: + * pmap pointer to pmap structure + * va virtual address + * pap storage for result. + * + * Calls: + * PMAP_LOCK, PMAP_UNLOCK + * pmap_pte + * + * If BATC mapping is enabled and the specified pmap is kernel_pmap, + * batc_entry is scanned to find out the mapping. + * + * Then the routine calls pmap_pte to get a (virtual) pointer to + * the page table entry (PTE) associated with the given virtual + * address. If the page table does not exist, or if the PTE is not valid, + * then 0 address is returned. Otherwise, the physical page address from + * the PTE is returned. + */ +boolean_t +pmap_extract(pmap_t pmap, vaddr_t va, paddr_t *pap) +{ + pt_entry_t *pte; + paddr_t pa; + int spl; + boolean_t rv = FALSE; + +#ifdef PMAP_USE_BATC + int i; +#endif + +#ifdef DIAGNOSTIC + if (pmap == PMAP_NULL) + panic("pmap_extract: pmap is NULL"); +#endif + +#ifdef PMAP_USE_BATC + /* + * check BATC first + */ + if (pmap == kernel_pmap && batc_used != 0) + for (i = batc_used - 1; i != 0; i--) + if (batc_entry[i].lba == M88K_BTOBLK(va)) { + if (pap != NULL) + *pap = (batc_entry[i].pba << BATC_BLKSHIFT) | + (va & BATC_BLKMASK); + return TRUE; + } +#endif + + PMAP_LOCK(pmap, spl); + + pte = pmap_pte(pmap, va); + if (pte != PT_ENTRY_NULL && PDT_VALID(pte)) { + rv = TRUE; + if (pap != NULL) { + pa = ptoa(PG_PFNUM(*pte)); + pa |= (va & PAGE_MASK); /* offset within page */ + *pap = pa; + } + } + + PMAP_UNLOCK(pmap, spl); + return rv; +} + +/* + * Routine: PMAP_COLLECT + * + * Runction: + * Garbage collects the physical map system for pages which are + * no longer used. there may well be pages which are not + * referenced, but others may be collected as well. + * Called by the pageout daemon when pages are scarce. + * + * Parameters: + * pmap pointer to pmap structure + * + * Calls: + * pmap_pte + * pmap_remove_range + * uvm_km_free + * + * The intent of this routine is to release memory pages being used + * by translation tables. They can be release only if they contain no + * valid mappings, and their parent table entry has been invalidated. + * + * The routine sequences through the entries user address space, + * inspecting page-sized groups of page tables for wired entries. If + * a full page of tables has no wired enties, any otherwise valid + * entries are invalidated (via pmap_remove_range). Then, the segment + * table entries corresponding to this group of page tables are + * invalidated. Finally, uvm_km_free is called to return the page to the + * system. + * + * If all entries in a segment table are invalidated, it too can + * be returned to the system. + */ +void +pmap_collect(pmap_t pmap) +{ + vaddr_t sdt_va; /* outer loop index */ + sdt_entry_t *sdtp; /* ptr to index into segment table */ + pt_entry_t *gdttbl; /* ptr to first entry in a page table */ + pt_entry_t *gdttblend; /* ptr to byte after last entry in + table group */ + pt_entry_t *gdtp; /* ptr to index into a page table */ + boolean_t found_gdt_wired; /* flag indicating a wired page exists + in a page table's address range */ + int spl; + +#ifdef DEBUG + if (pmap_con_dbg & CD_COL) + printf ("(pmap_collect: %x) pmap %x\n", curproc, pmap); +#endif + + PMAP_LOCK(pmap, spl); + + sdtp = pmap->pm_stab; /* addr of segment table */ + + /* Segment table loop */ + for (sdt_va = VM_MIN_ADDRESS; sdt_va < VM_MAX_ADDRESS; + sdt_va += PDT_VA_SPACE, sdtp++) { + gdttbl = pmap_pte(pmap, sdt_va); + if (gdttbl == PT_ENTRY_NULL) + continue; /* no maps in this range */ + + gdttblend = gdttbl + PDT_ENTRIES; + + /* scan page maps for wired pages */ + found_gdt_wired = FALSE; + for (gdtp = gdttbl; gdtp < gdttblend; gdtp++) { + if (pmap_pte_w(gdtp)) { + found_gdt_wired = TRUE; + break; + } + } + + if (found_gdt_wired) + continue; /* can't free this range */ + + /* invalidate all maps in this range */ + pmap_remove_range(pmap, sdt_va, sdt_va + PDT_VA_SPACE); + + /* + * we can safely deallocate the page map(s) + */ + *((sdt_entry_t *) sdtp) = 0; + *((sdt_entry_t *)(sdtp + SDT_ENTRIES)) = 0; + + /* + * we have to unlock before freeing the table, since + * uvm_km_free will invoke another pmap routine + */ + simple_unlock(&pmap->pm_lock); + uvm_km_free(kernel_map, (vaddr_t)gdttbl, PAGE_SIZE); + simple_lock(&pmap->pm_lock); + } + + PMAP_UNLOCK(pmap, spl); + +#ifdef DEBUG + if (pmap_con_dbg & CD_COL) + printf("(pmap_collect: %x) done\n", curproc); +#endif +} + +/* + * Routine: PMAP_ACTIVATE + * + * Function: + * Binds the pmap associated to the process to the current processor. + * + * Parameters: + * p pointer to proc structure + * + * Notes: + * If the specified pmap is not kernel_pmap, this routine stores its + * apr template into UAPR (user area pointer register) in the + * CMMUs connected to the specified CPU. + * + * Then it flushes the TLBs mapping user virtual space, in the CMMUs + * connected to the specified CPU. + */ +void +pmap_activate(struct proc *p) +{ + pmap_t pmap = vm_map_pmap(&p->p_vmspace->vm_map); + int cpu = cpu_number(); +#ifdef PMAP_USE_BATC + int n; +#endif + +#ifdef DEBUG + if (pmap_con_dbg & CD_ACTIVATE) + printf("(pmap_activate: %x) pmap 0x%p\n", p, pmap); +#endif + + if (pmap != kernel_pmap) { + /* + * Lock the pmap to put this cpu in its active set. + */ + simple_lock(&pmap->pm_lock); + +#ifdef PMAP_USE_BATC + /* + * cmmu_pmap_activate will set the uapr and the batc entries, + * then flush the *USER* TLB. IF THE KERNEL WILL EVER CARE + * ABOUT THE BATC ENTRIES, THE SUPERVISOR TLBs SHOULB BE + * FLUSHED AS WELL. + */ + cmmu_pmap_activate(cpu, pmap->pm_apr, + pmap->pm_ibatc, pmap->pm_dbatc); + for (n = 0; n < BATC_MAX; n++) + *(register_t *)&batc_entry[n] = pmap->pm_ibatc[n].bits; +#else + cmmu_set_uapr(pmap->pm_apr); + cmmu_flush_tlb(cpu, FALSE, VM_MIN_ADDRESS, + VM_MAX_ADDRESS - VM_MIN_ADDRESS); +#endif /* PMAP_USE_BATC */ + + /* + * Mark that this cpu is using the pmap. + */ + SETBIT_CPUSET(cpu, &(pmap->pm_cpus)); + simple_unlock(&pmap->pm_lock); + } +} + +/* + * Routine: PMAP_DEACTIVATE + * + * Function: + * Unbinds the pmap associated to the process from the current processor. + * + * Parameters: + * p pointer to proc structure + */ +void +pmap_deactivate(struct proc *p) +{ + pmap_t pmap = vm_map_pmap(&p->p_vmspace->vm_map); + int cpu = cpu_number(); + + if (pmap != kernel_pmap) { + /* + * we expect the spl is already raised to sched level. + */ + simple_lock(&pmap->pm_lock); + CLRBIT_CPUSET(cpu, &(pmap->pm_cpus)); + simple_unlock(&pmap->pm_lock); + } +} + +/* + * Routine: PMAP_COPY_PAGE + * + * Function: + * Copies the specified pages. + * + * Parameters: + * src PA of source page + * dst PA of destination page + * + * Extern/Global: + * phys_map_vaddr + * + * Calls: + * m88k_protection + * + * Special Assumptions: + * no locking required + * + * This routine maps the physical pages at the 'phys_map' virtual + * addresses set up in pmap_bootstrap. It flushes the TLB to make the + * new mappings effective, and performs the copy. + */ +void +pmap_copy_page(struct vm_page *srcpg, struct vm_page *dstpg) +{ + paddr_t src = VM_PAGE_TO_PHYS(srcpg); + paddr_t dst = VM_PAGE_TO_PHYS(dstpg); + vaddr_t dstva, srcva; + int spl; + pt_entry_t *dstpte, *srcpte; + int cpu = cpu_number(); + + CHECK_PAGE_ALIGN(src, "pmap_copy_page - src"); + CHECK_PAGE_ALIGN(dst, "pmap_copy_page - dst"); + + dstva = (vaddr_t)(phys_map_vaddr + 2 * (cpu << PAGE_SHIFT)); + srcva = dstva + PAGE_SIZE; + dstpte = pmap_pte(kernel_pmap, dstva); + srcpte = pmap_pte(kernel_pmap, srcva); + + SPLVM(spl); + + *dstpte = m88k_protection(kernel_pmap, VM_PROT_READ | VM_PROT_WRITE) | + CACHE_GLOBAL | PG_V | dst; + *srcpte = m88k_protection(kernel_pmap, VM_PROT_READ) | + CACHE_GLOBAL | PG_V | src; + + /* + * We don't need the flush_atc_entry() dance, as these pages are + * bound to only one cpu. + */ + cmmu_flush_tlb(cpu, TRUE, dstva, 2 * PAGE_SIZE); + + /* + * The source page is likely to be a non-kernel mapping, and as + * such write back. Also, we might have split U/S caches! + * So be sure to have the source pa flushed before the copy is + * attempted, and the destination pa flushed afterwards. + */ + cmmu_flush_data_cache(cpu, src, PAGE_SIZE); + bcopy((const void *)srcva, (void *)dstva, PAGE_SIZE); + cmmu_flush_data_cache(cpu, dst, PAGE_SIZE); + + SPLX(spl); +} + +/* + * Routine: PMAP_CHANGEBIT + * + * Function: + * Update the pte bits on the specified physical page. + * + * Parameters: + * pg physical page + * set bits to set + * mask bits to mask + * + * Extern/Global: + * pv_lists + * + * Calls: + * pmap_pte + * + * The pte bits corresponding to the page's frame index will be changed as + * requested. The PV list will be traversed. + * For each pmap/va the hardware the necessary bits in the page descriptor + * table entry will be altered as well if necessary. If any bits were changed, + * a TLB flush will be performed. + */ +void +pmap_changebit(struct vm_page *pg, int set, int mask) +{ + pv_entry_t pvl, pvep; + pt_entry_t *pte, npte, opte; + pmap_t pmap; + int spl; + vaddr_t va; + u_int users; + boolean_t kflush; + + SPLVM(spl); + +changebit_Retry: + pvl = pg_to_pvh(pg); + + /* + * Clear saved attributes (modify, reference) + */ + pvl->pv_flags &= mask; + + if (pvl->pv_pmap == PMAP_NULL) { +#ifdef DEBUG + if (pmap_con_dbg & CD_CBIT) + printf("(pmap_changebit: %x) vm page 0x%x not mapped\n", + curproc, pg); +#endif + SPLX(spl); + return; + } + + /* for each listed pmap, update the affected bits */ + for (pvep = pvl; pvep != PV_ENTRY_NULL; pvep = pvep->pv_next) { + pmap = pvep->pv_pmap; + if (!simple_lock_try(&pmap->pm_lock)) { + goto changebit_Retry; + } + users = pmap->pm_cpus; + kflush = pmap == kernel_pmap; + + va = pvep->pv_va; + pte = pmap_pte(pmap, va); + + /* + * Check for existing and valid pte + */ + if (pte == PT_ENTRY_NULL || !PDT_VALID(pte)) { + goto next; /* no page mapping */ + } +#ifdef DIAGNOSTIC + if (ptoa(PG_PFNUM(*pte)) != VM_PAGE_TO_PHYS(pg)) + panic("pmap_changebit: pte %x in pmap %p %d doesn't point to page %p %lx", + *pte, pmap, kflush, pg, VM_PAGE_TO_PHYS(pg)); +#endif + + /* + * Update bits + */ + opte = *pte; + npte = (opte | set) & mask; + + /* + * Flush TLB of which cpus using pmap. + * + * Invalidate pte temporarily to avoid the modified bit + * and/or the reference being written back by any other cpu. + */ + if (npte != opte) { + invalidate_pte(pte); + *pte = npte; + flush_atc_entry(users, va, kflush); + } +next: + simple_unlock(&pmap->pm_lock); + } + SPLX(spl); +} + +/* + * Routine: PMAP_TESTBIT + * + * Function: + * Test the modified/referenced bits of a physical page. + * + * Parameters: + * pg physical page + * bit bit to test + * + * Extern/Global: + * pv lists + * + * Calls: + * simple_lock, simple_unlock + * pmap_pte + * + * If the attribute list for the given page has the bit, this routine + * returns TRUE. + * + * Otherwise, this routine walks the PV list corresponding to the + * given page. For each pmap/va pair, the page descriptor table entry is + * examined. If the selected bit is found on, the function returns TRUE + * immediately (doesn't need to walk remainder of list), and updates the + * attribute list. + */ +boolean_t +pmap_testbit(struct vm_page *pg, int bit) +{ + pv_entry_t pvl, pvep; + pt_entry_t *pte; + int spl; + + SPLVM(spl); + +testbit_Retry: + pvl = pg_to_pvh(pg); + + if (pvl->pv_flags & bit) { + /* we've already cached this flag for this page, + no use looking further... */ +#ifdef DEBUG + if (pmap_con_dbg & CD_TBIT) + printf("(pmap_testbit: %x) already cached a %x flag for this page\n", + curproc, bit); +#endif + SPLX(spl); + return (TRUE); + } + + if (pvl->pv_pmap == PMAP_NULL) { +#ifdef DEBUG + if (pmap_con_dbg & CD_TBIT) + printf("(pmap_testbit: %x) vm page 0x%x not mapped\n", + curproc, pg); +#endif + SPLX(spl); + return (FALSE); + } + + /* for each listed pmap, check modified bit for given page */ + for (pvep = pvl; pvep != PV_ENTRY_NULL; pvep = pvep->pv_next) { + if (!simple_lock_try(&pvep->pv_pmap->pm_lock)) { + goto testbit_Retry; + } + + pte = pmap_pte(pvep->pv_pmap, pvep->pv_va); + if (pte == PT_ENTRY_NULL || !PDT_VALID(pte)) { + goto next; + } + +#ifdef DIAGNOSTIC + if (ptoa(PG_PFNUM(*pte)) != VM_PAGE_TO_PHYS(pg)) + panic("pmap_testbit: pte %x in pmap %p %d doesn't point to page %p %lx", + *pte, pvep->pv_pmap, pvep->pv_pmap == kernel_pmap ? 1 : 0, pg, VM_PAGE_TO_PHYS(pg)); +#endif + + if ((*pte & bit) != 0) { + simple_unlock(&pvep->pv_pmap->pm_lock); + pvl->pv_flags |= bit; +#ifdef DEBUG + if ((pmap_con_dbg & (CD_TBIT | CD_FULL)) == (CD_TBIT | CD_FULL)) + printf("(pmap_testbit: %x) true on page pte@0x%p\n", curproc, pte); +#endif + SPLX(spl); + return (TRUE); + } +next: + simple_unlock(&pvep->pv_pmap->pm_lock); + } + + SPLX(spl); + return (FALSE); +} + +/* + * Routine: PMAP_UNSETBIT + * + * Function: + * Clears a pte bit and returns its previous state, for the + * specified physical page. + * This is an optimized version of: + * rv = pmap_testbit(pg, bit); + * pmap_changebit(pg, 0, ~bit); + * return rv; + */ +boolean_t +pmap_unsetbit(struct vm_page *pg, int bit) +{ + boolean_t rv = FALSE; + pv_entry_t pvl, pvep; + pt_entry_t *pte, opte; + pmap_t pmap; + int spl; + vaddr_t va; + u_int users; + boolean_t kflush; + + SPLVM(spl); + +unsetbit_Retry: + pvl = pg_to_pvh(pg); + + /* + * Clear saved attributes + */ + pvl->pv_flags &= ~bit; + + if (pvl->pv_pmap == PMAP_NULL) { +#ifdef DEBUG + if (pmap_con_dbg & CD_USBIT) + printf("(pmap_unsetbit: %x) vm page 0x%x not mapped\n", + curproc, pg); +#endif + SPLX(spl); + return (FALSE); + } + + /* for each listed pmap, update the specified bit */ + for (pvep = pvl; pvep != PV_ENTRY_NULL; pvep = pvep->pv_next) { + pmap = pvep->pv_pmap; + if (!simple_lock_try(&pmap->pm_lock)) { + goto unsetbit_Retry; + } + users = pmap->pm_cpus; + kflush = pmap == kernel_pmap; + + va = pvep->pv_va; + pte = pmap_pte(pmap, va); + + /* + * Check for existing and valid pte + */ + if (pte == PT_ENTRY_NULL || !PDT_VALID(pte)) { + goto next; /* no page mapping */ + } +#ifdef DIAGNOSTIC + if (ptoa(PG_PFNUM(*pte)) != VM_PAGE_TO_PHYS(pg)) + panic("pmap_unsetbit: pte %x in pmap %p %d doesn't point to page %p %lx", + *pte, pmap, kflush, pg, VM_PAGE_TO_PHYS(pg)); +#endif + + /* + * Update bits + */ + opte = *pte; + if (opte & bit) { + /* + * Flush TLB of which cpus using pmap. + * + * Invalidate pte temporarily to avoid the specified + * bit being written back by any other cpu. + */ + invalidate_pte(pte); + *pte = opte ^ bit; + flush_atc_entry(users, va, kflush); + } else + rv = TRUE; +next: + simple_unlock(&pmap->pm_lock); + } + SPLX(spl); + + return (rv); +} + +/* + * Routine: PMAP_IS_MODIFIED + * + * Function: + * Return whether or not the specified physical page is modified + * by any physical maps. + */ +boolean_t +pmap_is_modified(struct vm_page *pg) +{ + return pmap_testbit(pg, PG_M); +} + +/* + * Routine: PMAP_IS_REFERENCED + * + * Function: + * Return whether or not the specified physical page is referenced by + * any physical maps. + */ +boolean_t +pmap_is_referenced(struct vm_page *pg) +{ + return pmap_testbit(pg, PG_U); +} + +/* + * Routine: PMAP_PAGE_PROTECT + * + * Calls: + * pmap_changebit + * pmap_remove_all + * + * Lower the permission for all mappings to a given page. + */ +void +pmap_page_protect(struct vm_page *pg, vm_prot_t prot) +{ + if ((prot & VM_PROT_READ) == VM_PROT_NONE) + pmap_remove_all(pg); + else if ((prot & VM_PROT_WRITE) == VM_PROT_NONE) + pmap_changebit(pg, PG_RO, ~0); +} + +void +pmap_virtual_space(vaddr_t *startp, vaddr_t *endp) +{ + *startp = virtual_avail; + *endp = virtual_end; +} + +void +pmap_kenter_pa(vaddr_t va, paddr_t pa, vm_prot_t prot) +{ + int spl; + pt_entry_t template, *pte; + u_int users; + + CHECK_PAGE_ALIGN(va, "pmap_kenter_pa - VA"); + CHECK_PAGE_ALIGN(pa, "pmap_kenter_pa - PA"); + +#ifdef DEBUG + if (pmap_con_dbg & CD_ENT) { + printf ("(pmap_kenter_pa: %x) va %x pa %x\n", curproc, va, pa); + } +#endif + + PMAP_LOCK(kernel_pmap, spl); + users = kernel_pmap->pm_cpus; + + template = m88k_protection(kernel_pmap, prot); + + /* + * Expand pmap to include this pte. + */ + while ((pte = pmap_pte(kernel_pmap, va)) == PT_ENTRY_NULL) + pmap_expand_kmap(va, VM_PROT_READ | VM_PROT_WRITE); + + /* + * And count the mapping. + */ + kernel_pmap->pm_stats.resident_count++; + kernel_pmap->pm_stats.wired_count++; + + invalidate_pte(pte); + if ((unsigned long)pa >= MAXPHYSMEM) + template |= CACHE_INH | PG_V | PG_W; + else + template |= CACHE_GLOBAL | PG_V | PG_W; + *pte = template | pa; + flush_atc_entry(users, va, TRUE); + + PMAP_UNLOCK(kernel_pmap, spl); +} + +void +pmap_kremove(vaddr_t va, vsize_t len) +{ + int spl; + u_int users; + vaddr_t e; + +#ifdef DEBUG + if (pmap_con_dbg & CD_RM) + printf("(pmap_kremove: %x) va %x len %x\n", curproc, va, len); +#endif + + CHECK_PAGE_ALIGN(va, "pmap_kremove addr"); + CHECK_PAGE_ALIGN(len, "pmap_kremove len"); + + PMAP_LOCK(kernel_pmap, spl); + users = kernel_pmap->pm_cpus; + + e = va + len; + for (; va < e; va += PAGE_SIZE) { + sdt_entry_t *sdt; + pt_entry_t *pte; + + sdt = SDTENT(kernel_pmap, va); + + /* If no segment table, skip a whole segment */ + if (!SDT_VALID(sdt)) { + va &= SDT_MASK; + va += (1 << SDT_SHIFT) - PAGE_SIZE; + continue; + } + + pte = pmap_pte(kernel_pmap, va); + if (pte == PT_ENTRY_NULL || !PDT_VALID(pte)) { + continue; /* no page mapping */ + } + + /* + * Update the counts + */ + kernel_pmap->pm_stats.resident_count--; + kernel_pmap->pm_stats.wired_count--; + + invalidate_pte(pte); + flush_atc_entry(users, va, TRUE); + } + PMAP_UNLOCK(map, spl); +} diff --git a/sys/arch/luna88k/luna88k/pmap_table.c b/sys/arch/luna88k/luna88k/pmap_table.c new file mode 100644 index 00000000000..9800edd714c --- /dev/null +++ b/sys/arch/luna88k/luna88k/pmap_table.c @@ -0,0 +1,89 @@ +/* $OpenBSD: pmap_table.c,v 1.1 2004/04/21 15:24:13 aoyama Exp $ */ + +/* + * Mach Operating System + * Copyright (c) 1993-1992 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/types.h> +#include <machine/board.h> +#include <machine/cmmu.h> +#include <uvm/uvm_extern.h> +#include <machine/pmap_table.h> + +#define R VM_PROT_READ +#define RW (VM_PROT_READ | VM_PROT_WRITE) +#define CW CACHE_WT +#define CI CACHE_INH +#define CG CACHE_GLOBAL + +/* phys_start, virt_start, size, prot, cacheability */ +const pmap_table_entry +luna88k_board_table[] = { +#if 0 + { 0 , VEQR_ADDR , 48*1024*1024 /* XXX memory size */, RW, CG}, +#endif + { 0 , 0 , round_page(0x20000) /* XXX kernel start */, RW, CG}, + { OBIO_START , OBIO_START , round_page(OBIO_SIZE), RW, CI }, +#if 0 + { TRI_PORT_RAM, TRI_PORT_RAM, TRI_PORT_RAM_SPACE, RW, CI }, + { 0 , VEQR_ADDR , 48*1024*1024 /* XXX memory size */, RW, CG}, + { 0 , 0 , 0x20000 /* XXX kernel start */, RW, CG}, + { PROM_ADDR , PROM_ADDR , PROM_SPACE , R, C }, + { FUSE_ROM_ADDR, FUSE_ROM_ADDR, FUSE_ROM_SPACE, RW, CI }, + { NVRAM_ADDR , NVRAM_ADDR , NVRAM_SPACE , RW, CI }, + { OBIO_PIO0_BASE, OBIO_PIO0_BASE, PAGE, RW, CI }, + { OBIO_PIO1_BASE, OBIO_PIO1_BASE, PAGE, RW, CI }, + { OBIO_SIO , OBIO_SIO, PAGE, RW, CI }, + { OBIO_TAS , OBIO_TAS, PAGE, RW, CI }, + { OBIO_CLOCK0 , OBIO_CLOCK0, PAGE, RW, CI }, + { INT_ST_MASK0 , INT_ST_MASK0, PAGE, RW, CI }, + { SOFT_INT0 , SOFT_INT0, PAGE, W, CI }, + { SOFT_INT_FLAG0, SOFT_INT_FLAG0, PAGE, RW, CI }, + { RESET_CPU0 , RESET_CPU0, PAGE, RW, CI }, +#if 0 + { EXT_A_ADDR , EXT_A_ADDR, EXT_A_SPACE, RW, CI }, + { EXT_B_ADDR , EXT_B_ADDR, EXT_B_SPACE, RW, CI }, + { PC_BASE , PC_BASE, PC_SPACE, RW, CI }, +#endif + { MROM_ADDR , MROM_ADDR, MROM_SPACE, R, CI }, + { BMAP_START , BMAP_START, BMAP_SPACE, RW, CI }, + { BMAP_PALLET0 , BMAP_PALLET0, PAGE, RW, CI }, + { BMAP_PALLET1 , BMAP_PALLET1, PAGE, RW, CI }, + { BMAP_PALLET2 , BMAP_PALLET2, PAGE, RW, CI }, + { BOARD_CHECK_REG, BOARD_CHECK_REG,PAGE, RW, CI }, + { BMAP_CRTC, BMAP_CRTC, PAGE, RW, CI }, + { SCSI_ADDR, SCSI_ADDR, PAGE, RW, C}, + { LANCE_ADDR, LANCE_ADDR, PAGE, RW, CI }, +#endif + { 0, 0, 0xffffffff, 0, 0 }, +}; + +pmap_table_t +pmap_table_build(void) +{ + return luna88k_board_table; +} diff --git a/sys/arch/luna88k/luna88k/process.S b/sys/arch/luna88k/luna88k/process.S new file mode 100644 index 00000000000..5ffa2bf65e1 --- /dev/null +++ b/sys/arch/luna88k/luna88k/process.S @@ -0,0 +1,332 @@ +/* $OpenBSD: process.S,v 1.1 2004/04/21 15:24:13 aoyama Exp $ */ +/* + * Copyright (c) 1996 Nivas Madhur + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Nivas Madhur. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "assym.h" +#include <machine/asm.h> +#include <machine/psl.h> +#include <machine/intr.h> + + data + align 4 +ASLOCAL(swchanpanic) + string "switch wchan %x\0" + align 4 +ASLOCAL(swsrunpanic) + string "switch SRUN %x\0" +#ifdef DEBUG + align 4 +ASLOCAL(boguspsr) + string "Invalid PSR in idle loop 0x%x\n\0" +#endif + + text + align 8 +ASLOCAL(Lswchanpanic) + or.u r2, r0, hi16(_ASM_LABEL(swchanpanic)) + or r2, r2, lo16(_ASM_LABEL(swchanpanic)) + bsr.n _C_LABEL(panic) + or r3, r0, r9 + +ASLOCAL(Lswsrunpanic) + or.u r2, r0, hi16(_ASM_LABEL(swsrunpanic)) + or r2, r2, lo16(_ASM_LABEL(swsrunpanic)) + bsr.n _C_LABEL(panic) + or r3, r0, r9 + +/* + * At exit of a process, do a cpu_switch for the last time. + * The mapping of the pcb at p->p_addr has already been deleted, + * and the memory for the pcb+stack has been freed. + * The ipl is high enough to prevent the memory from being reallocated. + * switch_exit(proc * p) + */ + +ENTRY(switch_exit) + /* + * Change pcb to idle u. area, i.e., set r31 to top of stack + * and set curpcb to point to _idle_u. r2 contains proc *p. + */ + or.u r30, r0, hi16(_C_LABEL(idle_u)) + or r30, r30,lo16(_C_LABEL(idle_u)) + addu r31, r30, USIZE /* now on idle_u stack */ + or.u r10, r0, hi16(_C_LABEL(curpcb)) + st r30, r10,lo16(_C_LABEL(curpcb)) /* curpcb = &idle_u */ + or.u r10, r0, hi16(_C_LABEL(curproc)) + st r0, r10, lo16(_C_LABEL(curproc)) /* curproc = NULL */ + + /* Schedule the vmspace and stack to be freed. */ + bsr.n _C_LABEL(exit2) + subu r31, r31, 48 /* allocate stack */ + addu r31, r31, 48 /* restore stack */ + bsr _C_LABEL(cpu_switch) /* goto final switch */ + +/* + * cpu_switch() + * XXX - Arg 1 is a proc pointer (curproc) but this doesn't use it. + * XXX - how about using stack for saving spl and last proc? + * XXX rewrite this whole mess in C nivas + */ +ENTRY(cpu_switch) + + /* + * Save state of previous process in its pcb. + */ + or.u r2, r0, hi16(_C_LABEL(curpcb)) + ld r2, r2, lo16(_C_LABEL(curpcb)) + st r1, r2, PCB_PC /* save return address */ + bsr _ASM_LABEL(__savectx) + /* note that we don't need to recover r1 at this point */ + + or.u r11, r0, hi16(_C_LABEL(curproc)) + ld r2, r11, lo16(_C_LABEL(curproc)) + bcnd eq0, r2, 1f + + bsr.n _C_LABEL(pmap_deactivate) + subu r31, r31,48 + addu r31, r31,48 + or.u r11, r0, hi16(_C_LABEL(curproc)) + +1: + st r0, r11, lo16(_C_LABEL(curproc)) /* curproc = NULL */ + +ASLOCAL(Lidleloop) + + /* + * Find the highest-priority queue that isn't empty, + * then take the first proc from that queue. + */ + + or.u r7, r0, hi16(_C_LABEL(whichqs)) + ld r7, r7, lo16(_C_LABEL(whichqs)) + + bcnd ne0, r7, _ASM_LABEL(Ldoneloop) + +ASLOCAL(Lloopchk) /* if whichqs is zero, keep checking */ + bsr.n _C_LABEL(setipl) /* unmask all ints... */ + or r2, r0, IPL_NONE + + ldcr r2, PSR + bb0 PSR_INTERRUPT_DISABLE_BIT, r2, 1f +#ifdef DEBUG + or r3, r2, r0 + or.u r2, r0, hi16(_ASM_LABEL(boguspsr)) + bsr.n _C_LABEL(printf) + or r2, r2, lo16(_ASM_LABEL(boguspsr)) + ldcr r2, PSR +#endif + clr r2, r2, 1<PSR_INTERRUPT_DISABLE_BIT> /* ...and enable them */ + stcr r2, PSR + FLUSH_PIPELINE +1: + br _ASM_LABEL(Lidleloop) + +ASLOCAL(Ldoneloop) + + bsr.n _C_LABEL(setipl) /* disable ints */ + or r2, r0, IPL_HIGH + + or.u r7, r0, hi16(_C_LABEL(whichqs)) /* reload whichqs */ + ld r7, r7, lo16(_C_LABEL(whichqs)) + + bcnd eq0, r7, _ASM_LABEL(Lloopchk) /* keep spinning for whichqs to be != 0 */ + + xor r6, r6, r6 /* set r6 to 0 */ +1: bb1 0, r7, 2f /* if rightmost bit set, done */ + extu r7, r7, 0<1> /* else, right shift whichqs, */ + br.n 1b /* increment r6, and repeat */ + addu r6, r6, 1 +2: + or.u r7, r0, hi16(_qs) + or r7, r7, lo16(_qs) + + /* + * Need to make + * p->p_forw->p_back = p->p_back and + * p->p_back->p_forw = p->p_forw where + * p is q->p_forw. + * Remember that q->p_forw == p and p->p_back == q. + */ + + lda.d r8, r7[r6] /* r8 = &qs[ff1(whichqs)] */ + ld r9, r8, P_FORW /* r8 is q, r9 is p */ + + ld r12, r9, P_FORW /* r12 = p->p_forw */ + st r8, r12, P_BACK /* p->p_forw->p_back = q (p->p_back) */ + st r12, r8, P_FORW /* q->p_forw = p->p_forw */ + lda.d r8, r7[r6] /* reload r8 with qs[ff1(whichqs)] */ + ld r12, r8, P_FORW /* q->p_forw */ + cmp r12, r12, r8 /* q == q->p_forw; anyone left on queue? */ + bb1 ne, r12, 3f /* yes, skip clearing bit in whichqs */ + + or r12, r0, 1 /* r12 is 1 now */ +1: bcnd eq0, r6, 2f + mak r12, r12, 0<1> /* shift left by 1 */ + br.n 1b + subu r6, r6, 1 /* keep doing this while r6 != 0 */ +2: + /* + * NOTE: we could have just used "mak r12, r12, r6" instead of the + * loop above. But that will break if NQS is made > 32. I can use + * preprocessor to do the right thing, but that means I have to + * include sys/proc.h in this file. XXX nivas + */ + or.u r7, r0, hi16(_C_LABEL(whichqs)) + ld r8, r7, lo16(_C_LABEL(whichqs)) + and.c r8, r8, r12 /* whichqs &= ~the bit */ + st r8, r7, lo16(_C_LABEL(whichqs)) +3: + ld r2, r9, P_WCHAN + bcnd ne0, r2, _ASM_LABEL(Lswchanpanic) + ld.b r2, r9, P_STAT + cmp r2, r2, SRUN + bb1 ne, r2, _ASM_LABEL(Lswsrunpanic) + + or.u r11, r0, hi16(_C_LABEL(want_resched)) + st r0, r11, lo16(_C_LABEL(want_resched)) /* clear want_resched */ + + or.u r11, r0, hi16(_C_LABEL(curproc)) + st r9, r11,lo16(_C_LABEL(curproc)) /* curproc = p */ + + /* r9 is curproc */ + st r0, r9, P_BACK /* p->p_back = 0 */ + ld r3, r9, P_ADDR + or.u r10, r0, hi16(_C_LABEL(curpcb)) + st r3, r10, lo16(_C_LABEL(curpcb)) /* curpcb = p->p_addr */ + + /* pmap_activate() the process' pmap */ + or r2, r0, r9 /* r2 = p */ + or r14, r0, r9 /* save p in r14 */ + bsr.n _C_LABEL(pmap_activate) + subu r31, r31,48 + addu r31, r31,48 + or r9, r0, r14 /* restore p saved in r14 */ + + or.u r31, r0, hi16(_ASM_LABEL(intstack_end)) + or r31,r31, lo16(_ASM_LABEL(intstack_end)) + subu r31, r31,48 + bsr.n _C_LABEL(load_u_area) + or r2, r0, r9 + addu r31, r31,48 + + or.u r10, r0, hi16(_C_LABEL(curpcb)) + ld r10, r10, lo16(_C_LABEL(curpcb)) + /* XXX Is this correct/necessary? */ + st r10, r14, P_ADDR /* p->p_addr = curpcb; restore p_addr */ + + /* restore from the current context */ + ld r2, r10, PCB_FCR62 + ld r3, r10, PCB_FCR63 + fstcr r2, fcr62 + fstcr r3, fcr63 + ld r1, r10, PCB_PC + ld r14,r10, PCB_R14 + ld r15,r10, PCB_R15 + ld r16,r10, PCB_R16 + ld r17,r10, PCB_R17 + ld r18,r10, PCB_R18 + ld r19,r10, PCB_R19 + ld r20,r10, PCB_R20 + ld r21,r10, PCB_R21 + ld r22,r10, PCB_R22 + ld r23,r10, PCB_R23 + ld r24,r10, PCB_R24 + ld r25,r10, PCB_R25 + ld r26,r10, PCB_R26 + ld r27,r10, PCB_R27 + ld r28,r10, PCB_R28 + ld r29,r10, PCB_R29 + ld r30,r10, PCB_R30 /* restore frame pointer & stack */ + ld r31,r10, PCB_SP + +/* XXX + * Should we postpone restoring stack till after ipl is restored? + * The stack access could fault + */ + subu r31,r31,48 + st r1, r31,36 /* save r1 on stack */ + bsr.n _C_LABEL(setipl) + ld r2, r10, PCB_IPL /* restore interrupt mask */ + ld r1, r31,36 /* restore r1 from stack */ + addu r31,r31,48 + jmp.n r1 + or r2, r0, 1 /* return 1 (for alternate returns) */ + +/* + * savectx(pcb) + * Update pcb, saving current processor state. + */ +ENTRY(savectx) + /* + * Save preserved general register set. + */ + st r1, r2, PCB_PC /* save return address */ +ASENTRY(__savectx) + st r14, r2, PCB_R14 + st r15, r2, PCB_R15 + st r16, r2, PCB_R16 + st r17, r2, PCB_R17 + st r18, r2, PCB_R18 + st r19, r2, PCB_R19 + st r20, r2, PCB_R20 + st r21, r2, PCB_R21 + st r22, r2, PCB_R22 + st r23, r2, PCB_R23 + st r24, r2, PCB_R24 + st r25, r2, PCB_R25 + st r26, r2, PCB_R26 + st r27, r2, PCB_R27 + st r28, r2, PCB_R28 + st r29, r2, PCB_R29 + st r30, r2, PCB_R30 /* save frame pointer & stack pointer */ + st r31, r2, PCB_SP + + /* + * Get the current spl. + * We need to save r1 on the stack because we don't know if we were + * called as savectx or __savectx. + */ + subu r31, r31, 40 /* allocate stack for r1 and args */ + st r1, r31, 32 + bsr.n _C_LABEL(getipl) /* get the current interrupt mask */ + or r14, r0, r2 + st r2, r14, PCB_IPL /* save interrupt mask */ + ld r1, r31, 32 /* recover return address */ + addu r31, r31, 40 /* put stack pointer back */ + + /* + * Save FP state. + */ + fldcr r2, fcr62 + fldcr r3, fcr63 + st r2, r14, PCB_FCR62 + jmp.n r1 + st r3, r14, PCB_FCR63 diff --git a/sys/arch/luna88k/luna88k/process_machdep.c b/sys/arch/luna88k/luna88k/process_machdep.c new file mode 100644 index 00000000000..27bf49b45ed --- /dev/null +++ b/sys/arch/luna88k/luna88k/process_machdep.c @@ -0,0 +1,149 @@ +/* $OpenBSD: process_machdep.c,v 1.1 2004/04/21 15:24:14 aoyama Exp $ */ + +/* + * Copyright (c) 1993 The Regents of the University of California. + * Copyright (c) 1993 Jan-Simon Pendry + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: Id: procfs_i386.c,v 4.1 1993/12/17 10:47:45 jsp Rel + */ + +/* + * This file may seem a bit stylized, but that so that it's easier to port. + * Functions to be implemented here are: + * + * process_read_regs(proc, regs) + * Get the current user-visible register set from the process + * and copy it into the regs structure (<machine/reg.h>). + * The process is stopped at the time read_regs is called. + * + * process_write_regs(proc, regs) + * Update the current register set from the passed in regs + * structure. Take care to avoid clobbering special CPU + * registers or privileged bits in the PSL. + * The process is stopped at the time write_regs is called. + * + * process_sstep(proc) + * Arrange for the process to trap after executing a single instruction. + * + * process_set_pc(proc) + * Set the process's program counter. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/time.h> +#include <sys/kernel.h> +#include <sys/proc.h> +#include <sys/user.h> +#include <sys/vnode.h> +#include <machine/psl.h> +#include <machine/reg.h> +#include <machine/trap.h> +#if 0 +#include <machine/frame.h> +#endif +#include <sys/ptrace.h> + +int +process_read_regs(p, regs) + struct proc *p; + struct reg *regs; +{ + bcopy((caddr_t)USER_REGS(p), (caddr_t)regs, sizeof(struct reg)); + return (0); +} + +int +process_read_fpregs(p, regs) + struct proc *p; + struct fpreg *regs; +{ +#if 0 + extern struct fpstate initfpstate; + struct fpstate *statep = &initfpstate; + + /* NOTE: struct fpreg == struct fpstate */ + if (p->p_md.md_fpstate) + statep = p->p_md.md_fpstate; + bcopy(statep, regs, sizeof(struct fpreg)); +#endif + return 0; +} + +#ifdef PTRACE + +int +process_write_regs(p, regs) + struct proc *p; + struct reg *regs; +{ + bcopy((caddr_t)regs, (caddr_t)USER_REGS(p), sizeof(struct reg)); + return (0); +} + +int +process_sstep(p, sstep) + struct proc *p; + int sstep; +{ + if (sstep) + cpu_singlestep(p); + return (0); +} + +int +process_set_pc(p, addr) + struct proc *p; + caddr_t addr; +{ + struct reg *regs; + + regs = USER_REGS(p); + regs->sxip = (u_int)addr; + regs->snip = (u_int)addr + 4; + return (0); +} + +int +process_write_fpregs(p, regs) + struct proc *p; + struct fpreg *regs; +{ +#if 0 + if (p->p_md.md_fpstate == NULL) + return EINVAL; + + bcopy(regs, p->p_md.md_fpstate, sizeof(struct fpreg)); +#endif + return 0; +} + +#endif /* PTRACE */ diff --git a/sys/arch/luna88k/luna88k/trap.c b/sys/arch/luna88k/luna88k/trap.c new file mode 100644 index 00000000000..d7bee6ad47b --- /dev/null +++ b/sys/arch/luna88k/luna88k/trap.c @@ -0,0 +1,1797 @@ +/* $OpenBSD: trap.c,v 1.1 2004/04/21 15:24:14 aoyama Exp $ */ +/* + * Copyright (c) 1998 Steve Murphree, Jr. + * Copyright (c) 1996 Nivas Madhur + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Nivas Madhur. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/* + * Mach Operating System + * Copyright (c) 1991 Carnegie Mellon University + * Copyright (c) 1991 OMRON Corporation + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/proc.h> +#include <sys/signalvar.h> +#include <sys/user.h> +#include <sys/syscall.h> +#include <sys/systm.h> +#include <sys/ktrace.h> + +#include "systrace.h" +#include <dev/systrace.h> + +#include <uvm/uvm_extern.h> + +#include <machine/asm_macro.h> /* enable/disable interrupts */ +#include <machine/cpu.h> +#include <machine/locore.h> +#ifdef M88100 +#include <machine/m88100.h> /* DMT_xxx */ +#include <machine/m8820x.h> /* CMMU_PFSR_xxx */ +#endif +#ifdef M88110 +#include <machine/m88110.h> +#endif +#include <machine/pcb.h> /* FIP_E, etc. */ +#include <machine/psl.h> /* FIP_E, etc. */ +#include <machine/trap.h> + +#include <machine/db_machdep.h> +#ifdef DDB +#include <ddb/db_output.h> /* db_printf() */ +#endif /* DDB */ +#define SSBREAKPOINT (0xF000D1F8U) /* Single Step Breakpoint */ + +#ifdef DDB +#define DEBUG_MSG(x) db_printf x +#else +#define DEBUG_MSG(x) +#endif /* DDB */ + +#define USERMODE(PSR) (((PSR) & PSR_MODE) == 0) +#define SYSTEMMODE(PSR) (((PSR) & PSR_MODE) != 0) + +/* sigh */ +extern int procfs_domem(struct proc *, struct proc *, void *, struct uio *); + +__dead void panictrap(int, struct trapframe *); +__dead void error_fatal(struct trapframe *); + +extern void regdump(struct trapframe *f); + +const char *trap_type[] = { + "Reset", + "Interrupt Exception", + "Instruction Access", + "Data Access Exception", + "Misaligned Access", + "Unimplemented Opcode", + "Privilege Violation" + "Bounds Check Violation", + "Illegal Integer Divide", + "Integer Overflow", + "Error Exception", + "Non-Maskable Exception", +}; +const int trap_types = sizeof trap_type / sizeof trap_type[0]; + +const char *pbus_exception_type[] = { + "Success (No Fault)", + "unknown 1", + "unknown 2", + "Bus Error", + "Segment Fault", + "Page Fault", + "Supervisor Violation", + "Write Violation", +}; + +static inline void +userret(struct proc *p, struct trapframe *frame, u_quad_t oticks) +{ + int sig; + + /* take pending signals */ + while ((sig = CURSIG(p)) != 0) + postsig(sig); + p->p_priority = p->p_usrpri; + + if (want_resched) { + /* + * We're being preempted. + */ + preempt(NULL); + while ((sig = CURSIG(p)) != 0) + postsig(sig); + } + + /* + * If profiling, charge recent system time to the trapped pc. + */ + if (p->p_flag & P_PROFIL) { + extern int psratio; + + addupc_task(p, frame->tf_sxip & XIP_ADDR, + (int)(p->p_sticks - oticks) * psratio); + } + curpriority = p->p_priority; +} + +__dead void +panictrap(int type, struct trapframe *frame) +{ +#ifdef DDB + static int panicing = 0; + + if (panicing++ == 0) { + switch (cputyp) { +#ifdef M88100 + case CPU_88100: + if (type == 2) { + /* instruction exception */ + db_printf("\nInstr access fault (%s) v = %x, " + "frame %p\n", + pbus_exception_type[ + CMMU_PFSR_FAULT(frame->tf_ipfsr)], + frame->tf_sxip & XIP_ADDR, frame); + } else if (type == 3) { + /* data access exception */ + db_printf("\nData access fault (%s) v = %x, " + "frame %p\n", + pbus_exception_type[ + CMMU_PFSR_FAULT(frame->tf_dpfsr)], + frame->tf_sxip & XIP_ADDR, frame); + } else + db_printf("\nTrap type %d, v = %x, frame %p\n", + type, frame->tf_sxip & XIP_ADDR, frame); + break; +#endif +#ifdef M88110 + case CPU_88110: + db_printf("\nTrap type %d, v = %x, frame %p\n", + type, frame->tf_exip, frame); + break; +#endif + } + regdump(frame); + } +#endif + if ((u_int)type < trap_types) + panic(trap_type[type]); + else + panic("trap %d", type); + /*NOTREACHED*/ +} + +#ifdef M88100 +void +m88100_trap(unsigned type, struct trapframe *frame) +{ + struct proc *p; + u_quad_t sticks = 0; + struct vm_map *map; + vaddr_t va; + vm_prot_t ftype; + int fault_type, pbus_type; + u_long fault_code; + unsigned nss, fault_addr; + struct vmspace *vm; + union sigval sv; + int result; +#ifdef DDB + int s; +#endif + int sig = 0; + + extern struct vm_map *kernel_map; + extern caddr_t guarded_access_start; + extern caddr_t guarded_access_end; + extern caddr_t guarded_access_bad; + + uvmexp.traps++; + if ((p = curproc) == NULL) + p = &proc0; + + if (USERMODE(frame->tf_epsr)) { + sticks = p->p_sticks; + type += T_USER; + p->p_md.md_tf = frame; /* for ptrace/signals */ + } + fault_type = 0; + fault_code = 0; + fault_addr = frame->tf_sxip & XIP_ADDR; + + switch (type) { + default: + panictrap(frame->tf_vector, frame); + break; + /*NOTREACHED*/ + +#if defined(DDB) + case T_KDB_BREAK: + s = splhigh(); + db_enable_interrupt(); + ddb_break_trap(T_KDB_BREAK, (db_regs_t*)frame); + db_disable_interrupt(); + splx(s); + return; + case T_KDB_ENTRY: + s = splhigh(); + db_enable_interrupt(); + ddb_entry_trap(T_KDB_ENTRY, (db_regs_t*)frame); + db_disable_interrupt(); + splx(s); + return; +#endif /* DDB */ + case T_ILLFLT: + DEBUG_MSG(("Unimplemented opcode!\n")); + panictrap(frame->tf_vector, frame); + break; + case T_INT: + case T_INT+T_USER: + /* This function pointer is set in machdep.c + It calls m188_ext_int or sbc_ext_int depending + on the value of brdtyp - smurph */ + (*md.interrupt_func)(T_INT, frame); + return; + + case T_MISALGNFLT: + DEBUG_MSG(("kernel misaligned " + "access exception @ 0x%08x\n", frame->tf_sxip)); + panictrap(frame->tf_vector, frame); + break; + + case T_INSTFLT: + /* kernel mode instruction access fault. + * Should never, never happen for a non-paged kernel. + */ +#ifdef TRAPDEBUG + pbus_type = CMMU_PFSR_FAULT(frame->tf_ipfsr); + printf("Kernel Instruction fault #%d (%s) v = 0x%x, frame 0x%x cpu %d\n", + pbus_type, pbus_exception_type[pbus_type], + fault_addr, frame, frame->tf_cpu); +#endif + panictrap(frame->tf_vector, frame); + break; + + case T_DATAFLT: + /* kernel mode data fault */ + + /* data fault on the user address? */ + if ((frame->tf_dmt0 & DMT_DAS) == 0) { + type = T_DATAFLT + T_USER; + goto user_fault; + } + + fault_addr = frame->tf_dma0; + if (frame->tf_dmt0 & (DMT_WRITE|DMT_LOCKBAR)) { + ftype = VM_PROT_READ|VM_PROT_WRITE; + fault_code = VM_PROT_WRITE; + } else { + ftype = VM_PROT_READ; + fault_code = VM_PROT_READ; + } + + va = trunc_page((vaddr_t)fault_addr); + if (va == 0) { + panic("trap: bad kernel access at %x", fault_addr); + } + + vm = p->p_vmspace; + map = kernel_map; + + pbus_type = CMMU_PFSR_FAULT(frame->tf_dpfsr); +#ifdef TRAPDEBUG + printf("Kernel Data access fault #%d (%s) v = 0x%x, frame 0x%x cpu %d\n", + pbus_type, pbus_exception_type[pbus_type], + fault_addr, frame, frame->tf_cpu); +#endif + + switch (pbus_type) { + case CMMU_PFSR_BERROR: + /* + * If it is a guarded access, bus error is OK. + */ + if ((frame->tf_sxip & XIP_ADDR) >= + (unsigned)&guarded_access_start && + (frame->tf_sxip & XIP_ADDR) <= + (unsigned)&guarded_access_end) { + frame->tf_snip = + ((unsigned)&guarded_access_bad ) | NIP_V; + frame->tf_sfip = + ((unsigned)&guarded_access_bad + 4) | FIP_V; + frame->tf_sxip = 0; + /* We sort of resolved the fault ourselves + * because we know where it came from + * [guarded_access()]. But we must still think + * about the other possible transactions in + * dmt1 & dmt2. Mark dmt0 so that + * data_access_emulation skips it. XXX smurph + */ + frame->tf_dmt0 |= DMT_SKIP; + data_access_emulation((unsigned *)frame); + frame->tf_dpfsr = 0; + frame->tf_dmt0 = 0; + return; + } + break; + case CMMU_PFSR_SUCCESS: + /* + * The fault was resolved. Call data_access_emulation + * to drain the data unit pipe line and reset dmt0 + * so that trap won't get called again. + */ + data_access_emulation((unsigned *)frame); + frame->tf_dpfsr = 0; + frame->tf_dmt0 = 0; + return; + case CMMU_PFSR_SFAULT: + case CMMU_PFSR_PFAULT: + result = uvm_fault(map, va, VM_FAULT_INVALID, ftype); + if (result == 0) { + /* + * We could resolve the fault. Call + * data_access_emulation to drain the data + * unit pipe line and reset dmt0 so that trap + * won't get called again. + */ + data_access_emulation((unsigned *)frame); + frame->tf_dpfsr = 0; + frame->tf_dmt0 = 0; + return; + } + break; + } +#ifdef TRAPDEBUG + printf("PBUS Fault %d (%s) va = 0x%x\n", pbus_type, + pbus_exception_type[pbus_type], va); +#endif + panictrap(frame->tf_vector, frame); + /* NOTREACHED */ + case T_INSTFLT+T_USER: + /* User mode instruction access fault */ + /* FALLTHROUGH */ + case T_DATAFLT+T_USER: +user_fault: + if (type == T_INSTFLT + T_USER) { + pbus_type = CMMU_PFSR_FAULT(frame->tf_ipfsr); +#ifdef TRAPDEBUG + printf("User Instruction fault #%d (%s) v = 0x%x, frame 0x%x cpu %d\n", + pbus_type, pbus_exception_type[pbus_type], + fault_addr, frame, frame->tf_cpu); +#endif + } else { + fault_addr = frame->tf_dma0; + pbus_type = CMMU_PFSR_FAULT(frame->tf_dpfsr); +#ifdef TRAPDEBUG + printf("User Data access fault #%d (%s) v = 0x%x, frame 0x%x cpu %d\n", + pbus_type, pbus_exception_type[pbus_type], + fault_addr, frame, frame->tf_cpu); +#endif + } + + if (frame->tf_dmt0 & (DMT_WRITE | DMT_LOCKBAR)) { + ftype = VM_PROT_READ | VM_PROT_WRITE; + fault_code = VM_PROT_WRITE; + } else { + ftype = VM_PROT_READ; + fault_code = VM_PROT_READ; + } + + va = trunc_page((vaddr_t)fault_addr); + + vm = p->p_vmspace; + map = &vm->vm_map; + + /* Call uvm_fault() to resolve non-bus error faults */ + switch (pbus_type) { + case CMMU_PFSR_SUCCESS: + result = 0; + break; + case CMMU_PFSR_BERROR: + result = EACCES; + break; + default: + result = uvm_fault(map, va, VM_FAULT_INVALID, ftype); + if (result == EACCES) + result = EFAULT; + break; + } + + if ((caddr_t)va >= vm->vm_maxsaddr) { + if (result == 0) { + nss = btoc(USRSTACK - va);/* XXX check this */ + if (nss > vm->vm_ssize) + vm->vm_ssize = nss; + } + } + + /* + * This could be a fault caused in copyin*() + * while accessing user space. + */ + if (result != 0 && p->p_addr->u_pcb.pcb_onfault != NULL) { + frame->tf_snip = p->p_addr->u_pcb.pcb_onfault | NIP_V; + frame->tf_sfip = (p->p_addr->u_pcb.pcb_onfault + 4) | FIP_V; + frame->tf_sxip = 0; + /* + * Continue as if the fault had been resolved, but + * do not try to complete the faulting access. + */ + frame->tf_dmt0 |= DMT_SKIP; + result = 0; + } + + if (result == 0) { + if (type == T_DATAFLT+T_USER) { + /* + * We could resolve the fault. Call + * data_access_emulation to drain the data unit + * pipe line and reset dmt0 so that trap won't + * get called again. + */ + data_access_emulation((unsigned *)frame); + frame->tf_dpfsr = 0; + frame->tf_dmt0 = 0; + } else { + /* + * back up SXIP, SNIP, + * clearing the Error bit + */ + frame->tf_sfip = frame->tf_snip & ~FIP_E; + frame->tf_snip = frame->tf_sxip & ~NIP_E; + frame->tf_ipfsr = 0; + } + } else { + sig = result == EACCES ? SIGBUS : SIGSEGV; + fault_type = result == EACCES ? + BUS_ADRERR : SEGV_MAPERR; + } + break; + case T_MISALGNFLT+T_USER: + sig = SIGBUS; + fault_type = BUS_ADRALN; + break; + case T_PRIVINFLT+T_USER: + case T_ILLFLT+T_USER: +#ifndef DDB + case T_KDB_BREAK: + case T_KDB_ENTRY: +#endif + case T_KDB_BREAK+T_USER: + case T_KDB_ENTRY+T_USER: + case T_KDB_TRACE: + case T_KDB_TRACE+T_USER: + sig = SIGILL; + break; + case T_BNDFLT+T_USER: + sig = SIGFPE; + break; + case T_ZERODIV+T_USER: + sig = SIGFPE; + fault_type = FPE_INTDIV; + break; + case T_OVFFLT+T_USER: + sig = SIGFPE; + fault_type = FPE_INTOVF; + break; + case T_FPEPFLT+T_USER: + case T_FPEIFLT+T_USER: + sig = SIGFPE; + break; + case T_SIGSYS+T_USER: + sig = SIGSYS; + break; + case T_SIGTRAP+T_USER: + sig = SIGTRAP; + fault_type = TRAP_TRACE; + break; + case T_STEPBPT+T_USER: + /* + * This trap is used by the kernel to support single-step + * debugging (although any user could generate this trap + * which should probably be handled differently). When a + * process is continued by a debugger with the PT_STEP + * function of ptrace (single step), the kernel inserts + * one or two breakpoints in the user process so that only + * one instruction (or two in the case of a delayed branch) + * is executed. When this breakpoint is hit, we get the + * T_STEPBPT trap. + */ + { + unsigned va; + unsigned instr; + struct uio uio; + struct iovec iov; + unsigned pc = PC_REGS(&frame->tf_regs); + + /* read break instruction */ + copyin((caddr_t)pc, &instr, sizeof(unsigned)); +#if 0 + printf("trap: %s (%d) breakpoint %x at %x: (adr %x ins %x)\n", + p->p_comm, p->p_pid, instr, pc, + p->p_md.md_ss_addr, p->p_md.md_ss_instr); /* XXX */ +#endif + /* check and see if we got here by accident */ + if ((p->p_md.md_ss_addr != pc && + p->p_md.md_ss_taken_addr != pc) || + instr != SSBREAKPOINT) { + sig = SIGTRAP; + fault_type = TRAP_TRACE; + break; + } + /* restore original instruction and clear BP */ + instr = p->p_md.md_ss_instr; + va = p->p_md.md_ss_addr; + if (va != 0) { + iov.iov_base = (caddr_t)&instr; + iov.iov_len = sizeof(int); + uio.uio_iov = &iov; + uio.uio_iovcnt = 1; + uio.uio_offset = (off_t)va; + uio.uio_resid = sizeof(int); + uio.uio_segflg = UIO_SYSSPACE; + uio.uio_rw = UIO_WRITE; + uio.uio_procp = curproc; + procfs_domem(p, p, NULL, &uio); + } + + /* branch taken instruction */ + instr = p->p_md.md_ss_taken_instr; + va = p->p_md.md_ss_taken_addr; + if (instr != 0) { + iov.iov_base = (caddr_t)&instr; + iov.iov_len = sizeof(int); + uio.uio_iov = &iov; + uio.uio_iovcnt = 1; + uio.uio_offset = (off_t)va; + uio.uio_resid = sizeof(int); + uio.uio_segflg = UIO_SYSSPACE; + uio.uio_rw = UIO_WRITE; + uio.uio_procp = curproc; + procfs_domem(p, p, NULL, &uio); + } +#if 1 + frame->tf_sfip = frame->tf_snip; /* set up next FIP */ + frame->tf_snip = pc; /* set up next NIP */ + frame->tf_snip |= 2; /* set valid bit */ +#endif + p->p_md.md_ss_addr = 0; + p->p_md.md_ss_instr = 0; + p->p_md.md_ss_taken_addr = 0; + p->p_md.md_ss_taken_instr = 0; + sig = SIGTRAP; + fault_type = TRAP_BRKPT; + } + break; + + case T_USERBPT+T_USER: + /* + * This trap is meant to be used by debuggers to implement + * breakpoint debugging. When we get this trap, we just + * return a signal which gets caught by the debugger. + */ + frame->tf_sfip = frame->tf_snip; /* set up the next FIP */ + frame->tf_snip = frame->tf_sxip; /* set up the next NIP */ + sig = SIGTRAP; + fault_type = TRAP_BRKPT; + break; + + case T_ASTFLT+T_USER: + uvmexp.softs++; + want_ast = 0; + if (p->p_flag & P_OWEUPC) { + p->p_flag &= ~P_OWEUPC; + ADDUPROF(p); + } + break; + } + + /* + * If trap from supervisor mode, just return + */ + if (type < T_USER) + return; + + if (sig) { + sv.sival_int = fault_addr; + trapsignal(p, sig, fault_code, fault_type, sv); + /* + * don't want multiple faults - we are going to + * deliver signal. + */ + frame->tf_dmt0 = 0; + frame->tf_ipfsr = frame->tf_dpfsr = 0; + } + + userret(p, frame, sticks); +} +#endif /* m88100 */ + +#ifdef M88110 +void +m88110_trap(unsigned type, struct trapframe *frame) +{ + struct proc *p; + u_quad_t sticks = 0; + struct vm_map *map; + vaddr_t va; + vm_prot_t ftype; + int fault_type; + u_long fault_code; + unsigned nss, fault_addr; + struct vmspace *vm; + union sigval sv; + int result; +#ifdef DDB + int s; /* IPL */ +#endif + int sig = 0; + pt_entry_t *pte; + + extern struct vm_map *kernel_map; + extern unsigned guarded_access_start; + extern unsigned guarded_access_end; + extern unsigned guarded_access_bad; + extern pt_entry_t *pmap_pte(pmap_t, vaddr_t); + + uvmexp.traps++; + if ((p = curproc) == NULL) + p = &proc0; + + if (USERMODE(frame->tf_epsr)) { + sticks = p->p_sticks; + type += T_USER; + p->p_md.md_tf = frame; /* for ptrace/signals */ + } + fault_type = 0; + fault_code = 0; + fault_addr = frame->tf_exip & XIP_ADDR; + + switch (type) { + default: + panictrap(frame->tf_vector, frame); + break; + /*NOTREACHED*/ + + case T_197_READ+T_USER: + case T_197_READ: + DEBUG_MSG(("DMMU read miss: Hardware Table Searches should be enabled!\n")); + panictrap(frame->tf_vector, frame); + break; + /*NOTREACHED*/ + case T_197_WRITE+T_USER: + case T_197_WRITE: + DEBUG_MSG(("DMMU write miss: Hardware Table Searches should be enabled!\n")); + panictrap(frame->tf_vector, frame); + break; + /*NOTREACHED*/ + case T_197_INST+T_USER: + case T_197_INST: + DEBUG_MSG(("IMMU miss: Hardware Table Searches should be enabled!\n")); + panictrap(frame->tf_vector, frame); + break; + /*NOTREACHED*/ +#ifdef DDB + case T_KDB_TRACE: + s = splhigh(); + db_enable_interrupt(); + ddb_break_trap(T_KDB_TRACE, (db_regs_t*)frame); + db_disable_interrupt(); + splx(s); + return; + case T_KDB_BREAK: + s = splhigh(); + db_enable_interrupt(); + ddb_break_trap(T_KDB_BREAK, (db_regs_t*)frame); + db_disable_interrupt(); + splx(s); + return; + case T_KDB_ENTRY: + s = splhigh(); + db_enable_interrupt(); + ddb_entry_trap(T_KDB_ENTRY, (db_regs_t*)frame); + db_disable_interrupt(); + /* skip one instruction */ + if (frame->tf_exip & 1) + frame->tf_exip = frame->tf_enip; + else + frame->tf_exip += 4; + frame->tf_enip = 0; + splx(s); + return; +#if 0 + case T_ILLFLT: + s = splhigh(); + db_enable_interrupt(); + ddb_error_trap(type == T_ILLFLT ? "unimplemented opcode" : + "error fault", (db_regs_t*)frame); + db_disable_interrupt(); + splx(s); + return; +#endif /* 0 */ +#endif /* DDB */ + case T_ILLFLT: + DEBUG_MSG(("Unimplemented opcode!\n")); + panictrap(frame->tf_vector, frame); + break; + case T_NON_MASK: + case T_NON_MASK+T_USER: + /* This function pointer is set in machdep.c + It calls m197_ext_int - smurph */ + (*md.interrupt_func)(T_NON_MASK, frame); + return; + case T_INT: + case T_INT+T_USER: + (*md.interrupt_func)(T_INT, frame); + return; + case T_MISALGNFLT: + DEBUG_MSG(("kernel mode misaligned " + "access exception @ 0x%08x\n", frame->tf_exip)); + panictrap(frame->tf_vector, frame); + break; + /*NOTREACHED*/ + + case T_INSTFLT: + /* kernel mode instruction access fault. + * Should never, never happen for a non-paged kernel. + */ +#ifdef TRAPDEBUG + printf("Kernel Instruction fault exip %x isr %x ilar %x\n", + frame->tf_exip, frame->tf_isr, frame->tf_ilar); +#endif + panictrap(frame->tf_vector, frame); + break; + /*NOTREACHED*/ + + case T_DATAFLT: + /* kernel mode data fault */ + + /* data fault on the user address? */ + if ((frame->tf_dsr & CMMU_DSR_SU) == 0) { + type = T_DATAFLT + T_USER; + goto m88110_user_fault; + } + +#ifdef TRAPDEBUG + printf("Kernel Data access fault exip %x dsr %x dlar %x\n", + frame->tf_exip, frame->tf_dsr, frame->tf_dlar); +#endif + + fault_addr = frame->tf_dlar; + if (frame->tf_dsr & CMMU_DSR_RW) { + ftype = VM_PROT_READ; + fault_code = VM_PROT_READ; + } else { + ftype = VM_PROT_READ|VM_PROT_WRITE; + fault_code = VM_PROT_WRITE; + } + + va = trunc_page((vaddr_t)fault_addr); + if (va == 0) { + panic("trap: bad kernel access at %x", fault_addr); + } + + vm = p->p_vmspace; + map = kernel_map; + + if (frame->tf_dsr & CMMU_DSR_BE) { + /* + * If it is a guarded access, bus error is OK. + */ + if ((frame->tf_exip & XIP_ADDR) >= + (unsigned)&guarded_access_start && + (frame->tf_exip & XIP_ADDR) <= + (unsigned)&guarded_access_end) { + frame->tf_exip = (unsigned)&guarded_access_bad; + frame->tf_enip = 0; + return; + } + } + if (frame->tf_dsr & (CMMU_DSR_SI | CMMU_DSR_PI)) { + frame->tf_dsr &= ~CMMU_DSR_WE; /* undefined */ + /* + * On a segment or a page fault, call uvm_fault() to + * resolve the fault. + */ + result = uvm_fault(map, va, VM_FAULT_INVALID, ftype); + if (result == 0) + return; + } + if (frame->tf_dsr & CMMU_DSR_WE) { /* write fault */ + /* + * This could be a write protection fault or an + * exception to set the used and modified bits + * in the pte. Basically, if we got a write error, + * then we already have a pte entry that faulted + * in from a previous seg fault or page fault. + * Get the pte and check the status of the + * modified and valid bits to determine if this + * indeed a real write fault. XXX smurph + */ + pte = pmap_pte(map->pmap, va); + if (pte == PT_ENTRY_NULL) + panic("NULL pte on write fault??"); + if (!(*pte & PG_M) && !(*pte & PG_RO)) { + /* Set modified bit and try the write again. */ +#ifdef TRAPDEBUG + printf("Corrected kernel write fault, map %x pte %x\n", + map->pmap, *pte); +#endif + *pte |= PG_M; + return; +#if 1 /* shouldn't happen */ + } else { + /* must be a real wp fault */ +#ifdef TRAPDEBUG + printf("Uncorrected kernel write fault, map %x pte %x\n", + map->pmap, *pte); +#endif + result = uvm_fault(map, va, VM_FAULT_INVALID, ftype); + if (result == 0) + return; +#endif + } + } + panictrap(frame->tf_vector, frame); + /* NOTREACHED */ + case T_INSTFLT+T_USER: + /* User mode instruction access fault */ + /* FALLTHROUGH */ + case T_DATAFLT+T_USER: +m88110_user_fault: + if (type == T_INSTFLT+T_USER) { + ftype = VM_PROT_READ; + fault_code = VM_PROT_READ; +#ifdef TRAPDEBUG + printf("User Instruction fault exip %x isr %x ilar %x\n", + frame->tf_exip, frame->tf_isr, frame->tf_ilar); +#endif + } else { + fault_addr = frame->tf_dlar; + if (frame->tf_dsr & CMMU_DSR_RW) { + ftype = VM_PROT_READ; + fault_code = VM_PROT_READ; + } else { + ftype = VM_PROT_READ|VM_PROT_WRITE; + fault_code = VM_PROT_WRITE; + } +#ifdef TRAPDEBUG + printf("User Data access fault exip %x dsr %x dlar %x\n", + frame->tf_exip, frame->tf_dsr, frame->tf_dlar); +#endif + } + + va = trunc_page((vaddr_t)fault_addr); + + vm = p->p_vmspace; + map = &vm->vm_map; + + /* + * Call uvm_fault() to resolve non-bus error faults + * whenever possible. + */ + if (type == T_DATAFLT+T_USER) { + /* data faults */ + if (frame->tf_dsr & CMMU_DSR_BE) { + /* bus error */ + result = EACCES; + } else + if (frame->tf_dsr & (CMMU_DSR_SI | CMMU_DSR_PI)) { + /* segment or page fault */ + result = uvm_fault(map, va, VM_FAULT_INVALID, ftype); + if (result == EACCES) + result = EFAULT; + } else + if (frame->tf_dsr & (CMMU_DSR_CP | CMMU_DSR_WA)) { + /* copyback or write allocate error */ + result = 0; + } else + if (frame->tf_dsr & CMMU_DSR_WE) { + /* write fault */ + /* This could be a write protection fault or an + * exception to set the used and modified bits + * in the pte. Basically, if we got a write + * error, then we already have a pte entry that + * faulted in from a previous seg fault or page + * fault. + * Get the pte and check the status of the + * modified and valid bits to determine if this + * indeed a real write fault. XXX smurph + */ + pte = pmap_pte(vm_map_pmap(map), va); +#ifdef DEBUG + if (pte == PT_ENTRY_NULL) + panic("NULL pte on write fault??"); +#endif + if (!(*pte & PG_M) && !(*pte & PG_RO)) { + /* + * Set modified bit and try the + * write again. + */ +#ifdef TRAPDEBUG + printf("Corrected userland write fault, map %x pte %x\n", + map->pmap, *pte); +#endif + *pte |= PG_M; + /* + * invalidate ATCs to force + * table search + */ + set_dcmd(CMMU_DCMD_INV_UATC); + return; + } else { + /* must be a real wp fault */ +#ifdef TRAPDEBUG + printf("Uncorrected userland write fault, map %x pte %x\n", + map->pmap, *pte); +#endif + result = uvm_fault(map, va, VM_FAULT_INVALID, ftype); + if (result == EACCES) + result = EFAULT; + } + } else { +#ifdef TRAPDEBUG + printf("Unexpected Data access fault dsr %x\n", + frame->tf_dsr); +#endif + panictrap(frame->tf_vector, frame); + } + } else { + /* instruction faults */ + if (frame->tf_isr & + (CMMU_ISR_BE | CMMU_ISR_SP | CMMU_ISR_TBE)) { + /* bus error, supervisor protection */ + result = EACCES; + } else + if (frame->tf_isr & (CMMU_ISR_SI | CMMU_ISR_PI)) { + /* segment or page fault */ + result = uvm_fault(map, va, VM_FAULT_INVALID, ftype); + if (result == EACCES) + result = EFAULT; + } else { +#ifdef TRAPDEBUG + printf("Unexpected Instruction fault isr %x\n", + frame->tf_isr); +#endif + panictrap(frame->tf_vector, frame); + } + } + + if ((caddr_t)va >= vm->vm_maxsaddr) { + if (result == 0) { + nss = btoc(USRSTACK - va);/* XXX check this */ + if (nss > vm->vm_ssize) + vm->vm_ssize = nss; + } + } + + /* + * This could be a fault caused in copyin*() + * while accessing user space. + */ + if (result != 0 && p->p_addr->u_pcb.pcb_onfault != NULL) { + frame->tf_exip = p->p_addr->u_pcb.pcb_onfault; + frame->tf_enip = 0; + frame->tf_dsr = frame->tf_isr = 0; + /* + * Continue as if the fault had been resolved. + */ + result = 0; + } + + if (result != 0) { + sig = result == EACCES ? SIGBUS : SIGSEGV; + fault_type = result == EACCES ? + BUS_ADRERR : SEGV_MAPERR; + } + break; + case T_MISALGNFLT+T_USER: + sig = SIGBUS; + fault_type = BUS_ADRALN; + break; + case T_PRIVINFLT+T_USER: + case T_ILLFLT+T_USER: +#ifndef DDB + case T_KDB_BREAK: + case T_KDB_ENTRY: + case T_KDB_TRACE: +#endif + case T_KDB_BREAK+T_USER: + case T_KDB_ENTRY+T_USER: + case T_KDB_TRACE+T_USER: + sig = SIGILL; + break; + case T_BNDFLT+T_USER: + sig = SIGFPE; + break; + case T_ZERODIV+T_USER: + sig = SIGFPE; + fault_type = FPE_INTDIV; + break; + case T_OVFFLT+T_USER: + sig = SIGFPE; + fault_type = FPE_INTOVF; + break; + case T_FPEPFLT+T_USER: + case T_FPEIFLT+T_USER: + sig = SIGFPE; + break; + case T_SIGSYS+T_USER: + sig = SIGSYS; + break; + case T_SIGTRAP+T_USER: + sig = SIGTRAP; + fault_type = TRAP_TRACE; + break; + case T_STEPBPT+T_USER: + /* + * This trap is used by the kernel to support single-step + * debugging (although any user could generate this trap + * which should probably be handled differently). When a + * process is continued by a debugger with the PT_STEP + * function of ptrace (single step), the kernel inserts + * one or two breakpoints in the user process so that only + * one instruction (or two in the case of a delayed branch) + * is executed. When this breakpoint is hit, we get the + * T_STEPBPT trap. + */ + { + unsigned instr; + struct uio uio; + struct iovec iov; + unsigned pc = PC_REGS(&frame->tf_regs); + + /* read break instruction */ + copyin((caddr_t)pc, &instr, sizeof(unsigned)); +#if 0 + printf("trap: %s (%d) breakpoint %x at %x: (adr %x ins %x)\n", + p->p_comm, p->p_pid, instr, pc, + p->p_md.md_ss_addr, p->p_md.md_ss_instr); /* XXX */ +#endif + /* check and see if we got here by accident */ +#ifdef notyet + if (p->p_md.md_ss_addr != pc || instr != SSBREAKPOINT) { + sig = SIGTRAP; + fault_type = TRAP_TRACE; + break; + } +#endif + /* restore original instruction and clear BP */ + /*sig = suiword((caddr_t)pc, p->p_md.md_ss_instr);*/ + instr = p->p_md.md_ss_instr; + if (instr != 0) { + iov.iov_base = (caddr_t)&instr; + iov.iov_len = sizeof(int); + uio.uio_iov = &iov; + uio.uio_iovcnt = 1; + uio.uio_offset = (off_t)pc; + uio.uio_resid = sizeof(int); + uio.uio_segflg = UIO_SYSSPACE; + uio.uio_rw = UIO_WRITE; + uio.uio_procp = curproc; + } + + p->p_md.md_ss_addr = 0; + sig = SIGTRAP; + fault_type = TRAP_BRKPT; + break; + } + case T_USERBPT+T_USER: + /* + * This trap is meant to be used by debuggers to implement + * breakpoint debugging. When we get this trap, we just + * return a signal which gets caught by the debugger. + */ + sig = SIGTRAP; + fault_type = TRAP_BRKPT; + break; + + case T_ASTFLT+T_USER: + uvmexp.softs++; + want_ast = 0; + if (p->p_flag & P_OWEUPC) { + p->p_flag &= ~P_OWEUPC; + ADDUPROF(p); + } + break; + } + + /* + * If trap from supervisor mode, just return + */ + if (type < T_USER) + return; + + if (sig) { + sv.sival_int = fault_addr; + trapsignal(p, sig, fault_code, fault_type, sv); + /* + * don't want multiple faults - we are going to + * deliver signal. + */ + frame->tf_dsr = frame->tf_isr = 0; + } + + userret(p, frame, sticks); +} +#endif /* MVME197 */ + +__dead void +error_fatal(struct trapframe *frame) +{ +#ifdef DDB + switch (frame->tf_vector) { + case 0: + db_printf("\n[RESET EXCEPTION (Really Bad News[tm]) frame %8p]\n", frame); + db_printf("This is usually caused by a branch to a NULL function pointer.\n"); + db_printf("e.g. jump to address 0. Use the debugger trace command to track it down.\n"); + break; + default: + db_printf("\n[ERROR EXCEPTION (Bad News[tm]) frame %p]\n", frame); + db_printf("This is usually an exception within an exception. The trap\n"); + db_printf("frame shadow registers you are about to see are invalid.\n"); + db_printf("(read totally useless) But R1 to R31 might be interesting.\n"); + break; + } + regdump((struct trapframe*)frame); +#endif /* DDB */ + panic("unrecoverable exception %d", frame->tf_vector); +} + +#ifdef M88100 +void +m88100_syscall(register_t code, struct trapframe *tf) +{ + int i, nsys, nap; + struct sysent *callp; + struct proc *p; + int error; + register_t args[11], rval[2], *ap; + u_quad_t sticks; +#ifdef DIAGNOSTIC + extern struct pcb *curpcb; +#endif + + uvmexp.syscalls++; + + p = curproc; + + callp = p->p_emul->e_sysent; + nsys = p->p_emul->e_nsysent; + +#ifdef DIAGNOSTIC + if (USERMODE(tf->tf_epsr) == 0) + panic("syscall"); + if (curpcb != &p->p_addr->u_pcb) + panic("syscall curpcb/ppcb"); + if (tf != (struct trapframe *)&curpcb->user_state) + panic("syscall trapframe"); +#endif + + sticks = p->p_sticks; + p->p_md.md_tf = tf; + + /* + * For 88k, all the arguments are passed in the registers (r2-r12) + * For syscall (and __syscall), r2 (and r3) has the actual code. + * __syscall takes a quad syscall number, so that other + * arguments are at their natural alignments. + */ + ap = &tf->tf_r[2]; + nap = 11; /* r2-r12 */ + + switch (code) { + case SYS_syscall: + code = *ap++; + nap--; + break; + case SYS___syscall: + if (callp != sysent) + break; + code = ap[_QUAD_LOWWORD]; + ap += 2; + nap -= 2; + break; + } + + /* Callp currently points to syscall, which returns ENOSYS. */ + if (code < 0 || code >= nsys) + callp += p->p_emul->e_nosys; + else { + callp += code; + i = callp->sy_argsize / sizeof(register_t); + if (i > nap) + panic("syscall nargs"); + /* + * just copy them; syscall stub made sure all the + * args are moved from user stack to registers. + */ + bcopy((caddr_t)ap, (caddr_t)args, i * sizeof(register_t)); + } + +#ifdef SYSCALL_DEBUG + scdebug_call(p, code, args); +#endif +#ifdef KTRACE + if (KTRPOINT(p, KTR_SYSCALL)) + ktrsyscall(p, code, callp->sy_argsize, args); +#endif + rval[0] = 0; + rval[1] = 0; +#if NSYSTRACE > 0 + if (ISSET(p->p_flag, P_SYSTRACE)) + error = systrace_redirect(code, p, args, rval); + else +#endif + error = (*callp->sy_call)(p, args, rval); + /* + * system call will look like: + * ld r10, r31, 32; r10,r11,r12 might be garbage. + * ld r11, r31, 36 + * ld r12, r31, 40 + * or r13, r0, <code> + * tb0 0, r0, <128> <- sxip + * br err <- snip + * jmp r1 <- sfip + * err: or.u r3, r0, hi16(errno) + * st r2, r3, lo16(errno) + * subu r2, r0, 1 + * jmp r1 + * + * So, when we take syscall trap, sxip/snip/sfip will be as + * shown above. + * Given this, + * 1. If the system call returned 0, need to skip nip. + * nip = fip, fip += 4 + * (doesn't matter what fip + 4 will be but we will never + * execute this since jmp r1 at nip will change the execution flow.) + * 2. If the system call returned an errno > 0, plug the value + * in r2, and leave nip and fip unchanged. This will have us + * executing "br err" on return to user space. + * 3. If the system call code returned ERESTART, + * we need to rexecute the trap instruction. Back up the pipe + * line. + * fip = nip, nip = xip + * 4. If the system call returned EJUSTRETURN, don't need to adjust + * any pointers. + */ + + switch (error) { + case 0: + /* + * If fork succeeded and we are the child, our stack + * has moved and the pointer tf is no longer valid, + * and p is wrong. Compute the new trapframe pointer. + * (The trap frame invariably resides at the + * tippity-top of the u. area.) + */ + p = curproc; + tf = (struct trapframe *)USER_REGS(p); + tf->tf_r[2] = rval[0]; + tf->tf_r[3] = rval[1]; + tf->tf_epsr &= ~PSR_C; + tf->tf_snip = tf->tf_sfip & ~NIP_E; + tf->tf_sfip = tf->tf_snip + 4; + break; + case ERESTART: + /* + * If (error == ERESTART), back up the pipe line. This + * will end up reexecuting the trap. + */ + tf->tf_epsr &= ~PSR_C; + tf->tf_sfip = tf->tf_snip & ~FIP_E; + tf->tf_snip = tf->tf_sxip & ~NIP_E; + break; + case EJUSTRETURN: + /* if (error == EJUSTRETURN), leave the ip's alone */ + tf->tf_epsr &= ~PSR_C; + break; + default: + /* error != ERESTART && error != EJUSTRETURN*/ + if (p->p_emul->e_errno) + error = p->p_emul->e_errno[error]; + tf->tf_r[2] = error; + tf->tf_epsr |= PSR_C; /* fail */ + tf->tf_snip = tf->tf_snip & ~NIP_E; + tf->tf_sfip = tf->tf_sfip & ~FIP_E; + break; + } +#ifdef SYSCALL_DEBUG + scdebug_ret(p, code, error, rval); +#endif + userret(p, tf, sticks); +#ifdef KTRACE + if (KTRPOINT(p, KTR_SYSRET)) + ktrsysret(p, code, error, rval[0]); +#endif +} +#endif /* M88100 */ + +#ifdef M88110 +/* Instruction pointers operate differently on mc88110 */ +void +m88110_syscall(register_t code, struct trapframe *tf) +{ + int i, nsys, nap; + struct sysent *callp; + struct proc *p; + int error; + register_t args[11], rval[2], *ap; + u_quad_t sticks; +#ifdef DIAGNOSTIC + extern struct pcb *curpcb; +#endif + + uvmexp.syscalls++; + + p = curproc; + + callp = p->p_emul->e_sysent; + nsys = p->p_emul->e_nsysent; + +#ifdef DIAGNOSTIC + if (USERMODE(tf->tf_epsr) == 0) + panic("syscall"); + if (curpcb != &p->p_addr->u_pcb) + panic("syscall curpcb/ppcb"); + if (tf != (struct trapframe *)&curpcb->user_state) + panic("syscall trapframe"); +#endif + + sticks = p->p_sticks; + p->p_md.md_tf = tf; + + /* + * For 88k, all the arguments are passed in the registers (r2-r12) + * For syscall (and __syscall), r2 (and r3) has the actual code. + * __syscall takes a quad syscall number, so that other + * arguments are at their natural alignments. + */ + ap = &tf->tf_r[2]; + nap = 11; /* r2-r12 */ + + switch (code) { + case SYS_syscall: + code = *ap++; + nap--; + break; + case SYS___syscall: + if (callp != sysent) + break; + code = ap[_QUAD_LOWWORD]; + ap += 2; + nap -= 2; + break; + } + + /* Callp currently points to syscall, which returns ENOSYS. */ + if (code < 0 || code >= nsys) + callp += p->p_emul->e_nosys; + else { + callp += code; + i = callp->sy_argsize / sizeof(register_t); + if (i > nap) + panic("syscall nargs"); + /* + * just copy them; syscall stub made sure all the + * args are moved from user stack to registers. + */ + bcopy((caddr_t)ap, (caddr_t)args, i * sizeof(register_t)); + } +#ifdef SYSCALL_DEBUG + scdebug_call(p, code, args); +#endif +#ifdef KTRACE + if (KTRPOINT(p, KTR_SYSCALL)) + ktrsyscall(p, code, callp->sy_argsize, args); +#endif + rval[0] = 0; + rval[1] = 0; +#if NSYSTRACE > 0 + if (ISSET(p->p_flag, P_SYSTRACE)) + error = systrace_redirect(code, p, args, rval); + else +#endif + error = (*callp->sy_call)(p, args, rval); + /* + * system call will look like: + * ld r10, r31, 32; r10,r11,r12 might be garbage. + * ld r11, r31, 36 + * ld r12, r31, 40 + * or r13, r0, <code> + * tb0 0, r0, <128> <- exip + * br err <- enip + * jmp r1 + * err: or.u r3, r0, hi16(errno) + * st r2, r3, lo16(errno) + * subu r2, r0, 1 + * jmp r1 + * + * So, when we take syscall trap, exip/enip will be as + * shown above. + * Given this, + * 1. If the system call returned 0, need to jmp r1. + * exip += 8 + * 2. If the system call returned an errno > 0, increment + * exip += 4 and plug the value in r2. This will have us + * executing "br err" on return to user space. + * 3. If the system call code returned ERESTART, + * we need to rexecute the trap instruction. leave exip as is. + * 4. If the system call returned EJUSTRETURN, just return. + * exip += 4 + */ + + switch (error) { + case 0: + /* + * If fork succeeded and we are the child, our stack + * has moved and the pointer tf is no longer valid, + * and p is wrong. Compute the new trapframe pointer. + * (The trap frame invariably resides at the + * tippity-top of the u. area.) + */ + p = curproc; + tf = (struct trapframe *)USER_REGS(p); + tf->tf_r[2] = rval[0]; + tf->tf_r[3] = rval[1]; + tf->tf_epsr &= ~PSR_C; + /* skip two instructions */ + if (tf->tf_exip & 1) + tf->tf_exip = tf->tf_enip + 4; + else + tf->tf_exip += 4 + 4; + tf->tf_enip = 0; + break; + case ERESTART: + /* + * Reexecute the trap. + * exip is already at the trap instruction, so + * there is nothing to do. + */ + tf->tf_epsr &= ~PSR_C; + break; + case EJUSTRETURN: + tf->tf_epsr &= ~PSR_C; + /* skip one instruction */ + if (tf->tf_exip & 1) + tf->tf_exip = tf->tf_enip; + else + tf->tf_exip += 4; + tf->tf_enip = 0; + break; + default: + if (p->p_emul->e_errno) + error = p->p_emul->e_errno[error]; + tf->tf_r[2] = error; + tf->tf_epsr |= PSR_C; /* fail */ + /* skip one instruction */ + if (tf->tf_exip & 1) + tf->tf_exip = tf->tf_enip; + else + tf->tf_exip += 4; + tf->tf_enip = 0; + break; + } + +#ifdef SYSCALL_DEBUG + scdebug_ret(p, code, error, rval); +#endif + userret(p, tf, sticks); +#ifdef KTRACE + if (KTRPOINT(p, KTR_SYSRET)) + ktrsysret(p, code, error, rval[0]); +#endif +} +#endif /* MVME197 */ + +/* + * Set up return-value registers as fork() libc stub expects, + * and do normal return-to-user-mode stuff. + */ +void +child_return(arg) + void *arg; +{ + struct proc *p = arg; + struct trapframe *tf; + + tf = (struct trapframe *)USER_REGS(p); + tf->tf_r[2] = 0; + tf->tf_r[3] = 0; + tf->tf_epsr &= ~PSR_C; + if (cputyp != CPU_88110) { + tf->tf_snip = tf->tf_sfip & XIP_ADDR; + tf->tf_sfip = tf->tf_snip + 4; + } else { + /* skip two instructions */ + if (tf->tf_exip & 1) + tf->tf_exip = tf->tf_enip + 4; + else + tf->tf_exip += 4 + 4; + tf->tf_enip = 0; + } + + userret(p, tf, p->p_sticks); +#ifdef KTRACE + if (KTRPOINT(p, KTR_SYSRET)) + ktrsysret(p, SYS_fork, 0, 0); +#endif +} + +#ifdef PTRACE + +/* + * User Single Step Debugging Support + */ + +#include <sys/ptrace.h> + +unsigned ss_get_value(struct proc *, unsigned, int); +int ss_put_value(struct proc *, unsigned, unsigned, int); +unsigned ss_branch_taken(unsigned, unsigned, + unsigned (*func)(unsigned int, struct reg *), struct reg *); +unsigned int ss_getreg_val(unsigned int, struct reg *); +int ss_inst_branch(unsigned); +int ss_inst_delayed(unsigned); +unsigned ss_next_instr_address(struct proc *, unsigned, unsigned); + +unsigned +ss_get_value(struct proc *p, unsigned addr, int size) +{ + struct uio uio; + struct iovec iov; + unsigned value; + + iov.iov_base = (caddr_t)&value; + iov.iov_len = size; + uio.uio_iov = &iov; + uio.uio_iovcnt = 1; + uio.uio_offset = (off_t)addr; + uio.uio_resid = size; + uio.uio_segflg = UIO_SYSSPACE; + uio.uio_rw = UIO_READ; + uio.uio_procp = curproc; + procfs_domem(curproc, p, NULL, &uio); + return value; +} + +int +ss_put_value(struct proc *p, unsigned addr, unsigned value, int size) +{ + struct uio uio; + struct iovec iov; + + iov.iov_base = (caddr_t)&value; + iov.iov_len = size; + uio.uio_iov = &iov; + uio.uio_iovcnt = 1; + uio.uio_offset = (off_t)addr; + uio.uio_resid = size; + uio.uio_segflg = UIO_SYSSPACE; + uio.uio_rw = UIO_WRITE; + uio.uio_procp = curproc; + return procfs_domem(curproc, p, NULL, &uio); +} + +/* + * ss_branch_taken(instruction, program counter, func, func_data) + * + * instruction will be a control flow instruction location at address pc. + * Branch taken is supposed to return the address to which the instruction + * would jump if the branch is taken. Func can be used to get the current + * register values when invoked with a register number and func_data as + * arguments. + * + * If the instruction is not a control flow instruction, panic. + */ +unsigned +ss_branch_taken(unsigned inst, unsigned pc, + unsigned (*func)(unsigned int, struct reg *), struct reg *func_data) +{ + /* check if br/bsr */ + if ((inst & 0xf0000000) == 0xc0000000) { + /* signed 26 bit pc relative displacement, shift left two bits */ + inst = (inst & 0x03ffffff) << 2; + /* check if sign extension is needed */ + if (inst & 0x08000000) + inst |= 0xf0000000; + return (pc + inst); + } + + /* check if bb0/bb1/bcnd case */ + switch (inst & 0xf8000000) { + case 0xd0000000: /* bb0 */ + case 0xd8000000: /* bb1 */ + case 0xe8000000: /* bcnd */ + /* signed 16 bit pc relative displacement, shift left two bits */ + inst = (inst & 0x0000ffff) << 2; + /* check if sign extension is needed */ + if (inst & 0x00020000) + inst |= 0xfffc0000; + return (pc + inst); + } + + /* check jmp/jsr case */ + /* check bits 5-31, skipping 10 & 11 */ + if ((inst & 0xfffff3e0) == 0xf400c000) + return (*func)(inst & 0x1f, func_data); /* the register value */ + + /* can't happen */ + return (0); +} + +/* + * ss_getreg_val - handed a register number and an exception frame. + * Returns the value of the register in the specified + * frame. Only makes sense for general registers. + */ +unsigned int +ss_getreg_val(unsigned int regno, struct reg *regs) +{ + return (regno == 0 ? 0 : regs->r[regno]); +} + +int +ss_inst_branch(unsigned ins) +{ + /* check high five bits */ + + switch (ins >> (32 - 5)) { + case 0x18: /* br */ + case 0x1a: /* bb0 */ + case 0x1b: /* bb1 */ + case 0x1d: /* bcnd */ + return TRUE; + break; + case 0x1e: /* could be jmp */ + if ((ins & 0xfffffbe0) == 0xf400c000) + return TRUE; + } + + return FALSE; +} + +/* ss_inst_delayed - this instruction is followed by a delay slot. Could be + br.n, bsr.n bb0.n, bb1.n, bcnd.n or jmp.n or jsr.n */ + +int +ss_inst_delayed(unsigned ins) +{ + /* check the br, bsr, bb0, bb1, bcnd cases */ + switch ((ins & 0xfc000000) >> (32 - 6)) { + case 0x31: /* br */ + case 0x33: /* bsr */ + case 0x35: /* bb0 */ + case 0x37: /* bb1 */ + case 0x3b: /* bcnd */ + return TRUE; + } + + /* check the jmp, jsr cases */ + /* mask out bits 0-4, bit 11 */ + return ((ins & 0xfffff7e0) == 0xf400c400) ? TRUE : FALSE; +} + +unsigned +ss_next_instr_address(struct proc *p, unsigned pc, unsigned delay_slot) +{ + if (delay_slot == 0) + return (pc + 4); + else { + if (ss_inst_delayed(ss_get_value(p, pc, sizeof(int)))) + return (pc + 4); + else + return pc; + } +} + +int +cpu_singlestep(p) + struct proc *p; +{ + struct reg *sstf = USER_REGS(p); + unsigned pc, brpc; + int bpinstr = SSBREAKPOINT; + unsigned curinstr; + + pc = PC_REGS(sstf); + /* + * User was stopped at pc, e.g. the instruction + * at pc was not executed. + * Fetch what's at the current location. + */ + curinstr = ss_get_value(p, pc, sizeof(int)); + + /* compute next address after current location */ + if (curinstr != 0) { + if (ss_inst_branch(curinstr) || + inst_call(curinstr) || inst_return(curinstr)) { + brpc = ss_branch_taken(curinstr, pc, ss_getreg_val, sstf); + if (brpc != pc) { /* self-branches are hopeless */ + p->p_md.md_ss_taken_addr = brpc; + p->p_md.md_ss_taken_instr = + ss_get_value(p, brpc, sizeof(int)); + /* Store breakpoint instruction at the + "next" location now. */ + if (ss_put_value(p, brpc, bpinstr, + sizeof(int)) != 0) + return (EFAULT); + } + } + pc = ss_next_instr_address(p, pc, 0); + } else { + pc = PC_REGS(sstf) + 4; + } + + if (p->p_md.md_ss_addr != NULL) { + return (EFAULT); + } + + p->p_md.md_ss_addr = pc; + + /* Fetch what's at the "next" location. */ + p->p_md.md_ss_instr = ss_get_value(p, pc, sizeof(int)); + + /* Store breakpoint instruction at the "next" location now. */ + if (ss_put_value(p, pc, bpinstr, sizeof(int)) != 0) + return (EFAULT); + + return (0); +} + +#endif /* PTRACE */ + +#ifdef DIAGNOSTIC +void +splassert_check(int wantipl, const char *func) +{ + int oldipl; + + /* + * This will raise the spl if too low, + * in a feeble attempt to reduce further damage. + */ + oldipl = raiseipl(wantipl); + + if (oldipl < wantipl) { + splassert_fail(wantipl, oldipl, func); + } +} +#endif diff --git a/sys/arch/luna88k/luna88k/vectors_88100.S b/sys/arch/luna88k/luna88k/vectors_88100.S new file mode 100644 index 00000000000..e9e5a66734e --- /dev/null +++ b/sys/arch/luna88k/luna88k/vectors_88100.S @@ -0,0 +1,89 @@ +/* $OpenBSD: vectors_88100.S,v 1.1 2004/04/21 15:24:14 aoyama Exp $ */ +/* + * Mach Operating System + * Copyright (c) 1991, 1992 Carnegie Mellon University + * Copyright (c) 1991 OMRON Corporation + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON AND OMRON ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON AND OMRON DISCLAIM ANY LIABILITY OF ANY KIND + * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie the + * rights to redistribute these changes. + */ + +#include <machine/asm.h> + +#define UNDEFINED16 \ + word UNKNOWN_HANDLER; word UNKNOWN_HANDLER; \ + word UNKNOWN_HANDLER; word UNKNOWN_HANDLER; \ + word UNKNOWN_HANDLER; word UNKNOWN_HANDLER; \ + word UNKNOWN_HANDLER; word UNKNOWN_HANDLER; \ + word UNKNOWN_HANDLER; word UNKNOWN_HANDLER; \ + word UNKNOWN_HANDLER; word UNKNOWN_HANDLER; \ + word UNKNOWN_HANDLER; word UNKNOWN_HANDLER; \ + word UNKNOWN_HANDLER; word UNKNOWN_HANDLER; + + data + .align 4096 /* VBR points to page aligned list */ +GLOBAL(vector_list) + VECTOR(reset_handler) /* 00 */ + VECTOR(interrupt_handler) /* 01 */ + VECTOR(instruction_access_handler) /* 02 */ + VECTOR(data_exception_handler) /* 03 */ + VECTOR(misaligned_handler) /* 04 */ + VECTOR(unimplemented_handler) /* 05 */ + VECTOR(privilege_handler) /* 06 */ + VECTOR(bounds_handler) /* 07 */ + VECTOR(divide_handler) /* 08 */ + VECTOR(overflow_handler) /* 09 */ + VECTOR(error_handler) /* 0a */ + word UNKNOWN_HANDLER /* 0b */ + word UNKNOWN_HANDLER /* 0c */ + word UNKNOWN_HANDLER /* 0d */ + word UNKNOWN_HANDLER /* 0e */ + word UNKNOWN_HANDLER /* 0f */ + UNDEFINED16 /* 1x */ + UNDEFINED16 /* 2x */ + UNDEFINED16 /* 3x */ + UNDEFINED16 /* 4x */ + UNDEFINED16 /* 5x */ + UNDEFINED16 /* 6x */ + word UNKNOWN_HANDLER /* 70 */ + word UNKNOWN_HANDLER /* 71 */ + VECTOR(fp_precise_handler) /* 72 */ + VECTOR(fp_imprecise_handler) /* 73 */ + VECTOR(unimplemented_handler) /* 74 */ + word UNKNOWN_HANDLER /* 75 */ + VECTOR(unimplemented_handler) /* 76 */ + word UNKNOWN_HANDLER /* 77 */ + VECTOR(unimplemented_handler) /* 78 */ + word UNKNOWN_HANDLER /* 79 */ + VECTOR(unimplemented_handler) /* 7a */ + word UNKNOWN_HANDLER /* 7b */ + VECTOR(unimplemented_handler) /* 7c */ + word UNKNOWN_HANDLER /* 7d */ + VECTOR(unimplemented_handler) /* 7e */ + word UNKNOWN_HANDLER /* 7f */ + VECTOR(syscall_handler) /* 80 */ + VECTOR(syscall_handler) /* 81 */ + VECTOR(break) /* 82 */ + VECTOR(trace) /* 83 */ + VECTOR(entry) /* 84 */ +GLOBAL(vector_list_end) + word END_OF_VECTOR_LIST + diff --git a/sys/arch/luna88k/luna88k/vm_machdep.c b/sys/arch/luna88k/luna88k/vm_machdep.c new file mode 100644 index 00000000000..bcbdf7f61ec --- /dev/null +++ b/sys/arch/luna88k/luna88k/vm_machdep.c @@ -0,0 +1,502 @@ +/* $OpenBSD: vm_machdep.c,v 1.1 2004/04/21 15:24:15 aoyama Exp $ */ + +/* + * Copyright (c) 1998 Steve Murphree, Jr. + * Copyright (c) 1996 Nivas Madhur + * Copyright (c) 1993 Adam Glass + * Copyright (c) 1988 University of Utah. + * Copyright (c) 1982, 1986, 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * the Systems Programming Group of the University of Utah Computer + * Science Department. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: Utah $Hdr: vm_machdep.c 1.21 91/04/06$ + * from: @(#)vm_machdep.c 7.10 (Berkeley) 5/7/91 + * vm_machdep.c,v 1.3 1993/07/07 07:09:32 cgd Exp + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/proc.h> +#include <sys/signalvar.h> +#include <sys/malloc.h> +#include <sys/buf.h> +#include <sys/user.h> +#include <sys/vnode.h> +#include <sys/extent.h> +#include <sys/core.h> +#include <sys/exec.h> +#include <sys/ptrace.h> + +#include <uvm/uvm_extern.h> + +#include <machine/mmu.h> +#include <machine/board.h> +#include <machine/cmmu.h> +#include <machine/cpu.h> +#include <machine/cpu_number.h> +#include <machine/locore.h> +#include <machine/trap.h> + +extern struct extent *iomap_extent; +extern struct vm_map *iomap_map; + +vaddr_t iomap_mapin(paddr_t, psize_t, boolean_t); +void iomap_mapout(vaddr_t, vsize_t); +void *mapiodev(void *, int); +void unmapiodev(void *, int); + +/* + * Finish a fork operation, with process p2 nearly set up. + * Copy and update the kernel stack and pcb, making the child + * ready to run, and marking it so that it can return differently + * than the parent. Returns 1 in the child process, 0 in the parent. + * We currently double-map the user area so that the stack is at the same + * address in each process; in the future we will probably relocate + * the frame pointers on the stack after copying. + */ + +void +cpu_fork(p1, p2, stack, stacksize, func, arg) + struct proc *p1, *p2; + void *stack; + size_t stacksize; + void (*func)(void *); + void *arg; +{ + struct switchframe *p2sf; + struct ksigframe { + void (*func)(void *); + void *proc; + } *ksfp; + extern struct pcb *curpcb; + extern void proc_trampoline(void); + extern void save_u_area(struct proc *, vaddr_t); + + /* Copy pcb from p1 to p2. */ + if (p1 == curproc) { + /* Sync the PCB before we copy it. */ + savectx(curpcb); + } +#ifdef DIAGNOSTIC + else if (p1 != &proc0) + panic("cpu_fork: curproc"); +#endif + + bcopy(&p1->p_addr->u_pcb, &p2->p_addr->u_pcb, sizeof(struct pcb)); + p2->p_addr->u_pcb.kernel_state.pcb_ipl = IPL_NONE; /* XXX */ + p2->p_md.md_tf = (struct trapframe *)USER_REGS(p2); + + /*XXX these may not be necessary nivas */ + save_u_area(p2, (vaddr_t)p2->p_addr); + + /* + * Create a switch frame for proc 2 + */ + p2sf = (struct switchframe *)((char *)p2->p_addr + USPACE - 8) - 1; + + p2sf->sf_pc = (u_int)proc_do_uret; + p2sf->sf_proc = p2; + p2->p_addr->u_pcb.kernel_state.pcb_sp = (u_int)p2sf; + + /* + * If specified, give the child a different stack. + */ + if (stack != NULL) + USER_REGS(p2)->r[31] = (u_int)stack + stacksize; + + ksfp = (struct ksigframe *)p2->p_addr->u_pcb.kernel_state.pcb_sp - 1; + + ksfp->func = func; + ksfp->proc = arg; + + /* + * When this process resumes, r31 will be ksfp and + * the process will be at the beginning of proc_trampoline(). + * proc_trampoline will execute the function func, pop off + * ksfp frame, and call the function in the switchframe + * now exposed. + */ + + p2->p_addr->u_pcb.kernel_state.pcb_sp = (u_int)ksfp; + p2->p_addr->u_pcb.kernel_state.pcb_pc = (u_int)proc_trampoline; +} + +/* + * cpu_exit is called as the last action during exit. + * We release the address space and machine-dependent resources, + * including the memory for the user structure and kernel stack. + * Once finished, we call switch_exit, which switches to a temporary + * pcb and stack and never returns. We block memory allocation + * until switch_exit has made things safe again. + */ +void +cpu_exit(struct proc *p) +{ + pmap_deactivate(p); + + splhigh(); + + uvmexp.swtch++; + switch_exit(p); + /* NOTREACHED */ +} + +/* + * Dump the machine specific header information at the start of a core dump. + */ +int +cpu_coredump(p, vp, cred, chdr) + struct proc *p; + struct vnode *vp; + struct ucred *cred; + struct core *chdr; +{ + struct reg reg; + struct coreseg cseg; + int error; + + CORE_SETMAGIC(*chdr, COREMAGIC, MID_MACHINE, 0); + chdr->c_hdrsize = ALIGN(sizeof(*chdr)); + chdr->c_seghdrsize = ALIGN(sizeof(cseg)); + chdr->c_cpusize = sizeof(reg); + + /* Save registers. */ + error = process_read_regs(p, ®); + if (error) + return error; + + CORE_SETMAGIC(cseg, CORESEGMAGIC, MID_MACHINE, CORE_CPU); + cseg.c_addr = 0; + cseg.c_size = chdr->c_cpusize; + + error = vn_rdwr(UIO_WRITE, vp, (caddr_t)&cseg, chdr->c_seghdrsize, + (off_t)chdr->c_hdrsize, UIO_SYSSPACE, IO_NODELOCKED|IO_UNIT, cred, + NULL, p); + if (error) + return error; + + error = vn_rdwr(UIO_WRITE, vp, (caddr_t)®, sizeof(reg), + (off_t)(chdr->c_hdrsize + chdr->c_seghdrsize), UIO_SYSSPACE, + IO_NODELOCKED|IO_UNIT, cred, NULL, p); + if (error) + return error; + + chdr->c_nseg++; + return 0; +} + +/* + * Finish a swapin operation. + * We neded to update the cached PTEs for the user area in the + * machine dependent part of the proc structure. + */ + +void +cpu_swapin(struct proc *p) +{ + extern void save_u_area(struct proc *, vaddr_t); + + save_u_area(p, (vaddr_t)p->p_addr); +} + +/* + * Map an IO request into kernel virtual address space. Requests fall into + * one of five catagories: + * + * B_PHYS|B_UAREA: User u-area swap. + * Address is relative to start of u-area (p_addr). + * B_PHYS|B_PAGET: User page table swap. + * Address is a kernel VA in usrpt (Usrptmap). + * B_PHYS|B_DIRTY: Dirty page push. + * Address is a VA in proc2's address space. + * B_PHYS|B_PGIN: Kernel pagein of user pages. + * Address is VA in user's address space. + * B_PHYS: User "raw" IO request. + * Address is VA in user's address space. + * + * All requests are (re)mapped into kernel VA space via phys_map + * + * XXX we allocate KVA space by using kmem_alloc_wait which we know + * allocates space without backing physical memory. This implementation + * is a total crock, the multiple mappings of these physical pages should + * be reflected in the higher-level VM structures to avoid problems. + */ +void +vmapbuf(bp, len) + struct buf *bp; + vsize_t len; +{ + caddr_t addr; + vaddr_t kva, off; + paddr_t pa; + struct pmap *pmap; + +#ifdef DIAGNOSTIC + if ((bp->b_flags & B_PHYS) == 0) + panic("vmapbuf"); +#endif + + addr = (caddr_t)trunc_page((vaddr_t)(bp->b_saveaddr = bp->b_data)); + off = (vaddr_t)bp->b_saveaddr & PGOFSET; + len = round_page(off + len); + pmap = vm_map_pmap(&bp->b_proc->p_vmspace->vm_map); + + /* + * You may ask: Why phys_map? kernel_map should be OK - after all, + * we are mapping user va to kernel va or remapping some + * kernel va to another kernel va. The answer is TLB flushing + * when the address gets a new mapping. + */ + + kva = uvm_km_valloc_wait(phys_map, len); + + /* + * Flush the TLB for the range [kva, kva + off]. Strictly speaking, + * we should do this in vunmapbuf(), but we do it lazily here, when + * new pages get mapped in. + */ + + cmmu_flush_tlb(cpu_number(), 1, kva, len); + + bp->b_data = (caddr_t)(kva + off); + while (len > 0) { + if (pmap_extract(pmap, (vaddr_t)addr, &pa) == FALSE) + panic("vmapbuf: null page frame"); + pmap_enter(vm_map_pmap(phys_map), kva, pa, + VM_PROT_READ | VM_PROT_WRITE, + VM_PROT_READ | VM_PROT_WRITE | PMAP_WIRED); + /* make sure snooping will be possible... */ + pmap_cache_ctrl(pmap_kernel(), kva, kva + PAGE_SIZE, + CACHE_GLOBAL); + addr += PAGE_SIZE; + kva += PAGE_SIZE; + len -= PAGE_SIZE; + } + pmap_update(pmap_kernel()); +} + +/* + * Free the io map PTEs associated with this IO operation. + * We also restore the original b_addr. + */ +void +vunmapbuf(bp, len) + struct buf *bp; + vsize_t len; +{ + vaddr_t addr, off; + +#ifdef DIAGNOSTIC + if ((bp->b_flags & B_PHYS) == 0) + panic("vunmapbuf"); +#endif + + addr = trunc_page((vaddr_t)bp->b_data); + off = (vaddr_t)bp->b_data & PGOFSET; + len = round_page(off + len); + uvm_km_free_wakeup(phys_map, addr, len); + bp->b_data = bp->b_saveaddr; + bp->b_saveaddr = 0; +} + + +/* + * Map a range [pa, pa+len] in the given map to a kernel address + * in iomap space. + * + * Note: To be flexible, I did not put a restriction on the alignment + * of pa. However, it is advisable to have pa page aligned since otherwise, + * we might have several mappings for a given chunk of the IO page. + */ +vaddr_t +iomap_mapin(paddr_t pa, psize_t len, boolean_t canwait) +{ + vaddr_t iova, tva, off; + paddr_t ppa; + int s, error; + + if (len == 0) + return NULL; + + ppa = pa; + off = (u_long)ppa & PGOFSET; + + len = round_page(off + len); + + s = splhigh(); + error = extent_alloc(iomap_extent, len, PAGE_SIZE, 0, EX_NOBOUNDARY, + canwait ? EX_WAITSPACE : EX_NOWAIT, &iova); + splx(s); + + if (error != 0) + return NULL; + + cmmu_flush_tlb(cpu_number(), 1, iova, len); /* necessary? */ + + ppa = trunc_page(ppa); + +#ifndef NEW_MAPPING + tva = iova; +#else + tva = ppa; +#endif + + while (len>0) { + pmap_enter(vm_map_pmap(iomap_map), tva, ppa, + VM_PROT_WRITE | VM_PROT_READ, + VM_PROT_WRITE | VM_PROT_READ | PMAP_WIRED); + len -= PAGE_SIZE; + tva += PAGE_SIZE; + ppa += PAGE_SIZE; + } + pmap_update(pmap_kernel()); +#ifndef NEW_MAPPING + return (iova + off); +#else + return (pa + off); +#endif + +} + +/* + * Free up the mapping in iomap. + */ +void +iomap_mapout(vaddr_t kva, vsize_t len) +{ + vaddr_t off; + int s, error; + + off = kva & PGOFSET; + kva = trunc_page(kva); + len = round_page(off + len); + + pmap_remove(vm_map_pmap(iomap_map), kva, kva + len); + pmap_update(vm_map_pmap(iomap_map)); + + s = splhigh(); + error = extent_free(iomap_extent, kva, len, EX_NOWAIT); + splx(s); + + if (error != 0) + printf("iomap_mapout: extent_free failed\n"); +} + +/* + * Allocate/deallocate a cache-inhibited range of kernel virtual address + * space mapping the indicated physical address range [pa - pa+size) + */ +void * +mapiodev(pa, size) + void *pa; + int size; +{ + paddr_t ppa; + ppa = (paddr_t)pa; + return ((void *)iomap_mapin(ppa, size, 0)); +} + +void +unmapiodev(kva, size) + void *kva; + int size; +{ + vaddr_t va; + va = (vaddr_t)kva; + iomap_mapout(va, size); +} + +int +badvaddr(vaddr_t va, int size) +{ + volatile int x; + + if (badaddr(va, size)) { + return -1; + } + + switch (size) { + case 1: + x = *(unsigned char *volatile)va; + break; + case 2: + x = *(unsigned short *volatile)va; + break; + case 4: + x = *(unsigned long *volatile)va; + break; + default: + return -1; + } + return (0); +} + +/* + * Move pages from one kernel virtual address to another. + */ +void +pagemove(from, to, size) + caddr_t from, to; + size_t size; +{ + paddr_t pa; + boolean_t rv; + +#ifdef DEBUG + if ((size & PAGE_MASK) != 0) + panic("pagemove"); +#endif + while (size > 0) { + rv = pmap_extract(pmap_kernel(), (vaddr_t)from, &pa); +#ifdef DEBUG + if (rv == FALSE) + panic("pagemove 2"); + if (pmap_extract(pmap_kernel(), (vaddr_t)to, NULL) == TRUE) + panic("pagemove 3"); +#endif + pmap_kremove((vaddr_t)from, PAGE_SIZE); + pmap_kenter_pa((vaddr_t)to, pa, VM_PROT_READ|VM_PROT_WRITE); + from += PAGE_SIZE; + to += PAGE_SIZE; + size -= PAGE_SIZE; + } + pmap_update(pmap_kernel()); +} + +u_int +kvtop(va) + vaddr_t va; +{ + paddr_t pa; + + pmap_extract(pmap_kernel(), va, &pa); + /* XXX check for failure */ + return ((u_int)pa); +} |