瀏覽代碼

Plan 9 from Bell Labs 2003-07-30

David du Colombier 21 年之前
父節點
當前提交
37ad0dea75

+ 46 - 4
dist/replica/plan9.db

@@ -3024,6 +3024,7 @@ rc/bin/service/tcp7 - 775 sys sys 945617224 19
 rc/bin/service/tcp9 - 775 sys sys 945617224 26
 rc/bin/service/telcodata - 775 sys sys 945617224 95
 rc/bin/service/telcofax - 775 sys sys 945617224 93
+rc/bin/sig - 775 sys sys 1059490402 491
 rc/bin/slay - 775 sys sys 1018387014 123
 rc/bin/spell - 775 sys sys 964540791 312
 rc/bin/src - 775 sys sys 1015089596 869
@@ -3039,7 +3040,7 @@ rc/bin/troff2gif - 775 sys sys 1045504085 178
 rc/bin/ups - 775 sys sys 1051028686 889
 rc/bin/usbstart - 775 sys sys 1044894155 81
 rc/bin/usps - 775 sys sys 1016826030 450
-rc/bin/vwhois - 775 sys sys 953999875 294
+rc/bin/vwhois - 775 sys sys 1059484222 297
 rc/bin/wdoc2txt - 755 sys sys 1017431153 277
 rc/bin/weather - 775 sys sys 1016825765 795
 rc/bin/who - 775 sys sys 945617210 61
@@ -4243,6 +4244,8 @@ sys/lib/sysconfig/auth/files/rewrite - 664 sys sys 1016833537 428
 sys/lib/sysconfig/auth/files/tcp566 - 775 sys sys 1016833537 36
 sys/lib/sysconfig/auth/files/tcp567 - 775 sys sys 1016833537 34
 sys/lib/sysconfig/auth/mkfile - 664 sys sys 1016833657 2937
+sys/lib/sysconfig/blast - 20000000775 sys sys 1059490895 0
+sys/lib/sysconfig/blast/boot - 775 sys sys 1059490900 571
 sys/lib/sysconfig/fl - 20000000775 sys sys 1042004836 0
 sys/lib/sysconfig/fl/boot - 775 sys sys 1048637071 773
 sys/lib/sysconfig/fl/flproto - 664 sys sys 1048637070 137
@@ -4451,7 +4454,7 @@ sys/log/httpd/url - 666 sys sys 958933934 0
 sys/log/imap4d - 10000000666 sys sys 958934039 0
 sys/log/ipboot - 10000000666 sys sys 958934040 0
 sys/log/listen - 10000000666 sys sys 958934040 0
-sys/log/mail - 10000000666 sys sys 958934039 0
+sys/log/mail - 10000000666 sys sys 1059490995 114
 sys/log/nfs - 10000000666 sys sys 958934039 0
 sys/log/nfsserver - 10000000666 sys sys 958934040 0
 sys/log/pop3 - 10000000666 sys sys 958934040 0
@@ -4539,7 +4542,7 @@ sys/man/1/look - 664 sys sys 944959673 1348
 sys/man/1/lp - 664 sys sys 1015024739 3304
 sys/man/1/ls - 664 sys sys 1049813576 2791
 sys/man/1/mail - 664 sys sys 1050078464 2158
-sys/man/1/man - 664 sys sys 954378836 1611
+sys/man/1/man - 664 sys sys 1059484724 1740
 sys/man/1/marshal - 664 sys sys 1050078464 3307
 sys/man/1/mc - 664 sys sys 1045501392 508
 sys/man/1/mk - 664 sys sys 964455061 13154
@@ -4652,7 +4655,7 @@ sys/man/2/des - 664 sys sys 1032058673 3517
 sys/man/2/dial - 664 sys sys 1015701029 6523
 sys/man/2/dirread - 664 sys sys 1015091519 1901
 sys/man/2/disk - 664 sys sys 1015091519 3188
-sys/man/2/draw - 664 sys sys 1040457027 19338
+sys/man/2/draw - 664 sys sys 1059484405 19362
 sys/man/2/dsa - 664 sys sys 1027629169 2072
 sys/man/2/dup - 664 sys sys 950593489 827
 sys/man/2/elgamal - 664 sys sys 1027629189 2152
@@ -5355,6 +5358,45 @@ sys/src/9/port/thwack.h - 664 sys sys 1015278340 1792
 sys/src/9/port/tod.c - 664 sys sys 1036813006 3328
 sys/src/9/port/unthwack.c - 664 sys sys 1057323394 5249
 sys/src/9/port/xalloc.c - 664 sys sys 1032052814 4030
+sys/src/9/ppc - 20000000775 sys sys 1059490838 0
+sys/src/9/ppc/blast - 664 sys sys 1059490754 685
+sys/src/9/ppc/blast.c - 664 sys sys 1059490750 1422
+sys/src/9/ppc/blast.h - 664 sys sys 1059490750 3109
+sys/src/9/ppc/clock.c - 664 sys sys 1059490750 1066
+sys/src/9/ppc/dat.h - 664 sys sys 1059490750 4696
+sys/src/9/ppc/devether.c - 664 sys sys 1059490750 9264
+sys/src/9/ppc/devflash.c - 664 sys sys 1059490750 19885
+sys/src/9/ppc/devirq.c - 664 sys sys 1059490750 6086
+sys/src/9/ppc/devtls.c - 664 sys sys 1059490751 43721
+sys/src/9/ppc/errstr.h - 664 sys sys 1059490751 2137
+sys/src/9/ppc/etherfcc.c - 664 sys sys 1059490751 14295
+sys/src/9/ppc/etherif.h - 664 sys sys 1059490751 785
+sys/src/9/ppc/ethersaturn.c - 664 sys sys 1059490751 4321
+sys/src/9/ppc/fns.h - 664 sys sys 1059490751 4081
+sys/src/9/ppc/init9.s - 664 sys sys 1059490827 572
+sys/src/9/ppc/initcode - 664 sys sys 1059490754 444
+sys/src/9/ppc/io.h - 664 sys sys 1059490752 1017
+sys/src/9/ppc/l.s - 664 sys sys 1059490827 16022
+sys/src/9/ppc/lblast.h - 664 sys sys 1059490752 1694
+sys/src/9/ppc/lucu.h - 664 sys sys 1059490752 935
+sys/src/9/ppc/m8260.c - 664 sys sys 1059490752 14264
+sys/src/9/ppc/m8260.h - 664 sys sys 1059490752 20892
+sys/src/9/ppc/main.c - 664 sys sys 1059490752 9203
+sys/src/9/ppc/mcc.c - 664 sys sys 1059490752 9667
+sys/src/9/ppc/mem.h - 664 sys sys 1059490752 7015
+sys/src/9/ppc/mkfile - 664 sys sys 1059490754 1811
+sys/src/9/ppc/mmu.c - 664 sys sys 1059490753 4865
+sys/src/9/ppc/msaturn.c - 664 sys sys 1059490753 3009
+sys/src/9/ppc/msaturn.h - 664 sys sys 1059490753 99
+sys/src/9/ppc/mtx.c - 664 sys sys 1059490753 177
+sys/src/9/ppc/random.c - 664 sys sys 1059490753 1983
+sys/src/9/ppc/rcmain - 664 sys sys 1059490838 579
+sys/src/9/ppc/saturntimer.c - 664 sys sys 1059490753 1734
+sys/src/9/ppc/trap.c - 664 sys sys 1059490753 17248
+sys/src/9/ppc/uartsaturn.c - 664 sys sys 1059490754 7151
+sys/src/9/ppc/uartsmc.c - 664 sys sys 1059490754 11713
+sys/src/9/ppc/ucu - 664 sys sys 1059490754 715
+sys/src/9/ppc/ucu.h - 664 sys sys 1059490754 531
 sys/src/NOTICE - 444 sys sys 1018803112 63
 sys/src/ape - 20000000775 sys sys 1014921996 0
 sys/src/ape/9src - 20000000775 sys sys 1016944136 0

+ 125 - 0
dist/replica/plan9.log

@@ -12876,3 +12876,128 @@
 1059181299 7 a acme/bin/dial - 20000000775 sys sys 1059180057 0
 1059181299 8 a rc/bin/dial - 20000000775 sys sys 1059180057 0
 1059312772 0 c 386/bin/aux/mouse - 775 sys sys 1059312460 44513
+1059485463 0 a rc/bin/sig - 775 sys sys 1059484565 640
+1059485463 1 c rc/bin/vwhois - 775 sys sys 1059484222 297
+1059485463 2 c sys/man/1/man - 664 sys sys 1059484724 1740
+1059485463 3 c sys/man/2/draw - 664 sys sys 1059484405 19362
+1059490866 0 c rc/bin/sig - 775 sys sys 1059490402 491
+1059490866 1 a sys/lib/sysconfig/blast - 20000000775 sys sys 1059490895 0
+1059490866 2 a sys/src/9/power - 20000000775 sys sys 1059490838 0
+1059490866 3 a sys/src/9/power/blast - 664 sys sys 1059490754 685
+1059490866 4 a sys/src/9/power/blast.c - 664 sys sys 1059490750 1422
+1059490866 5 a sys/src/9/power/blast.h - 664 sys sys 1059490750 3109
+1059490866 6 a sys/src/9/power/clock.c - 664 sys sys 1059490750 1066
+1059490866 7 a sys/src/9/power/dat.h - 664 sys sys 1059490750 4696
+1059490866 8 a sys/src/9/power/devether.c - 664 sys sys 1059490750 9264
+1059490866 9 a sys/src/9/power/devflash.c - 664 sys sys 1059490750 19885
+1059490866 10 a sys/src/9/power/devirq.c - 664 sys sys 1059490750 6086
+1059490866 11 a sys/src/9/power/devtls.c - 664 sys sys 1059490751 43721
+1059490866 12 a sys/src/9/power/errstr.h - 664 sys sys 1059490751 2137
+1059490866 13 a sys/src/9/power/etherfcc.c - 664 sys sys 1059490751 14295
+1059490866 14 a sys/src/9/power/etherif.h - 664 sys sys 1059490751 785
+1059490866 15 a sys/src/9/power/ethersaturn.c - 664 sys sys 1059490751 4321
+1059490866 16 a sys/src/9/power/fns.h - 664 sys sys 1059490751 4081
+1059490866 17 a sys/src/9/power/init9.s - 664 sys sys 1059490827 572
+1059490866 18 a sys/src/9/power/initcode - 664 sys sys 1059490754 444
+1059490866 19 a sys/src/9/power/io.h - 664 sys sys 1059490752 1017
+1059490866 20 a sys/src/9/power/l.s - 664 sys sys 1059490827 16022
+1059490866 21 a sys/src/9/power/lblast.h - 664 sys sys 1059490752 1694
+1059490866 22 a sys/src/9/power/lucu.h - 664 sys sys 1059490752 935
+1059490866 23 a sys/src/9/power/m8260.c - 664 sys sys 1059490752 14264
+1059490866 24 a sys/src/9/power/m8260.h - 664 sys sys 1059490752 20892
+1059490866 25 a sys/src/9/power/main.c - 664 sys sys 1059490752 9203
+1059490866 26 a sys/src/9/power/mcc.c - 664 sys sys 1059490752 9667
+1059490866 27 a sys/src/9/power/mem.h - 664 sys sys 1059490752 7015
+1059490866 28 a sys/src/9/power/mkfile - 664 sys sys 1059490754 1811
+1059490866 29 a sys/src/9/power/mmu.c - 664 sys sys 1059490753 4865
+1059490866 30 a sys/src/9/power/msaturn.c - 664 sys sys 1059490753 3009
+1059490866 31 a sys/src/9/power/msaturn.h - 664 sys sys 1059490753 99
+1059490866 32 a sys/src/9/power/mtx.c - 664 sys sys 1059490753 177
+1059490866 33 a sys/src/9/power/random.c - 664 sys sys 1059490753 1983
+1059490866 34 a sys/src/9/power/rcmain - 664 sys sys 1059490838 579
+1059490866 35 a sys/src/9/power/saturntimer.c - 664 sys sys 1059490753 1734
+1059490866 36 a sys/src/9/power/trap.c - 664 sys sys 1059490753 17248
+1059490866 37 a sys/src/9/power/uartsaturn.c - 664 sys sys 1059490754 7151
+1059490866 38 a sys/src/9/power/uartsmc.c - 664 sys sys 1059490754 11713
+1059490866 39 a sys/src/9/power/ucu - 664 sys sys 1059490754 715
+1059490866 40 a sys/src/9/power/ucu.h - 664 sys sys 1059490754 531
+1059492669 0 a sys/lib/sysconfig/blast/boot - 775 sys sys 1059490900 571
+1059492669 1 c sys/log/mail - 10000000666 sys sys 1059490995 114
+1059496271 0 a sys/src/9/ppc - 20000000775 sys sys 1059490838 0
+1059496271 1 a sys/src/9/ppc/blast - 664 sys sys 1059490754 685
+1059496271 2 a sys/src/9/ppc/blast.c - 664 sys sys 1059490750 1422
+1059496271 3 a sys/src/9/ppc/blast.h - 664 sys sys 1059490750 3109
+1059496271 4 a sys/src/9/ppc/clock.c - 664 sys sys 1059490750 1066
+1059496271 5 a sys/src/9/ppc/dat.h - 664 sys sys 1059490750 4696
+1059496271 6 a sys/src/9/ppc/devether.c - 664 sys sys 1059490750 9264
+1059496271 7 a sys/src/9/ppc/devflash.c - 664 sys sys 1059490750 19885
+1059496271 8 a sys/src/9/ppc/devirq.c - 664 sys sys 1059490750 6086
+1059496271 9 a sys/src/9/ppc/devtls.c - 664 sys sys 1059490751 43721
+1059496271 10 a sys/src/9/ppc/errstr.h - 664 sys sys 1059490751 2137
+1059496271 11 a sys/src/9/ppc/etherfcc.c - 664 sys sys 1059490751 14295
+1059496271 12 a sys/src/9/ppc/etherif.h - 664 sys sys 1059490751 785
+1059496271 13 a sys/src/9/ppc/ethersaturn.c - 664 sys sys 1059490751 4321
+1059496271 14 a sys/src/9/ppc/fns.h - 664 sys sys 1059490751 4081
+1059496271 15 a sys/src/9/ppc/init9.s - 664 sys sys 1059490827 572
+1059496271 16 a sys/src/9/ppc/initcode - 664 sys sys 1059490754 444
+1059496271 17 a sys/src/9/ppc/io.h - 664 sys sys 1059490752 1017
+1059496271 18 a sys/src/9/ppc/l.s - 664 sys sys 1059490827 16022
+1059496271 19 a sys/src/9/ppc/lblast.h - 664 sys sys 1059490752 1694
+1059496271 20 a sys/src/9/ppc/lucu.h - 664 sys sys 1059490752 935
+1059496271 21 a sys/src/9/ppc/m8260.c - 664 sys sys 1059490752 14264
+1059496271 22 a sys/src/9/ppc/m8260.h - 664 sys sys 1059490752 20892
+1059496271 23 a sys/src/9/ppc/main.c - 664 sys sys 1059490752 9203
+1059496271 24 a sys/src/9/ppc/mcc.c - 664 sys sys 1059490752 9667
+1059496271 25 a sys/src/9/ppc/mem.h - 664 sys sys 1059490752 7015
+1059496271 26 a sys/src/9/ppc/mkfile - 664 sys sys 1059490754 1811
+1059496271 27 a sys/src/9/ppc/mmu.c - 664 sys sys 1059490753 4865
+1059496271 28 a sys/src/9/ppc/msaturn.c - 664 sys sys 1059490753 3009
+1059496271 29 a sys/src/9/ppc/msaturn.h - 664 sys sys 1059490753 99
+1059496271 30 a sys/src/9/ppc/mtx.c - 664 sys sys 1059490753 177
+1059496271 31 a sys/src/9/ppc/random.c - 664 sys sys 1059490753 1983
+1059496271 32 a sys/src/9/ppc/rcmain - 664 sys sys 1059490838 579
+1059496271 33 a sys/src/9/ppc/saturntimer.c - 664 sys sys 1059490753 1734
+1059496271 34 a sys/src/9/ppc/trap.c - 664 sys sys 1059490753 17248
+1059496271 35 a sys/src/9/ppc/uartsaturn.c - 664 sys sys 1059490754 7151
+1059496271 36 a sys/src/9/ppc/uartsmc.c - 664 sys sys 1059490754 11713
+1059496271 37 a sys/src/9/ppc/ucu - 664 sys sys 1059490754 715
+1059496271 38 a sys/src/9/ppc/ucu.h - 664 sys sys 1059490754 531
+1059496271 39 d sys/src/9/power - 20000000775 sys sys 1059490838 0
+1059496271 40 d sys/src/9/power/blast - 664 sys sys 1059490754 0
+1059496271 41 d sys/src/9/power/blast.c - 664 sys sys 1059490750 0
+1059496271 42 d sys/src/9/power/blast.h - 664 sys sys 1059490750 0
+1059496271 43 d sys/src/9/power/clock.c - 664 sys sys 1059490750 0
+1059496271 44 d sys/src/9/power/dat.h - 664 sys sys 1059490750 0
+1059496271 45 d sys/src/9/power/devether.c - 664 sys sys 1059490750 0
+1059496271 46 d sys/src/9/power/devflash.c - 664 sys sys 1059490750 0
+1059496271 47 d sys/src/9/power/devirq.c - 664 sys sys 1059490750 0
+1059496271 48 d sys/src/9/power/devtls.c - 664 sys sys 1059490751 0
+1059496271 49 d sys/src/9/power/errstr.h - 664 sys sys 1059490751 0
+1059496271 50 d sys/src/9/power/etherfcc.c - 664 sys sys 1059490751 0
+1059496271 51 d sys/src/9/power/etherif.h - 664 sys sys 1059490751 0
+1059496271 52 d sys/src/9/power/ethersaturn.c - 664 sys sys 1059490751 0
+1059496271 53 d sys/src/9/power/fns.h - 664 sys sys 1059490751 0
+1059496271 54 d sys/src/9/power/init9.s - 664 sys sys 1059490827 0
+1059496271 55 d sys/src/9/power/initcode - 664 sys sys 1059490754 0
+1059496271 56 d sys/src/9/power/io.h - 664 sys sys 1059490752 0
+1059496271 57 d sys/src/9/power/l.s - 664 sys sys 1059490827 0
+1059496271 58 d sys/src/9/power/lblast.h - 664 sys sys 1059490752 0
+1059496271 59 d sys/src/9/power/lucu.h - 664 sys sys 1059490752 0
+1059496271 60 d sys/src/9/power/m8260.c - 664 sys sys 1059490752 0
+1059496271 61 d sys/src/9/power/m8260.h - 664 sys sys 1059490752 0
+1059496271 62 d sys/src/9/power/main.c - 664 sys sys 1059490752 0
+1059496271 63 d sys/src/9/power/mcc.c - 664 sys sys 1059490752 0
+1059496271 64 d sys/src/9/power/mem.h - 664 sys sys 1059490752 0
+1059496271 65 d sys/src/9/power/mkfile - 664 sys sys 1059490754 0
+1059496271 66 d sys/src/9/power/mmu.c - 664 sys sys 1059490753 0
+1059496271 67 d sys/src/9/power/msaturn.c - 664 sys sys 1059490753 0
+1059496271 68 d sys/src/9/power/msaturn.h - 664 sys sys 1059490753 0
+1059496271 69 d sys/src/9/power/mtx.c - 664 sys sys 1059490753 0
+1059496271 70 d sys/src/9/power/random.c - 664 sys sys 1059490753 0
+1059496271 71 d sys/src/9/power/rcmain - 664 sys sys 1059490838 0
+1059496271 72 d sys/src/9/power/saturntimer.c - 664 sys sys 1059490753 0
+1059496271 73 d sys/src/9/power/trap.c - 664 sys sys 1059490753 0
+1059496271 74 d sys/src/9/power/uartsaturn.c - 664 sys sys 1059490754 0
+1059496271 75 d sys/src/9/power/uartsmc.c - 664 sys sys 1059490754 0
+1059496271 76 d sys/src/9/power/ucu - 664 sys sys 1059490754 0
+1059496271 77 d sys/src/9/power/ucu.h - 664 sys sys 1059490754 0

+ 22 - 0
rc/bin/sig

@@ -0,0 +1,22 @@
+#!/bin/rc
+# Usage: sig key ...
+#	prints out function signatures by grepping the manual
+
+
+*=`{echo $*|tr A-Z a-z|tr -dc 'a-z0-9_ \012'}	# fold case, delete funny chars
+if(~ $#* 0){
+	echo Usage: sig function ... >/fd/2
+	exit 1
+}
+
+for (i) {
+	files=`{grep -l '[ 	]\*?'$i'\(' /sys/man/2/*}
+	for(j in $files) {
+		{echo .nr LL 20i; sed -n '/^.SH SYNOPSIS/,/^.SH.*DESCR/p'  $j } |
+			sed 's/[ 	]+/ /g;s/^ +//;/^\.nf/d' |
+			nroff -man |
+			grep -i -e '[ 	]\*?'$i'\(' | sed 's/^[ +]/	/'
+	}
+}
+
+exit 0

+ 1 - 1
rc/bin/vwhois

@@ -2,7 +2,7 @@
 
 if(test -f /mnt/plumb/seemail || test -f /mnt/term/mnt/plumb/seemail){
 	for(i)
-		plumb -dseemail -a 'filetype=vwhois digest=poot mailtype=new sender='^$i /mail/fs/mbox/XXX
+		plumb -dseemail -a 'filetype=vwhois digest=$i.$pid mailtype=new sender='^$i /mail/fs/mbox/XXX
 }
 if not for (i){
 	echo vwhois: vwhois: vwhois: delivered `{cat /dev/user} From $i '(vwho)' >> /sys/log/mail

+ 32 - 0
sys/lib/sysconfig/blast/boot

@@ -0,0 +1,32 @@
+#!/boot/rc -m /boot/rcmain
+
+cpuserver=yes
+cd /boot
+echo boot...
+bind -a '#I' /net
+bind -a '#l0' /net
+bind -a '#S' /dev
+bind '#p' /proc
+bind '#d' /fd
+bind -a /boot /
+
+ipconfig -D
+
+authaddr=`{sed -n 's/	auth=(.*)/\1/p' /net/ndb|sed 1q}
+fsaddr=`{sed -n 's/	fs=(.*)/\1/p' /net/ndb|sed 1q}
+
+factotum -sfactotum -S -a $authaddr
+
+if(! srv il!$fsaddr!17008 boot){
+	if(! srv tcp!$fsaddr!564 boot)
+		exec ./rc -m/boot/rcmain -i
+}
+
+if(! mount -c /srv/boot /root)
+	exec ./rc -m/boot/rcmain -i
+
+bind -ac /root /
+rootdir=/root
+rootspec=''
+/$cputype/init -c
+exec ./rc -m/boot/rcmain -i

+ 1 - 0
sys/log/mail

@@ -0,0 +1 @@
+olive Jul 29 11:03:15 remote cse.psu.edu!9fans From presotto Tue Jul 29 11:03:14 EDT 2003 (9fans@cse.psu.edu) 331

+ 8 - 0
sys/man/1/man

@@ -13,6 +13,9 @@ man, lookman \- print or find pages of this manual
 .PP
 .B lookman
 .I key ...
+.PP
+.B sig
+.I function ...
 .SH DESCRIPTION
 .I Man
 locates and prints pages of this manual named
@@ -63,6 +66,11 @@ prints the names of all manual sections that contain
 all of the
 .I key
 words given on the command line.
+.PP
+.B Sig
+prints the signature (i.e. C definition) of the
+.IR function 's
+given on the command line.
 .SH FILES
 .TF /sys/lib/man/lookman/index
 .TP

+ 5 - 0
sys/man/2/draw

@@ -11,6 +11,11 @@ runestringnbg, _string, ARROW, drawsetdebug \- graphics functions
 .nf
 ..
 .SH SYNOPSIS
+.de PB
+.PP
+.ft L
+.nf
+..
 .PB
 #include <u.h>
 #include <libc.h>

+ 62 - 0
sys/src/9/ppc/blast

@@ -0,0 +1,62 @@
+dev
+	root
+	cons
+	env
+	flash
+	pipe
+	proc
+	mnt
+	srv
+	dup
+	ssl
+	cap
+	kprof
+	uart
+	irq
+	realtime	realtimesub edf
+
+	ether		netif
+	ip		arp chandial ip ipv6 ipaux iproute netif netlog nullmedium pktmedium ptclbsum inferno
+
+link
+	etherfcc
+	ethermedium
+	netdevmedium
+
+misc
+	uartsmc
+	m8260
+
+ip
+	il
+	tcp
+	udp
+	ipifc
+	icmp
+	icmp6
+
+port
+	int cpuserver = 1;
+
+boot cpu
+	tcp
+	il
+
+bootdir
+	/power/bin/rc
+	/rc/lib/rcmain
+	/power/bin/bind
+	/power/bin/sed
+	/power/bin/srv
+	/power/bin/cat
+	/power/bin/cp
+	/power/bin/rm
+	/power/bin/echo
+	/power/bin/mount
+	/power/bin/sleep
+	/power/bin/auth/factotum
+	/power/bin/ip/ipconfig
+	/power/bin/ls
+	/power/bin/ps
+	/power/bin/auth/wrkey
+	/sys/lib/sysconfig/blast/boot

+ 83 - 0
sys/src/9/ppc/blast.c

@@ -0,0 +1,83 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+extern Dev rootdevtab;
+extern Dev consdevtab;
+extern Dev envdevtab;
+extern Dev flashdevtab;
+extern Dev pipedevtab;
+extern Dev procdevtab;
+extern Dev mntdevtab;
+extern Dev srvdevtab;
+extern Dev dupdevtab;
+extern Dev ssldevtab;
+extern Dev capdevtab;
+extern Dev kprofdevtab;
+extern Dev uartdevtab;
+extern Dev irqdevtab;
+extern Dev realtimedevtab;
+extern Dev etherdevtab;
+extern Dev ipdevtab;
+Dev* devtab[]={
+	&rootdevtab,
+	&consdevtab,
+	&envdevtab,
+	&flashdevtab,
+	&pipedevtab,
+	&procdevtab,
+	&mntdevtab,
+	&srvdevtab,
+	&dupdevtab,
+	&ssldevtab,
+	&capdevtab,
+	&kprofdevtab,
+	&uartdevtab,
+	&irqdevtab,
+	&realtimedevtab,
+	&etherdevtab,
+	&ipdevtab,
+	nil,
+};
+
+extern void etherfcclink(void);
+extern void ethermediumlink(void);
+extern void netdevmediumlink(void);
+void links(void){
+	bootlinks();
+
+	etherfcclink();
+	ethermediumlink();
+	netdevmediumlink();
+}
+
+extern PhysUart smcphysuart;
+PhysUart* physuart[] = {
+	&smcphysuart,
+	nil,
+};
+
+#include "../ip/ip.h"
+extern void ilinit(Fs*);
+extern void tcpinit(Fs*);
+extern void udpinit(Fs*);
+extern void ipifcinit(Fs*);
+extern void icmpinit(Fs*);
+extern void icmp6init(Fs*);
+void (*ipprotoinit[])(Fs*) = {
+	ilinit,
+	tcpinit,
+	udpinit,
+	ipifcinit,
+	icmpinit,
+	icmp6init,
+	nil,
+};
+
+	int cpuserver = 1;
+char* conffile = "blast";
+ulong kerndate = KERNDATE;

+ 99 - 0
sys/src/9/ppc/blast.h

@@ -0,0 +1,99 @@
+/*
+ * Here, we define everything that is specific for the blast board from Crawford Hill
+ */
+
+
+/* Clock speed of the blast board */
+#define	CLKIN	72000000
+
+/*
+ * Blast memory layout:
+ *	CS0: FE000000 -> FFFFFFFF (Flash)
+ *	CS1: FC000000 -> FCFFFFFF (DSP hpi)
+ *	CS2: 00000000 -> 03FFFFFF (60x sdram)
+ *	CS3: 04000000 -> 04FFFFFF (FPGA)
+ *	CS4: 05000000 -> 06FFFFFF (local bus sdram)
+ *	CS5: 07000000 -> 070FFFFF (eeprom - not populated)
+ *	CS6: E0000000 -> E0FFFFFF (FPGA)
+ *
+ * Main Board memory lay out:
+ *	CS0: FE000000 -> FEFFFFFF (16 M FLASH)
+ *	CS1: FC000000 -> FCFFFFFF (16 M DSP1)
+ *	CS2: 00000000 -> 03FFFFFF (64 M SDRAM)
+ *	CS3: 04000000 -> 04FFFFFF (16M DSP2)
+ *	CS4: 05000000 -> 06FFFFFF (32 M Local SDRAM)
+ *	CS5: 07000000 -> 070FFFFF (eeprom - not populated)
+ *	CS6: E0000000 -> E0FFFFFF (16 M FPGA)
+ *
+ *	CS2, CS3, CS4, (and CS5) are covered by DBAT 0,  CS0 and CS1 by DBAT 3, CS6 by DBAT 2
+ */
+#define	FLASHMEM	0xfe000000
+#define	FLASHSIZE	0x01000000
+#define	DSP1BASE		0xfc000000
+#define	DSP1SIZE		0x01000000
+#define	MEM1BASE	0x00000000
+#define	MEM1SIZE	0x04000000
+#define	DSP2BASE		0x04000000
+#define	DSP2SIZE		0x01000000
+#define	MEM2BASE	0x05000000
+/* #define	MEM2SIZE	0x02000000 */
+#define	MEM2SIZE	0
+#define	FPGABASE		0xe0000000
+#define	FPGASIZE		0x01000000
+
+#define	PLAN9INI		0x00460000
+
+#define	TLBENTRIES	32
+/*
+ *  PTE bits for fault.c.  These belong to the second PTE word.  Validity is
+ *  implied for putmmu(), and we always set PTE0_V.  PTEVALID is used
+ *  here to set cache policy bits on a global basis.
+ */
+#define	PTEVALID		PTE1_M
+#define	PTEWRITE		(PTE1_RW|PTE1_C)
+#define	PTERONLY	PTE1_RO
+#define	PTEUNCACHED	PTE1_I
+
+/* SMC Uart configuration */
+#define	SMC1PORT	3	/* Port D */
+#define	SMTXD1		BIT(9)
+#define	SMRXD1		BIT(8)
+
+/* Ethernet FCC configuration */
+#define	A1txer	 	0x00000004
+#define	A1rxdv	 	0x00000010
+#define	A1txen		 0x00000008
+#define	A1rxer		 0x00000020
+#define	A1col		 0x00000001
+#define	A1crs		 0x00000002
+#define	A1txdat		 0x00003c00
+#define	A1rxdat		 0x0003c000
+#define	B2txer		 0x00000001
+#define	B2rxdv		 0x00000002
+#define	B2txen		 0x00000004
+#define	B2rxer		 0x00000008
+#define	B2col		 0x00000010
+#define	B2crs		 0x00000020
+#define	B2txdat		 0x000003c0
+#define	B2rxdat		 0x00003c00
+#define	B3rxdv		 0x00004000
+#define	B3rxer		 0x00008000
+#define	B3txer		 0x00010000
+#define	B3txen		 0x00020000
+#define	B3col		 0x00040000
+#define	B3crs		 0x00080000
+#define	B3txdat		 0x0f000000
+#define	B3rxdat		 0x00f00000
+
+#define	A1psor0		 (A1rxdat | A1txdat)
+#define	A1psor1		 (A1col | A1crs | A1txer | A1txen | A1rxdv | A1rxer)
+#define	A1dir0		 (A1rxdat | A1crs | A1col | A1rxer | A1rxdv)
+#define	A1dir1		 (A1txdat | A1txen | A1txer)
+#define	B2psor0		 (B2rxdat | B2txdat | B2crs | B2col | B2rxer | B2rxdv | B2txer)
+#define	B2psor1		 (B2txen)
+#define	B2dir0		 (B2rxdat | B2crs | B2col | B2rxer | B2rxdv)
+#define	B2dir1		 (B2txdat | B2txen | B2txer)
+#define	B3psor0		 (B3rxdat | B3txdat | B3crs | B3col | B3rxer | B3rxdv | B3txer | B3txen)
+#define	B3psor1		 0
+#define	B3dir0		 (B3rxdat | B3crs | B3col | B3rxer | B3rxdv)
+#define	B3dir1		 (B3txdat | B3txen | B3txer)

+ 85 - 0
sys/src/9/ppc/clock.c

@@ -0,0 +1,85 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"io.h"
+#include	"fns.h"
+#include	"ureg.h"
+
+static	ulong	clkreload;
+
+void
+delayloopinit(void)
+{
+	ulong v;
+	uvlong x;
+
+	/* initial value for loopconst set in machinit */
+	v = getdec();
+	delay(1000);
+	v -= getdec();
+
+	x = m->loopconst;
+	x *= m->dechz;
+	x /= v;
+	m->loopconst = x;
+}
+
+void
+clockinit(void)
+{
+	m->dechz = m->bushz/4;	/* true for all 604e */
+	m->tbhz = m->dechz;		/* conjecture; manual doesn't say */
+
+	delayloopinit();
+
+	clkreload = m->dechz/HZ-1;
+	putdec(clkreload);
+}
+
+void
+clockintr(Ureg *)
+{
+	long v;
+
+	v = -getdec();
+	if(v > clkreload/2){
+		if(v > clkreload)
+			m->ticks += v/clkreload;
+		v = 0;
+	}
+	putdec(clkreload-v);
+
+//	timerintr(ureg, 0);		We now use the 8260 fast clock
+}
+
+void
+delay(int l)
+{
+	ulong i, j;
+
+	j = m->loopconst;
+	while(l-- > 0)
+		for(i=0; i < j; i++)
+			;
+}
+
+void
+microdelay(int l)
+{
+	ulong i;
+
+	l *= m->loopconst;
+	l += 500;
+	l /= 1000;
+	if(l <= 0)
+		l = 1;
+	for(i = 0; i < l; i++)
+		;
+}
+
+ulong
+perfticks(void)
+{
+	return (ulong)fastticks(nil);
+}

+ 227 - 0
sys/src/9/ppc/dat.h

@@ -0,0 +1,227 @@
+typedef struct Conf	Conf;
+typedef struct FPsave	FPsave;
+typedef struct ISAConf	ISAConf;
+typedef struct Label	Label;
+typedef struct Lock	Lock;
+typedef struct Mach	Mach;
+typedef struct Notsave	Notsave;
+typedef struct Page	Page;
+typedef struct PCArch	PCArch;
+typedef struct Pcidev	Pcidev;
+typedef struct PMMU	PMMU;
+typedef struct Proc	Proc;
+typedef struct Sys	Sys;
+typedef struct Ureg	Ureg;
+typedef struct Vctl	Vctl;
+typedef struct Imap	Imap;
+typedef struct Vctl	Vctl;
+
+#define MAXSYSARG	5	/* for mount(fd, mpt, flag, arg, srv) */
+
+/*
+ *  parameters for sysproc.c
+ */
+#define AOUT_MAGIC	Q_MAGIC
+
+/*
+ *  machine dependent definitions used by ../port/dat.h
+ */
+
+struct Lock
+{
+	ulong	key;			/* semaphore (non-zero = locked) */
+	ulong	sr;
+	ulong	pc;
+	Proc	*p;
+	ulong	pid;
+	ushort	isilock;
+};
+
+struct Label
+{
+	ulong	sp;
+	ulong	pc;
+};
+
+/*
+ * Proc.fpstate
+ */
+enum
+{
+	/* Floating point states */
+	FPinit = 0,
+	FPactive = 1,
+	FPinactive = 2,
+	/* Bit that's or-ed in during note handling (FP is illegal in note handlers) */
+	FPillegal = 0x100,
+};
+
+/*
+ * This structure must agree with fpsave and fprestore asm routines
+ */
+struct	FPsave
+{
+	double	fpreg[32];
+	union {
+		double	fpscrd;
+		struct {
+			ulong	pad;
+			ulong	fpscr;
+		};
+	};
+};
+
+struct Conf
+{
+	ulong	nmach;		/* processors */
+	ulong	nproc;		/* processes */
+	ulong	npage0;		/* total physical pages of memory */
+	ulong	npage1;		/* total physical pages of memory */
+	ulong	npage;		/* total physical pages of memory */
+	ulong	base0;		/* base of bank 0 */
+	ulong	base1;		/* base of bank 1 */
+	ulong	upages;		/* user page pool */
+	ulong	nimage;		/* number of page cache image headers */
+	ulong	nswap;		/* number of swap pages */
+	int		nswppo;		/* max # of pageouts per segment pass */
+	ulong	copymode;	/* 0 is copy on write, 1 is copy on reference */
+	int		monitor;		/* has display? */
+	ulong	ialloc;		/* bytes available for interrupt time allocation */
+	ulong	pipeqsize;		/* size in bytes of pipe queues */
+};
+
+/*
+ *  mmu goo in the Proc structure
+ */
+#define NCOLOR 1
+struct PMMU
+{
+	int		mmupid;
+	Ureg	*	mmureg;		/* pointer to ureg structure */
+	ulong	mmuinstr;	/* last miss was an instruction miss */
+};
+
+/*
+ *  things saved in the Proc structure during a notify
+ */
+struct Notsave
+{
+	ulong	UNUSED;
+};
+
+#include "../port/portdat.h"
+
+/*
+ *  machine dependent definitions not used by ../port/dat.h
+ */
+/*
+ * Fake kmap
+ */
+typedef	void		KMap;
+#define	VA(k)		((ulong)(k))
+#define	kmap(p)		(KMap*)((p)->pa|KZERO)
+#define	kunmap(k)
+
+struct Mach
+{
+	/* OFFSETS OF THE FOLLOWING KNOWN BY l.s */
+/*0x00*/	int		machno;			/* physical id of processor */
+/*0x04*/	ulong	splpc;			/* pc that called splhi() */
+/*0x08*/	Proc	*	proc;			/* current process on this processor */
+	/* Debugging/statistics for software TLB in l.s (therefore, also known by l.s) */
+/*0x0c*/	ulong	tlbfault;			/* type of last miss */
+/*0x10*/	ulong	imiss;			/* number of instruction misses */
+/*0x14*/	ulong	dmiss;			/* number of data misses */
+
+	/* ordering from here on irrelevant */
+
+	Imap*	imap;
+	uchar*	flash;
+
+	ulong	ticks;			/* of the clock since boot time */
+	Label	sched;			/* scheduler wakeup */
+	Lock		alarmlock;		/* access to alarm list */
+	void	*	alarm;			/* alarms bound to this clock */
+	int		inclockintr;
+	int		cputype;
+	ulong	loopconst;
+	Perf	perf;			/* performance counters */
+
+	ulong	fairness;		/* for runproc */
+
+	ulong	clkin;		/* basic clock frequency */
+	ulong	vco_out;
+	vlong	cpuhz;
+	ulong	bushz;
+	ulong	dechz;
+	ulong	tbhz;
+	ulong	cpmhz;		/* communications processor module frequency */
+	ulong	brghz;		/* baud rate generator frequency */
+
+	ulong	pcclast;
+	uvlong	fastclock;
+
+	int		tlbpurge;		/* # of tlb purges */
+	int		pfault;		/* # of page faults */
+	int		cs;
+	int		syscall;
+	int		load;
+	int		intr;
+	int		flushmmu;	/* make current proc flush it's mmu state */
+	int		ilockdepth;
+
+	ulong	ptabbase;		/* start of page table in kernel virtual space */
+	int		slotgen;		/* next pte (byte offset) when pteg is full */
+	int		mmupid;		/* next mmu pid to use */
+	int		sweepcolor;
+	int		trigcolor;
+	Rendez	sweepr;
+
+	ulong	spuriousintr;
+	int		lastintr;
+
+	/* MUST BE LAST */
+	int		stack[1];
+};
+
+struct
+{
+	Lock;
+	short	machs;
+	short	exiting;
+	short	ispanic;
+}active;
+
+/*
+ *  a parsed plan9.ini line
+ */
+#define NISAOPT		8
+
+struct ISAConf {
+	char		*type;
+	ulong	port;
+	int	irq;
+	ulong	dma;
+	ulong	mem;
+	ulong	size;
+	ulong	freq;
+
+	int	nopt;
+	char	*opt[NISAOPT];
+};
+
+struct Vctl {
+	Vctl*	next;			/* handlers on this vector */
+
+	char	name[KNAMELEN];	/* of driver */
+	int	isintr;			/* interrupt or fault/trap */
+	int	irq;
+
+	void	(*f)(Ureg*, void*);	/* handler to call */
+	void*	a;			/* argument to call it with */
+};
+
+extern Mach		mach0;
+
+extern register Mach	*m;
+extern register Proc	*up;

+ 468 - 0
sys/src/9/ppc/devether.c

@@ -0,0 +1,468 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+
+#include "etherif.h"
+
+static Ether *etherxx[MaxEther];
+
+Chan*
+etherattach(char* spec)
+{
+	ulong ctlrno;
+	char *p;
+	Chan *chan;
+
+	ctlrno = 0;
+	if(spec && *spec){
+		ctlrno = strtoul(spec, &p, 0);
+		if((ctlrno == 0 && p == spec) || *p || (ctlrno >= MaxEther))
+			error(Ebadarg);
+	}
+	if(etherxx[ctlrno] == 0)
+		error(Enodev);
+
+	chan = devattach('l', spec);
+	chan->dev = ctlrno;
+	if(etherxx[ctlrno]->attach)
+		etherxx[ctlrno]->attach(etherxx[ctlrno]);
+	return chan;
+}
+
+static Walkqid*
+etherwalk(Chan* chan, Chan* nchan, char** name, int nname)
+{
+	return netifwalk(etherxx[chan->dev], chan, nchan, name, nname);
+}
+
+static int
+etherstat(Chan* chan, uchar* dp, int n)
+{
+	return netifstat(etherxx[chan->dev], chan, dp, n);
+}
+
+static Chan*
+etheropen(Chan* chan, int omode)
+{
+	return netifopen(etherxx[chan->dev], chan, omode);
+}
+
+static void
+ethercreate(Chan*, char*, int, ulong)
+{
+}
+
+static void
+etherclose(Chan* chan)
+{
+	netifclose(etherxx[chan->dev], chan);
+}
+
+static long
+etherread(Chan* chan, void* buf, long n, vlong off)
+{
+	Ether *ether;
+	ulong offset = off;
+
+	ether = etherxx[chan->dev];
+	if((chan->qid.type & QTDIR) == 0 && ether->ifstat){
+		/*
+		 * With some controllers it is necessary to reach
+		 * into the chip to extract statistics.
+		 */
+		if(NETTYPE(chan->qid.path) == Nifstatqid)
+			return ether->ifstat(ether, buf, n, offset);
+		else if(NETTYPE(chan->qid.path) == Nstatqid)
+			ether->ifstat(ether, buf, 0, offset);
+	}
+
+	return netifread(ether, chan, buf, n, offset);
+}
+
+static Block*
+etherbread(Chan* chan, long n, ulong offset)
+{
+	return netifbread(etherxx[chan->dev], chan, n, offset);
+}
+
+static int
+etherwstat(Chan* chan, uchar* dp, int n)
+{
+	return netifwstat(etherxx[chan->dev], chan, dp, n);
+}
+
+static void
+etherrtrace(Netfile* f, Etherpkt* pkt, int len)
+{
+	int i, n;
+	Block *bp;
+
+	if(qwindow(f->in) <= 0)
+		return;
+	if(len > 58)
+		n = 58;
+	else
+		n = len;
+	bp = iallocb(64);
+	if(bp == nil)
+		return;
+	memmove(bp->wp, pkt->d, n);
+	i = TK2MS(MACHP(0)->ticks);
+	bp->wp[58] = len>>8;
+	bp->wp[59] = len;
+	bp->wp[60] = i>>24;
+	bp->wp[61] = i>>16;
+	bp->wp[62] = i>>8;
+	bp->wp[63] = i;
+	bp->wp += 64;
+	qpass(f->in, bp);
+}
+
+Block*
+etheriq(Ether* ether, Block* bp, int fromwire)
+{
+	Etherpkt *pkt;
+	ushort type;
+	int len, multi, tome, fromme;
+	Netfile **ep, *f, **fp, *fx;
+	Block *xbp;
+
+	ether->inpackets++;
+
+	pkt = (Etherpkt*)bp->rp;
+	len = BLEN(bp);
+	type = (pkt->type[0]<<8)|pkt->type[1];
+	fx = 0;
+	ep = &ether->f[Ntypes];
+
+	multi = pkt->d[0] & 1;
+	/* check for valid multcast addresses */
+	if(multi && memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) && ether->prom == 0){
+		if(!activemulti(ether, pkt->d, sizeof(pkt->d))){
+			if(fromwire){
+				freeb(bp);
+				bp = 0;
+			}
+			return bp;
+		}
+	}
+
+	/* is it for me? */
+	tome = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0;
+	fromme = memcmp(pkt->s, ether->ea, sizeof(pkt->s)) == 0;
+
+	/*
+	 * Multiplex the packet to all the connections which want it.
+	 * If the packet is not to be used subsequently (fromwire != 0),
+	 * attempt to simply pass it into one of the connections, thereby
+	 * saving a copy of the data (usual case hopefully).
+	 */
+	for(fp = ether->f; fp < ep; fp++){
+		if(f = *fp)
+		if(f->type == type || f->type < 0)
+		if(tome || multi || f->prom){
+			/* Don't want to hear bridged packets */
+			if(f->bridge && !fromwire && !fromme)
+				continue;
+			if(!f->headersonly){
+				if(fromwire && fx == 0)
+					fx = f;
+				else if(xbp = iallocb(len)){
+					memmove(xbp->wp, pkt, len);
+					xbp->wp += len;
+					qpass(f->in, xbp);
+				}
+				else
+					ether->soverflows++;
+			}
+			else
+				etherrtrace(f, pkt, len);
+		}
+	}
+
+	if(fx){
+		if(qpass(fx->in, bp) < 0)
+			ether->soverflows++;
+		return 0;
+	}
+	if(fromwire){
+		freeb(bp);
+		return 0;
+	}
+
+	return bp;
+}
+
+static int
+etheroq(Ether* ether, Block* bp)
+{
+	int len, loopback, s;
+	Etherpkt *pkt;
+
+	ether->outpackets++;
+	/*
+	 * Check if the packet has to be placed back onto the input queue,
+	 * i.e. if it's a loopback or broadcast packet or the interface is
+	 * in promiscuous mode.
+	 * If it's a loopback packet indicate to etheriq that the data isn't
+	 * needed and return, etheriq will pass-on or free the block.
+	 * To enable bridging to work, only packets that were originated
+	 * by this interface are fed back.
+	 */
+	pkt = (Etherpkt*)bp->rp;
+	len = BLEN(bp);
+	loopback = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0;
+	if(loopback || memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) == 0 || ether->prom){
+		s = splhi();
+		etheriq(ether, bp, 0);
+		splx(s);
+	}
+
+	if(!loopback){
+		qbwrite(ether->oq, bp);
+		ether->transmit(ether);
+	} else
+		freeb(bp);
+
+	return len;
+}
+
+static long
+etherwrite(Chan* chan, void* buf, long n, vlong)
+{
+	Ether *ether;
+	Block *bp;
+	int nn;
+
+	ether = etherxx[chan->dev];
+	if(NETTYPE(chan->qid.path) != Ndataqid) {
+		nn = netifwrite(ether, chan, buf, n);
+		if(nn >= 0)
+			return nn;
+		if(n == sizeof("nonblocking")-1 && strncmp((char*)buf, "nonblocking", n) == 0){
+			qnoblock(ether->oq, 1);
+			return n;
+		}
+		if(ether->ctl!=nil)
+			return ether->ctl(ether,buf,n);
+			
+		error(Ebadctl);
+	}
+
+	if(n > ether->maxmtu)
+		error(Etoobig);
+	if(n < ether->minmtu)
+		error(Etoosmall);
+
+	bp = allocb(n);
+	if(waserror()){
+		freeb(bp);
+		nexterror();
+	}
+	memmove(bp->rp, buf, n);
+	memmove(bp->rp+Eaddrlen, ether->ea, Eaddrlen);
+	poperror();
+	bp->wp += n;
+
+	return etheroq(ether, bp);
+}
+
+static long
+etherbwrite(Chan* chan, Block* bp, ulong)
+{
+	Ether *ether;
+	long n;
+
+	n = BLEN(bp);
+	if(NETTYPE(chan->qid.path) != Ndataqid){
+		if(waserror()) {
+			freeb(bp);
+			nexterror();
+		}
+		n = etherwrite(chan, bp->rp, n, 0);
+		poperror();
+		freeb(bp);
+		return n;
+	}
+	ether = etherxx[chan->dev];
+
+	if(n > ether->maxmtu){
+		freeb(bp);
+		error(Etoobig);
+	}
+	if(n < ether->minmtu){
+		freeb(bp);
+		error(Etoosmall);
+	}
+
+	return etheroq(ether, bp);
+}
+
+static struct {
+	char*	type;
+	int	(*reset)(Ether*);
+} cards[MaxEther+1];
+
+void
+addethercard(char* t, int (*r)(Ether*))
+{
+	static int ncard;
+
+	if(ncard == MaxEther)
+		panic("too many ether cards");
+	cards[ncard].type = t;
+	cards[ncard].reset = r;
+	ncard++;
+}
+
+int
+parseether(uchar *to, char *from)
+{
+	char nip[4];
+	char *p;
+	int i;
+
+	p = from;
+	for(i = 0; i < 6; i++){
+		if(*p == 0)
+			return -1;
+		nip[0] = *p++;
+		if(*p == 0)
+			return -1;
+		nip[1] = *p++;
+		nip[2] = 0;
+		to[i] = strtoul(nip, 0, 16);
+		if(*p == ':')
+			p++;
+	}
+	return 0;
+}
+
+static void
+etherreset(void)
+{
+	Ether *ether;
+	int i, n, ctlrno;
+	char name[32], buf[128];
+
+	for(ether = 0, ctlrno = 0; ctlrno < MaxEther; ctlrno++){
+		if(ether == 0)
+			ether = malloc(sizeof(Ether));
+		memset(ether, 0, sizeof(Ether));
+		ether->ctlrno = ctlrno;
+		ether->tbdf = BUSUNKNOWN;
+		ether->mbps = 10;
+		ether->minmtu = ETHERMINTU;
+		ether->maxmtu = ETHERMAXTU;
+		if(isaconfig("ether", ctlrno, ether) == 0)
+			continue;
+		for(n = 0; cards[n].type; n++){
+			if(cistrcmp(cards[n].type, ether->type))
+				continue;
+			for(i = 0; i < ether->nopt; i++){
+				if(strncmp(ether->opt[i], "ea=", 3))
+					continue;
+				if(parseether(ether->ea, &ether->opt[i][3]) == -1)
+					memset(ether->ea, 0, Eaddrlen);
+			}	
+			if(cards[n].reset(ether))
+				break;
+
+			/*
+			 * IRQ2 doesn't really exist, it's used to gang the interrupt
+			 * controllers together. A device set to IRQ2 will appear on
+			 * the second interrupt controller as IRQ9.
+			 */
+			if(ether->irq == 2 && BUSTYPE(ether->tbdf) != BusPCI)
+				ether->irq = 9;
+			snprint(name, sizeof(name), "ether%d", ctlrno);
+
+			/*
+			 * If ether->irq is <0, it is a hack to indicate no interrupt
+			 * used by ethersink.
+			 */
+			if(ether->irq >= 0)
+				intrenable(ether->irq, ether->interrupt, ether, name);
+			i = sprint(buf, "#l%d: %s: %dMbps port 0x%luX irq %d",
+				ctlrno, ether->type, ether->mbps, ether->port, ether->irq);
+			if(ether->mem)
+				i += sprint(buf+i, " addr 0x%luX", PADDR(ether->mem));
+			if(ether->size)
+				i += sprint(buf+i, " size 0x%luX", ether->size);
+			i += sprint(buf+i, ": %2.2uX%2.2uX%2.2uX%2.2uX%2.2uX%2.2uX",
+				ether->ea[0], ether->ea[1], ether->ea[2],
+				ether->ea[3], ether->ea[4], ether->ea[5]);
+			sprint(buf+i, "\n");
+			print(buf);
+
+			if(ether->mbps >= 100){
+				netifinit(ether, name, Ntypes, 256*1024);
+				if(ether->oq == 0)
+					ether->oq = qopen(256*1024, Qmsg, 0, 0);
+			}
+			else{
+				netifinit(ether, name, Ntypes, 65*1024);
+				if(ether->oq == 0)
+					ether->oq = qopen(65*1024, Qmsg, 0, 0);
+			}
+			if(ether->oq == 0)
+				panic("etherreset %s", name);
+			ether->alen = Eaddrlen;
+			memmove(ether->addr, ether->ea, Eaddrlen);
+			memset(ether->bcast, 0xFF, Eaddrlen);
+
+			etherxx[ctlrno] = ether;
+			ether = 0;
+			break;
+		}
+	}
+	if(ether)
+		free(ether);
+}
+
+#define POLY 0xedb88320
+
+/* really slow 32 bit crc for ethers */
+ulong
+ethercrc(uchar *p, int len)
+{
+	int i, j;
+	ulong crc, b;
+
+	crc = 0xffffffff;
+	for(i = 0; i < len; i++){
+		b = *p++;
+		for(j = 0; j < 8; j++){
+			crc = (crc>>1) ^ (((crc^b) & 1) ? POLY : 0);
+			b >>= 1;
+		}
+	}
+	return crc;
+}
+
+Dev etherdevtab = {
+	'l',
+	"ether",
+
+	etherreset,
+	devinit,
+	devshutdown,
+	etherattach,
+	etherwalk,
+	etherstat,
+	etheropen,
+	ethercreate,
+	etherclose,
+	etherread,
+	etherbread,
+	etherwrite,
+	etherbwrite,
+	devremove,
+	etherwstat,
+};

+ 963 - 0
sys/src/9/ppc/devflash.c

@@ -0,0 +1,963 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+enum {
+	Nflash = 2,
+	Maxwchunk=	1024,	/* maximum chunk written by one call to falg->write */
+};
+
+
+/*
+ *  Flashes are either 8 or 16 bits wide.  On some installations (e.g., the
+ *  bitsy, they are interleaved: address 0 is in the first chip, address 2
+ *  on the second, address 4 on the first, etc.
+ *  We define Funit as the unit that matches the width of a single flash chip,
+ *  so Funit is either `uchar' or `ushort' (I haven't seen 32-bit wide flashes),
+ *  and we define Fword as the unit that matches a set of interleaved Funits.
+ *  We access interleaved flashes simultaneously, by doing single reads and
+ *  writes to both.  The macro `mirror' takes a command and replicates it for
+ *  this purpose.
+ *  The Blast board has a non-interleaved 16-bit wide flash.  When doing
+ *  writes to it, we must swap bytes.
+ */
+
+typedef struct FlashAlg FlashAlg;
+typedef struct Flash Flash;
+typedef struct FlashRegion FlashRegion;
+
+#ifdef WIDTH8
+	typedef		uchar		Funit;		/* Width of the flash (uchar or ushort) */
+#	define		toendian(x)	(x)			/* Little or big endianness */
+#	define		fromendian(x)	(x)
+#	define		reg(x)		((x)<<1)
+#	ifdef INTERLEAVED
+#		define	mirror(x)		((x)<<8|(x))	/* Double query for interleaved flashes */
+		typedef	ushort		Fword;		/* Width after interleaving */
+#		define	Wshift		1
+#	else
+#		define 	mirror(x)		(x)
+		typedef	uchar		Fword;
+#		define	Wshift		0
+#	endif
+#else
+	typedef		ushort		Funit;
+#	define		toendian(x)	((x)<<8)
+#	define		fromendian(x)	((x)>>8)
+#	define		reg(x)		(x)
+#	ifdef INTERLEAVED
+#		define	mirror(x)		(toendian(x)<<16|toendian(x))
+		typedef	ulong		Fword;
+#		define	Wshift		2
+#	else
+#		define mirror(x)		toendian(x)
+		typedef	ushort		Fword;
+#		define	Wshift		1
+#	endif
+#endif
+
+/* this defines a contiguous set of erase blocks of one size */
+struct FlashRegion
+{
+	ulong	addr;	/* start of region */
+	ulong	end;		/* end of region + 1 */
+	ulong	n;		/* number of blocks */
+	ulong	size;		/* size of each block */
+};
+
+struct Flash
+{
+	ISAConf;					/* contains size */
+	RWlock;
+	Fword		*p;
+	ushort		algid;		/* access algorithm */
+	FlashAlg		*alg;
+	ushort		manid;		/* manufacturer id */
+	ushort		devid;		/* device id */
+	int			wbsize;		/* size of write buffer */ 
+	ulong		nr;			/* number of regions */
+	uchar		bootprotect;
+	ulong		offset;		/* beginning offset of this flash */
+	FlashRegion	r[32];
+};
+
+/* this defines a particular access algorithm */
+struct FlashAlg
+{
+	int	id;
+	char	*name;
+	void	(*identify)(Flash*);	/* identify device */
+	void	(*erase)(Flash*, ulong);	/* erase a region */
+	void	(*write)(Flash*, void*, long, ulong);	/* write a region */
+};
+
+static void	ise_id(Flash*);
+static void	ise_erase(Flash*, ulong);
+static void	ise_write(Flash*, void*, long, ulong);
+
+static void	afs_id(Flash*);
+static void	afs_erase(Flash*, ulong);
+static void	afs_write(Flash*, void*, long, ulong);
+
+static ulong	blockstart(Flash*, ulong);
+static ulong	blockend(Flash*, ulong);
+
+FlashAlg falg[] =
+{
+	{ 1,	"Intel/Sharp Extended",	ise_id, ise_erase, ise_write	},
+	{ 2,	"AMD/Fujitsu Standard",	afs_id, afs_erase, afs_write	},
+};
+
+Flash flashes[Nflash];
+
+/*
+ *  common flash interface
+ */
+static uchar
+cfigetc(Flash *flash, int off)
+{
+	uchar rv;
+
+	flash->p[reg(0x55)] = mirror(0x98);
+	rv = fromendian(flash->p[reg(off)]);
+	flash->p[reg(0x55)] = mirror(0xFF);
+	return rv;
+}
+
+static ushort
+cfigets(Flash *flash, int off)
+{
+	return (cfigetc(flash, off+1)<<8)|cfigetc(flash, off);
+}
+
+static ulong
+cfigetl(Flash *flash, int off)
+{
+	return (cfigetc(flash, off+3)<<24)|(cfigetc(flash, off+2)<<16)|
+		(cfigetc(flash, off+1)<<8)|cfigetc(flash, off);
+}
+
+static void
+cfiquery(Flash *flash)
+{
+	uchar q, r, y;
+	ulong x, addr;
+
+	q = cfigetc(flash, 0x10);
+	r = cfigetc(flash, 0x11);
+	y = cfigetc(flash, 0x12);
+	if(q != 'Q' || r != 'R' || y != 'Y'){
+		print("cfi query failed: %ux %ux %ux\n", q, r, y);
+		return;
+	}
+	flash->algid = cfigetc(flash, 0x13);
+	flash->size = (sizeof(Fword)/sizeof(Funit)) * (1<<(cfigetc(flash, 0x27)));
+	flash->wbsize = (sizeof(Fword)/sizeof(Funit)) * (1<<(cfigetc(flash, 0x2a)));
+	flash->nr = cfigetc(flash, 0x2c);
+	if(flash->nr > nelem(flash->r)){
+		print("cfi reports > %d regions\n", nelem(flash->r));
+		flash->nr = nelem(flash->r);
+	}
+	addr = 0;
+	for(q = 0; q < flash->nr; q++){
+		x = cfigetl(flash, q+0x2d);
+		flash->r[q].size = (sizeof(Fword)/sizeof(Funit)) * 256 * (x>>16);
+		flash->r[q].n = (x&0xffff)+1;
+		flash->r[q].addr = addr;
+		addr += flash->r[q].size*flash->r[q].n;
+		flash->r[q].end = addr;
+	}
+}
+
+/*
+ *  flash device interface
+ */
+
+enum
+{
+	Qtopdir,
+	Q2nddir,
+	Qfctl,
+	Qfdata,
+
+	Maxpart= 8,
+};
+
+
+typedef struct FPart FPart;
+struct FPart
+{
+	Flash	*flash;
+	char		*name;
+	char		*ctlname;
+	ulong	start;
+	ulong	end;
+};
+static FPart	part[Maxpart];
+
+#define FQID(p,q)	((p)<<8|(q))
+#define FTYPE(q)	((q) & 0xff)
+#define FPART(q)	(&part[(q) >>8])
+
+static int
+gen(Chan *c, char*, Dirtab*, int, int i, Dir *dp)
+{
+	Qid q;
+	FPart *fp;
+
+	q.vers = 0;
+
+	/* top level directory contains the name of the network */
+	if(c->qid.path == Qtopdir){
+		switch(i){
+		case DEVDOTDOT:
+			q.path = Qtopdir;
+			q.type = QTDIR;
+			devdir(c, q, "#F", 0, eve, DMDIR|0555, dp);
+			break;
+		case 0:
+			q.path = Q2nddir;
+			q.type = QTDIR;
+			devdir(c, q, "flash", 0, eve, DMDIR|0555, dp);
+			break;
+		default:
+			return -1;
+		}
+		return 1;
+	}
+
+	/* second level contains all partitions and their control files */
+	switch(i) {
+	case DEVDOTDOT:
+		q.path = Qtopdir;
+		q.type = QTDIR;
+		devdir(c, q, "#F", 0, eve, DMDIR|0555, dp);
+		break;
+	default:
+		if(i >= 2*Maxpart)
+			return -1;
+		fp = &part[i>>1];
+		if(fp->name == nil)
+			return 0;
+		if(i & 1){
+			q.path = FQID(i>>1, Qfdata);
+			q.type = QTFILE;
+			devdir(c, q, fp->name, fp->end-fp->start, eve, 0660, dp);
+		} else {
+			q.path = FQID(i>>1, Qfctl);
+			q.type = QTFILE;
+			devdir(c, q, fp->ctlname, 0, eve, 0660, dp);
+		}
+		break;
+	}
+	return 1;
+}
+
+static Flash *
+findflash(ulong addr)
+{
+	Flash *flash;
+
+	for (flash = flashes; flash < flashes + Nflash; flash++)
+		if(addr >= flash->offset && addr < flash->offset + flash->size)
+			return flash;
+	return nil;
+}
+
+static FPart*
+findpart(char *name)
+{
+	int i;
+
+	for(i = 0; i < Maxpart; i++)
+		if(part[i].name != nil && strcmp(name, part[i].name) == 0)
+			break;
+	if(i >= Maxpart)
+		return nil;
+	return &part[i];
+}
+
+static void
+addpart(FPart *fp, char *name, ulong start, ulong end)
+{
+	int i;
+	char ctlname[64];
+	Flash *flash;
+	if (start > end)
+		error(Ebadarg);
+	if(fp == nil){
+		flash = findflash(start);
+		if (flash == nil || end > flash->offset + flash->size)
+			error(Ebadarg);
+		start -= flash->offset;
+		end -= flash->offset;
+	} else {
+		start += fp->start;
+		end += fp->start;
+		if(start >= fp->end || end > fp->end){
+			error(Ebadarg);
+		}
+		flash = fp->flash;
+	}
+	if(blockstart(flash, start) != start)
+		error("must start on erase boundary");
+	if(blockstart(flash, end) != end && end != flash->size)
+		error("must end on erase boundary");
+
+	fp = findpart(name);
+	if(fp != nil)
+		error(Eexist);
+	for(i = 0; i < Maxpart; i++)
+		if(part[i].name == nil)
+			break;
+	if(i == Maxpart)
+		error("no more partitions");
+	fp = &part[i];
+	kstrdup(&fp->name, name);
+	snprint(ctlname, sizeof ctlname, "%sctl", name);
+	kstrdup(&fp->ctlname, ctlname);
+	fp->flash = flash;
+	fp->start = start;
+	fp->end = end;
+}
+
+static void
+rempart(FPart *fp)
+{
+	char *p, *cp;
+
+	p = fp->name;
+	fp->name = nil;
+	cp = fp->ctlname;
+	fp->ctlname = nil;
+	free(p);
+	free(cp);
+}
+
+void
+flashinit(void)
+{
+	int i, ctlrno;
+	char *fname;
+	ulong offset;
+	Flash *flash;
+
+	offset = 0;
+	for (ctlrno = 0; ctlrno < Nflash; ctlrno++){
+		flash = flashes + ctlrno;
+		if(isaconfig("flash", ctlrno, flash) == 0)
+			continue;
+		flash->p = (Fword*)flash->mem;
+		cfiquery(flash);
+		for(i = 0; i < nelem(falg); i++)
+			if(flash->algid == falg[i].id){
+				flash->alg = &falg[i];
+				(*flash->alg->identify)(flash);
+				break;
+			}
+		flash->bootprotect = 1;
+		flash->offset = offset;
+		fname = malloc(8);
+		sprint(fname, "flash%d", ctlrno);
+		addpart(nil, fname, offset, offset + flash->size);
+		offset += flash->size;
+	}
+}
+
+static Chan*
+flashattach(char* spec)
+{
+	return devattach('F', spec);
+}
+
+static Walkqid*
+flashwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, nil, 0, gen);
+}
+
+static int	 
+flashstat(Chan *c, uchar *db, int n)
+{
+	return devstat(c, db, n, nil, 0, gen);
+}
+
+static Chan*
+flashopen(Chan* c, int omode)
+{
+	omode = openmode(omode);
+	if(strcmp(up->user, eve)!=0)
+		error(Eperm);
+	return devopen(c, omode, nil, 0, gen);
+}
+
+static void	 
+flashclose(Chan*)
+{
+}
+
+static long
+flashctlread(FPart *fp, void* a, long n, vlong off)
+{
+	char *buf, *p, *e;
+	int i;
+	ulong addr, end;
+	Flash *flash;
+
+	flash = fp->flash;
+	buf = smalloc(1024);
+	e = buf + 1024;
+	p = seprint(buf, e, "0x%-9lux 0x%-9lux 0x%-9lux 0x%-9x 0x%-9ux 0x%-9ux\n",
+		flash->offset, fp->start, fp->end-fp->start, flash->wbsize, flash->manid, flash->devid);
+	addr = fp->start;
+	for(i = 0; i < flash->nr && addr < fp->end; i++)
+		if(flash->r[i].addr <= addr && flash->r[i].end > addr){
+			if(fp->end <= flash->r[i].end)
+				end = fp->end;
+			else
+				end = flash->r[i].end;
+			p = seprint(p, e, "0x%-9lux 0x%-9lux 0x%-9lux\n", addr,
+				(end-addr)/flash->r[i].size, flash->r[i].size);
+			addr = end;
+		}
+	n = readstr(off, a, n, buf);
+	free(buf);
+	return n;
+}
+
+static long
+flashdataread(FPart *fp, void* a, long n, vlong off)
+{
+	Flash *flash;
+
+	flash = fp->flash;
+	rlock(flash);
+	if(waserror()){
+		runlock(flash);
+		nexterror();
+	}
+	if(fp->name == nil)
+		error("partition vanished");
+	if(!iseve())
+		error(Eperm);
+	off += fp->start;
+	if(off >= fp->end)
+		n = 0;
+	if(off+n >= fp->end)
+		n = fp->end - off;
+	if(n > 0)
+		memmove(a, ((uchar*)flash->mem)+off, n);
+	runlock(flash);
+	poperror();
+
+	return n;
+}
+
+static long	 
+flashread(Chan* c, void* a, long n, vlong off)
+{
+	int t;
+
+	if(c->qid.type == QTDIR)
+		return devdirread(c, a, n, nil, 0, gen);
+	t = FTYPE(c->qid.path);
+	switch(t){
+	default:
+		error(Eperm);
+	case Qfctl:
+		n = flashctlread(FPART(c->qid.path), a, n, off);
+		break;
+	case Qfdata:
+		n = flashdataread(FPART(c->qid.path), a, n, off);
+		break;
+	}
+	return n;
+}
+
+static void
+bootprotect(ulong addr)
+{
+	FlashRegion *r;
+	Flash *flash;
+
+	flash = findflash(addr);
+	if (flash == nil)
+		error(Ebadarg);
+	if(flash->bootprotect == 0)
+		return;
+	if(flash->nr == 0)
+		error("writing over boot loader disallowed");
+	r = flash->r;
+	if(addr >= r->addr && addr < r->addr + r->size)
+		error("writing over boot loader disallowed");
+}
+
+static ulong
+blockstart(Flash *flash, ulong addr)
+{
+	FlashRegion *r, *e;
+	ulong x;
+
+	r = flash->r;
+	for(e = &flash->r[flash->nr]; r < e; r++){
+		if(addr >= r->addr && addr < r->end){
+			x = addr - r->addr;
+			x /= r->size;
+			return r->addr + x*r->size;
+		}
+	}
+			
+	return (ulong)-1;
+}
+
+static ulong
+blockend(Flash *flash, ulong addr)
+{
+	FlashRegion *r, *e;
+	ulong x;
+
+	r = flash->r;
+	for(e = &flash->r[flash->nr]; r < e; r++)
+		if(addr >= r->addr && addr < r->end){
+			x = addr - r->addr;
+			x /= r->size;
+			return r->addr + (x+1)*r->size;
+		}
+			
+	return (ulong)-1;
+}
+
+static long
+flashctlwrite(FPart *fp, char *p, long n)
+{
+	Cmdbuf *cmd;
+	ulong off;
+	Flash *flash;
+
+	if(fp == nil)
+		panic("flashctlwrite");
+
+	flash = fp->flash;
+	cmd = parsecmd(p, n);
+	wlock(flash);
+	if(waserror()){
+		wunlock(flash);
+		nexterror();
+	}
+	if(strcmp(cmd->f[0], "erase") == 0){
+		switch(cmd->nf){
+		case 2:
+			/* erase a single block in the partition */
+			off = atoi(cmd->f[1]);
+			off += fp->start;
+			if(off >= fp->end)
+				error("region not in partition");
+			if(off != blockstart(flash, off))
+				error("erase must be a block boundary");
+			bootprotect(off);
+			(*flash->alg->erase)(flash, off);
+			break;
+		case 1:
+			/* erase the whole partition */
+			bootprotect(fp->start);
+			for(off = fp->start; off < fp->end; off = blockend(flash, off))
+				(*flash->alg->erase)(flash, off);
+			break;
+		default:
+			error(Ebadarg);
+		}
+	} else if(strcmp(cmd->f[0], "add") == 0){
+		if(cmd->nf != 4)
+			error(Ebadarg);
+		addpart(fp, cmd->f[1], strtoul(cmd->f[2], nil, 0), strtoul(cmd->f[3], nil, 0));
+	} else if(strcmp(cmd->f[0], "remove") == 0){
+		rempart(fp);
+	} else if(strcmp(cmd->f[0], "protectboot") == 0){
+		if(cmd->nf == 0 || strcmp(cmd->f[1], "off") != 0)
+			flash->bootprotect = 1;
+		else
+			flash->bootprotect = 0;
+	} else
+		error(Ebadarg);
+	poperror();
+	wunlock(flash);
+	free(cmd);
+
+	return n;
+}
+
+static long
+flashdatawrite(FPart *fp, uchar *p, long n, long off)
+{
+	uchar *end;
+	int m;
+	int on;
+	long ooff;
+	uchar *buf;
+	Flash *flash;
+
+	if(fp == nil)
+		panic("flashdatawrite");
+
+	flash = fp->flash;
+	buf = nil;
+	wlock(flash);
+	if(waserror()){
+		wunlock(flash);
+		if(buf != nil)
+			free(buf);
+		nexterror();
+	}
+
+	if(fp->name == nil)
+		error("partition vanished");
+	if(!iseve())
+		error(Eperm);
+
+	/* can't cross partition boundaries */
+	off += fp->start;
+	if(off >= fp->end || off+n > fp->end || n <= 0)
+		error(Ebadarg);
+
+	/* make sure we're not writing the boot sector */
+	bootprotect(off);
+
+	on = n;
+
+	/*
+	 *  get the data into kernel memory to avoid faults during writing.
+	 *  if write is not on a quad boundary or not a multiple of 4 bytes,
+	 *  extend with data already in flash.
+	 */
+	buf = smalloc(n+8);
+	m = off & 3;
+	if(m){
+		*(ulong*)buf = flash->p[off>>Wshift];
+		n += m;
+		off -= m;
+	}
+	if(n & 3){
+		n -= n & 3;
+		*(ulong*)(&buf[n]) = flash->p[(off+n)>>Wshift];
+		n += 4;
+	}
+	memmove(&buf[m], p, on);
+
+	/* (*flash->alg->write) can't cross blocks */
+	ooff = off;
+	p = buf;
+	for(end = p + n; p < end; p += m){
+		m = blockend(flash, off) - off;
+		if(m > end - p)
+			m = end - p;
+		if(m > Maxwchunk)
+			m = Maxwchunk;
+		(*flash->alg->write)(flash, p, m, off);
+		off += m;
+	}
+
+	/* make sure write succeeded */
+	if(memcmp(buf, &flash->p[ooff>>Wshift], n) != 0)
+		error("written bytes don't match");
+
+	wunlock(flash);
+	free(buf);
+	poperror();
+
+	return on;
+}
+
+static long	 
+flashwrite(Chan* c, void* a, long n, vlong off)
+{
+	int t;
+
+	if(c->qid.type == QTDIR)
+		error(Eperm);
+
+	if(!iseve())
+		error(Eperm);
+
+	t = FTYPE(c->qid.path);
+	switch(t){
+	default:
+		panic("flashwrite");
+	case Qfctl:
+		n = flashctlwrite(FPART(c->qid.path), a, n);
+		break;
+	case Qfdata:
+		n = flashdatawrite(FPART(c->qid.path), a, n, off);
+		break;
+	}
+	return n;
+}
+
+Dev flashdevtab = {
+	'F',
+	"flash",
+
+	devreset,
+	flashinit,
+	devshutdown,
+	flashattach,
+	flashwalk,
+	flashstat,
+	flashopen,
+	devcreate,
+	flashclose,
+	flashread,
+	devbread,
+	flashwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
+
+enum
+{
+	/* status register */
+	ISEs_lockerr=		1<<1,
+	ISEs_powererr=		1<<3,
+	ISEs_progerr=		1<<4,
+	ISEs_eraseerr=		1<<5,
+	ISEs_ready=		1<<7,
+	ISEs_err= (ISEs_lockerr|ISEs_powererr|ISEs_progerr|ISEs_eraseerr),
+
+	/* extended status register */
+	ISExs_bufavail=		1<<7,
+};
+
+/* intel/sharp extended command set */
+static void
+ise_reset(Flash* flash)
+{
+	flash->p[reg(0xaa)] = mirror(0xff);	/* reset */
+}
+
+static void
+ise_id(Flash* flash)
+{
+	ise_reset(flash);
+	flash->p[reg(0xaaa)] = mirror(0x90);	/* uncover vendor info */
+	flash->manid = fromendian(flash->p[reg(0x0)]);
+	flash->devid = fromendian(flash->p[reg(0x1)]);
+	ise_reset(flash);
+}
+
+static void
+ise_clearerror(Flash* flash)
+{
+	flash->p[reg(0x200)] = mirror(0x50);
+
+}
+
+static void
+ise_error(int bank, ulong status)
+{
+	char err[64];
+
+	if(status & (ISEs_lockerr)){
+		sprint(err, "flash%d: block locked %lux", bank, status);
+		error(err);
+	}
+	if(status & (ISEs_powererr)){
+		sprint(err, "flash%d: low prog voltage %lux", bank, status);
+		error(err);
+	}
+	if(status & (ISEs_progerr|ISEs_eraseerr)){
+		sprint(err, "flash%d: i/o error %lux", bank, status);
+		error(err);
+	}
+}
+static void
+ise_erase(Flash *flash, ulong addr)
+{
+	ulong start;
+	ulong x;
+
+	addr >>= Wshift;
+
+	flashprogpower(1);
+	flash->p[addr] = mirror(0x20);
+	flash->p[addr] = mirror(0xd0);
+	start = m->ticks;
+	do {
+		x = fromendian(flash->p[addr]);
+		if((x & mirror(ISEs_ready)) == mirror(ISEs_ready))
+			break;
+	} while(TK2MS(m->ticks-start) < 1500);
+	flashprogpower(0);
+
+	ise_clearerror(flash);
+	ise_error(0, x);
+	ise_error(1, x>>16);
+
+	ise_reset(flash);
+}
+/*
+ *  the flash spec claimes writing goes faster if we use
+ *  the write buffer.  We fill the write buffer and then
+ *  issue the write request.  After the write request,
+ *  subsequent reads will yield the status register.
+ *
+ *  returns the status, even on timeouts.
+ *
+ *  NOTE: I tried starting back to back buffered writes
+ *	without reading the status in between, as the
+ *	flowchart in the intel data sheet suggests.
+ *	However, it always responded with an illegal
+ *	command sequence, so I must be missing something.
+ *	If someone learns better, please email me, though
+ *	I doubt it will be much faster. -  presotto@bell-labs.com
+ */
+static ulong
+ise_wbwrite(Flash *flash, Fword *p, int n, ulong off, ulong baddr, ulong *status)
+{
+	Fword x;
+	ulong start;
+	int i;
+	int s;
+
+	/* put flash into write buffer mode */
+	start = m->ticks;
+	for(;;) {
+		s = splhi();
+		/* request write buffer mode */
+		flash->p[baddr] = mirror(0xe8);
+
+		/* look at extended status reg for status */
+		if((flash->p[baddr] & mirror(1<<7)) == mirror(1<<7))
+			break;
+		splx(s);
+
+		/* didn't work, keep trying for 2 secs */
+		if(TK2MS(m->ticks-start) > 2000){
+			/* set up to read status */
+			flash->p[baddr] = mirror(0x70);
+			*status = fromendian(flash->p[baddr]);
+			pprint("write buffered cmd timed out\n");
+			return -1;
+		}
+	}
+
+	/* fill write buffer */
+	flash->p[baddr] = mirror(n-1);
+	for(i = 0; i < n; i++)
+		flash->p[off+i] = *p++;
+
+	/* program from buffer */
+	flash->p[baddr] = mirror(0xd0);
+	splx(s);
+
+	/* wait till the programming is done */
+	start = m->ticks;
+	for(;;) {
+		x = flash->p[baddr];	/* read status register */
+		*status = fromendian(x);
+		if((x & mirror(ISEs_ready)) == mirror(ISEs_ready))
+			break;
+		if(TK2MS(m->ticks-start) > 2000){
+			pprint("read status timed out\n");
+			return -1;
+		}
+	}
+	if(x & mirror(ISEs_err))
+		return -1;
+
+	return n;
+}
+
+static void
+ise_write(Flash *flash, void *a, long n, ulong off)
+{
+	Fword *p, *end;
+	int i, wbsize;
+	ulong x, baddr;
+
+ 	/* everything in terms of Fwords */
+	wbsize = flash->wbsize >> Wshift;
+	baddr = blockstart(flash, off) >> Wshift;
+	off >>= Wshift;
+	n >>= Wshift;
+	p = a;
+
+	/* first see if write will succeed */
+	for(i = 0; i < n; i++)
+		if((p[i] & flash->p[off+i]) != p[i])
+			error("flash needs erase");
+
+	if(waserror()){
+		ise_reset(flash);
+		flashprogpower(0);
+		nexterror();
+	}
+	flashprogpower(1);
+
+	/*
+	 *  use the first write to reach
+ 	 *  a write buffer boundary.  the intel maunal
+	 *  says writes starting at wb boundaries
+	 *  maximize speed.
+	 */
+	i = wbsize - (off & (wbsize-1));
+	for(end = p + n; p < end;){
+		if(i > end - p)
+			i = end - p;
+
+		if(ise_wbwrite(flash, p, i, off, baddr, &x) < 0)
+			break;
+
+		off += i;
+		p += i;
+		i = wbsize;
+	}
+
+	ise_clearerror(flash);
+	ise_error(0, x);
+	ise_error(1, x>>16);
+
+	ise_reset(flash);
+	flashprogpower(0);
+	poperror();
+}
+
+/* amd/fujitsu standard command set
+ *	I don't have an amd chipset to work with
+ *	so I'm loathe to write this yet.  If someone
+ *	else does, please send it to me and I'll
+ *	incorporate it -- presotto@bell-labs.com
+ */
+static void
+afs_reset(Flash *flash)
+{
+	flash->p[reg(0xaa)] = mirror(0xf0);	/* reset */
+}
+static void
+afs_id(Flash *flash)
+{
+	afs_reset(flash);
+	flash->p[reg(0xaa)] = mirror(0xf0);	/* reset */
+	flash->p[reg(0xaaa)] = mirror(0xaa);	/* query vendor block */
+	flash->p[reg(0x554)] = mirror(0x55);
+	flash->p[reg(0xaaa)] = mirror(0x90);
+	flash->manid = fromendian(flash->p[reg(0x00)]);
+	afs_reset(flash);
+	flash->p[reg(0xaaa)] = mirror(0xaa);	/* query vendor block */
+	flash->p[reg(0x554)] = mirror(0x55);
+	flash->p[reg(0xaaa)] = mirror(0x90);
+	flash->devid = fromendian(flash->p[reg(0x02)]);
+	afs_reset(flash);
+}
+static void
+afs_erase(Flash*, ulong)
+{
+	error("amd/fujistsu erase not implemented");
+}
+static void
+afs_write(Flash*, void*, long, ulong)
+{
+	error("amd/fujistsu write not implemented");
+}

+ 342 - 0
sys/src/9/ppc/devirq.c

@@ -0,0 +1,342 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include "io.h"
+#include "m8260.h"
+#include	"../port/error.h"
+
+enum{
+	IRQ0 = 18,
+	Level = 0,
+	Edge = 1,
+};
+
+enum{
+	Qdir,
+	Qirq1,
+	Qirq2,
+	Qirq3,
+	Qirq4,
+	Qirq5,
+	Qirq6,
+	Qirq7,
+	Qmstimer,
+	Qfpgareset,
+	NIRQ,
+};
+
+static Dirtab irqdir[]={
+	".",			{Qdir, 0, QTDIR},	0,		DMDIR|0555,
+	"irq1",		{Qirq1},			0,		0666,
+	"irq2",		{Qirq2},			0,		0666,
+	"irq3",		{Qirq1},			0,		0666,
+	"irq4",		{Qirq1},			0,		0666,
+	"irq5",		{Qirq1},			0,		0666,
+	"irq6",		{Qirq1},			0,		0666,
+	"irq7",		{Qirq1},			0,		0666,
+	"mstimer",	{Qmstimer},		0,		0666,
+	"fpgareset",	{Qfpgareset},		0,		0222,
+};
+
+enum
+{
+	CMinterrupt,
+	CMmode,
+	CMreset,
+	CMwait,
+	CMdebug,
+};
+
+Cmdtab irqmsg[] =
+{
+	CMinterrupt,	"interrupt",	2,
+	CMmode,		"mode",		2,
+	CMreset,		"reset",		1,
+	CMwait,		"wait",		1,
+	CMdebug,		"debug",		1,
+};
+
+typedef struct Irqconfig Irqconfig;
+struct Irqconfig {
+	int		intenable;		/* Interrupts are enabled */
+	int		mode;		/* level == 0; edge == 1 */
+	ulong	interrupts;	/* Count interrupts */
+	Rendez	r;			/* Rendez-vous point for interrupt waiting */
+	Irqconfig	*next;
+	Timer;
+};
+
+Irqconfig *irqconfig[NIRQ];	/* irqconfig[0] is not used */
+Lock irqlock;
+
+static void interrupt(Ureg*, void*);
+static void ticmstimer(Ureg*, Timer*);
+
+static void
+ticmstimer(Ureg*, Timer *t)
+{
+	Irqconfig *ic;
+
+	ic = t->a;
+ 	ic->interrupts++;
+	wakeup(&ic->r);
+}
+
+void
+irqenable(Irqconfig *ic, int irq)
+{
+	/* call with ilock(&irqlock) held */
+
+	if (ic->intenable)
+		return;
+	if (irq == Qmstimer){
+		if (ic->period == 0)
+			ic->period = ms2fastticks(ic->mode);
+		ic->when = 0;
+		timeradd(&ic->Timer);
+	}else{
+		if (irqconfig[irq]){
+			ic->next = irqconfig[irq];
+			irqconfig[irq] = ic;
+		}else{
+			ic->next = nil;
+			irqconfig[irq] = ic;
+			intrenable(IRQ0 + irq, interrupt, &irqconfig[irq], irqdir[irq].name);
+		}
+	}
+	ic->intenable = 1;
+}
+
+void
+irqdisable(Irqconfig *ic, int irq)
+{
+	Irqconfig **pic;
+
+	/* call with ilock(&irqlock) held */
+
+	if (ic->intenable == 0)
+		return;
+	if (irq == Qmstimer){
+		timerdel(&ic->Timer);
+	}else{
+		for(pic = &irqconfig[irq]; *pic != ic; pic = &(*pic)->next)
+			assert(*pic);
+		*pic = (*pic)->next;
+		if (irqconfig[irq] == nil)
+			intrdisable(IRQ0 + irq, interrupt, &irqconfig[irq], irqdir[irq].name);
+	}
+	ic->intenable = 0;
+}
+
+static Chan*
+irqattach(char *spec)
+{
+	return devattach('b', spec);
+}
+
+static Walkqid*
+irqwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name,nname, irqdir, nelem(irqdir), devgen);
+}
+
+static int
+irqstat(Chan *c, uchar *dp, int n)
+{
+	return devstat(c, dp, n, irqdir, nelem(irqdir), devgen);
+}
+
+static Chan*
+irqopen(Chan *c, int omode)
+{
+	Irqconfig *ic;
+	int irq;
+
+	irq = (ulong)c->qid.path;
+	if(irq != Qdir){
+		ic = mallocz(sizeof(Irqconfig), 1);
+		ic->f = ticmstimer;
+		ic->a = ic;
+		if (irq == Qmstimer)
+			ic->mode = 1000;
+		c->aux = ic;
+	}
+	return devopen(c, omode, irqdir, nelem(irqdir), devgen);
+}
+
+static void
+irqclose(Chan *c)
+{
+	int irq;
+	Irqconfig *ic;
+
+	irq = (ulong)c->qid.path;
+	if(irq == Qdir)
+		return;
+	ic = c->aux;
+	if (irq > Qmstimer)
+		return;
+	ilock(&irqlock);
+	irqdisable(ic, irq);
+	iunlock(&irqlock);
+	free(ic);
+}
+
+static long
+irqread(Chan *c, void *buf, long n, vlong)
+{
+	int irq;
+	Irqconfig *ic;
+	char tmp[24];
+
+	if(n <= 0)
+		return n;
+	irq = (ulong)c->qid.path;
+	if(irq == Qdir)
+		return devdirread(c, buf, n, irqdir, nelem(irqdir), devgen);
+	if(irq >= Qmstimer){
+		print("irqread 0x%llux\n", c->qid.path);
+		error(Egreg);
+	}
+	ic = c->aux;
+	if (ic->intenable == 0)
+		error("disabled");
+	sleep(&ic->r, return0, 0);
+	if (irq == Qmstimer)
+		snprint(tmp, sizeof tmp, "%11lud %d", ic->interrupts, ic->mode);
+	else
+		snprint(tmp, sizeof tmp, "%11lud %s", ic->interrupts, ic->mode?"edge":"level");
+	n = readstr(0, buf, n, tmp);
+	return n;
+}
+
+static long
+irqwrite(Chan *c, void *a, long n, vlong)
+{
+	int irq;
+	Irqconfig *ic;
+	Cmdbuf *cb;
+	Cmdtab *ct;
+
+	if(n <= 0)
+		return n;
+
+	irq = (ulong)c->qid.path;
+	if(irq <= 0 || irq >= nelem(irqdir)){
+		print("irqwrite 0x%llux\n", c->qid.path);
+		error(Egreg);
+	}
+	if (irq == Qfpgareset){
+		if (strncmp(a, "reset", 5) == 0)
+			fpgareset();
+		else
+			error(Egreg);
+		return n;
+	}
+	ic = c->aux;
+
+	cb = parsecmd(a, n);
+
+	if(waserror()) {
+		free(cb);
+		nexterror();
+	}
+	ct = lookupcmd(cb, irqmsg, nelem(irqmsg));
+	switch(ct->index) {
+	case 	CMinterrupt:
+		/* Turn interrupts on or off */
+		if (strcmp(cb->f[1], "on") == 0){
+			ilock(&irqlock);
+			irqenable(ic, irq);
+			iunlock(&irqlock);
+		}else if (strcmp(cb->f[1], "off") == 0){
+			ilock(&irqlock);
+			irqdisable(ic, irq);
+			iunlock(&irqlock);
+		}else
+			error(Ebadarg);
+		break;
+	case CMmode:
+		/* Set mode */
+		if (irq == Qmstimer){
+			ic->mode = strtol(cb->f[1], nil, 0);
+			if (ic->mode <= 0){
+				ic->period = ms2fastticks(1000);
+				ic->mode = 1000;
+				error(Ebadarg);
+			}
+			ic->period = ms2fastticks(ic->mode);
+		}else if (strcmp(cb->f[1], "level") == 0){
+			ic->mode = 0;
+			iomem->siexr &= ~(0x8000 >> irq);
+		}else if (strcmp(cb->f[1], "edge") == 0){
+			ic->mode = 1;
+			iomem->siexr |= 0x8000 >> irq;
+		}else
+			error(Ebadarg);
+		break;
+	case CMreset:
+		ic->interrupts = 0;
+		break;
+	case CMwait:
+		if (ic->intenable == 0)
+			error("interrupts are off");
+		sleep(&ic->r, return0, 0);
+		break;
+	case CMdebug:
+		print("simr h/l 0x%lux/0x%lux, sipnr h/l 0x%lux/0x%lux, siexr 0x%lux\n",
+			iomem->simr_h, iomem->simr_l,
+			iomem->sipnr_h, iomem->sipnr_l,
+			iomem->siexr);
+	}
+	poperror();
+	free(cb);
+
+	/* Irqi */
+	return n;
+}
+
+static void
+interrupt(Ureg*, void *arg)
+{
+	Irqconfig **pic, *ic;
+	int irq;
+
+	pic = arg;
+	irq = pic - irqconfig;
+	if (irq <= 0 || irq > nelem(irqdir)){
+		print("Unexpected interrupt: %d\n", irq);
+		return;
+	}
+	if (irq <= Qirq7)
+		iomem->sipnr_h |= 0x8000 >> irq;	/* Clear the interrupt */
+	ilock(&irqlock);
+	for(ic = *pic; ic; ic = ic->next){
+		ic->interrupts++;
+		wakeup(&ic->r);
+	}
+	iunlock(&irqlock);
+}
+
+Dev irqdevtab = {
+	'b',
+	"irq",
+
+	devreset,
+	devinit,
+	devshutdown,
+	irqattach,
+	irqwalk,
+	irqstat,
+	irqopen,
+	devcreate,
+	irqclose,
+	irqread,
+	devbread,
+	irqwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};

+ 2113 - 0
sys/src/9/ppc/devtls.c

@@ -0,0 +1,2113 @@
+/*
+ *  devtls - record layer for transport layer security 1.0 and secure sockets layer 3.0
+ */
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+#include	<libsec.h>
+
+typedef struct OneWay	OneWay;
+typedef struct Secret		Secret;
+typedef struct TlsRec	TlsRec;
+typedef struct TlsErrs	TlsErrs;
+
+enum {
+	Statlen=	1024,		/* max. length of status or stats message */
+	/* buffer limits */
+	MaxRecLen		= 1<<14,	/* max payload length of a record layer message */
+	MaxCipherRecLen	= MaxRecLen + 2048,
+	RecHdrLen		= 5,
+	MaxMacLen		= SHA1dlen,
+
+	/* protocol versions we can accept */
+	TLSVersion		= 0x0301,
+	SSL3Version		= 0x0300,
+	ProtocolVersion	= 0x0301,	/* maximum version we speak */
+	MinProtoVersion	= 0x0300,	/* limits on version we accept */
+	MaxProtoVersion	= 0x03ff,
+
+	/* connection states */
+	SHandshake	= 1 << 0,	/* doing handshake */
+	SOpen		= 1 << 1,	/* application data can be sent */
+	SRClose		= 1 << 2,	/* remote side has closed down */
+	SLClose		= 1 << 3,	/* sent a close notify alert */
+	SAlert		= 1 << 5,	/* sending or sent a fatal alert */
+	SError		= 1 << 6,	/* some sort of error has occured */
+	SClosed		= 1 << 7,	/* it is all over */
+
+	/* record types */
+	RChangeCipherSpec = 20,
+	RAlert,
+	RHandshake,
+	RApplication,
+
+	SSL2ClientHello = 1,
+	HSSL2ClientHello = 9,  /* local convention;  see tlshand.c */
+
+	/* alerts */
+	ECloseNotify 			= 0,
+	EUnexpectedMessage 	= 10,
+	EBadRecordMac 		= 20,
+	EDecryptionFailed 		= 21,
+	ERecordOverflow 		= 22,
+	EDecompressionFailure 	= 30,
+	EHandshakeFailure 		= 40,
+	ENoCertificate 			= 41,
+	EBadCertificate 		= 42,
+	EUnsupportedCertificate 	= 43,
+	ECertificateRevoked 		= 44,
+	ECertificateExpired 		= 45,
+	ECertificateUnknown 	= 46,
+	EIllegalParameter 		= 47,
+	EUnknownCa 			= 48,
+	EAccessDenied 		= 49,
+	EDecodeError 			= 50,
+	EDecryptError 			= 51,
+	EExportRestriction 		= 60,
+	EProtocolVersion 		= 70,
+	EInsufficientSecurity 	= 71,
+	EInternalError 			= 80,
+	EUserCanceled 			= 90,
+	ENoRenegotiation 		= 100,
+
+	EMAX = 256
+};
+
+struct Secret
+{
+	char		*encalg;	/* name of encryption alg */
+	char		*hashalg;	/* name of hash alg */
+	int		(*enc)(Secret*, uchar*, int);
+	int		(*dec)(Secret*, uchar*, int);
+	int		(*unpad)(uchar*, int, int);
+	DigestState	*(*mac)(uchar*, ulong, uchar*, ulong, uchar*, DigestState*);
+	int		block;		/* encryption block len, 0 if none */
+	int		maclen;
+	void		*enckey;
+	uchar	mackey[MaxMacLen];
+};
+
+struct OneWay
+{
+	QLock		io;		/* locks io access */
+	QLock		seclock;	/* locks secret paramaters */
+	ulong		seq;
+	Secret		*sec;		/* cipher in use */
+	Secret		*new;		/* cipher waiting for enable */
+};
+
+struct TlsRec
+{
+	Chan	*c;				/* io channel */
+	int		ref;				/* serialized by tdlock for atomic destroy */
+	int		version;			/* version of the protocol we are speaking */
+	char		verset;			/* version has been set */
+	char		opened;			/* opened command every issued? */
+	char		err[ERRMAX];		/* error message to return to handshake requests */
+	vlong	handin;			/* bytes communicated by the record layer */
+	vlong	handout;
+	vlong	datain;
+	vlong	dataout;
+
+	Lock		statelk;
+	int		state;
+
+	/* record layer mac functions for different protocol versions */
+	void		(*packMac)(Secret*, uchar*, uchar*, uchar*, uchar*, int, uchar*);
+
+	/* input side -- protected by in.io */
+	OneWay		in;
+	Block		*processed;	/* next bunch of application data */
+	Block		*unprocessed;	/* data read from c but not parsed into records */
+
+	/* handshake queue */
+	Lock		hqlock;			/* protects hqref, alloc & free of handq, hprocessed */
+	int		hqref;
+	Queue		*handq;		/* queue of handshake messages */
+	Block		*hprocessed;	/* remainder of last block read from handq */
+	QLock		hqread;		/* protects reads for hprocessed, handq */
+
+	/* output side */
+	OneWay		out;
+
+	/* protections */
+	char		*user;
+	int		perm;
+};
+
+struct TlsErrs{
+	int	err;
+	int	sslerr;
+	int	tlserr;
+	int	fatal;
+	char	*msg;
+};
+
+static TlsErrs tlserrs[] = {
+	{ECloseNotify,			ECloseNotify,			ECloseNotify,			0, 	"close notify"},
+	{EUnexpectedMessage,	EUnexpectedMessage,	EUnexpectedMessage, 	1, "unexpected message"},
+	{EBadRecordMac,		EBadRecordMac,		EBadRecordMac, 		1, "bad record mac"},
+	{EDecryptionFailed,		EIllegalParameter,		EDecryptionFailed,		1, "decryption failed"},
+	{ERecordOverflow,		EIllegalParameter,		ERecordOverflow,		1, "record too long"},
+	{EDecompressionFailure,	EDecompressionFailure,	EDecompressionFailure,	1, "decompression failed"},
+	{EHandshakeFailure,		EHandshakeFailure,		EHandshakeFailure,		1, "could not negotiate acceptable security paramters"},
+	{ENoCertificate,		ENoCertificate,			ECertificateUnknown,	1, "no appropriate certificate available"},
+	{EBadCertificate,		EBadCertificate,		EBadCertificate,		1, "corrupted or invalid certificate"},
+	{EUnsupportedCertificate,	EUnsupportedCertificate,	EUnsupportedCertificate,	1, "unsupported certificate type"},
+	{ECertificateRevoked,	ECertificateRevoked,		ECertificateRevoked,		1, "revoked certificate"},
+	{ECertificateExpired,		ECertificateExpired,		ECertificateExpired,		1, "expired certificate"},
+	{ECertificateUnknown,	ECertificateUnknown,	ECertificateUnknown,	1, "unacceptable certificate"},
+	{EIllegalParameter,		EIllegalParameter,		EIllegalParameter,		1, "illegal parameter"},
+	{EUnknownCa,			EHandshakeFailure,		EUnknownCa,			1, "unknown certificate authority"},
+	{EAccessDenied,		EHandshakeFailure,		EAccessDenied,		1, "access denied"},
+	{EDecodeError,			EIllegalParameter,		EDecodeError,			1, "error decoding message"},
+	{EDecryptError,			EIllegalParameter,		EDecryptError,			1, "error decrypting message"},
+	{EExportRestriction,		EHandshakeFailure,		EExportRestriction,		1, "export restriction violated"},
+	{EProtocolVersion,		EIllegalParameter,		EProtocolVersion,		1, "protocol version not supported"},
+	{EInsufficientSecurity,	EHandshakeFailure,		EInsufficientSecurity,	1, "stronger security routines required"},
+	{EInternalError,			EHandshakeFailure,		EInternalError,			1, "internal error"},
+	{EUserCanceled,		ECloseNotify,			EUserCanceled,			0, "handshake canceled by user"},
+	{ENoRenegotiation,		EUnexpectedMessage,	ENoRenegotiation,		0, "no renegotiation"},
+};
+
+enum
+{
+	/* max. open tls connections */
+	MaxTlsDevs	= 1024
+};
+
+static	Lock	tdlock;
+static	int	tdhiwat;
+static	int	maxtlsdevs = 128;
+static	TlsRec	**tlsdevs;
+static	char	**trnames;
+static	char	*encalgs;
+static	char	*hashalgs;
+
+enum{
+	Qtopdir		= 1,	/* top level directory */
+	Qprotodir,
+	Qclonus,
+	Qencalgs,
+	Qhashalgs,
+	Qconvdir,		/* directory for a conversation */
+	Qdata,
+	Qctl,
+	Qhand,
+	Qstatus,
+	Qstats,
+};
+
+#define TYPE(x) 	((x).path & 0xf)
+#define CONV(x) 	(((x).path >> 5)&(MaxTlsDevs-1))
+#define QID(c, y) 	(((c)<<5) | (y))
+
+static void	checkstate(TlsRec *, int, int);
+static void	ensure(TlsRec*, Block**, int);
+static void	consume(Block**, uchar*, int);
+static Chan*	buftochan(char*);
+static void	tlshangup(TlsRec*);
+static void	tlsError(TlsRec*, char *);
+static void	alertHand(TlsRec*, char *);
+static TlsRec	*newtls(Chan *c);
+static TlsRec	*mktlsrec(void);
+static DigestState*sslmac_md5(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s);
+static DigestState*sslmac_sha1(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s);
+static DigestState*nomac(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s);
+static void	sslPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac);
+static void	tlsPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac);
+static void	put64(uchar *p, vlong x);
+static void	put32(uchar *p, u32int);
+static void	put24(uchar *p, int);
+static void	put16(uchar *p, int);
+static u32int	get32(uchar *p);
+static int	get16(uchar *p);
+static void	tlsSetState(TlsRec *tr, int new, int old);
+static void	rcvAlert(TlsRec *tr, int err);
+static void	sendAlert(TlsRec *tr, int err);
+static void	rcvError(TlsRec *tr, int err, char *msg, ...);
+static int	rc4enc(Secret *sec, uchar *buf, int n);
+static int	des3enc(Secret *sec, uchar *buf, int n);
+static int	des3dec(Secret *sec, uchar *buf, int n);
+static int	noenc(Secret *sec, uchar *buf, int n);
+static int	sslunpad(uchar *buf, int n, int block);
+static int	tlsunpad(uchar *buf, int n, int block);
+static void	freeSec(Secret *sec);
+static char	*tlsstate(int s);
+
+#pragma	varargck	argpos	rcvError	3
+
+static char *tlsnames[] = {
+[Qclonus]		"clone",
+[Qencalgs]	"encalgs",
+[Qhashalgs]	"hashalgs",
+[Qdata]		"data",
+[Qctl]		"ctl",
+[Qhand]		"hand",
+[Qstatus]		"status",
+[Qstats]		"stats",
+};
+
+static int convdir[] = { Qctl, Qdata, Qhand, Qstatus, Qstats };
+
+static int
+tlsgen(Chan *c, char*, Dirtab *, int, int s, Dir *dp)
+{
+	Qid q;
+	TlsRec *tr;
+	char *name, *nm;
+	int perm, t;
+
+	q.vers = 0;
+	q.type = QTFILE;
+
+	t = TYPE(c->qid);
+	switch(t) {
+	case Qtopdir:
+		if(s == DEVDOTDOT){
+			q.path = QID(0, Qtopdir);
+			q.type = QTDIR;
+			devdir(c, q, "#a", 0, eve, 0555, dp);
+			return 1;
+		}
+		if(s > 0)
+			return -1;
+		q.path = QID(0, Qprotodir);
+		q.type = QTDIR;
+		devdir(c, q, "tls", 0, eve, 0555, dp);
+		return 1;
+	case Qprotodir:
+		if(s == DEVDOTDOT){
+			q.path = QID(0, Qtopdir);
+			q.type = QTDIR;
+			devdir(c, q, ".", 0, eve, 0555, dp);
+			return 1;
+		}
+		if(s < 3){
+			switch(s) {
+			default:
+				return -1;
+			case 0:
+				q.path = QID(0, Qclonus);
+				break;
+			case 1:
+				q.path = QID(0, Qencalgs);
+				break;
+			case 2:
+				q.path = QID(0, Qhashalgs);
+				break;
+			}
+			perm = 0444;
+			if(TYPE(q) == Qclonus)
+				perm = 0555;
+			devdir(c, q, tlsnames[TYPE(q)], 0, eve, perm, dp);
+			return 1;
+		}
+		s -= 3;
+		if(s >= tdhiwat)
+			return -1;
+		q.path = QID(s, Qconvdir);
+		q.type = QTDIR;
+		lock(&tdlock);
+		tr = tlsdevs[s];
+		if(tr != nil)
+			nm = tr->user;
+		else
+			nm = eve;
+		if((name = trnames[s]) == nil) {
+			name = trnames[s] = smalloc(16);
+			sprint(name, "%d", s);
+		}
+		devdir(c, q, name, 0, nm, 0555, dp);
+		unlock(&tdlock);
+		return 1;
+	case Qconvdir:
+		if(s == DEVDOTDOT){
+			q.path = QID(0, Qprotodir);
+			q.type = QTDIR;
+			devdir(c, q, "tls", 0, eve, 0555, dp);
+			return 1;
+		}
+		if(s < 0 || s >= nelem(convdir))
+			return -1;
+		lock(&tdlock);
+		tr = tlsdevs[CONV(c->qid)];
+		if(tr != nil){
+			nm = tr->user;
+			perm = tr->perm;
+		}else{
+			perm = 0;
+			nm = eve;
+		}
+		t = convdir[s];
+		if(t == Qstatus || t == Qstats)
+			perm &= 0444;
+		q.path = QID(CONV(c->qid), t);
+		devdir(c, q, tlsnames[t], 0, nm, perm, dp);
+		unlock(&tdlock);
+		return 1;
+	case Qclonus:
+	case Qencalgs:
+	case Qhashalgs:
+		perm = 0444;
+		if(t == Qclonus)
+			perm = 0555;
+		devdir(c, c->qid, tlsnames[t], 0, eve, perm, dp);
+		return 1;
+	default:
+		lock(&tdlock);
+		tr = tlsdevs[CONV(c->qid)];
+		if(tr != nil){
+			nm = tr->user;
+			perm = tr->perm;
+		}else{
+			perm = 0;
+			nm = eve;
+		}
+		if(t == Qstatus || t == Qstats)
+			perm &= 0444;
+		devdir(c, c->qid, tlsnames[t], 0, nm, perm, dp);
+		unlock(&tdlock);
+		return 1;
+	}
+	return -1;
+}
+
+static Chan*
+tlsattach(char *spec)
+{
+	Chan *c;
+
+	c = devattach('a', spec);
+	c->qid.path = QID(0, Qtopdir);
+	c->qid.type = QTDIR;
+	c->qid.vers = 0;
+	return c;
+}
+
+static Walkqid*
+tlswalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, nil, 0, tlsgen);
+}
+
+static int
+tlsstat(Chan *c, uchar *db, int n)
+{
+	return devstat(c, db, n, nil, 0, tlsgen);
+}
+
+static Chan*
+tlsopen(Chan *c, int omode)
+{
+	TlsRec *tr, **pp;
+	int t, perm;
+
+	perm = 0;
+	omode &= 3;
+	switch(omode) {
+	case OREAD:
+		perm = 4;
+		break;
+	case OWRITE:
+		perm = 2;
+		break;
+	case ORDWR:
+		perm = 6;
+		break;
+	}
+
+	t = TYPE(c->qid);
+	switch(t) {
+	default:
+		panic("tlsopen");
+	case Qtopdir:
+	case Qprotodir:
+	case Qconvdir:
+		if(omode != OREAD)
+			error(Eperm);
+		break;
+	case Qclonus:
+		tr = newtls(c);
+		if(tr == nil)
+			error(Enodev);
+		break;
+	case Qctl:
+	case Qdata:
+	case Qhand:
+	case Qstatus:
+	case Qstats:
+		if((t == Qstatus || t == Qstats) && omode != OREAD)
+			error(Eperm);
+		if(waserror()) {
+			unlock(&tdlock);
+			nexterror();
+		}
+		lock(&tdlock);
+		pp = &tlsdevs[CONV(c->qid)];
+		tr = *pp;
+		if(tr == nil)
+			error("must open connection using clone");
+		if((perm & (tr->perm>>6)) != perm
+		&& (strcmp(up->user, tr->user) != 0
+		    || (perm & tr->perm) != perm))
+			error(Eperm);
+		if(t == Qhand){
+			if(waserror()){
+				unlock(&tr->hqlock);
+				nexterror();
+			}
+			lock(&tr->hqlock);
+			if(tr->handq != nil)
+				error(Einuse);
+			tr->handq = qopen(2 * MaxCipherRecLen, 0, nil, nil);
+			if(tr->handq == nil)
+				error("can't allocate handshake queue");
+			tr->hqref = 1;
+			unlock(&tr->hqlock);
+			poperror();
+		}
+		tr->ref++;
+		unlock(&tdlock);
+		poperror();
+		break;
+	case Qencalgs:
+	case Qhashalgs:
+		if(omode != OREAD)
+			error(Eperm);
+		break;
+	}
+	c->mode = openmode(omode);
+	c->flag |= COPEN;
+	c->offset = 0;
+	c->iounit = qiomaxatomic;
+	return c;
+}
+
+static int
+tlswstat(Chan *c, uchar *dp, int n)
+{
+	Dir *d;
+	TlsRec *tr;
+	int rv;
+
+	d = nil;
+	if(waserror()){
+		free(d);
+		unlock(&tdlock);
+		nexterror();
+	}
+
+	lock(&tdlock);
+	tr = tlsdevs[CONV(c->qid)];
+	if(tr == nil)
+		error(Ebadusefd);
+	if(strcmp(tr->user, up->user) != 0)
+		error(Eperm);
+
+	d = smalloc(n + sizeof *d);
+	rv = convM2D(dp, n, &d[0], (char*) &d[1]);
+	if(rv == 0)
+		error(Eshortstat);
+	if(!emptystr(d->uid))
+		kstrdup(&tr->user, d->uid);
+	if(d->mode != ~0UL)
+		tr->perm = d->mode;
+
+	free(d);
+	poperror();
+	unlock(&tdlock);
+
+	return rv;
+}
+
+static void
+dechandq(TlsRec *tr)
+{
+	lock(&tr->hqlock);
+	if(--tr->hqref == 0){
+		if(tr->handq != nil){
+			qfree(tr->handq);
+			tr->handq = nil;
+		}
+		if(tr->hprocessed != nil){
+			freeb(tr->hprocessed);
+			tr->hprocessed = nil;
+		}
+	}
+	unlock(&tr->hqlock);
+}
+
+static void
+tlsclose(Chan *c)
+{
+	TlsRec *tr;
+	int t;
+
+	t = TYPE(c->qid);
+	switch(t) {
+	case Qctl:
+	case Qdata:
+	case Qhand:
+	case Qstatus:
+	case Qstats:
+		if((c->flag & COPEN) == 0)
+			break;
+
+		tr = tlsdevs[CONV(c->qid)];
+		if(tr == nil)
+			break;
+
+		if(t == Qhand)
+			dechandq(tr);
+
+		lock(&tdlock);
+		if(--tr->ref > 0) {
+			unlock(&tdlock);
+			return;
+		}
+		tlsdevs[CONV(c->qid)] = nil;
+		unlock(&tdlock);
+
+		if(tr->c != nil && !waserror()){
+			checkstate(tr, 0, SOpen|SHandshake|SRClose);
+			sendAlert(tr, ECloseNotify);
+			poperror();
+		}
+		tlshangup(tr);
+		if(tr->c != nil)
+			cclose(tr->c);
+		freeSec(tr->in.sec);
+		freeSec(tr->in.new);
+		freeSec(tr->out.sec);
+		freeSec(tr->out.new);
+		free(tr->user);
+		free(tr);
+		break;
+	}
+}
+
+/*
+ *  make sure we have at least 'n' bytes in list 'l'
+ */
+static void
+ensure(TlsRec *s, Block **l, int n)
+{
+	int sofar, i;
+	Block *b, *bl;
+
+	sofar = 0;
+	for(b = *l; b; b = b->next){
+		sofar += BLEN(b);
+		if(sofar >= n)
+			return;
+		l = &b->next;
+	}
+
+	while(sofar < n){
+		bl = devtab[s->c->type]->bread(s->c, MaxCipherRecLen + RecHdrLen, 0);
+		if(bl == 0)
+			error(Ehungup);
+		*l = bl;
+		i = 0;
+		for(b = bl; b; b = b->next){
+			i += BLEN(b);
+			l = &b->next;
+		}
+		if(i == 0)
+			error(Ehungup);
+		sofar += i;
+	}
+}
+
+/*
+ *  copy 'n' bytes from 'l' into 'p' and free
+ *  the bytes in 'l'
+ */
+static void
+consume(Block **l, uchar *p, int n)
+{
+	Block *b;
+	int i;
+
+	for(; *l && n > 0; n -= i){
+		b = *l;
+		i = BLEN(b);
+		if(i > n)
+			i = n;
+		memmove(p, b->rp, i);
+		b->rp += i;
+		p += i;
+		if(BLEN(b) < 0)
+			panic("consume");
+		if(BLEN(b))
+			break;
+		*l = b->next;
+		freeb(b);
+	}
+}
+
+/*
+ *  give back n bytes
+ */
+static void
+regurgitate(TlsRec *s, uchar *p, int n)
+{
+	Block *b;
+
+	if(n <= 0)
+		return;
+	b = s->unprocessed;
+	if(s->unprocessed == nil || b->rp - b->base < n) {
+		b = allocb(n);
+		memmove(b->wp, p, n);
+		b->wp += n;
+		b->next = s->unprocessed;
+		s->unprocessed = b;
+	} else {
+		b->rp -= n;
+		memmove(b->rp, p, n);
+	}
+}
+
+/*
+ *  remove at most n bytes from the queue
+ */
+static Block*
+qgrab(Block **l, int n)
+{
+	Block *bb, *b;
+	int i;
+
+	b = *l;
+	if(BLEN(b) == n){
+		*l = b->next;
+		b->next = nil;
+		return b;
+	}
+
+	i = 0;
+	for(bb = b; bb != nil && i < n; bb = bb->next)
+		i += BLEN(bb);
+	if(i > n)
+		i = n;
+
+	bb = allocb(i);
+	consume(l, bb->wp, i);
+	bb->wp += i;
+	return bb;
+}
+
+static void
+tlsclosed(TlsRec *tr, int new)
+{
+	lock(&tr->statelk);
+	if(tr->state == SOpen || tr->state == SHandshake)
+		tr->state = new;
+	else if((new | tr->state) == (SRClose|SLClose))
+		tr->state = SClosed;
+	unlock(&tr->statelk);
+	alertHand(tr, "close notify");
+}
+
+/*
+ *  read and process one tls record layer message
+ *  must be called with tr->in.io held
+ *  We can't let Eintrs lose data, since doing so will get
+ *  us out of sync with the sender and break the reliablity
+ *  of the channel.  Eintr only happens during the reads in
+ *  consume.  Therefore we put back any bytes consumed before
+ *  the last call to ensure.
+ */
+static void
+tlsrecread(TlsRec *tr)
+{
+	OneWay *volatile in;
+	Block *volatile b;
+	uchar *p, seq[8], header[RecHdrLen], hmac[MD5dlen];
+	int volatile nconsumed;
+	int len, type, ver, unpad_len;
+
+	nconsumed = 0;
+	if(waserror()){
+		if(strcmp(up->errstr, Eintr) == 0 && !waserror()){
+			regurgitate(tr, header, nconsumed);
+			poperror();
+		}else
+			tlsError(tr, "channel error");
+		nexterror();
+	}
+	ensure(tr, &tr->unprocessed, RecHdrLen);
+	consume(&tr->unprocessed, header, RecHdrLen);
+	nconsumed = RecHdrLen;
+
+	if((tr->handin == 0) && (header[0] & 0x80)){
+		/* Cope with an SSL3 ClientHello expressed in SSL2 record format.
+			This is sent by some clients that we must interoperate
+			with, such as Java's JSSE and Microsoft's Internet Explorer. */
+		len = (get16(header) & ~0x8000) - 3;
+		type = header[2];
+		ver = get16(header + 3);
+		if(type != SSL2ClientHello || len < 22)
+			rcvError(tr, EProtocolVersion, "invalid initial SSL2-like message");
+	}else{  /* normal SSL3 record format */
+		type = header[0];
+		ver = get16(header+1);
+		len = get16(header+3);
+	}
+	if(ver != tr->version && (tr->verset || ver < MinProtoVersion || ver > MaxProtoVersion))
+		rcvError(tr, EProtocolVersion, "devtls expected ver=%x%s, saw (len=%d) type=%x ver=%x '%.12s'",
+			tr->version, tr->verset?"/set":"", len, type, ver, (char*)header);
+	if(len > MaxCipherRecLen || len < 0)
+		rcvError(tr, ERecordOverflow, "record message too long %d", len);
+	ensure(tr, &tr->unprocessed, len);
+	nconsumed = 0;
+	poperror();
+
+	/*
+	 * If an Eintr happens after this, we'll get out of sync.
+	 * Make sure nothing we call can sleep.
+	 * Errors are ok, as they kill the connection.
+	 * Luckily, allocb won't sleep, it'll just error out.
+	 */
+	b = nil;
+	if(waserror()){
+		if(b != nil)
+			freeb(b);
+		tlsError(tr, "channel error");
+		nexterror();
+	}
+	b = qgrab(&tr->unprocessed, len);
+
+	in = &tr->in;
+	if(waserror()){
+		qunlock(&in->seclock);
+		nexterror();
+	}
+	qlock(&in->seclock);
+	p = b->rp;
+	if(in->sec != nil) {
+		/* to avoid Canvel-Hiltgen-Vaudenay-Vuagnoux attack, all errors here
+		        should look alike, including timing of the response. */
+		unpad_len = (*in->sec->dec)(in->sec, p, len);
+		if(unpad_len > in->sec->maclen)
+			len = unpad_len - in->sec->maclen;
+
+		/* update length */
+		put16(header+3, len);
+		put64(seq, in->seq);
+		in->seq++;
+		(*tr->packMac)(in->sec, in->sec->mackey, seq, header, p, len, hmac);
+		if(unpad_len <= in->sec->maclen || memcmp(hmac, p+len, in->sec->maclen) != 0)
+			rcvError(tr, EBadRecordMac, "record mac mismatch");
+		b->wp = b->rp + len;
+	}
+	qunlock(&in->seclock);
+	poperror();
+	if(len <= 0)
+		rcvError(tr, EDecodeError, "runt record message");
+
+	switch(type) {
+	default:
+		rcvError(tr, EIllegalParameter, "invalid record message 0x%x", type);
+		break;
+	case RChangeCipherSpec:
+		if(len != 1 || p[0] != 1)
+			rcvError(tr, EDecodeError, "invalid change cipher spec");
+		qlock(&in->seclock);
+		if(in->new == nil){
+			qunlock(&in->seclock);
+			rcvError(tr, EUnexpectedMessage, "unexpected change cipher spec");
+		}
+		freeSec(in->sec);
+		in->sec = in->new;
+		in->new = nil;
+		in->seq = 0;
+		qunlock(&in->seclock);
+		break;
+	case RAlert:
+		if(len != 2)
+			rcvError(tr, EDecodeError, "invalid alert");
+		if(p[0] == 2)
+			rcvAlert(tr, p[1]);
+		if(p[0] != 1)
+			rcvError(tr, EIllegalParameter, "invalid alert fatal code");
+
+		/*
+		 * propate non-fatal alerts to handshaker
+		 */
+		if(p[1] == ECloseNotify) {
+			tlsclosed(tr, SRClose);
+			if(tr->opened)
+				error("tls hungup");
+			error("close notify");
+		}
+		if(p[1] == ENoRenegotiation)
+			alertHand(tr, "no renegotiation");
+		else if(p[1] == EUserCanceled)
+			alertHand(tr, "handshake canceled by user");
+		else
+			rcvError(tr, EIllegalParameter, "invalid alert code");
+		break;
+	case RHandshake:
+		/*
+		 * don't worry about dropping the block
+		 * qbwrite always queues even if flow controlled and interrupted.
+		 *
+		 * if there isn't any handshaker, ignore the request,
+		 * but notify the other side we are doing so.
+		 */
+		lock(&tr->hqlock);
+		if(tr->handq != nil){
+			tr->hqref++;
+			unlock(&tr->hqlock);
+			if(waserror()){
+				dechandq(tr);
+				nexterror();
+			}
+			b = padblock(b, 1);
+			*b->rp = RHandshake;
+			qbwrite(tr->handq, b);
+			b = nil;
+			poperror();
+			dechandq(tr);
+		}else{
+			unlock(&tr->hqlock);
+			if(tr->verset && tr->version != SSL3Version && !waserror()){
+				sendAlert(tr, ENoRenegotiation);
+				poperror();
+			}
+		}
+		break;
+	case SSL2ClientHello:
+		lock(&tr->hqlock);
+		if(tr->handq != nil){
+			tr->hqref++;
+			unlock(&tr->hqlock);
+			if(waserror()){
+				dechandq(tr);
+				nexterror();
+			}
+			/* Pass the SSL2 format data, so that the handshake code can compute
+				the correct checksums.  HSSL2ClientHello = HandshakeType 9 is
+				unused in RFC2246. */
+			b = padblock(b, 8);
+			b->rp[0] = RHandshake;
+			b->rp[1] = HSSL2ClientHello;
+			put24(&b->rp[2], len+3);
+			b->rp[5] = SSL2ClientHello;
+			put16(&b->rp[6], ver);
+			qbwrite(tr->handq, b);
+			b = nil;
+			poperror();
+			dechandq(tr);
+		}else{
+			unlock(&tr->hqlock);
+			if(tr->verset && tr->version != SSL3Version && !waserror()){
+				sendAlert(tr, ENoRenegotiation);
+				poperror();
+			}
+		}
+		break;
+	case RApplication:
+		if(!tr->opened)
+			rcvError(tr, EUnexpectedMessage, "application message received before handshake completed");
+		tr->processed = b;
+		b = nil;
+		break;
+	}
+	if(b != nil)
+		freeb(b);
+	poperror();
+}
+
+/*
+ * got a fatal alert message
+ */
+static void
+rcvAlert(TlsRec *tr, int err)
+{
+	char *s;
+	int i;
+
+	s = "unknown error";
+	for(i=0; i < nelem(tlserrs); i++){
+		if(tlserrs[i].err == err){
+			s = tlserrs[i].msg;
+			break;
+		}
+	}
+
+	tlsError(tr, s);
+	if(!tr->opened)
+		error(s);
+	error("tls error");
+}
+
+/*
+ * found an error while decoding the input stream
+ */
+static void
+rcvError(TlsRec *tr, int err, char *fmt, ...)
+{
+	char msg[ERRMAX];
+	va_list arg;
+
+	va_start(arg, fmt);
+	vseprint(msg, msg+sizeof(msg), fmt, arg);
+	va_end(arg);
+
+	sendAlert(tr, err);
+
+	if(!tr->opened)
+		error(msg);
+	error("tls error");
+}
+
+/*
+ * make sure the next hand operation returns with a 'msg' error
+ */
+static void
+alertHand(TlsRec *tr, char *msg)
+{
+	Block *b;
+	int n;
+
+	lock(&tr->hqlock);
+	if(tr->handq == nil){
+		unlock(&tr->hqlock);
+		return;
+	}
+	tr->hqref++;
+	unlock(&tr->hqlock);
+
+	n = strlen(msg);
+	if(waserror()){
+		dechandq(tr);
+		nexterror();
+	}
+	b = allocb(n + 2);
+	*b->wp++ = RAlert;
+	memmove(b->wp, msg, n + 1);
+	b->wp += n + 1;
+
+	qbwrite(tr->handq, b);
+
+	poperror();
+	dechandq(tr);
+}
+
+static void
+checkstate(TlsRec *tr, int ishand, int ok)
+{
+	int state;
+
+	lock(&tr->statelk);
+	state = tr->state;
+	unlock(&tr->statelk);
+	if(state & ok)
+		return;
+	switch(state){
+	case SHandshake:
+	case SOpen:
+		break;
+	case SError:
+	case SAlert:
+		if(ishand)
+			error(tr->err);
+		error("tls error");
+	case SRClose:
+	case SLClose:
+	case SClosed:
+		error("tls hungup");
+	}
+	error("tls improperly configured");
+}
+
+static Block*
+tlsbread(Chan *c, long n, ulong offset)
+{
+	int ty;
+	Block *b;
+	TlsRec *volatile tr;
+
+	ty = TYPE(c->qid);
+	switch(ty) {
+	default:
+		return devbread(c, n, offset);
+	case Qhand:
+	case Qdata:
+		break;
+	}
+
+	tr = tlsdevs[CONV(c->qid)];
+	if(tr == nil)
+		panic("tlsbread");
+
+	if(waserror()){
+		qunlock(&tr->in.io);
+		nexterror();
+	}
+	qlock(&tr->in.io);
+	if(ty == Qdata){
+		checkstate(tr, 0, SOpen);
+		while(tr->processed == nil)
+			tlsrecread(tr);
+
+		/* return at most what was asked for */
+		b = qgrab(&tr->processed, n);
+		qunlock(&tr->in.io);
+		poperror();
+		tr->datain += BLEN(b);
+	}else{
+		checkstate(tr, 1, SOpen|SHandshake|SLClose);
+
+		/*
+		 * it's ok to look at state without the lock
+		 * since it only protects reading records,
+		 * and we have that tr->in.io held.
+		 */
+		while(!tr->opened && tr->hprocessed == nil && !qcanread(tr->handq))
+			tlsrecread(tr);
+
+		qunlock(&tr->in.io);
+		poperror();
+
+		if(waserror()){
+			qunlock(&tr->hqread);
+			nexterror();
+		}
+		qlock(&tr->hqread);
+		if(tr->hprocessed == nil){
+			b = qbread(tr->handq, MaxRecLen + 1);
+			if(*b->rp++ == RAlert){
+				strecpy(up->errstr, up->errstr+ERRMAX, (char*)b->rp);
+				freeb(b);
+				nexterror();
+			}
+			tr->hprocessed = b;
+		}
+		b = qgrab(&tr->hprocessed, n);
+		poperror();
+		qunlock(&tr->hqread);
+		tr->handin += BLEN(b);
+	}
+
+	return b;
+}
+
+static long
+tlsread(Chan *c, void *a, long n, vlong off)
+{
+	Block *volatile b;
+	Block *nb;
+	uchar *va;
+	int i, ty;
+	char *buf, *s, *e;
+	ulong offset = off;
+	TlsRec * tr;
+
+	if(c->qid.type & QTDIR)
+		return devdirread(c, a, n, 0, 0, tlsgen);
+
+	tr = tlsdevs[CONV(c->qid)];
+	ty = TYPE(c->qid);
+	switch(ty) {
+	default:
+		error(Ebadusefd);
+	case Qstatus:
+		buf = smalloc(Statlen);
+		qlock(&tr->in.seclock);
+		qlock(&tr->out.seclock);
+		s = buf;
+		e = buf + Statlen;
+		s = seprint(s, e, "State: %s\n", tlsstate(tr->state));
+		s = seprint(s, e, "Version: 0x%x\n", tr->version);
+		if(tr->in.sec != nil)
+			s = seprint(s, e, "EncIn: %s\nHashIn: %s\n", tr->in.sec->encalg, tr->in.sec->hashalg);
+		if(tr->in.new != nil)
+			s = seprint(s, e, "NewEncIn: %s\nNewHashIn: %s\n", tr->in.new->encalg, tr->in.new->hashalg);
+		if(tr->out.sec != nil)
+			s = seprint(s, e, "EncOut: %s\nHashOut: %s\n", tr->out.sec->encalg, tr->out.sec->hashalg);
+		if(tr->out.new != nil)
+			seprint(s, e, "NewEncOut: %s\nNewHashOut: %s\n", tr->out.new->encalg, tr->out.new->hashalg);
+		qunlock(&tr->in.seclock);
+		qunlock(&tr->out.seclock);
+		n = readstr(offset, a, n, buf);
+		free(buf);
+		return n;
+	case Qstats:
+		buf = smalloc(Statlen);
+		s = buf;
+		e = buf + Statlen;
+		s = seprint(s, e, "DataIn: %lld\n", tr->datain);
+		s = seprint(s, e, "DataOut: %lld\n", tr->dataout);
+		s = seprint(s, e, "HandIn: %lld\n", tr->handin);
+		seprint(s, e, "HandOut: %lld\n", tr->handout);
+		n = readstr(offset, a, n, buf);
+		free(buf);
+		return n;
+	case Qctl:
+		buf = smalloc(Statlen);
+		snprint(buf, Statlen, "%llud", CONV(c->qid));
+		n = readstr(offset, a, n, buf);
+		free(buf);
+		return n;
+	case Qdata:
+	case Qhand:
+		b = tlsbread(c, n, offset);
+		break;
+	case Qencalgs:
+		return readstr(offset, a, n, encalgs);
+	case Qhashalgs:
+		return readstr(offset, a, n, hashalgs);
+	}
+
+	if(waserror()){
+		freeblist(b);
+		nexterror();
+	}
+
+	n = 0;
+	va = a;
+	for(nb = b; nb; nb = nb->next){
+		i = BLEN(nb);
+		memmove(va+n, nb->rp, i);
+		n += i;
+	}
+
+	freeblist(b);
+	poperror();
+
+	return n;
+}
+
+/*
+ *  write a block in tls records
+ */
+static void
+tlsrecwrite(TlsRec *tr, int type, Block *b)
+{
+	Block *volatile bb;
+	Block *nb;
+	uchar *p, seq[8];
+	OneWay *volatile out;
+	int n, maclen, pad, ok;
+
+	out = &tr->out;
+	bb = b;
+	if(waserror()){
+		qunlock(&out->io);
+		if(bb != nil)
+			freeb(bb);
+		nexterror();
+	}
+	qlock(&out->io);
+
+	ok = SHandshake|SOpen|SRClose;
+	if(type == RAlert)
+		ok |= SAlert;
+	while(bb != nil){
+		checkstate(tr, type != RApplication, ok);
+
+		/*
+		 * get at most one maximal record's input,
+		 * with padding on the front for header and
+		 * back for mac and maximal block padding.
+		 */
+		if(waserror()){
+			qunlock(&out->seclock);
+			nexterror();
+		}
+		qlock(&out->seclock);
+		maclen = 0;
+		pad = 0;
+		if(out->sec != nil){
+			maclen = out->sec->maclen;
+			pad = maclen + out->sec->block;
+		}
+		n = BLEN(bb);
+		if(n > MaxRecLen){
+			n = MaxRecLen;
+			nb = allocb(n + pad + RecHdrLen);
+			memmove(nb->wp + RecHdrLen, bb->rp, n);
+			bb->rp += n;
+		}else{
+			/*
+			 * carefully reuse bb so it will get freed if we're out of memory
+			 */
+			bb = padblock(bb, RecHdrLen);
+			if(pad)
+				nb = padblock(bb, -pad);
+			else
+				nb = bb;
+			bb = nil;
+		}
+
+		p = nb->rp;
+		p[0] = type;
+		put16(p+1, tr->version);
+		put16(p+3, n);
+
+		if(out->sec != nil){
+			put64(seq, out->seq);
+			out->seq++;
+			(*tr->packMac)(out->sec, out->sec->mackey, seq, p, p + RecHdrLen, n, p + RecHdrLen + n);
+			n += maclen;
+
+			/* encrypt */
+			n = (*out->sec->enc)(out->sec, p + RecHdrLen, n);
+			nb->wp = p + RecHdrLen + n;
+
+			/* update length */
+			put16(p+3, n);
+		}
+		if(type == RChangeCipherSpec){
+			if(out->new == nil)
+				error("change cipher without a new cipher");
+			freeSec(out->sec);
+			out->sec = out->new;
+			out->new = nil;
+			out->seq = 0;
+		}
+		qunlock(&out->seclock);
+		poperror();
+
+		/*
+		 * if bwrite error's, we assume the block is queued.
+		 * if not, we're out of sync with the receiver and will not recover.
+		 */
+		if(waserror()){
+			if(strcmp(up->errstr, "interrupted") != 0)
+				tlsError(tr, "channel error");
+			nexterror();
+		}
+		devtab[tr->c->type]->bwrite(tr->c, nb, 0);
+		poperror();
+	}
+	qunlock(&out->io);
+	poperror();
+}
+
+static long
+tlsbwrite(Chan *c, Block *b, ulong offset)
+{
+	int ty;
+	ulong n;
+	TlsRec *tr;
+
+	n = BLEN(b);
+
+	tr = tlsdevs[CONV(c->qid)];
+	if(tr == nil)
+		panic("tlsbread");
+
+	ty = TYPE(c->qid);
+	switch(ty) {
+	default:
+		return devbwrite(c, b, offset);
+	case Qhand:
+		tlsrecwrite(tr, RHandshake, b);
+		tr->handout += n;
+		break;
+	case Qdata:
+		checkstate(tr, 0, SOpen);
+		tlsrecwrite(tr, RApplication, b);
+		tr->dataout += n;
+		break;
+	}
+
+	return n;
+}
+
+typedef struct Hashalg Hashalg;
+struct Hashalg
+{
+	char	*name;
+	int	maclen;
+	void	(*initkey)(Hashalg *, int, Secret *, uchar*);
+};
+
+static void
+initmd5key(Hashalg *ha, int version, Secret *s, uchar *p)
+{
+	s->maclen = ha->maclen;
+	if(version == SSL3Version)
+		s->mac = sslmac_md5;
+	else
+		s->mac = hmac_md5;
+	memmove(s->mackey, p, ha->maclen);
+}
+
+static void
+initclearmac(Hashalg *, int, Secret *s, uchar *)
+{
+	s->maclen = 0;
+	s->mac = nomac;
+}
+
+static void
+initsha1key(Hashalg *ha, int version, Secret *s, uchar *p)
+{
+	s->maclen = ha->maclen;
+	if(version == SSL3Version)
+		s->mac = sslmac_sha1;
+	else
+		s->mac = hmac_sha1;
+	memmove(s->mackey, p, ha->maclen);
+}
+
+static Hashalg hashtab[] =
+{
+	{ "clear", 0, initclearmac, },
+	{ "md5", MD5dlen, initmd5key, },
+	{ "sha1", SHA1dlen, initsha1key, },
+	{ 0 }
+};
+
+static Hashalg*
+parsehashalg(char *p)
+{
+	Hashalg *ha;
+
+	for(ha = hashtab; ha->name; ha++)
+		if(strcmp(p, ha->name) == 0)
+			return ha;
+	error("unsupported hash algorithm");
+	return nil;
+}
+
+typedef struct Encalg Encalg;
+struct Encalg
+{
+	char	*name;
+	int	keylen;
+	int	ivlen;
+	void	(*initkey)(Encalg *ea, Secret *, uchar*, uchar*);
+};
+
+static void
+initRC4key(Encalg *ea, Secret *s, uchar *p, uchar *)
+{
+	s->enckey = smalloc(sizeof(RC4state));
+	s->enc = rc4enc;
+	s->dec = rc4enc;
+	s->block = 0;
+	setupRC4state(s->enckey, p, ea->keylen);
+}
+
+static void
+initDES3key(Encalg *, Secret *s, uchar *p, uchar *iv)
+{
+	s->enckey = smalloc(sizeof(DES3state));
+	s->enc = des3enc;
+	s->dec = des3dec;
+	s->block = 8;
+	setupDES3state(s->enckey, (uchar(*)[8])p, iv);
+}
+
+static void
+initclearenc(Encalg *, Secret *s, uchar *, uchar *)
+{
+	s->enc = noenc;
+	s->dec = noenc;
+	s->block = 0;
+}
+
+static Encalg encrypttab[] =
+{
+	{ "clear", 0, 0, initclearenc },
+	{ "rc4_128", 128/8, 0, initRC4key },
+	{ "3des_ede_cbc", 3 * 8, 8, initDES3key },
+	{ 0 }
+};
+
+static Encalg*
+parseencalg(char *p)
+{
+	Encalg *ea;
+
+	for(ea = encrypttab; ea->name; ea++)
+		if(strcmp(p, ea->name) == 0)
+			return ea;
+	error("unsupported encryption algorithm");
+	return nil;
+}
+
+static long
+tlswrite(Chan *c, void *a, long n, vlong off)
+{
+	Encalg *ea;
+	Hashalg *ha;
+	TlsRec *volatile tr;
+	Secret *volatile tos, *volatile toc;
+	Block *volatile b;
+	Cmdbuf *volatile cb;
+	int m, ty;
+	char *p, *e;
+	uchar *volatile x;
+	ulong offset = off;
+
+	tr = tlsdevs[CONV(c->qid)];
+	if(tr == nil)
+		panic("tlswrite");
+
+	ty = TYPE(c->qid);
+	switch(ty){
+	case Qdata:
+	case Qhand:
+		p = a;
+		e = p + n;
+		do{
+			m = e - p;
+			if(m > MaxRecLen)
+				m = MaxRecLen;
+
+			b = allocb(m);
+			if(waserror()){
+				freeb(b);
+				nexterror();
+			}
+			memmove(b->wp, p, m);
+			poperror();
+			b->wp += m;
+
+			tlsbwrite(c, b, offset);
+
+			p += m;
+		}while(p < e);
+		return n;
+	case Qctl:
+		break;
+	default:
+		error(Ebadusefd);
+		return -1;
+	}
+
+	cb = parsecmd(a, n);
+	if(waserror()){
+		free(cb);
+		nexterror();
+	}
+	if(cb->nf < 1)
+		error("short control request");
+
+	/* mutex with operations using what we're about to change */
+	if(waserror()){
+		qunlock(&tr->in.seclock);
+		qunlock(&tr->out.seclock);
+		nexterror();
+	}
+	qlock(&tr->in.seclock);
+	qlock(&tr->out.seclock);
+
+	if(strcmp(cb->f[0], "fd") == 0){
+		if(cb->nf != 3)
+			error("usage: fd open-fd version");
+		if(tr->c != nil)
+			error(Einuse);
+		m = strtol(cb->f[2], nil, 0);
+		if(m < MinProtoVersion || m > MaxProtoVersion)
+			error("unsupported version");
+		tr->c = buftochan(cb->f[1]);
+		tr->version = m;
+		tlsSetState(tr, SHandshake, SClosed);
+	}else if(strcmp(cb->f[0], "version") == 0){
+		if(cb->nf != 2)
+			error("usage: version vers");
+		if(tr->c == nil)
+			error("must set fd before version");
+		if(tr->verset)
+			error("version already set");
+		m = strtol(cb->f[1], nil, 0);
+		if(m == SSL3Version)
+			tr->packMac = sslPackMac;
+		else if(m == TLSVersion)
+			tr->packMac = tlsPackMac;
+		else
+			error("unsupported version");
+		tr->verset = 1;
+		tr->version = m;
+	}else if(strcmp(cb->f[0], "secret") == 0){
+		if(cb->nf != 5)
+			error("usage: secret hashalg encalg isclient secretdata");
+		if(tr->c == nil || !tr->verset)
+			error("must set fd and version before secrets");
+
+		if(tr->in.new != nil){
+			freeSec(tr->in.new);
+			tr->in.new = nil;
+		}
+		if(tr->out.new != nil){
+			freeSec(tr->out.new);
+			tr->out.new = nil;
+		}
+
+		ha = parsehashalg(cb->f[1]);
+		ea = parseencalg(cb->f[2]);
+
+		p = cb->f[4];
+		m = (strlen(p)*3)/2;
+		x = smalloc(m);
+		tos = nil;
+		toc = nil;
+		if(waserror()){
+			freeSec(tos);
+			freeSec(toc);
+			free(x);
+			nexterror();
+		}
+		m = dec64(x, m, p, strlen(p));
+		if(m < 2 * ha->maclen + 2 * ea->keylen + 2 * ea->ivlen)
+			error("not enough secret data provided");
+
+		tos = smalloc(sizeof(Secret));
+		toc = smalloc(sizeof(Secret));
+		if(!ha->initkey || !ea->initkey)
+			error("misimplemented secret algorithm");
+		(*ha->initkey)(ha, tr->version, tos, &x[0]);
+		(*ha->initkey)(ha, tr->version, toc, &x[ha->maclen]);
+		(*ea->initkey)(ea, tos, &x[2 * ha->maclen], &x[2 * ha->maclen + 2 * ea->keylen]);
+		(*ea->initkey)(ea, toc, &x[2 * ha->maclen + ea->keylen], &x[2 * ha->maclen + 2 * ea->keylen + ea->ivlen]);
+
+		if(!tos->mac || !tos->enc || !tos->dec
+		|| !toc->mac || !toc->enc || !toc->dec)
+			error("missing algorithm implementations");
+		if(strtol(cb->f[3], nil, 0) == 0){
+			tr->in.new = tos;
+			tr->out.new = toc;
+		}else{
+			tr->in.new = toc;
+			tr->out.new = tos;
+		}
+		if(tr->version == SSL3Version){
+			toc->unpad = sslunpad;
+			tos->unpad = sslunpad;
+		}else{
+			toc->unpad = tlsunpad;
+			tos->unpad = tlsunpad;
+		}
+		toc->encalg = ea->name;
+		toc->hashalg = ha->name;
+		tos->encalg = ea->name;
+		tos->hashalg = ha->name;
+
+		free(x);
+		poperror();
+	}else if(strcmp(cb->f[0], "changecipher") == 0){
+		if(cb->nf != 1)
+			error("usage: changecipher");
+		if(tr->out.new == nil)
+			error("can't change cipher spec without setting secret");
+
+		qunlock(&tr->in.seclock);
+		qunlock(&tr->out.seclock);
+		poperror();
+		free(cb);
+		poperror();
+
+		/*
+		 * the real work is done as the message is written
+		 * so the stream is encrypted in sync.
+		 */
+		b = allocb(1);
+		*b->wp++ = 1;
+		tlsrecwrite(tr, RChangeCipherSpec, b);
+		return n;
+	}else if(strcmp(cb->f[0], "opened") == 0){
+		if(cb->nf != 1)
+			error("usage: opened");
+		if(tr->in.sec == nil || tr->out.sec == nil)
+			error("cipher must be configured before enabling data messages");
+		lock(&tr->statelk);
+		if(tr->state != SHandshake && tr->state != SOpen){
+			unlock(&tr->statelk);
+			error("can't enable data messages");
+		}
+		tr->state = SOpen;
+		unlock(&tr->statelk);
+		tr->opened = 1;
+	}else if(strcmp(cb->f[0], "alert") == 0){
+		if(cb->nf != 2)
+			error("usage: alert n");
+		if(tr->c == nil)
+			error("must set fd before sending alerts");
+		m = strtol(cb->f[1], nil, 0);
+
+		qunlock(&tr->in.seclock);
+		qunlock(&tr->out.seclock);
+		poperror();
+		free(cb);
+		poperror();
+
+		sendAlert(tr, m);
+
+		if(m == ECloseNotify)
+			tlsclosed(tr, SLClose);
+
+		return n;
+	} else
+		error(Ebadarg);
+
+	qunlock(&tr->in.seclock);
+	qunlock(&tr->out.seclock);
+	poperror();
+	free(cb);
+	poperror();
+
+	return n;
+}
+
+static void
+tlsinit(void)
+{
+	struct Encalg *e;
+	struct Hashalg *h;
+	int n;
+	char *cp;
+
+	tlsdevs = smalloc(sizeof(TlsRec*) * maxtlsdevs);
+	trnames = smalloc((sizeof *trnames) * maxtlsdevs);
+
+	n = 1;
+	for(e = encrypttab; e->name != nil; e++)
+		n += strlen(e->name) + 1;
+	cp = encalgs = smalloc(n);
+	for(e = encrypttab;;){
+		strcpy(cp, e->name);
+		cp += strlen(e->name);
+		e++;
+		if(e->name == nil)
+			break;
+		*cp++ = ' ';
+	}
+	*cp = 0;
+
+	n = 1;
+	for(h = hashtab; h->name != nil; h++)
+		n += strlen(h->name) + 1;
+	cp = hashalgs = smalloc(n);
+	for(h = hashtab;;){
+		strcpy(cp, h->name);
+		cp += strlen(h->name);
+		h++;
+		if(h->name == nil)
+			break;
+		*cp++ = ' ';
+	}
+	*cp = 0;
+}
+
+Dev tlsdevtab = {
+	'a',
+	"tls",
+
+	devreset,
+	tlsinit,
+	devshutdown,
+	tlsattach,
+	tlswalk,
+	tlsstat,
+	tlsopen,
+	devcreate,
+	tlsclose,
+	tlsread,
+	tlsbread,
+	tlswrite,
+	tlsbwrite,
+	devremove,
+	tlswstat,
+};
+
+/* get channel associated with an fd */
+static Chan*
+buftochan(char *p)
+{
+	Chan *c;
+	int fd;
+
+	if(p == 0)
+		error(Ebadarg);
+	fd = strtoul(p, 0, 0);
+	if(fd < 0)
+		error(Ebadarg);
+	c = fdtochan(fd, -1, 0, 1);	/* error check and inc ref */
+	return c;
+}
+
+static void
+sendAlert(TlsRec *tr, int err)
+{
+	Block *b;
+	int i, fatal;
+	char *msg;
+
+	fatal = 1;
+	msg = "tls unknown alert";
+	for(i=0; i < nelem(tlserrs); i++) {
+		if(tlserrs[i].err == err) {
+			msg = tlserrs[i].msg;
+			if(tr->version == SSL3Version)
+				err = tlserrs[i].sslerr;
+			else
+				err = tlserrs[i].tlserr;
+			fatal = tlserrs[i].fatal;
+			break;
+		}
+	}
+
+	if(!waserror()){
+		b = allocb(2);
+		*b->wp++ = fatal + 1;
+		*b->wp++ = err;
+		if(fatal)
+			tlsSetState(tr, SAlert, SOpen|SHandshake|SRClose);
+		tlsrecwrite(tr, RAlert, b);
+		poperror();
+	}
+	if(fatal)
+		tlsError(tr, msg);
+}
+
+static void
+tlsError(TlsRec *tr, char *msg)
+{
+	int s;
+
+	lock(&tr->statelk);
+	s = tr->state;
+	tr->state = SError;
+	if(s != SError){
+		strncpy(tr->err, msg, ERRMAX - 1);
+		tr->err[ERRMAX - 1] = '\0';
+	}
+	unlock(&tr->statelk);
+	if(s != SError)
+		alertHand(tr, msg);
+}
+
+static void
+tlsSetState(TlsRec *tr, int new, int old)
+{
+	lock(&tr->statelk);
+	if(tr->state & old)
+		tr->state = new;
+	unlock(&tr->statelk);
+}
+
+/* hand up a digest connection */
+static void
+tlshangup(TlsRec *tr)
+{
+	Block *b;
+
+	qlock(&tr->in.io);
+	for(b = tr->processed; b; b = tr->processed){
+		tr->processed = b->next;
+		freeb(b);
+	}
+	if(tr->unprocessed != nil){
+		freeb(tr->unprocessed);
+		tr->unprocessed = nil;
+	}
+	qunlock(&tr->in.io);
+
+	tlsSetState(tr, SClosed, ~0);
+}
+
+static TlsRec*
+newtls(Chan *ch)
+{
+	TlsRec **pp, **ep, **np;
+	char **nmp;
+	int t, newmax;
+
+	if(waserror()) {
+		unlock(&tdlock);
+		nexterror();
+	}
+	lock(&tdlock);
+	ep = &tlsdevs[maxtlsdevs];
+	for(pp = tlsdevs; pp < ep; pp++)
+		if(*pp == nil)
+			break;
+	if(pp >= ep) {
+		if(maxtlsdevs >= MaxTlsDevs) {
+			unlock(&tdlock);
+			poperror();
+			return nil;
+		}
+		newmax = 2 * maxtlsdevs;
+		if(newmax > MaxTlsDevs)
+			newmax = MaxTlsDevs;
+		np = smalloc(sizeof(TlsRec*) * newmax);
+		memmove(np, tlsdevs, sizeof(TlsRec*) * maxtlsdevs);
+		tlsdevs = np;
+		pp = &tlsdevs[maxtlsdevs];
+		memset(pp, 0, sizeof(TlsRec*)*(newmax - maxtlsdevs));
+
+		nmp = smalloc(sizeof *nmp * newmax);
+		memmove(nmp, trnames, sizeof *nmp * maxtlsdevs);
+		trnames = nmp;
+
+		maxtlsdevs = newmax;
+	}
+	*pp = mktlsrec();
+	if(pp - tlsdevs >= tdhiwat)
+		tdhiwat++;
+	t = TYPE(ch->qid);
+	if(t == Qclonus)
+		t = Qctl;
+	ch->qid.path = QID(pp - tlsdevs, t);
+	ch->qid.vers = 0;
+	unlock(&tdlock);
+	poperror();
+	return *pp;
+}
+
+static TlsRec *
+mktlsrec(void)
+{
+	TlsRec *tr;
+
+	tr = mallocz(sizeof(*tr), 1);
+	if(tr == nil)
+		error(Enomem);
+	tr->state = SClosed;
+	tr->ref = 1;
+	kstrdup(&tr->user, up->user);
+	tr->perm = 0660;
+	return tr;
+}
+
+static char*
+tlsstate(int s)
+{
+	switch(s){
+	case SHandshake:
+		return "Handshaking";
+	case SOpen:
+		return "Established";
+	case SRClose:
+		return "RemoteClosed";
+	case SLClose:
+		return "LocalClosed";
+	case SAlert:
+		return "Alerting";
+	case SError:
+		return "Errored";
+	case SClosed:
+		return "Closed";
+	}
+	return "Unknown";
+}
+
+static void
+freeSec(Secret *s)
+{
+	if(s != nil){
+		free(s->enckey);
+		free(s);
+	}
+}
+
+static int
+noenc(Secret *, uchar *, int n)
+{
+	return n;
+}
+
+static int
+rc4enc(Secret *sec, uchar *buf, int n)
+{
+	rc4(sec->enckey, buf, n);
+	return n;
+}
+
+static int
+tlsunpad(uchar *buf, int n, int block)
+{
+	int pad, nn;
+
+	pad = buf[n - 1];
+	nn = n - 1 - pad;
+	if(nn <= 0 || n % block)
+		return -1;
+	while(--n > nn)
+		if(pad != buf[n - 1])
+			return -1;
+	return nn;
+}
+
+static int
+sslunpad(uchar *buf, int n, int block)
+{
+	int pad, nn;
+
+	pad = buf[n - 1];
+	nn = n - 1 - pad;
+	if(nn <= 0 || n % block)
+		return -1;
+	return nn;
+}
+
+static int
+blockpad(uchar *buf, int n, int block)
+{
+	int pad, nn;
+
+	nn = n + block;
+	nn -= nn % block;
+	pad = nn - (n + 1);
+	while(n < nn)
+		buf[n++] = pad;
+	return nn;
+}
+		
+static int
+des3enc(Secret *sec, uchar *buf, int n)
+{
+	n = blockpad(buf, n, 8);
+	des3CBCencrypt(buf, n, sec->enckey);
+	return n;
+}
+
+static int
+des3dec(Secret *sec, uchar *buf, int n)
+{
+	des3CBCdecrypt(buf, n, sec->enckey);
+	return (*sec->unpad)(buf, n, 8);
+}
+static DigestState*
+nomac(uchar *, ulong, uchar *, ulong, uchar *, DigestState *)
+{
+	return nil;
+}
+
+/*
+ * sslmac: mac calculations for ssl 3.0 only; tls 1.0 uses the standard hmac.
+ */
+static DigestState*
+sslmac_x(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s,
+	DigestState*(*x)(uchar*, ulong, uchar*, DigestState*), int xlen, int padlen)
+{
+	int i;
+	uchar pad[48], innerdigest[20];
+
+	if(xlen > sizeof(innerdigest)
+	|| padlen > sizeof(pad))
+		return nil;
+
+	if(klen>64)
+		return nil;
+
+	/* first time through */
+	if(s == nil){
+		for(i=0; i<padlen; i++)
+			pad[i] = 0x36;
+		s = (*x)(key, klen, nil, nil);
+		s = (*x)(pad, padlen, nil, s);
+		if(s == nil)
+			return nil;
+	}
+
+	s = (*x)(p, len, nil, s);
+	if(digest == nil)
+		return s;
+
+	/* last time through */
+	for(i=0; i<padlen; i++)
+		pad[i] = 0x5c;
+	(*x)(nil, 0, innerdigest, s);
+	s = (*x)(key, klen, nil, nil);
+	s = (*x)(pad, padlen, nil, s);
+	(*x)(innerdigest, xlen, digest, s);
+	return nil;
+}
+
+static DigestState*
+sslmac_sha1(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s)
+{
+	return sslmac_x(p, len, key, klen, digest, s, sha1, SHA1dlen, 40);
+}
+
+static DigestState*
+sslmac_md5(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s)
+{
+	return sslmac_x(p, len, key, klen, digest, s, md5, MD5dlen, 48);
+}
+
+static void
+sslPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac)
+{
+	DigestState *s;
+	uchar buf[11];
+
+	memmove(buf, seq, 8);
+	buf[8] = header[0];
+	buf[9] = header[3];
+	buf[10] = header[4];
+
+	s = (*sec->mac)(buf, 11, mackey, sec->maclen, 0, 0);
+	(*sec->mac)(body, len, mackey, sec->maclen, mac, s);
+}
+
+static void
+tlsPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac)
+{
+	DigestState *s;
+	uchar buf[13];
+
+	memmove(buf, seq, 8);
+	memmove(&buf[8], header, 5);
+
+	s = (*sec->mac)(buf, 13, mackey, sec->maclen, 0, 0);
+	(*sec->mac)(body, len, mackey, sec->maclen, mac, s);
+}
+
+static void
+put32(uchar *p, u32int x)
+{
+	p[0] = x>>24;
+	p[1] = x>>16;
+	p[2] = x>>8;
+	p[3] = x;
+}
+
+static void
+put64(uchar *p, vlong x)
+{
+	put32(p, (u32int)(x >> 32));
+	put32(p+4, (u32int)x);
+}
+
+static void
+put24(uchar *p, int x)
+{
+	p[0] = x>>16;
+	p[1] = x>>8;
+	p[2] = x;
+}
+
+static void
+put16(uchar *p, int x)
+{
+	p[0] = x>>8;
+	p[1] = x;
+}
+
+static u32int
+get32(uchar *p)
+{
+	return (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3];
+}
+
+static int
+get16(uchar *p)
+{
+	return (p[0]<<8)|p[1];
+}

+ 51 - 0
sys/src/9/ppc/errstr.h

@@ -0,0 +1,51 @@
+char Enoerror[] = "no error";
+char Emount[] = "inconsistent mount";
+char Eunmount[] = "not mounted";
+char Eismtpt[] = "is a mount point";
+char Eunion[] = "not in union";
+char Emountrpc[] = "mount rpc error";
+char Eshutdown[] = "device shut down";
+char Enocreate[] = "mounted directory forbids creation";
+char Enonexist[] = "file does not exist";
+char Eexist[] = "file already exists";
+char Ebadsharp[] = "unknown device in # filename";
+char Enotdir[] = "not a directory";
+char Eisdir[] = "file is a directory";
+char Ebadchar[] = "bad character in file name";
+char Efilename[] = "file name syntax";
+char Eperm[] = "permission denied";
+char Ebadusefd[] = "inappropriate use of fd";
+char Ebadarg[] = "bad arg in system call";
+char Einuse[] = "device or object already in use";
+char Eio[] = "i/o error";
+char Etoobig[] = "read or write too large";
+char Etoosmall[] = "read or write too small";
+char Enoport[] = "network port not available";
+char Ehungup[] = "i/o on hungup channel";
+char Ebadctl[] = "bad process or channel control request";
+char Enodev[] = "no free devices";
+char Eprocdied[] = "process exited";
+char Enochild[] = "no living children";
+char Eioload[] = "i/o error in demand load";
+char Enovmem[] = "virtual memory allocation failed";
+char Ebadfd[] = "fd out of range or not open";
+char Enofd[] = "no free file descriptors";
+char Eisstream[] = "seek on a stream";
+char Ebadexec[] = "exec header invalid";
+char Etimedout[] = "connection timed out";
+char Econrefused[] = "connection refused";
+char Econinuse[] = "connection in use";
+char Eintr[] = "interrupted";
+char Enomem[] = "kernel allocate failed";
+char Enoswap[] = "swap space full";
+char Esoverlap[] = "segments overlap";
+char Emouseset[] = "mouse type already set";
+char Eshort[] = "i/o count too small";
+char Egreg[] = "ken has left the building";
+char Ebadspec[] = "bad attach specifier";
+char Enoreg[] = "process has no saved registers";
+char Enoattach[] = "mount/attach disallowed";
+char Eshortstat[] = "stat buffer too small";
+char Ebadstat[] = "malformed stat buffer";
+char Enegoff[] = "negative i/o offset";
+char Ecmdargs[] = "wrong #args in control message";

+ 627 - 0
sys/src/9/ppc/etherfcc.c

@@ -0,0 +1,627 @@
+/*
+ * FCCn ethernet
+ */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "m8260.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+
+#include "etherif.h"
+
+enum {
+	Nrdre		= 128,	/* receive descriptor ring entries */
+	Ntdre		= 128,	/* transmit descriptor ring entries */
+
+	Rbsize		= ETHERMAXTU+4,		/* ring buffer size (+4 for CRC) */
+	Bufsize		= (Rbsize+7)&~7,		/* aligned */
+};
+
+enum {
+
+	/* ether-specific Rx BD bits */
+	RxMiss=		SBIT(7),
+	RxeLG=		SBIT(10),
+	RxeNO=		SBIT(11),
+	RxeSH=		SBIT(12),
+	RxeCR=		SBIT(13),
+	RxeOV=		SBIT(14),
+	RxeCL=		SBIT(15),
+	RxError=		(RxeLG|RxeNO|RxeSH|RxeCR|RxeOV|RxeCL),	/* various error flags */
+
+	/* ether-specific Tx BD bits */
+	TxPad=		SBIT(1),	/* pad short frames */
+	TxTC=		SBIT(5),	/* transmit CRC */
+	TxeDEF=		SBIT(6),
+	TxeHB=		SBIT(7),
+	TxeLC=		SBIT(8),
+	TxeRL=		SBIT(9),
+	TxeUN=		SBIT(14),
+	TxeCSL=		SBIT(15),
+
+	/* psmr */
+	PRO=		BIT(9),	/* promiscuous mode */
+	LPB=			BIT(3),	/* local protect bit */
+	FDE=		BIT(5),	/* full duplex ethernet */
+	CRCE=		BIT(24),	/* Ethernet CRC */
+
+	/* gfmr */
+	ENET=		0xc,		/* ethernet mode */
+	ENT=		BIT(27),
+	ENR=		BIT(26),
+	TCI=			BIT(2),
+
+	/* FCC function code register */
+	GBL=		0x20,
+	BO=			0x18,
+	EB=			0x10,	/* Motorola byte order */
+	TC2=		0x04,
+	DTB=		0x02,
+	BDB=		0x01,
+
+	/* FCC Event/Mask bits */
+	GRA=		SBIT(8),
+	RXC=		SBIT(9),
+	TXC=		SBIT(10),
+	TXE=			SBIT(11),
+	RXF=			SBIT(12),
+	BSY=			SBIT(13),
+	TXB=		SBIT(14),
+	RXB=		SBIT(15),
+};
+
+typedef struct Etherparam Etherparam;
+struct Etherparam {
+/*0x00*/	FCCparam;
+/*0x3c*/	ulong	stat_buf;
+/*0x40*/	ulong	cam_ptr;
+/*0x44*/	ulong	cmask;
+/*0x48*/	ulong	cpres;
+/*0x4c*/	ulong	crcec;
+/*0x50*/	ulong	alec;
+/*0x54*/	ulong	disfc;
+/*0x58*/	ushort	retlim;
+/*0x5a*/	ushort	retcnt;
+/*0x5c*/	ushort	p_per;
+/*0x5e*/	ushort	boff_cnt;
+/*0x60*/	ulong	gaddr[2];
+/*0x68*/	ushort	tfcstat;
+/*0x6a*/	ushort	tfclen;
+/*0x6c*/	ulong	tfcptr;
+/*0x70*/	ushort	mflr;
+/*0x72*/	ushort	paddr[3];
+/*0x78*/	ushort	ibd_cnt;
+/*0x7a*/	ushort	ibd_start;
+/*0x7c*/	ushort	ibd_end;
+/*0x7e*/	ushort	tx_len;
+/*0x80*/	uchar	ibd_base[32];
+/*0xa0*/	ulong	iaddr[2];
+/*0xa8*/	ushort	minflr;
+/*0xaa*/	ushort	taddr[3];
+/*0xb0*/	ushort	padptr;
+/*0xb2*/	ushort	Rsvdb2;
+/*0xb4*/	ushort	cf_range;
+/*0xb6*/	ushort	max_b;
+/*0xb8*/	ushort	maxd1;
+/*0xba*/	ushort	maxd2;
+/*0xbc*/	ushort	maxd;
+/*0xbe*/	ushort	dma_cnt;
+/*0xc0*/	ulong	octc;
+/*0xc4*/	ulong	colc;
+/*0xc8*/	ulong	broc;
+/*0xcc*/	ulong	mulc;
+/*0xd0*/	ulong	uspc;
+/*0xd4*/	ulong	frgc;
+/*0xd8*/	ulong	ospc;
+/*0xdc*/	ulong	jbrc;
+/*0xe0*/	ulong	p64c;
+/*0xe4*/	ulong	p65c;
+/*0xe8*/	ulong	p128c;
+/*0xec*/	ulong	p256c;
+/*0xf0*/	ulong	p512c;
+/*0xf4*/	ulong	p1024c;
+/*0xf8*/	ulong	cam_buf;
+/*0xfc*/	ulong	Rsvdfc;
+/*0x100*/
+};
+
+typedef struct {
+	Lock;
+	int		fccid;
+	int		port;
+	ulong	pmdio;
+	ulong	pmdck;
+	int		init;
+	int		active;
+	FCC*		fcc;
+
+	Ring;
+
+	ulong	interrupts;			/* statistics */
+	ulong	deferred;
+	ulong	heartbeat;
+	ulong	latecoll;
+	ulong	retrylim;
+	ulong	underrun;
+	ulong	overrun;
+	ulong	carrierlost;
+	ulong	retrycount;
+} Ctlr;
+
+static	int	fccirq[] = {0x20, 0x21, 0x22};
+static	int	fccid[] = {FCC1ID, FCC2ID, FCC3ID};
+
+int	ioringinit(Ring* r, int nrdre, int ntdre, int bufsize);
+
+static void
+attach(Ether *ether)
+{
+	Ctlr *ctlr;
+
+	ctlr = ether->ctlr;
+	ctlr->active = 1;
+	ctlr->fcc->gfmr |= ENR|ENT;
+	eieio();
+}
+
+static void
+closed(Ether *ether)
+{
+	Ctlr *ctlr;
+
+	ctlr = ether->ctlr;
+	ilock(ctlr);
+	ctlr->active = 0;
+	ctlr->fcc->gfmr &= ~(ENR|ENT);
+	iunlock(ctlr);
+	print("Ether closed\n");
+}
+
+static void
+promiscuous(void* arg, int on)
+{
+	Ether *ether;
+	Ctlr *ctlr;
+
+	ether = (Ether*)arg;
+	ctlr = ether->ctlr;
+
+	ilock(ctlr);
+	if(on || ether->nmaddr)
+		ctlr->fcc->fpsmr |= PRO;
+	else
+		ctlr->fcc->fpsmr &= ~PRO;
+	iunlock(ctlr);
+}
+
+static void
+multicast(void* arg, uchar *addr, int on)
+{
+	Ether *ether;
+	Ctlr *ctlr;
+
+	USED(addr, on);	/* if on, could SetGroupAddress; if !on, it's hard */
+
+	ether = (Ether*)arg;
+	ctlr = ether->ctlr;
+
+	ilock(ctlr);
+	if(ether->prom || ether->nmaddr)
+		ctlr->fcc->fpsmr |= PRO;
+	else
+		ctlr->fcc->fpsmr &= ~PRO;
+	iunlock(ctlr);
+}
+
+static void
+txstart(Ether *ether)
+{
+	int len;
+	Ctlr *ctlr;
+	Block *b;
+	BD *dre;
+
+	ctlr = ether->ctlr;
+	if(ctlr->init)
+		return;
+	while(ctlr->ntq < Ntdre-1){
+		b = qget(ether->oq);
+		if(b == 0)
+			break;
+
+		dre = &ctlr->tdr[ctlr->tdrh];
+		dczap(dre, sizeof(BD));
+		if(dre->status & BDReady)
+			panic("ether: txstart");
+
+		/*
+		 * Give ownership of the descriptor to the chip, increment the
+		 * software ring descriptor pointer and tell the chip to poll.
+		 */
+		len = BLEN(b);
+		if(ctlr->txb[ctlr->tdrh] != nil)
+			panic("fcc/ether: txstart");
+		ctlr->txb[ctlr->tdrh] = b;
+		if((ulong)b->rp&1)
+			panic("fcc/ether: txstart align");	/* TO DO: ensure alignment */
+		dre->addr = PADDR(b->rp);
+		dre->length = len;
+		dcflush(b->rp, len);
+		dcflush(dre, sizeof(BD));
+		dre->status = (dre->status & BDWrap) | BDReady|TxPad|BDInt|BDLast|TxTC;
+		dcflush(dre, sizeof(BD));
+		ctlr->fcc->ftodr = 1<<15;	/* transmit now */
+		ctlr->ntq++;
+		ctlr->tdrh = NEXT(ctlr->tdrh, Ntdre);
+	}
+}
+
+static void
+transmit(Ether* ether)
+{
+	Ctlr *ctlr;
+
+	ctlr = ether->ctlr;
+	ilock(ctlr);
+	txstart(ether);
+	iunlock(ctlr);
+}
+
+static void
+interrupt(Ureg*, void *arg)
+{
+	int len, status;
+	ushort events;
+	Ctlr *ctlr;
+	BD *dre;
+	Block *b;
+	Ether *ether = arg;
+
+	ctlr = ether->ctlr;
+	if(!ctlr->active)
+		return;	/* not ours */
+
+	/*
+	 * Acknowledge all interrupts and whine about those that shouldn't
+	 * happen.
+	 */
+	events = ctlr->fcc->fcce;
+	eieio();
+	ctlr->fcc->fcce = events;		/* clear events */
+	eieio();
+	ctlr->interrupts++;
+
+	if(events & RXB)
+		ctlr->overrun++;
+	if(events & TXE)
+		ether->oerrs++;
+
+	/*
+	 * Receiver interrupt: run round the descriptor ring logging
+	 * errors and passing valid receive data up to the higher levels
+	 * until we encounter a descriptor still owned by the chip.
+	 */
+	if(events & (RXF|RXB)){
+		dre = &ctlr->rdr[ctlr->rdrx];
+		dczap(dre, sizeof(BD));
+		while(((status = dre->status) & BDEmpty) == 0){
+			if(status & RxError || (status & (BDFirst|BDLast)) != (BDFirst|BDLast)){
+				if(status & (RxeLG|RxeSH))
+					ether->buffs++;
+				if(status & RxeNO)
+					ether->frames++;
+				if(status & RxeCR)
+					ether->crcs++;
+				if(status & RxeOV)
+					ether->overflows++;
+				print("eth rx: %ux\n", status);
+			}
+			else{
+				/*
+				 * We have a packet. Read it in.
+				 */
+				len = dre->length-4;
+				dczap(KADDR(dre->addr), len);
+				if((b = iallocb(len)) != 0){
+					memmove(b->wp, KADDR(dre->addr), len);
+					b->wp += len;
+					etheriq(ether, b, 1);
+				}else
+					ether->soverflows++;
+			}
+
+			/*
+			 * Finished with this descriptor, reinitialise it,
+			 * give it back to the chip, then on to the next...
+			 */
+			dre->length = 0;
+			dre->status = (status & BDWrap) | BDEmpty | BDInt;
+			dcflush(dre, sizeof(BD));
+
+			ctlr->rdrx = NEXT(ctlr->rdrx, Nrdre);
+			dre = &ctlr->rdr[ctlr->rdrx];
+			dczap(dre, sizeof(BD));
+		}
+	}
+
+	/*
+	 * Transmitter interrupt: handle anything queued for a free descriptor.
+	 */
+	if(events & (TXB|TXE)){
+		lock(ctlr);
+		while(ctlr->ntq){
+			dre = &ctlr->tdr[ctlr->tdri];
+			dczap(dre, sizeof(BD));
+			status = dre->status;
+			if(status & BDReady)
+				break;
+			if(status & TxeDEF)
+				ctlr->deferred++;
+			if(status & TxeHB)
+				ctlr->heartbeat++;
+			if(status & TxeLC)
+				ctlr->latecoll++;
+			if(status & TxeRL)
+				ctlr->retrylim++;
+			if(status & TxeUN)
+				ctlr->underrun++;
+			if(status & TxeCSL)
+				ctlr->carrierlost++;
+			ctlr->retrycount += (status>>2)&0xF;
+			b = ctlr->txb[ctlr->tdri];
+			if(b == nil)
+				panic("fcce/interrupt: bufp");
+			ctlr->txb[ctlr->tdri] = nil;
+			freeb(b);
+			ctlr->ntq--;
+			ctlr->tdri = NEXT(ctlr->tdri, Ntdre);
+		}
+
+		if(events & TXE)
+			cpmop(RestartTx, ctlr->fccid, 0xc);
+
+		txstart(ether);
+		unlock(ctlr);
+	}
+}
+
+static long
+ifstat(Ether* ether, void* a, long n, ulong offset)
+{
+	char *p;
+	int len;
+	Ctlr *ctlr;
+
+	if(n == 0)
+		return 0;
+
+	ctlr = ether->ctlr;
+
+	p = malloc(READSTR);
+	len = snprint(p, READSTR, "interrupts: %lud\n", ctlr->interrupts);
+	len += snprint(p+len, READSTR-len, "carrierlost: %lud\n", ctlr->carrierlost);
+	len += snprint(p+len, READSTR-len, "heartbeat: %lud\n", ctlr->heartbeat);
+	len += snprint(p+len, READSTR-len, "retrylimit: %lud\n", ctlr->retrylim);
+	len += snprint(p+len, READSTR-len, "retrycount: %lud\n", ctlr->retrycount);
+	len += snprint(p+len, READSTR-len, "latecollisions: %lud\n", ctlr->latecoll);
+	len += snprint(p+len, READSTR-len, "rxoverruns: %lud\n", ctlr->overrun);
+	len += snprint(p+len, READSTR-len, "txunderruns: %lud\n", ctlr->underrun);
+	snprint(p+len, READSTR-len, "framesdeferred: %lud\n", ctlr->deferred);
+	n = readstr(offset, a, n, p);
+	free(p);
+
+	return n;
+}
+
+/*
+ * This follows the MPC8260 user guide: section28.9's initialisation sequence.
+ */
+static void
+fccsetup(Ctlr *ctlr, FCC *fcc, uchar *ea)
+{
+	int i;
+	Etherparam *p;
+
+	/* Turn Ethernet off */
+	fcc->gfmr &= ~(ENR | ENT);
+
+	switch(ctlr->port) {
+	default:
+		iopunlock();
+		return;
+	case 0:
+		/* Step 1 (Section 28.9), write the parallel ports */
+		ctlr->pmdio = 0x01000000;
+		ctlr->pmdck = 0x08000000;
+		iomem->port[0].pdir &= ~A1dir0;
+		iomem->port[0].pdir |= A1dir1;
+		iomem->port[0].psor &= ~A1psor0;
+		iomem->port[0].psor |= A1psor1;
+		iomem->port[0].ppar |= (A1dir0 | A1dir1);
+		/* Step 2, Port C clocks */
+		iomem->port[2].psor &= ~0x00000c00;
+		iomem->port[2].pdir &= ~0x00000c00;
+		iomem->port[2].ppar |= 0x00000c00;
+		iomem->port[3].pdat |= (ctlr->pmdio | ctlr->pmdck);
+		iomem->port[3].podr |= ctlr->pmdio;
+		iomem->port[3].pdir |= (ctlr->pmdio | ctlr->pmdck);
+		iomem->port[3].ppar &= ~(ctlr->pmdio | ctlr->pmdck);
+		eieio();
+		/* Step 3, Serial Interface clock routing */
+		iomem->cmxfcr &= ~0xff000000;	/* Clock mask */
+		iomem->cmxfcr |= 0x37000000;	/* Clock route */
+		break;
+
+	case 1:
+		/* Step 1 (Section 28.9), write the parallel ports */
+		ctlr->pmdio = 0x00400000;
+		ctlr->pmdck = 0x00200000;
+		iomem->port[1].pdir &= ~B2dir0;
+		iomem->port[1].pdir |= B2dir1;
+		iomem->port[1].psor &= ~B2psor0;
+		iomem->port[1].psor |= B2psor1;
+		iomem->port[1].ppar |= (B2dir0 | B2dir1);
+		/* Step 2, Port C clocks */
+		iomem->port[2].psor &= ~0x00003000;
+		iomem->port[2].pdir &= ~0x00003000;
+		iomem->port[2].ppar |= 0x00003000;
+
+		iomem->port[2].pdat |= (ctlr->pmdio | ctlr->pmdck);
+		iomem->port[2].podr |= ctlr->pmdio;
+		iomem->port[2].pdir |= (ctlr->pmdio | ctlr->pmdck);
+		iomem->port[2].ppar &= ~(ctlr->pmdio | ctlr->pmdck);
+		eieio();
+		/* Step 3, Serial Interface clock routing */
+		iomem->cmxfcr &= ~0x00ff0000;
+		iomem->cmxfcr |= 0x00250000;
+		break;
+
+	case 2:
+		/* Step 1 (Section 28.9), write the parallel ports */
+		iomem->port[1].pdir &= ~B3dir0;
+		iomem->port[1].pdir |= B3dir1;
+		iomem->port[1].psor &= ~B3psor0;
+		iomem->port[1].psor |= B3psor1;
+		iomem->port[1].ppar |= (B3dir0 | B3dir1);
+		/* Step 2, Port C clocks */
+		iomem->port[2].psor &= ~0x0000c000;
+		iomem->port[2].pdir &= ~0x0000c000;
+		iomem->port[2].ppar |= 0x0000c000;
+		iomem->port[3].pdat |= (ctlr->pmdio | ctlr->pmdck);
+		iomem->port[3].podr |= ctlr->pmdio;
+		iomem->port[3].pdir |= (ctlr->pmdio | ctlr->pmdck);
+		iomem->port[3].ppar &= ~(ctlr->pmdio | ctlr->pmdck);
+		eieio();
+		/* Step 3, Serial Interface clock routing */
+		iomem->cmxfcr &= ~0x0000ff00;
+		iomem->cmxfcr |= 0x00003700;
+		break;
+	}
+	iopunlock();
+
+	p = (Etherparam*)(m->imap->prmfcc + ctlr->port);
+	memset(p, 0, sizeof(Etherparam));
+
+	/* Step 4 */
+//	fcc->gfmr |= (TCI | ENET);
+	fcc->gfmr |= ENET;
+
+	/* Step 5 */
+	fcc->fpsmr = CRCE | FDE | LPB;		/* full duplex operation */
+//	fcc->fpsmr = CRCE;		/* half duplex operation */
+
+	/* Step 6 */
+	fcc->fdsr = 0xd555;
+
+	/* Step 7, initialize parameter ram */
+	p->rbase = PADDR(ctlr->rdr);
+	p->tbase = PADDR(ctlr->tdr);
+	p->rstate = (GBL | EB) << 24;
+	p->tstate = (GBL | EB) << 24;
+
+	p->cmask = 0xdebb20e3;
+	p->cpres = 0xffffffff;
+
+	p->retlim = 15;	/* retry limit */
+
+	p->mrblr = Bufsize;
+	p->mflr = Rbsize;
+	p->minflr = ETHERMINTU + 4;
+	p->maxd1 = (Rbsize+3) & ~3;
+	p->maxd2 = (Rbsize+3) & ~3;
+
+	for(i=0; i<Eaddrlen; i+=2)
+		p->paddr[2-i/2] = (ea[i+1]<<8)|ea[i];
+
+	/* Step 7, initialize parameter ram, configuration-dependent values */
+	p->riptr = m->imap->fccextra[ctlr->port].ri - (uchar*)INTMEM;
+	p->tiptr = m->imap->fccextra[ctlr->port].ti - (uchar*)INTMEM;
+	p->padptr = m->imap->fccextra[ctlr->port].pad - (uchar*)INTMEM;
+	memset(m->imap->fccextra[ctlr->port].pad, 0x88, 0x20);
+
+	/* Step 8, clear out events */
+	fcc->fcce = ~0;
+
+	/* Step 9, Interrupt enable */
+	fcc->fccm = TXE | RXF | TXB;
+
+	/* Step 10, Configure interrupt priority (not done here) */
+	/* Step 11, Clear out current events */
+	/* Step 12, Enable interrupts to the CP interrupt controller */
+
+	/* Step 13, Issue the Init Tx and Rx command, specifying 0xc for ethernet*/
+	cpmop(InitRxTx, fccid[ctlr->port], 0xc);
+
+	// Step 14, Enable ethernet: done at attach time
+}
+
+static int
+reset(Ether* ether)
+{
+	uchar ea[Eaddrlen];
+	Ctlr *ctlr;
+	FCC *fcc;
+
+	if(m->cpuhz < 24000000){
+		print("%s ether: system speed must be >= 24MHz for ether use\n", ether->type);
+		return -1;
+	}
+
+	if(!(ether->port >= 0 && ether->port < 3)){
+		print("%s ether: no FCC port %ld\n", ether->type, ether->port);
+		return -1;
+	}
+	ether->irq = fccirq[ether->port];
+	ether->tbdf = BusPPC;
+	fcc = iomem->fcc + ether->port;
+
+	ctlr = malloc(sizeof(*ctlr));
+	ether->ctlr = ctlr;
+	memset(ctlr, 0, sizeof(*ctlr));
+	ctlr->fcc = fcc;
+	ctlr->port = ether->port;
+	ctlr->fccid = fccid[ether->port];
+
+	/* Ioringinit will allocate the buffer descriptors in normal memory
+	 * and NOT in Dual-Ported Ram, as prescribed by the MPC8260
+	 * PowerQUICC II manual (Section 28.6).  When they are allocated
+	 * in DPram and the Dcache is enabled, the processor will hang
+	 */
+	if(ioringinit(ctlr, Nrdre, Ntdre, Bufsize) < 0)
+		panic("etherfcc init");
+
+	fccsetup(ctlr, fcc, ether->ea);
+
+	ether->mbps = 100;	/* TO DO: could be 10mbps */
+	ether->attach = attach;
+	ether->transmit = transmit;
+	ether->interrupt = interrupt;
+	ether->ifstat = ifstat;
+
+	ether->arg = ether;
+	ether->promiscuous = promiscuous;
+	ether->multicast = multicast;
+
+	/*
+	 * Until we know where to find it, insist that the plan9.ini
+	 * entry holds the Ethernet address.
+	 */
+	memset(ea, 0, Eaddrlen);
+	if(memcmp(ea, ether->ea, Eaddrlen) == 0){
+		print("no ether address");
+		return -1;
+	}
+
+	return 0;
+}
+
+void
+etherfcclink(void)
+{
+	addethercard("fcc", reset);
+}

+ 35 - 0
sys/src/9/ppc/etherif.h

@@ -0,0 +1,35 @@
+enum {
+	MaxEther	= 24,
+	Ntypes		= 8,
+};
+
+typedef struct Ether Ether;
+struct Ether {
+	ISAConf;			/* hardware info */
+
+	int	ctlrno;
+	int	tbdf;			/* type+busno+devno+funcno */
+	int	minmtu;
+	int 	maxmtu;
+	uchar	ea[Eaddrlen];
+
+	void	(*attach)(Ether*);	/* filled in by reset routine */
+	void	(*transmit)(Ether*);
+	void	(*interrupt)(Ureg*, void*);
+	long	(*ifstat)(Ether*, void*, long, ulong);
+	long 	(*ctl)(Ether*, void*, long); /* custom ctl messages */
+	void	*ctlr;
+
+	Queue*	oq;
+
+	Netif;
+};
+
+extern Block* etheriq(Ether*, Block*, int);
+extern void addethercard(char*, int(*)(Ether*));
+extern ulong ethercrc(uchar*, int);
+
+#define NEXT(x, l)	(((x)+1)%(l))
+#define PREV(x, l)	(((x) == 0) ? (l)-1: (x)-1)
+#define	HOWMANY(x, y)	(((x)+((y)-1))/(y))
+#define ROUNDUP(x, y)	(HOWMANY((x), (y))*(y))

+ 229 - 0
sys/src/9/ppc/ethersaturn.c

@@ -0,0 +1,229 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+#include "msaturn.h"
+
+#include "etherif.h"
+
+enum{
+	Etcr = Saturn + 0x0c00,
+	Etsr = Saturn + 0x0c02,
+	Ercr = Saturn + 0x0c04,
+	Ersr = Saturn + 0x0c06,
+	Eisr = Saturn + 0x0d04,
+	Eimr = Saturn + 0x0d06,
+	Emacaddr0 = Saturn + 0x0e02,
+	Miicr = Saturn + 0x0f02,
+	Miiwdr = Saturn + 0x0f04,
+	Miirdr = Saturn + 0x0f06,
+
+	Ethermem = 0xf2c00000,
+	Etherfsize = 0x2000,
+	Nrx = 14,
+	Ntx = 2,		// Nrx + Ntx must be 16
+
+	Ersr_rxfpmask = 0xf,
+	Ersr_rxevent = RBIT(0, ushort),
+	Etcr_txfpmask = 0xf,
+	Ercr_rxenab = RBIT(0, ushort),
+	Ercr_auienab = RBIT(2, ushort),
+	Etcr_txstart = RBIT(1, ushort),
+	Etcr_retries = 0xf<<8,
+	Ei_txecall = RBIT(0, ushort),
+	Ei_txretry = RBIT(2, ushort),
+	Ei_txdefer = RBIT(3, ushort),
+	Ei_txcrs = RBIT(4, ushort),
+	Ei_txdone = RBIT(5, ushort),
+	Ei_rxcrcerr = RBIT(8, ushort),
+	Ei_rxdrib = RBIT(9, ushort),
+	Ei_rxdone = RBIT(10, ushort),
+	Ei_rxshort = RBIT(11, ushort),
+	Ei_rxlong = RBIT(12, ushort),
+
+	Miicr_regshift = 6,
+	Miicr_read = RBIT(10, ushort),
+	Miicr_preambledis = RBIT(12, ushort),
+	Miicr_accack = RBIT(14, ushort),
+	Miicr_accbsy = RBIT(15, ushort),
+};
+
+typedef struct {
+	Lock;
+	int		txbusy;
+	int		txempty;
+	int		txfull;
+	int		ntx;			/* number of entries in transmit ring */
+	int		rxlast;
+
+	int		active;
+	ulong	interrupts;	/* statistics */
+	ulong	overflows;
+} Ctlr;
+
+static ushort*etcr=(ushort*)Etcr;
+static ushort*etsr=(ushort*)Etsr;
+static ushort*ercr=(ushort*)Ercr;
+static ushort*ersr=(ushort*)Ersr;
+static ushort*eimr=(ushort*)Eimr;
+static ushort*eisr=(ushort*)Eisr;
+static ushort*miicr=(ushort*)Miicr;
+static ushort*miirdr=(ushort*)Miirdr;
+
+static void
+txfill(Ether*ether, Ctlr*ctlr)
+{
+	int len;
+	Block *b;
+	ushort*dst;
+
+	while(ctlr->ntx<Ntx){
+		if((b=qget(ether->oq)) == nil)
+			break;
+
+		len = BLEN(b);
+		dst = (ushort*)(Ethermem+(ctlr->txempty+Nrx)*Etherfsize);
+		*dst = len;
+		memmove(&dst[1], b->rp, len);
+		ctlr->ntx++;
+		ctlr->txempty++;
+		if(ctlr->txempty==Ntx)
+			ctlr->txempty = 0;
+		freeb(b);
+	}
+}
+
+static void
+txrestart(Ctlr*ctlr)
+{
+	if(ctlr->ntx==0 || ctlr->txbusy)
+		return;
+	ctlr->txbusy = 1;
+	*etcr = Etcr_txstart|Etcr_retries|(ctlr->txfull+Nrx);
+}
+
+static void interrupt(Ureg*, void*);
+
+static void
+transmit(Ether*ether)
+{
+	Ctlr *ctlr;
+
+	ctlr = ether->ctlr;
+	ilock(ctlr);
+	txfill(ether, ctlr);
+	txrestart(ctlr);
+	iunlock(ctlr);
+
+}
+
+static void
+interrupt(Ureg*, void*arg)
+{
+	Ctlr*ctlr;
+	Ether*ether = arg;
+	Etherpkt*pkt;
+	ushort ie;
+	int rx, len;
+	Block *b;
+
+	ctlr = ether->ctlr;
+	if(!ctlr->active)
+		return;	/* not ours */
+	ctlr->interrupts++;
+
+	ilock(ctlr);
+	ie = *eisr;
+	*eisr = ie;
+	intack();
+
+	if(ie==0)
+		iprint("interrupt: no interrupt source?\n");
+
+	if(ie&Ei_txdone){
+		if((*etcr&Etcr_txstart)==0){
+			if(ctlr->txbusy){
+				ctlr->txbusy = 0;
+				ctlr->ntx--;
+				ctlr->txfull++;
+				if(ctlr->txfull==Ntx)
+					ctlr->txfull = 0;
+			}
+			txrestart(ctlr);
+			txfill(ether, ctlr);
+			txrestart(ctlr);
+		}
+		else
+			iprint("interrupt: bogus tx interrupt\n");
+		ie &= ~Ei_txdone;
+	}
+
+	if(ie&Ei_rxdone){
+		rx=*ersr&Ersr_rxfpmask;
+		while(ctlr->rxlast!=rx){
+
+			ctlr->rxlast++;
+			if(ctlr->rxlast >= Nrx)
+				ctlr->rxlast = 0;
+
+			pkt = (Etherpkt*)(Ethermem+ctlr->rxlast*Etherfsize);
+			len = *(ushort*)pkt;
+			if((b = iallocb(len+sizeof(ushort))) != nil){
+				memmove(b->wp, pkt, len+sizeof(ushort));
+				b->rp += sizeof(ushort);
+				b->wp = b->rp + len;
+				etheriq(ether, b, 1);
+			}else
+				ether->soverflows++;
+			rx=*ersr&Ersr_rxfpmask;
+		}
+		ie &= ~Ei_rxdone;
+	}
+
+	if(ie&Ei_txretry){
+		iprint("ethersaturn: txretry!\n");
+		ie &= ~Ei_txretry;
+		ctlr->txbusy = 0;
+		txrestart(ctlr);
+	}
+
+	ie &= ~Ei_txcrs;
+	if(ie)
+		iprint("interrupt: unhandled interrupts %.4uX\n", ie);
+	iunlock(ctlr);
+}
+
+static int
+reset(Ether* ether)
+{
+	Ctlr*ctlr;
+
+	*ercr = 0;
+	ctlr = malloc(sizeof(*ctlr));
+	memset(ctlr, 0, sizeof(*ctlr));
+	ctlr->active = 1;
+
+	ether->ctlr = ctlr;
+	ether->transmit = transmit;
+	ether->interrupt = interrupt;
+	ether->irq = Vecether;
+	ether->arg = ether;
+	memmove(ether->ea, (ushort*)Emacaddr0, Eaddrlen);
+
+	*ercr = Ercr_rxenab|Ercr_auienab|(Nrx-1);
+	*eimr = Ei_rxdone|Ei_txretry|Ei_txdone;
+	
+	iprint("reset: ercr %.4uX\n", *ercr);
+	return 0;
+}
+
+void
+ethersaturnlink(void)
+{
+	addethercard("saturn", reset);
+}
+

+ 163 - 0
sys/src/9/ppc/fns.h

@@ -0,0 +1,163 @@
+#include "../port/portfns.h"
+
+void	archinit(void);
+int	cistrcmp(char*, char*);
+int	cistrncmp(char*, char*, int);
+void	clockcheck(void);
+void	clockinit(void);
+void	clockintr(Ureg*);
+void	clockintrsched(void);
+void	cpuidentify(void);
+void	cpuidprint(void);
+void	dbgputc(int c);
+void	dcacheenb(void);
+void	dcflush(void*, ulong);
+void	dczap(void*, ulong);
+void	delay(int);
+void	delayloopinit(void);
+void	dmiss(void);
+void*	dpalloc(int);
+void	dumpregs(Ureg*);
+void	eieio(void);
+void	evenaddr(ulong);
+void	faultpower(Ureg*, ulong addr, int read);
+void	flashprogpower(int);
+void	fpinit(void);
+void	fpgareset(void);
+void	fprestore(FPsave*);
+void	fpsave(FPsave*);
+void	fptrap(Ureg*);
+char*	getconf(char*);
+ulong	getdar(void);
+ulong	getdcmp(void);
+ulong	getdec(void);
+ulong	getdepn(void);
+ulong	getdmiss(void);
+ulong	getdsisr(void);
+ulong	gethash1(void);
+ulong	gethash2(void);
+ulong	gethid0(void);
+ulong	gethid1(void);
+ulong	geticmp(void);
+ulong	geticmp(void);
+ulong	getimiss(void);
+ulong	getimmr(void);
+ulong	getmsr(void);
+ulong	getpvr(void);
+ulong	getrpa(void);
+ulong	getsdr1(void);
+ulong	getsr(int);
+ulong	getsrr1(void);
+ulong	gettbl(void);
+ulong	gettbu(void);
+void	gotopc(ulong);
+void	hwintrinit(void);
+void	icacheenb(void);
+void	icflush(void*, ulong);
+void	idle(void);
+#define	idlehands()		/* nothing to do in the runproc */
+void	imiss(void);
+int	inb(int);
+ulong	inl(int);
+ushort	ins(int);
+void	insb(int, void*, int);
+void	insl(int, void*, int);
+void	inss(int, void*, int);
+void	intr(Ureg*);
+void	intrenable(int, void (*)(Ureg*, void*), void*, char*);
+void	intrdisable(int, void (*)(Ureg*, void*), void*, char*);
+int	ioalloc(int, int, int, char*);
+void	iofree(int);
+void	ioinit(void);
+int	iprint(char*, ...);
+int	isaconfig(char*, int, ISAConf*);
+void	kbdinit(void);
+void	kbdreset(void);
+void	kernelmmu(void);
+void	kfpinit(void);
+#define	kmapinval()
+void	links(void);
+void	vectordisable(Vctl *);
+int	vectorenable(Vctl *);
+void	intack(void);
+void	intend(int);
+int	intvec(void);
+void	l2disable(void);
+void	mathinit(void);
+void	mmuinit(void);
+void	mmusweep(void*);
+void	mpicdisable(int);
+void	mpicenable(int, Vctl*);
+int	mpiceoi(int);
+int	mpicintack(void);
+int	newmmupid(void);
+void	outb(int, int);
+void	outl(int, ulong);
+void	outs(int, ushort);
+void	outsb(int, void*, int);
+void	outsl(int, void*, int);
+void	outss(int, void*, int);
+int	pcicfgr16(Pcidev*, int);
+int	pcicfgr32(Pcidev*, int);
+int	pcicfgr8(Pcidev*, int);
+void	pcicfgw16(Pcidev*, int, int);
+void	pcicfgw32(Pcidev*, int, int);
+void	pcicfgw8(Pcidev*, int, int);
+void	pcihinv(Pcidev*);
+void	pcireset(void);
+int	pciscan(int, Pcidev **);
+void	pcisetbme(Pcidev*);
+void	procsave(Proc*);
+void	procsetup(Proc*);
+void	putcasid(ulong);
+void	putdcmp(ulong);
+void	putdec(ulong);
+void	puthash1(ulong);
+void	puthash2(ulong);
+void	puthid0(ulong);
+void	puthid1(ulong);
+void	puthid2(ulong);
+void	puticmp(ulong);
+void	puticmp(ulong);
+void	putmsr(ulong);
+void	putrpa(ulong);
+void	putsdr1(ulong);
+void	putsdr1(ulong);
+void	putsr(int, ulong);
+void	putsrr1(ulong);
+void	raveninit(void);
+void	screeninit(void);
+int	screenprint(char*, ...);		/* debugging */
+int	segflush(void*, ulong);
+void	sethvec(int, void (*)(void));
+void	setmvec(int, void (*)(void), void (*)(void));
+void	sharedseginit(void);
+void	console(void);
+void	sync(void);
+int	tas(void*);
+void	timeradd(Timer *);
+void	timerdel(Timer *);
+void	timerinit(void);
+void	tlbflush(ulong);
+void	tlbflushall(void);
+void	tlbld(ulong);
+void	tlbli(ulong);
+void	tlbvec(void);
+void	touser(void*);
+void	trapinit(void);
+void	trapvec(void);
+void	uartinstall(void);
+void	uartwait(void);	/* debugging */
+#define	userureg(ur) (((ur)->status & MSR_PR) != 0)
+#define	waserror()	(up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1]))
+void	watchreset(void);
+void	wbflush(void);
+#define KADDR(a)	((void*)((ulong)(a)|KZERO))
+#define PADDR(a)	((((ulong)(a)&0xf0000000)==0xf0000000)?(ulong)(a):((ulong)(a)&~KZERO))
+#define coherence()	eieio()
+ulong pcibarsize(Pcidev *, int);
+void pciclrbme(Pcidev*);
+uchar pciipin(Pcidev *, uchar);
+Pcidev* pcimatch(Pcidev*, int, int);
+Pcidev* pcimatchtbdf(int);
+#define procrestore(p)

+ 29 - 0
sys/src/9/ppc/init9.s

@@ -0,0 +1,29 @@
+/* this is the same as a c program:
+ * main(char *argv0){
+ *	startboot(argv0, &argv0);
+ * }
+ *
+ * it is in asm because we need to set the SB before
+ * doing it and the only way to do this in c drags in
+ * too many other routines.
+ */
+
+TEXT	_main(SB),$8
+
+	MOVW	$setSB(SB), R2
+
+	/* make a frame */
+	SUB	$16,R1
+
+	/* argv0 is already passed to us in R3 so it is already the first arg */
+
+	/* copy argv0 into the stack and push its address as the second arg */
+	MOVW	R3,0x14(R1)
+	ADD	$0x14,R1,R6
+	MOVW	R6,0x8(R1)
+
+	BL	startboot(SB)
+
+	/* should never get here */
+loop:
+	BR	loop

+ 25 - 0
sys/src/9/ppc/initcode

@@ -0,0 +1,25 @@
+#include "/sys/src/libc/9syscall/sys.h"
+
+/*
+ *  we pass in the argument of the exec parameters as 0(FP)
+ */
+
+TEXT	main(SB),$8
+
+	MOVW	$setSB(SB), R2
+	MOVW	$boot(SB), R3
+	ADD	$12, R1, R4	/* get a pointer to 0(FP) */
+	MOVW	R3, 4(R1)
+	MOVW	R4, 8(R1)
+	MOVW	$EXEC, R3
+	SYSCALL
+
+	/* should never get here */
+loop:
+	BR	loop
+
+DATA	boot+0(SB)/5,$"/boot"
+DATA	boot+5(SB)/5,$"/boot"
+DATA	bootv+0(SB)/4,$boot+6(SB)
+GLOBL	boot+0(SB),$11
+GLOBL	bootv+0(SB),$8

+ 30 - 0
sys/src/9/ppc/io.h

@@ -0,0 +1,30 @@
+
+enum {
+	BusCBUS		= 0,		/* Corollary CBUS */
+	BusCBUSII,			/* Corollary CBUS II */
+	BusEISA,				/* Extended ISA */
+	BusFUTURE,			/* IEEE Futurebus */
+	BusINTERN,			/* Internal bus */
+	BusISA,				/* Industry Standard Architecture */
+	BusMBI,				/* Multibus I */
+	BusMBII,				/* Multibus II */
+	BusMCA,				/* Micro Channel Architecture */
+	BusMPI,				/* MPI */
+	BusMPSA,				/* MPSA */
+	BusNUBUS,			/* Apple Macintosh NuBus */
+	BusPCI,				/* Peripheral Component Interconnect */
+	BusPCMCIA,			/* PC Memory Card International Association */
+	BusTC,				/* DEC TurboChannel */
+	BusVL,				/* VESA Local bus */
+	BusVME,				/* VMEbus */
+	BusXPRESS,			/* Express System Bus */
+	BusPPC				/* Power PC internal bus */
+};
+
+#define MKBUS(t,b,d,f)	(((t)<<24)|(((b)&0xFF)<<16)|(((d)&0x1F)<<11)|(((f)&0x07)<<8))
+#define BUSFNO(tbdf)	(((tbdf)>>8)&0x07)
+#define BUSDNO(tbdf)	(((tbdf)>>11)&0x1F)
+#define BUSBNO(tbdf)	(((tbdf)>>16)&0xFF)
+#define BUSTYPE(tbdf)	((tbdf)>>24)
+#define BUSBDF(tbdf)	((tbdf)&0x00FFFF00)
+#define BUSUNKNOWN	(-1)

+ 841 - 0
sys/src/9/ppc/l.s

@@ -0,0 +1,841 @@
+#include	"mem.h"
+
+/* use of SPRG registers in save/restore */
+#define	SAVER0	SPRG0
+#define	SAVER1	SPRG1
+#define	SAVELR	SPRG2
+#define	SAVEXX	SPRG3
+
+/* special instruction definitions */
+#define	BDNZ	BC	16,0,
+#define	BDNE	BC	0,2,
+#define	MTCRF(r,crm)	WORD	$((31<<26)|((r)<<21)|(crm<<12)|(144<<1))
+
+/* #define	TLBIA	WORD	$((31<<26)|(370<<1)) Not implemented on the 603e */
+#define	TLBSYNC	WORD	$((31<<26)|(566<<1))
+#define	TLBLI(n)	WORD	$((31<<26)|((n)<<11)|(1010<<1))
+#define	TLBLD(n)	WORD	$((31<<26)|((n)<<11)|(978<<1))
+
+/* on some models mtmsr doesn't synchronise enough (eg, 603e) */
+#define	MSRSYNC	SYNC
+
+#define	UREGSPACE	(UREGSIZE+8)
+
+	TEXT start(SB), $-4
+
+	/*
+	 * setup MSR
+	 * turn off interrupts
+	 * use 0x000 as exception prefix
+	 * enable machine check
+	 */
+	MOVW	MSR, R3
+	MOVW	$(MSR_EE|MSR_IP), R4
+	ANDN	R4, R3
+	OR		$(MSR_ME), R3
+	SYNC
+	MOVW	R3, MSR
+	MSRSYNC
+
+	/* except during trap handling, R0 is zero from now on */
+	MOVW	$0, R0
+
+	/* setup SB for pre mmu */
+	MOVW	$setSB(SB), R2
+	MOVW	$KZERO, R3
+	ANDN	R3, R2
+
+	BL		mmuinit0(SB)
+
+	/* running with MMU on!! */
+
+	/* set R2 to correct value */
+	MOVW	$setSB(SB), R2
+
+	/* set up Mach */
+	MOVW	$MACHADDR, R(MACH)
+	ADD		$(MACHSIZE-8), R(MACH), R1	/* set stack */
+
+	MOVW	R0, R(USER)		/* up-> set to zero */
+	MOVW	R0, 0(R(MACH))	/* machno set to zero */
+
+	BL	main(SB)
+
+	RETURN		/* not reached */
+
+#ifdef ucuconf
+#include "lucu.h"
+#endif
+#ifdef blastconf
+#include "lblast.h"
+#endif
+
+TEXT	kfpinit(SB), $0
+	MOVFL	$0,FPSCR(7)
+	MOVFL	$0xD,FPSCR(6)	/* VE, OE, ZE */
+	MOVFL	$0, FPSCR(5)
+	MOVFL	$0, FPSCR(3)
+	MOVFL	$0, FPSCR(2)
+	MOVFL	$0, FPSCR(1)
+	MOVFL	$0, FPSCR(0)
+
+	FMOVD	$4503601774854144.0, F27
+	FMOVD	$0.5, F29
+	FSUB		F29, F29, F28
+	FADD	F29, F29, F30
+	FADD	F30, F30, F31
+	FMOVD	F28, F0
+	FMOVD	F28, F1
+	FMOVD	F28, F2
+	FMOVD	F28, F3
+	FMOVD	F28, F4
+	FMOVD	F28, F5
+	FMOVD	F28, F6
+	FMOVD	F28, F7
+	FMOVD	F28, F8
+	FMOVD	F28, F9
+	FMOVD	F28, F10
+	FMOVD	F28, F11
+	FMOVD	F28, F12
+	FMOVD	F28, F13
+	FMOVD	F28, F14
+	FMOVD	F28, F15
+	FMOVD	F28, F16
+	FMOVD	F28, F17
+	FMOVD	F28, F18
+	FMOVD	F28, F19
+	FMOVD	F28, F20
+	FMOVD	F28, F21
+	FMOVD	F28, F22
+	FMOVD	F28, F23
+	FMOVD	F28, F24
+	FMOVD	F28, F25
+	FMOVD	F28, F26
+	RETURN
+
+TEXT	splhi(SB), $0
+	MOVW	LR, R31
+	MOVW	R31, 4(R(MACH))	/* save PC in m->splpc */
+	MOVW	MSR, R3
+	RLWNM	$0, R3, $~MSR_EE, R4
+	SYNC
+	MOVW	R4, MSR
+	MSRSYNC
+	RETURN
+
+TEXT	splx(SB), $0
+	/* fall though */
+
+TEXT	splxpc(SB), $0
+	MOVW	LR, R31
+	MOVW	R31, 4(R(MACH))	/* save PC in m->splpc */
+	MOVW	MSR, R4
+	RLWMI	$0, R3, $MSR_EE, R4
+	SYNC
+	MOVW	R4, MSR
+	MSRSYNC
+	RETURN
+
+TEXT	spllo(SB), $0
+	MOVW	MSR, R3
+	OR	$MSR_EE, R3, R4
+	SYNC
+	MOVW	R4, MSR
+	MSRSYNC
+	RETURN
+
+TEXT	spldone(SB), $0
+	RETURN
+
+TEXT	islo(SB), $0
+	MOVW	MSR, R3
+	RLWNM	$0, R3, $MSR_EE, R3
+	RETURN
+
+TEXT	setlabel(SB), $-4
+	MOVW	LR, R31
+	MOVW	R1, 0(R3)
+	MOVW	R31, 4(R3)
+	MOVW	$0, R3
+	RETURN
+
+TEXT	gotolabel(SB), $-4
+	MOVW	4(R3), R31
+	MOVW	R31, LR
+	MOVW	0(R3), R1
+	MOVW	$1, R3
+	RETURN
+
+TEXT	touser(SB), $-4
+	MOVW	$(UTZERO+32), R5	/* header appears in text */
+	MOVW	$(MSR_EE|MSR_PR|MSR_IR|MSR_DR|MSR_RI), R4
+	MOVW	R4, SPR(SRR1)
+	MOVW	R3, R1
+	MOVW	R5, SPR(SRR0)
+	RFI
+
+TEXT	dczap(SB), $-4	/* dczap(virtaddr, count) */
+	MOVW	n+4(FP), R4
+	RLWNM	$0, R3, $~(CACHELINESZ-1), R5
+	CMP		R4, $0
+	BLE		dcz1
+	SUB		R5, R3
+	ADD		R3, R4
+	ADD		$(CACHELINESZ-1), R4
+	SRAW	$CACHELINELOG, R4
+	MOVW	R4, CTR
+dcz0:	DCBI	(R5)
+	ADD		$CACHELINESZ, R5
+	BDNZ	dcz0
+dcz1:
+	SYNC
+	RETURN
+
+TEXT	dcflush(SB), $-4	/* dcflush(virtaddr, count) */
+	MOVW	n+4(FP), R4
+	RLWNM	$0, R3, $~(CACHELINESZ-1), R5
+	CMP		R4, $0
+	BLE		dcf1
+	SUB		R5, R3
+	ADD		R3, R4
+	ADD		$(CACHELINESZ-1), R4
+	SRAW	$CACHELINELOG, R4
+	MOVW	R4, CTR
+dcf0:	DCBST	(R5)
+	ADD		$CACHELINESZ, R5
+	BDNZ	dcf0
+dcf1:
+	SYNC
+	RETURN
+
+TEXT	icflush(SB), $-4	/* icflush(virtaddr, count) */
+	MOVW	n+4(FP), R4
+	RLWNM	$0, R3, $~(CACHELINESZ-1), R5
+	CMP		R4, $0
+	BLE		icf1
+	SUB		R5, R3
+	ADD		R3, R4
+	ADD		$(CACHELINESZ-1), R4
+	SRAW	$CACHELINELOG, R4
+	MOVW	R4, CTR
+icf0:	ICBI		(R5)		/* invalidate the instruction cache */
+	ADD		$CACHELINESZ, R5
+	BDNZ	icf0
+	ISYNC
+icf1:
+	RETURN
+
+TEXT	tas(SB), $0
+	MOVW	R3, R4
+	MOVW	$0xdead,R5
+tas1:
+	DCBF	(R4)	/* fix for 603x bug */
+	SYNC
+	LWAR	(R4), R3
+	CMP		R3, $0
+	BNE		tas0
+	STWCCC	R5, (R4)
+	BNE		tas1
+	EIEIO
+tas0:
+	SYNC
+	RETURN
+
+TEXT	tlbflushall(SB), $0
+	MOVW	$TLBENTRIES, R3
+	MOVW	R3, CTR
+	MOVW	$0, R4
+	ISYNC
+tlbflushall0:
+	TLBIE	R4
+	SYNC
+	ADD		$BIT(19), R4
+	BDNZ	tlbflushall0
+	TLBSYNC
+	RETURN
+
+TEXT	tlbflush(SB), $0
+	ISYNC
+	TLBIE	R3
+	SYNC
+	TLBSYNC
+	RETURN
+
+TEXT	gotopc(SB), $0
+	MOVW	R3, CTR
+	MOVW	LR, R31	/* for trace back */
+	BR		(CTR)
+
+/* On an imiss, we get here.  If we can resolve it, we do.
+ * Otherwise take the real trap.  The code at the vector is
+ *	MOVW	R0, SPR(SAVER0)	No point to this, of course
+ *	MOVW	LR, R0
+ *	MOVW	R0, SPR(SAVELR) 
+ *	BL		imiss(SB)			or dmiss, as the case may be
+ *	BL		tlbvec(SB)
+ */
+TEXT	imiss(SB), $-4
+	/* Statistics */
+	MOVW	$MACHPADDR, R1
+	MOVW	0xc(R1), R3		/* count m->tlbfault */
+	ADD		$1, R3
+	MOVW	R3, 0xc(R1)
+	MOVW	0x10(R1), R3		/* count m->imiss */
+	ADD		$1, R3
+	MOVW	R3, 0x10(R1)
+
+	/* Real work */
+	MOVW	SPR(HASH1), R1	/* (phys) pointer into the hash table */
+	ADD		$BY2PTEG, R1, R2	/* end pointer */
+	MOVW	SPR(iCMP), R3		/* pattern to look for */
+imiss1:
+	MOVW	(R1), R0
+	CMP		R3, R0
+	BEQ		imiss2			/* found the entry */
+	ADD		$8, R1
+	CMP		R1, R2			/* test end of loop */
+	BNE		imiss1			/* Loop */
+	/* Failed to find an entry; take the full trap */
+	MOVW	SPR(SRR1), R1
+	MTCRF(1,0x80)			/* restore CR0 bits (they're auto saved in SRR1) */
+	RETURN
+imiss2:
+	/* Found the entry */
+	MOVW	4(R1), R2			/* Phys addr */
+	MOVW	R2, SPR(RPA)
+	MOVW	SPR(IMISS),R3
+	TLBLI(3)
+
+	/* Restore Registers */
+	MOVW	SPR(SRR1), R1		/* Restore the CR0 field of the CR register from SRR1 */
+	MTCRF(1,0x80)
+	MOVW	SPR(SAVELR), R0
+	MOVW	R0, LR
+	RFI
+
+/* On a data load or store miss, we get here.  If we can resolve it, we do.
+ * Otherwise take the real trap
+ */
+TEXT	dmiss(SB), $-4
+	/* Statistics */
+	MOVW	$MACHPADDR, R1
+	MOVW	0xc(R1), R3		/* count m->tlbfault */
+	ADD		$1, R3
+	MOVW	R3, 0xc(R1)
+	MOVW	0x14(R1), R3		/* count m->dmiss */
+	ADD		$1, R3
+	MOVW	R3, 0x20(R1)
+	/* Real work */
+	MOVW	SPR(HASH1), R1	/* (phys) pointer into the hash table */
+	ADD		$BY2PTEG, R1, R2	/* end pointer */
+	MOVW	SPR(DCMP), R3		/* pattern to look for */
+dmiss1:
+	MOVW	(R1), R0
+	CMP		R3, R0
+	BEQ		dmiss2			/* found the entry */
+	ADD		$8, R1
+	CMP		R1, R2			/* test end of loop */
+	BNE		dmiss1			/* Loop */
+	/* Failed to find an entry; take the full trap */
+	MOVW	SPR(SRR1), R1
+	MTCRF(1,0x80)			/* restore CR0 bits (they're auto saved in SRR1) */
+	RETURN
+dmiss2:
+	/* Found the entry */
+	MOVW	4(R1), R2			/* Phys addr */
+	MOVW	R2, SPR(RPA)
+	MOVW	SPR(DMISS),R3
+	TLBLD(3)
+	/* Restore Registers */
+	MOVW	SPR(SRR1), R1		/* Restore the CR0 field of the CR register from SRR1 */
+	MTCRF(1,0x80)
+	MOVW	SPR(SAVELR), R0
+	MOVW	R0, LR
+	RFI
+
+/*
+ * When a trap sets the TGPR bit (TLB miss traps on the 8260 do this),
+ * registers get remapped: R0-R31 are temporarily inaccessible,
+ * and Temporary Registers TR0-TR3 are mapped onto R0-R3.
+ * While this bit is set, R4-R31 cannot be used.
+ * The code at the vector has executed this code before
+ * coming to tlbvec:
+ *	MOVW	R0, SPR(SAVER0)	No point to this, of course
+ *	MOVW	LR, R0
+ *	MOVW	R0, SPR(SAVELR) 
+ *	BL		tlbvec(SB)
+ * SAVER0 can be reused.  We're not interested in the value of TR0
+ */
+TEXT tlbvec(SB), $-4
+ 	MOVW	MSR, R1
+	RLWNM	$0, R1, $~MSR_TGPR, R1	/* Clear the dreaded TGPR bit in the MSR */
+	SYNC
+	MOVW	R1, MSR
+	MSRSYNC
+	/* Now the GPRs are what they're supposed to be, save R0 again */
+	MOVW	R0, SPR(SAVER0)
+	/* Fall through to trapvec */
+
+/*
+ * traps force memory mapping off.
+ * the following code has been executed at the exception
+ * vector location
+ *	MOVW R0, SPR(SAVER0)
+ *	MOVW LR, R0
+ *	MOVW R0, SPR(SAVELR) 
+ *	bl	trapvec(SB)
+ *
+ */
+TEXT	trapvec(SB), $-4
+	MOVW	LR, R0
+	MOVW	R1, SPR(SAVER1)
+	MOVW	R0, SPR(SAVEXX)	/* vector */
+
+	/* did we come from user space */
+	MOVW	SPR(SRR1), R0
+	MOVW	CR, R1
+	MOVW	R0, CR
+	BC		4,17,ktrap
+
+	/* switch to kernel stack */
+	MOVW	R1, CR
+	MOVW	$MACHPADDR, R1		/* PADDR(m->) */
+	MOVW	8(R1), R1				/* m->proc  */
+	RLWNM	$0, R1, $~KZERO, R1		/* PADDR(m->proc) */
+	MOVW	8(R1), R1				/* m->proc->kstack */
+	RLWNM	$0, R1, $~KZERO, R1		/* PADDR(m->proc->kstack) */
+	ADD		$(KSTACK-UREGSIZE), R1	/* make room on stack */
+
+	BL		saveureg(SB)
+	BL		trap(SB)
+	BR		restoreureg
+
+ktrap:
+	MOVW	R1, CR
+	MOVW	SPR(SAVER1), R1
+	RLWNM	$0, R1, $~KZERO, R1		/* set stack pointer */
+	SUB		$UREGSPACE, R1
+
+	BL		saveureg(SB)			/* addressed relative to PC */
+	BL		trap(SB)
+	BR		restoreureg
+
+/*
+ * enter with stack set and mapped.
+ * on return, SB (R2) has been set, and R3 has the Ureg*,
+ * the MMU has been re-enabled, kernel text and PC are in KSEG,
+ * R(MACH) has been set, and R0 contains 0.
+ *
+ */
+TEXT	saveureg(SB), $-4
+/*
+ * save state
+ */
+	MOVMW	R2, 48(R1)				/* save r2 .. r31 in 48(R1) .. 164(R1) */
+	MOVW	$MACHPADDR, R(MACH)		/* PADDR(m->) */
+	MOVW	8(R(MACH)), R(USER)		/* up-> */
+	MOVW	$MACHADDR, R(MACH)		/* m-> */
+	MOVW	SPR(SAVER1), R4
+	MOVW	R4, 44(R1)
+	MOVW	SPR(SAVER0), R5
+	MOVW	R5, 40(R1)
+	MOVW	CTR, R6
+	MOVW	R6, 36(R1)
+	MOVW	XER, R4
+	MOVW	R4, 32(R1)
+	MOVW	CR, R5
+	MOVW	R5, 28(R1)
+	MOVW	SPR(SAVELR), R6			/* LR */
+	MOVW	R6, 24(R1)
+	/* pad at 20(R1) */
+	MOVW	SPR(SRR0), R0
+	MOVW	R0, 16(R1)				/* old PC */
+	MOVW	SPR(SRR1), R0
+	MOVW	R0, 12(R1)				/* old status */
+	MOVW	SPR(SAVEXX), R0
+	MOVW	R0, 8(R1)					/* cause/vector */
+	MOVW	SPR(DCMP), R0
+	MOVW	R0, (160+8)(R1)
+	MOVW	SPR(iCMP), R0
+	MOVW	R0, (164+8)(R1)
+	MOVW	SPR(DMISS), R0
+	MOVW	R0, (168+8)(R1)
+	MOVW	SPR(IMISS), R0
+	MOVW	R0, (172+8)(R1)
+	MOVW	SPR(HASH1), R0
+	MOVW	R0, (176+8)(R1)
+	MOVW	SPR(HASH2), R0
+	MOVW	R0, (180+8)(R1)
+	MOVW	SPR(DAR), R0
+	MOVW	R0, (184+8)(R1)
+	MOVW	SPR(DSISR), R0
+	MOVW	R0, (188+8)(R1)
+	ADD		$8, R1, R3					/* Ureg* */
+	OR		$KZERO, R3				/* fix ureg */
+	STWCCC	R3, (R1)					/* break any pending reservations */
+	MOVW	$0, R0					/* compiler/linker expect R0 to be zero */
+	MOVW	$setSB(SB), R2				/* SB register */
+
+	MOVW	MSR, R5
+	OR		$(MSR_IR|MSR_DR|MSR_FP|MSR_RI), R5	/* enable MMU */
+	MOVW	R5, SPR(SRR1)
+	MOVW	LR, R31
+	OR		$KZERO, R31				/* return PC in KSEG0 */
+	MOVW	R31, SPR(SRR0)
+	OR		$KZERO, R1				/* fix stack pointer */
+	RFI								/* returns to trap handler */
+
+/*
+ * restore state from Ureg and return from trap/interrupt
+ */
+TEXT	forkret(SB), $0
+	BR	restoreureg
+
+restoreureg:
+	MOVMW	48(R1), R2				/* restore r2 through r31 */
+	/* defer R1 */
+	MOVW	40(R1), R0
+	MOVW	R0, SPR(SAVER0)			/* resave saved R0 */
+	MOVW	36(R1), R0
+	MOVW	R0, CTR
+	MOVW	32(R1), R0
+	MOVW	R0, XER
+	MOVW	28(R1), R0
+	MOVW	R0, CR					/* Condition register*/
+	MOVW	24(R1), R0
+	MOVW	R0, LR
+	/* pad, skip */
+	MOVW	16(R1), R0
+	MOVW	R0, SPR(SRR0)				/* old PC */
+	MOVW	12(R1), R0
+	MOVW	R0, SPR(SRR1)				/* old MSR */
+	/* cause, skip */
+	MOVW	44(R1), R1				/* old SP */
+	MOVW	SPR(SAVER0), R0
+	RFI
+
+TEXT	getpvr(SB), $0
+	MOVW	SPR(PVR), R3
+	RETURN
+
+TEXT	getdec(SB), $0
+	MOVW	SPR(DEC), R3
+	RETURN
+
+TEXT	putdec(SB), $0
+	MOVW	R3, SPR(DEC)
+	RETURN
+
+TEXT	getdar(SB), $0
+	MOVW	SPR(DAR), R3
+	RETURN
+
+TEXT	getdsisr(SB), $0
+	MOVW	SPR(DSISR), R3
+	RETURN
+
+TEXT	getmsr(SB), $0
+	MOVW	MSR, R3
+	RETURN
+
+TEXT	putmsr(SB), $0
+	MOVW	R3, MSR
+	MSRSYNC
+	RETURN
+
+TEXT	putsdr1(SB), $0
+	SYNC
+	MOVW	R3, SPR(SDR1)
+	ISYNC
+	RETURN
+
+TEXT	putsr(SB), $0
+	MOVW	4(FP), R4
+	SYNC
+	MOVW	R4, SEG(R3)
+	MSRSYNC
+	RETURN
+
+TEXT	getsr(SB), $0
+	MOVW	SEG(R3), R3
+	RETURN
+
+TEXT	gethid0(SB), $0
+	MOVW	SPR(HID0), R3
+	RETURN
+
+TEXT	puthid0(SB), $0
+	SYNC
+	ISYNC
+	MOVW	R3, SPR(HID0)
+	SYNC
+	RETURN
+
+TEXT	gethid1(SB), $0
+	MOVW	SPR(HID1), R3
+	RETURN
+
+TEXT	gethid2(SB), $0
+	MOVW	SPR(HID2), R3
+	RETURN
+
+TEXT	puthid2(SB), $0
+	MOVW	R3, SPR(HID2)
+	RETURN
+
+TEXT	eieio(SB), $0
+	EIEIO
+	RETURN
+
+TEXT	sync(SB), $0
+	SYNC
+	RETURN
+
+/* Power PC 603e specials */
+TEXT	getimiss(SB), $0
+	MOVW	SPR(IMISS), R3
+	RETURN
+
+TEXT	geticmp(SB), $0
+	MOVW	SPR(iCMP), R3
+	RETURN
+
+TEXT	puticmp(SB), $0
+	MOVW	R3, SPR(iCMP)
+	RETURN
+
+TEXT	getdmiss(SB), $0
+	MOVW	SPR(DMISS), R3
+	RETURN
+
+TEXT	getdcmp(SB), $0
+	MOVW	SPR(DCMP), R3
+	RETURN
+
+TEXT	putdcmp(SB), $0
+	MOVW	R3, SPR(DCMP)
+	RETURN
+
+TEXT	getsdr1(SB), $0
+	MOVW	SPR(SDR1), R3
+	RETURN
+
+TEXT	gethash1(SB), $0
+	MOVW	SPR(HASH1), R3
+	RETURN
+
+TEXT	puthash1(SB), $0
+	MOVW	R3, SPR(HASH1)
+	RETURN
+
+TEXT	gethash2(SB), $0
+	MOVW	SPR(HASH2), R3
+	RETURN
+
+TEXT	puthash2(SB), $0
+	MOVW	R3, SPR(HASH2)
+	RETURN
+
+TEXT	getrpa(SB), $0
+	MOVW	SPR(RPA), R3
+	RETURN
+
+TEXT	putrpa(SB), $0
+	MOVW	R3, SPR(RPA)
+	RETURN
+
+TEXT tlbli(SB), $0
+	TLBLI(3)
+	ISYNC
+	RETURN
+
+TEXT tlbld(SB), $0
+	SYNC
+	TLBLD(3)
+	ISYNC
+	RETURN
+
+TEXT	getsrr1(SB), $0
+	MOVW	SPR(SRR1), R3
+	RETURN
+
+TEXT	putsrr1(SB), $0
+	MOVW	R3, SPR(SRR1)
+	RETURN
+
+TEXT	fpsave(SB), $0
+	FMOVD	F0, (0*8)(R3)
+	FMOVD	F1, (1*8)(R3)
+	FMOVD	F2, (2*8)(R3)
+	FMOVD	F3, (3*8)(R3)
+	FMOVD	F4, (4*8)(R3)
+	FMOVD	F5, (5*8)(R3)
+	FMOVD	F6, (6*8)(R3)
+	FMOVD	F7, (7*8)(R3)
+	FMOVD	F8, (8*8)(R3)
+	FMOVD	F9, (9*8)(R3)
+	FMOVD	F10, (10*8)(R3)
+	FMOVD	F11, (11*8)(R3)
+	FMOVD	F12, (12*8)(R3)
+	FMOVD	F13, (13*8)(R3)
+	FMOVD	F14, (14*8)(R3)
+	FMOVD	F15, (15*8)(R3)
+	FMOVD	F16, (16*8)(R3)
+	FMOVD	F17, (17*8)(R3)
+	FMOVD	F18, (18*8)(R3)
+	FMOVD	F19, (19*8)(R3)
+	FMOVD	F20, (20*8)(R3)
+	FMOVD	F21, (21*8)(R3)
+	FMOVD	F22, (22*8)(R3)
+	FMOVD	F23, (23*8)(R3)
+	FMOVD	F24, (24*8)(R3)
+	FMOVD	F25, (25*8)(R3)
+	FMOVD	F26, (26*8)(R3)
+	FMOVD	F27, (27*8)(R3)
+	FMOVD	F28, (28*8)(R3)
+	FMOVD	F29, (29*8)(R3)
+	FMOVD	F30, (30*8)(R3)
+	FMOVD	F31, (31*8)(R3)
+	MOVFL	FPSCR, F0
+	FMOVD	F0, (32*8)(R3)
+	RETURN
+
+TEXT	fprestore(SB), $0
+	FMOVD	(32*8)(R3), F0
+	MOVFL	F0, FPSCR
+	FMOVD	(0*8)(R3), F0
+	FMOVD	(1*8)(R3), F1
+	FMOVD	(2*8)(R3), F2
+	FMOVD	(3*8)(R3), F3
+	FMOVD	(4*8)(R3), F4
+	FMOVD	(5*8)(R3), F5
+	FMOVD	(6*8)(R3), F6
+	FMOVD	(7*8)(R3), F7
+	FMOVD	(8*8)(R3), F8
+	FMOVD	(9*8)(R3), F9
+	FMOVD	(10*8)(R3), F10
+	FMOVD	(11*8)(R3), F11
+	FMOVD	(12*8)(R3), F12
+	FMOVD	(13*8)(R3), F13
+	FMOVD	(14*8)(R3), F14
+	FMOVD	(15*8)(R3), F15
+	FMOVD	(16*8)(R3), F16
+	FMOVD	(17*8)(R3), F17
+	FMOVD	(18*8)(R3), F18
+	FMOVD	(19*8)(R3), F19
+	FMOVD	(20*8)(R3), F20
+	FMOVD	(21*8)(R3), F21
+	FMOVD	(22*8)(R3), F22
+	FMOVD	(23*8)(R3), F23
+	FMOVD	(24*8)(R3), F24
+	FMOVD	(25*8)(R3), F25
+	FMOVD	(26*8)(R3), F26
+	FMOVD	(27*8)(R3), F27
+	FMOVD	(28*8)(R3), F28
+	FMOVD	(29*8)(R3), F29
+	FMOVD	(30*8)(R3), F30
+	FMOVD	(31*8)(R3), F31
+	RETURN
+
+TEXT	dcacheenb(SB), $0
+	SYNC
+	MOVW	SPR(HID0), R4			/* Get HID0 and clear unwanted bits */
+	RLWNM	$0, R4, $~(HID_DLOCK), R4
+	MOVW	$(HID_DCFI|HID_DCE), R5
+	OR		R4, R5
+	MOVW	$HID_DCE, R3
+	OR		R4, R3
+	SYNC
+//	MOVW	R5, SPR(HID0)			/* Cache enable and flash invalidate */
+	MOVW	R3, SPR(HID0)			/* Cache enable */
+	SYNC
+	RETURN
+
+TEXT	icacheenb(SB), $0
+	SYNC
+	MOVW	SPR(HID0), R4			/* Get HID0 and clear unwanted bits */
+	RLWNM	$0, R4, $~(HID_ILOCK), R4
+	MOVW	$(HID_ICFI|HID_ICE), R5
+	OR		R4, R5
+	MOVW	$HID_ICE, R3
+	OR		R4, R3
+	SYNC
+	MOVW	R5, SPR(HID0)			/* Cache enable and flash invalidate */
+	MOVW	R3, SPR(HID0)			/* Cache enable */
+	SYNC
+	RETURN
+
+TEXT getl2pm(SB),$0
+	MOVW	SPR(1016),R3
+	RETURN
+
+TEXT getl2cr(SB),$0
+	MOVW	SPR(1017),R3
+	RETURN
+
+TEXT putl2cr(SB),$0
+	MOVW	R3,SPR(1017)
+	RETURN
+
+TEXT	dcachedis(SB), $0
+	SYNC
+/*	MOVW	SPR(HID0), R4			
+	RLWNM	$0, R4, $~(HID_DCE), R4
+	MOVW	R4, SPR(HID0)			/* L1 Cache disable */
+
+	MOVW	SPR(1017), R4			
+	RLWNM	$0, R4, $~(0x80000000), R4
+	MOVW	R4, SPR(1017)			/* L2 Cache disable */
+	
+	SYNC
+	RETURN
+
+TEXT	l2disable(SB), $0
+	SYNC
+	MOVW	SPR(1017), R4			
+	RLWNM	$0, R4, $~(0x80000000), R4
+	MOVW	R4, SPR(1017)			/* L2 Cache disable */
+	SYNC
+	RETURN
+
+TEXT getbats(SB),$0
+	MOVW	SPR(DBATU(0)),R4
+	MOVW	R4,0(R3)
+	MOVW	SPR(DBATL(0)),R4
+	MOVW	R4,4(R3)
+	MOVW	SPR(IBATU(0)),R4
+	MOVW	R4,8(R3)
+	MOVW	SPR(IBATL(0)),R4
+	MOVW	R4,12(R3)
+	MOVW	SPR(DBATU(1)),R4
+	MOVW	R4,16(R3)
+	MOVW	SPR(DBATL(1)),R4
+	MOVW	R4,20(R3)
+	MOVW	SPR(IBATU(1)),R4
+	MOVW	R4,24(R3)
+	MOVW	SPR(IBATL(1)),R4
+	MOVW	R4,28(R3)
+	MOVW	SPR(DBATU(2)),R4
+	MOVW	R4,32(R3)
+	MOVW	SPR(DBATL(2)),R4
+	MOVW	R4,36(R3)
+	MOVW	SPR(IBATU(2)),R4
+	MOVW	R4,40(R3)
+	MOVW	SPR(IBATL(2)),R4
+	MOVW	R4,44(R3)
+	MOVW	SPR(DBATU(3)),R4
+	MOVW	R4,48(R3)
+	MOVW	SPR(DBATL(3)),R4
+	MOVW	R4,52(R3)
+	MOVW	SPR(IBATU(3)),R4
+	MOVW	R4,56(R3)
+	MOVW	SPR(IBATL(3)),R4
+	MOVW	R4,60(R3)
+	RETURN
+
+TEXT setdbat0(SB),$0
+	MOVW	0(R3),R4
+	MOVW	R4,SPR(DBATU(0))
+	MOVW	4(R3),R4
+	MOVW	R4,SPR(DBATL(0))
+	RETURN

+ 66 - 0
sys/src/9/ppc/lblast.h

@@ -0,0 +1,66 @@
+/*
+ * on return from this function we will be running in virtual mode.
+ * We set up the Block Address Translation (BAT) registers thus:
+ * 1) first 3 BATs are 256M blocks, starting from KZERO->0
+ * 2) remaining BAT maps last 256M directly
+ */
+TEXT	mmuinit0(SB), $0
+	/* reset all the tlbs */
+	MOVW	$64, R3
+	MOVW	R3, CTR
+	MOVW	$0, R4
+tlbloop:
+	TLBIE	R4
+	SYNC
+	ADD		$BIT(19), R4
+	BDNZ	tlbloop
+	TLBSYNC
+
+	/* BATs 0 and 1 cover memory from 0x00000000 to 0x20000000 */
+
+	/* KZERO -> 0, IBAT and DBAT, 256 MB */
+	MOVW	$(KZERO|(0x7ff<<2)|2), R3
+	MOVW	$(PTEVALID|PTEWRITE), R4	/* PTEVALID => Cache coherency on */
+	MOVW	R3, SPR(IBATU(0))
+	MOVW	R4, SPR(IBATL(0))
+	MOVW	R3, SPR(DBATU(0))
+	MOVW	R4, SPR(DBATL(0))
+
+	/* KZERO+256M -> 256M, IBAT and DBAT, 256 MB */
+	ADD		$(1<<28), R3
+	ADD		$(1<<28), R4
+	MOVW	R3, SPR(IBATU(1))
+	MOVW	R4, SPR(IBATL(1))
+	MOVW	R3, SPR(DBATU(1))
+	MOVW	R4, SPR(DBATL(1))
+
+	/* FPGABASE -> FPGABASE, DBAT, 16 MB */
+	MOVW	$(FPGABASE|(0x7f<<2)|2), R3
+	MOVW	$(FPGABASE|PTEWRITE|PTEUNCACHED), R4	/* FPGA memory, don't cache */
+	MOVW	R3, SPR(DBATU(2))
+	MOVW	R4, SPR(DBATL(2))
+
+	/* IBAT 2 unused */
+	MOVW	R0, SPR(IBATU(2))
+	MOVW	R0, SPR(IBATL(2))
+
+	/* direct map last block, uncached, (not guarded, doesn't work for BAT), DBAT only */
+	MOVW	$(INTMEM|(0x7ff<<2)|2), R3
+	MOVW	$(INTMEM|PTEWRITE|PTEUNCACHED), R4	/* Don't set PTEVALID here */
+	MOVW	R3, SPR(DBATU(3))
+	MOVW	R4, SPR(DBATL(3))
+
+	/* IBAT 3 unused */
+	MOVW	R0, SPR(IBATU(3))
+	MOVW	R0, SPR(IBATL(3))
+
+	/* enable MMU */
+	MOVW	LR, R3
+	OR		$KZERO, R3
+	MOVW	R3, SPR(SRR0)	/* Stored PC for RFI instruction */
+	MOVW	MSR, R4
+	OR		$(MSR_IR|MSR_DR|MSR_RI|MSR_FP), R4
+	MOVW	R4, SPR(SRR1)
+	RFI		/* resume in kernel mode in caller */
+
+	RETURN

+ 39 - 0
sys/src/9/ppc/lucu.h

@@ -0,0 +1,39 @@
+/*
+ * on return from this function we will be running in virtual mode.
+ * We set up the Block Address Translation (BAT) registers thus:
+ * 1) first 3 BATs are 256M blocks, starting from KZERO->0
+ * 2) remaining BAT maps last 256M directly
+ */
+TEXT	mmuinit0(SB), $0
+	/* reset all the tlbs */
+	MOVW	$64, R3
+	MOVW	R3, CTR
+	MOVW	$0, R4
+
+tlbloop:
+	TLBIE	R4
+	SYNC
+	ADD		$BIT(19), R4
+	BDNZ	tlbloop
+	TLBSYNC
+
+	/* BATs 0 and 1 cover memory from 0x00000000 to 0x20000000 */
+
+	/* KZERO -> 0, IBAT2 and DBAT2, 256 MB */
+	MOVW	$(KZERO|(0x7ff<<2)|2), R3
+	MOVW	$(PTEVALID|PTEWRITE), R4	/* PTEVALID => Cache coherency on */
+	MOVW	R3, SPR(DBATU(2))
+	MOVW	R4, SPR(DBATL(2))
+	MOVW	R3, SPR(IBATU(2))
+	MOVW	R4, SPR(IBATL(2))
+
+	/* enable MMU */
+	MOVW	LR, R3
+	OR		$KZERO, R3
+	MOVW	R3, SPR(SRR0)	/* Stored PC for RFI instruction */
+	MOVW	MSR, R4
+	OR		$(MSR_IR|MSR_DR|MSR_RI|MSR_FP), R4
+	MOVW	R4, SPR(SRR1)
+	RFI		/* resume in kernel mode in caller */
+
+	RETURN

+ 638 - 0
sys/src/9/ppc/m8260.c

@@ -0,0 +1,638 @@
+/*
+ *	8260 specific stuff:
+ *		Interrupt handling
+ */
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"io.h"
+#include	"m8260.h"
+#include	"fns.h"
+
+enum {
+	Pin4 = BIT(4),
+};
+
+static union {
+	struct {
+		ulong	hi;
+		ulong	lo;
+	};
+	uvlong		val;
+} ticks;
+
+struct {
+	ulong	hi;
+	ulong	lo;
+} vec2mask[64]	= {
+[0]	= {0,			0	},	/* Error,			No interrupt */
+[1]	= {0,			BIT(16)},	/* I2C */
+[2]	= {0,			BIT(17)},	/* SPI */
+[3]	= {0,			BIT(18)},	/* Risc Timers */
+[4]	= {0,			BIT(19)},	/* SMC1 */
+[5]	= {0,			BIT(20)},	/* SMC2 */
+[6]	= {0,			BIT(21)},	/* IDMA1 */
+[7]	= {0,			BIT(22)},	/* IDMA2 */
+[8]	= {0,			BIT(23)},	/* IDMA3 */
+[9]	= {0,			BIT(24)},	/* IDMA4 */
+[10]	= {0,			BIT(25)},	/* SDMA */
+[11]	= {0,			0	},	/* Reserved */
+[12]	= {0,			BIT(27)},	/* Timer1 */
+[13]	= {0,			BIT(28)},	/* Timer2 */
+[14]	= {0,			BIT(29)},	/* Timer3 */
+[15]	= {0,			BIT(30)},	/* Timer4 */
+
+[16]	= {BIT(29),	0	},	/* TMCNT */
+[17]	= {BIT(30),	0	},	/* PIT */
+[18]	= {0,			0	},	/* Reserved */
+[19]	= {BIT(17),	0	},	/* IRQ1 */
+[20]	= {BIT(18),	0	},	/* IRQ2 */
+[21]	= {BIT(19),	0	},	/* IRQ3 */
+[22]	= {BIT(20),	0	},	/* IRQ4 */
+[23]	= {BIT(21),	0	},	/* IRQ5 */
+[24]	= {BIT(22),	0	},	/* IRQ6 */
+[25]	= {BIT(23),	0	},	/* IRQ7 */
+[26]	= {0,			0	},	/* Reserved */
+[27]	= {0,			0	},	/* Reserved */
+[28]	= {0,			0	},	/* Reserved */
+[29]	= {0,			0	},	/* Reserved */
+[30]	= {0,			0	},	/* Reserved */
+[31]	= {0,			0	},	/* Reserved */
+
+[32]	= {0,			BIT(0)},	/* FCC1 */
+[33]	= {0,			BIT(1)},	/* FCC2 */
+[34]	= {0,			BIT(2)},	/* FCC3 */
+[35]	= {0,			0	},	/* Reserved */
+[36]	= {0,			BIT(4)},	/* MCC1 */
+[37]	= {0,			BIT(5)},	/* MCC2 */
+[38]	= {0,			0	},	/* Reserved */
+[39]	= {0,			0	},	/* Reserved */
+[40]	= {0,			BIT(8)},	/* SCC1 */
+[41]	= {0,			BIT(9)},	/* SCC2 */
+[42]	= {0,			BIT(10)},	/* SCC3 */
+[43]	= {0,			BIT(11)},	/* SCC4 */
+[44]	= {0,			0	},	/* Reserved */
+[45]	= {0,			0	},	/* Reserved */
+[46]	= {0,			0	},	/* Reserved */
+[47]	= {0,			0	},	/* Reserved */
+
+[48]	= {BIT(15),	0	},	/* PC15 */
+[49]	= {BIT(14),	0	},	/* PC14 */
+[50]	= {BIT(13),	0	},	/* PC13 */
+[51]	= {BIT(12),	0	},	/* PC12 */
+[52]	= {BIT(11),	0	},	/* PC11 */
+[53]	= {BIT(10),	0	},	/* PC10 */
+[54]	= {BIT(9),		0	},	/* PC9 */
+[55]	= {BIT(8),		0	},	/* PC8 */
+[56]	= {BIT(7),		0	},	/* PC7 */
+[57]	= {BIT(6),		0	},	/* PC6 */
+[58]	= {BIT(5),		0	},	/* PC5 */
+[59]	= {BIT(4),		0	},	/* PC4 */
+[60]	= {BIT(3),		0	},	/* PC3 */
+[61]	= {BIT(2),		0	},	/* PC2 */
+[62]	= {BIT(1),		0	},	/* PC1 */
+[63]	= {BIT(0),		0	},	/* PC0 */
+};
+
+/* Blast memory layout:
+ *	CS0: FE000000 -> FFFFFFFF (Flash)
+ *	CS1: FC000000 -> FCFFFFFF (DSP hpi)
+ *	CS2: 00000000 -> 03FFFFFF (60x sdram)
+ *	CS3: 04000000 -> 04FFFFFF (FPGA)
+ *	CS4: 05000000 -> 06FFFFFF (local bus sdram)
+ *	CS5: 07000000 -> 0700FFFF (eeprom - not populated)
+ *	CS6: E0000000 -> E0FFFFFF (FPGA - 64bits)
+ *
+ * Main Board memory layout:
+ *	CS0: FE000000 -> FEFFFFFF (16 M FLASH)
+ *	CS1: FC000000 -> FCFFFFFF (16 M DSP1)
+ *	CS2: 00000000 -> 03FFFFFF (64 M SDRAM)
+ *	CS3: 04000000 -> 04FFFFFF (16M DSP2)
+ *	CS4: 05000000 -> 06FFFFFF (32 M Local SDRAM)
+ *	CS5: 07000000 -> 0700FFFF (eeprom - not populated)
+ *	CS6: unused
+ *	CS7: E0000000 -> E0FFFFFF (16 M FPGA)
+ */
+
+IMM* iomem;
+
+static Lock cpmlock;
+
+void
+fpgareset(void)
+{
+	print("fpga reset\n");
+
+	ioplock();
+
+	iomem->port[1].pdat &= ~Pin4;	/* force reset signal to 0 */
+	delay(100);
+	iomem->port[1].pdat |= Pin4;		/* force reset signal back to one */
+
+	iopunlock();
+}
+
+void
+hwintrinit(void)
+{
+	iomem->sicr = 2 << 8;
+	/* Write ones into most bits of the interrupt pending registers to clear interrupts */
+	iomem->sipnr_h = ~7;
+	iomem->sipnr_h = ~1;
+	/* Clear the interrupt masks, thereby disabling all interrupts */
+	iomem->simr_h = 0;
+	iomem->simr_l = 0;
+
+	iomem->sypcr &= ~2;	/* cause a machine check interrupt on memory timeout */
+
+	/* Flashed CS configuration is wrong for DSP2.  It's set to 64 bits, should be 16 */
+	if (iomem->bank[3].br != 0x04000001)
+		print("We got the wrong guy!\n");
+	else
+		iomem->bank[3].br = 0x04001001;	/* Set 16-bit port */
+
+	/*
+	 * FPGA is capable of doing 64-bit transfers.  To use these, set br to 0xe0000001.
+	 * Currently we use 32-bit transfers, because the 8260 does not easily do 64-bit operations.
+	 */
+	iomem->bank[6].br = 0xe0001801;
+	iomem->bank[6].or = 0xff000816;	/* Was 0xff000856; one wait state */
+
+	/* Initialize fpga reset pin */
+	iomem->port[1].pdir |= Pin4;	/* 1 is an output */
+	iomem->port[1].ppar &= ~Pin4;
+	iomem->port[1].pdat |= Pin4;		/* force reset signal back to one */
+}
+
+int
+vectorenable(Vctl *v)
+{
+	ulong hi, lo;
+
+	if (v->irq & ~0x3f){
+		print("m8260enable: interrupt vector %d out of range\n", v->irq);
+		return -1;
+	}
+	hi = vec2mask[v->irq].hi;
+	lo = vec2mask[v->irq].lo;
+	if (hi == 0 && lo == 0){
+		print("m8260enable: nonexistent vector %d\n", v->irq);
+		return -1;
+	}
+	/* Clear the interrupt before enabling */
+	iomem->sipnr_h |= hi;
+	iomem->sipnr_l |= lo;
+	/* Enable */
+	iomem->simr_h |= hi;
+	iomem->simr_l |= lo;
+	return v->irq;
+}
+
+void
+vectordisable(Vctl *v)
+{
+	ulong hi, lo;
+
+	if (v->irq & ~0x3f){
+		print("m8260disable: interrupt vector %d out of range\n", v->irq);
+		return;
+	}
+	hi = vec2mask[v->irq].hi;
+	lo = vec2mask[v->irq].lo;
+	if (hi == 0 && lo == 0){
+		print("m8260disable: nonexistent vector %d\n", v->irq);
+		return;
+	}
+	iomem->simr_h &= ~hi;
+	iomem->simr_l &= ~lo;
+}
+
+int
+intvec(void)
+{
+	return iomem->sivec >> 26;
+}
+
+void
+intend(int vno)
+{
+	/* Clear interrupt */
+	iomem->sipnr_h |= vec2mask[vno].hi;
+	iomem->sipnr_l |= vec2mask[vno].lo;
+}
+
+int
+m8260eoi(int)
+{
+	return 0;
+}
+
+int
+m8260isr(int)
+{
+	return 0;
+}
+
+void
+flashprogpower(int)
+{
+}
+
+enum {
+	TgcrCas 			= 0x80,
+	TgcrGm 			= 0x08,
+	TgcrStp 			= 0x2,	/* There are two of these, timer-2 bits are bits << 4 */
+	TgcrRst 			= 0x1,
+
+	TmrIclkCasc		= 0x00<<1,
+	TmrIclkIntclock	= 0x01<<1,
+	TmrIclkIntclock16	= 0x02<<1,
+	TmrIclkTin		= 0x03<<1,
+	TmrCERising		= 0x1 << 6,
+	TmrCEFalling		= 0x2 << 6,
+	TmrCEAny		= 0x3 << 6,
+	TmrFrr			= SBIT(12),
+	TmrOri			= SBIT(11),
+
+	TerRef			= SBIT(14),
+	TerCap			= SBIT(15),
+};
+
+uvlong
+fastticks(uvlong *hz)
+{
+	ulong count;
+	static Lock fasttickslock;
+
+	if (hz)
+		*hz = m->clkin>>1;
+	ilock(&fasttickslock);
+	count = iomem->tcnl1;
+	if (count < ticks.lo)
+		ticks.hi += 1;
+	ticks.lo = count;
+	iunlock(&fasttickslock);
+	return ticks.val;
+}
+
+void
+timerset(uvlong next)
+{
+	long offset;
+	uvlong now;
+	static int cnt;
+
+	now = fastticks(nil);
+	offset = next - now;
+	if (offset < 2500)
+		next = now + 2500;	/* 10000 instructions */
+	else if (offset > m->clkin / HZ){
+		print("too far in the future: offset %llux, now %llux\n", next, now);
+		next = now + m->clkin / HZ;
+	}
+	iomem->trrl1 = next;
+}
+
+void
+m8260timerintr(Ureg *u, void*)
+{
+	iomem->ter2 |= TerRef | TerCap;		/* Clear interrupt */
+	timerintr(u, 0);
+}
+
+void
+timerinit(void)
+{
+
+	iomem->tgcr1 = TgcrCas | TgcrGm;		/* cascade timers 1 & 2, normal gate mode */
+	iomem->tcnl1 = 0;
+	iomem->trrl1 = m->clkin / HZ;		/* first capture in 1/HZ seconds */
+	iomem->tmr1 = TmrIclkCasc;
+	iomem->tmr2 = TmrIclkIntclock | TmrOri;
+	intrenable(13, m8260timerintr, nil, "timer");	/* Timer 2 interrupt is on 13 */
+	iomem->tgcr1 |= TgcrRst << 4;
+}
+
+static void
+addseg(char *name, ulong start, ulong length)
+{
+	Physseg segbuf;
+
+	memset(&segbuf, 0, sizeof(segbuf));
+	segbuf.attr = SG_PHYSICAL;
+	kstrdup(&segbuf.name, name);
+	segbuf.pa = start;
+	segbuf.size = length;
+	if (addphysseg(&segbuf) == -1) {
+		print("addphysseg: %s\n", name);
+		return;
+	}
+}
+
+void
+sharedseginit(void)
+{
+	int i, j;
+	ulong base, size;
+	char name[16], *a, *b, *s;
+	static char *segnames[] = {
+		"fpga",
+		"dsp",
+	};
+
+	for (j = 0; j < nelem(segnames); j++){
+		for (i = 0; i < 8; i++){
+			snprint(name, sizeof name, "%s%d", segnames[j], i);
+			if ((a = getconf(name)) == nil)
+				continue;
+			if ((b = strstr(a, "mem=")) == nil){
+				print("blastseginit: %s: no base\n", name);
+				continue;
+			}
+			b += 4;
+			base = strtoul(b, nil, 0);
+			if (base == 0){
+				print("blastseginit: %s: bad base: %s\n", name, b);
+				continue;
+			}
+			if ((s = strstr(a, "size=")) == nil){
+				print("blastseginit: %s: no size\n", name);
+				continue;
+			}
+			s += 5;
+			size = strtoul(s, nil, 0);
+			if (size == 0){
+				print("blastseginit: %s: bad size: %s\n", name, s);
+				continue;
+			}
+			addseg(name, base, size);
+		}
+	}
+}
+
+void
+cpmop(int op, int dev, int mcn)
+{
+	ioplock();
+	eieio();
+	while(iomem->cpcr & 0x10000)
+		eieio();
+	iomem->cpcr = dev<<(31-10) | mcn<<(31-25) | op | 0x10000;
+	eieio();
+	while(iomem->cpcr & 0x10000)
+		eieio();
+	iopunlock();
+}
+
+/*
+ * connect SCCx clocks in NSMI mode (x=1 for USB)
+ */
+void
+sccnmsi(int x, int rcs, int tcs)
+{
+	ulong v;
+	int sh;
+
+	sh = (x-1)*8;	/* each SCCx field in sicr is 8 bits */
+	v = (((rcs&7)<<3) | (tcs&7)) << sh;
+	iomem->sicr = (iomem->sicr & ~(0xFF<<sh)) | v;
+}
+
+/*
+ * lock the shared IO memory and return a reference to it
+ */
+void
+ioplock(void)
+{
+	ilock(&cpmlock);
+}
+
+/*
+ * release the lock on the shared IO memory
+ */
+void
+iopunlock(void)
+{
+	eieio();
+	iunlock(&cpmlock);
+}
+
+BD*
+bdalloc(int n)
+{
+	static BD *palloc = ((Imap*)INTMEM)->bd;
+	BD *p;
+	
+	p = palloc;
+	if (palloc > ((Imap*)INTMEM)->bd + nelem(((Imap*)INTMEM)->bd)){
+		print("bdalloc: out of BDs\n");
+		return nil;
+	}
+	palloc += n;
+	return p;
+}
+
+/*
+ * Initialise receive and transmit buffer rings.  Only used for FCC
+ * EThernet now.
+ *
+ * Ioringinit will allocate the buffer descriptors in normal memory
+ * and NOT in Dual-Ported Ram, as prescribed by the MPC8260
+ * PowerQUICC II manual (Section 28.6).  When they are allocated
+ * in DPram and the Dcache is enabled, the processor will hang.
+ * This has been observed for the FCCs, it may or may not be true
+ * for SCCs or DMA.
+ * The SMC Uart buffer descriptors are not allocated here; (1) they
+ * can ONLY be in DPram and (2) they are not configured as a ring.
+ */
+int
+ioringinit(Ring* r, int nrdre, int ntdre, int bufsize)
+{
+	int i, x;
+	static uchar *dpmallocaddr;
+	static uchar *dpmallocend;
+
+	if (dpmallocaddr == nil){
+		dpmallocaddr = m->imap->dpram1;
+		dpmallocend = dpmallocaddr + sizeof(m->imap->dpram1);
+	}
+	/* the ring entries must be aligned on sizeof(BD) boundaries */
+	r->nrdre = nrdre;
+	if(r->rdr == nil)
+		r->rdr = xspanalloc(nrdre*sizeof(BD), 0, 8);
+	if(r->rrb == nil)
+		r->rrb = xspanalloc(nrdre*bufsize, 0, CACHELINESZ);
+	if(r->rdr == nil || r->rrb == nil)
+		return -1;
+	x = PADDR(r->rrb);
+	for(i = 0; i < nrdre; i++){
+		r->rdr[i].length = 0;
+		r->rdr[i].addr = x;
+		r->rdr[i].status = BDEmpty|BDInt;
+		x += bufsize;
+	}
+	r->rdr[i-1].status |= BDWrap;
+	r->rdrx = 0;
+
+	r->ntdre = ntdre;
+	if(r->tdr == nil)
+		r->tdr = xspanalloc(ntdre*sizeof(BD), 0, 8);
+	if(r->txb == nil)
+		r->txb = xspanalloc(ntdre*sizeof(Block*), 0, CACHELINESZ);
+	if(r->tdr == nil || r->txb == nil)
+		return -1;
+	for(i = 0; i < ntdre; i++){
+		r->txb[i] = nil;
+		r->tdr[i].addr = 0;
+		r->tdr[i].length = 0;
+		r->tdr[i].status = 0;
+	}
+	r->tdr[i-1].status |= BDWrap;
+	r->tdrh = 0;
+	r->tdri = 0;
+	r->ntq = 0;
+	return 0;
+}
+
+void
+machinit(void)
+{
+	ulong scmr;
+	int pllmf;
+	extern char* plan9inistr;
+
+	memset(m, 0, sizeof(*m));
+	m->cputype = getpvr()>>16;	/* pvr = 0x00810101 for the 8260 */
+	m->imap = (Imap*)INTMEM;
+	iomem = (IMM*)IOMEM;
+	m->flash = (uchar*)FLASHMEM;
+
+	m->loopconst = 1096;
+
+	/* Make sure Ethernet is disabled (boot code may have buffers allocated anywhere in memory) */
+	iomem->fcc[0].gfmr &= ~(BIT(27)|BIT(26));
+	iomem->fcc[1].gfmr &= ~(BIT(27)|BIT(26));
+	iomem->fcc[2].gfmr &= ~(BIT(27)|BIT(26));
+/*
+ * All systems with rev. A.1 (0K26N) silicon had serious problems when doing
+ * DMA transfers with data cache enabled (usually this shows when  using
+ * one of the FCC's with some traffic on the ethernet).  Allocating FCC buffer
+ * descriptors in main memory instead of DP ram solves this problem.
+ */
+
+	/* Guess at clocks based upon the PLL configuration from the
+	 * power-on reset.
+	 */
+	scmr = iomem->scmr;
+
+	/* The EST8260 is typically run using either 33 or 66 MHz
+	 * external clock.  The configuration byte in the Flash will
+	 * tell us which is configured.  The blast appears to be slightly
+	 * overclocked at 72 MHz (if set to 66 MHz, the uart runs too fast)
+	 */
+
+	m->clkin = CLKIN;
+
+	pllmf = scmr & 0xfff;
+
+	/* This is arithmetic from the 8260 manual, section 9.4.1. */
+
+	/* Collect the bits from the scmr.
+	*/
+	m->vco_out = m->clkin * (pllmf + 1);
+	if (scmr & BIT(19))	/* plldf (division factor is 1 or 2) */
+		m->vco_out >>= 1;
+
+	m->cpmhz = m->vco_out >> 1;	/* cpm hz is half of vco_out */
+	m->brghz = m->vco_out >> (2 * ((iomem->sccr & 0x3) + 1));
+	m->bushz = m->vco_out / (((scmr & 0x00f00000) >> 20) + 1);
+
+	/* Serial init sets BRG clock....I don't know how to compute
+	 * core clock from core configuration, but I think I know the
+	 * mapping....
+	 */
+	switch(scmr >> (31-7)){
+	case 0x0a:
+		m->cpuhz = m->clkin * 2;
+		break;
+	case 0x0b:
+		m->cpuhz = (m->clkin >> 1) * 5;
+		break;
+	default:
+	case 0x0d:
+		m->cpuhz = m->clkin * 3;
+		break;
+	case 0x14:
+		m->cpuhz = (m->clkin >> 1) * 7;
+		break;
+	case 0x1c:
+		m->cpuhz = m->clkin * 4;
+	}
+
+/*	Expect:
+	intfreq	133		m->cpuhz
+	busfreq	33		m->bushz
+	cpmfreq	99		m->cpmhz
+	brgfreq	49.5		m->brghz
+	vco		198
+*/
+
+	active.machs = 1;
+	active.exiting = 0;
+
+	putmsr(getmsr() | MSR_ME);
+
+	/*
+	 * turn on data cache before instruction cache;
+	 * for some reason which I don't understand,
+	 * you can't turn on both caches at once
+	 */
+	icacheenb();
+	dcacheenb();
+
+	kfpinit();
+
+	/* Plan9.ini location in flash is FLASHMEM+PLAN9INI
+	 * if PLAN9INI == ~0, it's not stored in flash or there is no flash
+	 * if *cp == 0xff, flash memory is not initialized
+	 */
+	if (PLAN9INI == ~0 || *(plan9inistr = (char*)(FLASHMEM+PLAN9INI)) == 0xff){
+		/* No plan9.ini in flash */
+		plan9inistr =
+			"console=0\n"
+			"ether0=type=fcc port=0 ea=00601d3d8fc2\n"
+			"flash0=mem=0xfe000000\n"
+			"fs=135.104.9.42\n"
+			"auth=135.104.9.7\n"
+			"authdom=cs.bell-labs.com\n"
+			"sys=pe0\n"
+			"ntp=135.104.9.52\n";
+	}
+}
+
+void
+trapinit(void)
+{
+	int i;
+
+	/*
+	 * set all exceptions to trap
+	 */
+	for(i = 0x0; i < 0x2000; i += 0x100)
+		sethvec(i, trapvec);
+
+	setmvec(0x1000, imiss, tlbvec);
+	setmvec(0x1100, dmiss, tlbvec);
+	setmvec(0x1200, dmiss, tlbvec);
+
+/*	Useful for avoiding assembler miss handling:
+	sethvec(0x1000, tlbvec);
+	sethvec(0x1100, tlbvec);
+	sethvec(0x1200, tlbvec);
+/* */
+	dcflush(KADDR(0), 0x2000);
+	icflush(KADDR(0), 0x2000);
+
+	putmsr(getmsr() & ~MSR_IP);
+}

+ 657 - 0
sys/src/9/ppc/m8260.h

@@ -0,0 +1,657 @@
+
+typedef struct BD BD;
+struct BD {
+	ushort	status;
+	ushort	length;
+	ulong	addr;
+};
+
+enum{
+	BDEmpty=	SBIT(0),
+	BDReady=		SBIT(0),
+	BDWrap=		SBIT(2),
+	BDInt=		SBIT(3),
+	BDLast=		SBIT(4),
+	BDFirst=		SBIT(5),
+};
+
+typedef struct Ring Ring;
+
+struct Ring {
+	BD*		rdr;			/* receive descriptor ring */
+	void*	rrb;			/* receive ring buffers */
+	int		rdrx;			/* index into rdr */
+	int		nrdre;		/* length of rdr */
+
+	BD*		tdr;			/* transmit descriptor ring */
+	void**	txb;			/* corresponding transmit ring buffers */
+	int		tdrh;			/* host index into tdr */
+	int		tdri;			/* interface index into tdr */
+	int		ntdre;		/* length of tdr */
+	int		ntq;			/* pending transmit requests */
+};
+
+int	ioringinit(Ring*, int, int, int);
+
+/*
+ * MCC parameters
+ */
+typedef struct MCCparam MCCparam;
+struct MCCparam {
+/*0x00*/	ulong	mccbase;
+/*0x04*/	ushort	mccstate;
+/*0x06*/	ushort	mrblr;	
+/*0x08*/	ushort	grfthr;	
+/*0x0a*/	ushort	grfcnt;	
+/*0x0c*/	ulong	rinttmp;
+/*0x10*/	ulong	data0;
+/*0x14*/	ulong	data1;
+/*0x18*/	ulong	tintbase;
+/*0x1c*/	ulong	tintptr;
+/*0x20*/	ulong	tinttmp;
+/*0x24*/	ushort	sctpbase;
+/*0x26*/	ushort	Rsvd26;
+/*0x28*/	ulong	cmask32;
+/*0x2c*/	ushort	xtrabase;
+/*0x2e*/	ushort	cmask16;
+/*0x30*/	ulong	rinttmp[4];
+/*0x40*/	struct {
+			ulong	base;
+			ulong	ptr;
+		}		rint[4];
+/*0x60*/	ulong	tstmp;
+/*0x64*/
+};
+/*
+ * IO controller parameters
+ */
+typedef struct IOCparam IOCparam;
+struct IOCparam {
+/*0x00*/	ushort	rbase;
+/*0x02*/	ushort	tbase;
+/*0x04*/	uchar	rfcr;
+/*0x05*/	uchar	tfcr;
+/*0x06*/	ushort	mrblr;
+/*0x08*/	ulong	rstate;
+/*0x0c*/	ulong	rxidp;
+/*0x10*/	ushort	rbptr;
+/*0x12*/	ushort	rxibc;
+/*0x14*/	ulong	rxtemp;
+/*0x18*/	ulong	tstate;
+/*0x1c*/	ulong	txidp;
+/*0x20*/	ushort	tbptr;
+/*0x22*/	ushort	txibc;
+/*0x24*/	ulong	txtemp;
+/*0x28*/
+};
+
+typedef struct SCCparam SCCparam;
+struct SCCparam {
+	IOCparam;
+	ulong	rcrc;
+	ulong	tcrc;
+};
+
+typedef struct FCCparam FCCparam;
+struct FCCparam {
+/*0x00*/	ushort	riptr;
+/*0x02*/	ushort	tiptr;
+/*0x04*/	ushort	Rsvd04;
+/*0x06*/	ushort	mrblr;
+/*0x08*/	ulong	rstate;
+/*0x0c*/	ulong	rbase;
+/*0x10*/	ushort	rbdstat;
+/*0x12*/	ushort	rbdlen;
+/*0x14*/	char*	rdptr;
+/*0x18*/	ulong	tstate;
+/*0x1c*/	ulong	tbase;
+/*0x20*/	ushort	tbdstat;
+/*0x22*/	ushort	tbdlen;
+/*0x24*/	ulong	tdptr;
+/*0x28*/	ulong	rbptr;
+/*0x2c*/	ulong	tbptr;
+/*0x30*/	ulong	rcrc;
+/*0x34*/	ulong	Rsvd34;
+/*0x38*/	ulong	tcrc;
+/*0x3c*/
+};
+
+typedef struct SCC SCC;
+struct SCC {
+	ulong	gsmrl;
+	ulong	gsmrh;
+	ushort	psmr;
+	uchar	rsvscc0[2];
+	ushort	todr;
+	ushort	dsr;
+	ushort	scce;
+	uchar	rsvscc1[2];
+	ushort	sccm;
+	uchar	rsvscc2;
+	uchar	sccs;
+	ushort	irmode;
+	ushort	irsip;
+	uchar	rsvscc3[4];	/* BUG */
+};
+
+typedef struct FCC FCC;
+struct FCC {
+/*0x00*/	ulong	gfmr;	/*  general mode register 28.2/28-3 */
+/*0x04*/	ulong	fpsmr;	/*  protocol-specific mode reg. 29.13.2(ATM) 30.18.1(Ether) */
+/*0x08*/	ushort	ftodr;	/*  transmit on demand register 28.5/28-7 */
+/*0x0A*/	ushort	Rsvd0A;
+/*0x0C*/	ushort	fdsr;		/*  data synchronization register 28.4/28-7 */
+/*0x0E*/	ushort	Rsvd0E;
+/*0x10*/	ushort	fcce;		/* event register 29.13.3 (ATM), 30.18.2 (Ethernet) */
+/*0x12*/	ushort	Rsvd12;
+/*0x14*/	ushort	fccm;	/* mask register */
+/*0x16*/	ushort	Rsvd16;
+/*0x18*/	uchar	fccs;		/* status register 8 bits 31.10 (HDLC) */
+/*0x19*/	uchar	Rsvd19[3];
+/*0x1C*/	uchar	ftirrphy[4];	/* transmit internal rate registers for PHY0DH3 29.13.4/29-88 (ATM) */
+/*0x20*/
+};
+
+typedef struct SMC SMC;
+struct SMC {
+/*0x0*/	ushort	pad1;
+/*0x2*/	ushort	smcmr;
+/*0x4*/	ushort	pad2;
+/*0x6*/	uchar	smce;
+/*0x7*/	uchar	pad3[3];
+/*0xa*/	uchar	smcm;
+/*0xb*/	uchar	pad4[5];
+/*0x10*/
+};
+
+typedef struct SPI SPI;
+struct SPI {
+	ushort	spmode;
+	uchar	res1[4];
+	uchar	spie;
+	uchar	res2[3];
+	uchar	spim;
+	uchar	res3[2];
+	uchar	spcom;
+	uchar	res4[2];
+};
+
+typedef struct Bankmap Bankmap;
+struct Bankmap {
+/*0*/	ulong	br;		/*  Base register bank 32 bits 10.3.1/10-14 */
+/*4*/	ulong	or;		/*  Option register bank 32 bits 10.3.2/10-16 */
+/*8*/
+};
+
+typedef struct Port Port;
+struct Port {
+/*0x00*/	ulong	pdir;		/*  Port A data direction register 32 bits 35.2.3/35-3 */
+/*0x04*/	ulong	ppar;	/*  Port Apin assignment register 32 bits 35.2.4/35-4 */
+/*0x08*/	ulong	psor;		/*  Port A special options register 32 bits 35.2.5/35-4 */
+/*0x0C*/	ulong	podr;	/*  Port Aopen drain register 32 bits 35.2.1/35-2 */
+/*0x10*/	ulong	pdat;		/*  Port A data register 32 bits 35.2.2/35-2 */
+/*0x14*/	uchar	Rsvd14[12];
+/*0x20*/
+};
+
+typedef struct IDMA IDMA;
+struct IDMA {
+/*0x0*/	uchar	idsr;		/*  IDMA event register 8 bits 18.8.4/18-22 */
+/*0x1*/	uchar	Rsvd1[3];
+/*0x4*/	uchar	idmr;	/*  IDMA mask register 8 bits 18.8.4/18-22 */
+/*0x5*/	uchar	Rsvd5[3];
+/*0x8*/
+};
+
+typedef struct PrmSCC PrmSCC;
+struct PrmSCC {
+	uchar	sccbytes[0x100];
+};
+
+typedef struct PrmFCC PrmFCC;
+struct PrmFCC {
+	uchar	fccbytes[0x100];
+};
+
+typedef struct Bases Bases;
+struct Bases {
+/*0x00*/	uchar	mcc[0x80];
+/*0x80*/	uchar	Rsvd80[0x60];
+/*0xe0*/	uchar	risctimers[0x10];
+/*0xf0*/	ushort	revnum;
+/*0xf2*/	uchar	Rsvdf2[6];
+/*0xf8*/	ulong	rand;
+/*0xfc*/	ushort	smcbase;
+#define	i2cbase	smcbase
+/*0xfe*/	ushort	idmabase;
+/*0x100*/
+};
+
+typedef struct Uartsmc Uartsmc;
+struct Uartsmc {
+/*0x00*/	IOCparam;
+/*0x28*/	ushort	maxidl;
+/*0x2a*/	ushort	idlc;
+/*0x2c*/	ushort	brkln;
+/*0x2e*/	ushort	brkec;
+/*0x30*/	ushort	brkcr;
+/*0x32*/	ushort	r_mask;
+/*0x34*/	ulong	sdminternal;
+/*0x38*/	uchar	Rsvd38[8];
+/*0x40*/
+};
+
+typedef struct SI SI;
+struct SI {
+/*0x11B20*/	ushort	siamr;		/*  SI TDMA1 mode register 16 bits 14.5.2/14-17 */
+/*0x11B22*/	ushort	sibmr;		/*  SI TDMB1 mode register 16 bits */
+/*0x11B24*/	ushort	sicmr;		/*  SI TDMC1 mode register 16 bits */
+/*0x11B26*/	ushort	sidmr;		/*  SI TDMD1 mode register 16 bits */
+/*0x11B28*/	uchar	sigmr;		/*  SI global mode register 8 bits 14.5.1/14-17 */
+/*0x11B29*/	uchar	Rsvd11B29;
+/*0x11B2A*/	ushort	sicmdr;		/*  SI command register 8 bits 14.5.4/14-24 */
+/*0x11B2C*/	ushort	sistr;			/*  SI status register 8 bits 14.5.5/14-25 */
+/*0x11B2E*/	ushort	sirsr;			/*  SI RAM shadow address register 16 bits 14.5.3/14-23 */
+};
+
+typedef struct IMM IMM;
+struct IMM {
+/* General SIU */
+/*0x10000*/	ulong	siumcr;		/*  SIU module configuration register 32 bits 4.3.2.6/4-31 */
+/*0x10004*/	ulong	sypcr;		/*  System protection control register 32 bits 4.3.2.8/4-35 */
+/*0x10008*/	uchar	Rsvd10008[0xe-0x8];
+/*0x1000E*/	ushort	swsr;		/*  Softwareservice register 16 bits 4.3.2.9/4-36 */
+/*0x10010*/	uchar	Rsvd10010[0x14];
+/*0x10024*/	ulong	bcr;			/*  Bus configuration register 32 bits 4.3.2.1/4-25 */
+/*0x10028*/	ulong	PPC_ACR;		/*  60x bus arbiter configuration register 8 bits 4.3.2.2/4-28 */
+/*0x1002C*/	ulong	PPCALRH;		/*  60x bus arbitration-level register high (first 8 clients) 32 bits 4.3.2.3/4-28 */
+/*0x10030*/	ulong	PPC_ALRL;	/*  60x bus arbitration-level register low (next 8 clients) 32 bits 4.3.2.3/4-28 */
+/*0x10034*/	ulong	LCL_ACR;		/*  Local arbiter configuration register 8 bits 4.3.2.4/4-29 */
+/*0x10038*/	ulong	LCL_ALRH;	/*  Local arbitration-level register (first 8 clients) 32 bits 4.3.2.5/4-30 */
+
+/*0x1003C*/	ulong	LCL_ALRL;	/*  Local arbitration-level register (next 8 clients) 32 bits 4.3.2.3/4-28 */
+/*0x10040*/	ulong	TESCR1;		/*  60x bus transfer error status control register1 32 bits 4.3.2.10/4-36 */
+/*0x10044*/	ulong	TESCR2;		/*  60x bus transfer error status control register2 32 bits 4.3.2.11/4-37 */
+/*0x10048*/	ulong	L_TESCR1;	/*  Local bus transfer error status control register1 32 bits 4.3.2.12/4-38 */
+/*0x1004C*/	ulong	L_TESCR2;	/*  Local bus transfer error status control register2 32 bits 4.3.2.13/4-39 */
+/*0x10050*/	ulong	pdtea;		/*  60x bus DMAtransfer error address 32 bits 18.2.3/18-4 */
+/*0x10054*/	uchar	pdtem;		/*  60x bus DMAtransfer error MSNUM 8 bits 18.2.4/18-4 */
+/*0x10055*/	uchar	Rsvd10055[3];
+/*0x10058*/	void*	ldtea;		/*  Local bus DMA transfer error address 32 bits 18.2.3/18-4 */
+/*0x1005C*/	uchar	ldtem;		/*  Local bus DMA transfer error MSNUM 8 bits 18.2.4/18-4 */
+/*0x1005D*/	uchar	Rsvd1005D[163];
+
+/* Memory Controller */
+/*0x10100*/	Bankmap	bank[12];
+
+/*0x10160*/	uchar	Rsvd10160[8];
+/*0x10168*/	void*	MAR;		/*  Memory address register 32 bits 10.3.7/10-29 */
+/*0x1016C*/	ulong	Rsvd1016C;
+/*0x10170*/	ulong	MAMR;		/*  Machine A mode register 32 bits 10.3.5/10-26 */
+/*0x10174*/	ulong	MBMR;		/*  Machine B mode register 32 bits */
+/*0x10178*/	ulong	MCMR;		/*  Machine C mode register 32 bits */
+/*0x1017C*/	uchar	Rsvd1017C[6];
+/*0x10184*/	ulong	mptpr;		/*  Memory periodic timer prescaler 16 bits 10.3.12/10-32 */
+/*0x10188*/	ulong	mdr;			/*  Memorydata register 32 bits 10.3.6/10-28 */
+/*0x1018C*/	ulong	Rsvd1018C;
+/*0x10190*/	ulong	psdmr;		/*  60x bus SDRAM mode register 32 bits 10.3.3/10-21 */
+/*0x10194*/	ulong	lsdmr;		/*  Local bus SDRAM mode register 32 bits 10.3.4/10-24 */
+/*0x10198*/	ulong	PURT;		/*  60x bus-assigned UPM refresh timer 8 bits 10.3.8/10-30 */
+/*0x1019C*/	ulong	PSRT;		/*  60x bus-assigned SDRAM refresh timer 8 bits 10.3.10/10-31 */
+
+/*0x101A0*/	ulong	LURT;		/*  Local bus-assigned UPM refresh timer8 bits 10.3.9/10-30 */
+/*0x101A4*/	ulong	LSRT;		/*  Local bus-assigned SDRAM refresh timer 8 bits 10.3.11/10-32 */
+
+/*0x101A8*/	ulong	immr;		/*  Internal memory map register 32 bits 4.3.2.7/4-34 */
+/*0x101AC*/	uchar	Rsvd101AC[84];
+/* System Integration Timers */
+/*0x10200*/	uchar	Rsvd10200[32];
+/*0x10220*/	ulong	TMCNTSC;	/*  Time counter statusand control register 16 bits 4.3.2.14/4-40 */
+
+/*0x10224*/	ulong	TMCNT;		/*  Time counter register 32 bits 4.3.2.15/4-41 */
+/*0x10228*/	ulong	Rsvd10228;
+/*0x1022C*/	ulong	TMCNTAL;	/*  Time counter alarm register 32 bits 4.3.2.16/4-41 */
+/*0x10230*/	uchar	Rsvd10230[0x10];
+/*0x10240*/	ulong	PISCR;		/*  Periodic interrupt statusand control register 16 bits 4.3.3.1/4-42 */
+
+/*0x10244*/	ulong	PITC;		/*  Periodic interrupt count register 32 bits 4.3.3.2/4-43 */
+/*0x10248*/	ulong	PITR;			/*  Periodic interrupt timer register 32 bits 4.3.3.3/4-44 */
+/*0x1024C*/	uchar	Rsvd1024C[94];
+/*0x102AA*/	uchar	Rsvd102AA[2390];
+
+/* Interrupt Controller */
+/*0x10C00*/	ushort	sicr;			/*  SIU interrupt configuration register 16 bits 4.3.1.1/4-17 */
+/*0x10C02*/	ushort	Rsvd10C02;
+/*0x10C04*/	ulong	sivec;		/*  SIU interrupt vector register 32 bits 4.3.1.6/4-23 */
+/*0x10C08*/	ulong	sipnr_h;		/*  SIU interrupt pending register(high) 32 bits 4.3.1.4/4-21 */
+/*0x10C0C*/	ulong	sipnr_l;		/*  SIU interrupt pending register(low) 32 bits 4.3.1.4/4-21 */
+/*0x10C10*/	ulong	siprr;		/*  SIU interrupt priority register 32 bits 4.3.1.2/4-18 */
+/*0x10C14*/	ulong	scprr_h;		/*  CPM interrupt priority register(high) 32 bits 4.3.1.3/4-19 */
+/*0x10C18*/	ulong	scprr_l;		/*  CPM interrupt priority register(low) 32 bits 4.3.1.3/4-19 */
+/*0x10C1C*/	ulong	simr_h;		/*  SIU interrupt mask register(high) 32 bits 4.3.1.5/4-22 */
+/*0x10C20*/	ulong	simr_l;		/*  SIU interrupt mask register(low) 32 bits 4.3.1.5/4-22 */
+/*0x10C24*/	ulong	siexr;		/*  SIUexternal interrupt control register 32 bits 4.3.1.7/4-24 */
+/*0x10C28*/	uchar	Rsvd10C28[88];
+
+/* Clocksand Reset */
+/*0x10C80*/	ulong	sccr;			/*  Systemclock control register 32 bits 9.8/9-8 */
+/*0x10C84*/	uchar	Rsvd10C84[4];
+/*0x10C88*/	ulong	scmr;		/*  Systemclock mode register 32 bits 9.9/9-9 */
+/*0x10C8C*/	uchar	Rsvd10C8C[4];
+/*0x10C90*/	ulong	rsr;			/*  Reset status register 32 bits 5.2/5-4 */
+/*0x10C94*/	ulong	rmr;			/*  Reset mode register 32 bits 5.3/5-5 */
+/*0x10C98*/	uchar	Rsvd10C98[104];
+
+/* Part I.Overview Input/Output Port */
+/*0x10D00*/	Port		port[4];
+
+/* CPMTimers */
+/*0x10D80*/	uchar	tgcr1;		/*  Timer1 and timer2 global configuration register 8 bits 17.2.2/17-4 */
+
+/*0x10D81*/	uchar	Rsvd10D81[3];
+/*0x10D84*/	uchar	tgcr2;		/*  Timer3 and timer4 global configuration register 8 bits 17.2.2/17-4 */
+/*0x10D85*/	uchar	Rsvd10D85[3];
+
+/*0x10D88*/	uchar	Rsvd10D88[8];
+/*0x10D90*/	ushort	tmr1;		/*  Timer1 mode register 16 bits 17.2.3/17-6 */
+/*0x10D92*/	ushort	tmr2;		/*  Timer2 mode register 16 bits 17.2.3/17-6 */
+		union{
+			struct {
+/*0x10D94*/	ushort	trr1;			/*  Timer1 reference register 16 bits 17.2.4/17-7 */
+/*0x10D96*/	ushort	trr2;			/*  Timer2 reference register 16 bits 17.2.4/17-7 */
+			};
+/*0x10D94*/	ulong	trrl1;			/*  Combined Timer 1/2 trr register */
+		};
+		union{
+			struct {
+/*0x10D98*/	ushort	tcr1;			/*  Timer1 capture register 16 bits 17.2.5/17-8 */
+/*0x10D9A*/	ushort	tcr2;			/*  Timer2 capture register 16 bits 17.2.5/17-8 */
+			};
+/*0x10D98*/	ulong	tcrl1;		/*  Combined timer1/2 capture register */
+		};
+		union{
+			struct {
+/*0x10D9C*/	ushort	tcn1;			/*  Timer1 counter 16 bits 17.2.6/17-8 */
+/*0x10D9E*/	ushort	tcn2;			/*  Timer2 counter 16 bits 17.2.6/17-8 */
+			};
+/*0x10D9C*/	ulong	tcnl1;		/*  Combined timer1/2 counter */
+		};
+/*0x10DA0*/	ushort	tmr3;		/*  Timer3 mode register 16 bits 17.2.3/17-6 */
+/*0x10DA2*/	ushort	tmr4;		/*  Timer4 mode register 16 bits 17.2.3/17-6 */
+		union{
+			struct {
+/*0x10DA4*/	ushort	trr3;			/*  Timer3 reference register 16 bits 17.2.4/17-7 */
+/*0x10DA6*/	ushort	trr4;			/*  Timer4 reference register 16 bits 17.2.4/17-7 */
+			};
+/*0x10DA4*/	ulong	trrl3;
+		};			
+		union{
+			struct {
+/*0x10DA8*/	ushort	tcr3;			/*  Timer3 capture register 16 bits 17.2.5/17-8 */
+/*0x10DAA*/	ushort	tcr4;			/*  Timer4 capture register 16 bits 17.2.5/17-8 */
+			};
+/*0x10DA8*/	ulong	tcrl3;
+		};
+		union{
+			struct {
+/*0x10DAC*/	ushort	tcn3;			/*  Timer3 counter 16 bits 17.2.6/17-8 */
+/*0x10DAE*/	ushort	tcn4;			/*  Timer4 counter 16 bits 17.2.6/17-8 */
+			};
+/*0x10DAC*/	ulong	tcnl3;
+		};
+/*0x10DB0*/	ushort	ter1;			/*  Timer1 event register 16 bits 17.2.7/17-8 */
+/*0x10DB2*/	ushort	ter2;			/*  Timer2 event register 16 bits 17.2.7/17-8 */
+/*0x10DB4*/	ushort	ter3;			/*  Timer3 event register 16 bits 17.2.7/17-8 */
+/*0x10DB6*/	ushort	ter4;			/*  Timer4 event register 16 bits 17.2.7/17-8 */
+/*0x10DB8*/	uchar	Rsvd10DB8[608];
+
+/* SDMADHGeneral */
+/*0x11018*/	uchar	sdsr;			/*  SDMA status register 8 bits 18.2.1/18-3 */
+/*0x11019*/	uchar	Rsvd11019[3];
+/*0x1101C*/	uchar	sdmr;		/*  SDMA mask register 8 bits 18.2.2/18-4 */
+/*0x1101D*/	uchar	Rsvd1101D[3];
+
+/* IDMA */
+/*0x11020*/	IDMA	idma[4];
+
+/*0x11040*/	uchar	Rsvd11040[704];
+
+/*0x11300*/	FCC		fcc[3];
+
+/*0x11360*/	uchar	Rsvd11360[0x290];
+
+/* BRGs5DH8 */
+/*0x115F0*/	ulong	BRGC5;		/*  BRG5 configuration register 32 bits 16.1/16-2 */
+/*0x115F4*/	ulong	BRGC6;		/*  BRG6configuration register 32 bits */
+/*0x115F8*/	ulong	BRGC7;		/*  BRG7configuration register 32 bits */
+/*0x115FC*/	ulong	BRGC8;		/*  BRG8configuration register 32 bits */
+/*0x11600*/	uchar	Rsvd11600[0x260];
+/*0x11860*/	uchar	I2MOD;		/*  I2C mode register 8 bits 34.4.1/34-6 */
+/*0x11861*/	uchar	Rsvd11861[3];
+/*0x11864*/	uchar	I2ADD;		/*  I2C address register 8 bits 34.4.2/34-7 */
+/*0x11865*/	uchar	Rsvd11865[3];
+/*0x11868*/	uchar	I2BRG;		/*  I2C BRG register 8 bits 34.4.3/34-7 */
+/*0x11869*/	uchar	Rsvd11869[3];
+/*0x1186C*/	uchar	I2COM;		/*  I2C command register 8 bits 34.4.5/34-8 */
+/*0x1186D*/	uchar	Rsvd1186D[3];
+/*0x11870*/	uchar	I2CER;		/*  I2C event register 8 bits 34.4.4/34-8 */
+/*0x11871*/	uchar	Rsvd11871[3];
+/*0x11874*/	uchar	I2CMR;		/*  I2C mask register 8 bits 34.4.4/34-8 */
+/*0x11875*/	uchar	Rsvd11875[331];
+
+/* Communications Processor */
+/*0x119C0*/	ulong	cpcr;		/*  Communications processor command register 32 bits 13.4.1/13-11 */
+
+/*0x119C4*/	ulong	rccr;		/*  CP configuration register 32 bits 13.3.6/13-7 */
+/*0x119C8*/	uchar	Rsvd119C8[14];
+/*0x119D6*/	ushort	rter;		/*  CP timers event register 16 bits 13.6.4/13-21 */
+/*0x119D8*/	ushort	Rsvd119D8;
+/*0x119DA*/	ushort	rtmr;		/*  CP timers mask register 16 bits */
+/*0x119DC*/	ushort	rtscr;		/*  CPtime-stamp timer control register 16 bits 13.3.7/13-9 */
+/*0x119DE*/	ushort	Rsvd119DE;
+/*0x119E0*/	ulong	rtsr;		/*  CPtime-stamp register 32 bits 13.3.8/13-10 */
+/*0x119E4*/	uchar	Rsvd119E4[12];
+
+/*0x119F0*/	ulong	brgc[4];		/*  BRG configuration registers 32 bits 16.1/16-2 */
+
+/*0x11A00*/	SCC		scc[4];
+
+/*0x11A80*/	SMC		smc[2];
+
+			SPI		spi;
+
+/*0x11AB0*/	uchar	Rsvd11AB0[80];
+
+/* CPMMux */
+/*0x11B00*/	uchar	cmxsi1cr;	/*  CPM mux SI1clock route register 8 bits 15.4.2/15-10 */
+/*0x11B01*/	uchar	Rsvd11B01;
+/*0x11B02*/	uchar	cmxsi2cr;	/*  CPM mux SI2clock route register 8 bits 15.4.3/15-11 */
+/*0x11B03*/	uchar	Rsvd11B03;
+/*0x11B04*/	ulong	cmxfcr;	/*  CPM mux FCC clock route register 32 bits 15.4.4/15-12 */
+/*0x11B08*/	ulong	cmxscr;	/*  CPM mux SCC clock route register 32 bits 15.4.5/15-14 */
+/*0x11B0C*/	uchar	cmxsmr;	/*  CPM mux SMC clock route register 8 bits 15.4.6/15-17 */
+/*0x11B0D*/	uchar	Rsvd11B0D;
+/*0x11B0E*/	ushort	cmxuar;	/*  CPM mux UTOPIA address register 16 bits 15.4.1/15-7 */
+/*0x11B10*/	uchar	Rsvd11B10[16];
+
+			SI		si1;			/* SI 1 Registers */
+
+/* MCC1Registers */
+/*0x11B30*/	ushort	MCCE1;		/*  MCC1 event register 16 bits 27.10.1/27-18 */
+/*0x11B32*/	ushort	Rsvd11B32;
+/*0x11B34*/	ushort	MCCM1;		/*  MCC1 mask register 16 bits */
+/*0x11B36*/	ushort	Rsvd11B36;
+/*0x11B38*/	uchar	MCCF1;		/*  MCC1 configuration register 8 bits 27.8/27-15 */
+/*0x11B39*/	uchar	Rsvd11B39[7];
+
+			SI		si2;			/* SI 2 Registers */
+
+/* MCC2Registers */
+/*0x11B50*/	ushort	MCCE2;		/*  MCC2 event register 16 bits 27.10.1/27-18 */
+/*0x11B52*/	ushort	Rsvd11B52;
+/*0x11B54*/	ushort	MCCM2;		/*  MCC2 mask register 16 bits */
+/*0x11B56*/	ushort	Rsvd11B56;
+/*0x11B58*/	uchar	MCCF2;		/*  MCC2 configuration register 8 bits 27.8/27-15 */
+/*0x11B59*/	uchar	Rsvd11B59[1191];
+
+/* SI1RAM */
+/*0x12000*/	uchar	SI1TxRAM[0x200];/*  SI1 transmit routing RAM	512 14.4.3/14-10 */
+/*0x12200*/	uchar	Rsvd12200[0x200];
+/*0x12400*/	uchar	SI1RxRAM[0x200];/*  SI1 receive routing RAM	512 14.4.3/14-10 */
+/*0x12600*/	uchar	Rsvd12600[0x200];
+
+/* SI2RAM */
+/*0x12800*/	uchar	SI2TxRAM[0x200];/*  SI2 transmit routing RAM	512 14.4.3/14-10 */
+/*0x12A00*/	uchar	Rsvd12A00[0x200];
+/*0x12C00*/	uchar	SI2RxRAM[0x200];/*  SI2 receive routing RAM	512 14.4.3/14-10 */
+/*0x12E00*/	uchar	Rsvd12E00[0x200];
+/*0x13000*/	uchar	Rsvd13000[0x800];
+/*0x13800*/	uchar	Rsvd13800[0x800];
+};
+
+typedef struct FCCextra FCCextra;
+struct FCCextra {
+/*0x00*/	uchar	ri[0x20];
+/*0x20*/	uchar	ti[0x20];
+/*0x40*/	uchar	pad[0x20];
+};
+
+typedef struct Imap Imap;
+struct Imap {
+/* CPMDual-Port RAM */
+/*0x00000*/	uchar	dpram1[0x3800];	/*  Dual-port RAM	16Kbytes 13.5/13-15 */
+/*0x03800*/	FCCextra	fccextra[4];
+/*0x03980*/	Uartsmc	uartsmc[2];
+/*0x03a00*/	uchar	dsp1p[0x40];
+/*0x03a40*/	uchar	dsp2p[0x40];
+/*0x03a80*/	BD		bd[(0x04000-0x03a80)/sizeof(BD)];	/* Buffer descriptors */
+/*0x04000*/	uchar	Rsvd4000[0x04000];
+
+/* Dual port RAM bank 2 -- Parameter Ram, Section 13.5 */
+/*0x08000*/	PrmSCC	prmscc[4];
+/*0x08400*/	PrmFCC	prmfcc[3];
+/*0x08700*/	Bases	param[4];
+/*0x08b00*/	uchar	dpram2[0x500];
+
+/*0x09000*/	uchar	Rsvd9000[0x2000];
+
+/* Dual port RAM bank 3 -- Section 13.5 */
+/*0x0B000*/	uchar	dpram3[0x1000];	/*  Dual-port RAM	4Kbytes 13.5/13-15 */
+/*0x0C000*/	uchar	Rsvdc000[0x4000];
+
+/*0x10000*/	IMM;
+};
+
+enum {
+/* CPM Command register. */
+	cpm_rst		= 0x80000000,
+	cpm_page	= 0x7c000000,
+	cpm_sblock	= 0x03e00000,
+	cpm_flg		= 0x00010000,
+	cpm_mcn		= 0x00003fc0,
+	cpm_opcode	= 0x0000000f,
+
+/* Device sub-block and page codes. */
+	cpm_fcc1_sblock	= 0x10,
+	cpm_fcc2_sblock	= 0x11,
+	cpm_fcc3_sblock	= 0x12,
+	cpm_scc1_sblock	= 0x04,
+	cpm_scc2_sblock	= 0x05,
+	cpm_scc3_sblock	= 0x06,
+	cpm_scc4_sblock	= 0x07,
+	cpm_smc1_sblock	= 0x08,
+	cpm_smc2_sblock	= 0x09,
+	cpm_rand_sblock	= 0x0e,
+	cpm_spi_sblock	= 0x0a,
+	cpm_i2c_sblock	= 0x0b,
+	cpm_timer_sblock	= 0x0f,
+	cpm_mcc1_sblock	= 0x1c,
+	cpm_mcc2_sblock	= 0x1d,
+	cpm_idma1_sblock	= 0x14,
+	cpm_idma2_sblock	= 0x15,
+	cpm_idma3_sblock	= 0x16,
+	cpm_idma4_sblock	= 0x17,
+
+	cpm_scc1_page	= 0x00,
+	cpm_scc2_page	= 0x01,
+	cpm_scc3_page	= 0x02,
+	cpm_scc4_page	= 0x03,
+	cpm_smc1_page	= 0x07,
+	cpm_smc2_page	= 0x08,
+	cpm_spi_page		= 0x09,
+	cpm_i2c_page		= 0x0a,
+	cpm_timer_page	= 0x0a,
+	cpm_rand_page	= 0x0a,
+	cpm_fcc1_page	= 0x04,
+	cpm_fcc2_page	= 0x05,
+	cpm_fcc3_page	= 0x06,
+	cpm_idma1_page	= 0x07,
+	cpm_idma2_page	= 0x08,
+	cpm_idma3_page	= 0x09,
+	cpm_idma4_page	= 0x0a,
+	cpm_mcc1_page	= 0x07,
+	cpm_mcc2_page	= 0x08,
+
+};
+
+/*
+ * CPM
+ */
+enum {
+	/* commands */
+	InitRxTx =	0,
+	InitRx =		1,
+	InitTx =		2,
+	EnterHunt=	3,
+	StopTx=		4,
+	GracefulStopTx = 5,
+	InitIDMA =	5,
+	RestartTx =	6,
+	CloseRxBD =	7,
+	SetGroupAddr = 8,
+	SetTimer =	8,
+	GCITimeout =	9,
+	GCIAbort =	10,
+	StopIDMA =	11,
+	StartDSP = 	12,
+	ArmIDMA =	13,
+	InitDSP =		13,
+	USBCmd =	15,
+
+	/* channel IDs */
+	SCC1ID=	cpm_scc1_page << 5 | cpm_scc1_sblock,
+	SCC2ID=	cpm_scc2_page << 5 | cpm_scc2_sblock,
+	SCC3ID=	cpm_scc3_page << 5 | cpm_scc3_sblock,
+	SMC1ID=	cpm_smc1_page << 5 | cpm_smc1_sblock,
+	SMC2ID=	cpm_smc2_page << 5 | cpm_smc2_sblock,
+	FCC1ID=	cpm_fcc1_page << 5 | cpm_fcc1_sblock,
+	FCC2ID=	cpm_fcc2_page << 5 | cpm_fcc2_sblock,
+	FCC3ID=	cpm_fcc3_page << 5 | cpm_fcc3_sblock,
+//	USBID=	0,		These are wrong
+//	I2CID=	1,
+//	IDMA1ID= 1,
+//	SPIID=	5,
+//	IDMA2ID= 5,
+//	TIMERID=	5,
+//	DSP1ID=9,
+//	SCC4ID=	10,
+//	DSP2ID=	13,
+
+	/* sicr */
+	BRG1 = 0,
+	BRG2 = 1,
+	BRG3 = 2,
+	BRG4 = 4,
+	CLK1 = 4,
+	CLK2 = 5,
+	CLK3 = 6,
+	CLK4 = 7,
+
+};
+
+extern IMM* iomem;
+
+BD*	bdalloc(int);
+void	cpmop(int, int, int);
+void	ioplock(void);
+void	iopunlock(void);

+ 506 - 0
sys/src/9/ppc/main.c

@@ -0,0 +1,506 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"init.h"
+#include	"pool.h"
+
+#define	MAXCONF		64
+
+typedef struct Plan9ini Plan9ini;
+struct Plan9ini
+{
+	char	*name;
+	char *val;
+};
+
+char		*plan9inistr;
+Plan9ini	plan9ini[MAXCONF];
+int		nconf;
+
+Conf	conf;
+FPsave	initfp;
+Lock		testlock;
+
+static void	plan9iniinit(void);
+
+void (*_reboot)(void);
+
+void
+main(void)
+{
+	memset(edata, 0, (ulong)end-(ulong)edata);
+	conf.nmach = 1;
+	machinit();
+	confinit();
+	xinit();
+	trapinit();
+	mmuinit();
+	plan9iniinit();
+	hwintrinit();
+	clockinit();
+	timerinit();
+	console();
+	quotefmtinstall();
+	printinit();
+	cpuidprint();
+	print("\nPlan 9 from Bell Labs\n");
+	procinit0();
+	initseg();
+	timersinit();
+	links();
+	chandevreset();
+	pageinit();
+	swapinit();
+	sharedseginit();
+	fpsave(&initfp);
+	initfp.fpscr = 0;
+	userinit();
+	schedinit();
+}
+
+void
+cpuidprint(void)
+{
+	char *id;
+
+	id = "unknown PowerPC";
+	switch(m->cputype) {
+	case 8:
+		id = "PowerPC 750";
+		break;
+	case 9:
+		id = "PowerPC 604e";
+		break;
+	case 0x81:
+		id = "PowerPC 8260";
+		break;
+	case 0x8081:
+		id = "PowerPC 826xA";
+		break;
+	default:
+		break;
+	}
+	print("cpu0: %s, rev 0x%lux\n", id, getpvr()&0xffff);
+}
+
+char*
+getconf(char *name)
+{
+	int i;
+
+	for(i = 0; i < nconf; i++)
+		if(cistrcmp(name, plan9ini[i].name) == 0)
+			return plan9ini[i].val;
+	return nil;
+}
+
+static void
+plan9iniinit(void)
+{
+	long i;
+	int c;
+	char *cp, line[MAXCONF], *p, *q;
+
+	_reboot = (void (*)(void))0xff400000;
+
+	/*
+	 *  parse configuration args from dos file plan9.ini
+	 */
+
+	/* Plan9.ini location in flash is FLASHMEM+PLAN9INI
+	 * if PLAN9INI == ~0, it's not stored in flash or there is no flash
+	 * if *cp == 0xff, flash memory is not initialized
+	 */
+	if (PLAN9INI == ~0 || *(cp = (char*)(FLASHMEM+PLAN9INI)) == 0xff){
+		/* No plan9.ini in flash */
+		cp = plan9inistr;
+	}
+	for(i = 0; i < MAXCONF; i++){
+		/*
+		 * Strip out '\r', change '\t' -> ' ', test for 0xff which is end of file
+		 */
+		p = line;
+		for(q = cp; c = (uchar)*q; q++){
+			if(c == '\r')
+				continue;
+			if(c == '\t')
+				c = ' ';
+			if(c == 0xff || c == '\n')
+				break;
+			*p++ = c;
+		}
+		*p = 0;
+		if (*line == 0)
+			break;
+		if(*line != '#' && (cp = strchr(line, '='))){
+			*cp++ = '\0';
+			kstrdup(&plan9ini[nconf].name, line);
+			kstrdup(&plan9ini[nconf].val, cp);
+			nconf++;
+		}
+		if (c == 0xff)
+			break;
+
+		cp = q + 1;
+	}
+}
+
+/* still to do */
+void
+reboot(void*, void*, ulong)
+{
+	_reboot();
+}
+
+void
+init0(void)
+{
+//	char **p, *q, name[KNAMELEN];
+	int i;
+	char buf[2*KNAMELEN];
+
+	up->nerrlab = 0;
+	spllo();
+
+	/*
+	 * These are o.k. because rootinit is null.
+	 * Then early kproc's will have a root and dot.
+	 */
+	up->slash = namec("#/", Atodir, 0, 0);
+	cnameclose(up->slash->name);
+	up->slash->name = newcname("/");
+	up->dot = cclone(up->slash);
+
+	chandevinit();
+
+	if(!waserror()){
+		snprint(buf, sizeof(buf), "power %s mtx", conffile);
+		ksetenv("terminal", buf, 0);
+		ksetenv("cputype", "power", 0);
+		if(cpuserver)
+			ksetenv("service", "cpu", 0);
+		else
+			ksetenv("service", "terminal", 0);
+
+		for(i = 0; i < nconf; i++){
+			if(plan9ini[i].name[0] != '*')
+				ksetenv(plan9ini[i].name, plan9ini[i].val, 0);
+			ksetenv(plan9ini[i].name, plan9ini[i].val, 1);
+		}
+		poperror();
+	}
+	kproc("alarm", alarmkproc, 0);
+	kproc("mmusweep", mmusweep, 0);
+	touser((void*)(USTKTOP-8));
+}
+
+void
+userinit(void)
+{
+	Proc *p;
+	Segment *s;
+	KMap *k;
+	Page *pg;
+
+	p = newproc();
+	p->pgrp = newpgrp();
+	p->egrp = smalloc(sizeof(Egrp));
+	p->egrp->ref = 1;
+	p->fgrp = dupfgrp(nil);
+	p->rgrp = newrgrp();
+	p->procmode = 0640;
+
+	kstrdup(&eve, "");
+	kstrdup(&p->text, "*init*");
+	kstrdup(&p->user, eve);
+
+	p->fpstate = FPinit;
+
+	/*
+	 *  Stack
+	 *
+	 * N.B. The -12 for the stack pointer is important.
+	 *	4 bytes for gotolabel's return PC
+	 */
+	p->sched.pc = (ulong)init0;
+	p->sched.sp = (ulong)p->kstack+KSTACK-(sizeof(Sargs)+BY2WD);
+
+	/*
+	 * User Stack
+	 */
+	s = newseg(SG_STACK, USTKTOP-USTKSIZE, USTKSIZE/BY2PG);
+	p->seg[SSEG] = s;
+	pg = newpage(1, 0, USTKTOP-BY2PG);
+	segpage(s, pg);
+
+	/*
+	 * Text
+	 */
+	s = newseg(SG_TEXT, UTZERO, 1);
+	s->flushme++;
+	p->seg[TSEG] = s;
+	pg = newpage(1, 0, UTZERO);
+	memset(pg->cachectl, PG_TXTFLUSH, sizeof(pg->cachectl));
+	segpage(s, pg);
+	k = kmap(s->map[0]->pages[0]);
+	memmove((ulong*)VA(k), initcode, sizeof initcode);
+	kunmap(k);
+
+	ready(p);
+}
+
+void
+exit(int ispanic)
+{
+	int ms, once;
+
+	lock(&active);
+	if(ispanic)
+		active.ispanic = ispanic;
+	else if(m->machno == 0 && (active.machs & (1<<m->machno)) == 0)
+		active.ispanic = 0;
+	once = active.machs & (1<<m->machno);
+	active.machs &= ~(1<<m->machno);
+	active.exiting = 1;
+	unlock(&active);
+
+	if(once)
+		print("cpu%d: exiting\n", m->machno);
+	spllo();
+	for(ms = 5*1000; ms > 0; ms -= TK2MS(2)){
+		delay(TK2MS(2));
+		if(active.machs == 0 && consactive() == 0)
+			break;
+	}
+
+	if(active.ispanic && m->machno == 0){
+		if(cpuserver)
+			delay(10000);
+		else if(conf.monitor)
+			for(;;);
+	}
+	else
+		delay(1000);
+
+}
+
+/*
+ *  set up floating point for a new process
+ */
+void
+procsetup(Proc *p)
+{
+	p->fpstate = FPinit;
+}
+
+/*
+ *  Save the mach dependent part of the process state.
+ */
+void
+procsave(Proc *p)
+{
+	if(p->fpstate == FPactive){
+		if(p->state != Moribund)
+			fpsave(&up->fpsave);
+		p->fpstate = FPinactive;
+	}
+}
+
+void
+confinit(void)
+{
+	char *p;
+	int userpcnt;
+	ulong pa, kpages;
+	/* passed in from ROM monitor: */
+
+	if(p = getconf("*kernelpercent"))
+		userpcnt = 100 - strtol(p, 0, 0);
+	else
+		userpcnt = 0;
+
+	pa = PGROUND(PADDR(end));
+
+	/* Blast Board specific */
+	conf.npage0 = (MEM1SIZE - pa)/BY2PG;
+	conf.base0 = pa;
+	
+	conf.npage1 = MEM2SIZE/BY2PG;
+	conf.base1 = MEM2BASE;
+
+	conf.npage = conf.npage0 + conf.npage1;
+
+	conf.nmach = 1;
+	conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5;
+	if(cpuserver)
+		conf.nproc *= 3;
+	if(conf.nproc > 2000)
+		conf.nproc = 2000;
+	conf.nimage = 200;
+	conf.nswap = conf.nproc*80;
+	conf.nswppo = 4096;
+	conf.copymode = 0;			/* copy on write */
+
+	if(cpuserver) {
+		if(userpcnt < 10)
+			userpcnt = 70;
+		kpages = conf.npage - (conf.npage*userpcnt)/100;
+
+		/*
+		 * Hack for the big boys. Only good while physmem < 4GB.
+		 * Give the kernel a max. of 16MB + enough to allocate the
+		 * page pool.
+		 * This is an overestimate as conf.upages < conf.npages.
+		 * The patch of nimage is a band-aid, scanning the whole
+		 * page list in imagereclaim just takes too long.
+		 */
+		if(kpages > (16*MB + conf.npage*sizeof(Page))/BY2PG){
+			kpages = (16*MB + conf.npage*sizeof(Page))/BY2PG;
+			conf.nimage = 2000;
+			kpages += (conf.nproc*KSTACK)/BY2PG;
+		}
+	} else {
+		if(userpcnt < 10) {
+			if(conf.npage*BY2PG < 16*MB)
+				userpcnt = 40;
+			else
+				userpcnt = 60;
+		}
+		kpages = conf.npage - (conf.npage*userpcnt)/100;
+
+		/*
+		 * Make sure terminals with low memory get at least
+		 * 4MB on the first Image chunk allocation.
+		 */
+		if(conf.npage*BY2PG < 16*MB)
+			imagmem->minarena = 4*1024*1024;
+	}
+	conf.upages = conf.npage - kpages;
+	conf.ialloc = (kpages/2)*BY2PG;
+
+	/*
+	 * Guess how much is taken by the large permanent
+	 * datastructures. Mntcache and Mntrpc are not accounted for
+	 * (probably ~300KB).
+	 */
+	kpages *= BY2PG;
+	kpages -= conf.upages*sizeof(Page)
+		+ conf.nproc*sizeof(Proc)
+		+ conf.nimage*sizeof(Image)
+		+ conf.nswap
+		+ conf.nswppo*sizeof(Page);
+	mainmem->maxsize = kpages;
+	if(!cpuserver){
+		/*
+		 * give terminals lots of image memory, too; the dynamic
+		 * allocation will balance the load properly, hopefully.
+		 * be careful with 32-bit overflow.
+		 */
+		imagmem->maxsize = kpages;
+	}
+
+//	conf.monitor = 1;	/* BUG */
+}
+
+static int
+getcfields(char* lp, char** fields, int n, char* sep)
+{
+	int i;
+
+	for(i = 0; lp && *lp && i < n; i++){
+		while(*lp && strchr(sep, *lp) != 0)
+			*lp++ = 0;
+		if(*lp == 0)
+			break;
+		fields[i] = lp;
+		while(*lp && strchr(sep, *lp) == 0){
+			if(*lp == '\\' && *(lp+1) == '\n')
+				*lp++ = ' ';
+			lp++;
+		}
+	}
+
+	return i;
+}
+
+int
+isaconfig(char *class, int ctlrno, ISAConf *isa)
+{
+	int i;
+	char cc[KNAMELEN], *p;
+
+	sprint(cc, "%s%d", class, ctlrno);
+
+	p = getconf(cc);
+	if(p == 0)
+		return 0;
+	isa->nopt = tokenize(p, isa->opt, NISAOPT);
+	for(i = 0; i < isa->nopt; i++){
+		p = isa->opt[i];
+		if(cistrncmp(p, "type=", 5) == 0)
+			isa->type = p + 5;
+		else if(cistrncmp(p, "port=", 5) == 0)
+			isa->port = strtoul(p+5, &p, 0);
+		else if(cistrncmp(p, "irq=", 4) == 0)
+			isa->irq = strtoul(p+4, &p, 0);
+		else if(cistrncmp(p, "dma=", 4) == 0)
+			isa->dma = strtoul(p+4, &p, 0);
+		else if(cistrncmp(p, "mem=", 4) == 0)
+			isa->mem = strtoul(p+4, &p, 0);
+		else if(cistrncmp(p, "size=", 5) == 0)
+			isa->size = strtoul(p+5, &p, 0);
+		else if(cistrncmp(p, "freq=", 5) == 0)
+			isa->freq = strtoul(p+5, &p, 0);
+	}
+	return 1;
+}
+
+int
+cistrcmp(char *a, char *b)
+{
+	int ac, bc;
+
+	for(;;){
+		ac = *a++;
+		bc = *b++;
+	
+		if(ac >= 'A' && ac <= 'Z')
+			ac = 'a' + (ac - 'A');
+		if(bc >= 'A' && bc <= 'Z')
+			bc = 'a' + (bc - 'A');
+		ac -= bc;
+		if(ac)
+			return ac;
+		if(bc == 0)
+			break;
+	}
+	return 0;
+}
+
+int
+cistrncmp(char *a, char *b, int n)
+{
+	unsigned ac, bc;
+
+	while(n > 0){
+		ac = *a++;
+		bc = *b++;
+		n--;
+
+		if(ac >= 'A' && ac <= 'Z')
+			ac = 'a' + (ac - 'A');
+		if(bc >= 'A' && bc <= 'Z')
+			bc = 'a' + (bc - 'A');
+
+		ac -= bc;
+		if(ac)
+			return ac;
+		if(bc == 0)
+			break;
+	}
+
+	return 0;
+}

+ 351 - 0
sys/src/9/ppc/mcc.c

@@ -0,0 +1,351 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+/*
+ * 
+ * mcc.c
+ *
+ * This is the driver for the Multi-Channel Communications Controller
+ * of the MPC8260.  This version is constructed for the EST SBC8260 to
+ * handle data from an interface to an offboard T1 framer.  The driver
+ * supports MCC2 + TDM A:2 (channel 128) which is connected to the
+ * external interface.
+ *
+ * Neville Chandler
+ * Lucent Technologies - Bell Labs
+ * March 2001
+ *
+ */
+
+#define	PKT_LEN			40
+#define MPC82XX_INIT_DELAY      0x10000   
+
+#define HPIC		0xFC000000
+#define HPIA		0xFC000010
+#define HPID_A		0xFC000020
+#define HPID		0xFC000030
+
+
+#include "mcc2.h"
+
+static ssize_t mcc2_read( struct file *, char *, size_t, loff_t * );
+static ssize_t mcc2_write( struct file *, const char *, size_t, loff_t *);
+static loff_t mcc2_lseek( struct file *, loff_t, int );
+static int mcc2_release( struct inode *, struct file * );
+static ssize_t mcc2_ioctl( struct inode *, struct file *, unsigned int, unsigned long );
+//static ssize_t mcc2_ioctl( struct inode *, struct file *, unsigned int, char * );
+
+void MPC82xxCpmInit( void );
+void PortInit( void );
+void PortSelectPin( unsigned short );
+
+void InitMemAlloc( void );
+void HeapCreate( U32, U32, U32, U32, char *);
+void HeapCreate( U32, U32, U32, U32, char *);
+void *HeapSearchMem( U32, U32);
+void *HeapAllocMem( U32, U32);
+void HeapFreeMem( U32, void *);
+
+void InitLinkedList( void );
+boolean DwCreateList( ListDB * );
+void *DwMalloc( U32 );
+void DwFree( U32, void * );
+
+void ppc_irq_dispatch_handler(struct pt_regs *regs, int irq);
+
+#define NR_MASK_WORDS   ((NR_IRQS + 31) / 32)
+
+extern int ppc_spurious_interrupts;
+extern int ppc_second_irq;
+extern struct irqaction *ppc_irq_action[NR_IRQS];
+extern unsigned int ppc_local_bh_count[NR_CPUS];
+extern unsigned int ppc_local_irq_count[NR_CPUS];
+extern unsigned int ppc_cached_irq_mask[NR_MASK_WORDS];
+extern unsigned int ppc_lost_interrupts[NR_MASK_WORDS];
+extern atomic_t ppc_n_lost_interrupts;
+
+//static void disp_led( unsigned char );
+
+void	Mcc2Init( void );
+void	MccDisable( unsigned char );
+void	MccEnable( unsigned char, unsigned char, unsigned char );
+void	MccRiscCmd( unsigned char, dwv_RISC_OPCODE, unsigned char );
+boolean	MccTest( void );
+int	MccTxBuffer( unsigned char, unsigned char, char *, unsigned short, unsigned short );
+extern	U32 PpcDisable( void );
+extern	void PpcMsrRestore( U32 );
+
+static	int	mcc2_major = MCC_MAJOR;
+
+static	BOOLEAN insertBD_T( BD_PFIFO *, BD_P );
+static	BOOLEAN removBD_T( BD_PFIFO *, BD_P * );
+BOOLEAN	empty(volatile register FIFO *);
+int	insert( FIFO *, char * );
+int	remove( FIFO *, char ** );
+void	AppInit( void );
+
+#define physaddr(ADDR)	(0x60020000 | ((ADDR) << 23) | (2 << 18))
+  
+mcc_iorw_t mcc_iorw;
+
+#if 0
+typedef struct mcc_io {
+	unsigned int	cmd;
+        unsigned int    address;
+        unsigned int    *buf;
+	int		ind;
+        int             nbytes;
+	siramctl_t	SiRam;
+	cpmux_t		CpMux;
+	mcc_t		Mcc_T;
+	iop8260_t	Io_Ports;
+} mcc_iorw_t;
+#endif
+
+static void
+ioctl_parm( unsigned int loop_mode )
+{
+
+	/* Setup the SIMODE Register */
+  	Si2Regs->SiAmr = SIxMR_SAD_BANK0_FIRST_HALF    |  /* SADx  */
+			 loop_mode                     |  /* SDMx  */
+			 SIxMR_NO_BIT_RX_SYNC_DELAY    |  /* RFSDx */
+			 SIxMR_DSC_CH_DATA_CLK_EQU     |  /* DSCx  */
+			 SIxMR_CRT_SPEPARATE_PINS      |  /* CRTx  */
+			 SIxMR_SLx_NORMAL_OPERATION    |  /* SLx   */
+			 SIxMR_CE_TX_RISING_RX_FALLING |  /* CEx   */
+			 SIxMR_FE_FALLING_EDGE         |  /* FEx   */
+			 SIxMR_GM_GCI_SCIT_MODE        |  /* GMx   */
+			 SIxMR_NO_BIT_TX_SYNC_DELAY;      /* TFSDx */
+}
+
+#if 0
+static void
+disp_led( unsigned char byte )
+{
+     //int i;
+
+     *leds = byte;
+     //for(i=0; i<1000; i++);
+
+}
+#endif
+
+
+
+static ssize_t
+mcc2_ioctl( struct inode *inode, struct file *file,
+			      unsigned int ioctl_cmd,		// IOCTL number
+			      unsigned long param )
+//			      char *param )			// IOCTL parameter
+{
+	static unsigned char mode;
+	char cp, *cptr;
+	void *vptr;
+	unsigned long *lptr;
+	int i, j;
+	unsigned int ld;
+	unsigned long lng;
+	volatile immap_t *Mmap;
+
+	cptr = (char *)param;
+	mode = (unsigned char)*cptr;
+	Mmap = ((volatile immap_t *)IMAP_ADDR);
+	switch(ioctl_cmd)
+	{
+		case IOCTL_SET_MODE:
+			    //mode = (unsigned char)*param;
+			    mode = ((mcc_iorw_t *)param)->cmd;
+			    switch( mode )
+			    {
+				case NORMAL_OPERATION:
+					/* Setup the SIMODE Register */
+					D( printk("mcc2_ioctl: ioctl set NORMAL_OPERATION mode\n"); )
+					ioctl_parm( (unsigned int)SIxMR_SDM_NORMAL_OPERATION );		/* SDMx  */
+					break;
+
+				case AUTOMATIC_ECHO:
+					/* Setup the SIMODE Register */
+					D( printk("mcc2_ioctl: ioctl set AUTOMATIC_ECHO mode\n"); )
+					ioctl_parm( (unsigned int)SIxMR_SDM_AUTOMATIC_ECHO );		/* SDMx  */
+					break;
+
+				case INTERNAL_LOOPBACK:
+					/* Setup the SIMODE Register */
+					D( printk("mcc2_ioctl: ioctl set INTERNAL_LOOPBACK mode\n"); )
+					ioctl_parm( (unsigned int)SIxMR_SDM_INTERNAL_LOOPBACK );	/* SDMx  */
+					break;
+
+				case LOOPBACK_CONTROL:
+					/* Setup the SIMODE Register */
+					D( printk("mcc2_ioctl: ioctl set LOOPBACK_CONTROL mode\n"); )
+					ioctl_parm( (unsigned int)SIxMR_SDM_LOOPBACK_CONTROL );		/* SDMx  */
+					break;
+
+
+				default:
+					printk("mcc2_ioctl: Error, unrecognized ioctl parameter, device operation unchanged.\n");
+					break;
+
+			    }
+			break;
+
+		case IOCTL_RWX_MODE:
+			mode = ((mcc_iorw_t *)param)->cmd;
+			switch(mode)
+			{
+				case HPI_RD:
+					lng = (long)(((mcc_iorw_t *)param)->address);
+					lptr =  ((unsigned long *)lng);
+					vptr = (void *)lptr;
+					if (copy_to_user( (((mcc_iorw_t *)param)->buf), (void *)vptr, (((mcc_iorw_t *)param)->nbytes))) {
+						printk("mcc2_ioctl: Failed during  read from hpi.\n");
+						return -EFAULT;
+					}
+					break; 
+					
+								    	  
+				case HPI_WR:
+					lng = (long)(((mcc_iorw_t *)param)->address);
+					lptr =  ((unsigned long *)lng);
+					vptr = (void *)lptr;
+					if (copy_from_user( (void *)vptr, (((mcc_iorw_t *)param)->buf), (((mcc_iorw_t *)param)->nbytes))) {
+						printk("mcc2_ioctl: Failed during  write to hpi\n");
+						return -EFAULT;
+					}
+					break; 
+					
+
+
+                                case FPGA_RD:
+                                        lng = (long)(((mcc_iorw_t *)param)->address);
+                                        lptr =  ((unsigned long *)lng);
+                                        vptr = (void *)lptr;
+                                        if (copy_to_user( (((mcc_iorw_t *)param)->buf), (void *)vptr, (((mcc_iorw_t *)param)->nbytes))) {
+                                                printk("mcc2_ioctl: Failed during  read from FPGA.\n");
+                                                return -EFAULT;
+                                        }
+                                        break;
+
+
+                                case FPGA_WR:
+                                        lng = (long)(((mcc_iorw_t *)param)->address);
+                                        lptr =  ((unsigned long *)lng);
+                                        vptr = (void *)lptr;
+                                        if (copy_from_user( (void *)vptr, (((mcc_iorw_t *)param)->buf), (((mcc_iorw_t *)param)->nbytes))) {
+                                                printk("mcc2_ioctl: Failed during  write to FPGA\n");
+                                                return -EFAULT;
+                                        }
+                                        break;
+
+
+
+
+			   
+				case MEM_MODR:
+					cptr = (char *)Mmap;
+					cptr += ((mcc_iorw_t *)param)->address;
+					if (copy_to_user( (((mcc_iorw_t *)param)->buf), (void *)cptr, (((mcc_iorw_t *)param)->nbytes))) {
+						printk("mcc2_ioctl: Failed during read of read-modify memory\n");
+						return -EFAULT;
+					}
+					break;
+
+				case MEM_MODW:
+					cptr = (char *)Mmap;
+					cptr += ((mcc_iorw_t *)param)->address;
+					if (copy_from_user( (void *)cptr, (((mcc_iorw_t *)param)->buf), (((mcc_iorw_t *)param)->nbytes))) {
+						printk("mcc2_ioctl: Failed during modify of read-modify memory\n");
+						return -EFAULT;
+					}
+					break;
+
+				case IO_PORTS:
+					break;
+				case SI_RAM_CTL1:
+					break;
+				case SI_RAM_CTL2:
+					if (copy_to_user( (void *)param, (siramctl_t *)&(Mmap->im_siramctl2), sizeof(siramctl_t))) {
+						printk("mcc2_ioctl: Failed to copy SI_RAM_CTL2 struct\n");
+						return -EFAULT;
+					}
+					break;
+
+
+
+				default:
+					break;
+			}
+			break;
+
+				default:
+					//if (copy_to_user((void *)param, &mode, sizeof(mode)))
+					printk("We are at the end ...\n");
+					return -EFAULT;
+					break;
+			}
+			break;
+
+		default:
+			break;
+	}
+
+	return 0;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+////////////////////////////////////////////////////////////////////////////////
+
+static ssize_t
+mcc2_open( struct inode *inode, struct file *file )
+{
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+////////////////////////////////////////////////////////////////////////////////
+
+static int
+mcc2_release( struct inode *inode, struct file *file )
+{
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+
+
+
+
+
+#ifndef MODULE
+
+////////////////////////////////////////////////////////////////////////////////
+//
+////////////////////////////////////////////////////////////////////////////////
+
+long
+mcc2_init( long mem_start, long mem_end )
+{
+
+	if ((mcc2_major = register_chrdev(MCC_MAJOR, MCC_NAME, &mcc2_fops)))
+		printk("mcc2_init: Unable to get major for mcc2 device %d\n", MCC_MAJOR);
+	else {
+		//MPC82xxSiuInit();
+		MPC82xxCpmInit();
+	}
+	return mem_start;
+}
+
+#else
+

+ 230 - 0
sys/src/9/ppc/mem.h

@@ -0,0 +1,230 @@
+/*
+ * Memory and machine-specific definitions.  Used in C and assembler.
+ */
+
+#ifdef ucuconf
+#include "ucu.h"
+#endif
+#ifdef blastconf
+#include "blast.h"
+#endif
+
+/*
+ * Sizes
+ */
+
+#define	BI2BY		8			/* bits per byte */
+#define	BI2WD		32			/* bits per word */
+#define	BY2WD		4			/* bytes per word */
+#define 	BY2V		8			/* bytes per vlong */
+#define	BY2PG		4096		/* bytes per page */
+#define	WD2PG		(BY2PG/BY2WD)	/* words per page */
+#define	PGSHIFT		12			/* log(BY2PG) */
+#define 	ROUND(s, sz)	(((s)+(sz-1))&~(sz-1))
+#define 	PGROUND(s)	ROUND(s, BY2PG)
+#define	CACHELINELOG	5
+#define	CACHELINESZ	(1<<CACHELINELOG)
+#define	BLOCKALIGN	CACHELINESZ
+
+#define	MHz	1000000
+
+#define	BY2PTE		8				/* bytes per pte entry */
+#define	BY2PTEG		64				/* bytes per pte group */
+
+#define	MAXMACH	1				/* max # cpus system can run */
+#define	MACHSIZE	BY2PG
+#define	KSTACK		4096			/* Size of kernel stack */
+
+/*
+ * Time
+ */
+#define	HZ		100			/* clock frequency */
+#define	TK2SEC(t)	((t)/HZ)		/* ticks to seconds */
+
+/*
+ * Standard PPC Special Purpose Registers (OEA and VEA)
+ */
+#define DSISR	18
+#define DAR	19		/* Data Address Register */
+#define DEC	22		/* Decrementer */
+#define SDR1	25
+#define SRR0	26		/* Saved Registers (exception) */
+#define SRR1	27
+#define SPRG0	272		/* Supervisor Private Registers */
+#define SPRG1	273
+#define SPRG2	274
+#define SPRG3	275
+#define SPRG4	276
+#define SPRG5	277
+#define SPRG6	278
+#define SPRG7	279
+#define ASR	280		/* Address Space Register */
+#define EAR	282		/* External Access Register (optional) */
+#define TBRU	269		/* Time base Upper/Lower (Reading) */
+#define TBRL	268
+#define TBWU	284		/* Time base Upper/Lower (Writing) */
+#define TBWL	285
+#define PVR	287		/* Processor Version */
+#define IABR	1010	/* Instruction Address Breakpoint Register (optional) */
+#define DABR	1013	/* Data Address Breakpoint Register (optional) */
+#define FPECR	1022	/* Floating-Point Exception Cause Register (optional) */
+#define PIR	1023	/* Processor Identification Register (optional) */
+
+#define IBATU(i)	(528+2*(i))	/* Instruction BAT register (upper) */
+#define IBATL(i)	(529+2*(i))	/* Instruction BAT register (lower) */
+#define DBATU(i)	(536+2*(i))	/* Data BAT register (upper) */
+#define DBATL(i)	(537+2*(i))	/* Data BAT register (lower) */
+
+/*
+ * PPC604e-specific Special Purpose Registers (OEA)
+ */
+#define MMCR0	952		/* Monitor Control Register 0 */
+#define PMC1		953		/* Performance Monitor Counter 1 */
+#define PMC2		954		/* Performance Monitor Counter 2 */
+#define SIA		955		/* Sampled Instruction Address */
+#define MMCR1	956		/* Monitor Control Register 0 */
+#define PMC3		957		/* Performance Monitor Counter 3 */
+#define PMC4		958		/* Performance Monitor Counter 4 */
+#define SDA		959		/* Sampled Data Address */
+/*
+ * PPC603e-specific Special Purpose Registers
+ */
+#define DMISS		976		/* Data Miss Address Register */
+#define DCMP		977		/* Data Miss Address Register */
+#define HASH1		978
+#define HASH2		979
+#define IMISS		980		/* Instruction Miss Address Register */
+#define iCMP		981		/* Instruction Miss Address Register */
+#define RPA		982
+#define HID0		1008	/* Hardware Implementation Dependent Register 0 */
+#define HID1		1009	/* Hardware Implementation Dependent Register 1 */
+/*
+ * PowerQUICC II (MPC 8260) Special Purpose Registers
+ */
+#define HID2		1011	/* Hardware Implementation Dependent Register 2 */
+
+#define	BIT(i)	(1<<(31-(i)))	/* Silly backwards register bit numbering scheme */
+#define	SBIT(n)	((ushort)1<<(15-(n)))
+#define	RBIT(b,n)	(1<<(8*sizeof(n)-1-(b)))
+
+/*
+ * Bit encodings for Machine State Register (MSR)
+ */
+#define MSR_POW		BIT(13)		/* Enable Power Management */
+#define MSR_TGPR		BIT(14)		/* Temporary GPR Registers in use (603e) */
+#define MSR_ILE		BIT(15)		/* Interrupt Little-Endian enable */
+#define MSR_EE		BIT(16)		/* External Interrupt enable */
+#define MSR_PR		BIT(17)		/* Supervisor/User privilege */
+#define MSR_FP		BIT(18)		/* Floating Point enable */
+#define MSR_ME		BIT(19)		/* Machine Check enable */
+#define MSR_FE0		BIT(20)		/* Floating Exception mode 0 */
+#define MSR_SE		BIT(21)		/* Single Step (optional) */
+#define MSR_BE		BIT(22)		/* Branch Trace (optional) */
+#define MSR_FE1		BIT(23)		/* Floating Exception mode 1 */
+#define MSR_IP		BIT(25)		/* Exception prefix 0x000/0xFFF */
+#define MSR_IR		BIT(26)		/* Instruction MMU enable */
+#define MSR_DR		BIT(27)		/* Data MMU enable */
+#define MSR_PM		BIT(29)		/* Performance Monitor marked mode (604e specific) */
+#define MSR_RI		BIT(30)		/* Recoverable Exception */
+#define MSR_LE		BIT(31)		/* Little-Endian enable */
+/* SRR1 bits for TLB operations */
+#define MSR_SR0		0xf0000000	/* Saved bits from CR register */
+#define MSR_KEY		BIT(12)		/* Copy of Ks or Kp bit */
+#define MSR_IMISS		BIT(13)		/* It was an I miss */
+#define MSR_WAY		BIT(14)		/* TLB set to be replaced */
+#define MSR_STORE		BIT(15)		/* Miss caused by a store */
+
+/*
+ * Exception codes (trap vectors)
+ */
+#define CRESET	0x01
+#define CMCHECK	0x02
+#define CDSI		0x03
+#define CISI		0x04
+#define CEI		0x05
+#define CALIGN	0x06
+#define CPROG		0x07
+#define CFPU		0x08
+#define CDEC		0x09
+#define CSYSCALL	0x0C
+#define CTRACE	0x0D	/* optional */
+#define CFPA		0x0E		/* not implemented in 603e */
+
+/* PPC603e-specific: */
+#define CIMISS		0x10	/* Instruction TLB miss */
+#define CLMISS	0x11	/* Data load TLB miss */
+#define CSMISS	0x12	/* Data store TLB miss */
+#define CIBREAK	0x13
+#define CSMI		0x14
+
+/*
+ * Magic registers
+ */
+
+#define	MACH	30		/* R30 is m-> */
+#define	USER		29		/* R29 is up-> */
+
+
+/*
+ *  virtual MMU
+ */
+#define PTEMAPMEM	(1024*1024)	
+#define PTEPERTAB	(PTEMAPMEM/BY2PG)
+#define SEGMAPSIZE	1984
+#define SSEGMAPSIZE	16
+#define PPN(x)		((x)&~(BY2PG-1))
+
+/*
+ *  First pte word
+ */
+#define	PTE0(v, vsid, h, va)	(((v)<<31)|((vsid)<<7)|((h)<<6)|(((va)>>22)&0x3f))
+
+/*
+ *  Second pte word; WIMG & PP(RW/RO) common to page table and BATs
+ */
+#define	PTE1_R	BIT(23)
+#define	PTE1_C	BIT(24)
+
+#define	PTE1_W	BIT(25)
+#define	PTE1_I	BIT(26)
+#define	PTE1_M	BIT(27)
+#define	PTE1_G	BIT(28)
+
+#define	PTE1_RW	BIT(30)
+#define	PTE1_RO	BIT(31)
+
+/* HID0 register bits */
+#define	HID_ICE		BIT(16)
+#define	HID_DCE		BIT(17)
+#define	HID_ILOCK	BIT(18)
+#define	HID_DLOCK	BIT(19)
+#define	HID_ICFI		BIT(20)
+#define	HID_DCFI		BIT(21)
+#define	HID_IFEM		BIT(24)
+
+/*
+ * Address spaces
+ */
+
+#define	KZERO		0x80000000		/* base of kernel address space */
+#define	KTZERO		0x80100000		/* first address in kernel text */
+#define	UZERO		0				/* base of user address space */
+#define	UTZERO		(UZERO+BY2PG)	/* first address in user text */
+#define	USTKTOP		(TSTKTOP-TSTKSIZ*BY2PG)	/* byte just beyond user stack */
+#define	TSTKTOP		KZERO			/* top of temporary stack */
+#define	TSTKSIZ		100
+#define	USTKSIZE		(4*1024*1024)		/* size of user stack */
+#define	UREGSIZE		((8+40)*4)
+#define	MACHADDR	(KTZERO-MAXMACH*MACHSIZE)
+#define	MACHPADDR	(MACHADDR&~KZERO)
+#define	MACHP(n)		((Mach *)(MACHADDR+(n)*MACHSIZE))
+
+#define isphys(x) (((ulong)x&KZERO)!=0)
+
+/*
+ * MPC8xx addresses
+ */
+#define	INTMEM		0xf0000000
+#define	IOMEM		(INTMEM+0x10000)
+
+#define getpgcolor(a)	0

+ 108 - 0
sys/src/9/ppc/mkfile

@@ -0,0 +1,108 @@
+CONF=ucu
+CONFLIST=ucu
+
+loadaddr = 0x80100000
+
+objtype=power
+</$objtype/mkfile
+p=9
+
+DEVS=`{rc ../port/mkdevlist $CONF}
+
+PORT=\
+	alarm.$O\
+	alloc.$O\
+	allocb.$O\
+	auth.$O\
+	cache.$O\
+	chan.$O\
+	dev.$O\
+	fault.$O\
+	latin1.$O\
+	log.$O\
+	rebootcmd.$O\
+	nulledf.$O\
+	page.$O\
+	parse.$O\
+	pgrp.$O\
+	portclock.$O\
+	print.$O\
+	proc.$O\
+	qio.$O\
+	qlock.$O\
+	rdb.$O\
+	segment.$O\
+	swap.$O\
+	sysfile.$O\
+	sysproc.$O\
+	taslock.$O\
+	tod.$O\
+	xalloc.$O\
+
+OBJ=\
+	l.$O\
+	clock.$O\
+	main.$O\
+	mmu.$O\
+	random.$O\
+	trap.$O\
+	$CONF.root.$O\
+	$CONF.rootc.$O\
+	$DEVS\
+	$PORT\
+
+HFILES=\
+	dat.h\
+	errstr.h\
+	etherif.h\
+	fns.h\
+	init.h\
+	io.h\
+	mem.h\
+
+LIB=\
+	/$objtype/lib/libmemlayer.a\
+	/$objtype/lib/libmemdraw.a\
+	/$objtype/lib/libdraw.a\
+	/$objtype/lib/libc.a\
+	/$objtype/lib/libsec.a\
+
+ETHER=`{echo devether.c ether*.c | sed 's/\.c/.'$O'/g'}
+VGA=`{echo devvga.c screen.c vga*.c | sed 's/\.c/.'$O'/g'}
+SDEV=`{echo devsd.c sd*.c | sed 's/\.c/.'$O'/g'}
+
+CFLAGS=$CFLAGS -D$CONF'conf='$CONF
+AFLAGS=$AFLAGS -D$CONF'conf='$CONF
+
+it:V: $p$CONF
+
+9blast:	$CONF.c $OBJ $LIB
+	$CC $CFLAGS '-DKERNDATE='`{date -n} $CONF.c
+	$LD -o $target -T$loadaddr -R4096 -l $OBJ $CONF.$O $LIB
+	size $p$CONF
+
+9ucu:	$CONF.c $OBJ $LIB
+	$CC $CFLAGS '-DKERNDATE='`{date -n} $CONF.c
+	$LD -R0x1000 -H5 -o $target -T$loadaddr -l $OBJ $CONF.$O $LIB
+
+install:V: $p$CONF
+	cp $p$CONF /$objtype/$p$CONF
+
+<../boot/bootmkfile
+<../port/portmkfile
+<|../port/mkbootrules $CONF
+
+clock.$O devether.$O main.$O trap.$O:	/$objtype/include/ureg.h
+
+%.$O:	$HFILES
+
+$ETHER: 			etherif.h ../port/netif.h
+
+init.h:	../port/initcode.c init9.s
+	$CC ../port/initcode.c
+	$AS init9.s
+	$LD -l -s -R4 -o init.out init9.$O initcode.$O /power/lib/libc.a
+	{echo 'uchar initcode[]={'
+	 strip < init.out | xd -1x |
+		sed -e 's/^[0-9a-f]+ //' -e 's/ ([0-9a-f][0-9a-f])/0x\1,/g'
+	 echo '};'} > init.h

+ 259 - 0
sys/src/9/ppc/mmu.c

@@ -0,0 +1,259 @@
+#include	<u.h>
+#include	<ureg.h>
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+
+/*
+ *	We have one page table per processor.
+ *
+ *	Different processes are distinguished via the VSID field in
+ *	the segment registers.  As flushing the entire page table is an
+ *	expensive operation, we implement an aging algorithm for
+ *	mmu pids, with a background kproc to purge stale pids en mass.
+ *
+ *	This needs modifications to run on a multiprocessor.
+ */
+
+static ulong	ptabsize;			/* number of bytes in page table */
+static ulong	ptabmask;		/* hash mask */
+
+/*
+ *	VSID is 24 bits.  3 are required to distinguish segments in user
+ *	space (kernel space only uses the BATs).  pid 0 is reserved.
+ *	The top 2 bits of the pid are used as a `color' for the background
+ *	pid reclaimation algorithm.
+ */
+
+enum {
+	PIDBASE = 1,
+	PIDBITS = 21,
+	COLBITS = 2,
+	PIDMAX = ((1<<PIDBITS)-1),
+	COLMASK = ((1<<COLBITS)-1),
+};
+
+#define	VSID(pid, i)	(((pid)<<3)|i)
+#define	PIDCOLOR(pid)	((pid)>>(PIDBITS-COLBITS))
+#define	PTECOL(color)	PTE0(1, VSID(((color)<<(PIDBITS-COLBITS)), 0), 0, 0)
+
+void
+mmuinit(void)
+{
+	int lhash, mem, i;
+	ulong memsize;
+
+	memsize = conf.npage * BY2PG;
+	if(ptabsize == 0) {
+		/* heuristically size the hash table */
+		lhash = 10;
+		mem = (1<<23);
+		while(mem < memsize) {
+			lhash++;
+			mem <<= 1;
+		}
+		ptabsize = (1<<(lhash+6));
+		ptabmask = (1<<lhash)-1;
+	}
+	m->ptabbase = (ulong)xspanalloc(ptabsize, 0, ptabsize);
+	/* set page table base address */
+	putsdr1(PADDR(m->ptabbase) | (ptabmask>>10));
+	m->mmupid = PIDBASE;
+	m->sweepcolor = 0;
+	m->trigcolor = COLMASK;
+
+	for(i = 0; i < 16; i++)
+		putsr(i<<28, 0);
+}
+
+static int
+work(void*)
+{
+	return PIDCOLOR(m->mmupid) == m->trigcolor;
+}
+
+void
+mmusweep(void*)
+{
+	Proc *p;
+	int i, x, sweepcolor;
+	ulong *ptab, *ptabend, ptecol;
+
+	for(;;) {
+		if(PIDCOLOR(m->mmupid) != m->trigcolor)
+			sleep(&m->sweepr, work, nil);
+
+		sweepcolor = m->sweepcolor;
+		x = splhi();
+		p = proctab(0);
+		for(i = 0; i < conf.nproc; i++, p++)
+			if(PIDCOLOR(p->mmupid) == sweepcolor)
+				p->mmupid = 0;
+		splx(x);
+
+		ptab = (ulong*)m->ptabbase;
+		ptabend = (ulong*)(m->ptabbase+ptabsize);
+		ptecol = PTECOL(sweepcolor);
+		while(ptab < ptabend) {
+			if((*ptab & PTECOL(3)) == ptecol){
+				*ptab = 0;
+			}
+			ptab += 2;
+		}
+
+		m->sweepcolor = (sweepcolor+1) & COLMASK;
+		m->trigcolor = (m->trigcolor+1) & COLMASK;
+	}
+}
+
+int
+newmmupid(void)
+{
+	int pid, newcolor, i, x;
+	Proc *p;
+
+	pid = m->mmupid++;
+	if(m->mmupid > PIDMAX){
+		/* Used up all mmupids, start again from first.  Flush the tlb
+		 * to delete any entries with old pids remaining, then reassign
+		 * all pids.
+		 */
+		m->mmupid = PIDBASE;
+		x = splhi();
+		tlbflushall();
+		p = proctab(0);
+		for(i = 0; i < conf.nproc; i++, p++)
+			p->mmupid = 0;
+		splx(x);
+		wakeup(&m->sweepr);
+	}
+	newcolor = PIDCOLOR(m->mmupid);
+	if(newcolor != PIDCOLOR(pid)) {
+		if(newcolor == m->sweepcolor) {
+			/* desperation time.  can't block here.  punt to fault/putmmu */
+			print("newmmupid: %uld: no free mmu pids\n", up->pid);
+			if(m->mmupid == PIDBASE)
+				m->mmupid = PIDMAX;
+			else
+				m->mmupid--;
+			pid = 0;
+		}
+		else if(newcolor == m->trigcolor)
+			wakeup(&m->sweepr);
+	}
+	up->mmupid = pid;
+	return pid;
+}
+
+void
+flushmmu(void)
+{
+	int x;
+
+	x = splhi();
+	up->newtlb = 1;
+	mmuswitch(up);
+	splx(x);
+}
+
+/*
+ * called with splhi
+ */
+void
+mmuswitch(Proc *p)
+{
+	int i, mp;
+	ulong r;
+
+	if(p->kp) {
+		for(i = 0; i < 8; i++)
+			putsr(i<<28, 0);
+		return;
+	}
+
+	if(p->newtlb) {
+		p->mmupid = 0;
+		p->newtlb = 0;
+	}
+	mp = p->mmupid;
+	if(mp == 0)
+		mp = newmmupid();
+
+	for(i = 0; i < 8; i++){
+		r = VSID(mp, i)|BIT(1)|BIT(2);
+		putsr(i<<28, r);
+	}
+}
+
+void
+mmurelease(Proc* p)
+{
+	p->mmupid = 0;
+}
+
+void
+putmmu(ulong va, ulong pa, Page *pg)
+{
+	int mp;
+	char *ctl;
+	ulong *p, *ep, *q, pteg;
+	ulong vsid, hash;
+	ulong ptehi, x;
+	static ulong pva;
+
+	/*
+	 *	If mmupid is 0, mmuswitch/newmmupid was unable to assign us
+	 *	a pid, hence we faulted.  Keep calling sched() until the mmusweep
+	 *	proc catches up, and we are able to get a pid.
+	 */
+	while((mp = up->mmupid) == 0)
+		sched();
+
+	vsid = VSID(mp, va>>28);
+	hash = (vsid ^ ((va>>12)&0xffff)) & ptabmask;
+	ptehi = PTE0(1, vsid, 0, va);
+	pteg = m->ptabbase + BY2PTEG*hash;
+
+	p = (ulong*)pteg;
+	ep = (ulong*)(pteg+BY2PTEG);
+	q = nil;
+
+	while(p < ep) {
+		x = p[0];
+		if(x == ptehi) {
+			q = p;
+			break;
+		}
+		if(q == nil && (x & BIT(0)) == 0)
+			q = p;
+		p += 2;
+	}
+	if(q == nil) {
+		q = (ulong*)(pteg+m->slotgen);
+		m->slotgen = (m->slotgen + BY2PTE) & (BY2PTEG-1);
+	}
+
+	if (q[0] != ptehi || q[1] != pa){
+		tlbflush(va);
+		m->tlbpurge++;
+	}
+	q[0] = ptehi;
+	q[1] = pa;
+
+	ctl = &pg->cachectl[m->machno];
+	switch(*ctl) {
+	case PG_NEWCOL:
+	default:
+		panic("putmmu: %d\n", *ctl);
+		break;
+	case PG_TXTFLUSH:
+		dcflush((void*)pg->va, BY2PG);
+		icflush((void*)pg->va, BY2PG);
+		*ctl = PG_NOFLUSH;
+		break;
+	case PG_NOFLUSH:
+		break;
+	}
+
+}

+ 188 - 0
sys/src/9/ppc/msaturn.c

@@ -0,0 +1,188 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "msaturn.h"
+
+enum {
+	Isr = Saturn + 0x0400,
+	Ipol = Saturn + 0x0404,
+	Issr = Saturn + 0x0500,
+	Ier = Saturn + 0x0504,
+	Ipri = Saturn + 0x0600,
+	Ithresh = Saturn + 0x0706,
+#define Iar	Ithresh
+};
+
+enum{
+	Syscfg = Saturn + 0x0100,
+};
+
+static uchar intprio[] = {
+		Vecuart0,			// uart 0
+		Vecunused,		// uart 1
+		Vecunused,		// sint
+		Vectimer0,		// timer 0
+		Vecunused,		// timer 1
+		Vecether,			// ethernet
+		Vecunused,		// tea
+		Vecunused,		// irq0	
+		Vecunused,		// irq1	
+		Vecunused,		// irq2	
+		Vecunused,		// irq3	
+		Vecunused,		// irq4	
+		Vecunused,		// irq5	
+		Vecunused,		// irq6	
+		Vecunused,		// irq7	
+		Vecunused,		// irq8	
+};
+
+void
+intend(int)
+{
+}
+
+void
+hwintrinit(void)
+{
+	int i;
+	ushort iar;
+
+	*(ulong*)Ier=0;
+if(0){
+	iar=*(ushort*)Iar;		// reset the stack
+	if(iar != 0xf)
+		panic("saturnintinit: iar not at 0xf (-> %d)\n", iar);
+	intack();
+}
+
+	for(i=0; i<nelem(intprio)/2; i++)
+		((uchar*)Ipri)[i] = (intprio[2*i]<<4)|intprio[2*i+1];
+}
+
+int
+vectorenable(Vctl*v)
+{
+	int i;
+
+	for(i=0; i<nelem(intprio); i++)
+		if(v->irq==intprio[i]){
+			*(ulong*)Ier |= 1<<(31-i);
+			return v->irq;
+		}
+	print("intrenable: cannot enable intr %d\n", v->irq);
+	return -1;
+}
+
+void
+vectordisable(Vctl*v)
+{
+	int i;
+
+	for(i=0; i<nelem(intprio); i++)
+		if(v->irq==intprio[i]){
+			*(ulong*)Ier &= ~(1<<(31-i));
+			return;
+		}
+}
+
+int
+intvec(void)
+{
+	ushort iar;
+	int i;
+
+	iar = *(ushort*)Iar;		// push(prio) onto stack
+	for(i=0; i<nelem(intprio); i++)
+		if(iar==intprio[i])
+			return iar;
+		
+	iprint("saturnint: no vector %d\n", iar);
+	intack();
+	return -1;
+}
+
+void
+intack(void)
+{
+	*(ushort*)Ithresh = 0;	// pop(prio) stack
+}
+
+void
+machinit(void)
+{
+	int rrate;
+	ulong hid;
+	extern char* plan9inistr;
+
+	memset(m, 0, sizeof(*m));
+	m->cputype = getpvr()>>16;
+	m->imap = (Imap*)INTMEM;
+
+	m->loopconst = 1096;
+
+	rrate = (*(ushort*)Syscfg >> 6) & 3;
+	switch(rrate){
+	case 0:
+		m->bushz = 66666666;
+		break;
+	case 1:
+		m->bushz = 83333333;
+		break;
+	case 2:
+		m->bushz = 100000000;
+		break;
+	case 3:
+		m->bushz = 133333333;
+		break;
+	}
+
+	active.machs = 1;
+	active.exiting = 0;
+
+	putmsr(getmsr() | MSR_ME);
+
+	/* disable the l2 cache because it slows down the processor.
+	     the only way (that i know) to get a consistent memory view
+	     with l2 enabled is by enabling write-through operations in 
+	     the bats and ptes.  disabling write-through and only using the
+	     l1 caches improves performance */
+	l2disable();		
+
+	kfpinit();
+
+	hid=gethid0();
+	hid |= BIT(28)|BIT(26)|BIT(24);
+	puthid0(hid);
+
+	plan9inistr =
+		"console=0\n"
+		"ether0=type=saturn\n"
+		"fs=135.104.9.42\n"
+		"auth=135.104.9.7\n"
+		"authdom=cs.bell-labs.com\n"
+		"sys=ucu\n"
+		"ntp=135.104.9.52\n";
+}
+
+void
+sharedseginit(void)
+{
+}
+
+void
+trapinit(void)
+{
+	int i;
+
+	for(i = 0x0; i < 0x2000; i += 0x100)
+		sethvec(i, trapvec);
+
+	dcflush(KADDR(0), 0x2000);
+	icflush(KADDR(0), 0x2000);
+
+	putmsr(getmsr() & ~MSR_IP);
+}

+ 7 - 0
sys/src/9/ppc/msaturn.h

@@ -0,0 +1,7 @@
+enum {
+	Vecuart0 = 1,
+	Vecuart1 = Vecuart0 + 1,
+	Vectimer0 = 0,
+	Vecether = 3,
+	Vecunused = 15,
+};

+ 11 - 0
sys/src/9/ppc/mtx.c

@@ -0,0 +1,11 @@
+/*
+ *	mtx specific stuff:
+ *		Interrupt handling
+ */
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"io.h"
+#include	"mtx.h"
+#include	"fns.h"

+ 138 - 0
sys/src/9/ppc/random.c

@@ -0,0 +1,138 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+
+struct Rb
+{
+	QLock;
+	Rendez	producer;
+	Rendez	consumer;
+	ulong	randomcount;
+	uchar	buf[1024];
+	uchar	*ep;
+	uchar	*rp;
+	uchar	*wp;
+	uchar	next;
+	uchar	wakeme;
+	ushort	bits;
+	ulong	randn;
+} rb;
+
+static int
+rbnotfull(void*)
+{
+	int i;
+
+	i = rb.rp - rb.wp;
+	return i != 1 && i != (1 - sizeof(rb.buf));
+}
+
+static int
+rbnotempty(void*)
+{
+	return rb.wp != rb.rp;
+}
+
+static void
+genrandom(void*)
+{
+	up->basepri = PriNormal;
+	up->priority = up->basepri;
+
+	for(;;){
+		for(;;)
+			if(++rb.randomcount > 100000)
+				break;
+		if(anyhigher())
+			sched();
+		if(!rbnotfull(0))
+			sleep(&rb.producer, rbnotfull, 0);
+	}
+}
+
+/*
+ *  produce random bits in a circular buffer
+ */
+static void
+randomclock(void)
+{
+	if(rb.randomcount == 0 || !rbnotfull(0))
+		return;
+
+	rb.bits = (rb.bits<<2) ^ rb.randomcount;
+	rb.randomcount = 0;
+
+	rb.next++;
+	if(rb.next != 8/2)
+		return;
+	rb.next = 0;
+
+	*rb.wp ^= rb.bits;
+	if(rb.wp+1 == rb.ep)
+		rb.wp = rb.buf;
+	else
+		rb.wp = rb.wp+1;
+
+	if(rb.wakeme)
+		wakeup(&rb.consumer);
+}
+
+void
+randominit(void)
+{
+	addclock0link(randomclock, 1000/HZ);
+	rb.ep = rb.buf + sizeof(rb.buf);
+	rb.rp = rb.wp = rb.buf;
+	kproc("genrandom", genrandom, 0);
+}
+
+/*
+ *  consume random bytes from a circular buffer
+ */
+ulong
+randomread(void *xp, ulong n)
+{
+	uchar *e, *p;
+	ulong x;
+
+	p = xp;
+
+	if(waserror()){
+		qunlock(&rb);
+		nexterror();
+	}
+
+	qlock(&rb);
+	for(e = p + n; p < e; ){
+		if(rb.wp == rb.rp){
+			rb.wakeme = 1;
+			wakeup(&rb.producer);
+			sleep(&rb.consumer, rbnotempty, 0);
+			rb.wakeme = 0;
+			continue;
+		}
+
+		/*
+		 *  beating clocks will be precictable if
+		 *  they are synchronized.  Use a cheap pseudo
+		 *  random number generator to obscure any cycles.
+		 */
+		x = rb.randn*1103515245 ^ *rb.rp;
+		*p++ = rb.randn = x;
+
+		if(rb.rp+1 == rb.ep)
+			rb.rp = rb.buf;
+		else
+			rb.rp = rb.rp+1;
+	}
+	qunlock(&rb);
+	poperror();
+
+	wakeup(&rb.producer);
+
+	return n;
+}

+ 34 - 0
sys/src/9/ppc/rcmain

@@ -0,0 +1,34 @@
+# rcmain: Plan 9 version
+if(~ $#home 0) home=/
+if(~ $#ifs 0) ifs=' 	
+'
+switch($#prompt){
+case 0
+	prompt=('% ' '	')
+case 1
+	prompt=($prompt '	')
+}
+if(~ $rcname ?.out) prompt=('broken! ' '	')
+if(flag p) path=/bin
+if not{
+	finit
+	if(~ $#path 0) path=(. /bin)
+}
+fn sigexit
+if(! ~ $#cflag 0){
+	if(flag l && /bin/test -r $home/lib/profile) . $home/lib/profile
+	status=''
+	eval $cflag
+}
+if not if(flag i){
+	if(flag l && /bin/test -r $home/lib/profile) . $home/lib/profile
+	status=''
+	if(! ~ $#* 0) . $*
+	. -i '#d/0'
+}
+if not if(~ $#* 0) . '#d/0'
+if not{
+	status=''
+	. $*
+}
+exit $status

+ 94 - 0
sys/src/9/ppc/saturntimer.c

@@ -0,0 +1,94 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "msaturn.h"
+
+enum {
+	Timer_ctrl = Saturn + 0x0106,
+	Timer0_load = Saturn + 0x0200,
+	Timer0_cnt = Saturn + 0x0204,
+	Timer1_load = Saturn + 0x0300,
+	Timer1_cnt = Saturn + 0x0304,
+	
+	T0_event = RBIT(13, ushort),
+	T0_ie = RBIT(14, ushort),
+	T0_cen = RBIT(15, ushort),
+	T1_event = RBIT(5, ushort),
+	T1_ie = RBIT(6, ushort),
+	T1_cen = RBIT(7, ushort),
+};
+
+static ulong ticks;
+static Lock tlock;
+static ushort timer_ctl;
+
+void
+saturntimerintr(Ureg *u, void*)
+{
+	ushort ctl = *(ushort*)Timer_ctrl, v = 0;
+
+	if(ctl&T1_event){
+		v = T1_event;
+		ticks++;
+	}
+		
+	*(ushort*)Timer_ctrl = timer_ctl|T0_event|v;
+	intack();
+	timerintr(u, 0);
+}
+
+void
+timerinit(void)
+{
+	*(ushort*)Timer_ctrl = 0;
+	*(ulong*)Timer0_load = m->bushz  / HZ;
+	*(ulong*)Timer0_cnt = m->bushz / HZ;
+	*(ulong*)Timer1_load = m->bushz;
+	*(ulong*)Timer1_cnt = m->bushz;
+	intrenable(Vectimer0, saturntimerintr, nil, "timer");
+
+	timer_ctl = T0_cen|T0_ie|T1_cen;
+	*(ushort*)Timer_ctrl = timer_ctl;
+}
+
+uvlong
+fastticks(uvlong *hz)
+{
+	assert(*(ushort*)Timer_ctrl&T1_cen);
+	if(*(ushort*)Timer_ctrl&T1_event){
+		*(ushort*)Timer_ctrl = timer_ctl|T1_event;
+		ticks++;
+	}
+		
+	if (hz)
+		*hz = m->bushz;
+
+	return (uvlong)ticks*m->bushz+(uvlong)(m->bushz-*(ulong*)Timer1_cnt);
+}
+
+void
+timerset(uvlong next)
+{
+	ulong offset;
+	uvlong now;
+
+	ilock(&tlock);
+	*(ushort*)Timer_ctrl = T1_cen;
+
+	now = fastticks(nil);
+	offset = next - now;
+	if((long)offset<25000)
+		offset = 25000;
+	else if(offset > m->bushz)
+		offset = m->bushz;
+
+	*(ulong*)Timer0_cnt = offset;
+	*(ushort*)Timer_ctrl = timer_ctl;
+	assert(*(ushort*)Timer_ctrl & T1_cen);
+	iunlock(&tlock);
+}
+

+ 871 - 0
sys/src/9/ppc/trap.c

@@ -0,0 +1,871 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"ureg.h"
+#include	"../port/error.h"
+
+static Lock vctllock;
+static Vctl *vctl[256];
+
+void
+intrenable(int irq, void (*f)(Ureg*, void*), void* a, char *name)
+{
+	int vno;
+	Vctl *v;
+
+	if(f == nil){
+		print("intrenable: nil handler for %d for %s\n",
+			irq, name);
+		return;
+	}
+
+	v = xalloc(sizeof(Vctl));
+	v->isintr = 1;
+	v->irq = irq;
+	v->f = f;
+	v->a = a;
+	strncpy(v->name, name, KNAMELEN-1);
+	v->name[KNAMELEN-1] = 0;
+
+	ilock(&vctllock);
+	vno = vectorenable(v);
+	if(vno == -1){
+		iunlock(&vctllock);
+		print("intrenable: couldn't enable irq %d for %s\n",
+			irq, v->name);
+		xfree(v);
+		return;
+	}
+	v->next = vctl[vno];
+	vctl[vno] = v;
+	iunlock(&vctllock);
+}
+
+void
+intrdisable(int irq, void (*f)(Ureg *, void *), void *a, char *name)
+{
+	Vctl **pv, *v;
+
+	ilock(&vctllock);
+	pv = &vctl[irq];
+	while (*pv && 
+		  ((*pv)->irq != irq || (*pv)->f != f || (*pv)->a != a ||
+		   strcmp((*pv)->name, name)))
+		pv = &((*pv)->next);
+	assert(*pv);
+
+	v = *pv;
+	*pv = (*pv)->next;	/* Link out the entry */
+	
+	if(vctl[irq] == nil)
+		vectordisable(v);
+	iunlock(&vctllock);
+	xfree(v);
+}
+
+void	syscall(Ureg*);
+void	noted(Ureg*, ulong);
+static void _dumpstack(Ureg*);
+
+char *excname[] =
+{
+	"reserved 0",
+	"system reset",
+	"machine check",
+	"data access",
+	"instruction access",
+	"external interrupt",
+	"alignment",
+	"program exception",
+	"floating-point unavailable",
+	"decrementer",
+	"reserved A",
+	"reserved B",
+	"system call",
+	"trace trap",
+	"floating point assist",
+	"reserved F",
+	"reserved 10",
+	"data load translation miss",
+	"data store translation miss",
+	"instruction address breakpoint",
+	"system management interrupt",
+};
+
+char *fpcause[] =
+{
+	"inexact operation",
+	"division by zero",
+	"underflow",
+	"overflow",
+	"invalid operation",
+};
+char	*fpexcname(Ureg*, ulong, char*);
+#define FPEXPMASK	0xfff80300		/* Floating exception bits in fpscr */
+
+
+char *regname[]={
+	"CAUSE",	"SRR1",
+	"PC",		"GOK",
+	"LR",		"CR",
+	"XER",	"CTR",
+	"R0",		"R1",
+	"R2",		"R3",
+	"R4",		"R5",
+	"R6",		"R7",
+	"R8",		"R9",
+	"R10",	"R11",
+	"R12",	"R13",
+	"R14",	"R15",
+	"R16",	"R17",
+	"R18",	"R19",
+	"R20",	"R21",
+	"R22",	"R23",
+	"R24",	"R25",
+	"R26",	"R27",
+	"R28",	"R29",
+	"R30",	"R31",
+	"DCMP",	"ICMP",
+	"DMISS",	"IMISS",
+	"HASH1",	"HASH2",
+	"DAR",	"DSISR",
+};
+
+void
+trap(Ureg *ureg)
+{
+	int ecode, user;
+	char buf[ERRMAX], *s;
+
+	ecode = (ureg->cause >> 8) & 0xff;
+	user = (ureg->srr1 & MSR_PR) != 0;
+	if(user)
+		up->dbgreg = ureg;
+
+	if(ureg->status & MSR_RI == 0)
+		print("double fault?: ecode = %d\n", ecode);
+
+	switch(ecode) {
+	case CEI:
+		m->intr++;
+		intr(ureg);
+		break;
+	case CDEC:
+		clockintr(ureg);
+		break;
+	case CDSI:
+		m->pfault++;
+		if (up == nil){
+			dumpregs(ureg);
+			panic("kernel fault");
+		}
+		if (up->mmureg)
+			panic("recursing");
+		up->mmureg = ureg;
+		up->mmuinstr = 0;
+		faultpower(ureg, ureg->dar, (ureg->dsisr & BIT(6)) == 0);
+		up->mmureg = nil;
+		break;
+	case CISI:
+		m->pfault++;
+		if (up == nil){
+			dumpregs(ureg);
+			panic("kernel fault");
+		}
+		if (up->mmureg)
+			panic("recursing");
+		up->mmureg = ureg;
+		up->mmuinstr = 0;
+		faultpower(ureg, ureg->pc, 1);
+		up->mmureg = nil;
+		break;
+	case CIMISS:	/* instruction miss */
+		if (up == nil){
+			dumpregs(ureg);
+			panic("kernel fault");
+		}
+		if (up->mmureg)
+			panic("recursing");
+		up->mmureg = ureg;
+		up->mmuinstr = 0;
+		faultpower(ureg, ureg->imiss, 1);
+		up->mmureg = nil;
+		break;
+	case CLMISS:	/* data load miss */
+		if (up == nil){
+			dumpregs(ureg);
+			panic("kernel fault");
+		}
+		if (up->mmureg)
+			panic("recursing");
+		up->mmureg = ureg;
+		up->mmuinstr = 0;
+		faultpower(ureg, ureg->dmiss, 1);
+		up->mmureg = nil;
+		break;
+	case CSMISS:	/* data store miss */
+		if (up == nil){
+			dumpregs(ureg);
+			panic("kernel fault");
+		}
+		if (up->mmureg)
+			panic("recursing");
+		up->mmureg = ureg;
+		up->mmuinstr = 0;
+		faultpower(ureg, ureg->dmiss, 0);
+		up->mmureg = nil;
+		break;
+
+	case CSYSCALL:
+		if(!user)
+			panic("syscall in kernel: srr1 0x%4.4luX\n", ureg->srr1);
+		syscall(ureg);
+		return;		/* syscall() calls notify itself, don't do it again */
+
+	case CFPU:
+		if(!user || up == nil) {
+			dumpregs(ureg);
+			panic("floating point in kernel");
+		}
+		switch(up->fpstate){
+		case FPinit:
+			fprestore(&initfp);
+			up->fpstate = FPactive;
+			break;
+		case FPinactive:
+			fprestore(&up->fpsave);
+			up->fpstate = FPactive;
+			break;
+		case FPactive:
+			print("up->fpstate %d\n", up->fpstate);
+			delay(100);
+			dumpregs(ureg);
+			delay(200);
+			panic("fpstate");
+			break;
+		default:
+			if(user){
+				spllo();
+				sprint(buf, "sys: floating point in note handler:");
+				postnote(up, 1, buf, NDebug);
+				break;
+			}
+			panic("kernel fpstate illegal");
+		}
+		ureg->srr1 |= MSR_FP;
+		break;
+	case CPROG:
+		if(ureg->status & (1<<19))
+			s = "floating point exception";
+		else if(ureg->status & (1<<18))
+			s = "illegal instruction";
+		else if(ureg->status & (1<<17))
+			s = "privileged instruction";
+		else
+			s = "undefined program exception";
+		if(user){
+			spllo();
+			sprint(buf, "sys: trap: %s", s);
+			postnote(up, 1, buf, NDebug);
+			break;
+		}
+		dumpregs(ureg);
+		panic(s);
+		break;
+	default:
+		if(ecode <= nelem(excname) && user){
+			spllo();
+			sprint(buf, "sys: trap: %s", excname[ecode]);
+			postnote(up, 1, buf, NDebug);
+			break;
+		}
+		dumpregs(ureg);
+		if(ecode < nelem(excname))
+			panic("%s", excname[ecode]);
+		panic("unknown trap/intr: %d\n", ecode);
+	}
+
+	/* restoreureg must execute at high IPL */
+	splhi();
+	if(user) {
+		if (up->fpstate == FPactive && (ureg->srr1 & MSR_FP) == 0){
+			postnote(up, 1, buf, NDebug);
+		}
+		notify(ureg);
+		if(up->fpstate != FPactive)
+			ureg->srr1 &= ~MSR_FP;
+	}
+}
+
+void
+faultpower(Ureg *ureg, ulong addr, int read)
+{
+	int user, insyscall, n;
+	char buf[ERRMAX];
+
+	user = (ureg->srr1 & MSR_PR) != 0;
+	insyscall = up->insyscall;
+	up->insyscall = 1;
+	n = fault(addr, read);
+	if(n < 0){
+		if(!user){
+			dumpregs(ureg);
+			panic("fault: 0x%lux", addr);
+		}
+		sprint(buf, "sys: trap: fault %s addr=0x%lux", read? "read" : "write", addr);
+		postnote(up, 1, buf, NDebug);
+	}
+	up->insyscall = insyscall;
+}
+
+void
+sethvec(int v, void (*r)(void))
+{
+	ulong *vp, pa, o;
+
+	vp = KADDR(v);
+	vp[0] = 0x7c1043a6;			/* MOVW R0, SPR(SPRG0) */
+	vp[1] = 0x7c0802a6;			/* MOVW LR, R0 */
+	vp[2] = 0x7c1243a6;			/* MOVW R0, SPR(SPRG2) */
+	pa = PADDR(r);
+	o = pa >> 25;
+	if(o != 0 && o != 0x7F){
+		/* a branch too far */
+		vp[3] = (15<<26)|(pa>>16);	/* MOVW $r&~0xFFFF, R0 */
+		vp[4] = (24<<26)|(pa&0xFFFF);	/* OR $r&0xFFFF, R0 */
+		vp[5] = 0x7c0803a6;		/* MOVW	R0, LR */
+		vp[6] = 0x4e800021;		/* BL (LR) */
+	}else
+		vp[3] = (18<<26)|(pa&0x3FFFFFC)|3;	/* bla */
+}
+
+void
+setmvec(int v, void (*r)(void), void (*t)(void))
+{
+	ulong *vp, pa, o, n;
+
+	vp = KADDR(v);
+	n = 0;
+	vp[n++] = 0x7c1043a6;			/* MOVW R0, SPR(SPRG0) */
+	vp[n++] = 0x7c0802a6;			/* MOVW LR, R0 */
+	vp[n++] = 0x7c1243a6;			/* MOVW R0, SPR(SPRG2) */
+	pa = PADDR(r);
+	o = pa >> 25;
+	if(o != 0 && o != 0x7F){
+		/* a branch too far */
+		vp[n++] = (15<<26)|(pa>>16);	/* MOVW $r&~0xFFFF, R0 */
+		vp[n++] = (24<<26)|(pa&0xFFFF);	/* OR $r&0xFFFF, R0 */
+		vp[n++] = 0x7c0803a6;		/* MOVW	R0, LR */
+		vp[n++] = 0x4e800021;		/* BL (LR) */
+	}else
+		vp[n++] = (18<<26)|(pa&0x3FFFFFC)|3;	/* bla */
+	pa = PADDR(t);
+	o = pa >> 25;
+	if(o != 0 && o != 0x7F){
+		/* a branch too far */
+		vp[n++] = (15<<26)|(pa>>16);	/* MOVW $r&~0xFFFF, R0 */
+		vp[n++] = (24<<26)|(pa&0xFFFF);	/* OR $r&0xFFFF, R0 */
+		vp[n++] = 0x7c0803a6;		/* MOVW	R0, LR */
+		vp[n] = 0x4e800021;		/* BL (LR) */
+	}else
+		vp[n] = (18<<26)|(pa&0x3FFFFFC)|3;	/* bla */
+}
+
+void
+intr(Ureg *ureg)
+{
+	int vno;
+	Vctl *ctl, *v;
+	static int inintr;
+
+	if (inintr > 4)
+		panic("recursing");
+	inintr++;
+	vno = intvec();
+
+	if(vno > nelem(vctl) || (ctl = vctl[vno]) == 0) {
+		iprint("spurious intr %d\n", vno);
+		inintr--;
+		return;
+	}
+
+	for(v = ctl; v != nil; v = v->next){
+		if(v->f)
+			v->f(ureg, v->a);
+	}
+
+	intend(vno);	/* reenable the interrupt */
+
+	inintr--;
+
+	preempted();
+}
+
+char*
+fpexcname(Ureg *ur, ulong fpscr, char *buf)
+{
+	int i;
+	char *s;
+	ulong fppc;
+
+	fppc = ur->pc;
+	s = 0;
+	fpscr >>= 3;		/* trap enable bits */
+	fpscr &= (fpscr>>22);	/* anded with exceptions */
+	for(i=0; i<5; i++)
+		if(fpscr & (1<<i))
+			s = fpcause[i];
+	if(s == 0)
+		return "no floating point exception";
+	sprint(buf, "%s fppc=0x%lux", s, fppc);
+	return buf;
+}
+
+/*
+ * Fill in enough of Ureg to get a stack trace, and call a function.
+ * Used by debugging interface rdb.
+ */
+
+static void
+getpcsp(ulong *pc, ulong *sp)
+{
+	*pc = getcallerpc(&pc);
+	*sp = (ulong)&pc-4;
+}
+
+void
+callwithureg(void (*fn)(Ureg*))
+{
+	Ureg ureg;
+
+	getpcsp((ulong*)&ureg.pc, (ulong*)&ureg.sp);
+	ureg.lr = getcallerpc(&fn);
+	fn(&ureg);
+}
+
+static void
+_dumpstack(Ureg *ureg)
+{
+	ulong l, sl, el, v;
+	int i;
+
+	l = (ulong)&l;
+	if(up == 0){
+		el = (ulong)m+BY2PG;
+		sl = el-KSTACK;
+	}
+	else{
+		sl = (ulong)up->kstack;
+		el = sl + KSTACK;
+	}
+	if(l > el || l < sl){
+		el = (ulong)m+BY2PG;
+		sl = el-KSTACK;
+	}
+	if(l > el || l < sl)
+		return;
+	print("ktrace /kernel/path %.8lux %.8lux %.8lux\n", ureg->pc, ureg->sp, ureg->lr);
+	i = 0;
+	for(; l < el; l += 4){
+		v = *(ulong*)l;
+		if(KTZERO < v && v < (ulong)etext){
+			print("%.8lux=%.8lux ", l, v);
+			if(i++ == 4){
+				print("\n");
+				i = 0;
+			}
+		}
+	}
+}
+
+void
+dumpstack(void)
+{
+	callwithureg(_dumpstack);
+}
+
+void
+dumpregs(Ureg *ur)
+{
+	int i;
+	ulong *l;
+
+	if(up) {
+		print("registers for %s %ld\n", up->text, up->pid);
+		if(ur->srr1 & MSR_PR == 0)
+		if(ur->usp < (ulong)up->kstack || ur->usp > (ulong)up->kstack+KSTACK)
+			print("invalid stack ptr\n");
+	}
+	else
+		print("registers for kernel\n");
+
+	for(i=0; i<16; i+=2)
+		print("sr[%x]\t0x%.8lux\tsr[%x]\t0x%.8lux\n", i, getsr(i<<28), i+1, getsr((i+1)<<28));
+	l = &ur->cause;
+	for(i=0; i<sizeof regname/sizeof(char*); i+=2, l+=2)
+		print("%s\t0x%.8lux\t%s\t0x%.8lux\n", regname[i], l[0], regname[i+1], l[1]);
+	delay(500);
+}
+
+static void
+linkproc(void)
+{
+	spllo();
+	(*up->kpfun)(up->kparg);
+	pexit("", 0);
+}
+
+void
+kprocchild(Proc *p, void (*func)(void*), void *arg)
+{
+	p->sched.pc = (ulong)linkproc;
+	p->sched.sp = (ulong)p->kstack+KSTACK;
+
+	p->kpfun = func;
+	p->kparg = arg;
+}
+
+/*
+ * called in sysfile.c
+ */
+void
+evenaddr(ulong addr)
+{
+	if(addr & 3){
+		postnote(up, 1, "sys: odd address", NDebug);
+		error(Ebadarg);
+	}
+}
+
+long
+execregs(ulong entry, ulong ssize, ulong nargs)
+{
+	ulong *sp;
+	Ureg *ureg;
+
+	sp = (ulong*)(USTKTOP - ssize);
+	*--sp = nargs;
+
+	ureg = up->dbgreg;
+	ureg->usp = (ulong)sp;
+	ureg->pc = entry;
+	ureg->srr1 &= ~MSR_FP;		/* disable floating point */
+	up->fpstate = FPinit;
+	return USTKTOP-BY2WD;		/* address of user-level clock */
+}
+
+void
+forkchild(Proc *p, Ureg *ur)
+{
+	Ureg *cur;
+
+	p->sched.sp = (ulong)p->kstack+KSTACK-UREGSIZE;
+	p->sched.pc = (ulong)forkret;
+
+	cur = (Ureg*)(p->sched.sp+2*BY2WD);
+	memmove(cur, ur, sizeof(Ureg));
+	cur->r3 = 0;
+	
+	/* Things from bottom of syscall we never got to execute */
+	p->psstate = 0;
+	p->insyscall = 0;
+}
+
+ulong
+userpc(void)
+{
+	Ureg *ureg;
+
+	ureg = (Ureg*)up->dbgreg;
+	return ureg->pc;
+}
+
+
+/* This routine must save the values of registers the user is not 
+ * permitted to write from devproc and then restore the saved values 
+ * before returning
+ */
+void
+setregisters(Ureg *xp, char *pureg, char *uva, int n)
+{
+	ulong status;
+
+	status = xp->status;
+	memmove(pureg, uva, n);
+	xp->status = status;
+}
+
+/* Give enough context in the ureg to produce a kernel stack for
+ * a sleeping process
+ */
+void
+setkernur(Ureg* ureg, Proc* p)
+{
+	ureg->pc = p->sched.pc;
+	ureg->sp = p->sched.sp+4;
+}
+
+ulong
+dbgpc(Proc *p)
+{
+	Ureg *ureg;
+
+	ureg = p->dbgreg;
+	if(ureg == 0)
+		return 0;
+
+	return ureg->pc;
+}
+
+/*
+ *  system calls
+ */
+#include "../port/systab.h"
+
+/* TODO: make this trap a separate asm entry point, like on other RISC architectures */
+void
+syscall(Ureg* ureg)
+{
+	int i;
+	char *e;
+	long	ret;
+	ulong sp, scallnr;
+
+	m->syscall++;
+	up->insyscall = 1;
+	up->pc = ureg->pc;
+	up->dbgreg = ureg;
+
+	if (up->fpstate == FPactive && (ureg->srr1 & MSR_FP) == 0){
+		print("fpstate check, entry syscall\n");
+		delay(200);
+		dumpregs(ureg);
+		print("fpstate check, entry syscall\n");
+	}
+
+	scallnr = ureg->r3;
+	up->scallnr = ureg->r3;
+	if(scallnr == RFORK && up->fpstate == FPactive){
+		fpsave(&up->fpsave);
+		up->fpstate = FPinactive;
+	}
+	spllo();
+
+	sp = ureg->usp;
+	up->nerrlab = 0;
+	ret = -1;
+	if(!waserror()){
+		if(scallnr >= nsyscall || systab[scallnr] == nil){
+			pprint("bad sys call number %d pc %lux\n", scallnr, ureg->pc);
+			postnote(up, 1, "sys: bad sys call", NDebug);
+			error(Ebadarg);
+		}
+
+		if(sp<(USTKTOP-BY2PG) || sp>(USTKTOP-sizeof(Sargs)-BY2WD))
+			validaddr(sp, sizeof(Sargs)+BY2WD, 0);
+
+		up->s = *((Sargs*)(sp+BY2WD));
+		up->psstate = sysctab[scallnr];
+
+		ret = systab[scallnr](up->s.args);
+		poperror();
+	}else{
+		/* failure: save the error buffer for errstr */
+		e = up->syserrstr;
+		up->syserrstr = up->errstr;
+		up->errstr = e;
+	}
+	if(up->nerrlab){
+		print("bad errstack [%uld]: %d extra\n", scallnr, up->nerrlab);
+		print("scall %s lr =%lux\n", sysctab[scallnr], ureg->lr);
+		for(i = 0; i < NERR; i++)
+			print("sp=%lux pc=%lux\n", up->errlab[i].sp, up->errlab[i].pc);
+		panic("error stack");
+	}
+
+	up->insyscall = 0;
+	up->psstate = 0;
+
+	/*
+	 *  Put return value in frame.  On the x86 the syscall is
+	 *  just another trap and the return value from syscall is
+	 *  ignored.  On other machines the return value is put into
+	 *  the results register by caller of syscall.
+	 */
+	ureg->r3 = ret;
+
+	if(scallnr == NOTED)
+		noted(ureg, *(ulong*)(sp+BY2WD));
+
+	/* restoreureg must execute at high IPL */
+	splhi();
+	if(scallnr!=RFORK)
+		notify(ureg);
+
+	if (up->fpstate == FPactive && (ureg->srr1 & MSR_FP) == 0){
+		print("fpstate check, exit syscall nr %lud, pid %lud\n", scallnr, up->pid);
+		dumpregs(ureg);
+	}
+	if(up->fpstate != FPactive)
+		ureg->srr1 &= ~MSR_FP;
+}
+
+/*
+ *  Call user, if necessary, with note.
+ *  Pass user the Ureg struct and the note on his stack.
+ */
+int
+notify(Ureg* ur)
+{
+	int l;
+	ulong s, sp;
+	Note *n;
+
+	if(up->procctl)
+		procctl(up);
+	if(up->nnote == 0)
+		return 0;
+
+	s = spllo();
+	qlock(&up->debug);
+	up->notepending = 0;
+	n = &up->note[0];
+	if(strncmp(n->msg, "sys:", 4) == 0){
+		l = strlen(n->msg);
+		if(l > ERRMAX-15)	/* " pc=0x12345678\0" */
+			l = ERRMAX-15;
+		sprint(n->msg+l, " pc=0x%.8lux", ur->pc);
+	}
+
+	if(n->flag!=NUser && (up->notified || up->notify==0)){
+		if(n->flag == NDebug)
+			pprint("suicide: %s\n", n->msg);
+		qunlock(&up->debug);
+		pexit(n->msg, n->flag!=NDebug);
+	}
+
+	if(up->notified) {
+		qunlock(&up->debug);
+		splhi();
+		return 0;
+	}
+
+	if(!up->notify) {
+		qunlock(&up->debug);
+		pexit(n->msg, n->flag!=NDebug);
+	}
+
+	if(up->fpstate == FPactive){
+		fpsave(&up->fpsave);
+		up->fpstate = FPinactive;
+	}
+	up->fpstate |= FPillegal;
+
+	sp = ur->usp & ~(BY2V-1);
+	sp -= sizeof(Ureg);
+
+	if(!okaddr((ulong)up->notify, BY2WD, 0) ||
+	   !okaddr(sp-ERRMAX-4*BY2WD, sizeof(Ureg)+ERRMAX+4*BY2WD, 1)) {
+		pprint("suicide: bad address or sp in notify\n");
+		qunlock(&up->debug);
+		pexit("Suicide", 0);
+	}
+
+	memmove((Ureg*)sp, ur, sizeof(Ureg));
+	*(Ureg**)(sp-BY2WD) = up->ureg;	/* word under Ureg is old up->ureg */
+	up->ureg = (void*)sp;
+	sp -= BY2WD+ERRMAX;
+	memmove((char*)sp, up->note[0].msg, ERRMAX);
+	sp -= 3*BY2WD;
+	*(ulong*)(sp+2*BY2WD) = sp+3*BY2WD;	/* arg 2 is string */
+	ur->r1 = (long)up->ureg;		/* arg 1 is ureg* */
+	((ulong*)sp)[1] = (ulong)up->ureg;	/* arg 1 0(FP) is ureg* */
+	((ulong*)sp)[0] = 0;			/* arg 0 is pc */
+	ur->usp = sp;
+	ur->pc = (ulong)up->notify;
+	up->notified = 1;
+	up->nnote--;
+	memmove(&up->lastnote, &up->note[0], sizeof(Note));
+	memmove(&up->note[0], &up->note[1], up->nnote*sizeof(Note));
+
+	qunlock(&up->debug);
+	splx(s);
+	return 1;
+}
+
+
+/*
+ *   Return user to state before notify()
+ */
+void
+noted(Ureg* ureg, ulong arg0)
+{
+	Ureg *nureg;
+	ulong oureg, sp;
+
+	qlock(&up->debug);
+	if(arg0!=NRSTR && !up->notified) {
+		qunlock(&up->debug);
+		pprint("call to noted() when not notified\n");
+		pexit("Suicide", 0);
+	}
+	up->notified = 0;
+
+	nureg = up->ureg;	/* pointer to user returned Ureg struct */
+
+	/* sanity clause */
+	oureg = (ulong)nureg;
+	if(!okaddr((ulong)oureg-BY2WD, BY2WD+sizeof(Ureg), 0)){
+		pprint("bad ureg in noted or call to noted when not notified\n");
+		qunlock(&up->debug);
+		pexit("Suicide", 0);
+	}
+
+	memmove(ureg, nureg, sizeof(Ureg));
+
+	switch(arg0){
+	case NCONT:
+	case NRSTR:
+		if(!okaddr(nureg->pc, 1, 0) || !okaddr(nureg->usp, BY2WD, 0)){
+			pprint("suicide: trap in noted\n");
+			qunlock(&up->debug);
+			pexit("Suicide", 0);
+		}
+		up->ureg = (Ureg*)(*(ulong*)(oureg-BY2WD));
+		qunlock(&up->debug);
+		break;
+
+	case NSAVE:
+		if(!okaddr(nureg->pc, BY2WD, 0)
+		|| !okaddr(nureg->usp, BY2WD, 0)){
+			pprint("suicide: trap in noted\n");
+			qunlock(&up->debug);
+			pexit("Suicide", 0);
+		}
+		qunlock(&up->debug);
+		sp = oureg-4*BY2WD-ERRMAX;
+		splhi();
+		ureg->sp = sp;
+		((ulong*)sp)[1] = oureg;	/* arg 1 0(FP) is ureg* */
+		((ulong*)sp)[0] = 0;		/* arg 0 is pc */
+		break;
+
+	default:
+		pprint("unknown noted arg 0x%lux\n", arg0);
+		up->lastnote.flag = NDebug;
+		/* fall through */
+		
+	case NDFLT:
+		if(up->lastnote.flag == NDebug)
+			pprint("suicide: %s\n", up->lastnote.msg);
+		qunlock(&up->debug);
+		pexit(up->lastnote.msg, up->lastnote.flag!=NDebug);
+	}
+	up->fpstate &= ~FPillegal;
+	if (up->fpstate == FPactive)
+		ureg->srr1 |= MSR_FP;
+	else
+		ureg->srr1 &= ~MSR_FP;
+}

+ 472 - 0
sys/src/9/ppc/uartsaturn.c

@@ -0,0 +1,472 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "msaturn.h"
+
+enum{
+	UartAoffs = Saturn + 0x0a00,
+	UartBoffs = Saturn + 0x0b00,
+	Nuart = 2,
+
+	Baudfreq = 14745600 / 16,
+	Lcr_div = RBIT(1, uchar),
+	Lcr_peven = RBIT(3, uchar),
+	Lcr_pen = RBIT(4, uchar),
+	Lcr_stop = RBIT(5, uchar),
+	Lcr_wrdlenmask = RBIT(6, uchar) | RBIT(7, uchar),
+	Lcr_wrdlenshift = 0,
+	Lsr_tbre = RBIT(2, uchar),	
+	Fcr_txreset = RBIT(5, uchar),
+	Fcr_rxreset = RBIT(6, uchar),
+	Iir_txempty = RBIT(5, uchar),
+	Iir_rxfull = RBIT(6, uchar),
+	Iir_rxerr = RBIT(7, uchar),
+	Ier_rxerr = RBIT(5, uchar),
+	Ier_txempty = RBIT(6, uchar),
+	Ier_rxfull = RBIT(7, uchar),
+	Lsr_rxavail = RBIT(7, uchar),
+	Txsize = 16,
+	Rxsize = 16,
+};
+
+typedef struct Saturnuart Saturnuart;
+struct Saturnuart {
+	uchar	rxb;
+#define txb	rxb
+#define dll	rxb
+	uchar	ier;			// Interrupt enable, divisor latch
+#define dlm	ier
+	uchar	iir;			// Interrupt identification, fifo control
+#define fcr	iir	
+	uchar	lcr;			// Line control register
+	uchar	f1;		
+	uchar	lsr;			// Line status register
+	ushort	f2;
+};
+
+typedef struct UartData UartData;
+struct UartData {
+	int			suno;	/* saturn uart number: 0 or 1 */
+	Saturnuart	*su;
+	char			*rxbuf;
+	char			*txbuf;
+	int			initialized;
+	int			enabled;
+} uartdata[Nuart];
+
+extern PhysUart saturnphysuart;
+
+Uart suart[Nuart] = {
+	{
+		.name = "SaturnUart1",
+		.baud = 19200,
+		.bits = 8,
+		.stop = 1,
+		.parity = 'n',
+		.phys = &saturnphysuart,
+		.special = 0,
+	},
+	{
+		.name = "SaturnUart2",
+		.baud = 115200,
+		.bits = 8,
+		.stop = 1,
+		.parity = 'n',
+		.phys = &saturnphysuart,
+		.special = 0,
+	},
+};
+
+static void suinterrupt(Ureg*, void*);
+
+static Uart*
+supnp(void)
+{
+	int i;
+
+	for (i = 0; i < nelem(suart)-1; i++)
+		suart[i].next = &suart[i + 1];
+	suart[nelem(suart)-1].next=nil;
+	return suart;
+}
+
+static void
+suinit(Uart*uart)
+{
+	UartData *ud;
+	Saturnuart *su;
+
+	ud = uart->regs;
+	su = ud->su;
+	su->fcr=Fcr_txreset|Fcr_rxreset;
+	ud->initialized=1;
+}
+
+static void
+suenable(Uart*uart, int ie)
+{
+	Saturnuart *su;
+	UartData *ud;
+	int nr;
+
+	nr = uart - suart;
+	if (nr < 0 || nr > Nuart)
+		panic("No uart %d", nr);
+	ud = uartdata + nr;
+	ud->suno = nr;
+	su=ud->su = (Saturnuart*)((nr == 0)? UartAoffs: UartBoffs);
+	uart->regs = ud;
+
+	if(ud->initialized==0)
+		suinit(uart);
+
+	if(!ud->enabled && ie){
+		intrenable(Vecuart0+nr , suinterrupt, uart, uart->name);
+		su->ier=Ier_txempty|Ier_rxfull;
+		ud->enabled=1;
+	}
+}
+
+
+static long
+sustatus(Uart* uart, void* buf, long n, long offset)
+{
+	Saturnuart *su;
+	char p[128];
+
+	su = ((UartData*)uart->regs)->su;
+	snprint(p, sizeof p, "b%d c%d e%d l%d m0 p%c s%d i1\n"
+		"dev(%d) type(%d) framing(%d) overruns(%d)\n",
+
+		uart->baud,
+		uart->hup_dcd, 
+		uart->hup_dsr,
+		Txsize,
+		(su->lcr & Lcr_pen)? ((su->lcr & Lcr_peven) ? 'e': 'o'): 'n',
+		(su->lcr & Lcr_stop)? 2: 1,
+
+		uart->dev,
+		uart->type,
+		uart->ferr,
+		uart->oerr);
+	n = readstr(offset, buf, n, p);
+	free(p);
+
+	return n;
+}
+
+static void
+sufifo(Uart*, int)
+{}
+
+static void
+sudtr(Uart*, int)
+{}
+
+static void
+surts(Uart*, int)
+{}
+
+static void
+sumodemctl(Uart*, int)
+{}
+
+static int
+suparity(Uart*uart, int parity)
+{
+	int lcr;
+	Saturnuart *su;
+
+	su = ((UartData*)uart->regs)->su;
+
+	lcr = su->lcr & ~(Lcr_pen|Lcr_peven);
+
+	switch(parity){
+	case 'e':
+		lcr |= (Lcr_pen|Lcr_peven);
+		break;
+	case 'o':
+		lcr |= Lcr_pen;
+		break;
+	case 'n':
+	default:
+		break;
+	}
+
+	su->lcr = lcr;
+	uart->parity = parity;
+
+	return 0;
+}
+
+static int
+sustop(Uart* uart, int stop)
+{
+	int lcr;
+	Saturnuart *su;
+
+	su = ((UartData*)uart->regs)->su;
+	lcr = su->lcr & ~Lcr_stop;
+
+	switch(stop){
+	case 1:
+		break;
+	case 2:
+		lcr |= Lcr_stop;
+		break;
+	default:
+		return -1;
+	}
+
+	/* Set new value and reenable if device was previously enabled */
+	su->lcr = lcr;
+	uart->stop = stop;
+
+	return 0;
+}
+
+static int
+subits(Uart*uart, int n)
+{	
+	Saturnuart *su;
+	uchar lcr;
+
+	su = ((UartData*)uart->regs)->su;
+	if(n<5||n>8)
+		return -1;
+
+	lcr = su->lcr & ~Lcr_wrdlenmask;
+	lcr |= (n-5) << Lcr_wrdlenshift;
+	su->lcr = lcr;
+	return 0;
+}
+
+static int
+subaud(Uart* uart, int baud)
+{
+	ushort v;
+	Saturnuart *su;
+
+	if (uart->enabled){
+		su = ((UartData*)uart->regs)->su;
+	
+		if(baud <= 0)
+			return -1;
+
+		v = Baudfreq / baud;
+		su->lcr |= Lcr_div;
+		su->dll = v;
+		su->dlm = v >> 8;
+		su->lcr &= ~Lcr_div;
+	}
+	uart->baud = baud;
+
+	return 0;
+}
+
+static void
+subreak(Uart*, int)
+{}
+
+static void
+sukick(Uart *uart)
+{
+	Saturnuart *su;
+	int i;
+
+	if(uart->blocked)
+		return;
+
+	su = ((UartData*)uart->regs)->su;
+	if((su->iir & Iir_txempty) == 0)
+		return;
+
+	for(i = 0; i < Txsize; i++){
+		if(uart->op >= uart->oe && uartstageoutput(uart) == 0)
+			break;
+		su->txb = *(uart->op++);
+		su->ier |= Ier_txempty;
+		break;
+	}
+}
+
+static void
+suputc(Uart *uart, int c)
+{
+	Saturnuart *su;
+
+	su = ((UartData*)uart->regs)->su;
+	while((su->lsr&Lsr_tbre) == 0)
+		;
+
+	su->txb=c;
+	while((su->lsr&Lsr_tbre) == 0)
+			;
+}
+
+static int
+getchars(Uart *uart, uchar *cbuf)
+{
+	int nc;
+	UartData *ud;
+	Saturnuart *su;
+
+	ud = uart->regs;
+	su = ud->su;
+
+	while((su->lsr&Lsr_rxavail) == 0)
+		;
+
+	*cbuf++ = su->rxb;
+	nc = 1;
+	while(su->lsr&Lsr_rxavail){
+		*cbuf++ = su->rxb;
+		nc++;
+	}
+	return nc;
+}
+
+static int
+sugetc(Uart *uart)
+{
+	static uchar buf[128], *p;
+	static int cnt;
+	char	c;
+
+	if (cnt <= 0) {
+		cnt = getchars(uart, buf);
+		p = buf;
+	}
+	c = *p++;
+	cnt--;
+	return c;
+}
+
+static void
+suinterrupt(Ureg*, void*u)
+{
+	Saturnuart *su;
+	Uart *uart;
+	uchar iir;
+
+	uart = u;
+	if (uart == nil)
+		panic("uart is nil");
+	su = ((UartData*)uart->regs)->su;
+	iir = su->iir;
+	if(iir&Iir_rxfull)
+		while(su->lsr&Lsr_rxavail)
+			uartrecv(uart, su->rxb);
+	if(iir & Iir_txempty){
+		su->ier&=~Ier_txempty;
+		uartkick(uart);
+	}
+	if (iir & Iir_rxerr)
+		uart->oerr++;
+	intack();
+}
+
+static void
+sudisable(Uart* uart)
+{
+	Saturnuart *su;
+
+	su = ((UartData*)uart->regs)->su;
+	su->ier&=~(Ier_txempty|Ier_rxfull);
+}
+
+PhysUart saturnphysuart = {
+	.name		= "su",
+	.pnp			= supnp,
+	.enable		= suenable,
+	.disable		= sudisable,
+	.kick			= sukick,
+	.dobreak		= subreak,
+	.baud		= subaud,
+	.bits			= subits,
+	.stop			= sustop,
+	.parity		= suparity,
+	.modemctl	= sumodemctl,
+	.rts			= surts,
+	.dtr			= sudtr,
+	.status		= sustatus,
+	.fifo			= sufifo,
+	.getc			= sugetc,
+	.putc			= suputc,
+};
+
+void
+console(void)
+{
+	Uart *uart;
+	int n;
+	char *cmd, *p;
+
+	if((p = getconf("console")) == nil)
+		return;
+	n = strtoul(p, &cmd, 0);
+	if(p == cmd)
+		return;
+	if(n < 0 || n >= nelem(suart))
+		return;
+
+	uart = suart + n;
+
+/*	uartctl(uart, "b115200 l8 pn s1"); */
+	if(*cmd != '\0')
+		uartctl(uart, cmd);
+	(*uart->phys->enable)(uart, 0);
+
+	consuart = uart;
+	uart->console = 1;
+} 
+
+Saturnuart*uart = (Saturnuart*)UartAoffs;
+
+void
+dbgputc(int c)
+{
+	while((uart->lsr&Lsr_tbre) == 0)
+		;
+
+	uart->txb=c;
+	while((uart->lsr&Lsr_tbre) == 0)
+			;
+}
+
+void
+dbgputs(char*s)
+{
+	while(*s)
+		dbgputc(*s++);
+}
+
+void
+dbgputx(ulong x)
+{
+	int i;
+	char c;
+
+	for(i=0; i < sizeof(ulong) * 2; i++){
+		c = ((x >> (28 - i * 4))) & 0xf;
+		if(c >= 0 && c <= 9)
+			c += '0';
+		else
+			c += 'a' - 10;
+
+		while((uart->lsr&Lsr_tbre) == 0)
+			;
+
+		uart->txb=c;
+	}
+	while((uart->lsr&Lsr_tbre) == 0)
+			;
+
+	uart->txb='\n';
+	while((uart->lsr&Lsr_tbre) == 0)
+			;
+}

+ 679 - 0
sys/src/9/ppc/uartsmc.c

@@ -0,0 +1,679 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "m8260.h"
+#include "../port/error.h"
+
+/*
+ * PowerPC 8260 SMC UART
+ */
+
+enum {
+	Nuart	= 1,		/* Number of SMC Uarts */
+
+	/* SMC Mode Registers */
+	Clen		= 0x7800,	/* Character length */
+	Sl		= 0x0400,	/* Stop length, 0: one stop bit, 1: two */
+	Pen		= 0x0200,	/* Parity enable */
+	Pm		= 0x0100,	/* Parity mode, 0 is odd */
+	Sm		= 0x0030,	/* SMC mode, two bits */
+	SMUart	= 0x0020,	/* SMC mode, 0b10 is uart */
+	Dm		= 0x000c,	/* Diagnostic mode, 00 is normal */
+	Ten		= 0x0002,	/* Transmit enable, 1 is enabled */
+	Ren		= 0x0001,	/* Receive enable, 1 is enabled */
+
+	/* SMC Event/Mask Registers */
+	ce_Brke	= 0x0040,	/* Break end */
+	ce_Br	= 0x0020,	/* Break character received */
+	ce_Bsy	= 0x0004,	/* Busy condition */
+	ce_Txb	= 0x0002,	/* Tx buffer */
+	ce_Rxb	= 0x0001,	/* Rx buffer */
+
+	/* Receive/Transmit Buffer Descriptor Control bits */
+	BDContin=	1<<9,
+	BDIdle=		1<<8,
+	BDPreamble=	1<<8,
+	BDBreak=		1<<5,
+	BDFrame=	1<<4,
+	BDParity=		1<<3,
+	BDOverrun=	1<<1,
+
+	/* Tx and Rx buffer sizes (32 bytes) */
+	Rxsize=		CACHELINESZ,
+	Txsize=		CACHELINESZ,
+};
+
+extern PhysUart smcphysuart;
+
+Uart smcuart[Nuart] = {
+	{
+		.name = "SMC1",
+		.baud = 115200,
+		.bits = 8,
+		.stop = 1,
+		.parity = 'n',
+		.phys = &smcphysuart,
+		.special = 0,
+	},
+/*	Only configure SMC1 for now
+	{
+		.name = "SMC2",
+		.baud = 115200,
+		.bits = 8,
+		.stop = 1,
+		.parity = 'n',
+		.phys = &smcphysuart,
+		.special = 0,
+	},
+*/
+};
+
+typedef struct UartData UartData;
+struct UartData
+{
+	int		smcno;	/* smc number: 0 or 1 */
+	SMC		*smc;
+	Uartsmc	*usmc;
+	char		*rxbuf;
+	char		*txbuf;
+	BD*		rxb;
+	BD*		txb;
+	int		initialized;
+	int		enabled;
+} uartdata[Nuart];
+
+int uartinited = 0;
+
+static void smcinterrupt(Ureg*, void*);
+static void smcputc(Uart *uart, int c);
+
+static int
+baudgen(int baud)
+{
+	int d;
+
+	d = ((m->brghz+(baud>>1))/baud)>>4;
+	if(d >= (1<<12))
+		return ((d+15)>>3)|1;
+	return d<<1;
+}
+
+static Uart*
+smcpnp(void)
+{
+	int i;
+
+	for (i = 0; i < nelem(smcuart) - 1; i++)
+		smcuart[i].next = smcuart + i + 1;
+	return smcuart;
+}
+
+static void
+smcinit(Uart *uart)
+{
+	Uartsmc *p;
+	SMC *smc;
+	UartData *ud;
+	ulong lcr;
+	int bits;
+
+	ud = uart->regs;
+
+	if (ud->initialized)
+		return;
+
+	/* magic addresses */
+	p = m->imap->uartsmc + ud->smcno;
+	smc = iomem->smc + ud->smcno;	/* SMC1 */
+	ud->smc = smc;
+	ud->usmc = p;
+
+	/* setup my uart structure */
+	if (ud->rxb == nil)
+		ud->rxb = bdalloc(1);
+	if (ud->txb == nil)
+		ud->txb = bdalloc(1);
+
+	/* step 0: disable rx/tx */
+	smc->smcmr &= ~3;
+
+	/* step 1, Using Port D */
+	if (ud->smcno == 0){
+		iomem->port[SMC1PORT].ppar |= SMRXD1|SMTXD1;
+		iomem->port[SMC1PORT].pdir |= SMTXD1;
+		iomem->port[SMC1PORT].pdir &= ~SMRXD1;
+		iomem->port[SMC1PORT].psor &= ~(SMRXD1|SMTXD1);
+	}else{
+		panic("Don't know how to set Port D bits");
+	}
+	/* step 2: set up brgc1 */
+	iomem->brgc[ud->smcno]  = baudgen(uart->baud) | 0x10000;
+
+	/* step 3: route clock to SMC1 */
+	iomem->cmxsmr &= (ud->smcno == 0) ? ~0xb0 : ~0xb;	/* clear smcx and smcxcs */
+
+	iopunlock();
+
+	/* step 4: assign a pointer to the SMCparameter RAM */
+	m->imap->param[ud->smcno].smcbase = (ulong)p - INTMEM;
+
+	/* step 5: set up buffer descriptors */
+	p->rbase = ((ulong)ud->rxb) - (ulong)INTMEM;
+	p->tbase = ((ulong)ud->txb) - (ulong)INTMEM;
+
+	/* step 6: issue command to CP */
+	if (ud->smcno == 0)
+		cpmop(InitRxTx, SMC1ID, 0);
+	else
+		cpmop(InitRxTx, SMC2ID, 0);
+
+	/* step 7: protocol parameters */
+	p->rfcr = 0x30;
+	p->tfcr = 0x30;
+
+	/* step 8: receive buffer size */
+	p->mrblr = Rxsize;
+
+	/* step 9: */
+	p->maxidl = 15;
+
+	/* step 10: */
+	p->brkln = 0;
+	p->brkec = 0;
+
+	/* step 11: */
+	p->brkcr = 0;
+
+	/* step 12: setup receive buffer */
+	ud->rxb->status = BDEmpty|BDWrap|BDInt;
+	ud->rxb->length = 0;
+	ud->rxbuf = xspanalloc(Rxsize, 0, CACHELINESZ);
+	ud->rxb->addr = PADDR(ud->rxbuf);
+
+	/* step 13: step transmit buffer */
+	ud->txb->status = BDWrap|BDInt;
+	ud->txb->length = 0;
+	ud->txbuf = xspanalloc(Txsize, 0, CACHELINESZ);
+	ud->txb->addr = PADDR(ud->txbuf);
+
+	/* step 14: clear events */
+	smc->smce = ce_Brke | ce_Br | ce_Bsy | ce_Txb | ce_Rxb;
+
+	/* 
+	 * step 15: enable interrupts (done later)
+	 * smc->smcm = ce_Brke | ce_Br | ce_Bsy | ce_Txb | ce_Rxb;
+
+	 * intrenable(4 + ud->smcno, smcinterrupt, up, 0, uart->name);
+	 */
+
+	/* step 17: set parity, no of bits, UART mode, ... */
+	lcr = SMUart;
+	bits = uart->bits + 1;
+
+	switch(uart->parity){
+	case 'e':
+		lcr |= (Pen|Pm);
+		bits +=1;
+		break;
+	case 'o':
+		lcr |= Pen;
+		bits +=1;
+		break;
+	case 'n':
+	default:
+		break;
+	}
+
+	if(uart->stop == 2){
+		lcr |= Sl;
+		bits += 1;
+	}
+
+	/* Set new value and reenable if device was previously enabled */
+	smc->smcmr = lcr |  bits <<11 | 0x3;
+
+	ud->initialized = 1;
+}
+
+static void
+smcenable(Uart *uart, int intenb)
+{
+	UartData *ud;
+	SMC *smc;
+	int nr;
+
+	nr = uart - smcuart;
+	if (nr < 0 || nr > Nuart)
+		panic("No SMC %d", nr);
+	ud = uartdata + nr;
+	ud->smcno = nr;
+	uart->regs = ud;
+	if (ud->initialized == 0)
+		smcinit(uart);
+	if (ud->enabled || intenb == 0)
+		return;
+	smc = ud->smc;
+	/* clear events */
+	smc->smce = ce_Brke | ce_Br | ce_Bsy | ce_Txb | ce_Rxb;
+	/* enable interrupts */
+	smc->smcm = ce_Brke | ce_Br | ce_Bsy | ce_Txb | ce_Rxb;
+	intrenable(4 + ud->smcno, smcinterrupt, uart, uart->name);
+	ud->enabled = 1;
+}
+
+static long
+smcstatus(Uart* uart, void* buf, long n, long offset)
+{
+	SMC *sp;
+	char p[128];
+
+	sp = ((UartData*)uart->regs)->smc;
+	snprint(p, sizeof p, "b%d c%d e%d l%d m0 p%c s%d i1\n"
+		"dev(%d) type(%d) framing(%d) overruns(%d)\n",
+
+		uart->baud,
+		uart->hup_dcd, 
+		uart->hup_dsr,
+		((sp->smcmr & Clen) >>11) - ((sp->smcmr&Pen) ? 1 : 0) - ((sp->smcmr&Sl) ? 2 : 1),
+		(sp->smcmr & Pen) ? ((sp->smcmr & Pm) ? 'e': 'o'): 'n',
+		(sp->smcmr & Sl) ? 2: 1,
+
+		uart->dev,
+		uart->type,
+		uart->ferr,
+		uart->oerr 
+	);
+	n = readstr(offset, buf, n, p);
+	free(p);
+
+	return n;
+}
+
+static void
+smcfifo(Uart*, int)
+{
+	/*
+	 * Toggle FIFOs:
+	 * if none, do nothing;
+	 * reset the Rx and Tx FIFOs;
+	 * empty the Rx buffer and clear any interrupt conditions;
+	 * if enabling, try to turn them on.
+	 */
+	return;
+}
+
+static void
+smcdtr(Uart*, int)
+{
+}
+
+static void
+smcrts(Uart*, int)
+{
+}
+
+static void
+smcmodemctl(Uart*, int)
+{
+}
+
+static int
+smcparity(Uart* uart, int parity)
+{
+	int lcr;
+	SMC *sp;
+
+	sp = ((UartData*)uart->regs)->smc;
+
+	lcr = sp->smcmr & ~(Pen|Pm);
+
+	/* Disable transmitter/receiver. */
+	sp->smcmr &= ~(Ren | Ten);
+
+	switch(parity){
+	case 'e':
+		lcr |= (Pen|Pm);
+		break;
+	case 'o':
+		lcr |= Pen;
+		break;
+	case 'n':
+	default:
+		break;
+	}
+	/* Set new value and reenable if device was previously enabled */
+	sp->smcmr = lcr;
+
+	uart->parity = parity;
+
+	return 0;
+}
+
+static int
+smcstop(Uart* uart, int stop)
+{
+	int lcr, bits;
+	SMC *sp;
+
+	sp = ((UartData*)uart->regs)->smc;
+	lcr = sp->smcmr & ~(Sl | Clen);
+
+	/* Disable transmitter/receiver. */
+	sp->smcmr &= ~(Ren | Ten);
+
+	switch(stop){
+	case 1:
+		break;
+	case 2:
+		lcr |= Sl;
+		break;
+	default:
+		return -1;
+	}
+
+	bits = uart->bits + ((lcr & Pen) ? 1 : 0) + ((lcr & Sl) ? 2 : 1);
+	lcr |= bits<<11;
+
+	/* Set new value and reenable if device was previously enabled */
+	sp->smcmr = lcr;
+
+	uart->stop = stop;
+
+	return 0;
+}
+
+static int
+smcbits(Uart* uart, int bits)
+{
+	int lcr, b;
+	SMC *sp;
+
+	if (bits < 5 || bits > 14)
+		return -1;
+
+	sp = ((UartData*)uart->regs)->smc;
+	lcr = sp->smcmr & ~Clen;
+
+	b = bits + ((sp->smcmr & Pen) ? 1 : 0) + ((sp->smcmr & Sl) ? 2 : 1);
+
+	if (b > 15)
+		return -1;
+
+	/* Disable transmitter/receiver */
+	sp->smcmr &= ~(Ren | Ten);
+
+	/* Set new value and reenable if device was previously enabled */
+	sp->smcmr = lcr |  b<<11;
+
+	uart->bits = bits;
+
+	return 0;
+}
+
+static int
+smcbaud(Uart* uart, int baud)
+{
+	int i;
+	SMC *sp;
+
+	if (uart->enabled){
+		sp = ((UartData*)uart->regs)->smc;
+	
+		if(uart->freq == 0 || baud <= 0)
+			return -1;
+	
+		i = sp - iomem->smc;
+		iomem->brgc[i] = (((m->brghz >> 4) / baud) << 1) | 0x00010000;
+	}
+	uart->baud = baud;
+
+	return 0;
+}
+
+static void
+smcbreak(Uart*, int)
+{
+}
+
+static void
+smckick(Uart *uart)
+{
+	BD *txb;
+	UartData *ud;
+	int i;
+
+	if(uart->blocked)
+		return;
+
+	ud = uart->regs;
+	txb = ud->txb;
+
+	if (txb->status & BDReady)
+		return;	/* Still busy */
+
+	for(i = 0; i < Txsize; i++){
+		if(uart->op >= uart->oe && uartstageoutput(uart) == 0)
+			break;
+		ud->txbuf[i] = *(uart->op++);
+	}
+	if (i == 0)
+		return;
+	dcflush(ud->txbuf, Txsize);
+	txb->length = i;
+	sync();
+	txb->status |= BDReady|BDInt;
+}
+
+static void
+smcinterrupt(Ureg*, void* u)
+{
+	int i, nc;
+	char *buf;
+	BD *rxb;
+	UartData *ud;
+	Uart *uart;
+	uchar events;
+
+	uart = u;
+	if (uart == nil)
+		panic("uart is nil");
+	ud = uart->regs;
+	if (ud == nil)
+		panic("ud is nil");
+
+	events = ud->smc->smce;
+	ud->smc->smce = events;	/* Clear events */
+
+	if (events & 0x10)
+		iprint("smc%d: break\n", ud->smcno);
+	if (events & 0x4)
+		uart->oerr++;
+	if (events & 0x1){
+		/* Receive characters
+		*/
+		rxb = ud->rxb;
+		buf = ud->rxbuf;
+		dczap(buf, Rxsize);	/* invalidate data cache before copying */
+		if ((rxb->status & BDEmpty) == 0){
+			nc = rxb->length;
+			for (i=0; i<nc; i++)
+				uartrecv(uart, *buf++);
+			sync();
+			rxb->status |= BDEmpty;
+		}else{
+			iprint("uartsmc: unexpected receive event\n");
+		}
+	}
+	if (events & 0x2){
+		if ((ud->txb->status & BDReady) == 0)
+			uartkick(uart);
+	}
+}
+
+static void
+smcdisable(Uart* uart)
+{
+	SMC *sp;
+
+	sp = ((UartData*)uart->regs)->smc;
+	sp->smcmr &= ~(Ren | Ten);
+}
+
+static int
+getchars(Uart *uart, uchar *cbuf)
+{
+	int i, nc;
+	char *buf;
+	BD *rxb;
+	UartData *ud;
+
+	ud = uart->regs;
+	rxb = ud->rxb;
+
+	/* Wait for character to show up.
+	*/
+	buf = ud->rxbuf;
+	while (rxb->status & BDEmpty)
+		;
+	nc = rxb->length;
+	for (i=0; i<nc; i++)
+		*cbuf++ = *buf++;
+	sync();
+	rxb->status |= BDEmpty;
+
+	return(nc);
+}
+
+static int
+smcgetc(Uart *uart)
+{
+	static uchar buf[128], *p;
+	static int cnt;
+	char	c;
+
+	if (cnt <= 0) {
+		cnt = getchars(uart, buf);
+		p = buf;
+	}
+	c = *p++;
+	cnt--;
+
+	return(c);
+}
+
+static void
+smcputc(Uart *uart, int c)
+{
+	BD *txb;
+	UartData *ud;
+	SMC *smc;
+
+	ud = uart->regs;
+	txb = ud->txb;
+	smc = ud->smc;
+	smc->smcm = 0;
+
+	/* Wait for last character to go.
+	*/
+	while (txb->status & BDReady)
+		;
+
+	ud->txbuf[0] = c;
+	dcflush(ud->txbuf, 1);
+	txb->length = 1;
+	sync();
+	txb->status |= BDReady;
+
+	while (txb->status & BDReady)
+		;
+}
+
+PhysUart smcphysuart = {
+	.name		= "smc",
+	.pnp			= smcpnp,
+	.enable		= smcenable,
+	.disable		= smcdisable,
+	.kick			= smckick,
+	.dobreak		= smcbreak,
+	.baud		= smcbaud,
+	.bits			= smcbits,
+	.stop			= smcstop,
+	.parity		= smcparity,
+	.modemctl	= smcmodemctl,
+	.rts			= smcrts,
+	.dtr			= smcdtr,
+	.status		= smcstatus,
+	.fifo			= smcfifo,
+	.getc			= smcgetc,
+	.putc			= smcputc,
+};
+
+void
+console(void)
+{
+	Uart *uart;
+	int n;
+	char *cmd, *p;
+
+	if((p = getconf("console")) == nil)
+		return;
+	n = strtoul(p, &cmd, 0);
+	if(p == cmd)
+		return;
+	if(n < 0 || n >= nelem(smcuart))
+		return;
+	uart = smcuart + n;
+
+/*	uartctl(uart, "b115200 l8 pn s1"); */
+	if(*cmd != '\0')
+		uartctl(uart, cmd);
+	(*uart->phys->enable)(uart, 0);
+
+	consuart = uart;
+	uart->console = 1;
+} 
+
+void
+dbgputc(int c)
+{
+	Uartsmc *su;
+	BD *tbdf;
+	Imap *imap;
+	char *addr;
+	uchar smcm;
+	SMC *smc;
+	IMM *io;
+
+	/* Should work as long as Imap is mapped at 0xf0000000 (INTMEM) */
+
+	imap = (Imap*)INTMEM;
+	su = (Uartsmc *)(INTMEM | imap->param[0].smcbase);
+	tbdf = (BD *)(INTMEM | su->tbase);
+
+	io = (IMM*)IOMEM;
+	smc = io->smc;
+	smcm = smc->smcm;	/* save interrupt state */
+	smc->smcm = 0;		/* turn interrupts off */
+
+	/* Wait for last character to go.
+	*/
+	while (tbdf->status & BDReady)
+		;
+
+	addr = KADDR(tbdf->addr);
+	*addr = c;
+	tbdf->length = 1;
+	sync();
+	tbdf->status |= BDReady;
+
+	while (tbdf->status & BDReady)
+		;
+
+	smc->smce = ce_Txb;	/* clear xmit events */
+	smc->smcm = smcm;	/* restore interrupts */
+
+	delay(300);
+}

+ 61 - 0
sys/src/9/ppc/ucu

@@ -0,0 +1,61 @@
+dev
+	root
+	cons
+	env
+	pipe
+	proc
+	mnt
+	srv
+	dup
+	ssl
+	cap
+	kprof
+	uart
+	realtime	realtimesub edf
+
+	ether		netif
+	ip		arp chandial ip ipv6 ipaux iproute netif netlog nullmedium pktmedium ptclbsum inferno
+
+link
+	ethersaturn
+	ethermedium
+	netdevmedium
+
+misc
+	uartsaturn
+	saturntimer
+	msaturn
+
+ip
+	il
+	tcp
+	udp
+	ipifc
+	icmp
+	icmp6
+
+port
+	int cpuserver = 1;
+
+boot boot #S/sdC0/
+	il
+	local
+
+bootdir
+	/power/bin/rc
+	/sys/src/9/power/rcmain
+	/power/bin/bind
+	/power/bin/sed
+	/power/bin/srv
+	/power/bin/cat
+	/power/bin/cp
+	/power/bin/rm
+	/power/bin/echo
+	/power/bin/mount
+	/power/bin/sleep
+	/power/bin/auth/factotum
+	/power/bin/ip/ipconfig
+	/power/bin/ls
+	/power/bin/ps
+	/power/bin/auth/wrkey
+	/sys/lib/sysconfig/blast/boot

+ 21 - 0
sys/src/9/ppc/ucu.h

@@ -0,0 +1,21 @@
+
+#define	FLASHMEM	(~0)
+#define	MEM1BASE	0
+#define	MEM1SIZE	0x02000000
+#define	MEM2BASE	0
+#define	MEM2SIZE	0
+#define	PLAN9INI		(~0)
+
+#define Saturn 0xf0000000
+
+#define	TLBENTRIES	128
+
+/*
+ *  PTE bits for fault.c.  These belong to the second PTE word.  Validity is
+ *  implied for putmmu(), and we always set PTE0_V.  PTEVALID is used
+ *  here to set cache policy bits on a global basis.
+ */
+#define	PTEVALID		(PTE1_M|PTE1_W)	/* write through */
+#define	PTEWRITE		(PTE1_RW|PTE1_C)
+#define	PTERONLY	PTE1_RO
+#define	PTEUNCACHED	PTE1_I