فهرست منبع

Plan 9 from Bell Labs 2003-07-30

David du Colombier 21 سال پیش
والد
کامیت
37ad0dea75
46فایلهای تغییر یافته به همراه12142 افزوده شده و 5 حذف شده
  1. 46 4
      dist/replica/plan9.db
  2. 125 0
      dist/replica/plan9.log
  3. 22 0
      rc/bin/sig
  4. 1 1
      rc/bin/vwhois
  5. 32 0
      sys/lib/sysconfig/blast/boot
  6. 1 0
      sys/log/mail
  7. 8 0
      sys/man/1/man
  8. 5 0
      sys/man/2/draw
  9. 62 0
      sys/src/9/ppc/blast
  10. 83 0
      sys/src/9/ppc/blast.c
  11. 99 0
      sys/src/9/ppc/blast.h
  12. 85 0
      sys/src/9/ppc/clock.c
  13. 227 0
      sys/src/9/ppc/dat.h
  14. 468 0
      sys/src/9/ppc/devether.c
  15. 963 0
      sys/src/9/ppc/devflash.c
  16. 342 0
      sys/src/9/ppc/devirq.c
  17. 2113 0
      sys/src/9/ppc/devtls.c
  18. 51 0
      sys/src/9/ppc/errstr.h
  19. 627 0
      sys/src/9/ppc/etherfcc.c
  20. 35 0
      sys/src/9/ppc/etherif.h
  21. 229 0
      sys/src/9/ppc/ethersaturn.c
  22. 163 0
      sys/src/9/ppc/fns.h
  23. 29 0
      sys/src/9/ppc/init9.s
  24. 25 0
      sys/src/9/ppc/initcode
  25. 30 0
      sys/src/9/ppc/io.h
  26. 841 0
      sys/src/9/ppc/l.s
  27. 66 0
      sys/src/9/ppc/lblast.h
  28. 39 0
      sys/src/9/ppc/lucu.h
  29. 638 0
      sys/src/9/ppc/m8260.c
  30. 657 0
      sys/src/9/ppc/m8260.h
  31. 506 0
      sys/src/9/ppc/main.c
  32. 351 0
      sys/src/9/ppc/mcc.c
  33. 230 0
      sys/src/9/ppc/mem.h
  34. 108 0
      sys/src/9/ppc/mkfile
  35. 259 0
      sys/src/9/ppc/mmu.c
  36. 188 0
      sys/src/9/ppc/msaturn.c
  37. 7 0
      sys/src/9/ppc/msaturn.h
  38. 11 0
      sys/src/9/ppc/mtx.c
  39. 138 0
      sys/src/9/ppc/random.c
  40. 34 0
      sys/src/9/ppc/rcmain
  41. 94 0
      sys/src/9/ppc/saturntimer.c
  42. 871 0
      sys/src/9/ppc/trap.c
  43. 472 0
      sys/src/9/ppc/uartsaturn.c
  44. 679 0
      sys/src/9/ppc/uartsmc.c
  45. 61 0
      sys/src/9/ppc/ucu
  46. 21 0
      sys/src/9/ppc/ucu.h

