summaryrefslogtreecommitdiff
path: root/sys/src/9/ppc
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/9/ppc
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/9/ppc')
-rwxr-xr-xsys/src/9/ppc/blast59
-rwxr-xr-xsys/src/9/ppc/blast.h99
-rwxr-xr-xsys/src/9/ppc/clock.c88
-rwxr-xr-xsys/src/9/ppc/dat.h231
-rwxr-xr-xsys/src/9/ppc/devether.c470
-rwxr-xr-xsys/src/9/ppc/devflash.c963
-rwxr-xr-xsys/src/9/ppc/devirq.c355
-rwxr-xr-xsys/src/9/ppc/devtls.c2113
-rwxr-xr-xsys/src/9/ppc/etherfcc.c891
-rwxr-xr-xsys/src/9/ppc/etherif.h35
-rwxr-xr-xsys/src/9/ppc/ethersaturn.c229
-rwxr-xr-xsys/src/9/ppc/fns.h124
-rwxr-xr-xsys/src/9/ppc/init9.s29
-rwxr-xr-xsys/src/9/ppc/initcode25
-rwxr-xr-xsys/src/9/ppc/io.h30
-rwxr-xr-xsys/src/9/ppc/l.s1037
-rwxr-xr-xsys/src/9/ppc/lblast.h66
-rwxr-xr-xsys/src/9/ppc/lucu.h39
-rwxr-xr-xsys/src/9/ppc/m8260.c661
-rwxr-xr-xsys/src/9/ppc/m8260.h658
-rwxr-xr-xsys/src/9/ppc/main.c511
-rwxr-xr-xsys/src/9/ppc/mcc.c351
-rwxr-xr-xsys/src/9/ppc/mem.h229
-rwxr-xr-xsys/src/9/ppc/mkfile108
-rwxr-xr-xsys/src/9/ppc/mmu.c282
-rwxr-xr-xsys/src/9/ppc/msaturn.c190
-rwxr-xr-xsys/src/9/ppc/msaturn.h7
-rwxr-xr-xsys/src/9/ppc/mtx.c11
-rwxr-xr-xsys/src/9/ppc/random.c138
-rwxr-xr-xsys/src/9/ppc/saturntimer.c95
-rwxr-xr-xsys/src/9/ppc/trap.c857
-rwxr-xr-xsys/src/9/ppc/uartsaturn.c472
-rwxr-xr-xsys/src/9/ppc/uartsmc.c585
-rwxr-xr-xsys/src/9/ppc/ucu59
-rwxr-xr-xsys/src/9/ppc/ucu.h21
35 files changed, 12118 insertions, 0 deletions
diff --git a/sys/src/9/ppc/blast b/sys/src/9/ppc/blast
new file mode 100755
index 000000000..5581d7a40
--- /dev/null
+++ b/sys/src/9/ppc/blast
@@ -0,0 +1,59 @@
+dev
+ root
+ cons
+ env
+ flash
+ pipe
+ proc
+ mnt
+ srv
+ dup
+ ssl
+ cap
+ kprof
+ uart
+ irq
+
+ ether netif
+ ip arp chandial ip ipv6 ipaux iproute netif netlog nullmedium pktmedium ptclbsum inferno
+
+link
+ etherfcc ethermii
+ ethermedium
+ netdevmedium
+ loopbackmedium
+
+misc
+ uartsmc
+ m8260
+
+ip
+ tcp
+ udp
+ ipifc
+ icmp
+ icmp6
+
+port
+ int cpuserver = 1;
+
+boot cpu
+ tcp
+
+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/ip/ipconfig
+ /power/bin/auth/factotum
+ /power/bin/ls
+ /power/bin/auth/wrkey
+ /sys/lib/sysconfig/blast/boot
diff --git a/sys/src/9/ppc/blast.h b/sys/src/9/ppc/blast.h
new file mode 100755
index 000000000..4b4904842
--- /dev/null
+++ b/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)
diff --git a/sys/src/9/ppc/clock.c b/sys/src/9/ppc/clock.c
new file mode 100755
index 000000000..30d350a2d
--- /dev/null
+++ b/sys/src/9/ppc/clock.c
@@ -0,0 +1,88 @@
+#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 */
+ m->loopconst = 1000;
+ 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 >> 1)){
+ if(v > clkreload)
+ m->ticks += v/clkreload;
+ v = 0;
+ }
+ putdec(clkreload-v);
+}
+
+void
+delay(int l)
+{
+ ulong i, j;
+
+ j = 0;
+ if(m)
+ j = m->loopconst;
+ if(j == 0)
+ j = 1096;
+ 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);
+}
diff --git a/sys/src/9/ppc/dat.h b/sys/src/9/ppc/dat.h
new file mode 100755
index 000000000..f92367764
--- /dev/null
+++ b/sys/src/9/ppc/dat.h
@@ -0,0 +1,231 @@
+typedef struct Conf Conf;
+typedef struct FPsave FPsave;
+typedef struct ISAConf ISAConf;
+typedef struct Imap Imap;
+typedef struct Label Label;
+typedef struct Lock Lock;
+typedef struct Mach Mach;
+typedef struct Notsave Notsave;
+typedef struct PCArch PCArch;
+typedef struct PMMU PMMU;
+typedef struct Page Page;
+typedef struct Pcidev Pcidev;
+typedef struct Proc Proc;
+typedef struct Sys Sys;
+typedef struct Ureg Ureg;
+typedef struct Vctl Vctl;
+
+#pragma incomplete Ureg
+#pragma incomplete Imap
+
+#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 */
+};
+
+/*
+ * 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;
+
+ 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 */
+
+ Proc* readied; /* for runproc */
+ ulong schedticks; /* next forced context switch */
+
+ ulong clkin; /* basic clock frequency */
+ ulong vco_out;
+ vlong cpuhz;
+ uvlong cyclefreq; /* Frequency of user readable cycle counter */
+ 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;
+
+extern FPsave initfp;
diff --git a/sys/src/9/ppc/devether.c b/sys/src/9/ppc/devether.c
new file mode 100755
index 000000000..6d58e70dd
--- /dev/null
+++ b/sys/src/9/ppc/devether.c
@@ -0,0 +1,470 @@
+#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];
+extern uchar etheraddr[];
+
+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 multicast 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;
+ memmove(ether->ea, etheraddr, 6);
+ 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,
+};
diff --git a/sys/src/9/ppc/devflash.c b/sys/src/9/ppc/devflash.c
new file mode 100755
index 000000000..b6fe365c0
--- /dev/null
+++ b/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 long
+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");
+}
diff --git a/sys/src/9/ppc/devirq.c b/sys/src/9/ppc/devirq.c
new file mode 100755
index 000000000..dab8ceac2
--- /dev/null
+++ b/sys/src/9/ppc/devirq.c
@@ -0,0 +1,355 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.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 */
+ ulong sleepints; /* interrupt count when waiting */
+ 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*);
+void dumpvno(void);
+
+static void
+ticmstimer(Ureg*, Timer *t)
+{
+ Irqconfig *ic;
+
+ ic = t->ta;
+ 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->tnext == nil)
+ ic->tns = MS2NS(ic->mode);
+ ic->tmode = Tperiodic;
+ 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->tf = ticmstimer;
+ ic->ta = 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 int
+irqtfn(void *arg)
+{
+ Irqconfig *ic;
+
+ ic = arg;
+ return ic->sleepints != ic->interrupts;
+}
+
+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");
+ ic->sleepints = ic->interrupts;
+ sleep(&ic->r, irqtfn, ic);
+ 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);
+ iomem->siprr = 0x65009770;
+ 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->tns = MS2NS(1000);
+ ic->mode = 1000;
+ error(Ebadarg);
+ }
+ ic->tns = MS2NS(ic->mode);
+ }else if (strcmp(cb->f[1], "level") == 0){
+ ic->mode = Level;
+ iomem->siexr &= ~(0x8000 >> irq);
+ }else if (strcmp(cb->f[1], "edge") == 0){
+ ic->mode = Edge;
+ iomem->siexr |= 0x8000 >> irq;
+ }else
+ error(Ebadarg);
+ break;
+ case CMreset:
+ ic->interrupts = 0;
+ break;
+ case CMwait:
+ if (ic->intenable == 0)
+ error("interrupts are off");
+ ic->sleepints = ic->interrupts;
+ sleep(&ic->r, irqtfn, ic);
+ break;
+ case CMdebug:
+ print("simr h/l 0x%lux/0x%lux, sipnr h/l 0x%lux/0x%lux, siexr 0x%lux, siprr 0x%lux\n",
+ iomem->simr_h, iomem->simr_l,
+ iomem->sipnr_h, iomem->sipnr_l,
+ iomem->siexr, iomem->siprr);
+ dumpvno();
+ }
+ 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;
+ }
+ ilock(&irqlock);
+ if (irq <= Qirq7)
+ iomem->sipnr_h |= 0x8000 >> irq; /* Clear the interrupt */
+ 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,
+};
diff --git a/sys/src/9/ppc/devtls.c b/sys/src/9/ppc/devtls.c
new file mode 100755
index 000000000..d333b10b9
--- /dev/null
+++ b/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];
+}
diff --git a/sys/src/9/ppc/etherfcc.c b/sys/src/9/ppc/etherfcc.c
new file mode 100755
index 000000000..0b2e1f21e
--- /dev/null
+++ b/sys/src/9/ppc/etherfcc.c
@@ -0,0 +1,891 @@
+/*
+ * FCCn ethernet
+ */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "imm.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+
+#include "etherif.h"
+#include "../ppc/ethermii.h"
+
+#define DBG 1
+
+enum {
+ Nrdre = 128, /* receive descriptor ring entries */
+ Ntdre = 128, /* transmit descriptor ring entries */
+
+ Rbsize = ETHERMAXTU+4, /* ring buffer size (+4 for CRC) */
+ Bufsize = Rbsize+CACHELINESZ, /* extra room for alignment */
+};
+
+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 */
+ CRCE= BIT(24), /* Ethernet CRC */
+ FCE= BIT(10), /* flow control */
+ PRO= BIT(9), /* promiscuous mode */
+ FDE= BIT(5), /* full duplex ethernet */
+ LPB= BIT(3), /* local protect bit */
+
+ /* 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),
+};
+
+enum { /* Mcr */
+ MDIread = 0x60020000, /* read opcode */
+ MDIwrite = 0x50020000, /* write opcode */
+};
+
+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 Ctlr Ctlr;
+struct Ctlr {
+ Lock;
+ int fccid;
+ int port;
+ ulong pmdio;
+ ulong pmdck;
+ int init;
+ int active;
+ int duplex; /* 1 == full */
+ FCC* fcc;
+
+ Ring;
+ Block* rcvbufs[Nrdre];
+ Mii* mii;
+ Timer;
+
+ ulong interrupts; /* statistics */
+ ulong deferred;
+ ulong heartbeat;
+ ulong latecoll;
+ ulong retrylim;
+ ulong underrun;
+ ulong overrun;
+ ulong carrierlost;
+ ulong retrycount;
+};
+
+static int fccirq[] = {0x20, 0x21, 0x22};
+static int fccid[] = {FCC1ID, FCC2ID, FCC3ID};
+
+#ifdef DBG
+ulong fccrhisto[16];
+ulong fccthisto[16];
+ulong fccrthisto[16];
+ulong fcctrhisto[16];
+ulong ehisto[0x80];
+#endif
+
+static int fccmiimir(Mii*, int, int);
+static int fccmiimiw(Mii*, int, int, int);
+static void fccltimer(Ureg*, Timer*);
+
+static void
+attach(Ether *ether)
+{
+ Ctlr *ctlr;
+
+ ctlr = ether->ctlr;
+ ilock(ctlr);
+ ctlr->active = 1;
+ ctlr->fcc->gfmr |= ENR|ENT;
+ iunlock(ctlr);
+ ctlr->tmode = Tperiodic;
+ ctlr->tf = fccltimer;
+ ctlr->ta = ether;
+ ctlr->tns = 5000000000LL; /* 5 seconds */
+ timeradd(ctlr);
+}
+
+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; Don't do this according to errata */
+ 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, rcvd, xmtd, restart;
+ ushort events;
+ Ctlr *ctlr;
+ BD *dre;
+ Block *b, *nb;
+ 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;
+ ctlr->fcc->fcce = events; /* clear events */
+
+#ifdef DBG
+ ehisto[events & 0x7f]++;
+#endif
+
+ ctlr->interrupts++;
+
+ if(events & BSY)
+ ctlr->overrun++;
+ if(events & TXE)
+ ether->oerrs++;
+
+#ifdef DBG
+ rcvd = xmtd = 0;
+#endif
+ /*
+ * 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){
+ dre = &ctlr->rdr[ctlr->rdrx];
+ dczap(dre, sizeof(BD));
+ while(((status = dre->status) & BDEmpty) == 0){
+ rcvd++;
+ 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;
+ b = ctlr->rcvbufs[ctlr->rdrx];
+ assert(dre->addr == PADDR(b->rp));
+ dczap(b->rp, len);
+ if(nb = iallocb(Bufsize)){
+ b->wp += len;
+ etheriq(ether, b, 1);
+ b = nb;
+ b->rp = (uchar*)(((ulong)b->rp + CACHELINESZ-1) & ~(CACHELINESZ-1));
+ b->wp = b->rp;
+ ctlr->rcvbufs[ctlr->rdrx] = b;
+ ctlr->rdr[ctlr->rdrx].addr = PADDR(b->wp);
+ }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)){
+ ilock(ctlr);
+ restart = 0;
+ 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++;
+ if(status & (TxeLC|TxeRL|TxeUN))
+ restart = 1;
+ 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);
+ xmtd++;
+ }
+
+ if(restart){
+ ctlr->fcc->gfmr &= ~ENT;
+ delay(10);
+ ctlr->fcc->gfmr |= ENT;
+ cpmop(RestartTx, ctlr->fccid, 0xc);
+ }
+ txstart(ether);
+ iunlock(ctlr);
+ }
+#ifdef DBG
+ if(rcvd >= nelem(fccrhisto))
+ rcvd = nelem(fccrhisto) - 1;
+ if(xmtd >= nelem(fccthisto))
+ xmtd = nelem(fccthisto) - 1;
+ if(rcvd)
+ fcctrhisto[xmtd]++;
+ else
+ fccthisto[xmtd]++;
+ if(xmtd)
+ fccrthisto[rcvd]++;
+ else
+ fccrhisto[rcvd]++;
+#endif
+}
+
+static long
+ifstat(Ether* ether, void* a, long n, ulong offset)
+{
+ char *p;
+ int len, i, r;
+ Ctlr *ctlr;
+ MiiPhy *phy;
+
+ 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);
+ len += snprint(p+len, READSTR-len, "framesdeferred: %lud\n", ctlr->deferred);
+ miistatus(ctlr->mii);
+ phy = ctlr->mii->curphy;
+ len += snprint(p+len, READSTR-len, "phy: link=%d, tfc=%d, rfc=%d, speed=%d, fd=%d\n",
+ phy->link, phy->tfc, phy->rfc, phy->speed, phy->fd);
+
+#ifdef DBG
+ if(ctlr->mii != nil && ctlr->mii->curphy != nil){
+ len += snprint(p+len, READSTR, "phy: ");
+ for(i = 0; i < NMiiPhyr; i++){
+ if(i && ((i & 0x07) == 0))
+ len += snprint(p+len, READSTR-len, "\n ");
+ r = miimir(ctlr->mii, i);
+ len += snprint(p+len, READSTR-len, " %4.4uX", r);
+ }
+ snprint(p+len, READSTR-len, "\n");
+ }
+#endif
+ snprint(p+len, READSTR-len, "\n");
+
+ n = readstr(offset, a, n, p);
+ free(p);
+
+ return n;
+}
+
+/*
+ * This follows the MPC8260 user guide: section28.9's initialisation sequence.
+ */
+static int
+fccsetup(Ctlr *ctlr, FCC *fcc, uchar *ea)
+{
+ int i;
+ Etherparam *p;
+ MiiPhy *phy;
+
+ /* Turn Ethernet off */
+ fcc->gfmr &= ~(ENR | ENT);
+
+ ioplock();
+ switch(ctlr->port) {
+ default:
+ iopunlock();
+ return -1;
+ case 0:
+ /* Step 1 (Section 28.9), write the parallel ports */
+ ctlr->pmdio = 0x01000000;
+ ctlr->pmdck = 0x08000000;
+ imm->port[0].pdir &= ~A1dir0;
+ imm->port[0].pdir |= A1dir1;
+ imm->port[0].psor &= ~A1psor0;
+ imm->port[0].psor |= A1psor1;
+ imm->port[0].ppar |= (A1dir0 | A1dir1);
+ /* Step 2, Port C clocks */
+ imm->port[2].psor &= ~0x00000c00;
+ imm->port[2].pdir &= ~0x00000c00;
+ imm->port[2].ppar |= 0x00000c00;
+ imm->port[3].pdat |= (ctlr->pmdio | ctlr->pmdck);
+ imm->port[3].podr |= ctlr->pmdio;
+ imm->port[3].pdir |= (ctlr->pmdio | ctlr->pmdck);
+ imm->port[3].ppar &= ~(ctlr->pmdio | ctlr->pmdck);
+ eieio();
+ /* Step 3, Serial Interface clock routing */
+ imm->cmxfcr &= ~0xff000000; /* Clock mask */
+ imm->cmxfcr |= 0x37000000; /* Clock route */
+ break;
+
+ case 1:
+ /* Step 1 (Section 28.9), write the parallel ports */
+ ctlr->pmdio = 0x00400000;
+ ctlr->pmdck = 0x00200000;
+ imm->port[1].pdir &= ~B2dir0;
+ imm->port[1].pdir |= B2dir1;
+ imm->port[1].psor &= ~B2psor0;
+ imm->port[1].psor |= B2psor1;
+ imm->port[1].ppar |= (B2dir0 | B2dir1);
+ /* Step 2, Port C clocks */
+ imm->port[2].psor &= ~0x00003000;
+ imm->port[2].pdir &= ~0x00003000;
+ imm->port[2].ppar |= 0x00003000;
+
+ imm->port[2].pdat |= (ctlr->pmdio | ctlr->pmdck);
+ imm->port[2].podr |= ctlr->pmdio;
+ imm->port[2].pdir |= (ctlr->pmdio | ctlr->pmdck);
+ imm->port[2].ppar &= ~(ctlr->pmdio | ctlr->pmdck);
+ eieio();
+ /* Step 3, Serial Interface clock routing */
+ imm->cmxfcr &= ~0x00ff0000;
+ imm->cmxfcr |= 0x00250000;
+ break;
+
+ case 2:
+ /* Step 1 (Section 28.9), write the parallel ports */
+ imm->port[1].pdir &= ~B3dir0;
+ imm->port[1].pdir |= B3dir1;
+ imm->port[1].psor &= ~B3psor0;
+ imm->port[1].psor |= B3psor1;
+ imm->port[1].ppar |= (B3dir0 | B3dir1);
+ /* Step 2, Port C clocks */
+ imm->port[2].psor &= ~0x0000c000;
+ imm->port[2].pdir &= ~0x0000c000;
+ imm->port[2].ppar |= 0x0000c000;
+ imm->port[3].pdat |= (ctlr->pmdio | ctlr->pmdck);
+ imm->port[3].podr |= ctlr->pmdio;
+ imm->port[3].pdir |= (ctlr->pmdio | ctlr->pmdck);
+ imm->port[3].ppar &= ~(ctlr->pmdio | ctlr->pmdck);
+ eieio();
+ /* Step 3, Serial Interface clock routing */
+ imm->cmxfcr &= ~0x0000ff00;
+ imm->cmxfcr |= 0x00003700;
+ break;
+ }
+ iopunlock();
+
+ p = (Etherparam*)(m->immr->prmfcc + ctlr->port);
+ memset(p, 0, sizeof(Etherparam));
+
+ /* Step 4 */
+ fcc->gfmr |= ENET;
+
+ /* Step 5 */
+ fcc->fpsmr = CRCE | FDE | LPB; /* full duplex operation */
+ ctlr->duplex = ~0;
+
+ /* 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 = (Rbsize+0x1f)&~0x1f; /* multiple of 32 */
+ p->mflr = Rbsize;
+ p->minflr = ETHERMINTU;
+ p->maxd1 = (Rbsize+7) & ~7;
+ p->maxd2 = (Rbsize+7) & ~7;
+
+ 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->immr->fccextra[ctlr->port].ri - (uchar*)IMMR;
+ p->tiptr = m->immr->fccextra[ctlr->port].ti - (uchar*)IMMR;
+ p->padptr = m->immr->fccextra[ctlr->port].pad - (uchar*)IMMR;
+ memset(m->immr->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, Link management */
+ if((ctlr->mii = malloc(sizeof(Mii))) == nil)
+ return -1;
+ ctlr->mii->mir = fccmiimir;
+ ctlr->mii->miw = fccmiimiw;
+ ctlr->mii->ctlr = ctlr;
+
+ if(mii(ctlr->mii, ~0) == 0 || (phy = ctlr->mii->curphy) == nil){
+ free(ctlr->mii);
+ ctlr->mii = nil;
+ return -1;
+ }
+ miiane(ctlr->mii, ~0, ~0, ~0);
+#ifdef DBG
+ print("oui=%X, phyno=%d, ", phy->oui, phy->phyno);
+ print("anar=%ux, ", phy->anar);
+ print("fc=%ux, ", phy->fc);
+ print("mscr=%ux, ", phy->mscr);
+
+ print("link=%ux, ", phy->link);
+ print("speed=%ux, ", phy->speed);
+ print("fd=%ux, ", phy->fd);
+ print("rfc=%ux, ", phy->rfc);
+ print("tfc=%ux\n", phy->tfc);
+#endif
+ /* Step 15, Enable ethernet: done at attach time */
+ return 0;
+}
+
+static int
+reset(Ether* ether)
+{
+ uchar ea[Eaddrlen];
+ Ctlr *ctlr;
+ FCC *fcc;
+ Block *b;
+ int i;
+
+ if(m->cpuhz < 24000000){
+ print("%s ether: system speed must be >= 24MHz for ether use\n", ether->type);
+ return -1;
+ }
+
+ if(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 = imm->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, 0) < 0)
+ panic("etherfcc init");
+ for(i = 0; i < Nrdre; i++){
+ b = iallocb(Bufsize);
+ b->rp = (uchar*)(((ulong)b->rp + CACHELINESZ-1) & ~(CACHELINESZ-1));
+ b->wp = b->rp;
+ ctlr->rcvbufs[i] = b;
+ ctlr->rdr[i].addr = PADDR(b->wp);
+ }
+
+ 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);
+}
+
+static void
+nanodelay(void)
+{
+ static int count;
+ int i;
+
+ for(i = 0; i < 500; i++)
+ count++;
+ return;
+}
+
+static
+void miiwriteloop(Ctlr *ctlr, Port *port, int cnt, ulong cmd)
+{
+ int i;
+
+ for(i = 0; i < cnt; i++){
+ port->pdat &= ~ctlr->pmdck;
+ if(cmd & BIT(i))
+ port->pdat |= ctlr->pmdio;
+ else
+ port->pdat &= ~ctlr->pmdio;
+ nanodelay();
+ port->pdat |= ctlr->pmdck;
+ nanodelay();
+ }
+}
+
+static int
+fccmiimiw(Mii *mii, int pa, int ra, int data)
+{
+ int x;
+ Port *port;
+ ulong cmd;
+ Ctlr *ctlr;
+
+ /*
+ * MII Management Interface Write.
+ */
+
+ ctlr = mii->ctlr;
+ port = imm->port + 3;
+ cmd = MDIwrite | (pa<<(5+2+16))| (ra<<(2+16)) | (data & 0xffff);
+
+ x = splhi();
+
+ port->pdir |= (ctlr->pmdio|ctlr->pmdck);
+ nanodelay();
+
+ miiwriteloop(ctlr, port, 32, ~0);
+ miiwriteloop(ctlr, port, 32, cmd);
+
+ port->pdir |= (ctlr->pmdio|ctlr->pmdck);
+ nanodelay();
+
+ miiwriteloop(ctlr, port, 32, ~0);
+
+ splx(x);
+ return 1;
+}
+
+static int
+fccmiimir(Mii *mii, int pa, int ra)
+{
+ int data, i, x;
+ Port *port;
+ ulong cmd;
+ Ctlr *ctlr;
+
+ ctlr = mii->ctlr;
+ port = imm->port + 3;
+
+ cmd = MDIread | pa<<(5+2+16) | ra<<(2+16);
+
+ x = splhi();
+ port->pdir |= (ctlr->pmdio|ctlr->pmdck);
+ nanodelay();
+
+ miiwriteloop(ctlr, port, 32, ~0);
+
+ /* Clock out the first 14 MS bits of the command */
+ miiwriteloop(ctlr, port, 14, cmd);
+
+ /* Turn-around */
+ port->pdat &= ~ctlr->pmdck;
+ port->pdir &= ~ctlr->pmdio;
+ nanodelay();
+
+ /* For read, clock in 18 bits, use 16 */
+ data = 0;
+ for(i=0; i<18; i++){
+ data <<= 1;
+ if(port->pdat & ctlr->pmdio)
+ data |= 1;
+ port->pdat |= ctlr->pmdck;
+ nanodelay();
+ port->pdat &= ~ctlr->pmdck;
+ nanodelay();
+ }
+ port->pdir |= (ctlr->pmdio|ctlr->pmdck);
+ nanodelay();
+ miiwriteloop(ctlr, port, 32, ~0);
+ splx(x);
+ return data & 0xffff;
+}
+
+static void
+fccltimer(Ureg*, Timer *t)
+{
+ Ether *ether;
+ Ctlr *ctlr;
+ MiiPhy *phy;
+ ulong gfmr;
+
+ ether = t->ta;
+ ctlr = ether->ctlr;
+ if(ctlr->mii == nil || ctlr->mii->curphy == nil)
+ return;
+ phy = ctlr->mii->curphy;
+ if(miistatus(ctlr->mii) < 0){
+ print("miistatus failed\n");
+ return;
+ }
+ if(phy->link == 0){
+ print("link lost\n");
+ return;
+ }
+ ether->mbps = phy->speed;
+
+ if(phy->fd != ctlr->duplex)
+ print("set duplex\n");
+ ilock(ctlr);
+ gfmr = ctlr->fcc->gfmr;
+ if(phy->fd != ctlr->duplex){
+ ctlr->fcc->gfmr &= ~(ENR|ENT);
+ if(phy->fd)
+ ctlr->fcc->fpsmr |= FDE | LPB; /* full duplex operation */
+ else
+ ctlr->fcc->fpsmr &= ~(FDE | LPB); /* half duplex operation */
+ ctlr->duplex = phy->fd;
+ }
+ ctlr->fcc->gfmr = gfmr;
+ iunlock(ctlr);
+}
diff --git a/sys/src/9/ppc/etherif.h b/sys/src/9/ppc/etherif.h
new file mode 100755
index 000000000..34fa2cc02
--- /dev/null
+++ b/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))
diff --git a/sys/src/9/ppc/ethersaturn.c b/sys/src/9/ppc/ethersaturn.c
new file mode 100755
index 000000000..893835f87
--- /dev/null
+++ b/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);
+}
+
diff --git a/sys/src/9/ppc/fns.h b/sys/src/9/ppc/fns.h
new file mode 100755
index 000000000..56b6aceab
--- /dev/null
+++ b/sys/src/9/ppc/fns.h
@@ -0,0 +1,124 @@
+#include "../port/portfns.h"
+
+int cistrcmp(char*, char*);
+int cistrncmp(char*, char*, int);
+void clockinit(void);
+void clockintr(Ureg*);
+void cpuidprint(void);
+void cycles(uvlong*);
+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 dumpregs(Ureg*);
+void eieio(void);
+void evenaddr(ulong);
+void faultpower(Ureg*, ulong addr, int read);
+void flashprogpower(int);
+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 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 getmsr(void);
+ulong getpvr(void);
+ulong getrpa(void);
+ulong getsdr1(void);
+ulong getsr(int);
+ulong getsrr1(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);
+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);
+int iprint(char*, ...);
+int isaconfig(char*, int, ISAConf*);
+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 mmuinit(void);
+void mmusweep(void*);
+int newmmupid(void);
+void outb(int, 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 procsave(Proc*);
+void procsetup(Proc*);
+void putdcmp(ulong);
+void putdec(ulong);
+void puthash1(ulong);
+void puthash2(ulong);
+void puthid0(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 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);
+#define userureg(ur) (((ur)->status & MSR_PR) != 0)
+#define waserror() (up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1]))
+#define KADDR(a) ((void*)((ulong)(a)|KZERO))
+#define PADDR(a) ((((ulong)(a)&0xf0000000)==0xf0000000)?(ulong)(a):((ulong)(a)&~KZERO))
+#define coherence() eieio()
+Pcidev* pcimatch(Pcidev*, int, int);
+Pcidev* pcimatchtbdf(int);
+void procrestore(Proc*);
+
+#ifdef ucuconf
+extern ulong getpll(void);
+extern ulong getl2cr(void);
+extern void putl2cr(ulong);
+#endif
diff --git a/sys/src/9/ppc/init9.s b/sys/src/9/ppc/init9.s
new file mode 100755
index 000000000..3445f26f8
--- /dev/null
+++ b/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
diff --git a/sys/src/9/ppc/initcode b/sys/src/9/ppc/initcode
new file mode 100755
index 000000000..1d846abe5
--- /dev/null
+++ b/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
diff --git a/sys/src/9/ppc/io.h b/sys/src/9/ppc/io.h
new file mode 100755
index 000000000..c14051b2e
--- /dev/null
+++ b/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)
diff --git a/sys/src/9/ppc/l.s b/sys/src/9/ppc/l.s
new file mode 100755
index 000000000..878aa457d
--- /dev/null
+++ b/sys/src/9/ppc/l.s
@@ -0,0 +1,1037 @@
+#include "mem.h"
+
+/* use of SPRG registers in save/restore */
+#define SAVER0 SPRG0
+#define SAVER1 SPRG1
+#define SAVELR SPRG2
+#define SAVEXX SPRG3
+
+#ifdef ucuconf
+/* These only exist on the PPC 755: */
+#define SAVER4 SPRG4
+#define SAVER5 SPRG5
+#define SAVER6 SPRG6
+#define SAVER7 SPRG7
+#endif /* ucuconf */
+
+/* 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_ME|MSR_EE|MSR_IP), R4
+ ANDN R4, 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
+
+ /* before this we're not running above KZERO */
+ BL mmuinit0(SB)
+ /* after this we are */
+
+#ifdef ucuconf
+ MOVW $0x2000000, R4 /* size */
+ MOVW $0, R3 /* base address */
+ 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: DCBF (R5)
+ ADD $CACHELINESZ, R5
+ BDNZ _dcf0
+_dcf1:
+ SYNC
+
+ /* BAT0, 3 unused, copy of BAT2 */
+ MOVW SPR(IBATL(2)), R3
+ MOVW R3, SPR(IBATL(0))
+ MOVW SPR(IBATU(2)), R3
+ MOVW R3, SPR(IBATU(0))
+ MOVW SPR(DBATL(2)), R3
+ MOVW R3, SPR(DBATL(0))
+ MOVW SPR(DBATU(2)), R3
+ MOVW R3, SPR(DBATU(0))
+
+ MOVW SPR(IBATL(2)), R3
+ MOVW R3, SPR(IBATL(3))
+ MOVW SPR(IBATU(2)), R3
+ MOVW R3, SPR(IBATU(3))
+ MOVW SPR(DBATL(2)), R3
+ MOVW R3, SPR(DBATL(3))
+ MOVW SPR(DBATU(2)), R3
+ MOVW R3, SPR(DBATU(3))
+#endif /* ucuconf */
+
+ /* 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 */
+
+/*
+ * 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
+
+#ifndef ucuconf
+ /* 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))
+#else /* ucuconf */
+ /* BAT 2 covers memory from 0x00000000 to 0x10000000 */
+
+ /* 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))
+#endif /* ucuconf */
+
+ /* 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
+
+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 _xinc(SB), $0 /* void _xinc(long *); */
+ MOVW R3, R4
+xincloop:
+ DCBF (R4) /* fix for 603x bug */
+ LWAR (R4), R3
+ ADD $1, R3
+ STWCCC R3, (R4)
+ BNE xincloop
+ RETURN
+
+TEXT _xdec(SB), $0 /* long _xdec(long *); */
+ MOVW R3, R4
+xdecloop:
+ DCBF (R4) /* fix for 603x bug */
+ LWAR (R4), R3
+ ADD $-1, R3
+ STWCCC R3, (R4)
+ BNE xdecloop
+ 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, 0x14(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 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
+
+#ifdef ucuconf
+TEXT getpll(SB), $0
+ MOVW SPR(1009), R3
+ ISYNC
+ 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
+#endif /* ucuconf */
+
+TEXT mmudisable(SB), $0
+ /* disable MMU */
+ MOVW LR, R4
+ MOVW $KZERO, R5
+ ANDN R5, R4
+ MOVW R4, SPR(SRR0) /* Stored PC for RFI instruction */
+
+ MOVW MSR, R4
+ MOVW $(MSR_IR|MSR_DR|MSR_RI|MSR_FP), R5
+ ANDN R5, R4
+ MOVW R4, SPR(SRR1)
+
+ MOVW SPR(HID0), R4 /* Get HID0 and clear unwanted bits */
+ MOVW $(HID_ICE|HID_DCE), R5
+ ANDN R5, R4
+ MOVW R4, SPR(HID0) /* Cache disable */
+ RFI /* resume caller with MMU off */
+ RETURN
+
+TEXT kreboot(SB), $0
+ BL mmudisable(SB)
+ MOVW R3, LR
+ RETURN
+
+TEXT mul64fract(SB), $0
+ MOVW a0+8(FP), R9
+ MOVW a1+4(FP), R10
+ MOVW b0+16(FP), R4
+ MOVW b1+12(FP), R5
+
+ MULLW R10, R5, R13 /* c2 = lo(a1*b1) */
+
+ MULLW R10, R4, R12 /* c1 = lo(a1*b0) */
+ MULHWU R10, R4, R7 /* hi(a1*b0) */
+ ADD R7, R13 /* c2 += hi(a1*b0) */
+
+ MULLW R9, R5, R6 /* lo(a0*b1) */
+ MULHWU R9, R5, R7 /* hi(a0*b1) */
+ ADDC R6, R12 /* c1 += lo(a0*b1) */
+ ADDE R7, R13 /* c2 += hi(a0*b1) + carry */
+
+ MULHWU R9, R4, R7 /* hi(a0*b0) */
+ ADDC R7, R12 /* c1 += hi(a0*b0) */
+ ADDE R0, R13 /* c2 += carry */
+
+ MOVW R12, 4(R3)
+ MOVW R13, 0(R3)
+ RETURN
diff --git a/sys/src/9/ppc/lblast.h b/sys/src/9/ppc/lblast.h
new file mode 100755
index 000000000..2b3bf4833
--- /dev/null
+++ b/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
diff --git a/sys/src/9/ppc/lucu.h b/sys/src/9/ppc/lucu.h
new file mode 100755
index 000000000..e8b040e7e
--- /dev/null
+++ b/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
diff --git a/sys/src/9/ppc/m8260.c b/sys/src/9/ppc/m8260.c
new file mode 100755
index 000000000..4f84ec135
--- /dev/null
+++ b/sys/src/9/ppc/m8260.c
@@ -0,0 +1,661 @@
+/*
+ * 8260 specific stuff:
+ * Interrupt handling
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "io.h"
+#include "fns.h"
+#include "m8260.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 = (IMM*)IOMEM;
+
+static Lock cpmlock;
+
+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;
+
+ 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));
+
+ /* Flashed CS configuration is wrong for DSP2. It's set to 64 bits, should be 16 */
+ 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 = 0xff000830; /* Was 0xff000816 */
+
+/*
+ * 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;
+ break;
+ }
+
+ m->cyclefreq = m->bushz / 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=00601d051dd8\n"
+ "flash0=mem=0xfe000000\n"
+ "fs=135.104.9.42\n"
+ "auth=135.104.9.7\n"
+ "authdom=cs.bell-labs.com\n"
+ "sys=blast\n"
+ "ntp=135.104.9.52\n";
+ }
+}
+
+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 */
+
+ /* 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;
+ }
+ ioplock();
+ /* Clear the interrupt before enabling */
+ iomem->sipnr_h |= hi;
+ iomem->sipnr_l |= lo;
+ /* Enable */
+ iomem->simr_h |= hi;
+ iomem->simr_l |= lo;
+ iopunlock();
+ 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;
+ }
+ ioplock();
+ iomem->simr_h &= ~hi;
+ iomem->simr_l &= ~lo;
+ iopunlock();
+}
+
+int
+intvec(void)
+{
+ return iomem->sivec >> 26;
+}
+
+void
+intend(int vno)
+{
+ /* Clear interrupt */
+ ioplock();
+ iomem->sipnr_h |= vec2mask[vno].hi;
+ iomem->sipnr_l |= vec2mask[vno].lo;
+ iopunlock();
+}
+
+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->rdr == nil)
+ return -1;
+ if(r->rrb == nil && bufsize){
+ r->rrb = xspanalloc(nrdre*bufsize, 0, CACHELINESZ);
+ if(r->rrb == nil)
+ return -1;
+ }
+ x = bufsize ? PADDR(r->rrb) : 0;
+ 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
+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);
+}
+
+void
+reboot(void*, void*, ulong)
+{
+ ulong *p;
+ int x;
+
+ p = (ulong*)0x90000000;
+ x = splhi();
+ iomem->sypcr |= 0xc0;
+ print("iomem->sypcr = 0x%lux\n", iomem->sypcr);
+ *p = 0;
+ print("still alive\n");
+ splx(x);
+}
diff --git a/sys/src/9/ppc/m8260.h b/sys/src/9/ppc/m8260.h
new file mode 100755
index 000000000..9aa0ffb82
--- /dev/null
+++ b/sys/src/9/ppc/m8260.h
@@ -0,0 +1,658 @@
+
+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);
+void kreboot(ulong);
diff --git a/sys/src/9/ppc/main.c b/sys/src/9/ppc/main.c
new file mode 100755
index 000000000..380d6fd6f
--- /dev/null
+++ b/sys/src/9/ppc/main.c
@@ -0,0 +1,511 @@
+#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"
+#include "tos.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);
+
+char *
+cpuid(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;
+ }
+ return id;
+}
+
+void
+cpuidprint(void)
+{
+ print("cpu0: %s, rev 0x%lux, cpu hz %lld, bus hz %ld\n",
+ cpuid(), getpvr()&0xffff, m->cpuhz, m->bushz);
+}
+
+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();
+}
+
+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;
+
+ /*
+ * parse configuration args from dos file plan9.ini
+ */
+
+ 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;
+ }
+}
+
+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);
+ pathclose(up->slash->path);
+ up->slash->path = newpath("/");
+ 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-sizeof(Tos)));
+}
+
+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;
+}
+
+void
+procrestore(Proc *p)
+{
+ uvlong t;
+
+ if(p->kp)
+ return;
+ cycles(&t);
+ p->pcycles -= t;
+}
+
+/*
+ * Save the mach dependent part of the process state.
+ */
+void
+procsave(Proc *p)
+{
+ uvlong t;
+
+ cycles(&t);
+ p->pcycles += t;
+ 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.mem[0].npage = (MEM1SIZE - pa)/BY2PG;
+ conf.mem[0].base = pa;
+
+ conf.mem[1].npage = MEM2SIZE/BY2PG;
+ conf.mem[1].base = MEM2BASE;
+
+ conf.npage = conf.mem[0].npage + conf.mem[1].npage;
+
+ 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;
+}
diff --git a/sys/src/9/ppc/mcc.c b/sys/src/9/ppc/mcc.c
new file mode 100755
index 000000000..8d89f2d69
--- /dev/null
+++ b/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
+
diff --git a/sys/src/9/ppc/mem.h b/sys/src/9/ppc/mem.h
new file mode 100755
index 000000000..dcaa0ad68
--- /dev/null
+++ b/sys/src/9/ppc/mem.h
@@ -0,0 +1,229 @@
+/*
+ * Memory and machine-specific definitions. Used in C and assembler.
+ */
+
+#ifdef ucuconf
+#include "ucu.h"
+#else
+#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 1000 /* 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 TBRL 268
+#define TBRU 269 /* Time base Upper/Lower (Reading) */
+#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 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
diff --git a/sys/src/9/ppc/mkfile b/sys/src/9/ppc/mkfile
new file mode 100755
index 000000000..37b2486c9
--- /dev/null
+++ b/sys/src/9/ppc/mkfile
@@ -0,0 +1,108 @@
+CONF=ucu
+CONFLIST=blast 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\
+ edf.$O\
+ fault.$O\
+ latin1.$O\
+ log.$O\
+ rebootcmd.$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
diff --git a/sys/src/9/ppc/mmu.c b/sys/src/9/ppc/mmu.c
new file mode 100755
index 000000000..73155e448
--- /dev/null
+++ b/sys/src/9/ppc/mmu.c
@@ -0,0 +1,282 @@
+#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 reclamation 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;
+ }
+
+}
+
+void
+checkmmu(ulong, ulong)
+{
+}
+
+void
+countpagerefs(ulong*, int)
+{
+}
+
+/*
+ * Return the number of bytes that can be accessed via KADDR(pa).
+ * If pa is not a valid argument to KADDR, return 0.
+ */
+ulong
+cankaddr(ulong pa)
+{
+ if(pa >= -KZERO)
+ return 0;
+ return -KZERO - pa;
+}
+
diff --git a/sys/src/9/ppc/msaturn.c b/sys/src/9/ppc/msaturn.c
new file mode 100755
index 000000000..94aa1fb17
--- /dev/null
+++ b/sys/src/9/ppc/msaturn.c
@@ -0,0 +1,190 @@
+#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;
+
+ *(ulong*)Ier=0;
+
+ 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;
+ ulong l2cr;
+
+ 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;
+ }
+
+ if(getpll() == 0x80000000)
+ m->cpuhz = 300000000;
+ else
+ m->cpuhz = 200000000; /* 750FX? */
+ m->cyclefreq = m->bushz / 4;
+
+ active.machs = 1;
+ active.exiting = 0;
+
+ putmsr(getmsr() | MSR_ME);
+
+ dcflush((void*)KZERO, 0x2000000);
+ l2cr = getl2cr();
+ putl2cr(l2cr|BIT(10));
+
+ 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);
+}
+
+void
+reboot(void*, void*, ulong)
+{
+}
diff --git a/sys/src/9/ppc/msaturn.h b/sys/src/9/ppc/msaturn.h
new file mode 100755
index 000000000..0beab872b
--- /dev/null
+++ b/sys/src/9/ppc/msaturn.h
@@ -0,0 +1,7 @@
+enum {
+ Vecuart0 = 1,
+ Vecuart1 = Vecuart0 + 1,
+ Vectimer0 = 0,
+ Vecether = 3,
+ Vecunused = 15,
+};
diff --git a/sys/src/9/ppc/mtx.c b/sys/src/9/ppc/mtx.c
new file mode 100755
index 000000000..aaf8ebd7b
--- /dev/null
+++ b/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"
diff --git a/sys/src/9/ppc/random.c b/sys/src/9/ppc/random.c
new file mode 100755
index 000000000..3c204b8d1
--- /dev/null
+++ b/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;
+}
diff --git a/sys/src/9/ppc/saturntimer.c b/sys/src/9/ppc/saturntimer.c
new file mode 100755
index 000000000..2b9ef66cc
--- /dev/null
+++ b/sys/src/9/ppc/saturntimer.c
@@ -0,0 +1,95 @@
+#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 < 10000)
+ offset = 10000;
+ 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);
+}
+
diff --git a/sys/src/9/ppc/trap.c b/sys/src/9/ppc/trap.c
new file mode 100755
index 000000000..ceacce8a8
--- /dev/null
+++ b/sys/src/9/ppc/trap.c
@@ -0,0 +1,857 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "ureg.h"
+#include "../port/error.h"
+#include "tos.h"
+#include <trace.h>
+
+static Lock vctllock;
+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);
+
+ if(*pv == nil){
+ print("intrdisable: irq %d not found\n", irq);
+ iunlock(&vctllock);
+ return;
+ }
+
+ 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
+kexit(Ureg*)
+{
+ uvlong t;
+ Tos *tos;
+
+ /* precise time accounting, kernel exit */
+ tos = (Tos*)(USTKTOP-sizeof(Tos));
+ cycles(&t);
+ tos->kcycles += t - up->kentry;
+ tos->pcycles = up->pcycles;
+ tos->pid = up->pid;
+}
+
+void
+trap(Ureg *ureg)
+{
+ int ecode, user;
+ char buf[ERRMAX], *s;
+ extern FPsave initfp;
+
+ ecode = (ureg->cause >> 8) & 0xff;
+ user = (ureg->srr1 & MSR_PR) != 0;
+ if(user){
+ cycles(&up->kentry);
+ 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");
+ }
+ up->mmureg = ureg;
+ faultpower(ureg, ureg->dar, (ureg->dsisr & BIT(6)) == 0);
+ break;
+ case CISI:
+ m->pfault++;
+ if (up == nil){
+ dumpregs(ureg);
+ panic("kernel fault");
+ }
+ up->mmureg = ureg;
+ faultpower(ureg, ureg->pc, 1);
+ break;
+ case CIMISS: /* instruction miss */
+ if (up == nil){
+ dumpregs(ureg);
+ panic("kernel fault");
+ }
+ up->mmureg = ureg;
+ faultpower(ureg, ureg->imiss, 1);
+ break;
+ case CLMISS: /* data load miss */
+ if (up == nil){
+ dumpregs(ureg);
+ panic("kernel fault");
+ }
+ up->mmureg = ureg;
+ faultpower(ureg, ureg->dmiss, 1);
+ break;
+ case CSMISS: /* data store miss */
+ if (up == nil){
+ dumpregs(ureg);
+ panic("kernel fault");
+ }
+ up->mmureg = ureg;
+ faultpower(ureg, ureg->dmiss, 0);
+ break;
+
+ case CSYSCALL:
+ if(!user)
+ panic("syscall in kernel: srr1 0x%4.4luX\n", ureg->srr1);
+ syscall(ureg);
+ if (up->delaysched){
+ sched();
+ splhi();
+ }
+ kexit(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();
+
+ /* delaysched set because we held a lock or because our quantum ended */
+ if(up && up->delaysched && ecode == CDEC){
+ sched();
+ 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;
+ kexit(ureg);
+ }
+}
+
+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 */
+}
+
+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-sizeof(Tos); /* address of kernel/user shared data */
+}
+
+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;
+}
diff --git a/sys/src/9/ppc/uartsaturn.c b/sys/src/9/ppc/uartsaturn.c
new file mode 100755
index 000000000..51f91f94c
--- /dev/null
+++ b/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)
+ ;
+}
diff --git a/sys/src/9/ppc/uartsmc.c b/sys/src/9/ppc/uartsmc.c
new file mode 100755
index 000000000..fe48f53fe
--- /dev/null
+++ b/sys/src/9/ppc/uartsmc.c
@@ -0,0 +1,585 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "imm.h"
+#include "../port/error.h"
+#include "../ppc/uartsmc.h"
+
+/*
+ * PowerPC 8260 SMC UART
+ */
+
+enum {
+ /* 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,
+ },
+*/
+};
+
+int uartinited = 0;
+
+static void smcinterrupt(Ureg*, void*);
+static void smcputc(Uart *uart, int c);
+
+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;
+}
+
+void
+smcinit(Uart *uart)
+{
+ Uartsmc *p;
+ SMC *smc;
+ UartData *ud;
+ ulong lcr;
+ int bits;
+
+ ud = uart->regs;
+
+ if (ud->initialized)
+ return;
+
+ smcsetup(uart); /* Steps 1 through 4, PPC-dependent */
+ p = ud->usmc;
+ smc = ud->smc;
+
+ /* step 5: set up buffer descriptors */
+ /* setup my uart structure */
+ if (ud->rxb == nil)
+ ud->rxb = bdalloc(1);
+ if (ud->txb == nil)
+ ud->txb = bdalloc(1);
+
+ p->rbase = ((ulong)ud->rxb) - (ulong)IMMR;
+ p->tbase = ((ulong)ud->txb) - (ulong)IMMR;
+
+ /* 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;
+ */
+
+ /* 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(VecSMC1 + 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 - imm->smc;
+ imm->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;
+}
diff --git a/sys/src/9/ppc/ucu b/sys/src/9/ppc/ucu
new file mode 100755
index 000000000..56631f851
--- /dev/null
+++ b/sys/src/9/ppc/ucu
@@ -0,0 +1,59 @@
+dev
+ root
+ cons
+ env
+ pipe
+ proc
+ mnt
+ srv
+ dup
+ ssl
+ cap
+ kprof
+ uart
+
+ ether netif
+ ip arp chandial ip ipv6 ipaux iproute netif netlog nullmedium pktmedium ptclbsum inferno
+ ce
+
+link
+ ethersaturn
+ ethermedium
+ netdevmedium
+
+misc
+ uartsaturn
+ saturntimer
+ msaturn
+
+ip
+ tcp
+ udp
+ ipifc
+ icmp
+ icmp6
+
+port
+ int cpuserver = 1;
+
+boot cpu
+ tcp
+
+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
diff --git a/sys/src/9/ppc/ucu.h b/sys/src/9/ppc/ucu.h
new file mode 100755
index 000000000..91653bc42
--- /dev/null
+++ b/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