+ 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/tcp9 - 775 sys sys 945617224 26
 rc/bin/service/telcodata - 775 sys sys 945617224 95
 rc/bin/service/telcodata - 775 sys sys 945617224 95
 rc/bin/service/telcofax - 775 sys sys 945617224 93
 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/slay - 775 sys sys 1018387014 123
 rc/bin/spell - 775 sys sys 964540791 312
 rc/bin/spell - 775 sys sys 964540791 312
 rc/bin/src - 775 sys sys 1015089596 869
 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/ups - 775 sys sys 1051028686 889
 rc/bin/usbstart - 775 sys sys 1044894155 81
 rc/bin/usbstart - 775 sys sys 1044894155 81
 rc/bin/usps - 775 sys sys 1016826030 450
 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/wdoc2txt - 755 sys sys 1017431153 277
 rc/bin/weather - 775 sys sys 1016825765 795
 rc/bin/weather - 775 sys sys 1016825765 795
 rc/bin/who - 775 sys sys 945617210 61
 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/tcp566 - 775 sys sys 1016833537 36
 sys/lib/sysconfig/auth/files/tcp567 - 775 sys sys 1016833537 34
 sys/lib/sysconfig/auth/files/tcp567 - 775 sys sys 1016833537 34
 sys/lib/sysconfig/auth/mkfile - 664 sys sys 1016833657 2937
 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 - 20000000775 sys sys 1042004836 0
 sys/lib/sysconfig/fl/boot - 775 sys sys 1048637071 773
 sys/lib/sysconfig/fl/boot - 775 sys sys 1048637071 773
 sys/lib/sysconfig/fl/flproto - 664 sys sys 1048637070 137
 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/imap4d - 10000000666 sys sys 958934039 0
 sys/log/ipboot - 10000000666 sys sys 958934040 0
 sys/log/ipboot - 10000000666 sys sys 958934040 0
 sys/log/listen - 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/nfs - 10000000666 sys sys 958934039 0
 sys/log/nfsserver - 10000000666 sys sys 958934040 0
 sys/log/nfsserver - 10000000666 sys sys 958934040 0
 sys/log/pop3 - 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/lp - 664 sys sys 1015024739 3304
 sys/man/1/ls - 664 sys sys 1049813576 2791
 sys/man/1/ls - 664 sys sys 1049813576 2791
 sys/man/1/mail - 664 sys sys 1050078464 2158
 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/marshal - 664 sys sys 1050078464 3307
 sys/man/1/mc - 664 sys sys 1045501392 508
 sys/man/1/mc - 664 sys sys 1045501392 508
 sys/man/1/mk - 664 sys sys 964455061 13154
 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/dial - 664 sys sys 1015701029 6523
 sys/man/2/dirread - 664 sys sys 1015091519 1901
 sys/man/2/dirread - 664 sys sys 1015091519 1901
 sys/man/2/disk - 664 sys sys 1015091519 3188
 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/dsa - 664 sys sys 1027629169 2072
 sys/man/2/dup - 664 sys sys 950593489 827
 sys/man/2/dup - 664 sys sys 950593489 827
 sys/man/2/elgamal - 664 sys sys 1027629189 2152
 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/tod.c - 664 sys sys 1036813006 3328
 sys/src/9/port/unthwack.c - 664 sys sys 1057323394 5249
 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/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/NOTICE - 444 sys sys 1018803112 63
 sys/src/ape - 20000000775 sys sys 1014921996 0
 sys/src/ape - 20000000775 sys sys 1014921996 0
 sys/src/ape/9src - 20000000775 sys sys 1016944136 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 7 a acme/bin/dial - 20000000775 sys sys 1059180057 0
 1059181299 8 a rc/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
 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){
 if(test -f /mnt/plumb/seemail || test -f /mnt/term/mnt/plumb/seemail){
 	for(i)
 	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){
 if not for (i){
 	echo vwhois: vwhois: vwhois: delivered `{cat /dev/user} From $i '(vwho)' >> /sys/log/mail
 	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
 .PP
 .B lookman
 .B lookman
 .I key ...
 .I key ...
+.PP
+.B sig
+.I function ...
 .SH DESCRIPTION
 .SH DESCRIPTION
 .I Man
 .I Man
 locates and prints pages of this manual named
 locates and prints pages of this manual named
@@ -63,6 +66,11 @@ prints the names of all manual sections that contain
 all of the
 all of the
 .I key
 .I key
 words given on the command line.
 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
 .SH FILES
 .TF /sys/lib/man/lookman/index
 .TF /sys/lib/man/lookman/index
 .TP
 .TP

+ 5 - 0
sys/man/2/draw

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