diff -Nru /sys/src/fs/9netics32.16k/9net32.16kfs.c /sys/src/fs/9netics32.16k/9net32.16kfs.c --- /sys/src/fs/9netics32.16k/9net32.16kfs.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/9netics32.16k/9net32.16kfs.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,178 @@ +#include "all.h" +#include "mem.h" +#include "io.h" +#include "ureg.h" + +#include "../pc/dosfs.h" + +/* + * setting this to zero permits the use of discs of different sizes, but + * can make jukeinit() quite slow while the robotics work through each disc + * twice (once per side). + */ +int FIXEDSIZE = 1; + +#ifndef DATE +#define DATE 1094098624L +#endif + +Timet mktime = DATE; /* set by mkfile */ +Startsb startsb[] = +{ + "main", 2, /* */ + 0 +}; + +Dos dos; + +static struct +{ + char *name; + Off (*read)(int, void*, long); + Devsize (*seek)(int, Devsize); + Off (*write)(int, void*, long); + int (*part)(int, char*); +} nvrdevs[] = { + { "fd", floppyread, floppyseek, floppywrite, 0, }, + { "hd", ataread, ataseek, atawrite, setatapart, }, + /* { "sd", scsiread, scsiseek, scsiwrite, setscsipart, }, */ + { 0, }, +}; + +void apcinit(void); + +void +otherinit(void) +{ + int dev, i, nfd, nhd, s; + char *p, *q, buf[sizeof(nvrfile)+8]; + + kbdinit(); + printcpufreq(); + etherinit(); + scsiinit(); + apcinit(); + + s = spllo(); + nhd = atainit(); + nfd = floppyinit(); + dev = 0; + if(p = getconf("nvr")){ + strncpy(buf, p, sizeof(buf)-2); + buf[sizeof(buf)-1] = 0; + p = strchr(buf, '!'); + q = strrchr(buf, '!'); + if(p == 0 || q == 0 || strchr(p+1, '!') != q) + panic("malformed nvrfile: %s\n", buf); + *p++ = 0; + *q++ = 0; + dev = strtoul(p, 0, 0); + strcpy(nvrfile, q); + p = buf; + } else + if(p = getconf("bootfile")){ + strncpy(buf, p, sizeof(buf)-2); + buf[sizeof(buf)-1] = 0; + p = strchr(buf, '!'); + q = strrchr(buf, '!'); + if(p == 0 || q == 0 || strchr(p+1, '!') != q) + panic("malformed bootfile: %s\n", buf); + *p++ = 0; + *q = 0; + dev = strtoul(p, 0, 0); + p = buf; + } else + if(nfd) + p = "fd"; + else + if(nhd) + p = "hd"; + else + p = "sd"; + + for(i = 0; nvrdevs[i].name; i++){ + if(strcmp(p, nvrdevs[i].name) == 0){ + dos.dev = dev; + if(nvrdevs[i].part && (*nvrdevs[i].part)(dos.dev, "disk") == 0) + break; + dos.read = nvrdevs[i].read; + dos.seek = nvrdevs[i].seek; + dos.write = nvrdevs[i].write; + break; + } + } + if(dos.read == 0) + panic("no device for nvram\n"); + if(dosinit(&dos) < 0) + panic("can't init dos dosfs on %s\n", p); + splx(s); +} + +void +touser(void) +{ + int i; + + settime(rtctime()); + boottime = time(); + + print("sysinit\n"); + sysinit(); + + userinit(floppyproc, 0, "floppyproc"); + /* + * Ethernet i/o processes + */ + etherstart(); + + + /* + * read ahead processes + */ + userinit(rahead, 0, "rah"); + + /* + * server processes + */ + for(i=0; itext = "scp"; + synccopy(); +} + +void +localconfinit(void) +{ + /* conf.nfile = 60000; */ /* from emelie */ + conf.nodump = 0; + conf.dumpreread = 0; + conf.firstsb = 0; /* time- & jukebox-dependent optimisation */ + conf.recovsb = 0; /* 971531 is 24 june 2003, before w3 died */ + conf.ripoff = 1; + conf.nlgmsg = 1100; /* @8576 bytes, for packets */ + conf.nsmmsg = 500; /* @128 bytes */ + + conf.minuteswest = 8*60; + conf.dsttime = 1; +} + +int (*fsprotocol[])(Msgbuf*) = { + serve9p1, /* TODO: do we still need 9P1? */ + serve9p2, + nil, +}; diff -Nru /sys/src/fs/9netics32.16k/dat.h /sys/src/fs/9netics32.16k/dat.h --- /sys/src/fs/9netics32.16k/dat.h Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/9netics32.16k/dat.h Tue Nov 1 00:00:00 2011 @@ -0,0 +1,35 @@ +/* + * The most fundamental constant. + * The code will not compile with RBUFSIZE made a variable; + * for one thing, RBUFSIZE determines FEPERBUF, which determines + * the number of elements in a free-list-block array. + */ +#define RBUFSIZE (16*1024) /* raw buffer size */ + +#include "../port/portdat.h" + +extern Mach mach0; + +typedef struct Segdesc Segdesc; +struct Segdesc +{ + ulong d0; + ulong d1; +}; + +typedef struct Mbank { + ulong base; + ulong limit; +} Mbank; + +#define MAXBANK 8 + +typedef struct Mconf { + Lock; + Mbank bank[MAXBANK]; + int nbank; + ulong topofmem; +} Mconf; +extern Mconf mconf; + +extern char nvrfile[128]; diff -Nru /sys/src/fs/9netics32.16k/fns.h /sys/src/fs/9netics32.16k/fns.h --- /sys/src/fs/9netics32.16k/fns.h Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/9netics32.16k/fns.h Tue Nov 1 00:00:00 2011 @@ -0,0 +1,87 @@ +ulong strtoul(char*, char**, int); +vlong strtoll(char*, char**, int); + +#include "../port/portfns.h" + +void aamloop(int); +void cgaputc(int); +void cgaputs(char*, int); +int cistrcmp(char*, char*); +int cistrncmp(char*, char*, int); +void (*coherence)(void); +void etherinit(void); +void etherstart(void); +int floppyinit(void); +void floppyproc(void); +Off floppyread(int, void*, long); +Devsize floppyseek(int, Devsize); +Off floppywrite(int, void*, long); +void fpinit(void); +char* getconf(char*); +ulong getcr0(void); +ulong getcr2(void); +ulong getcr4(void); +int getfields(char*, char**, int, int, char*); +ulong getstatus(void); +int atainit(void); +Off ataread(int, void*, long); +Devsize ataseek(int, Devsize); +Off atawrite(int, void*, long); +void i8042a20(void); +void i8042reset(void); +int inb(int); +void insb(int, void*, int); +ushort ins(int); +void inss(int, void*, int); +ulong inl(int); +void insl(int, void*, int); +void kbdinit(void); +int kbdintr0(void); +int kbdgetc(void); +long* mapaddr(ulong); +void microdelay(int); +void mmuinit(void); +uchar nvramread(int); +void outb(int, int); +void outsb(int, void*, int); +void outs(int, ushort); +void outss(int, void*, int); +void outl(int, ulong); +void outsl(int, void*, int); +void printcpufreq(void); +void putgdt(Segdesc*, int); +void putidt(Segdesc*, int); +void putcr3(ulong); +void putcr4(ulong); +void puttr(ulong); +void rdmsr(int, vlong*); +void wrmsr(int, vlong); +void (*cycles)(uvlong*); +void scsiinit(void); +Off scsiread(int, void*, long); +Devsize scsiseek(int, Devsize); +Off scsiwrite(int, void*, long); +int setatapart(int, char*); +int setscsipart(int, char*); +void setvec(int, void (*)(Ureg*, void*), void*); +int tas(Lock*); +void trapinit(void); +void uartspecial(int, void (*)(int), int (*)(void), int); +int uartgetc(void); +void uartputc(int); +void wbflush(void); +void cpuid(char*, int*, int*); + +#define PADDR(a) ((ulong)(a)&~KZERO) + +/* pata */ +void ideinit(Device *d); +Devsize idesize(Device *d); +int ideread(Device *d, Devsize, void*); +int idewrite(Device *d, Devsize, void*); + +/* sata */ +void mvideinit(Device *d); +Devsize mvidesize(Device *d); +int mvideread(Device *d, Devsize, void*); +int mvidewrite(Device *d, Devsize, void*); diff -Nru /sys/src/fs/9netics32.16k/io.h /sys/src/fs/9netics32.16k/io.h --- /sys/src/fs/9netics32.16k/io.h Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/9netics32.16k/io.h Tue Nov 1 00:00:00 2011 @@ -0,0 +1,247 @@ +/* + * programmable interrupt vectors (for the 8259's) + */ +enum +{ + Bptvec= 3, /* breakpoints */ + Mathemuvec= 7, /* math coprocessor emulation interrupt */ + Mathovervec= 9, /* math coprocessor overrun interrupt */ + Matherr1vec= 16, /* math coprocessor error interrupt */ + Faultvec= 14, /* page fault */ + + Int0vec= 24, /* first 8259 */ + Clockvec= Int0vec+0, /* clock interrupts */ + Kbdvec= Int0vec+1, /* keyboard interrupts */ + Uart1vec= Int0vec+3, /* modem line */ + Uart0vec= Int0vec+4, /* serial line */ + PCMCIAvec= Int0vec+5, /* PCMCIA card change */ + Floppyvec= Int0vec+6, /* floppy interrupts */ + Parallelvec= Int0vec+7, /* parallel port interrupts */ + Int1vec= Int0vec+8, + Ethervec= Int0vec+10, /* ethernet interrupt */ + Mousevec= Int0vec+12, /* mouse interrupt */ + Matherr2vec= Int0vec+13, /* math coprocessor */ + ATA0vec= Int0vec+14, /* hard disk */ + + Syscallvec= 64, +}; + +/* + * 8259 interrupt controllers + */ +enum +{ + Int0ctl= 0x20, /* control port (ICW1, OCW2, OCW3) */ + Int0aux= 0x21, /* everything else (ICW2, ICW3, ICW4, OCW1) */ + Int1ctl= 0xA0, /* control port */ + Int1aux= 0xA1, /* everything else (ICW2, ICW3, ICW4, OCW1) */ + + Icw1= 0x10, /* select bit in ctl register */ + Ocw2= 0x00, + Ocw3= 0x08, + + EOI= 0x20, /* non-specific end of interrupt */ + + Elcr1= 0x4D0, /* Edge/Level Triggered Register */ + Elcr2= 0x4D1, +}; + +extern int int0mask; /* interrupts enabled for first 8259 */ +extern int int1mask; /* interrupts enabled for second 8259 */ + +#define NVRAUTHADDR 0 +#define LINESIZE 0 + +enum { + MaxEISA = 16, + EISAconfig = 0xC80, + + MaxScsi = 4, + NTarget = 16, + + MaxEther = 4, +}; + +#define DMAOK(x, l) ((ulong)(((ulong)(x))+(l)) < (ulong)(KZERO|16*1024*1024)) + +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 */ +}; + +#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) + +/* + * PCI support code. + */ +enum { /* type 0 and type 1 pre-defined header */ + PciVID = 0x00, /* vendor ID */ + PciDID = 0x02, /* device ID */ + PciPCR = 0x04, /* command */ + PciPSR = 0x06, /* status */ + PciRID = 0x08, /* revision ID */ + PciCCRp = 0x09, /* programming interface class code */ + PciCCRu = 0x0A, /* sub-class code */ + PciCCRb = 0x0B, /* base class code */ + PciCLS = 0x0C, /* cache line size */ + PciLTR = 0x0D, /* latency timer */ + PciHDT = 0x0E, /* header type */ + PciBST = 0x0F, /* BIST */ + + PciBAR0 = 0x10, /* base address */ + PciBAR1 = 0x14, + + PciINTL = 0x3C, /* interrupt line */ + PciINTP = 0x3D, /* interrupt pin */ +}; + +enum { /* type 0 pre-defined header */ + PciBAR2 = 0x18, + PciBAR3 = 0x1C, + PciBAR4 = 0x20, + PciBAR5 = 0x24, + PciCIS = 0x28, /* cardbus CIS pointer */ + PciSVID = 0x2C, /* subsystem vendor ID */ + PciSID = 0x2E, /* cardbus CIS pointer */ + PciEBAR0 = 0x30, /* expansion ROM base address */ + PciMGNT = 0x3E, /* burst period length */ + PciMLT = 0x3F, /* maximum latency between bursts */ +}; + +enum { /* type 1 pre-defined header */ + PciPBN = 0x18, /* primary bus number */ + PciSBN = 0x19, /* secondary bus number */ + PciUBN = 0x1A, /* subordinate bus number */ + PciSLTR = 0x1B, /* secondary latency timer */ + PciIBR = 0x1C, /* I/O base */ + PciILR = 0x1D, /* I/O limit */ + PciSPSR = 0x1E, /* secondary status */ + PciMBR = 0x20, /* memory base */ + PciMLR = 0x22, /* memory limit */ + PciPMBR = 0x24, /* prefetchable memory base */ + PciPMLR = 0x26, /* prefetchable memory limit */ + PciPUBR = 0x28, /* prefetchable base upper 32 bits */ + PciPULR = 0x2C, /* prefetchable limit upper 32 bits */ + PciIUBR = 0x30, /* I/O base upper 16 bits */ + PciIULR = 0x32, /* I/O limit upper 16 bits */ + PciEBAR1 = 0x28, /* expansion ROM base address */ + PciBCR = 0x3E, /* bridge control register */ +}; + +typedef struct Pcidev Pcidev; +typedef struct Pcidev { + int tbdf; /* type+bus+device+function */ + ushort vid; /* vendor ID */ + ushort did; /* device ID */ + + struct { + ulong bar; /* base address */ + int size; + } mem[6]; + + uchar rid; + uchar ccrp; + uchar ccrb; + uchar intl; /* interrupt line */ + ushort ccru; /* is uchar in cpu kernel */ + ulong pcr; + + Pcidev* list; + Pcidev* bridge; /* down a bus */ + Pcidev* link; /* next device on this bno */ +} Pcidev; + +extern int pcicfgr8(Pcidev*, int); +extern int pcicfgr16(Pcidev*, int); +extern int pcicfgr32(Pcidev*, int); +extern void pcicfgw8(Pcidev*, int, int); +extern void pcicfgw16(Pcidev*, int, int); +extern void pcicfgw32(Pcidev*, int, int); +extern void pciclrmwi(Pcidev*); +extern void pcihinv(Pcidev*, ulong); +extern Pcidev* pcimatch(Pcidev*, int, int); +extern Pcidev* pcimatchtbdf(int); +extern void pcireset(void); +extern void pcisetbme(Pcidev*); +extern void pciclrbme(Pcidev*); + +/* + * a parsed plan9.ini line + */ +#define ISAOPTLEN 16 +#define NISAOPT 8 + +typedef struct ISAConf { + char type[NAMELEN]; + ulong port; + ulong irq; + ulong dma; + ulong mem; + ulong size; + ulong freq; + + int nopt; + char opt[NISAOPT][ISAOPTLEN]; +} ISAConf; + +extern int isaconfig(char*, int, ISAConf*); + +/* + * SCSI support code. + */ +enum { + STblank =-6, /* blank block */ + STnomem =-5, /* buffer allocation failed */ + STtimeout =-4, /* bus timeout */ + STownid =-3, /* playing with myself */ + STharderr =-2, /* controller error of some kind */ + STinit =-1, /* */ + STok = 0, /* good */ + STcheck = 0x02, /* check condition */ + STcondmet = 0x04, /* condition met/good */ + STbusy = 0x08, /* busy */ + STintok = 0x10, /* intermediate/good */ + STintcondmet = 0x14, /* intermediate/condition met/good */ + STresconf = 0x18, /* reservation conflict */ + STterminated = 0x22, /* command terminated */ + STqfull = 0x28, /* queue full */ +}; + +typedef struct Target { + int ctlrno; + int targetno; + uchar* inquiry; + uchar* sense; + + QLock; + char id[NAMELEN]; + int ok; + + char fflag; + Filter work[3]; + Filter rate[3]; +} Target; + +typedef int (*Scsiio)(Target*, int, uchar*, int, void*, int*); diff -Nru /sys/src/fs/9netics32.16k/mem.h /sys/src/fs/9netics32.16k/mem.h --- /sys/src/fs/9netics32.16k/mem.h Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/9netics32.16k/mem.h Tue Nov 1 00:00:00 2011 @@ -0,0 +1,85 @@ +#define BY2WD 4 /* bytes per word */ +#define BY2V 8 /* bytes per vlong */ +#define BY2PG 4096 /* bytes per page */ +#define PGSHIFT 12 /* log(BY2PG) */ +#define PGROUND(s) (((s)+(BY2PG-1))&~(BY2PG-1)) +#define MB (1024*1024) + +#define HZ (82) /* clock frequency */ +#define TK2MS(t) (((ulong)(t)*1000)/HZ) /* ticks to milliseconds - beware rounding */ +#define MS2TK(t) (((ulong)(t)*HZ)/1000) /* milliseconds to ticks - beware rounding */ +#define TK2SEC(t) ((t)/HZ) /* ticks to seconds */ + +/* + * Fundamental addresses + */ +#define IDTADDR 0x80000800 /* idt */ +#define APBOOTSTRAP 0x80001000 /* AP bootstrap code */ +#define CONFADDR 0x80001200 /* info passed from boot loader */ +#define CPU0PDB 0x80002000 /* bootstrap processor PDB */ +#define CPU0PTE 0x80003000 /* bootstrap processor PTE's for 0-2MB */ +#define CPU0MACHPTE 0x80004000 /* bootstrap processor PTE for MACHADDR */ +#define CPU0MACH 0x80005000 /* Mach for bootstrap processor */ + +#define KZERO 0x80000000 /* base of kernel address space */ +#define KTZERO 0x80100000 /* first address in kernel text */ + +#define MACHSIZE 4096 + +/* + * known 80386 segments (in GDT) and their selectors + */ +#define NULLSEG 0 /* null segment */ +#define KDSEG 1 /* kernel data/stack */ +#define KESEG 2 /* kernel executable */ +#define UDSEG 3 /* user data/stack */ +#define UESEG 4 /* user executable */ +#define TSSSEG 5 /* task segment */ +#define N386SEG 6 /* number of segments */ + +#define SELGDT (0<<3) /* selector is in gdt */ +#define SELLDT (1<<3) /* selector is in ldt */ + +#define SELECTOR(i, t, p) (((i)<<3) | (t) | (p)) + +#define NULLSEL SELECTOR(NULLSEG, SELGDT, 0) +#define KESEL SELECTOR(KESEG, SELGDT, 0) +#define KDSEL SELECTOR(KDSEG, SELGDT, 0) +#define UESEL SELECTOR(UESEG, SELGDT, 3) +#define UDSEL SELECTOR(UDSEG, SELGDT, 3) +#define TSSSEL SELECTOR(TSSSEG, SELGDT, 0) + +/* + * fields in segment descriptors + */ +#define SEGDATA (0x10<<8) /* data/stack segment */ +#define SEGEXEC (0x18<<8) /* executable segment */ +#define SEGTSS (0x9<<8) /* TSS segment */ +#define SEGCG (0x0C<<8) /* call gate */ +#define SEGIG (0x0E<<8) /* interrupt gate */ +#define SEGTG (0x0F<<8) /* task gate */ +#define SEGTYPE (0x1F<<8) + +#define SEGP (1<<15) /* segment present */ +#define SEGPL(x) ((x)<<13) /* priority level */ +#define SEGB (1<<22) /* granularity 1==4k (for expand-down) */ +#define SEGG (1<<23) /* granularity 1==4k (for other) */ +#define SEGE (1<<10) /* expand down */ +#define SEGW (1<<9) /* writable (for data/stack) */ +#define SEGR (1<<9) /* readable (for code) */ +#define SEGD (1<<22) /* default 1==32bit (for code) */ + +/* + * physical MMU + */ +#define PTEVALID (1<<0) +#define PTEUNCACHED (1<<4) +#define PTEWRITE (1<<1) +#define PTERONLY (0<<1) +#define PTEKERNEL (0<<2) +#define PTEUSER (1<<2) +#define PTESIZE (1<<7) + +#define MACHADDR ((ulong)&mach0) /* hack number 1 */ + +#define IFLAG 0x200 /* psw: interrupt enable, to be accurate */ diff -Nru /sys/src/fs/9netics32.16k/mkfile /sys/src/fs/9netics32.16k/mkfile --- /sys/src/fs/9netics32.16k/mkfile Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/9netics32.16k/mkfile Tue Nov 1 00:00:00 2011 @@ -0,0 +1,145 @@ +CONF=net32.16k +p=9 + +objtype=386 +text = "scp"; + synccopy(); +} + +void +localconfinit(void) +{ + /* conf.nfile = 60000; */ /* from emelie */ + conf.nodump = 0; + conf.dumpreread = 0; + conf.firstsb = 0; /* time- & jukebox-dependent optimisation */ + conf.recovsb = 0; /* 971531 is 24 june 2003, before w3 died */ + conf.ripoff = 1; + conf.nlgmsg = 1100; /* @8576 bytes, for packets */ + conf.nsmmsg = 500; /* @128 bytes */ + + conf.minuteswest = 8*60; + conf.dsttime = 1; +} + +int (*fsprotocol[])(Msgbuf*) = { + /* 64-bit file servers can't serve 9P1 correctly: NAMELEN is too big */ + serve9p2, + nil, +}; diff -Nru /sys/src/fs/9netics64.8k/dat.h /sys/src/fs/9netics64.8k/dat.h --- /sys/src/fs/9netics64.8k/dat.h Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/9netics64.8k/dat.h Tue Nov 1 00:00:00 2011 @@ -0,0 +1,35 @@ +/* + * The most fundamental constant. + * The code will not compile with RBUFSIZE made a variable; + * for one thing, RBUFSIZE determines FEPERBUF, which determines + * the number of elements in a free-list-block array. + */ +#define RBUFSIZE (8*1024) /* raw buffer size */ + +#include "../port/portdat.h" + +extern Mach mach0; + +typedef struct Segdesc Segdesc; +struct Segdesc +{ + ulong d0; + ulong d1; +}; + +typedef struct Mbank { + ulong base; + ulong limit; +} Mbank; + +#define MAXBANK 8 + +typedef struct Mconf { + Lock; + Mbank bank[MAXBANK]; + int nbank; + ulong topofmem; +} Mconf; +extern Mconf mconf; + +extern char nvrfile[128]; diff -Nru /sys/src/fs/9netics64.8k/fns.h /sys/src/fs/9netics64.8k/fns.h --- /sys/src/fs/9netics64.8k/fns.h Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/9netics64.8k/fns.h Tue Nov 1 00:00:00 2011 @@ -0,0 +1,87 @@ +ulong strtoul(char*, char**, int); +vlong strtoll(char*, char**, int); + +#include "../port/portfns.h" + +void aamloop(int); +void cgaputc(int); +void cgaputs(char*, int); +int cistrcmp(char*, char*); +int cistrncmp(char*, char*, int); +void (*coherence)(void); +void etherinit(void); +void etherstart(void); +int floppyinit(void); +void floppyproc(void); +Off floppyread(int, void*, long); +Devsize floppyseek(int, Devsize); +Off floppywrite(int, void*, long); +void fpinit(void); +char* getconf(char*); +ulong getcr0(void); +ulong getcr2(void); +ulong getcr4(void); +int getfields(char*, char**, int, int, char*); +ulong getstatus(void); +int atainit(void); +Off ataread(int, void*, long); +Devsize ataseek(int, Devsize); +Off atawrite(int, void*, long); +void i8042a20(void); +void i8042reset(void); +int inb(int); +void insb(int, void*, int); +ushort ins(int); +void inss(int, void*, int); +ulong inl(int); +void insl(int, void*, int); +void kbdinit(void); +int kbdintr0(void); +int kbdgetc(void); +long* mapaddr(ulong); +void microdelay(int); +void mmuinit(void); +uchar nvramread(int); +void outb(int, int); +void outsb(int, void*, int); +void outs(int, ushort); +void outss(int, void*, int); +void outl(int, ulong); +void outsl(int, void*, int); +void printcpufreq(void); +void putgdt(Segdesc*, int); +void putidt(Segdesc*, int); +void putcr3(ulong); +void putcr4(ulong); +void puttr(ulong); +void rdmsr(int, vlong*); +void wrmsr(int, vlong); +void (*cycles)(uvlong*); +void scsiinit(void); +Off scsiread(int, void*, long); +Devsize scsiseek(int, Devsize); +Off scsiwrite(int, void*, long); +int setatapart(int, char*); +int setscsipart(int, char*); +void setvec(int, void (*)(Ureg*, void*), void*); +int tas(Lock*); +void trapinit(void); +void uartspecial(int, void (*)(int), int (*)(void), int); +int uartgetc(void); +void uartputc(int); +void wbflush(void); +void cpuid(char*, int*, int*); + +#define PADDR(a) ((ulong)(a)&~KZERO) + +/* pata */ +void ideinit(Device *d); +Devsize idesize(Device *d); +int ideread(Device *d, Devsize, void*); +int idewrite(Device *d, Devsize, void*); + +/* sata */ +void mvideinit(Device *d); +Devsize mvidesize(Device *d); +int mvideread(Device *d, Devsize, void*); +int mvidewrite(Device *d, Devsize, void*); diff -Nru /sys/src/fs/9netics64.8k/io.h /sys/src/fs/9netics64.8k/io.h --- /sys/src/fs/9netics64.8k/io.h Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/9netics64.8k/io.h Tue Nov 1 00:00:00 2011 @@ -0,0 +1,247 @@ +/* + * programmable interrupt vectors (for the 8259's) + */ +enum +{ + Bptvec= 3, /* breakpoints */ + Mathemuvec= 7, /* math coprocessor emulation interrupt */ + Mathovervec= 9, /* math coprocessor overrun interrupt */ + Matherr1vec= 16, /* math coprocessor error interrupt */ + Faultvec= 14, /* page fault */ + + Int0vec= 24, /* first 8259 */ + Clockvec= Int0vec+0, /* clock interrupts */ + Kbdvec= Int0vec+1, /* keyboard interrupts */ + Uart1vec= Int0vec+3, /* modem line */ + Uart0vec= Int0vec+4, /* serial line */ + PCMCIAvec= Int0vec+5, /* PCMCIA card change */ + Floppyvec= Int0vec+6, /* floppy interrupts */ + Parallelvec= Int0vec+7, /* parallel port interrupts */ + Int1vec= Int0vec+8, + Ethervec= Int0vec+10, /* ethernet interrupt */ + Mousevec= Int0vec+12, /* mouse interrupt */ + Matherr2vec= Int0vec+13, /* math coprocessor */ + ATA0vec= Int0vec+14, /* hard disk */ + + Syscallvec= 64, +}; + +/* + * 8259 interrupt controllers + */ +enum +{ + Int0ctl= 0x20, /* control port (ICW1, OCW2, OCW3) */ + Int0aux= 0x21, /* everything else (ICW2, ICW3, ICW4, OCW1) */ + Int1ctl= 0xA0, /* control port */ + Int1aux= 0xA1, /* everything else (ICW2, ICW3, ICW4, OCW1) */ + + Icw1= 0x10, /* select bit in ctl register */ + Ocw2= 0x00, + Ocw3= 0x08, + + EOI= 0x20, /* non-specific end of interrupt */ + + Elcr1= 0x4D0, /* Edge/Level Triggered Register */ + Elcr2= 0x4D1, +}; + +extern int int0mask; /* interrupts enabled for first 8259 */ +extern int int1mask; /* interrupts enabled for second 8259 */ + +#define NVRAUTHADDR 0 +#define LINESIZE 0 + +enum { + MaxEISA = 16, + EISAconfig = 0xC80, + + MaxScsi = 4, + NTarget = 16, + + MaxEther = 4, +}; + +#define DMAOK(x, l) ((ulong)(((ulong)(x))+(l)) < (ulong)(KZERO|16*1024*1024)) + +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 */ +}; + +#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) + +/* + * PCI support code. + */ +enum { /* type 0 and type 1 pre-defined header */ + PciVID = 0x00, /* vendor ID */ + PciDID = 0x02, /* device ID */ + PciPCR = 0x04, /* command */ + PciPSR = 0x06, /* status */ + PciRID = 0x08, /* revision ID */ + PciCCRp = 0x09, /* programming interface class code */ + PciCCRu = 0x0A, /* sub-class code */ + PciCCRb = 0x0B, /* base class code */ + PciCLS = 0x0C, /* cache line size */ + PciLTR = 0x0D, /* latency timer */ + PciHDT = 0x0E, /* header type */ + PciBST = 0x0F, /* BIST */ + + PciBAR0 = 0x10, /* base address */ + PciBAR1 = 0x14, + + PciINTL = 0x3C, /* interrupt line */ + PciINTP = 0x3D, /* interrupt pin */ +}; + +enum { /* type 0 pre-defined header */ + PciBAR2 = 0x18, + PciBAR3 = 0x1C, + PciBAR4 = 0x20, + PciBAR5 = 0x24, + PciCIS = 0x28, /* cardbus CIS pointer */ + PciSVID = 0x2C, /* subsystem vendor ID */ + PciSID = 0x2E, /* cardbus CIS pointer */ + PciEBAR0 = 0x30, /* expansion ROM base address */ + PciMGNT = 0x3E, /* burst period length */ + PciMLT = 0x3F, /* maximum latency between bursts */ +}; + +enum { /* type 1 pre-defined header */ + PciPBN = 0x18, /* primary bus number */ + PciSBN = 0x19, /* secondary bus number */ + PciUBN = 0x1A, /* subordinate bus number */ + PciSLTR = 0x1B, /* secondary latency timer */ + PciIBR = 0x1C, /* I/O base */ + PciILR = 0x1D, /* I/O limit */ + PciSPSR = 0x1E, /* secondary status */ + PciMBR = 0x20, /* memory base */ + PciMLR = 0x22, /* memory limit */ + PciPMBR = 0x24, /* prefetchable memory base */ + PciPMLR = 0x26, /* prefetchable memory limit */ + PciPUBR = 0x28, /* prefetchable base upper 32 bits */ + PciPULR = 0x2C, /* prefetchable limit upper 32 bits */ + PciIUBR = 0x30, /* I/O base upper 16 bits */ + PciIULR = 0x32, /* I/O limit upper 16 bits */ + PciEBAR1 = 0x28, /* expansion ROM base address */ + PciBCR = 0x3E, /* bridge control register */ +}; + +typedef struct Pcidev Pcidev; +typedef struct Pcidev { + int tbdf; /* type+bus+device+function */ + ushort vid; /* vendor ID */ + ushort did; /* device ID */ + + struct { + ulong bar; /* base address */ + int size; + } mem[6]; + + uchar rid; + uchar ccrp; + uchar ccrb; + uchar intl; /* interrupt line */ + ushort ccru; /* is uchar in cpu kernel */ + ulong pcr; + + Pcidev* list; + Pcidev* bridge; /* down a bus */ + Pcidev* link; /* next device on this bno */ +} Pcidev; + +extern int pcicfgr8(Pcidev*, int); +extern int pcicfgr16(Pcidev*, int); +extern int pcicfgr32(Pcidev*, int); +extern void pcicfgw8(Pcidev*, int, int); +extern void pcicfgw16(Pcidev*, int, int); +extern void pcicfgw32(Pcidev*, int, int); +extern void pciclrmwi(Pcidev*); +extern void pcihinv(Pcidev*, ulong); +extern Pcidev* pcimatch(Pcidev*, int, int); +extern Pcidev* pcimatchtbdf(int); +extern void pcireset(void); +extern void pcisetbme(Pcidev*); +extern void pciclrbme(Pcidev*); + +/* + * a parsed plan9.ini line + */ +#define ISAOPTLEN 16 +#define NISAOPT 8 + +typedef struct ISAConf { + char type[NAMELEN]; + ulong port; + ulong irq; + ulong dma; + ulong mem; + ulong size; + ulong freq; + + int nopt; + char opt[NISAOPT][ISAOPTLEN]; +} ISAConf; + +extern int isaconfig(char*, int, ISAConf*); + +/* + * SCSI support code. + */ +enum { + STblank =-6, /* blank block */ + STnomem =-5, /* buffer allocation failed */ + STtimeout =-4, /* bus timeout */ + STownid =-3, /* playing with myself */ + STharderr =-2, /* controller error of some kind */ + STinit =-1, /* */ + STok = 0, /* good */ + STcheck = 0x02, /* check condition */ + STcondmet = 0x04, /* condition met/good */ + STbusy = 0x08, /* busy */ + STintok = 0x10, /* intermediate/good */ + STintcondmet = 0x14, /* intermediate/condition met/good */ + STresconf = 0x18, /* reservation conflict */ + STterminated = 0x22, /* command terminated */ + STqfull = 0x28, /* queue full */ +}; + +typedef struct Target { + int ctlrno; + int targetno; + uchar* inquiry; + uchar* sense; + + QLock; + char id[NAMELEN]; + int ok; + + char fflag; + Filter work[3]; + Filter rate[3]; +} Target; + +typedef int (*Scsiio)(Target*, int, uchar*, int, void*, int*); diff -Nru /sys/src/fs/9netics64.8k/mem.h /sys/src/fs/9netics64.8k/mem.h --- /sys/src/fs/9netics64.8k/mem.h Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/9netics64.8k/mem.h Tue Nov 1 00:00:00 2011 @@ -0,0 +1,85 @@ +#define BY2WD 4 /* bytes per word */ +#define BY2V 8 /* bytes per vlong */ +#define BY2PG 4096 /* bytes per page */ +#define PGSHIFT 12 /* log(BY2PG) */ +#define PGROUND(s) (((s)+(BY2PG-1))&~(BY2PG-1)) +#define MB (1024*1024) + +#define HZ (82) /* clock frequency */ +#define TK2MS(t) (((ulong)(t)*1000)/HZ) /* ticks to milliseconds - beware rounding */ +#define MS2TK(t) (((ulong)(t)*HZ)/1000) /* milliseconds to ticks - beware rounding */ +#define TK2SEC(t) ((t)/HZ) /* ticks to seconds */ + +/* + * Fundamental addresses + */ +#define IDTADDR 0x80000800 /* idt */ +#define APBOOTSTRAP 0x80001000 /* AP bootstrap code */ +#define CONFADDR 0x80001200 /* info passed from boot loader */ +#define CPU0PDB 0x80002000 /* bootstrap processor PDB */ +#define CPU0PTE 0x80003000 /* bootstrap processor PTE's for 0-2MB */ +#define CPU0MACHPTE 0x80004000 /* bootstrap processor PTE for MACHADDR */ +#define CPU0MACH 0x80005000 /* Mach for bootstrap processor */ + +#define KZERO 0x80000000 /* base of kernel address space */ +#define KTZERO 0x80100000 /* first address in kernel text */ + +#define MACHSIZE 4096 + +/* + * known 80386 segments (in GDT) and their selectors + */ +#define NULLSEG 0 /* null segment */ +#define KDSEG 1 /* kernel data/stack */ +#define KESEG 2 /* kernel executable */ +#define UDSEG 3 /* user data/stack */ +#define UESEG 4 /* user executable */ +#define TSSSEG 5 /* task segment */ +#define N386SEG 6 /* number of segments */ + +#define SELGDT (0<<3) /* selector is in gdt */ +#define SELLDT (1<<3) /* selector is in ldt */ + +#define SELECTOR(i, t, p) (((i)<<3) | (t) | (p)) + +#define NULLSEL SELECTOR(NULLSEG, SELGDT, 0) +#define KESEL SELECTOR(KESEG, SELGDT, 0) +#define KDSEL SELECTOR(KDSEG, SELGDT, 0) +#define UESEL SELECTOR(UESEG, SELGDT, 3) +#define UDSEL SELECTOR(UDSEG, SELGDT, 3) +#define TSSSEL SELECTOR(TSSSEG, SELGDT, 0) + +/* + * fields in segment descriptors + */ +#define SEGDATA (0x10<<8) /* data/stack segment */ +#define SEGEXEC (0x18<<8) /* executable segment */ +#define SEGTSS (0x9<<8) /* TSS segment */ +#define SEGCG (0x0C<<8) /* call gate */ +#define SEGIG (0x0E<<8) /* interrupt gate */ +#define SEGTG (0x0F<<8) /* task gate */ +#define SEGTYPE (0x1F<<8) + +#define SEGP (1<<15) /* segment present */ +#define SEGPL(x) ((x)<<13) /* priority level */ +#define SEGB (1<<22) /* granularity 1==4k (for expand-down) */ +#define SEGG (1<<23) /* granularity 1==4k (for other) */ +#define SEGE (1<<10) /* expand down */ +#define SEGW (1<<9) /* writable (for data/stack) */ +#define SEGR (1<<9) /* readable (for code) */ +#define SEGD (1<<22) /* default 1==32bit (for code) */ + +/* + * physical MMU + */ +#define PTEVALID (1<<0) +#define PTEUNCACHED (1<<4) +#define PTEWRITE (1<<1) +#define PTERONLY (0<<1) +#define PTEKERNEL (0<<2) +#define PTEUSER (1<<2) +#define PTESIZE (1<<7) + +#define MACHADDR ((ulong)&mach0) /* hack number 1 */ + +#define IFLAG 0x200 /* psw: interrupt enable, to be accurate */ diff -Nru /sys/src/fs/9netics64.8k/mkfile /sys/src/fs/9netics64.8k/mkfile --- /sys/src/fs/9netics64.8k/mkfile Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/9netics64.8k/mkfile Tue Nov 1 00:00:00 2011 @@ -0,0 +1,145 @@ +CONF=net64.8k +p=9 + +objtype=386 +text = "scp"; + synccopy(); +} + +void +localconfinit(void) +{ + conf.nfile = 60000; + conf.nodump = 0; + conf.firstsb = 12565379; + conf.recovsb = 0; + conf.ripoff = 1; + conf.nlgmsg = 100; + conf.nsmmsg = 500; + + conf.minuteswest = 5*60; + conf.dsttime = 1; +} + +int (*fsprotocol[])(Msgbuf*) = { + serve9p1, + serve9p2, + nil, +}; diff -Nru /sys/src/fs/choline/dat.h /sys/src/fs/choline/dat.h --- /sys/src/fs/choline/dat.h Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/choline/dat.h Tue Nov 1 00:00:00 2011 @@ -0,0 +1,35 @@ +/* + * The most fundamental constant. + * The code will not compile with RBUFSIZE made a variable; + * for one thing, RBUFSIZE determines FEPERBUF, which determines + * the number of elements in a free-list-block array. + */ +#define RBUFSIZE (16*1024) /* raw buffer size */ + +#include "../port/portdat.h" + +extern Mach mach0; + +typedef struct Segdesc Segdesc; +struct Segdesc +{ + ulong d0; + ulong d1; +}; + +typedef struct Mbank { + ulong base; + ulong limit; +} Mbank; + +#define MAXBANK 8 + +typedef struct Mconf { + Lock; + Mbank bank[MAXBANK]; + int nbank; + ulong topofmem; +} Mconf; +extern Mconf mconf; + +extern char nvrfile[128]; diff -Nru /sys/src/fs/choline/fns.h /sys/src/fs/choline/fns.h --- /sys/src/fs/choline/fns.h Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/choline/fns.h Tue Nov 1 00:00:00 2011 @@ -0,0 +1,87 @@ +ulong strtoul(char*, char**, int); +vlong strtoll(char*, char**, int); + +#include "../port/portfns.h" + +void aamloop(int); +void cgaputc(int); +void cgaputs(char*, int); +int cistrcmp(char*, char*); +int cistrncmp(char*, char*, int); +void (*coherence)(void); +void etherinit(void); +void etherstart(void); +int floppyinit(void); +void floppyproc(void); +Off floppyread(int, void*, long); +Devsize floppyseek(int, Devsize); +Off floppywrite(int, void*, long); +void fpinit(void); +char* getconf(char*); +ulong getcr0(void); +ulong getcr2(void); +ulong getcr4(void); +int getfields(char*, char**, int, int, char*); +ulong getstatus(void); +int atainit(void); +Off ataread(int, void*, long); +Devsize ataseek(int, Devsize); +Off atawrite(int, void*, long); +void i8042a20(void); +void i8042reset(void); +int inb(int); +void insb(int, void*, int); +ushort ins(int); +void inss(int, void*, int); +ulong inl(int); +void insl(int, void*, int); +void kbdinit(void); +int kbdintr0(void); +int kbdgetc(void); +long* mapaddr(ulong); +void microdelay(int); +void mmuinit(void); +uchar nvramread(int); +void outb(int, int); +void outsb(int, void*, int); +void outs(int, ushort); +void outss(int, void*, int); +void outl(int, ulong); +void outsl(int, void*, int); +void printcpufreq(void); +void putgdt(Segdesc*, int); +void putidt(Segdesc*, int); +void putcr3(ulong); +void putcr4(ulong); +void puttr(ulong); +void rdmsr(int, vlong*); +void wrmsr(int, vlong); +void (*cycles)(uvlong*); +void scsiinit(void); +Off scsiread(int, void*, long); +Devsize scsiseek(int, Devsize); +Off scsiwrite(int, void*, long); +int setatapart(int, char*); +int setscsipart(int, char*); +void setvec(int, void (*)(Ureg*, void*), void*); +int tas(Lock*); +void trapinit(void); +void uartspecial(int, void (*)(int), int (*)(void), int); +int uartgetc(void); +void uartputc(int); +void wbflush(void); +void cpuid(char*, int*, int*); + +#define PADDR(a) ((ulong)(a)&~KZERO) + +/* pata */ +void ideinit(Device *d); +Devsize idesize(Device *d); +int ideread(Device *d, Devsize, void*); +int idewrite(Device *d, Devsize, void*); + +/* sata */ +void mvideinit(Device *d); +Devsize mvidesize(Device *d); +int mvideread(Device *d, Devsize, void*); +int mvidewrite(Device *d, Devsize, void*); diff -Nru /sys/src/fs/choline/io.h /sys/src/fs/choline/io.h --- /sys/src/fs/choline/io.h Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/choline/io.h Tue Nov 1 00:00:00 2011 @@ -0,0 +1,247 @@ +/* + * programmable interrupt vectors (for the 8259's) + */ +enum +{ + Bptvec= 3, /* breakpoints */ + Mathemuvec= 7, /* math coprocessor emulation interrupt */ + Mathovervec= 9, /* math coprocessor overrun interrupt */ + Matherr1vec= 16, /* math coprocessor error interrupt */ + Faultvec= 14, /* page fault */ + + Int0vec= 24, /* first 8259 */ + Clockvec= Int0vec+0, /* clock interrupts */ + Kbdvec= Int0vec+1, /* keyboard interrupts */ + Uart1vec= Int0vec+3, /* modem line */ + Uart0vec= Int0vec+4, /* serial line */ + PCMCIAvec= Int0vec+5, /* PCMCIA card change */ + Floppyvec= Int0vec+6, /* floppy interrupts */ + Parallelvec= Int0vec+7, /* parallel port interrupts */ + Int1vec= Int0vec+8, + Ethervec= Int0vec+10, /* ethernet interrupt */ + Mousevec= Int0vec+12, /* mouse interrupt */ + Matherr2vec= Int0vec+13, /* math coprocessor */ + ATA0vec= Int0vec+14, /* hard disk */ + + Syscallvec= 64, +}; + +/* + * 8259 interrupt controllers + */ +enum +{ + Int0ctl= 0x20, /* control port (ICW1, OCW2, OCW3) */ + Int0aux= 0x21, /* everything else (ICW2, ICW3, ICW4, OCW1) */ + Int1ctl= 0xA0, /* control port */ + Int1aux= 0xA1, /* everything else (ICW2, ICW3, ICW4, OCW1) */ + + Icw1= 0x10, /* select bit in ctl register */ + Ocw2= 0x00, + Ocw3= 0x08, + + EOI= 0x20, /* non-specific end of interrupt */ + + Elcr1= 0x4D0, /* Edge/Level Triggered Register */ + Elcr2= 0x4D1, +}; + +extern int int0mask; /* interrupts enabled for first 8259 */ +extern int int1mask; /* interrupts enabled for second 8259 */ + +#define NVRAUTHADDR 0 +#define LINESIZE 0 + +enum { + MaxEISA = 16, + EISAconfig = 0xC80, + + MaxScsi = 4, + NTarget = 16, + + MaxEther = 4, +}; + +#define DMAOK(x, l) ((ulong)(((ulong)(x))+(l)) < (ulong)(KZERO|16*1024*1024)) + +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 */ +}; + +#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) + +/* + * PCI support code. + */ +enum { /* type 0 and type 1 pre-defined header */ + PciVID = 0x00, /* vendor ID */ + PciDID = 0x02, /* device ID */ + PciPCR = 0x04, /* command */ + PciPSR = 0x06, /* status */ + PciRID = 0x08, /* revision ID */ + PciCCRp = 0x09, /* programming interface class code */ + PciCCRu = 0x0A, /* sub-class code */ + PciCCRb = 0x0B, /* base class code */ + PciCLS = 0x0C, /* cache line size */ + PciLTR = 0x0D, /* latency timer */ + PciHDT = 0x0E, /* header type */ + PciBST = 0x0F, /* BIST */ + + PciBAR0 = 0x10, /* base address */ + PciBAR1 = 0x14, + + PciINTL = 0x3C, /* interrupt line */ + PciINTP = 0x3D, /* interrupt pin */ +}; + +enum { /* type 0 pre-defined header */ + PciBAR2 = 0x18, + PciBAR3 = 0x1C, + PciBAR4 = 0x20, + PciBAR5 = 0x24, + PciCIS = 0x28, /* cardbus CIS pointer */ + PciSVID = 0x2C, /* subsystem vendor ID */ + PciSID = 0x2E, /* cardbus CIS pointer */ + PciEBAR0 = 0x30, /* expansion ROM base address */ + PciMGNT = 0x3E, /* burst period length */ + PciMLT = 0x3F, /* maximum latency between bursts */ +}; + +enum { /* type 1 pre-defined header */ + PciPBN = 0x18, /* primary bus number */ + PciSBN = 0x19, /* secondary bus number */ + PciUBN = 0x1A, /* subordinate bus number */ + PciSLTR = 0x1B, /* secondary latency timer */ + PciIBR = 0x1C, /* I/O base */ + PciILR = 0x1D, /* I/O limit */ + PciSPSR = 0x1E, /* secondary status */ + PciMBR = 0x20, /* memory base */ + PciMLR = 0x22, /* memory limit */ + PciPMBR = 0x24, /* prefetchable memory base */ + PciPMLR = 0x26, /* prefetchable memory limit */ + PciPUBR = 0x28, /* prefetchable base upper 32 bits */ + PciPULR = 0x2C, /* prefetchable limit upper 32 bits */ + PciIUBR = 0x30, /* I/O base upper 16 bits */ + PciIULR = 0x32, /* I/O limit upper 16 bits */ + PciEBAR1 = 0x28, /* expansion ROM base address */ + PciBCR = 0x3E, /* bridge control register */ +}; + +typedef struct Pcidev Pcidev; +typedef struct Pcidev { + int tbdf; /* type+bus+device+function */ + ushort vid; /* vendor ID */ + ushort did; /* device ID */ + + struct { + ulong bar; /* base address */ + int size; + } mem[6]; + + uchar rid; + uchar ccrp; + uchar ccrb; + uchar intl; /* interrupt line */ + ushort ccru; /* is uchar in cpu kernel */ + ulong pcr; + + Pcidev* list; + Pcidev* bridge; /* down a bus */ + Pcidev* link; /* next device on this bno */ +} Pcidev; + +extern int pcicfgr8(Pcidev*, int); +extern int pcicfgr16(Pcidev*, int); +extern int pcicfgr32(Pcidev*, int); +extern void pcicfgw8(Pcidev*, int, int); +extern void pcicfgw16(Pcidev*, int, int); +extern void pcicfgw32(Pcidev*, int, int); +extern void pciclrmwi(Pcidev*); +extern void pcihinv(Pcidev*, ulong); +extern Pcidev* pcimatch(Pcidev*, int, int); +extern Pcidev* pcimatchtbdf(int); +extern void pcireset(void); +extern void pcisetbme(Pcidev*); +extern void pciclrbme(Pcidev*); + +/* + * a parsed plan9.ini line + */ +#define ISAOPTLEN 16 +#define NISAOPT 8 + +typedef struct ISAConf { + char type[NAMELEN]; + ulong port; + ulong irq; + ulong dma; + ulong mem; + ulong size; + ulong freq; + + int nopt; + char opt[NISAOPT][ISAOPTLEN]; +} ISAConf; + +extern int isaconfig(char*, int, ISAConf*); + +/* + * SCSI support code. + */ +enum { + STblank =-6, /* blank block */ + STnomem =-5, /* buffer allocation failed */ + STtimeout =-4, /* bus timeout */ + STownid =-3, /* playing with myself */ + STharderr =-2, /* controller error of some kind */ + STinit =-1, /* */ + STok = 0, /* good */ + STcheck = 0x02, /* check condition */ + STcondmet = 0x04, /* condition met/good */ + STbusy = 0x08, /* busy */ + STintok = 0x10, /* intermediate/good */ + STintcondmet = 0x14, /* intermediate/condition met/good */ + STresconf = 0x18, /* reservation conflict */ + STterminated = 0x22, /* command terminated */ + STqfull = 0x28, /* queue full */ +}; + +typedef struct Target { + int ctlrno; + int targetno; + uchar* inquiry; + uchar* sense; + + QLock; + char id[NAMELEN]; + int ok; + + char fflag; + Filter work[3]; + Filter rate[3]; +} Target; + +typedef int (*Scsiio)(Target*, int, uchar*, int, void*, int*); diff -Nru /sys/src/fs/choline/mem.h /sys/src/fs/choline/mem.h --- /sys/src/fs/choline/mem.h Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/choline/mem.h Tue Nov 1 00:00:00 2011 @@ -0,0 +1,85 @@ +#define BY2WD 4 /* bytes per word */ +#define BY2V 8 /* bytes per vlong */ +#define BY2PG 4096 /* bytes per page */ +#define PGSHIFT 12 /* log(BY2PG) */ +#define PGROUND(s) (((s)+(BY2PG-1))&~(BY2PG-1)) +#define MB (1024*1024) + +#define HZ (82) /* clock frequency */ +#define TK2MS(t) (((ulong)(t)*1000)/HZ) /* ticks to milliseconds - beware rounding */ +#define MS2TK(t) (((ulong)(t)*HZ)/1000) /* milliseconds to ticks - beware rounding */ +#define TK2SEC(t) ((t)/HZ) /* ticks to seconds */ + +/* + * Fundamental addresses + */ +#define IDTADDR 0x80000800 /* idt */ +#define APBOOTSTRAP 0x80001000 /* AP bootstrap code */ +#define CONFADDR 0x80001200 /* info passed from boot loader */ +#define CPU0PDB 0x80002000 /* bootstrap processor PDB */ +#define CPU0PTE 0x80003000 /* bootstrap processor PTE's for 0-2MB */ +#define CPU0MACHPTE 0x80004000 /* bootstrap processor PTE for MACHADDR */ +#define CPU0MACH 0x80005000 /* Mach for bootstrap processor */ + +#define KZERO 0x80000000 /* base of kernel address space */ +#define KTZERO 0x80100000 /* first address in kernel text */ + +#define MACHSIZE 4096 + +/* + * known 80386 segments (in GDT) and their selectors + */ +#define NULLSEG 0 /* null segment */ +#define KDSEG 1 /* kernel data/stack */ +#define KESEG 2 /* kernel executable */ +#define UDSEG 3 /* user data/stack */ +#define UESEG 4 /* user executable */ +#define TSSSEG 5 /* task segment */ +#define N386SEG 6 /* number of segments */ + +#define SELGDT (0<<3) /* selector is in gdt */ +#define SELLDT (1<<3) /* selector is in ldt */ + +#define SELECTOR(i, t, p) (((i)<<3) | (t) | (p)) + +#define NULLSEL SELECTOR(NULLSEG, SELGDT, 0) +#define KESEL SELECTOR(KESEG, SELGDT, 0) +#define KDSEL SELECTOR(KDSEG, SELGDT, 0) +#define UESEL SELECTOR(UESEG, SELGDT, 3) +#define UDSEL SELECTOR(UDSEG, SELGDT, 3) +#define TSSSEL SELECTOR(TSSSEG, SELGDT, 0) + +/* + * fields in segment descriptors + */ +#define SEGDATA (0x10<<8) /* data/stack segment */ +#define SEGEXEC (0x18<<8) /* executable segment */ +#define SEGTSS (0x9<<8) /* TSS segment */ +#define SEGCG (0x0C<<8) /* call gate */ +#define SEGIG (0x0E<<8) /* interrupt gate */ +#define SEGTG (0x0F<<8) /* task gate */ +#define SEGTYPE (0x1F<<8) + +#define SEGP (1<<15) /* segment present */ +#define SEGPL(x) ((x)<<13) /* priority level */ +#define SEGB (1<<22) /* granularity 1==4k (for expand-down) */ +#define SEGG (1<<23) /* granularity 1==4k (for other) */ +#define SEGE (1<<10) /* expand down */ +#define SEGW (1<<9) /* writable (for data/stack) */ +#define SEGR (1<<9) /* readable (for code) */ +#define SEGD (1<<22) /* default 1==32bit (for code) */ + +/* + * physical MMU + */ +#define PTEVALID (1<<0) +#define PTEUNCACHED (1<<4) +#define PTEWRITE (1<<1) +#define PTERONLY (0<<1) +#define PTEKERNEL (0<<2) +#define PTEUSER (1<<2) +#define PTESIZE (1<<7) + +#define MACHADDR ((ulong)&mach0) /* hack number 1 */ + +#define IFLAG 0x200 /* psw: interrupt enable, to be accurate */ diff -Nru /sys/src/fs/choline/mkfile /sys/src/fs/choline/mkfile --- /sys/src/fs/choline/mkfile Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/choline/mkfile Tue Nov 1 00:00:00 2011 @@ -0,0 +1,147 @@ +CONF=choline +p=9 + +objtype=386 += apc.rxq.buf + sizeof(apc.rxq.buf)) + p = apc.rxq.buf; + apc.rxq.in = p; + apc.rxq.count++; + wakeup(&apc.rxq); + } + unlock(&apc.rxq); + splx(s); +} + +static int +done(void *p) +{ + return *(int *)p != 0; +} + +static int +eitherdone(void *) +{ + return apc.rxq.count != 0 || apc.kicked; +} + +int +apcgetc(int timo, int noevents) +{ + char c; + int s; + +loop: + if (timo < 0) + sleep(&apc.rxq, eitherdone, 0); + else + tsleep(&apc.rxq, eitherdone, 0, timo); + if (apc.kicked) + return Kicked; + + s = splhi(); + lock(&apc.rxq); + if (apc.rxq.count == 0) { + unlock(&apc.rxq); + splx(s); + if (timo >= 0) + return Timedout; + goto loop; + } + c = *apc.rxq.out++; + if (apc.rxq.out >= apc.rxq.buf + sizeof(apc.rxq.buf)) + apc.rxq.out = apc.rxq.buf; + apc.rxq.count--; + unlock(&apc.rxq); + splx(s); + + switch (c) { + case '!': + STATUSCHANGE(OnBattery, 1); +report_event: + print("apc: event %c\n", c); + if (noevents) + goto loop; + return Event; + case '$': + STATUSCHANGE(OnBattery, 0); + goto report_event; + case '%': + STATUSCHANGE(LowBattery, 1); + goto report_event; + case '+': + STATUSCHANGE(LowBattery, 0); + goto report_event; + case '?': + STATUSCHANGE(AbnormalCondition, 1); + goto report_event; + case '=': + STATUSCHANGE(AbnormalCondition, 0); + goto report_event; + case '*': + print("apc: turning off\n"); + goto loop; + case '#': + print("apc: replace battery\n"); + goto loop; + case '&': + print("apc: check alarm register\n"); + goto loop; + case 0x7c: + print("apc: eeprom modified\n"); + goto loop; + default: + break; + } + +// print("apc: got 0x%.2ux\n", c); + return c; +} + +char * +apcgets(char *buf, int len, int timo) +{ + char *q; + int c; + + q = buf; + while ((c = apcgetc(timo, 1)) >= 0 && c != '\r') + if (q < buf + len - 1) + *q++ = c; + if (c < 0) + return nil; + + c = apcgetc(timo, 1); + if (c < 0 || c != '\n') + return nil; + + *q = 0; + return buf; +} + +int +apcexpect(char *s, int skiprubbish, int timo) +{ + int first = 1; + + while (*s) { + int c = apcgetc(timo, 1); + + if (c < 0) + return 0; + if (*s == c) { + s++; + first = 0; + continue; + } + if (!first) + return 0; + if (!skiprubbish) + return 0; + first = 0; + } + return 1; +} + +int +apcattention(void) /* anybody home? */ +{ + apcputc('Y'); + if (!apcexpect("SM\r\n", 1, 1000)) + return 0; + apc.detected = 1; + return 1; +} + +char * +apccmdstrresponse(char *cmd, char *buf, int len) +{ + char *s; + + apcputs(cmd); + s = apcgets(buf, len, 1000); + if (s == nil) { + print("APC asleep...\n"); + if (!apcattention()) + return nil; + apcputs(cmd); + return apcgets(buf, len, 1000); + } + return s; +} + +char * +apccmdresponse(char cmd, char *buf, int len) +{ + char cmdstr[2]; + + cmdstr[0] = cmd; + cmdstr[1] = 0; + return apccmdstrresponse(cmdstr, buf, len); +} + +static void +parsecap(char *capstr, char locale) +{ + char cmd, lc, c; + int n, el, i, j, p; + + while (*capstr) { + char *s; + Capability *cap; + + cmd = *capstr++; + lc = *capstr++; + n = *capstr++ - '0'; + el = *capstr++ - '0'; + p = lc == '4' || lc == locale; + if (p) { + cap = ialloc(sizeof *cap + sizeof s*(n - 1), 0); + cap->cmd = cmd; + cap->n = n; + s = ialloc(n*(el + 1), 0); + for (i = 0; i < n; i++) { + cap->val[i] = s + i*(el + 1); + cap->val[i][el] = 0; + } + } else + cap = nil; + for (i = 0; i < n; i++) + for (j = 0; j < el; j++) { + c = *capstr++; + if (p) + cap->val[i][j] = c; + } + if (p) { + cap->next = apc.cap; + apc.cap = cap; + } + } +} + +static char * +cyclecmd(Capability *cap, int i) +{ + char *s; + + for (;;) { + char resp[10]; + + s = apccmdresponse(cap->cmd, resp, sizeof(resp)); + if (s == nil || strcmp(resp, cap->val[i]) == 0) + break; + s = apccmdresponse('-', resp, sizeof(resp)); + if (s == nil) + break; + } + return s; +} + +static ulong +getfloat(char *p, int dp) +{ + ulong total; + int afterdp = -1; + + total = 0; + for (; *p; p++) + if (*p == '.') + afterdp = 0; + else { + total = total*10 + *p - '0'; + if (afterdp >= 0) + afterdp++; + } + if (afterdp < 0) + afterdp = 0; + while (afterdp > dp + 1) { + afterdp--; + total /= 10; + } + if (afterdp > dp) { + afterdp--; + total = (total + 5) / 10; + } + while (dp > afterdp) { + afterdp++; + total *= 10; + } + return total; +} + +static int +apcgetstatus(void) +{ + char resp[10]; + ulong status, change; + + do { + change = apc.status.change; + if (apccmdresponse('Q', resp, sizeof(resp)) == nil) + return 0; + } while (apc.status.change != change); + status = strtoul(resp, 0, 16); + if (status&(1 << 3) && apc.status.bits&OnBattery) { /* online? */ + apc.status.bits &= ~OnBattery; + apc.status.change |= OnBattery; + } + if (status&(1 << 4) && apc.status.bits&OnBattery) { /* on battery */ +// apc.status.bits |= OnBattery; + apc.status.change |= OnBattery; + } + if (((status&(1 << 6)) != 0) != ((apc.status.bits&LowBattery) != 0)) { + /* low battery */ + apc.status.bits ^= LowBattery; + apc.status.change |= LowBattery; + } + if (apccmdresponse('f', resp, sizeof(resp)) == nil) + return 0; + apc.battpct = getfloat(resp, 1); + return 1; +} + +/* + * shutdown the file server gracefully. + */ +static void +apcshuffle(char *why) +{ + char resp[10]; + + print("Shutting down due to %s\n", why); + wlock(&mainlock); /* don't process incoming requests from net */ + sync("powerfail"); + apccmdstrresponse("@000", resp, sizeof(resp)); + print("APC responded: '%s'\n", resp); + print("File server is now idling.\n"); + delay(2000); + splhi(); + for (;;) + idle(); /* wait for the lights to go out */ +} + +static void +apckick(void) +{ + if (apc.detected) { /* don't blather once per minute */ + print("No APC ups detected\n"); + apc.detected = 0; + } + apc.kicked = 0; + tsleep(&apc.doze, kicked, 0, 1 * 60 * 1000); +} + +static void +apcsetup(int reinit) +{ + int dead; + Capability *cap; + + if (reinit) + apckick(); + do { + while (!apcattention()) + apckick(); + + apcputc(1); + apcgets(apc.model, sizeof(apc.model), -1); + print("APC UPS model: %s\n", apc.model); + + apcputc('b'); + apcgets(apc.fwrev, sizeof(apc.fwrev), -1); + print("Firmware revision: %s\n", apc.fwrev); + + apcputc(''); + apcgets(apc.user.resp, sizeof(apc.user.resp), -1); + parsecap(apc.user.resp, apc.fwrev[strlen(apc.fwrev) - 1]); + + for (cap = apc.cap; cap; cap = cap->next) { + int i; + + print("%c %d", cap->cmd, cap->n); + for (i = 0; i < cap->n; i++) + print(" %s", cap->val[i]); + print("\n"); + } + + apc.status.change = 0; + dead = 0; + if (!apcgetstatus()) { + apckick(); + dead = 1; + } + } while (dead); +} + +static void +apcbatton(void) +{ + Timet now, nextreport; + + now = MACHP(0)->ticks; + if (apc.status.change & OnBattery) { + apc.lastrepticks = apc.battonticks = nextreport = now; + apc.battpctthen = apc.battpct; + } else + nextreport = apc.lastrepticks + MS2TK(30 * 1000); + if (now - nextreport >= 0) { + print("apc: on battery %lud seconds (%lud.%lud%%)", + TK2SEC(now - apc.battonticks), + apc.battpct / 10, apc.battpct % 10); + if (apc.battpct < apc.battpctthen - 10) { + Timet remaining = ((apc.battpct - apc.trigger) * + TK2SEC(now - apc.battonticks)) / + (apc.battpctthen - apc.battpct); + + print(" - estimated %lud seconds left", remaining); + } + print("\n"); + apc.lastrepticks = now; + } + if (apc.battpct <= apc.trigger) + apcshuffle("battery percent too low"); + if (Trustlowbatt && apc.status.bits & LowBattery) + apcshuffle("low battery indicator"); +} + +void +apctask(void) +{ + tsleep(&apc.doze, kicked, 0, 10 * 1000); + + /* set up the serial port to the UPS */ + DEBUG("apc: running: port %d trigger below %lud%% action %s\n", + apc.port, apc.trigger / 10, + (apc.action == UpsOff? "ups off": "just off")); + apc.rxq.in = apc.rxq.out = apc.rxq.buf; + uartspecial1(apc.port, apcrxint, apctxint, Uartspeed); + + /* + * pretend we've been talking to it so we'll get an + * error message if it's not there. + */ + apc.detected = 1; + apcsetup(First); + for (;;) { + char *s; + int c; + + if ((apc.status.bits & OnBattery)) + apcbatton(); + apc.kicked = 0; + apc.status.change = 0; + + c = apcgetc(10 * 1000, 0); + if (c == Timedout || c == Event) { + if (!apcgetstatus()) + apcsetup(Reinit); + } else if (c == Kicked) { + apc.kicked = 0; + switch (apc.user.cmdtype) { + case General: + s = apccmdresponse(apc.user.cmd, + apc.user.resp, sizeof apc.user.resp); + break; + case Cycle: + s = cyclecmd((Capability *)apc.user.arg1, + (int)apc.user.arg2); + break; + default: + s = nil; + break; + } + apc.user.done = 1; + wakeup(&apc.user); + if (s == nil) + apcsetup(Reinit); + } else + print("apc: unexpected character '%c' (0x%.2ux)\n", + c, c); + } +} + +static void +enquiry(CmdType t, char c, void *arg1, void *arg2) +{ + apc.user.cmdtype = t; + apc.user.cmd = c; + apc.user.arg1 = arg1; + apc.user.arg2 = arg2; + apc.user.done = 0; + apc.kicked = 1; + apc.user.resp[0] = 0; + wakeup(&apc.rxq); + /* + * BUG: can hang here forever if cable to UPS falls out or + * is wired wrong (need a null modem). + */ + sleep(&apc.user, done, &apc.user.done); + if (apc.user.resp[0]) { + print("'%s'\n", apc.user.resp); + apc.user.resp[0] = 0; + } +} + +static struct { + char ch; + char *cmd; +} generalenquiries[] = { + { '', "capabilities" }, + { 'B', "batteryvolts" }, + { 'C', "temperature" }, + { 'E', "selftestinterval" }, + { 'F', "frequency" }, + { 'L', "lineinvolts" }, + { 'M', "maxlineinvolts" }, + { 'N', "minlineinvolts" }, + { 'O', "lineoutvolts" }, + { 'P', "powerload" }, + { 'Q', "status" }, + { 'V', "firmware" }, + { 'X', "selftestresults" }, + { 'a', "protocol" }, + { 'b', "localid" }, + { 'e', "returnthresh" }, + { 'g', "nominalbatteryvolts" }, + { 'f', "battpct" }, + { 'h', "humidity" }, + { 'i', "contacts" }, + { 'j', "runtime" }, + { 'k', "alarmdelay" }, + { 'l', "lowtransfervolts" }, + { 'm', "manufactured" }, + { 'n', "serial" }, + { 'o', "onbatteryvolts" }, + { 'p', "grace" }, + { 'q', "lowbatterywarntime" }, + { 'r', "wakeupdelay" }, + { 's', "sensitivity" }, + { 'u', "uppertransfervolts" }, + { 'x', "lastbatterychange" }, + { 'y', "copyright" }, + { '~', "register1" }, + { ''', "register2" }, + { '7', "switches" }, + { '8', "register3" }, + { '9', "linequality" }, + { '>', "batterypacks" }, + { '-', "cycle" }, +}; + +int +vaguelyequal(char *a, char *b) +{ + return strcmp(a, b) == 0; +} + +static void +cycle(char *name, char *val) +{ + int g, i; + Capability *cap; + + if (strcmp(name, "trigger") == 0) { + apc.trigger = getfloat(val, 1); + return; + } + + /* convert name to enquiry */ + for (g = 0; g < nelem(generalenquiries); g++) + if (strcmp(name, generalenquiries[g].cmd) == 0) + break; + if (g >= nelem(generalenquiries)) { + print("no such parameter '%s'\n", name); + return; + } + /* match enquiry to capability */ + for (cap = apc.cap; cap; cap = cap->next) + if (cap->cmd == generalenquiries[g].ch) + break; + if (cap == nil) { + print("parameter %s cannot be set\n", name); + return; + } + /* search capability's legal values */ + for (i = 0; i < cap->n; i++) + if (vaguelyequal(cap->val[i], val)) + break; + if (i >= cap->n) { + print("%s: illegal value %s; try one of [", name, val); + for (i = 0; i < cap->n; i++) { + if (i > 0) + print(" "); + print("%s", cap->val[i]); + } + print("]\n"); + } else + enquiry(Cycle, cap->cmd, cap, (void *)i); +} + +void +cmd_apc(int argc, char *argv[]) +{ + int i, x; + + if(argc <= 1) { + print("apc kick -- play now\n"); + print("apc set var val -- set var to val\n"); + print("apc enquiry... -- query the ups\n"); + return; + } + for (i = 1; i < argc; i++) { + if(strcmp(argv[i], "kick") == 0) { + apc.kicked = 1; + wakeup(&apc.doze); + continue; + } + if(strcmp(argv[i], "set") == 0) { + if (argc - i >= 3) + cycle(argv[i + 1], argv[i + 2]); + i += 2; + continue; + } + for (x = 0; x < nelem(generalenquiries); x++) + if (strcmp(argv[i], generalenquiries[x].cmd) == 0) + break; + if (x < nelem(generalenquiries)) + enquiry(General, generalenquiries[x].ch, nil, nil); + else { + print("no such parameter '%s'\n", argv[i]); + return; + } + } +} + +void +apcinit(void) +{ + ISAConf isa; + int o; + + print("apcinit..."); + memset(&isa, 0, sizeof isa); /* prevent surprises */ + isa.port = 1; /* default port */ + if (!isaconfig("ups", 0, &isa) || strcmp(isa.type, "apc") != 0) { + print("no ups in plan9.ini, or not type `apc'\n"); + return; + } + + cmd_install("apc", "subcommand -- apc ups driver", cmd_apc); + apc.flag = flag_install("apc", "-- verbose"); + + apc.trigger = 1000; + apc.action = UpsOff; + + for (o = 0; o < isa.nopt; o++) + if (cistrncmp(isa.opt[o], "trigger=", 8) == 0) + apc.trigger = strtoul(isa.opt[o] + 8, 0, 0) * 10; + else if (cistrncmp(isa.opt[o], "action=", 7) == 0) { + if (strcmp(isa.opt[o] + 8, "off") == 0) + apc.action = JustOff; + } + + apc.port = isa.port; + /* + * it's a little early to be starting this, before config mode is + * even started. + */ + print("apc...\n"); + userinit(apctask, 0, "apc"); +} diff -Nru /sys/src/fs/dev/cw.c /sys/src/fs/dev/cw.c --- /sys/src/fs/dev/cw.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/dev/cw.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,2271 @@ +#include "all.h" + +#define DEBUG 0 +#define FIRST SUPER_ADDR + +#define ADDFREE (100) +#define CACHE_ADDR SUPER_ADDR +#define MAXAGE 10000 + +#define CDEV(d) (d->cw.c) +#define WDEV(d) (d->cw.w) +#define RDEV(d) (d->cw.ro) + +/* cache state */ +enum +{ + /* states -- beware these are recorded on the cache */ + /* cache worm */ + Cnone = 0, /* 0 ? */ + Cdirty, /* 1 0 */ + Cdump, /* 1 0->1 */ + Cread, /* 1 1 */ + Cwrite, /* 2 1 */ + Cdump1, /* inactive form of dump */ + Cerror, + + /* opcodes -- these are not recorded */ + Onone, + Oread, + Owrite, + Ogrow, + Odump, + Orele, + Ofree, +}; + +typedef struct Cw Cw; +struct Cw +{ + Device* dev; + Device* cdev; + Device* wdev; + Device* rodev; + Cw* link; + + Filter ncwio[3]; + int dbucket; /* last bucket dumped */ + Off daddr; /* last block dumped */ + Off ncopy; + int nodump; +/* + * following are cached variables for dumps + */ + Off fsize; + Off ndump; + int depth; + int all; /* local flag to recur on modified directories */ + int allflag; /* global flag to recur on modified directories */ + Off falsehits; /* times recur found modified blocks */ + struct + { + char name[500]; + char namepad[NAMELEN+10]; + }; +}; + +static +char* cwnames[] = +{ + [Cnone] "none", + [Cdirty] "dirty", + [Cdump] "dump", + [Cread] "read", + [Cwrite] "write", + [Cdump1] "dump1", + [Cerror] "error", + + [Onone] "none", + [Oread] "read", + [Owrite] "write", + [Ogrow] "grow", + [Odump] "dump", + [Orele] "rele", +}; + +Centry* getcentry(Bucket*, Off); +int cwio(Device*, Off, void*, int); +void cmd_cwcmd(int, char*[]); + +/* + * console command + * initiate a dump + */ +void +cmd_dump(int argc, char *argv[]) +{ + Filsys *fs; + + fs = cons.curfs; + if(argc > 1) + fs = fsstr(argv[1]); + if(fs == 0) { + print("%s: unknown file system\n", argv[1]); + return; + } + cfsdump(fs); +} + +/* + * console command + * worm stats + */ +static +void +cmd_statw(int, char*[]) +{ + Filsys *fs; + Iobuf *p; + Superb *sb; + Cache *h; + Bucket *b; + Centry *c, *ce; + Off m, nw, bw, state[Onone]; + Off sbfsize, sbcwraddr, sbroraddr, sblast, sbnext; + Off hmsize, hmaddr, dsize, dsizepct; + Device *dev; + Cw *cw; + int s; + + fs = cons.curfs; + dev = fs->dev; + if(dev->type != Devcw) { + print("curfs not type cw\n"); + return; + } + + cw = dev->private; + if(cw == 0) { + print("curfs not inited\n"); + return; + } + + print("cwstats %s\n", fs->name); + + sbfsize = 0; + sbcwraddr = 0; + sbroraddr = 0; + sblast = 0; + sbnext = 0; + + print(" filesys %s\n", fs->name); + print(" nio =%7W%7W%7W\n", cw->ncwio+0, cw->ncwio+1, cw->ncwio+2); + p = getbuf(dev, cwsaddr(dev), Bread); + if(!p || checktag(p, Tsuper, QPSUPER)) { + print("cwstats: checktag super\n"); + if(p) { + putbuf(p); + p = 0; + } + } + if(p) { + sb = (Superb*)p->iobuf; + sbfsize = sb->fsize; + sbcwraddr = sb->cwraddr; + sbroraddr = sb->roraddr; + sblast = sb->last; + sbnext = sb->next; + putbuf(p); + } + + p = getbuf(cw->cdev, CACHE_ADDR, Bread|Bres); + if(!p || checktag(p, Tcache, QPSUPER)) { + print("cwstats: checktag c bucket\n"); + if(p) + putbuf(p); + return; + } + h = (Cache*)p->iobuf; + hmaddr = h->maddr; + hmsize = h->msize; + + print(" maddr = %8lld\n", (Wideoff)hmaddr); + print(" msize = %8lld\n", (Wideoff)hmsize); + print(" caddr = %8lld\n", (Wideoff)h->caddr); + print(" csize = %8lld\n", (Wideoff)h->csize); + print(" sbaddr = %8lld\n", (Wideoff)h->sbaddr); + print(" craddr = %8lld %8lld\n", (Wideoff)h->cwraddr, (Wideoff)sbcwraddr); + print(" roaddr = %8lld %8lld\n", (Wideoff)h->roraddr, (Wideoff)sbroraddr); + /* print stats in terms of (first-)disc sides */ + dsize = wormsizeside(dev, 0); + if (dsize < 1) { + if (DEBUG) + print("wormsizeside returned size %lld for %Z side 0\n", + (Wideoff)dsize, dev); + dsize = h->wsize; /* it's probably a fake worm */ + if (dsize < 1) + dsize = 1000; /* don't divide by zero */ + } + dsizepct = dsize/100; + print(" fsize = %8lld %8lld %2lld+%2lld%%\n", (Wideoff)h->fsize, + (Wideoff)sbfsize, (Wideoff)h->fsize/dsize, + (Wideoff)(h->fsize%dsize)/dsizepct); + print(" slast = %8lld\n", (Wideoff)sblast); + print(" snext = %8lld\n", (Wideoff)sbnext); + print(" wmax = %8lld %2lld+%2lld%%\n", (Wideoff)h->wmax, + (Wideoff)h->wmax/dsize, + (Wideoff)(h->wmax%dsize)/dsizepct); + print(" wsize = %8lld %2lld+%2lld%%\n", (Wideoff)h->wsize, + (Wideoff)h->wsize/dsize, + (Wideoff)(h->wsize%dsize)/dsizepct); + putbuf(p); + + bw = 0; /* max filled bucket */ + memset(state, 0, sizeof(state)); + for(m=0; mcdev, hmaddr + m/BKPERBLK, Bread); + if(!p || checktag(p, Tbuck, hmaddr + m/BKPERBLK)) { + print("cwstats: checktag c bucket\n"); + if(p) + putbuf(p); + return; + } + b = (Bucket*)p->iobuf + m%BKPERBLK; + ce = b->entry + CEPERBK; + nw = 0; + for(c=b->entry; cstate; + state[s]++; + if(s != Cnone && s != Cread) + nw++; + } + putbuf(p); + if(nw > bw) + bw = nw; + } + for(s=Cnone; sprivate; + if(cw == 0 || cw->nodump) + return 0; + + cb = getbuf(cw->cdev, CACHE_ADDR, Bread|Bres); + h = (Cache*)cb->iobuf; + msize = h->msize; + maddr = h->maddr; + wmax = h->wmax; + caddr = h->caddr; + putbuf(cb); + + for(m=msize; m>=0; m--) { + a = cw->dbucket + 1; + if(a < 0 || a >= msize) + a = 0; + cw->dbucket = a; + p = getbuf(cw->cdev, maddr + a/BKPERBLK, Bread); + b = (Bucket*)p->iobuf + a%BKPERBLK; + ce = b->entry + CEPERBK; + bc = 0; + for(c=b->entry; cstate == Cdump) { + if(bc == 0) { + bc = c; + continue; + } + if(c->waddr < cw->daddr) { + if(bc->waddr < cw->daddr && + bc->waddr > c->waddr) + bc = c; + continue; + } + if(bc->waddr < cw->daddr || + bc->waddr > c->waddr) + bc = c; + } + if(bc) { + c = bc; + goto found; + } + putbuf(p); + } + if(cw->ncopy) { + print("%lld blocks copied to worm\n", (Wideoff)cw->ncopy); + cw->ncopy = 0; + } + cw->nodump = 1; + return 0; + +found: + a = a*CEPERBK + (c - b->entry) + caddr; + p1 = getbuf(devnone, Cwdump1, 0); + count = 0; + +retry: + count++; + if(count > 10) + goto stop; + if(devread(cw->cdev, a, p1->iobuf)) + goto stop; + m = c->waddr; + cw->daddr = m; + s1 = devwrite(cw->wdev, m, p1->iobuf); + if(s1) { + p2 = getbuf(devnone, Cwdump2, 0); + s2 = devread(cw->wdev, m, p2->iobuf); + if(s2) { + if(s1 == 0x61 && s2 == 0x60) { + putbuf(p2); + goto retry; + } + goto stop1; + } + if(memcmp(p1->iobuf, p2->iobuf, RBUFSIZE)) + goto stop1; + putbuf(p2); + } + /* + * reread and compare + */ + if(conf.dumpreread) { + p2 = getbuf(devnone, Cwdump2, 0); + s1 = devread(cw->wdev, m, p2->iobuf); + if(s1) + goto stop1; + if(memcmp(p1->iobuf, p2->iobuf, RBUFSIZE)) { + print("reread C%lld W%lld didnt compare\n", (Wideoff)a, (Wideoff)m); + goto stop1; + } + putbuf(p2); + } + + putbuf(p1); + c->state = Cread; + p->flags |= Bmod; + putbuf(p); + + if(m > wmax) { + cb = getbuf(cw->cdev, CACHE_ADDR, Bread|Bmod|Bres); + h = (Cache*)cb->iobuf; + if(m > h->wmax) + h->wmax = m; + putbuf(cb); + } + cw->ncopy++; + return 1; + +stop1: + putbuf(p2); + putbuf(p1); + c->state = Cdump1; + p->flags |= Bmod; + putbuf(p); + return 1; + +stop: + putbuf(p1); + putbuf(p); + print("stopping dump!!\n"); + cw->nodump = 1; + return 0; +} + +void +cwinit1(Device *dev) +{ + Cw *cw; + static int first; + + cw = dev->private; + if(cw) + return; + + if(first == 0) { + cmd_install("dump", "-- make dump backup to worm", cmd_dump); + cmd_install("statw", "-- cache/worm stats", cmd_statw); + cmd_install("cwcmd", "subcommand -- cache/worm errata", cmd_cwcmd); + roflag = flag_install("ro", "-- ro reads and writes"); + first = 1; + } + cw = ialloc(sizeof(Cw), 0); + dev->private = cw; + + cw->allflag = 0; + dofilter(cw->ncwio+0, C0a, C0b, 1); + dofilter(cw->ncwio+1, C1a, C1b, 1); + dofilter(cw->ncwio+2, C2a, C2b, 1); + + cw->dev = dev; + cw->cdev = CDEV(dev); + cw->wdev = WDEV(dev); + cw->rodev = RDEV(dev); + + devinit(cw->cdev); + devinit(cw->wdev); +} + +void +cwinit(Device *dev) +{ + Cw *cw; + Cache *h; + Iobuf *cb, *p; + Off l, m; + + cwinit1(dev); + + cw = dev->private; + l = devsize(cw->wdev); + cb = getbuf(cw->cdev, CACHE_ADDR, Bread|Bmod|Bres); + h = (Cache*)cb->iobuf; + h->toytime = toytime() + SECOND(30); + h->time = time(); + m = h->wsize; + if(l != m) { + print("wdev changed size %lld to %lld\n", (Wideoff)m, (Wideoff)l); + h->wsize = l; + cb->flags |= Bmod; + } + + for(m=0; mmsize; m++) { + p = getbuf(cw->cdev, h->maddr + m/BKPERBLK, Bread); + if(!p || checktag(p, Tbuck, h->maddr + m/BKPERBLK)) + panic("cwinit: checktag c bucket"); + putbuf(p); + } + putbuf(cb); +} + +Off +cwsaddr(Device *dev) +{ + Iobuf *cb; + Off sa; + + cb = getbuf(CDEV(dev), CACHE_ADDR, Bread|Bres); + sa = ((Cache*)cb->iobuf)->sbaddr; + putbuf(cb); + return sa; +} + +Off +cwraddr(Device *dev) +{ + Iobuf *cb; + Off ra; + + switch(dev->type) { + default: + print("unknown dev in cwraddr %Z\n", dev); + return 1; + + case Devcw: + cb = getbuf(CDEV(dev), CACHE_ADDR, Bread|Bres); + ra = ((Cache*)cb->iobuf)->cwraddr; + break; + + case Devro: + cb = getbuf(CDEV(dev->ro.parent), CACHE_ADDR, Bread|Bres); + ra = ((Cache*)cb->iobuf)->roraddr; + break; + } + putbuf(cb); + return ra; +} + +Devsize +cwsize(Device *dev) +{ + Iobuf *cb; + Devsize fs; + + cb = getbuf(CDEV(dev), CACHE_ADDR, Bread|Bres); + fs = ((Cache*)cb->iobuf)->fsize; + putbuf(cb); + return fs; +} + +int +cwread(Device *dev, Off b, void *c) +{ + return cwio(dev, b, c, Oread) == Cerror; +} + +int +cwwrite(Device *dev, Off b, void *c) +{ + return cwio(dev, b, c, Owrite) == Cerror; +} + +int +roread(Device *dev, Off b, void *c) +{ + Device *d; + int s; + + /* + * maybe better is to try buffer pool first + */ + d = dev->ro.parent; + if(d == 0 || d->type != Devcw || + d->private == 0 || RDEV(d) != dev) { + print("bad rodev %Z\n", dev); + return 1; + } + s = cwio(d, b, 0, Onone); + if(s == Cdump || s == Cdump1 || s == Cread) { + s = cwio(d, b, c, Oread); + if(s == Cdump || s == Cdump1 || s == Cread) { + if(cons.flags & roflag) + print("roread: %Z %lld -> %Z(hit)\n", dev, (Wideoff)b, d); + return 0; + } + } + if(cons.flags & roflag) + print("roread: %Z %lld -> %Z(miss)\n", dev, (Wideoff)b, WDEV(d)); + return devread(WDEV(d), b, c); +} + +int +cwio(Device *dev, Off addr, void *buf, int opcode) +{ + Iobuf *p, *p1, *p2, *cb; + Cache *h; + Bucket *b; + Centry *c; + Off bn, a1, a2, max, newmax; + int state; + Cw *cw; + + cw = dev->private; + cw->ncwio[0].count++; + cw->ncwio[1].count++; + cw->ncwio[2].count++; + + cb = getbuf(cw->cdev, CACHE_ADDR, Bread|Bres); + h = (Cache*)cb->iobuf; + if(toytime() >= h->toytime) { + cb->flags |= Bmod; + h->toytime = toytime() + SECOND(30); + h->time = time(); + } + + if(addr < 0) { + putbuf(cb); + return Cerror; + } + + bn = addr % h->msize; + a1 = h->maddr + bn/BKPERBLK; + a2 = bn*CEPERBK + h->caddr; + max = h->wmax; + + putbuf(cb); + newmax = 0; + + p = getbuf(cw->cdev, a1, Bread|Bmod); + if(!p || checktag(p, Tbuck, a1)) + panic("cwio: checktag c bucket"); + b = (Bucket*)p->iobuf + bn%BKPERBLK; + + c = getcentry(b, addr); + if(c == 0) { + putbuf(p); + print("%Z disk cache bucket %lld is full\n", cw->cdev, (Wideoff)a1); + return Cerror; + } + a2 += c - b->entry; + + state = c->state; + switch(opcode) + { + default: + goto bad; + + case Onone: + break; + + case Oread: + switch(state) { + default: + goto bad; + + case Cread: + if(!devread(cw->cdev, a2, buf)) + break; + c->state = Cnone; + + case Cnone: + if(devread(cw->wdev, addr, buf)) { + state = Cerror; + break; + } + if(addr > max) + newmax = addr; + if(!devwrite(cw->cdev, a2, buf)) + c->state = Cread; + break; + + case Cdirty: + case Cdump: + case Cdump1: + case Cwrite: + if(devread(cw->cdev, a2, buf)) + state = Cerror; + break; + } + break; + + case Owrite: + switch(state) { + default: + goto bad; + + case Cdump: + case Cdump1: + /* + * this is hard part -- a dump block must be + * sent to the worm if it is rewritten. + * if this causes an error, there is no + * place to save the dump1 data. the block + * is just reclassified as 'dump1' (botch) + */ + p1 = getbuf(devnone, Cwio1, 0); + if(devread(cw->cdev, a2, p1->iobuf)) { + putbuf(p1); + print("cwio: write induced dump error - r cache\n"); + + casenone: + if(devwrite(cw->cdev, a2, buf)) { + state = Cerror; + break; + } + c->state = Cdump1; + break; + } + if(devwrite(cw->wdev, addr, p1->iobuf)) { + p2 = getbuf(devnone, Cwio2, 0); + if(devread(cw->wdev, addr, p2->iobuf)) { + putbuf(p1); + putbuf(p2); + print("cwio: write induced dump error - r+w worm\n"); + goto casenone; + } + if(memcmp(p1->iobuf, p2->iobuf, RBUFSIZE)) { + putbuf(p1); + putbuf(p2); + print("cwio: write induced dump error - w worm\n"); + goto casenone; + } + putbuf(p2); + } + putbuf(p1); + c->state = Cread; + if(addr > max) + newmax = addr; + cw->ncopy++; + + case Cnone: + case Cread: + if(devwrite(cw->cdev, a2, buf)) { + state = Cerror; + break; + } + c->state = Cwrite; + break; + + case Cdirty: + case Cwrite: + if(devwrite(cw->cdev, a2, buf)) + state = Cerror; + break; + } + break; + + case Ogrow: + if(state != Cnone) { + print("%Z for block %lld cwgrow with state = %s\n", + cw->cdev, (Wideoff)addr, cwnames[state]); + break; + } + c->state = Cdirty; + break; + + case Odump: + if(state != Cdirty) { /* BOTCH */ + print("%Z for block %lld cwdump with state = %s\n", + cw->cdev, (Wideoff)addr, cwnames[state]); + break; + } + c->state = Cdump; + cw->ndump++; /* only called from dump command */ + break; + + case Orele: + if(state != Cwrite) { + if(state != Cdump1) + print("%Z for block %lld cwrele with state = %s\n", + cw->cdev, (Wideoff)addr, cwnames[state]); + break; + } + c->state = Cnone; + break; + + case Ofree: + if(state == Cwrite || state == Cread) + c->state = Cnone; + break; + } + if(DEBUG) + print("cwio: %Z %lld s=%s o=%s ns=%s\n", + dev, (Wideoff)addr, cwnames[state], + cwnames[opcode], + cwnames[c->state]); + putbuf(p); + if(newmax) { + cb = getbuf(cw->cdev, CACHE_ADDR, Bread|Bmod|Bres); + h = (Cache*)cb->iobuf; + if(newmax > h->wmax) + h->wmax = newmax; + putbuf(cb); + } + return state; + +bad: + print("%Z block %lld cw state = %s; cw opcode = %s", + dev, (Wideoff)addr, cwnames[state], cwnames[opcode]); + return Cerror; +} + +extern Filsys* dev2fs(Device *dev); + +int +cwgrow(Device *dev, Superb *sb, int uid) +{ + char str[NAMELEN]; + Iobuf *cb; + Cache *h; + Filsys *filsys; + Off fs, nfs, ws; + + cb = getbuf(CDEV(dev), CACHE_ADDR, Bread|Bmod|Bres); + h = (Cache*)cb->iobuf; + ws = h->wsize; + fs = h->fsize; + if(fs >= ws) + return 0; + nfs = fs + ADDFREE; + if(nfs >= ws) + nfs = ws; + h->fsize = nfs; + putbuf(cb); + + sb->fsize = nfs; + filsys = dev2fs(dev); + if (filsys == nil) + print("%Z", dev); + else + print("%s", filsys->name); + uidtostr(str, uid, 1); + print(" grow from %lld to %lld limit %lld by %s uid=%d\n", + (Wideoff)fs, (Wideoff)nfs, (Wideoff)ws, str, uid); + for(nfs--; nfs>=fs; nfs--) { + switch(cwio(dev, nfs, 0, Ogrow)) { + case Cerror: + return 0; + case Cnone: + addfree(dev, nfs, sb); + } + } + return 1; +} + +int +cwfree(Device *dev, Off addr) +{ + int state; + + if(dev->type == Devcw) { + state = cwio(dev, addr, 0, Ofree); + if(state != Cdirty) + return 1; /* do not put in freelist */ + } + return 0; /* put in freelist */ +} + +int +bktcheck(Bucket *b) +{ + Centry *c, *c1, *c2, *ce; + int err; + + err = 0; + if(b->agegen < CEPERBK || b->agegen > MAXAGE) { + print("agegen %ld\n", b->agegen); + err = 1; + } + + ce = b->entry + CEPERBK; + c1 = 0; /* lowest age last pass */ + for(;;) { + c2 = 0; /* lowest age this pass */ + for(c = b->entry; c < ce; c++) { + if(c1 != 0 && c != c1) { + if(c->age == c1->age) { + print("same age %d\n", c->age); + err = 1; + } + if(c1->waddr == c->waddr) + if(c1->state != Cnone) + if(c->state != Cnone) { + print("same waddr %lld\n", (Wideoff)c->waddr); + err = 1; + } + } + if(c1 != 0 && c->age <= c1->age) + continue; + if(c2 == 0 || c->age < c2->age) + c2 = c; + } + if(c2 == 0) + break; + c1 = c2; + if(c1->age >= b->agegen) { + print("age >= generator %d %ld\n", c1->age, b->agegen); + err = 1; + } + } + return err; +} + +void +resequence(Bucket *b) +{ + Centry *c, *ce, *cr; + int age, i; + + ce = b->entry + CEPERBK; + for(c = b->entry; c < ce; c++) { + c->age += CEPERBK; + if(c->age < CEPERBK) + c->age = MAXAGE; + } + b->agegen += CEPERBK; + + age = 0; + for(i=0;; i++) { + cr = 0; + for(c = b->entry; c < ce; c++) { + if(c->age < i) + continue; + if(cr == 0 || c->age < age) { + cr = c; + age = c->age; + } + } + if(cr == 0) + break; + cr->age = i; + } + b->agegen = i; + cons.nreseq++; +} + +Centry* +getcentry(Bucket *b, Off addr) +{ + Centry *c, *ce, *cr; + int s, age; + + /* + * search for cache hit + * find oldest block as byproduct + */ + ce = b->entry + CEPERBK; + age = 0; + cr = 0; + for(c = b->entry; c < ce; c++) { + s = c->state; + if(s == Cnone) { + cr = c; + age = 0; + continue; + } + if(c->waddr == addr) + goto found; + if(s == Cread) { + if(cr == 0 || c->age < age) { + cr = c; + age = c->age; + } + } + } + + /* + * remap entry + */ + c = cr; + if(c == 0) + return 0; /* bucket is full */ + + c->state = Cnone; + c->waddr = addr; + +found: + /* + * update the age to get filo cache. + * small number in age means old + */ + if(!cons.noage || c->state == Cnone) { + age = b->agegen; + c->age = age; + age++; + b->agegen = age; + if(age < 0 || age >= MAXAGE) + resequence(b); + } + return c; +} + +/* + * ream the cache + * calculate new buckets + */ +Iobuf* +cacheinit(Device *dev) +{ + Iobuf *cb, *p; + Cache *h; + Device *cdev; + Off m; + + print("cache init %Z\n", dev); + cdev = CDEV(dev); + devinit(cdev); + + cb = getbuf(cdev, CACHE_ADDR, Bmod|Bres); + memset(cb->iobuf, 0, RBUFSIZE); + settag(cb, Tcache, QPSUPER); + h = (Cache*)cb->iobuf; + + /* + * calculate csize such that + * tsize = msize/BKPERBLK + csize and + * msize = csize/CEPERBK + */ + h->maddr = CACHE_ADDR + 1; + m = devsize(cdev) - h->maddr; + h->csize = ((Devsize)(m-1) * CEPERBK*BKPERBLK) / (CEPERBK*BKPERBLK+1); + h->msize = h->csize/CEPERBK - 5; + while(!prime(h->msize)) + h->msize--; + h->csize = h->msize*CEPERBK; + h->caddr = h->maddr + (h->msize+BKPERBLK-1)/BKPERBLK; + h->wsize = devsize(WDEV(dev)); + + if(h->msize <= 0) + panic("cache too small"); + if(h->caddr + h->csize > m) + panic("cache size error"); + + /* + * setup cache map + */ + for(m=h->maddr; mcaddr; m++) { + p = getbuf(cdev, m, Bmod); + memset(p->iobuf, 0, RBUFSIZE); + settag(p, Tbuck, m); + putbuf(p); + } + print("done cacheinit\n"); + return cb; +} + +Off +getstartsb(Device *dev) +{ + Filsys *f; + Startsb *s; + + for(f=filsys; f->name; f++) + if(devcmpr(f->dev, dev) == 0) { + for(s=startsb; s->name; s++) + if(strcmp(f->name, s->name) == 0) + return s->startsb; +print("getstartsb: no special starting superblock for %Z %s\n", dev, f->name); + return FIRST; + } +print("getstartsb: no filsys for device %Z\n", dev); + return FIRST; +} + +/* + * ream the cache + * calculate new buckets + * get superblock from + * last worm dump block. + */ +void +cwrecover(Device *dev) +{ + Iobuf *p, *cb; + Cache *h; + Superb *s; + Off m, baddr; + Device *wdev; + + cwinit1(dev); + wdev = WDEV(dev); + + p = getbuf(devnone, Cwxx1, 0); + s = (Superb*)p->iobuf; + baddr = 0; + m = getstartsb(dev); + localconfinit(); + if(conf.firstsb) + m = conf.firstsb; + for(;;) { + memset(p->iobuf, 0, RBUFSIZE); + if(devread(wdev, m, p->iobuf) || + checktag(p, Tsuper, QPSUPER)) + break; + baddr = m; + m = s->next; + print("dump %lld is good; %lld next\n", (Wideoff)baddr, (Wideoff)m); + if(baddr == conf.recovsb) + break; + } + putbuf(p); + if(!baddr) + panic("recover: no superblock\n"); + + p = getbuf(wdev, baddr, Bread); + s = (Superb*)p->iobuf; + + cb = cacheinit(dev); + h = (Cache*)cb->iobuf; + h->sbaddr = baddr; + h->cwraddr = s->cwraddr; + h->roraddr = s->roraddr; + h->fsize = s->fsize + 100; /* this must be conservative */ + if(conf.recovcw) + h->cwraddr = conf.recovcw; + if(conf.recovro) + h->roraddr = conf.recovro; + + putbuf(cb); + putbuf(p); + + p = getbuf(dev, baddr, Bread|Bmod); + s = (Superb*)p->iobuf; +/* TODO: check s->magic byte order, arrange autoswabbing */ + + memset(&s->fbuf, 0, sizeof(s->fbuf)); + s->fbuf.free[0] = 0; + s->fbuf.nfree = 1; + s->tfree = 0; + if(conf.recovcw) + s->cwraddr = conf.recovcw; + if(conf.recovro) + s->roraddr = conf.recovro; + + putbuf(p); + print("done recover\n"); +} + +/* + * ream the cache + * calculate new buckets + * initialize superblock. + */ +void +cwream(Device *dev) +{ + Iobuf *p, *cb; + Cache *h; + Superb *s; + Off m, baddr; + Device *cdev; + + print("cwream %Z\n", dev); + cwinit1(dev); + cdev = CDEV(dev); + devinit(cdev); + + baddr = FIRST; /* baddr = super addr + baddr+1 = cw root + baddr+2 = ro root + baddr+3 = reserved next superblock */ + + cb = cacheinit(dev); + h = (Cache*)cb->iobuf; + + h->sbaddr = baddr; + h->cwraddr = baddr+1; + h->roraddr = baddr+2; + h->fsize = 0; /* prevents superream from freeing */ + + putbuf(cb); + + for(m=0; m<3; m++) + cwio(dev, baddr+m, 0, Ogrow); + superream(dev, baddr); + rootream(dev, baddr+1); /* cw root */ + rootream(dev, baddr+2); /* ro root */ + + cb = getbuf(cdev, CACHE_ADDR, Bread|Bmod|Bres); + h = (Cache*)cb->iobuf; + h->fsize = baddr+4; + putbuf(cb); + + p = getbuf(dev, baddr, Bread|Bmod|Bimm); + s = (Superb*)p->iobuf; + s->last = baddr; + s->cwraddr = baddr+1; + s->roraddr = baddr+2; + s->next = baddr+3; + s->fsize = baddr+4; +#ifdef AUTOSWAB + s->magic = 0x123456789abcdef0; +#endif + putbuf(p); + + for(m=0; m<3; m++) + cwio(dev, baddr+m, 0, Odump); +} + +Off +rewalk1(Cw *cw, Off addr, int slot, Wpath *up) +{ + Iobuf *p, *p1; + Dentry *d; + + if(up == 0) + return cwraddr(cw->dev); + up->addr = rewalk1(cw, up->addr, up->slot, up->up); + p = getbuf(cw->dev, up->addr, Bread|Bmod); + d = getdir(p, up->slot); + if(!d || !(d->mode & DALLOC)) { + print("rewalk1 1\n"); + if(p) + putbuf(p); + return addr; + } + p1 = dnodebuf(p, d, slot/DIRPERBUF, 0, 0); + if(!p1) { + print("rewalk1 2\n"); + if(p) + putbuf(p); + return addr; + } + if(DEBUG) + print("rewalk1 %lld to %lld \"%s\"\n", + (Wideoff)addr, (Wideoff)p1->addr, d->name); + addr = p1->addr; + p1->flags |= Bmod; + putbuf(p1); + putbuf(p); + return addr; +} + +Off +rewalk2(Cw *cw, Off addr, int slot, Wpath *up) +{ + Iobuf *p, *p1; + Dentry *d; + + if(up == 0) + return cwraddr(cw->rodev); + up->addr = rewalk2(cw, up->addr, up->slot, up->up); + p = getbuf(cw->rodev, up->addr, Bread); + d = getdir(p, up->slot); + if(!d || !(d->mode & DALLOC)) { + print("rewalk2 1\n"); + if(p) + putbuf(p); + return addr; + } + p1 = dnodebuf(p, d, slot/DIRPERBUF, 0, 0); + if(!p1) { + print("rewalk2 2\n"); + if(p) + putbuf(p); + return addr; + } + if(DEBUG) + print("rewalk2 %lld to %lld \"%s\"\n", + (Wideoff)addr, (Wideoff)p1->addr, d->name); + addr = p1->addr; + putbuf(p1); + putbuf(p); + return addr; +} + +void +rewalk(Cw *cw) +{ + int h; + File *f; + + for(h=0; hnext) { + if(!f->fs) + continue; + if(cw->dev == f->fs->dev) + f->addr = rewalk1(cw, f->addr, f->slot, f->wpath); + else + if(cw->rodev == f->fs->dev) + f->addr = rewalk2(cw, f->addr, f->slot, f->wpath); + } + } +} + +Off +split(Cw *cw, Iobuf *p, Off addr) +{ + Off na; + int state; + + na = 0; + if(p && (p->flags & Bmod)) { + p->flags |= Bimm; + putbuf(p); + p = 0; + } + state = cwio(cw->dev, addr, 0, Onone); /* read the state (twice?) */ + switch(state) + { + default: + panic("split: unknown state %s", cwnames[state]); + + case Cerror: + case Cnone: + case Cdump: + case Cread: + break; + + case Cdump1: + case Cwrite: + /* + * botch.. could be done by relabeling + */ + if(!p) { + p = getbuf(cw->dev, addr, Bread); + if(!p) { + print("split: null getbuf\n"); + break; + } + } + na = cw->fsize; + cw->fsize = na+1; + cwio(cw->dev, na, 0, Ogrow); + cwio(cw->dev, na, p->iobuf, Owrite); + cwio(cw->dev, na, 0, Odump); + cwio(cw->dev, addr, 0, Orele); + break; + + case Cdirty: + cwio(cw->dev, addr, 0, Odump); + break; + } + if(p) + putbuf(p); + return na; +} + +int +isdirty(Cw *cw, Iobuf *p, Off addr, int tag) +{ + int s; + + if(p && (p->flags & Bmod)) + return 1; + s = cwio(cw->dev, addr, 0, Onone); + if(s == Cdirty || s == Cwrite) + return 1; + if(tag >= Tind1 && tag <= Tmaxind) + /* botch, get these modified */ + if(s != Cnone) + return 1; + return 0; +} + +Off +cwrecur(Cw *cw, Off addr, int tag, int tag1, long qp) +{ + Iobuf *p; + Dentry *d; + int i, j, shouldstop; + Off na; + char *np; + + shouldstop = 0; + p = getbuf(cw->dev, addr, Bprobe); + if(!isdirty(cw, p, addr, tag)) { + if(!cw->all) { + if(DEBUG) + print("cwrecur: %lld t=%s not dirty %s\n", + (Wideoff)addr, tagnames[tag], cw->name); + if(p) + putbuf(p); + return 0; + } + shouldstop = 1; + } + if(DEBUG) + print("cwrecur: %lld t=%s %s\n", + (Wideoff)addr, tagnames[tag], cw->name); + if(cw->depth >= 100) { + print("dump depth too great %s\n", cw->name); + if(p) + putbuf(p); + return 0; + } + cw->depth++; + + switch(tag) + { + default: + print("cwrecur: unknown tag %d %s\n", tag, cw->name); + + case Tfile: + break; + + case Tsuper: + case Tdir: + if(!p) { + p = getbuf(cw->dev, addr, Bread); + if(!p) { + print("cwrecur: Tdir p null %s\n", + cw->name); + break; + } + } + if(tag == Tdir) { + cw->namepad[0] = 0; /* force room */ + np = strchr(cw->name, 0); + *np++ = '/'; + } else { + np = 0; /* set */ + cw->name[0] = 0; + } + + for(i=0; imode & DALLOC)) + continue; + qp = d->qid.path & ~QPDIR; + if(tag == Tdir) + strncpy(np, d->name, NAMELEN); + else + if(i > 0) + print("cwrecur: root with >1 directory\n"); + tag1 = Tfile; + if(d->mode & DDIR) + tag1 = Tdir; + for(j=0; jdblock[j]; + if(na) { + na = cwrecur(cw, na, tag1, 0, qp); + if(na) { + d->dblock[j] = na; + p->flags |= Bmod; + } + } + } + for (j = 0; j < NIBLOCK; j++) { + na = d->iblocks[j]; + if(na) { + na = cwrecur(cw, na, Tind1+j, tag1, qp); + if(na) { + d->iblocks[j] = na; + p->flags |= Bmod; + } + } + } + } + break; + + case Tind1: + j = tag1; + tag1 = 0; + goto tind; + + case Tind2: +#ifndef OLD + case Tind3: + case Tind4: + /* add more Tind tags here ... */ +#endif + j = tag-1; + tind: + if(!p) { + p = getbuf(cw->dev, addr, Bread); + if(!p) { + print("cwrecur: Tind p null %s\n", cw->name); + break; + } + } + for(i=0; iiobuf)[i]; + if(na) { + na = cwrecur(cw, na, j, tag1, qp); + if(na) { + ((Off *)p->iobuf)[i] = na; + p->flags |= Bmod; + } + } + } + break; + } + na = split(cw, p, addr); + cw->depth--; + if(na && shouldstop) { + if(cw->falsehits < 10) + print("shouldstop %lld %lld t=%s %s\n", + (Wideoff)addr, (Wideoff)na, + tagnames[tag], cw->name); + cw->falsehits++; + } + return na; +} + +Timet nextdump(Timet t); + +void +cfsdump(Filsys *fs) +{ + long m, n, i; + Off orba, rba, oroa, roa, sba, a; + Timet tim; + char tstr[20]; + Iobuf *pr, *p1, *p; + Dentry *dr, *d1, *d; + Cache *h; + Superb *s; + Cw *cw; + + if(fs->dev->type != Devcw) { + print("cant dump; not cw device: %Z\n", fs->dev); + return; + } + cw = fs->dev->private; + if(cw == 0) { + print("cant dump: has not been inited: %Z\n", fs->dev); + return; + } + + tim = toytime(); + wlock(&mainlock); /* dump */ + + /* + * set up static structure + * with frequent variables + */ + cw->ndump = 0; + cw->name[0] = 0; + cw->depth = 0; + + /* + * cw root + */ + sync("before dump"); + cw->fsize = cwsize(cw->dev); + orba = cwraddr(cw->dev); + print("cwroot %lld", (Wideoff)orba); + cons.noage = 1; + cw->all = cw->allflag; + rba = cwrecur(cw, orba, Tsuper, 0, QPROOT); + if(rba == 0) + rba = orba; + print("->%lld\n", (Wideoff)rba); + sync("after cw"); + + /* + * partial super block + */ + p = getbuf(cw->dev, cwsaddr(cw->dev), Bread|Bmod|Bimm); + s = (Superb*)p->iobuf; + s->fsize = cw->fsize; + s->cwraddr = rba; +#ifdef AUTOSWAB + s->magic = 0x123456789abcdef0; +#endif + putbuf(p); + + /* + * partial cache block + */ + p = getbuf(cw->cdev, CACHE_ADDR, Bread|Bmod|Bimm|Bres); + h = (Cache*)p->iobuf; + h->fsize = cw->fsize; + h->cwraddr = rba; + putbuf(p); + + /* + * ro root + */ + oroa = cwraddr(cw->rodev); + pr = getbuf(cw->dev, oroa, Bread|Bmod); + dr = getdir(pr, 0); + + datestr(tstr, time()); /* tstr = "yyyymmdd" */ + n = 0; + for(a=0;; a++) { + p1 = dnodebuf(pr, dr, a, Tdir, 0); + if(!p1) + goto bad; + n++; + for(i=0; imode & DALLOC)) + goto found1; + if(!memcmp(d1->name, tstr, 4)) + goto found2; /* found entry */ + } + putbuf(p1); + } + + /* + * no year directory, create one + */ +found1: + p = getbuf(cw->dev, rba, Bread); + d = getdir(p, 0); + d1->qid = d->qid; + d1->qid.version += n; + memmove(d1->name, tstr, 4); + d1->mode = d->mode; + d1->uid = d->uid; + d1->gid = d->gid; + putbuf(p); + accessdir(p1, d1, FWRITE, 0); + + /* + * put mmdd[count] in year directory + */ +found2: + accessdir(p1, d1, FREAD, 0); + putbuf(pr); + pr = p1; + dr = d1; + + n = 0; + m = 0; + for(a=0;; a++) { + p1 = dnodebuf(pr, dr, a, Tdir, 0); + if(!p1) + goto bad; + n++; + for(i=0; imode & DALLOC)) + goto found; + if(!memcmp(d1->name, tstr+4, 4)) + m++; + } + putbuf(p1); + } + + /* + * empty slot put in root + */ +found: + if(m) /* how many dumps this date */ + sprint(tstr+8, "%ld", m); + + p = getbuf(cw->dev, rba, Bread); + d = getdir(p, 0); + *d1 = *d; /* qid is QPROOT */ + putbuf(p); + strcpy(d1->name, tstr+4); + d1->qid.version += n; + accessdir(p1, d1, FWRITE, 0); + putbuf(p1); + putbuf(pr); + + cw->fsize = cwsize(cw->dev); + oroa = cwraddr(cw->rodev); /* probably redundant */ + print("roroot %lld", (Wideoff)oroa); + + cons.noage = 0; + cw->all = 0; + roa = cwrecur(cw, oroa, Tsuper, 0, QPROOT); + if(roa == 0) { + print("[same]"); + roa = oroa; + } + print("->%lld /%.4s/%s\n", (Wideoff)roa, tstr, tstr+4); + sync("after ro"); + + /* + * final super block + */ + a = cwsaddr(cw->dev); + print("sblock %lld", (Wideoff)a); + p = getbuf(cw->dev, a, Bread|Bmod|Bimm); + s = (Superb*)p->iobuf; + s->last = a; + sba = s->next; + s->next = cw->fsize; + cw->fsize++; + s->fsize = cw->fsize; + s->roraddr = roa; +#ifdef AUTOSWAB + s->magic = 0x123456789abcdef0; +#endif + + cwio(cw->dev, sba, 0, Ogrow); + cwio(cw->dev, sba, p->iobuf, Owrite); + cwio(cw->dev, sba, 0, Odump); + print("->%lld (->%lld)\n", (Wideoff)sba, (Wideoff)s->next); + + putbuf(p); + + /* + * final cache block + */ + p = getbuf(cw->cdev, CACHE_ADDR, Bread|Bmod|Bimm|Bres); + h = (Cache*)p->iobuf; + h->fsize = cw->fsize; + h->roraddr = roa; + h->sbaddr = sba; + putbuf(p); + + rewalk(cw); + sync("all done"); + + print("%lld blocks queued for worm\n", (Wideoff)cw->ndump); + print("%lld falsehits\n", (Wideoff)cw->falsehits); + cw->nodump = 0; + + /* + * extend all of the locks + */ + tim = toytime() - tim; + for(i=0; i 0) + tlocks[i].time += tim; + + wunlock(&mainlock); + nextdump(time()); + return; + +bad: + panic("dump: bad"); +} + +void +mvstates(Device *dev, int s1, int s2, int side) +{ + Iobuf *p, *cb; + Cache *h; + Bucket *b; + Centry *c, *ce; + Off m, lo, hi, msize, maddr; + Cw *cw; + + cw = dev->private; + lo = 0; + hi = lo + devsize(dev->cw.w); /* size of all sides totalled */ + if(side >= 0) { + /* operate on only a single disc side */ + Sidestarts ss; + + wormsidestarts(dev, side, &ss); + lo = ss.sstart; + hi = ss.s1start; + } + cb = getbuf(cw->cdev, CACHE_ADDR, Bread|Bres); + if(!cb || checktag(cb, Tcache, QPSUPER)) + panic("cwstats: checktag c bucket"); + h = (Cache*)cb->iobuf; + msize = h->msize; + maddr = h->maddr; + putbuf(cb); + + for(m=0; mcdev, maddr + m/BKPERBLK, Bread|Bmod); + if(!p || checktag(p, Tbuck, maddr + m/BKPERBLK)) + panic("cwtest: checktag c bucket"); + b = (Bucket*)p->iobuf + m%BKPERBLK; + ce = b->entry + CEPERBK; + for(c=b->entry; cstate == s1 && c->waddr >= lo && c->waddr < hi) + c->state = s2; + putbuf(p); + } +} + +void +prchain(Device *dev, Off m, int flg) +{ + Iobuf *p; + Superb *s; + + if(m == 0) { + if(flg) + m = cwsaddr(dev); + else + m = getstartsb(dev); + } + p = getbuf(devnone, Cwxx2, 0); + s = (Superb*)p->iobuf; + for(;;) { + memset(p->iobuf, 0, RBUFSIZE); + if(devread(WDEV(dev), m, p->iobuf) || + checktag(p, Tsuper, QPSUPER)) + break; + if(flg) { + print("dump %lld is good; %lld prev\n", (Wideoff)m, + (Wideoff)s->last); + print("\t%lld cwroot; %lld roroot\n", (Wideoff)s->cwraddr, + (Wideoff)s->roraddr); + if(m <= s->last) + break; + m = s->last; + } else { + print("dump %lld is good; %lld next\n", (Wideoff)m, + (Wideoff)s->next); + print("\t%lld cwroot; %lld roroot\n", (Wideoff)s->cwraddr, + (Wideoff)s->roraddr); + if(m >= s->next) + break; + m = s->next; + } + } + putbuf(p); +} + +void +touchsb(Device *dev) +{ + Iobuf *p; + Off m; + + m = cwsaddr(dev); + p = getbuf(devnone, Cwxx2, 0); + + memset(p->iobuf, 0, RBUFSIZE); + if(devread(WDEV(dev), m, p->iobuf) || + checktag(p, Tsuper, QPSUPER)) + print("%Z block %lld WORM SUPER BLOCK READ FAILED\n", + WDEV(dev), (Wideoff)m); + else + print("%Z touch superblock %lld\n", WDEV(dev), (Wideoff)m); + putbuf(p); +} + +void +storesb(Device *dev, Off last, int doit) +{ + Iobuf *ph, *ps; + Cache *h; + Superb *s; + Off sbaddr, qidgen; + + sbaddr = cwsaddr(dev); + + ps = getbuf(devnone, Cwxx2, 0); + if(!ps) { + print("sbstore: getbuf\n"); + return; + } + + /* + * try to read last sb + */ + memset(ps->iobuf, 0, RBUFSIZE); + if(devread(WDEV(dev), last, ps->iobuf) || + checktag(ps, Tsuper, QPSUPER)) + print("read last failed\n"); + else + print("read last succeeded\n"); + + s = (Superb*)ps->iobuf; + qidgen = s->qidgen; + if(qidgen == 0) + qidgen = 0x31415; + qidgen += 1000; + if(s->next != sbaddr) + print("next(last) is not sbaddr %lld %lld\n", + (Wideoff)s->next, (Wideoff)sbaddr); + else + print("next(last) is sbaddr\n"); + + /* + * read cached superblock + */ + ph = getbuf(CDEV(dev), CACHE_ADDR, Bread|Bres); + if(!ph || checktag(ph, Tcache, QPSUPER)) { + print("cwstats: checktag c bucket\n"); + if(ph) + putbuf(ph); + putbuf(ps); + return; + } else + print("read cached sb succeeded\n"); + + h = (Cache*)ph->iobuf; + + memset(ps->iobuf, 0, RBUFSIZE); + settag(ps, Tsuper, QPSUPER); + ps->flags = 0; + s = (Superb*)ps->iobuf; + + s->cwraddr = h->cwraddr; + s->roraddr = h->roraddr; + s->fsize = h->fsize; + s->fstart = 2; + s->last = last; + s->next = h->roraddr+1; + + s->qidgen = qidgen; +#ifdef AUTOSWAB + s->magic = 0x123456789abcdef0; +#endif + putbuf(ph); + + if(s->fsize-1 != s->next || + s->fsize-2 != s->roraddr || + s->fsize-5 != s->cwraddr) { + print("addrs not in relationship %lld %lld %lld %lld\n", + (Wideoff)s->cwraddr, (Wideoff)s->roraddr, + (Wideoff)s->next, (Wideoff)s->fsize); + putbuf(ps); + return; + } else + print("addresses in relation\n"); + + if(doit) + if(devwrite(WDEV(dev), sbaddr, ps->iobuf)) + print("%Z block %lld WORM SUPER BLOCK WRITE FAILED\n", + WDEV(dev), (Wideoff)sbaddr); + ps->flags = 0; + putbuf(ps); +} + +void +savecache(Device *dev) +{ + Iobuf *p, *cb; + Cache *h; + Bucket *b; + Centry *c, *ce; + long n, left; + Off m, maddr, msize, *longp, nbyte; + Device *cdev; + + if(walkto("/adm/cache") || con_open(FID2, OWRITE|OTRUNC)) { + print("cant open /adm/cache\n"); + return; + } + cdev = CDEV(dev); + cb = getbuf(cdev, CACHE_ADDR, Bread|Bres); + if(!cb || checktag(cb, Tcache, QPSUPER)) + panic("savecache: checktag c bucket"); + h = (Cache*)cb->iobuf; + msize = h->msize; + maddr = h->maddr; + putbuf(cb); + + n = BUFSIZE; /* calculate write size */ + if(n > MAXDAT) + n = MAXDAT; + + cb = getbuf(devnone, Cwxx4, 0); + longp = (Off *)cb->iobuf; + left = n/sizeof(Off); + cons.offset = 0; + + for(m=0; miobuf, cons.offset, nbyte); + cons.offset += nbyte; + longp = (Off *)cb->iobuf; + left = n/sizeof(Off); + } + p = getbuf(cdev, maddr + m/BKPERBLK, Bread); + if(!p || checktag(p, Tbuck, maddr + m/BKPERBLK)) + panic("cwtest: checktag c bucket"); + b = (Bucket*)p->iobuf + m%BKPERBLK; + ce = b->entry + CEPERBK; + for(c = b->entry; c < ce; c++) + if(c->state == Cread) { + *longp++ = c->waddr; + left--; + } + putbuf(p); + } + nbyte = (n/sizeof(Off) - left) * sizeof(Off); + con_write(FID2, cb->iobuf, cons.offset, nbyte); + putbuf(cb); +} + +void +loadcache(Device *dev, int dskno) +{ + Iobuf *p, *cb; + Off m, nbyte, *longp, count; + Sidestarts ss; + + if(walkto("/adm/cache") || con_open(FID2, OREAD)) { + print("cant open /adm/cache\n"); + return; + } + + cb = getbuf(devnone, Cwxx4, 0); + cons.offset = 0; + count = 0; + + if (dskno >= 0) + wormsidestarts(dev, dskno, &ss); + for(;;) { + memset(cb->iobuf, 0, BUFSIZE); + nbyte = con_read(FID2, cb->iobuf, cons.offset, 100) / sizeof(Off); + if(nbyte <= 0) + break; + cons.offset += nbyte * sizeof(Off); + longp = (Off *)cb->iobuf; + while(nbyte > 0) { + m = *longp++; + nbyte--; + if(m == 0) + continue; + /* if given a diskno, restrict to just that disc side */ + if(dskno < 0 || m >= ss.sstart && m < ss.s1start) { + p = getbuf(dev, m, Bread); + if(p) + putbuf(p); + count++; + } + } + } + putbuf(cb); + print("%lld blocks loaded from worm %d\n", (Wideoff)count, dskno); +} + +void +morecache(Device *dev, int dskno, Off size) +{ + Iobuf *p; + Off m, ml, mh, mm, count; + Cache *h; + Sidestarts ss; + + p = getbuf(CDEV(dev), CACHE_ADDR, Bread|Bres); + if(!p || checktag(p, Tcache, QPSUPER)) + panic("savecache: checktag c bucket"); + h = (Cache*)p->iobuf; + mm = h->wmax; + putbuf(p); + + wormsidestarts(dev, dskno, &ss); + ml = ss.sstart; /* start at beginning of disc side #dskno */ + mh = ml + size; + if(mh > mm) { + mh = mm; + print("limited to %lld\n", (Wideoff)mh-ml); + } + + count = 0; + for(m=ml; m < mh; m++) { + p = getbuf(dev, m, Bread); + if(p) + putbuf(p); + count++; + } + print("%lld blocks loaded from worm %d\n", (Wideoff)count, dskno); +} + +void +blockcmp(Device *dev, Off wa, Off ca) +{ + Iobuf *p1, *p2; + int i, c; + + p1 = getbuf(WDEV(dev), wa, Bread); + if(!p1) { + print("blockcmp: wdev error\n"); + return; + } + + p2 = getbuf(CDEV(dev), ca, Bread); + if(!p2) { + print("blockcmp: cdev error\n"); + putbuf(p1); + return; + } + + c = 0; + for(i=0; iiobuf[i] != p2->iobuf[i]) { + print("%4d: %.2x %.2x\n", + i, + p1->iobuf[i]&0xff, + p2->iobuf[i]&0xff); + c++; + if(c >= 10) + break; + } + + if(c == 0) + print("no error\n"); + putbuf(p1); + putbuf(p2); +} + +void +wblock(Device *dev, Off addr) +{ + Iobuf *p1; + int i; + + p1 = getbuf(dev, addr, Bread); + if(p1) { + i = devwrite(WDEV(dev), addr, p1->iobuf); + print("i = %d\n", i); + putbuf(p1); + } +} + +void +cwtest(Device*) +{ +} + +#ifdef XXX +/* garbage to change sb size + * probably will need it someday + */ + fsz = number(0, 0, 10); + count = 0; + if(fsz == number(0, -1, 10)) + count = -1; /* really do it */ + print("fsize = %ld\n", fsz); + cdev = CDEV(dev); + cb = getbuf(cdev, CACHE_ADDR, Bread|Bres); + if(!cb || checktag(cb, Tcache, QPSUPER)) + panic("cwstats: checktag c bucket"); + h = (Cache*)cb->iobuf; + for(m=0; mmsize; m++) { + p = getbuf(cdev, h->maddr + m/BKPERBLK, Bread|Bmod); + if(!p || checktag(p, Tbuck, h->maddr + m/BKPERBLK)) + panic("cwtest: checktag c bucket"); + b = (Bucket*)p->iobuf + m%BKPERBLK; + ce = b->entry + CEPERBK; + for(c=b->entry; cwaddr < fsz) + continue; + if(count < 0) { + c->state = Cnone; + continue; + } + if(c->state != Cdirty) + count++; + } + putbuf(p); + } + if(count < 0) { + print("old cache hsize = %ld\n", h->fsize); + h->fsize = fsz; + cb->flags |= Bmod; + p = getbuf(dev, h->sbaddr, Bread|Bmod); + s = (Superb*)p->iobuf; + print("old super hsize = %ld\n", s->fsize); + s->fsize = fsz; + putbuf(p); + } + putbuf(cb); + print("count = %lld\n", (Wideoff)count); +#endif + +int +convstate(char *name) +{ + int i; + + for(i=0; iiobuf+BUFSIZE); + for(i=0; iiobuf, 0, RBUFSIZE); + if(devread(WDEV(d), a+i, p->iobuf)) { + if(n == 1000) + break; + continue; + } + if(t->tag == tag) { + print("tag %d found at %Z %lld\n", tag, d, (Wideoff)a+i); + break; + } + } + putbuf(p); +} + +void +cmd_cwcmd(int argc, char *argv[]) +{ + Device *dev; + char *arg; + char str[28]; + Off s1, s2, a, b, n; + Cw *cw; + + if(argc <= 1) { + print(" cwcmd mvstate state1 state2 [platter]\n"); + print(" cwcmd prchain [start] [bakflg]\n"); + print(" cwcmd searchtag [start] [tag] [blocks]\n"); + print(" cwcmd touchsb\n"); + print(" cwcmd savecache\n"); + print(" cwcmd loadcache [dskno]\n"); + print(" cwcmd morecache dskno [count]\n"); + print(" cwcmd blockcmp wbno cbno\n"); + print(" cwcmd startdump [01]\n"); + print(" cwcmd acct\n"); + print(" cwcmd clearacct\n"); + return; + } + arg = argv[1]; + + /* + * items not depend on a cw filesystem + */ + if(strcmp(arg, "acct") == 0) { + for(a=0; adev; + if(dev == 0 || dev->type != Devcw || dev->private == 0) { + print("cfs not a cw filesystem: %Z\n", dev); + return; + } + cw = dev->private; + if(strcmp(arg, "searchtag") == 0) { + a = 0; + if(argc > 2) + a = number(argv[2], 0, 10); + b = Tsuper; + if(argc > 3) + b = number(argv[3], 0, 10); + n = 1000; + if(argc > 4) + n = number(argv[4], 0, 10); + searchtag(dev, a, b, n); + } else if(strcmp(arg, "mvstate") == 0) { + if(argc < 4) + goto bad; + s1 = convstate(argv[2]); + s2 = convstate(argv[3]); + if(s1 < 0 || s2 < 0) + goto bad; + a = -1; + if(argc > 4) + a = number(argv[4], 0, 10); + mvstates(dev, s1, s2, a); + return; + bad: + print("cwcmd mvstate: bad args\n"); + } else if(strcmp(arg, "prchain") == 0) { + a = 0; + if(argc > 2) + a = number(argv[2], 0, 10); + s1 = 0; + if(argc > 3) + s1 = number(argv[3], 0, 10); + prchain(dev, a, s1); + } else if(strcmp(arg, "touchsb") == 0) + touchsb(dev); + else if(strcmp(arg, "savecache") == 0) + savecache(dev); + else if(strcmp(arg, "loadcache") == 0) { + s1 = -1; + if(argc > 2) + s1 = number(argv[2], 0, 10); + loadcache(dev, s1); + } else if(strcmp(arg, "morecache") == 0) { + if(argc <= 2) { + print("arg count\n"); + return; + } + s1 = number(argv[2], 0, 10); + if(argc > 3) + s2 = number(argv[3], 0, 10); + else + s2 = wormsizeside(dev, s1); /* default to 1 disc side */ + morecache(dev, s1, s2); + } else if(strcmp(arg, "blockcmp") == 0) { + if(argc < 4) { + print("cannot arg count\n"); + return; + } + s1 = number(argv[2], 0, 10); + s2 = number(argv[3], 0, 10); + blockcmp(dev, s1, s2); + } else if(strcmp(arg, "startdump") == 0) { + if(argc > 2) + cw->nodump = number(argv[2], 0, 10); + cw->nodump = !cw->nodump; + if(cw->nodump) + print("dump stopped\n"); + else + print("dump allowed\n"); + } else if(strcmp(arg, "allflag") == 0) { + if(argc > 2) + cw->allflag = number(argv[2], 0, 10); + else + cw->allflag = !cw->allflag; + print("allflag = %d; falsehits = %lld\n", + cw->allflag, (Wideoff)cw->falsehits); + } else if(strcmp(arg, "storesb") == 0) { + a = 4168344; + b = 0; + if(argc > 2) + a = number(argv[2], 4168344, 10); + if(argc > 3) + b = number(argv[3], 0, 10); + storesb(dev, a, b); + } else if(strcmp(arg, "test") == 0) + cwtest(dev); + else + print("unknown cwcmd %s\n", arg); +} diff -Nru /sys/src/fs/dev/fworm.c /sys/src/fs/dev/fworm.c --- /sys/src/fs/dev/fworm.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/dev/fworm.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,106 @@ +#include "all.h" + +#define DEBUG 0 +#define FDEV(d) (d->fw.fw) + +Devsize +fwormsize(Device *d) +{ + Device *fdev; + Devsize l; + + fdev = FDEV(d); + l = devsize(fdev); + l -= l/(BUFSIZE*8) + 1; + return l; +} + +void +fwormream(Device *d) +{ + Iobuf *p; + Device *fdev; + Off a, b; + + print("fworm ream\n"); + devinit(d); + fdev = FDEV(d); + a = fwormsize(d); + b = devsize(fdev); + print(" fwsize = %lld\n", (Wideoff)a); + print(" bwsize = %lld\n", (Wideoff)b-a); + for(; a < b; a++) { + p = getbuf(fdev, a, Bmod|Bres); + if(!p) + panic("fworm: init"); + memset(p->iobuf, 0, RBUFSIZE); + settag(p, Tvirgo, a); + putbuf(p); + } +} + +void +fworminit(Device *d) +{ + print("fworm init\n"); + devinit(FDEV(d)); +} + +int +fwormread(Device *d, Off b, void *c) +{ + Iobuf *p; + Device *fdev; + Devsize l; + + if(DEBUG) + print("fworm read %lld\n", (Wideoff)b); + fdev = FDEV(d); + l = devsize(fdev); + l -= l/(BUFSIZE*8) + 1; + if(b >= l) + panic("fworm: rbounds %lld\n", (Wideoff)b); + l += b/(BUFSIZE*8); + + p = getbuf(fdev, l, Bread|Bres); + if(!p || checktag(p, Tvirgo, l)) + panic("fworm: checktag %lld\n", (Wideoff)l); + l = b % (BUFSIZE*8); + if(!(p->iobuf[l/8] & (1<<(l%8)))) { + putbuf(p); + print("fworm: read %lld\n", (Wideoff)b); + return 1; + } + putbuf(p); + return devread(fdev, b, c); +} + +int +fwormwrite(Device *d, Off b, void *c) +{ + Iobuf *p; + Device *fdev; + Devsize l; + + if(DEBUG) + print("fworm write %lld\n", (Wideoff)b); + fdev = FDEV(d); + l = devsize(fdev); + l -= l/(BUFSIZE*8) + 1; + if(b >= l) + panic("fworm: wbounds %lld\n", (Wideoff)b); + l += b/(BUFSIZE*8); + + p = getbuf(fdev, l, Bread|Bmod|Bres); + if(!p || checktag(p, Tvirgo, l)) + panic("fworm: checktag %lld", (Wideoff)l); + l = b % (BUFSIZE*8); + if((p->iobuf[l/8] & (1<<(l%8)))) { + putbuf(p); + print("fworm: write %lld\n", (Wideoff)b); + return 1; + } + p->iobuf[l/8] |= 1<<(l%8); + putbuf(p); + return devwrite(fdev, b, c); +} diff -Nru /sys/src/fs/dev/juke.c /sys/src/fs/dev/juke.c --- /sys/src/fs/dev/juke.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/dev/juke.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,1153 @@ +#include "all.h" + +#define SCSInone SCSIread +#define MAXDRIVE 10 +#define MAXSIDE 500 + +#define TWORM MINUTE(10) +#define THYSTER SECOND(10) + +typedef struct Side Side; +struct Side +{ + QLock; /* protects loading/unloading */ + int elem; /* element number */ + int drive; /* if loaded, where */ + uchar status; /* Sunload, etc */ + uchar rot; /* if backside */ + int ord; /* ordinal number for labeling */ + + Timet time; /* time since last access, to unspin */ + Timet stime; /* time since last spinup, for hysteresis */ + long nblock; /* number of native blocks */ + long block; /* bytes per native block */ + long mult; /* multiplier to get plan9 blocks */ + long max; /* max size in plan9 blocks */ +}; + +typedef struct Juke Juke; +struct Juke +{ + QLock; /* protects drive mechanism */ + Side side[MAXSIDE]; + int nside; /* how many storage elements (*2 if rev) */ + int ndrive; /* number of transfer elements */ + Device* juke; /* devworm of changer */ + Device* drive[MAXDRIVE]; /* devworm for i/o */ + uchar offline[MAXDRIVE]; /* drives removed from service */ + long fixedsize; /* one size fits all */ + int probeok; /* wait for init to probe */ + + /* + * geometry returned by mode sense. + * a *0 number (such as mt0) is the `element number' of the + * first element of that type (e.g., mt, or motor transport). + * an n* number is the quantity of them. + */ + int mt0, nmt; /* motor transports (robot pickers) */ + int se0, nse; /* storage elements (discs, slots) */ + int ie0, nie; /* interchange elements (mailbox slots) */ + int dt0, ndt; /* drives (data transfer?) */ + int rot; /* if true, discs are double-sided */ + + Juke* link; +}; +static Juke* jukelist; + +enum +{ + Sempty = 0, /* does not exist */ + Sunload, /* on the shelf */ + Sstart, /* loaded and spinning */ +}; + +extern int FIXEDSIZE; + +static int wormsense(Device*); +static Side* wormunit(Device*); +static void shelves(void); +static int mmove(Juke*, int, int, int, int); +static int bestdrive(Juke*, int); +static void waitready(Device*); +static void element(Juke*, int); + +/* + * mounts and spins up the device + * locks the structure + */ +static +Side* +wormunit(Device *d) +{ + int p, s, drive; + Side *v; + Juke *w; + uchar cmd[10], buf[8]; + + w = d->private; + p = d->wren.targ; + if(p < 0 || p >= w->nside) { +// panic("wormunit partition %Z\n", d); + return 0; + } + + /* + * if disk is unloaded, must load it + * into next (circular) logical unit + */ + v = &w->side[p]; + qlock(v); + if(v->status == Sunload) { + for(;;) { + qlock(w); + drive = bestdrive(w, p); + if(drive >= 0) + break; + qunlock(w); + waitsec(100); + } + print(" load r%ld drive %Z\n", v-w->side, w->drive[drive]); + if(mmove(w, w->mt0, v->elem, w->dt0+drive, v->rot)) { + qunlock(w); + goto sbad; + } + v->drive = drive; + v->status = Sstart; + v->stime = toytime(); + qunlock(w); + waitready(w->drive[drive]); + v->stime = toytime(); + } + if(v->status != Sstart) { + if(v->status == Sempty) + print("worm: unit empty %Z\n", d); + else + print("worm: not started %Z\n", d); + goto sbad; + } + + v->time = toytime(); + if(v->block) + return v; + + /* + * capacity command + */ + memset(cmd, 0, sizeof(cmd)); + memset(buf, 0, sizeof(buf)); + cmd[0] = 0x25; /* read capacity */ + s = scsiio(w->drive[v->drive], SCSIread, + cmd, sizeof(cmd), buf, sizeof(buf)); + if(s) + goto sbad; + + v->nblock = + (buf[0]<<24) | + (buf[1]<<16) | + (buf[2]<<8) | + (buf[3]<<0); + v->block = + (buf[4]<<24) | + (buf[5]<<16) | + (buf[6]<<8) | + (buf[7]<<0); + v->mult = + (RBUFSIZE + v->block - 1) / + v->block; + v->max = + (v->nblock + 1) / v->mult; + + print(" worm %Z: drive %Z\n", d, w->drive[v->drive]); + print(" %ld blocks at %ld bytes each\n", + v->nblock, v->block); + print(" %ld logical blocks at %d bytes each\n", + v->max, RBUFSIZE); + print(" %ld multiplier\n", + v->mult); + if(d->type != Devlworm) + return v; + /* check for label */ + print("label %Z ordinal %d\n", d, v->ord); + qunlock(v); + return wormunit(d); + +sbad: + qunlock(v); +// panic("wormunit sbad"); + return 0; +} + +static +void +waitready(Device *d) +{ + uchar cmd[6]; + int s, e; + + for(e=0;e<100;e++) { + memset(cmd, 0, sizeof(cmd)); + s = scsiio(d, SCSInone, cmd, sizeof(cmd), cmd, 0); + if(s == 0) + break; + waitsec(100); + } +} + +static +int +bestdrive(Juke *w, int side) +{ + Side *v, *bv[MAXDRIVE]; + int i, s, e, drive; + Timet t, t0; + +loop: + /* build table of what platters on what drives */ + for(i=0; indt; i++) + bv[i] = 0; + + v = &w->side[0]; + for(i=0; inside; i++, v++) { + s = v->status; + if(s == Sstart) { + drive = v->drive; + if(drive >= 0 && drive < w->ndt) + bv[drive] = v; + } + } + + /* + * find oldest drive, but must be + * at least THYSTER old. + */ + e = w->side[side].elem; + t0 = toytime() - THYSTER; + t = t0; + drive = -1; + for(i=0; indt; i++) { + v = bv[i]; + if(v == 0) { /* 2nd priority: empty drive */ + if(w->offline[i]) + continue; + if(w->drive[i] != devnone) { + drive = i; + t = 0; + } + continue; + } + if(v->elem == e) { /* 1st priority: other side */ + drive = -1; + if(v->stime < t0) + drive = i; + break; + } + if(v->stime < t) { /* 3rd priority: by time */ + drive = i; + t = v->stime; + } + } + + if(drive >= 0) { + v = bv[drive]; + if(v) { + qlock(v); + if(v->status != Sstart) { + qunlock(v); + goto loop; + } + print(" unload r%ld drive %Z\n", + v-w->side, w->drive[drive]); + if(mmove(w, w->mt0, w->dt0+drive, v->elem, v->rot)) { + qunlock(v); + goto loop; + } + v->status = Sunload; + qunlock(v); + } + } + return drive; +} + +Devsize +wormsize(Device *d) +{ + Side *v; + Juke *w; + Devsize size; + + w = d->private; + if(w->fixedsize) + size = w->fixedsize; + else { + v = wormunit(d); + if(v == 0) + return 0; + size = v->max; + qunlock(v); + if(FIXEDSIZE) // TODO? push FIXEDSIZE into Device or Juke struct + w->fixedsize = size; + } + if(d->type == Devlworm) + return size-1; + return size; +} + +/* + * return a Devjuke or an mcat (normally of sides) from within d (or nil). + * if it's an mcat, the caller must walk it. + */ +static Device * +devtojuke(Device *d, Device *top) +{ + while (d != nil) + switch(d->type) { + default: + print("devtojuke: type of device %Z of %Z unknown\n", + d, top); + return nil; + + case Devjuke: + /* jackpot! d->private is a (Juke *) with nside, &c. */ + /* FALL THROUGH */ + case Devmcat: + case Devmlev: + case Devmirr: + /* squint hard & call an mlev or a mirr an mcat */ + return d; + + case Devworm: + case Devlworm: + /* + * d->private is a (Juke *) with nside, etc., + * but we're not supposed to get here. + */ + print("devtojuke: (l)worm %Z of %Z encountered\n", + d, top); + /* FALL THROUGH */ + case Devwren: + case Devide: + case Devmarvsata: + return nil; + + case Devcw: + d = d->cw.w; /* usually juke */ + break; + case Devro: + d = d->ro.parent; /* cw */ + break; + case Devfworm: + d = d->fw.fw; + break; + case Devpart: + d = d->part.d; + break; + case Devswab: + d = d->swab.d; + break; + } + return d; +} + +static int +devisside(Device *d) +{ + return d->type == Devworm || d->type == Devlworm; +} + +static Device * +findside(Device *juke, int side, Device *top) +{ + int i = 0; + Device *mcat = juke->j.m, *x; + Juke *w = juke->private; + + for (x = mcat->cat.first; x != nil; x = x->link) { + if (!devisside(x)) { + print("wormsizeside: %Z of %Z of %Z type not (l)worm\n", + x, mcat, top); + return nil; + } + i = x->wren.targ; + if (i < 0 || i >= w->nside) + panic("wormsizeside: side %d in %Z out of range", + i, mcat); + if (i == side) + break; + } + if (x == nil) + return nil; + if (w->side[i].time == 0) { + print("wormsizeside: side %d not in jukebox %Z\n", i, juke); + return nil; + } + return x; +} + +typedef struct { + int sleft; /* sides still to visit to reach desired side */ + int starget; /* side of topdev we want */ + Device *topdev; + int sawjuke; /* passed by a jukebox */ + int sized; /* flag: asked wormsize for size of starget */ +} Visit; + +/* + * walk the Device tree from d looking for Devjukes, counting sides. + * the main complication is mcats and the like with Devjukes in them. + * use Devjuke's d->private as Juke* and see sides. + */ +static Off +visitsides(Device *d, Device *parentj, Visit *vp) +{ + Off size = 0; + Device *x; + Juke *w; + + /* + * find the first juke or mcat. + * d==nil means we couldn't find one; typically harmless, due to a + * mirror of dissimilar devices. + */ + d = devtojuke(d, vp->topdev); + if (d == nil || vp->sleft < 0) + return 0; + if (d->type == Devjuke) { /* jackpot! d->private is a (Juke *) */ + vp->sawjuke = 1; + w = d->private; + /* + * if there aren't enough sides in this jukebox to reach + * the desired one, subtract these sides and pass. + */ + if (vp->sleft >= w->nside) { + vp->sleft -= w->nside; + return 0; + } + /* else this is the right juke, paw through mcat of sides */ + return visitsides(d->j.m, d, vp); + } + + /* + * d will usually be an mcat of sides, but it could be an mcat of + * jukes, for example. in that case, we need to walk the mcat, + * recursing as needed, until we find the right juke, then stop at + * the right side within its mcat of sides, by comparing side + * numbers, not just by counting (to allow for unused slots). + */ + x = d->cat.first; + if (x == nil) { + print("visitsides: %Z of %Z: empty mcat\n", d, vp->topdev); + return 0; + } + if (!devisside(x)) { + for (; x != nil && !vp->sized; x = x->link) + size = visitsides(x, parentj, vp); + return size; + } + + /* the side we want is in this jukebox, thus this mcat (d) */ + if (parentj == nil) { + print("visitsides: no parent juke for sides mcat %Z\n", d); + vp->sleft = -1; + return 0; + } + if (d != parentj->j.m) + panic("visitsides: mcat mismatch %Z vs %Z", d, parentj->j.m); + x = findside(parentj, vp->sleft, vp->topdev); + if (x == nil) { + vp->sleft = -1; + return 0; + } + + /* we've turned vp->starget into the right Device* */ + vp->sleft = 0; + vp->sized = 1; + return wormsize(x); +} + +/* + * d must be, or be within, a filesystem config that also contains + * the jukebox that `side' resides on. + * d is normally a Devcw, but could be Devwren, Devide, Devpart, Devfworm, + * etc. if called from chk.c Ctouch code. Note too that the worm part of + * the Devcw might be other than a Devjuke. + */ +Devsize +wormsizeside(Device *d, int side) +{ + Devsize size; + Visit visit; + + memset(&visit, 0, sizeof visit); + visit.starget = visit.sleft = side; + visit.topdev = d; + size = visitsides(d, nil, &visit); + if (visit.sawjuke && (visit.sleft != 0 || !visit.sized)) { + print("wormsizeside: fewer than %d sides in %Z\n", side, d); + return 0; + } + return size; +} + +/* + * returns starts (in blocks) of side #side and #(side+1) of dev in *stp. + * dev should be a Devcw. + */ +void +wormsidestarts(Device *dev, int side, Sidestarts *stp) +{ + int s; + Devsize dstart; + + for (dstart = s = 0; s < side; s++) + dstart += wormsizeside(dev, s); + stp->sstart = dstart; + stp->s1start = dstart + wormsizeside(dev, side); +} + +static +int +wormiocmd(Device *d, int io, Off b, void *c) +{ + Side *v; + Juke *w; + Off l; + int s; + long m; + uchar cmd[10]; + + w = d->private; + v = wormunit(d); + if(v == 0) + return 0x71; + if(b >= v->max) { + qunlock(v); + print("worm: wormiocmd out of range %Z(%lld)\n", d, (Wideoff)b); + return 0x071; + } + + memset(cmd, 0, sizeof(cmd)); + cmd[0] = 0x28; /* extended read */ + if(io != SCSIread) + cmd[0] = 0x2a; /* extended write */ + + m = v->mult; + l = b * m; + cmd[2] = l>>24; + cmd[3] = l>>16; + cmd[4] = l>>8; + cmd[5] = l; + + cmd[7] = m>>8; + cmd[8] = m; + + s = scsiio(w->drive[v->drive], io, cmd, sizeof(cmd), c, RBUFSIZE); + qunlock(v); + return s; +} + +int +wormread(Device *d, Off b, void *c) +{ + int s; + + s = wormiocmd(d, SCSIread, b, c); + if(s) { + print("wormread: %Z(%lld) bad status #%x\n", d, (Wideoff)b, s); + cons.nwormre++; + return s; + } + return 0; +} + +int +wormwrite(Device *d, Off b, void *c) +{ + int s; + + s = wormiocmd(d, SCSIwrite, b, c); + if(s) { + print("wormwrite: %Z(%lld) bad status #%x\n", d, (Wideoff)b, s); + cons.nwormwe++; + return s; + } + return 0; +} + +static +int +mmove(Juke *w, int trans, int from, int to, int rot) +{ + uchar cmd[12], buf[4]; + int s; + static recur = 0; + + memset(cmd, 0, sizeof(cmd)); + cmd[0] = 0xa5; /* move medium */ + cmd[2] = trans>>8; + cmd[3] = trans; + cmd[4] = from>>8; + cmd[5] = from; + cmd[6] = to>>8; + cmd[7] = to; + if(rot) + cmd[10] = 1; + s = scsiio(w->juke, SCSInone, cmd, sizeof(cmd), buf, 0); + if(s) { + print("scsio status #%x\n", s); + print("move medium t=%d fr=%d to=%d rot=%d\n", + trans, from, to, rot); +// panic("mmove"); + if(recur == 0) { + recur = 1; + print("element from=%d\n", from); + element(w, from); + print("element to=%d\n", to); + element(w, to); + print("element trans=%d\n", trans); + element(w, trans); + recur = 0; + } + return 1; + } + return 0; +} + +static +void +geometry(Juke *w) +{ + int s; + uchar cmd[6], buf[4+20]; + + memset(cmd, 0, sizeof(cmd)); + memset(buf, 0, sizeof(buf)); + cmd[0] = 0x1a; /* mode sense */ + cmd[2] = 0x1d; /* element address assignment */ + cmd[4] = sizeof(buf); /* allocation length */ + + s = scsiio(w->juke, SCSIread, cmd, sizeof(cmd), buf, sizeof(buf)); + if(s) + panic("geometry #%x\n", s); + + w->mt0 = (buf[4+2]<<8) | buf[4+3]; + w->nmt = (buf[4+4]<<8) | buf[4+5]; + w->se0 = (buf[4+6]<<8) | buf[4+7]; + w->nse = (buf[4+8]<<8) | buf[4+9]; + w->ie0 = (buf[4+10]<<8) | buf[4+11]; + w->nie = (buf[4+12]<<8) | buf[4+13]; + w->dt0 = (buf[4+14]<<8) | buf[4+15]; + w->ndt = (buf[4+16]<<8) | buf[4+17]; + + memset(cmd, 0, 6); + memset(buf, 0, sizeof(buf)); + cmd[0] = 0x1a; /* mode sense */ + cmd[2] = 0x1e; /* transport geometry */ + cmd[4] = sizeof(buf); /* allocation length */ + + s = scsiio(w->juke, SCSIread, cmd, sizeof(cmd), buf, sizeof(buf)); + if(s) + panic("geometry #%x\n", s); + + w->rot = buf[4+2] & 1; + + print(" mt %d %d\n", w->mt0, w->nmt); + print(" se %d %d\n", w->se0, w->nse); + print(" ie %d %d\n", w->ie0, w->nie); + print(" dt %d %d\n", w->dt0, w->ndt); + print(" rot %d\n", w->rot); + prflush(); + +} + +static +void +element(Juke *w, int e) +{ + uchar cmd[12], buf[8+8+88]; + int s, t; + + memset(cmd, 0, sizeof(cmd)); + memset(buf, 0, sizeof(buf)); + cmd[0] = 0xb8; /* read element status */ + cmd[2] = e>>8; /* starting element */ + cmd[3] = e; + cmd[5] = 1; /* number of elements */ + cmd[9] = sizeof(buf); /* allocation length */ + + s = scsiio(w->juke, SCSIread, cmd, sizeof(cmd), buf, sizeof(buf)); + if(s) { + print("scsiio #%x\n", s); + goto bad; + } + + s = (buf[0]<<8) | buf[1]; + if(s != e) { + print("element = %d\n", s); + goto bad; + } + if(buf[3] != 1) { + print("number reported = %d\n", buf[3]); + goto bad; + } + s = (buf[8+8+0]<<8) | buf[8+8+1]; + if(s != e) { + print("element1 = %d\n", s); + goto bad; + } + + switch(buf[8+0]) { /* element type */ + default: + print("unknown element %d: %d\n", e, buf[8+0]); + goto bad; + case 1: /* transport */ + s = e - w->mt0; + if(s < 0 || s >= w->nmt) + goto bad; + if(buf[8+8+2] & 1) + print("transport %d full %d.%d\n", s, + (buf[8+8+10]<<8) | buf[8+8+11], + (buf[8+8+9]>>6) & 1); + break; + case 2: /* storage */ + s = e - w->se0; + if(s < 0 || s >= w->nse) + goto bad; + w->side[s].status = Sempty; + if(buf[8+8+2] & 1) + w->side[s].status = Sunload; + if(w->rot) + w->side[w->nse+s].status = w->side[s].status; + break; + case 3: /* import/export */ + s = e - w->ie0; + if(s < 0 || s >= w->nie) + goto bad; + print("import/export %d #%.2x %d.%d\n", s, + buf[8+8+2], + (buf[8+8+10]<<8) | buf[8+8+11], + (buf[8+8+9]>>6) & 1); + break; + case 4: /* data transfer */ + s = e - w->dt0; + if(s < 0 || s >= w->ndt) + goto bad; + print("data transfer %d #%.2x %d.%d\n", s, + buf[8+8+2], + (buf[8+8+10]<<8) | buf[8+8+11], + (buf[8+8+9]>>6) & 1); + if(buf[8+8+2] & 1) { + t = ((buf[8+8+10]<<8) | buf[8+8+11]) - w->se0; + if (t < 0 || t >= w->nse || t >= MAXSIDE || + s >= MAXDRIVE) { + print( + "element: juke %Z lies; claims side %d is in drive %d\n", + w->juke, t, s); /* lying sack of ... */ + /* + * at minimum, we've avoided corrupting our + * data structures. if we know that numbers + * like w->nside are valid here, we could use + * them in more stringent tests. + * perhaps should whack the jukebox upside the + * head here to knock some sense into it. + */ + goto bad; + } + print("r%d in drive %d\n", t, s); + if(mmove(w, w->mt0, w->dt0+s, w->se0+t, (buf[8+8+9]>>6) & 1)) { + print("mmove initial unload\n"); + goto bad; + } + w->side[t].status = Sunload; + if(w->rot) + w->side[w->nse+t].status = Sunload; + } + if(buf[8+8+2] & 4) { + print("drive w%d has exception #%.2x #%.2x\n", s, + buf[8+8+4], buf[8+8+5]); + goto bad; + } + break; + } + return; +bad: + /* panic("element") */ ; +} + +static +void +positions(Juke *w) +{ + int i, f; + + /* mark empty shelves */ + for(i=0; inse; i++) + element(w, w->se0+i); + for(i=0; inmt; i++) + element(w, w->mt0+i); + for(i=0; inie; i++) + element(w, w->ie0+i); + for(i=0; indt; i++) + element(w, w->dt0+i); + + f = 0; + for(i=0; inse; i++) { + if(w->side[i].status == Sempty) { + if(f) { + print("r%d\n", i-1); + f = 0; + } + } else { + if(!f) { + print(" shelves r%d-", i); + f = 1; + } + } + } + if(f) + print("r%d\n", i-1); +} + +static +void +jinit(Juke *w, Device *d, int o) +{ + int p; + Device *dev = d; + + switch(d->type) { + default: + print("juke platter not (devmcat of) dev(l)worm: %Z\n", d); + panic("jinit: type"); + + case Devmcat: + /* + * we don't call mcatinit(d) here, so we have to set d->cat.ndev + * ourselves. + */ + for(d=d->cat.first; d; d=d->link) + jinit(w, d, o++); + dev->cat.ndev = o; + break; + + case Devlworm: + p = d->wren.targ; + if(p < 0 || p >= w->nside) + panic("jinit partition %Z\n", d); + w->side[p].ord = o; + /* FALL THROUGH */ + case Devworm: + if(d->private) { + print("juke platter private pointer set %p\n", + d->private); + panic("jinit: private"); + } + d->private = w; + break; + } +} + +Side* +wormi(char *arg) +{ + int i, j; + Juke *w; + Side *v; + + i = number(arg, -1, 10) - 1; + w = jukelist; + if(i < 0 || i >= w->nside) { + print("bad unit number %s (%d)\n", arg, i+1); + return 0; + } + j = i; + if(j >= w->nse) + j -= w->nse; + if(j < w->nside) { + v = &w->side[j]; + qlock(v); + if(v->status == Sstart) { + if(mmove(w, w->mt0, w->dt0+v->drive, v->elem, v->rot)) { + qunlock(v); + return 0; + } + v->status = Sunload; + } + qunlock(v); + } + j += w->nse; + if(j < w->nside) { + v = &w->side[j]; + qlock(v); + if(v->status == Sstart) { + if(mmove(w, w->mt0, w->dt0+v->drive, v->elem, v->rot)) { + qunlock(v); + return 0; + } + v->status = Sunload; + } + qunlock(v); + } + v = &w->side[i]; + qlock(v); + return v; +} + +static +void +cmd_wormoffline(int argc, char *argv[]) +{ + int u, i; + Juke *w; + + if(argc <= 1) { + print("usage: wormoffline drive\n"); + return; + } + u = number(argv[1], -1, 10); + w = jukelist; + if(u < 0 || u >= w->ndrive) { + print("bad drive %s (0<=%d<%d)\n", argv[1], u, w->ndrive); + return; + } + if(w->offline[u]) + print("drive %d already offline\n", u); + w->offline[u] = 1; + for(i=0; indrive; i++) + if(w->offline[i] == 0) + return; + print("that would take all drives offline\n"); + w->offline[u] = 0; +} + +static +void +cmd_wormonline(int argc, char *argv[]) +{ + int u; + Juke *w; + + if(argc <= 1) { + print("usage: wormonline drive\n"); + return; + } + u = number(argv[1], -1, 10); + w = jukelist; + if(u < 0 || u >= w->ndrive) { + print("bad drive %s (0<=%d<%d)\n", argv[1], u, w->ndrive); + return; + } + if(w->offline[u] == 0) + print("drive %d already online\n", u); + w->offline[u] = 0; +} + +static +void +cmd_wormreset(int, char *[]) +{ + Juke *w; + + for(w=jukelist; w; w=w->link) { + qlock(w); + positions(w); + qunlock(w); + } +} + +static +void +cmd_wormeject(int argc, char *argv[]) +{ + Juke *w; + Side *v; + + if(argc <= 1) { + print("usage: wormeject unit\n"); + return; + } + v = wormi(argv[1]); + if(v == 0) + return; + w = jukelist; + mmove(w, w->mt0, v->elem, w->ie0, 0); + qunlock(v); +} + +static +void +cmd_wormingest(int argc, char *argv[]) +{ + Juke *w; + Side *v; + + if(argc <= 1) { + print("usage: wormingest unit\n"); + return; + } + v = wormi(argv[1]); + if(v == 0) + return; + w = jukelist; + mmove(w, w->mt0, w->ie0, v->elem, 0); + qunlock(v); +} + +void +jukeinit(Device *d) +{ + Juke *w; + Device *xdev; + Side *v; + int i; + + /* j(ww...)(r) */ + xdev = d->j.j; + if(xdev->type != Devmcat) { + print("juke union not mcat\n"); + goto bad; + } + + /* + * pick up the changer device + */ + xdev = xdev->cat.first; + if(xdev->type != Devwren) { + print("juke changer not wren %Z\n", xdev); + goto bad; + } + for(w=jukelist; w; w=w->link) + if(xdev == w->juke) + goto found; + + /* + * allocate a juke structure + * no locking problems. + */ + w = ialloc(sizeof(Juke), 0); + w->link = jukelist; + jukelist = w; + + print("alloc juke %Z\n", xdev); + qlock(w); + qunlock(w); + w->name = "juke"; + w->juke = xdev; + geometry(w); + + /* + * pick up each side + */ + w->nside = w->nse; + if(w->rot) + w->nside += w->nside; + if(w->nside > MAXSIDE) { + print("too many sides: %d max %d\n", w->nside, MAXSIDE); + goto bad; + } + for(i=0; inse; i++) { + v = &w->side[i]; + qlock(v); + qunlock(v); + v->name = "shelf"; + v->elem = w->se0 + i; + v->rot = 0; + v->status = Sempty; + v->time = toytime(); + if(w->rot) { + v += w->nse; + qlock(v); + qunlock(v); + v->name = "shelf"; + v->elem = w->se0 + i; + v->rot = 1; + v->status = Sempty; + v->time = toytime(); + } + } + positions(w); + + w->ndrive = w->ndt; + if(w->ndrive > MAXDRIVE) { + print("ndrives truncated to %d\n", MAXDRIVE); + w->ndrive = MAXDRIVE; + } + + /* + * pick up each drive + */ + for(i=0; indrive; i++) + w->drive[i] = devnone; + + cmd_install("wormreset", "-- put drives back where jukebox thinks they belong", cmd_wormreset); + cmd_install("wormeject", "unit -- shelf to outside", cmd_wormeject); + cmd_install("wormingest", "unit -- outside to shelf", cmd_wormingest); + cmd_install("wormoffline", "unit -- disable drive", cmd_wormoffline); + cmd_install("wormonline", "unit -- enable drive", cmd_wormonline); + +found: + i = 0; + while(xdev = xdev->link) { + if(xdev->type != Devwren) { + print("drive not devwren: %Z\n", xdev); + goto bad; + } + if(w->drive[i]->type != Devnone && + xdev != w->drive[i]) { + print("double init drive %d %Z %Z\n", i, w->drive[i], xdev); + goto bad; + } + if(i >= w->ndrive) { + print("too many drives %Z\n", xdev); + goto bad; + } + w->drive[i++] = xdev; + } + + if(i <= 0) { + print("no drives\n"); + goto bad; + } + + /* + * put w pointer in each platter + */ + d->private = w; + jinit(w, d->j.m, 0); + w->probeok = 1; + return; + +bad: + panic("juke init"); +} + +/* + * called periodically + */ +void +wormprobe(void) +{ + int i, drive; + Timet t; + Side *v; + Juke *w; + + t = toytime() - TWORM; + for(w=jukelist; w; w=w->link) { + if(w->probeok == 0 || !canqlock(w)) + continue; + for(i=0; inside; i++) { + v = &w->side[i]; + if(!canqlock(v)) + continue; + if(v->status == Sstart && t > v->time) { + drive = v->drive; + print(" time r%ld drive %Z\n", + v-w->side, w->drive[drive]); + mmove(w, w->mt0, w->dt0+drive, v->elem, v->rot); + v->status = Sunload; + } + qunlock(v); + } + qunlock(w); + } +} diff -Nru /sys/src/fs/dev/mkfile /sys/src/fs/dev/mkfile --- /sys/src/fs/dev/mkfile Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/dev/mkfile Tue Nov 1 00:00:00 2011 @@ -0,0 +1,3 @@ +DEVFILES=`{builtin cd ../dev;echo *.c | sed 's/ /|/g; s/\.c//g'} +^($DEVFILES)\.$O:R: '../dev/\1.c' + $CC $CFLAGS -I. ../dev/$stem1.c diff -Nru /sys/src/fs/dev/mworm.c /sys/src/fs/dev/mworm.c --- /sys/src/fs/dev/mworm.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/dev/mworm.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,283 @@ +#include "all.h" + +/* + * multiple cat devices + */ +void +mcatinit(Device *d) +{ + Device *x, **list; + + d->cat.ndev = 0; + for(x=d->cat.first; x; x=x->link) { + devinit(x); + d->cat.ndev++; + } + + list = ialloc(d->cat.ndev*sizeof(Device*), 0); + d->private = list; + for(x=d->cat.first; x; x=x->link) { + *list++ = x; + x->size = devsize(x); + } +} + +Devsize +mcatsize(Device *d) +{ + Device *x; + Devsize l, m; + + l = 0; + for(x=d->cat.first; x; x=x->link) { + m = x->size; + if(m == 0) { + m = devsize(x); + x->size = m; + } + l += m; + } + return l; +} + +int +mcatread(Device *d, Off b, void *c) +{ + Device *x; + Devsize l, m; + + l = 0; + for(x=d->cat.first; x; x=x->link) { + m = x->size; + if(m == 0) { + m = devsize(x); + x->size = m; + } + if(b < l+m) + return devread(x, b-l, c); + l += m; + } + print("mcatread %lld %lld\n", (Wideoff)b, (Wideoff)l); + return 1; +} + +int +mcatwrite(Device *d, Off b, void *c) +{ + Device *x; + Devsize l, m; + + l = 0; + for(x=d->cat.first; x; x=x->link) { + m = x->size; + if(m == 0) { + m = devsize(x); + x->size = m; + } + if(b < l+m) + return devwrite(x, b-l, c); + l += m; + } + print("mcatwrite %lld %lld\n", (Wideoff)b, (Wideoff)l); + return 1; +} + +/* + * multiple interleave devices + */ +void +mlevinit(Device *d) +{ + Device *x; + + mcatinit(d); + for(x=d->cat.first; x; x=x->link) + x->size = devsize(x); +} + +Devsize +mlevsize(Device *d) +{ + Device *x; + int n; + Devsize m, min; + + min = 0; + n = 0; + for(x=d->cat.first; x; x=x->link) { + m = x->size; + if(m == 0) { + m = devsize(x); + x->size = m; + } + if(min == 0 || m < min) + min = m; + n++; + } + return n * min; +} + +int +mlevread(Device *d, Off b, void *c) +{ + int n; + Device **list; + + n = d->cat.ndev; + list = d->private; + return devread(list[b%n], b/n, c); +} + +int +mlevwrite(Device *d, Off b, void *c) +{ + int n; + Device **list; + + n = d->cat.ndev; + list = d->private; + return devwrite(list[b%n], b/n, c); +} + +/* + * partition device + */ +void +partinit(Device *d) +{ + + devinit(d->part.d); + d->part.d->size = devsize(d->part.d); +} + +Devsize +partsize(Device *d) +{ + Devsize size, l; + + l = d->part.d->size / 100; + size = d->part.size * l; + if(size == 0) + size = l*100; + return size; +} + +int +partread(Device *d, Off b, void *c) +{ + Devsize base, size, l; + + l = d->part.d->size / 100; + base = d->part.base * l; + size = d->part.size * l; + if(size == 0) + size = l*100; + if(b < size) + return devread(d->part.d, base+b, c); + print("partread %lld %lld\n", (Wideoff)b, (Wideoff)size); + return 1; +} + +int +partwrite(Device *d, Off b, void *c) +{ + Devsize base, size, l; + + l = d->part.d->size / 100; + base = d->part.base * l; + size = d->part.size * l; + if(size == 0) + size = l*100; + if(b < size) + return devwrite(d->part.d, base+b, c); + print("partwrite %lld %lld\n", (Wideoff)b, (Wideoff)size); + return 1; +} + +/* + * mirror device + */ +void +mirrinit(Device *d) +{ + Device *x; + + mcatinit(d); + for(x=d->cat.first; x; x=x->link) + x->size = devsize(x); +} + +Devsize +mirrsize(Device *d) +{ + Device *x; + int n; + Devsize m, min; + + min = 0; + n = 0; + for(x=d->cat.first; x; x=x->link) { + m = x->size; + if(m == 0) { + m = devsize(x); + x->size = m; + } + if(min == 0 || m < min) + min = m; + n++; + } + return min; +} + +int +mirrread(Device *d, Off b, void *c) +{ + Device *x; + + for(x=d->cat.first; x; x=x->link) { + if(x->size == 0) + x->size = devsize(x); + if (devread(x, b, c) == 0) /* okay? */ + return 0; + } + // DANGER WILL ROBINSON - all copies of this block were bad + print("mirrread %Z error at block %lld\n", d, (Wideoff)b); + return 1; +} + +/* + * write the mirror(s) first so that a power outage, for example, will + * find the main device written only if the mirrors are too, thus + * checking the main device will also correctly check the mirror(s). + * + * devread and devwrite are synchronous; all buffering must be + * implemented at higher levels. + */ +static int +ewrite(Device *x, Off b, void *c) +{ + if(x->size == 0) + x->size = devsize(x); + if (devwrite(x, b, c) != 0) { + print("mirrwrite %Z error at block %lld\n", x, (Wideoff)b); + return 1; + } + return 0; +} + +static int +wrmirrs1st(Device *x, Off b, void *c) // write any mirrors of x, then x +{ + int e; + + if (x == nil) + return 0; + e = wrmirrs1st(x->link, b, c); + return e | ewrite(x, b, c); +} + +int +mirrwrite(Device *d, Off b, void *c) +{ + return wrmirrs1st(d->cat.first, b, c); +} diff -Nru /sys/src/fs/dev/wren.c /sys/src/fs/dev/wren.c --- /sys/src/fs/dev/wren.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/dev/wren.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,132 @@ +#include "all.h" + +typedef struct Wren Wren; +struct Wren +{ + long block; /* size of a block -- from config */ + Devsize nblock; /* number of blocks -- from config */ + long mult; /* multiplier to get physical blocks */ + Devsize max; /* number of logical blocks */ +}; + +void +wreninit(Device *d) +{ + int s; + uchar cmd[10], buf[8]; + Wren *dr; + + dr = d->private; + if(dr) + return; + dr = ialloc(sizeof(Wren), 0); + d->private = dr; + +loop: + memset(cmd, 0, sizeof(cmd)); + cmd[0] = 0x25; /* read capacity */ + s = scsiio(d, SCSIread, cmd, sizeof(cmd), buf, sizeof(buf)); + if(s) { + print("wreninit: %Z bad status %.4x\n", d, s); + delay(1000); + goto loop; + } + dr->nblock = + (buf[0]<<24) | + (buf[1]<<16) | + (buf[2]<<8) | + (buf[3]<<0); + dr->block = + (buf[4]<<24) | + (buf[5]<<16) | + (buf[6]<<8) | + (buf[7]<<0); + if(dr->block <= 0 || dr->block >= 16*1024) { + print(" wreninit %Z block size %ld setting to 512\n", d, dr->block); + dr->block = 512; + } + dr->mult = + (RBUFSIZE + dr->block - 1) / + dr->block; + dr->max = + (dr->nblock + 1) / dr->mult; + print(" drive %Z:\n", d); + print(" %lld blocks at %ld bytes each\n", + (Wideoff)dr->nblock, dr->block); + print(" %lld logical blocks at %d bytes each\n", + (Wideoff)dr->max, RBUFSIZE); + print(" %ld multiplier\n", + dr->mult); +} + +Devsize +wrensize(Device *d) +{ + Wren *dr; + + dr = d->private; + return dr->max; +} + +int +wreniocmd(Device *d, int io, Off b, void *c) +{ + Off l, m; + uchar cmd[10]; + Wren *dr; + + dr = d->private; + if(d == 0) { + print("wreniocmd: no drive - a=%Z b=%lld\n", d, (Wideoff)b); + return 0x40; + } + if(b >= dr->max) { + print("wreniocmd out of range a=%Z b=%lld\n", d, (Wideoff)b); + return 0x40; + } + + memset(cmd, 0, sizeof(cmd)); + cmd[0] = 0x28; /* extended read */ + if(io != SCSIread) + cmd[0] = 0x2a; /* extended write */ + + m = dr->mult; + l = b * m; + cmd[2] = l>>24; + cmd[3] = l>>16; + cmd[4] = l>>8; + cmd[5] = l; + + cmd[7] = m>>8; + cmd[8] = m; + + return scsiio(d, io, cmd, sizeof(cmd), c, RBUFSIZE); +} + +int +wrenread(Device *d, Off b, void *c) +{ + int s; + + s = wreniocmd(d, SCSIread, b, c); + if(s) { + print("wrenread: %Z(%lld) bad status %.4x\n", d, (Wideoff)b, s); + cons.nwormre++; + return 1; + } + return 0; +} + +int +wrenwrite(Device *d, Off b, void *c) +{ + int s; + + s = wreniocmd(d, SCSIwrite, b, c); + if(s) { + print("wrenwrite: %Z(%lld) bad status %.4x\n", d, (Wideoff)b, s); + cons.nwormwe++; + return 1; + } + return 0; +} diff -Nru /sys/src/fs/doc/changes /sys/src/fs/doc/changes --- /sys/src/fs/doc/changes Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/doc/changes Tue Nov 1 00:00:00 2011 @@ -0,0 +1,68 @@ + changes to Ken's file server to make this 63-bit file server + + Geoff Collyer + July—October 2004 + +note: 2⁶⁳=9,223,372,036,854,775,808 or 8EB (9.2×10ⁱ⁸) + +• identified longs that refer to offsets, sizes and block numbers, and +changed them to type Off (vlong); fixed all print formats to match. +fixed byte-swapping for the 'x' config to match. + +• fixed VLONG 9p1 message packing and unpacking macros to actually +handle 64-bit offsets and sizes. + +• implemented triple-indirect blocks. affected code in + dev/cw.c port/con.c port/dentry.c port/sub.c + port/chk.c port/console.c port/portdat.h + +• Fri Aug 6 16:50:59 PDT 2004 + ; ./sizes + Plan 9 v4 63-bit file server + sizeof(Dentry) = 124 + sizeof(Cache) = 88 + +• added long(er) file name components (56 bytes), long enough for all but one + name in my /.longnames file (68-byte .xml name). + +• Fri Aug 6 21:43:41 PDT 2004 + ; ./sizes + Plan 9 v4 63-bit file server sizes + sizeof(Dentry) = 160 + sizeof(Cache) = 88 + +• touched up lib.h (from libc.h) to bring it up to date with formatting + functions, verbs & flags. +• check now reports stack usage: 320 bytes upon entry to fsck first time, + 92 bytes of stack per recursion. given 16000 bytes of stack, + that's 170 recursions maximum. +• booted xtc (terminal) from fs64 (used fs64 as main file system) + +note: current file server with triple-indirect blocks at 4k block size + has a maximum file size of ~505GB (5.42×10ⁱⁱ). + with quadruple-indirect blocks, max would be ~275TB @ 4k block size. + +• got igbe fs driver working (a couple small changes) +• eliminated some gotos (started with 580, down to 454) +• added quadruple indirect blocks: lets us reach 2⁶⁳ with a 32kB block size +• got igbe boot driver & pxe booting working +• on-disk qid paths are now Offs, but 9p1 qids on the wire are still ulongs +• generalised & parameterised indirect block implementation +• tested with plain w0 fs, cached fake worm on w0, cw jukebox (hp 160fx) +• ip directories in fs & fs64 are identical except for whitespace and + goto-elimination +• replaced most of nemo's ide code with newer ide code lifted from 9load, + then from cpu kernel (sdata.c & support). this brings us dma, rwm & lba48, + finds ide controllers by itself, even pci ones, & copes with dead drives + (i.e., doesn't panic). +• fixed long-standing bug that caused a 5-second delay before each console + prompt on systems without a serial console. +• further type parameterisation: Userid (short), Timet (long), Devsize (vlong). + Comment on v7 kernel portability work, quoting scj & dmr from BSTJ v57 + #6 part 2., p. 2038: ``The important data types used within the + system were identified and specified using typedef: disk offsets, + absolute times, internal device names, and the like. This effort was + carried out by K. Thompson.'' +• corrected compat.h dependencies in mkfiles +• eliminated all warnings +• implemented truncation via wstat diff -Nru /sys/src/fs/doc/words /sys/src/fs/doc/words --- /sys/src/fs/doc/words Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/doc/words Tue Nov 1 00:00:00 2011 @@ -0,0 +1,28 @@ +'emelie' is for any PC with supported hardware excluding the SONY +jukebox, and will make an object '9emeliefs' and use a 16KB block +size. It's set up for the US Eastern time zone. choline is similar, +but with conf.nfile cranked up. + +fs uses a 4KB block size, rereads all blocks written to the WORM, and +is configured for the US Pacific time zone and with more `large +message' buffers than is usual (for gigabit Ethernet). fs64 is +similar but uses an 8KB block size and 64-bit (rather than 32-bit) +file sizes, offsets and block numbers, and consequently can only serve +9P2000, not 9P1. + +9netics32.16k is like fs, but uses a 16KB block size and does not +reread blocks written to the WORM. 9netics64.8k is like fs64, but +uses an 8KB block size and does not reread blocks written to the WORM. + +To spin-off a new version to play with, say 'test': + + cd /sys/src/fs + mkdir test + cp emelie/9emeliefs.c test/9testfs.c + cp emelie/dat.h emelie/fns.h emelie/io.h emelie/mem.h test + sed '1s/emelie/test/' test/mkfile + +and hack as appropriate. + +The mkfiles aren't quite right yet to make this as automatic as it +should be. There are a lot of rough edges. diff -Nru /sys/src/fs/doc/worm.fs /sys/src/fs/doc/worm.fs --- /sys/src/fs/doc/worm.fs Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/doc/worm.fs Tue Nov 1 00:00:00 2011 @@ -0,0 +1,128 @@ +# fs: new main file server on 633MHz machine with 4 IDE disks & 4K blocks +# was called rosewood at first +config h0 +service fs +ip0 66.120.90.177 +ipgw0 66.120.90.186 +ipmask0 255.255.255.0 +ipsntp 66.120.90.185 +filsys main c{p(h0)0.25p(h2)0.25}f{p(h0)25.75p(h2)25.75} +filsys dump o +filsys other h3 +# later added: filsys spare h1 +# ream other +# ream main +# recover main +allow +end + +h0 20GB "main": 25% cache and 75% fake-worm; +h1 40GB "spare": holds test venti used for backup +h2 20GB "main": (h1.0.0) a mirror of h0. +h3 40GB "other": (h1.1.0) ``scratch'' space + +--- +halted. press a key to reboot. + +ether#0: i82557: port 0x9400 irq 10: 00A0C9E02756 +dev A0 port 1F0 config 427A capabilities 2F00 mwdma 0007 udma 103F +dev A0 port 170 config 427A capabilities 2F00 mwdma 0007 udma 043F +dev B0 port 170 config 045A capabilities 0F00 mwdma 0007 udma 043F +found 9PCFSIMR. attr 0x0 start 0x423 len 391193 +.239705.............................+41712.....+181524=462941 +entry: 0x80100020 +cpu0: 635MHz GenuineIntel PentiumIII/Xeon (cpuid: AX 0x0686 DX 0x383f9ff) +ether0: i82557: 100Mbps port 0x9400 irq 10: 00a0c9e02756 + +iobufinit + 114253 buffers; 14281 hashes + mem left = 44040191 + out of = 528482304 +nvr read +found plan9.nvr attr 0x0 start 0x18c len 677 +for config mode hit a key within 5 seconds + +config: filsys main c{p(h0)0.25p(h2)0.25}f{p(h0)25.75p(h2)25.75} +config: filsys dump o +config: filsys other h3 +config: recover main +config: ream other +config: allow +config: end +ysinits +config h0 + devinit h0 +i hd0: DW CDW02E0-B00HB0F lba/atapi/drqintr: 1/0/0 C/H/S: 0/0/0 CAP: 39102336 +hd0: LBA 39102336 sectors +ideinit(device 9ce00948 ctrl 0 targ 0) driveno 0 dp 802eff24 +config block written +config h0 + devinit h0 +ideinit(device 9ce00988 ctrl 0 targ 0) driveno 0 dp 802eff24 +service rosewood +ipauth 0.0.0.0 +ipsntp 66.120.90.185 +ip0 66.120.90.194 +ipgw0 66.120.90.186 +ipmask0 255.255.255.0 +filsys main c{p(h0)0.25p(h2)0.25}f{p(h0)25.75p(h2)25.75} +filsys dump o +filsys other h3 +sysinit: main +recover: c{p(h0)0.25p(h2)0.25}f{p(h0)25.75p(h2)25.75} + devinit {p(h0)0.25p(h2)0.25} + devinit p(h0)0.25 + devinit h0 +ideinit(device 9ce00a68 ctrl 0 targ 0) driveno 0 dp 802eff24 +atasize(9ce00a68): 39102336 -> 4887552 + devinit p(h2)0.25 + devinit h2 +i hd2: DW CDW02E0-B00HB0F lba/atapi/drqintr: 1/0/0 C/H/S: 0/0/0 CAP: 39102336 +hd2: LBA 39102336 sectors +ideinit(device 9ce00ae8 ctrl 0 targ 2) driveno 2 dp 802f4324 +atasize(9ce00ae8): 39102336 -> 4887552 + devinit f{p(h0)25.75p(h2)25.75} +fworm init + devinit {p(h0)25.75p(h2)25.75} + devinit p(h0)25.75 + devinit h0 +ideinit(device 9ce00bc8 ctrl 0 targ 0) driveno 0 dp 802eff24 +atasize(9ce00bc8): 39102336 -> 4887552 + devinit p(h2)25.75 + devinit h2 +ideinit(device 9ce00c48 ctrl 0 targ 2) driveno 2 dp 802f4324 +atasize(9ce00c48): 39102336 -> 4887552 +dump 2 is good; 5 next +dump 5 is good; 494408 next +dump 494408 is good; 495193 next +dump 495193 is good; 495224 next +dump 495224 is good; 496007 next +dump 496007 is good; 496062 next +dump 496062 is good; 496089 next +dump 496089 is good; 496096 next +dump 496096 is good; 496118 next +dump 496118 is good; 496882 next +fworm: read 496882 +cache init c{p(h0)0.25p(h2)0.25}f{p(h0)25.75p(h2)25.75} +done cacheinit +done recover + devinit c{p(h0)0.25p(h2)0.25}f{p(h0)25.75p(h2)25.75} +sysinit: dump + devinit o{p(h0)0.25p(h2)0.25}f{p(h0)25.75p(h2)25.75} +sysinit: other + devream: h3 1 + devinit h3 +i hd3: AMTXRO4 0K042H lba/atapi/drqintr: 1/0/0 C/H/S: 0/0/0 CAP: 78198750 +hd3: LBA 78198750 sectors +ideinit(device 9ce00ca8 ctrl 0 targ 3) driveno 3 dp 802f44f0 +atasize(9ce00ca8): 78198750 -> 9774592 +ether0o: 00a0c9e02756 66.120.90.194 +ether0i: 00a0c9e02756 66.120.90.194 +next dump at Mon Sep 10 05:00:00 2001 +current fs is "main" +il: allocating il!66.120.90.189!23230 +41 uids read, 21 groups used +rosewood as of Sun Sep 9 16:27:27 2001 + last boot Mon Sep 10 00:56:10 2001 +touch superblock 496118 +rosewood: diff -Nru /sys/src/fs/doc/worm.fs64 /sys/src/fs/doc/worm.fs64 --- /sys/src/fs/doc/worm.fs64 Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/doc/worm.fs64 Tue Nov 1 00:00:00 2011 @@ -0,0 +1,81 @@ +# 4k blocks +fs64: printconf +config w0 +service fs64 +filsys main w0 +ipauth 0.0.0.0 +ipsntp 216.240.55.164 +ip0 216.240.55.167 +ipmask0 255.255.255.224 +end +fs64: + +# 4k blocks +Wed Sep 1 17:46:10 PDT 2004 +fs64: printconf +config w0 +service fs64 +filsys main cp(w0)0.20fp(w0)20.80 +filsys dump o +ipauth 0.0.0.0 +ipsntp 216.240.55.164 +ip0 216.240.55.167 +ipmask0 255.255.255.224 +end +fs64: + +# 8k blocks, preparing for worm jukebox +Thu Sep 2 00:17:19 PDT 2004 +fs64: printconf +config w0 +service fs64 +filsys main cp(w0)0.20fp(w0)20.80 +filsys dump o +ipauth 0.0.0.0 +ipsntp 216.240.55.164 +ip0 216.240.55.167 +ipmask0 255.255.255.224 +end +fs64: + +# 8k blocks with hp 160fx worm jukebox +# only 1 MO disc inside currently +Fri Sep 3 23:06:30 PDT 2004 +fs64: printconf +config w0 +service fs64 +filsys main cp(w0)1.99j(w1.<1-5>.0)(l<0-15>l<16-31>) +filsys dump o +ipauth 0.0.0.0 +ipsntp 216.240.55.164 +ip0 216.240.55.167 +ipmask0 255.255.255.224 +end + +# add two ide disks: one is the juke's mirror, the other is new other +Thu Sep 30 00:38:38 PDT 2004 +fs64: printconf +config w0 +service fs64 +filsys main cp(w0)1.99{h0j(w1.<1-5>.0)(l<0-15>l<16-31>)} +filsys dump o +filsys other h2 +ipauth 0.0.0.0 +ipsntp 216.240.55.164 +ip0 216.240.55.167 +ipmask0 255.255.255.224 +end + +# now has 8 WO disks and 1 RW disk currently (25 nov 2004), +# treat 8 WO disks as one set of disks. +fs64: printconf +config w0 +service fs64 +filsys main cp(w0)1.99{h0j(w1.<1-5>.0)(l<0-7>l<64-71>l<8-62>l<72-126>)} +filsys dump o +filsys other h2 +ipauth 0.0.0.0 +ipsntp 216.240.55.164 +ip0 216.240.55.167 +ipmask0 255.255.255.224 +end diff -Nru /sys/src/fs/doc/worms.32-bit /sys/src/fs/doc/worms.32-bit --- /sys/src/fs/doc/worms.32-bit Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/doc/worms.32-bit Tue Nov 1 00:00:00 2011 @@ -0,0 +1,150 @@ +/* configuration commands */ + +/* configuration commands */ +filsys main j(w6)(r5.<0-18>r5.<20-38>) +e!0!helix:/386/9pcfs + +/* AUDIO */ +ip/dhcp +ip/tftpd +con /dev/eia1 +bootp()phaeton:/sys/src/fs/audio/audio + +/* AUDIO */ +touch superblock 2835710 + +config w2 +service audio +filsys main cw2j(w5w4)(l<0-39>) +filsys dump o +ip 135.104.3.106 +ipgw 135.104.3.1 +ipmask 255.255.255.0 +ipauth 135.104.9.7 +end + +/* RORO running 9pcfs (16KB blocks) */ +config w2 +service roro +filsys main [w2w3] +ip 135.104.9.29 +ipgw 135.104.9.1 +ipmask 255.255.255.0 +ipauth 135.104.9.7 +end + +/* RORO running plan9pc (4KB blocks) */ +config w1 +service roro +filsys main w1 +ip 135.104.9.29 +ipgw 135.104.9.1 +ipmask 255.255.255.0 +ipauth 135.104.9.7 +end + +/* EMELIE */ +e!0!helix:/usr/ken/fs/pc/9pcfs +130 disks loaded + +/* BOOTES 00006b8244f8 */ +superblocks: +bootes 44930632 + dump 44945151 is good; 44945224 next + 44945220 cwroot; 44945223 roroot +fornax 8917649 + dump 8919157 is good; 8919167 next + 8919163 cwroot; 8919166 roroot + sbaddr = 8919379 + craddr = 8919383 8919383 + +/* BOOTES little endian */ +config xw5 +service bootes +filsys main c[xw5]j(w1.1.8xw1.1.1)(r<0-44>r<50-94>) +filsys dump o +filsys fornax c[xw3]j(w1.2.8xw1.2.0)(r<0-18>) +filsys fdump o +ip 135.104.9.30 +ipgw 135.104.9.1 +ipmask 255.255.255.0 +ipauth 135.104.9.7 +end + +/* BOOTES little endian with only fornax filesystem */ +config xw5 +service bootes +//filsys main c[xw5]j(w1.1.8xw1.1.1)(r<0-44>r<50-94>) +//filsys dump o +filsys main c[xw3]j(w1.2.8xw1.2.0)(r<0-18>) +filsys dump o +ip 135.104.9.30 +ipgw 135.104.9.1 +ipmask 255.255.255.0 +ipauth 135.104.9.7 +end + +/* BOOTES little endian with only bootes filesystem */ +config xw5 +service bootes +filsys main c[xw5]j(w1.2.8xw1.2.1)(r<0-44>r<50-94>) +filsys dump o +ip 135.104.9.30 +ipgw 135.104.9.1 +ipmask 255.255.255.0 +ipauth 135.104.9.7 +end + +/* BOOTES 00006b8244f8 big endian */ +config w5 +service bootes +filsys main c[w5]j(w0.1.8w0.1.1)(r<0-44>r<50-94>) +filsys dump o +//filsys fornax c[w3]j(w0.2.8w0.2.0)(r<0-18>) +//filsys fdump o +ip 135.104.9.30 +ipgw 135.104.9.1 +ipmask 255.255.255.0 +ipauth 135.104.9.7 +end + +// jukefs +// DSIZE = 39815 +config w6 +service jukefs +filsys main cw6j(w5w<1-4>)(r<0-140>r<144-284>) +filsys dump o +ip 135.104.9.10 +ipgw 135.104.9.1 +ipmask 255.255.255.0 +ipauth 135.104.9.7 +end + +// emelie +// DSIZE = 157933 +config w1.0.0 +service emelie +filsys main c[w1.<0-5>.0]j(w6w5w4w3w2)(l<0-236>l<238-474>) +filsys dump o +filsys other [w1.<9-14>.0] +filsys temp [w1.8.0] +ipauth 135.104.9.7 +ip 135.104.9.42 +ipgw 135.104.9.1 +ipmask 255.255.255.0 +ipsntp 135.104.9.52 +end + +// choline +// DSIZE = 79563-1 +config w1.0.0 +service choline +filsys main c[w<1-3>]j(w1.<6-0>.0)(l<0-124>l<128-252>) +filsys dump o +filsys other [w<4-6>] +ipauth 135.104.9.7 +ip 135.104.72.2 +ipgw 135.104.72.1 +ipmask 255.255.255.0 +ipsntp 135.104.9.52 +end diff -Nru /sys/src/fs/emelie/9emeliefs.c /sys/src/fs/emelie/9emeliefs.c --- /sys/src/fs/emelie/9emeliefs.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/emelie/9emeliefs.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,177 @@ +#include "all.h" +#include "mem.h" +#include "io.h" +#include "ureg.h" + +#include "../pc/dosfs.h" + +/* + * setting this to zero permits the use of discs of different sizes, but + * can make jukeinit() quite slow while the robotics work through each disc + * twice (once per side). + */ +int FIXEDSIZE = 1; + +#ifndef DATE +#define DATE 1094098624L +#endif + +Timet mktime = DATE; /* set by mkfile */ +Startsb startsb[] = +{ + "main", 2, + 0 +}; + +Dos dos; + +static struct +{ + char *name; + Off (*read)(int, void*, long); + Devsize (*seek)(int, Devsize); + Off (*write)(int, void*, long); + int (*part)(int, char*); +} nvrdevs[] = { + { "fd", floppyread, floppyseek, floppywrite, 0, }, + { "hd", ataread, ataseek, atawrite, setatapart, }, + /* { "sd", scsiread, scsiseek, scsiwrite, setscsipart, }, */ + { 0, }, +}; + +void apcinit(void); + +void +otherinit(void) +{ + int dev, i, nfd, nhd, s; + char *p, *q, buf[sizeof(nvrfile)+8]; + + kbdinit(); + printcpufreq(); + etherinit(); + scsiinit(); + apcinit(); + + s = spllo(); + nhd = atainit(); + nfd = floppyinit(); + dev = 0; + if(p = getconf("nvr")){ + strncpy(buf, p, sizeof(buf)-2); + buf[sizeof(buf)-1] = 0; + p = strchr(buf, '!'); + q = strrchr(buf, '!'); + if(p == 0 || q == 0 || strchr(p+1, '!') != q) + panic("malformed nvrfile: %s\n", buf); + *p++ = 0; + *q++ = 0; + dev = strtoul(p, 0, 0); + strcpy(nvrfile, q); + p = buf; + } else + if(p = getconf("bootfile")){ + strncpy(buf, p, sizeof(buf)-2); + buf[sizeof(buf)-1] = 0; + p = strchr(buf, '!'); + q = strrchr(buf, '!'); + if(p == 0 || q == 0 || strchr(p+1, '!') != q) + panic("malformed bootfile: %s\n", buf); + *p++ = 0; + *q = 0; + dev = strtoul(p, 0, 0); + p = buf; + } else + if(nfd) + p = "fd"; + else + if(nhd) + p = "hd"; + else + p = "sd"; + + for(i = 0; nvrdevs[i].name; i++){ + if(strcmp(p, nvrdevs[i].name) == 0){ + dos.dev = dev; + if(nvrdevs[i].part && (*nvrdevs[i].part)(dos.dev, "disk") == 0) + break; + dos.read = nvrdevs[i].read; + dos.seek = nvrdevs[i].seek; + dos.write = nvrdevs[i].write; + break; + } + } + if(dos.read == 0) + panic("no device for nvram\n"); + if(dosinit(&dos) < 0) + panic("can't init dos dosfs on %s\n", p); + splx(s); +} + +void +touser(void) +{ + int i; + + settime(rtctime()); + boottime = time(); + + print("sysinit\n"); + sysinit(); + + userinit(floppyproc, 0, "floppyproc"); + /* + * Ethernet i/o processes + */ + etherstart(); + + + /* + * read ahead processes + */ + userinit(rahead, 0, "rah"); + + /* + * server processes + */ + for(i=0; itext = "scp"; + synccopy(); +} + +void +localconfinit(void) +{ + conf.nfile = 40000; + conf.nodump = 0; + conf.firstsb = 13219302; + conf.recovsb = 0; + conf.ripoff = 1; + conf.nlgmsg = 100; + conf.nsmmsg = 500; + + conf.minuteswest = 5*60; + conf.dsttime = 1; +} + +int (*fsprotocol[])(Msgbuf*) = { + serve9p1, + serve9p2, + nil, +}; diff -Nru /sys/src/fs/emelie/dat.h /sys/src/fs/emelie/dat.h --- /sys/src/fs/emelie/dat.h Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/emelie/dat.h Tue Nov 1 00:00:00 2011 @@ -0,0 +1,35 @@ +/* + * The most fundamental constant. + * The code will not compile with RBUFSIZE made a variable; + * for one thing, RBUFSIZE determines FEPERBUF, which determines + * the number of elements in a free-list-block array. + */ +#define RBUFSIZE (16*1024) /* raw buffer size */ + +#include "../port/portdat.h" + +extern Mach mach0; + +typedef struct Segdesc Segdesc; +struct Segdesc +{ + ulong d0; + ulong d1; +}; + +typedef struct Mbank { + ulong base; + ulong limit; +} Mbank; + +#define MAXBANK 8 + +typedef struct Mconf { + Lock; + Mbank bank[MAXBANK]; + int nbank; + ulong topofmem; +} Mconf; +extern Mconf mconf; + +extern char nvrfile[128]; diff -Nru /sys/src/fs/emelie/fns.h /sys/src/fs/emelie/fns.h --- /sys/src/fs/emelie/fns.h Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/emelie/fns.h Tue Nov 1 00:00:00 2011 @@ -0,0 +1,87 @@ +ulong strtoul(char*, char**, int); +vlong strtoll(char*, char**, int); + +#include "../port/portfns.h" + +void aamloop(int); +void cgaputc(int); +void cgaputs(char*, int); +int cistrcmp(char*, char*); +int cistrncmp(char*, char*, int); +void (*coherence)(void); +void etherinit(void); +void etherstart(void); +int floppyinit(void); +void floppyproc(void); +Off floppyread(int, void*, long); +Devsize floppyseek(int, Devsize); +Off floppywrite(int, void*, long); +void fpinit(void); +char* getconf(char*); +ulong getcr0(void); +ulong getcr2(void); +ulong getcr4(void); +int getfields(char*, char**, int, int, char*); +ulong getstatus(void); +int atainit(void); +Off ataread(int, void*, long); +Devsize ataseek(int, Devsize); +Off atawrite(int, void*, long); +void i8042a20(void); +void i8042reset(void); +int inb(int); +void insb(int, void*, int); +ushort ins(int); +void inss(int, void*, int); +ulong inl(int); +void insl(int, void*, int); +void kbdinit(void); +int kbdintr0(void); +int kbdgetc(void); +long* mapaddr(ulong); +void microdelay(int); +void mmuinit(void); +uchar nvramread(int); +void outb(int, int); +void outsb(int, void*, int); +void outs(int, ushort); +void outss(int, void*, int); +void outl(int, ulong); +void outsl(int, void*, int); +void printcpufreq(void); +void putgdt(Segdesc*, int); +void putidt(Segdesc*, int); +void putcr3(ulong); +void putcr4(ulong); +void puttr(ulong); +void rdmsr(int, vlong*); +void wrmsr(int, vlong); +void (*cycles)(uvlong*); +void scsiinit(void); +Off scsiread(int, void*, long); +Devsize scsiseek(int, Devsize); +Off scsiwrite(int, void*, long); +int setatapart(int, char*); +int setscsipart(int, char*); +void setvec(int, void (*)(Ureg*, void*), void*); +int tas(Lock*); +void trapinit(void); +void uartspecial(int, void (*)(int), int (*)(void), int); +int uartgetc(void); +void uartputc(int); +void wbflush(void); +void cpuid(char*, int*, int*); + +#define PADDR(a) ((ulong)(a)&~KZERO) + +/* pata */ +void ideinit(Device *d); +Devsize idesize(Device *d); +int ideread(Device *d, Devsize, void*); +int idewrite(Device *d, Devsize, void*); + +/* sata */ +void mvideinit(Device *d); +Devsize mvidesize(Device *d); +int mvideread(Device *d, Devsize, void*); +int mvidewrite(Device *d, Devsize, void*); diff -Nru /sys/src/fs/emelie/io.h /sys/src/fs/emelie/io.h --- /sys/src/fs/emelie/io.h Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/emelie/io.h Tue Nov 1 00:00:00 2011 @@ -0,0 +1,247 @@ +/* + * programmable interrupt vectors (for the 8259's) + */ +enum +{ + Bptvec= 3, /* breakpoints */ + Mathemuvec= 7, /* math coprocessor emulation interrupt */ + Mathovervec= 9, /* math coprocessor overrun interrupt */ + Matherr1vec= 16, /* math coprocessor error interrupt */ + Faultvec= 14, /* page fault */ + + Int0vec= 24, /* first 8259 */ + Clockvec= Int0vec+0, /* clock interrupts */ + Kbdvec= Int0vec+1, /* keyboard interrupts */ + Uart1vec= Int0vec+3, /* modem line */ + Uart0vec= Int0vec+4, /* serial line */ + PCMCIAvec= Int0vec+5, /* PCMCIA card change */ + Floppyvec= Int0vec+6, /* floppy interrupts */ + Parallelvec= Int0vec+7, /* parallel port interrupts */ + Int1vec= Int0vec+8, + Ethervec= Int0vec+10, /* ethernet interrupt */ + Mousevec= Int0vec+12, /* mouse interrupt */ + Matherr2vec= Int0vec+13, /* math coprocessor */ + ATA0vec= Int0vec+14, /* hard disk */ + + Syscallvec= 64, +}; + +/* + * 8259 interrupt controllers + */ +enum +{ + Int0ctl= 0x20, /* control port (ICW1, OCW2, OCW3) */ + Int0aux= 0x21, /* everything else (ICW2, ICW3, ICW4, OCW1) */ + Int1ctl= 0xA0, /* control port */ + Int1aux= 0xA1, /* everything else (ICW2, ICW3, ICW4, OCW1) */ + + Icw1= 0x10, /* select bit in ctl register */ + Ocw2= 0x00, + Ocw3= 0x08, + + EOI= 0x20, /* non-specific end of interrupt */ + + Elcr1= 0x4D0, /* Edge/Level Triggered Register */ + Elcr2= 0x4D1, +}; + +extern int int0mask; /* interrupts enabled for first 8259 */ +extern int int1mask; /* interrupts enabled for second 8259 */ + +#define NVRAUTHADDR 0 +#define LINESIZE 0 + +enum { + MaxEISA = 16, + EISAconfig = 0xC80, + + MaxScsi = 4, + NTarget = 16, + + MaxEther = 4, +}; + +#define DMAOK(x, l) ((ulong)(((ulong)(x))+(l)) < (ulong)(KZERO|16*1024*1024)) + +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 */ +}; + +#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) + +/* + * PCI support code. + */ +enum { /* type 0 and type 1 pre-defined header */ + PciVID = 0x00, /* vendor ID */ + PciDID = 0x02, /* device ID */ + PciPCR = 0x04, /* command */ + PciPSR = 0x06, /* status */ + PciRID = 0x08, /* revision ID */ + PciCCRp = 0x09, /* programming interface class code */ + PciCCRu = 0x0A, /* sub-class code */ + PciCCRb = 0x0B, /* base class code */ + PciCLS = 0x0C, /* cache line size */ + PciLTR = 0x0D, /* latency timer */ + PciHDT = 0x0E, /* header type */ + PciBST = 0x0F, /* BIST */ + + PciBAR0 = 0x10, /* base address */ + PciBAR1 = 0x14, + + PciINTL = 0x3C, /* interrupt line */ + PciINTP = 0x3D, /* interrupt pin */ +}; + +enum { /* type 0 pre-defined header */ + PciBAR2 = 0x18, + PciBAR3 = 0x1C, + PciBAR4 = 0x20, + PciBAR5 = 0x24, + PciCIS = 0x28, /* cardbus CIS pointer */ + PciSVID = 0x2C, /* subsystem vendor ID */ + PciSID = 0x2E, /* cardbus CIS pointer */ + PciEBAR0 = 0x30, /* expansion ROM base address */ + PciMGNT = 0x3E, /* burst period length */ + PciMLT = 0x3F, /* maximum latency between bursts */ +}; + +enum { /* type 1 pre-defined header */ + PciPBN = 0x18, /* primary bus number */ + PciSBN = 0x19, /* secondary bus number */ + PciUBN = 0x1A, /* subordinate bus number */ + PciSLTR = 0x1B, /* secondary latency timer */ + PciIBR = 0x1C, /* I/O base */ + PciILR = 0x1D, /* I/O limit */ + PciSPSR = 0x1E, /* secondary status */ + PciMBR = 0x20, /* memory base */ + PciMLR = 0x22, /* memory limit */ + PciPMBR = 0x24, /* prefetchable memory base */ + PciPMLR = 0x26, /* prefetchable memory limit */ + PciPUBR = 0x28, /* prefetchable base upper 32 bits */ + PciPULR = 0x2C, /* prefetchable limit upper 32 bits */ + PciIUBR = 0x30, /* I/O base upper 16 bits */ + PciIULR = 0x32, /* I/O limit upper 16 bits */ + PciEBAR1 = 0x28, /* expansion ROM base address */ + PciBCR = 0x3E, /* bridge control register */ +}; + +typedef struct Pcidev Pcidev; +typedef struct Pcidev { + int tbdf; /* type+bus+device+function */ + ushort vid; /* vendor ID */ + ushort did; /* device ID */ + + struct { + ulong bar; /* base address */ + int size; + } mem[6]; + + uchar rid; + uchar ccrp; + uchar ccrb; + uchar intl; /* interrupt line */ + ushort ccru; /* is uchar in cpu kernel */ + ulong pcr; + + Pcidev* list; + Pcidev* bridge; /* down a bus */ + Pcidev* link; /* next device on this bno */ +} Pcidev; + +extern int pcicfgr8(Pcidev*, int); +extern int pcicfgr16(Pcidev*, int); +extern int pcicfgr32(Pcidev*, int); +extern void pcicfgw8(Pcidev*, int, int); +extern void pcicfgw16(Pcidev*, int, int); +extern void pcicfgw32(Pcidev*, int, int); +extern void pciclrmwi(Pcidev*); +extern void pcihinv(Pcidev*, ulong); +extern Pcidev* pcimatch(Pcidev*, int, int); +extern Pcidev* pcimatchtbdf(int); +extern void pcireset(void); +extern void pcisetbme(Pcidev*); +extern void pciclrbme(Pcidev*); + +/* + * a parsed plan9.ini line + */ +#define ISAOPTLEN 16 +#define NISAOPT 8 + +typedef struct ISAConf { + char type[NAMELEN]; + ulong port; + ulong irq; + ulong dma; + ulong mem; + ulong size; + ulong freq; + + int nopt; + char opt[NISAOPT][ISAOPTLEN]; +} ISAConf; + +extern int isaconfig(char*, int, ISAConf*); + +/* + * SCSI support code. + */ +enum { + STblank =-6, /* blank block */ + STnomem =-5, /* buffer allocation failed */ + STtimeout =-4, /* bus timeout */ + STownid =-3, /* playing with myself */ + STharderr =-2, /* controller error of some kind */ + STinit =-1, /* */ + STok = 0, /* good */ + STcheck = 0x02, /* check condition */ + STcondmet = 0x04, /* condition met/good */ + STbusy = 0x08, /* busy */ + STintok = 0x10, /* intermediate/good */ + STintcondmet = 0x14, /* intermediate/condition met/good */ + STresconf = 0x18, /* reservation conflict */ + STterminated = 0x22, /* command terminated */ + STqfull = 0x28, /* queue full */ +}; + +typedef struct Target { + int ctlrno; + int targetno; + uchar* inquiry; + uchar* sense; + + QLock; + char id[NAMELEN]; + int ok; + + char fflag; + Filter work[3]; + Filter rate[3]; +} Target; + +typedef int (*Scsiio)(Target*, int, uchar*, int, void*, int*); diff -Nru /sys/src/fs/emelie/mem.h /sys/src/fs/emelie/mem.h --- /sys/src/fs/emelie/mem.h Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/emelie/mem.h Tue Nov 1 00:00:00 2011 @@ -0,0 +1,85 @@ +#define BY2WD 4 /* bytes per word */ +#define BY2V 8 /* bytes per vlong */ +#define BY2PG 4096 /* bytes per page */ +#define PGSHIFT 12 /* log(BY2PG) */ +#define PGROUND(s) (((s)+(BY2PG-1))&~(BY2PG-1)) +#define MB (1024*1024) + +#define HZ (82) /* clock frequency */ +#define TK2MS(t) (((ulong)(t)*1000)/HZ) /* ticks to milliseconds - beware rounding */ +#define MS2TK(t) (((ulong)(t)*HZ)/1000) /* milliseconds to ticks - beware rounding */ +#define TK2SEC(t) ((t)/HZ) /* ticks to seconds */ + +/* + * Fundamental addresses + */ +#define IDTADDR 0x80000800 /* idt */ +#define APBOOTSTRAP 0x80001000 /* AP bootstrap code */ +#define CONFADDR 0x80001200 /* info passed from boot loader */ +#define CPU0PDB 0x80002000 /* bootstrap processor PDB */ +#define CPU0PTE 0x80003000 /* bootstrap processor PTE's for 0-2MB */ +#define CPU0MACHPTE 0x80004000 /* bootstrap processor PTE for MACHADDR */ +#define CPU0MACH 0x80005000 /* Mach for bootstrap processor */ + +#define KZERO 0x80000000 /* base of kernel address space */ +#define KTZERO 0x80100000 /* first address in kernel text */ + +#define MACHSIZE 4096 + +/* + * known 80386 segments (in GDT) and their selectors + */ +#define NULLSEG 0 /* null segment */ +#define KDSEG 1 /* kernel data/stack */ +#define KESEG 2 /* kernel executable */ +#define UDSEG 3 /* user data/stack */ +#define UESEG 4 /* user executable */ +#define TSSSEG 5 /* task segment */ +#define N386SEG 6 /* number of segments */ + +#define SELGDT (0<<3) /* selector is in gdt */ +#define SELLDT (1<<3) /* selector is in ldt */ + +#define SELECTOR(i, t, p) (((i)<<3) | (t) | (p)) + +#define NULLSEL SELECTOR(NULLSEG, SELGDT, 0) +#define KESEL SELECTOR(KESEG, SELGDT, 0) +#define KDSEL SELECTOR(KDSEG, SELGDT, 0) +#define UESEL SELECTOR(UESEG, SELGDT, 3) +#define UDSEL SELECTOR(UDSEG, SELGDT, 3) +#define TSSSEL SELECTOR(TSSSEG, SELGDT, 0) + +/* + * fields in segment descriptors + */ +#define SEGDATA (0x10<<8) /* data/stack segment */ +#define SEGEXEC (0x18<<8) /* executable segment */ +#define SEGTSS (0x9<<8) /* TSS segment */ +#define SEGCG (0x0C<<8) /* call gate */ +#define SEGIG (0x0E<<8) /* interrupt gate */ +#define SEGTG (0x0F<<8) /* task gate */ +#define SEGTYPE (0x1F<<8) + +#define SEGP (1<<15) /* segment present */ +#define SEGPL(x) ((x)<<13) /* priority level */ +#define SEGB (1<<22) /* granularity 1==4k (for expand-down) */ +#define SEGG (1<<23) /* granularity 1==4k (for other) */ +#define SEGE (1<<10) /* expand down */ +#define SEGW (1<<9) /* writable (for data/stack) */ +#define SEGR (1<<9) /* readable (for code) */ +#define SEGD (1<<22) /* default 1==32bit (for code) */ + +/* + * physical MMU + */ +#define PTEVALID (1<<0) +#define PTEUNCACHED (1<<4) +#define PTEWRITE (1<<1) +#define PTERONLY (0<<1) +#define PTEKERNEL (0<<2) +#define PTEUSER (1<<2) +#define PTESIZE (1<<7) + +#define MACHADDR ((ulong)&mach0) /* hack number 1 */ + +#define IFLAG 0x200 /* psw: interrupt enable, to be accurate */ diff -Nru /sys/src/fs/emelie/mkfile /sys/src/fs/emelie/mkfile --- /sys/src/fs/emelie/mkfile Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/emelie/mkfile Tue Nov 1 00:00:00 2011 @@ -0,0 +1,148 @@ +CONF=emelie +p=9 + +objtype=386 +text = "scp"; + synccopy(); +} + +void +localconfinit(void) +{ + /* conf.nfile = 60000; */ /* from emelie */ + conf.nodump = 0; + conf.dumpreread = 1; + if (0) + conf.idedma = 0; /* for old machines */ + conf.firstsb = 0; /* time- & jukebox-dependent optimisation */ + conf.recovsb = 0; /* 971531 is 24 june 2003, before w3 died */ + conf.ripoff = 1; + conf.nlgmsg = 1100; /* @8576 bytes, for packets */ + conf.nsmmsg = 500; /* @128 bytes */ + + conf.minuteswest = 8*60; + conf.dsttime = 1; +} + +int (*fsprotocol[])(Msgbuf*) = { + serve9p1, /* do we still need 9P1? */ + serve9p2, + nil, +}; diff -Nru /sys/src/fs/fs/dat.h /sys/src/fs/fs/dat.h --- /sys/src/fs/fs/dat.h Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/fs/dat.h Tue Nov 1 00:00:00 2011 @@ -0,0 +1,35 @@ +/* + * The most fundamental constant. + * The code will not compile with RBUFSIZE made a variable; + * for one thing, RBUFSIZE determines FEPERBUF, which determines + * the number of elements in a free-list-block array. + */ +#define RBUFSIZE (4*1024) /* raw buffer size */ + +#include "../port/portdat.h" + +extern Mach mach0; + +typedef struct Segdesc Segdesc; +struct Segdesc +{ + ulong d0; + ulong d1; +}; + +typedef struct Mbank { + ulong base; + ulong limit; +} Mbank; + +#define MAXBANK 8 + +typedef struct Mconf { + Lock; + Mbank bank[MAXBANK]; + int nbank; + ulong topofmem; +} Mconf; +extern Mconf mconf; + +extern char nvrfile[128]; diff -Nru /sys/src/fs/fs/fns.h /sys/src/fs/fs/fns.h --- /sys/src/fs/fs/fns.h Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/fs/fns.h Tue Nov 1 00:00:00 2011 @@ -0,0 +1,87 @@ +ulong strtoul(char*, char**, int); +vlong strtoll(char*, char**, int); + +#include "../port/portfns.h" + +void aamloop(int); +void cgaputc(int); +void cgaputs(char*, int); +int cistrcmp(char*, char*); +int cistrncmp(char*, char*, int); +void (*coherence)(void); +void etherinit(void); +void etherstart(void); +int floppyinit(void); +void floppyproc(void); +Off floppyread(int, void*, long); +Devsize floppyseek(int, Devsize); +Off floppywrite(int, void*, long); +void fpinit(void); +char* getconf(char*); +ulong getcr0(void); +ulong getcr2(void); +ulong getcr4(void); +int getfields(char*, char**, int, int, char*); +ulong getstatus(void); +int atainit(void); +Off ataread(int, void*, long); +Devsize ataseek(int, Devsize); +Off atawrite(int, void*, long); +void i8042a20(void); +void i8042reset(void); +int inb(int); +void insb(int, void*, int); +ushort ins(int); +void inss(int, void*, int); +ulong inl(int); +void insl(int, void*, int); +void kbdinit(void); +int kbdintr0(void); +int kbdgetc(void); +long* mapaddr(ulong); +void microdelay(int); +void mmuinit(void); +uchar nvramread(int); +void outb(int, int); +void outsb(int, void*, int); +void outs(int, ushort); +void outss(int, void*, int); +void outl(int, ulong); +void outsl(int, void*, int); +void printcpufreq(void); +void putgdt(Segdesc*, int); +void putidt(Segdesc*, int); +void putcr3(ulong); +void putcr4(ulong); +void puttr(ulong); +void rdmsr(int, vlong*); +void wrmsr(int, vlong); +void (*cycles)(uvlong*); +void scsiinit(void); +Off scsiread(int, void*, long); +Devsize scsiseek(int, Devsize); +Off scsiwrite(int, void*, long); +int setatapart(int, char*); +int setscsipart(int, char*); +void setvec(int, void (*)(Ureg*, void*), void*); +int tas(Lock*); +void trapinit(void); +void uartspecial(int, void (*)(int), int (*)(void), int); +int uartgetc(void); +void uartputc(int); +void wbflush(void); +void cpuid(char*, int*, int*); + +#define PADDR(a) ((ulong)(a)&~KZERO) + +/* pata */ +void ideinit(Device *d); +Devsize idesize(Device *d); +int ideread(Device *d, Devsize, void*); +int idewrite(Device *d, Devsize, void*); + +/* sata */ +void mvideinit(Device *d); +Devsize mvidesize(Device *d); +int mvideread(Device *d, Devsize, void*); +int mvidewrite(Device *d, Devsize, void*); diff -Nru /sys/src/fs/fs/io.h /sys/src/fs/fs/io.h --- /sys/src/fs/fs/io.h Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/fs/io.h Tue Nov 1 00:00:00 2011 @@ -0,0 +1,247 @@ +/* + * programmable interrupt vectors (for the 8259's) + */ +enum +{ + Bptvec= 3, /* breakpoints */ + Mathemuvec= 7, /* math coprocessor emulation interrupt */ + Mathovervec= 9, /* math coprocessor overrun interrupt */ + Matherr1vec= 16, /* math coprocessor error interrupt */ + Faultvec= 14, /* page fault */ + + Int0vec= 24, /* first 8259 */ + Clockvec= Int0vec+0, /* clock interrupts */ + Kbdvec= Int0vec+1, /* keyboard interrupts */ + Uart1vec= Int0vec+3, /* modem line */ + Uart0vec= Int0vec+4, /* serial line */ + PCMCIAvec= Int0vec+5, /* PCMCIA card change */ + Floppyvec= Int0vec+6, /* floppy interrupts */ + Parallelvec= Int0vec+7, /* parallel port interrupts */ + Int1vec= Int0vec+8, + Ethervec= Int0vec+10, /* ethernet interrupt */ + Mousevec= Int0vec+12, /* mouse interrupt */ + Matherr2vec= Int0vec+13, /* math coprocessor */ + ATA0vec= Int0vec+14, /* hard disk */ + + Syscallvec= 64, +}; + +/* + * 8259 interrupt controllers + */ +enum +{ + Int0ctl= 0x20, /* control port (ICW1, OCW2, OCW3) */ + Int0aux= 0x21, /* everything else (ICW2, ICW3, ICW4, OCW1) */ + Int1ctl= 0xA0, /* control port */ + Int1aux= 0xA1, /* everything else (ICW2, ICW3, ICW4, OCW1) */ + + Icw1= 0x10, /* select bit in ctl register */ + Ocw2= 0x00, + Ocw3= 0x08, + + EOI= 0x20, /* non-specific end of interrupt */ + + Elcr1= 0x4D0, /* Edge/Level Triggered Register */ + Elcr2= 0x4D1, +}; + +extern int int0mask; /* interrupts enabled for first 8259 */ +extern int int1mask; /* interrupts enabled for second 8259 */ + +#define NVRAUTHADDR 0 +#define LINESIZE 0 + +enum { + MaxEISA = 16, + EISAconfig = 0xC80, + + MaxScsi = 4, + NTarget = 16, + + MaxEther = 4, +}; + +#define DMAOK(x, l) ((ulong)(((ulong)(x))+(l)) < (ulong)(KZERO|16*1024*1024)) + +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 */ +}; + +#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) + +/* + * PCI support code. + */ +enum { /* type 0 and type 1 pre-defined header */ + PciVID = 0x00, /* vendor ID */ + PciDID = 0x02, /* device ID */ + PciPCR = 0x04, /* command */ + PciPSR = 0x06, /* status */ + PciRID = 0x08, /* revision ID */ + PciCCRp = 0x09, /* programming interface class code */ + PciCCRu = 0x0A, /* sub-class code */ + PciCCRb = 0x0B, /* base class code */ + PciCLS = 0x0C, /* cache line size */ + PciLTR = 0x0D, /* latency timer */ + PciHDT = 0x0E, /* header type */ + PciBST = 0x0F, /* BIST */ + + PciBAR0 = 0x10, /* base address */ + PciBAR1 = 0x14, + + PciINTL = 0x3C, /* interrupt line */ + PciINTP = 0x3D, /* interrupt pin */ +}; + +enum { /* type 0 pre-defined header */ + PciBAR2 = 0x18, + PciBAR3 = 0x1C, + PciBAR4 = 0x20, + PciBAR5 = 0x24, + PciCIS = 0x28, /* cardbus CIS pointer */ + PciSVID = 0x2C, /* subsystem vendor ID */ + PciSID = 0x2E, /* cardbus CIS pointer */ + PciEBAR0 = 0x30, /* expansion ROM base address */ + PciMGNT = 0x3E, /* burst period length */ + PciMLT = 0x3F, /* maximum latency between bursts */ +}; + +enum { /* type 1 pre-defined header */ + PciPBN = 0x18, /* primary bus number */ + PciSBN = 0x19, /* secondary bus number */ + PciUBN = 0x1A, /* subordinate bus number */ + PciSLTR = 0x1B, /* secondary latency timer */ + PciIBR = 0x1C, /* I/O base */ + PciILR = 0x1D, /* I/O limit */ + PciSPSR = 0x1E, /* secondary status */ + PciMBR = 0x20, /* memory base */ + PciMLR = 0x22, /* memory limit */ + PciPMBR = 0x24, /* prefetchable memory base */ + PciPMLR = 0x26, /* prefetchable memory limit */ + PciPUBR = 0x28, /* prefetchable base upper 32 bits */ + PciPULR = 0x2C, /* prefetchable limit upper 32 bits */ + PciIUBR = 0x30, /* I/O base upper 16 bits */ + PciIULR = 0x32, /* I/O limit upper 16 bits */ + PciEBAR1 = 0x28, /* expansion ROM base address */ + PciBCR = 0x3E, /* bridge control register */ +}; + +typedef struct Pcidev Pcidev; +typedef struct Pcidev { + int tbdf; /* type+bus+device+function */ + ushort vid; /* vendor ID */ + ushort did; /* device ID */ + + struct { + ulong bar; /* base address */ + int size; + } mem[6]; + + uchar rid; + uchar ccrp; + uchar ccrb; + uchar intl; /* interrupt line */ + ushort ccru; /* is uchar in cpu kernel */ + ulong pcr; + + Pcidev* list; + Pcidev* bridge; /* down a bus */ + Pcidev* link; /* next device on this bno */ +} Pcidev; + +extern int pcicfgr8(Pcidev*, int); +extern int pcicfgr16(Pcidev*, int); +extern int pcicfgr32(Pcidev*, int); +extern void pcicfgw8(Pcidev*, int, int); +extern void pcicfgw16(Pcidev*, int, int); +extern void pcicfgw32(Pcidev*, int, int); +extern void pciclrmwi(Pcidev*); +extern void pcihinv(Pcidev*, ulong); +extern Pcidev* pcimatch(Pcidev*, int, int); +extern Pcidev* pcimatchtbdf(int); +extern void pcireset(void); +extern void pcisetbme(Pcidev*); +extern void pciclrbme(Pcidev*); + +/* + * a parsed plan9.ini line + */ +#define ISAOPTLEN 16 +#define NISAOPT 8 + +typedef struct ISAConf { + char type[NAMELEN]; + ulong port; + ulong irq; + ulong dma; + ulong mem; + ulong size; + ulong freq; + + int nopt; + char opt[NISAOPT][ISAOPTLEN]; +} ISAConf; + +extern int isaconfig(char*, int, ISAConf*); + +/* + * SCSI support code. + */ +enum { + STblank =-6, /* blank block */ + STnomem =-5, /* buffer allocation failed */ + STtimeout =-4, /* bus timeout */ + STownid =-3, /* playing with myself */ + STharderr =-2, /* controller error of some kind */ + STinit =-1, /* */ + STok = 0, /* good */ + STcheck = 0x02, /* check condition */ + STcondmet = 0x04, /* condition met/good */ + STbusy = 0x08, /* busy */ + STintok = 0x10, /* intermediate/good */ + STintcondmet = 0x14, /* intermediate/condition met/good */ + STresconf = 0x18, /* reservation conflict */ + STterminated = 0x22, /* command terminated */ + STqfull = 0x28, /* queue full */ +}; + +typedef struct Target { + int ctlrno; + int targetno; + uchar* inquiry; + uchar* sense; + + QLock; + char id[NAMELEN]; + int ok; + + char fflag; + Filter work[3]; + Filter rate[3]; +} Target; + +typedef int (*Scsiio)(Target*, int, uchar*, int, void*, int*); diff -Nru /sys/src/fs/fs/mem.h /sys/src/fs/fs/mem.h --- /sys/src/fs/fs/mem.h Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/fs/mem.h Tue Nov 1 00:00:00 2011 @@ -0,0 +1,85 @@ +#define BY2WD 4 /* bytes per word */ +#define BY2V 8 /* bytes per vlong */ +#define BY2PG 4096 /* bytes per page */ +#define PGSHIFT 12 /* log(BY2PG) */ +#define PGROUND(s) (((s)+(BY2PG-1))&~(BY2PG-1)) +#define MB (1024*1024) + +#define HZ (82) /* clock frequency */ +#define TK2MS(t) (((ulong)(t)*1000)/HZ) /* ticks to milliseconds - beware rounding */ +#define MS2TK(t) (((ulong)(t)*HZ)/1000) /* milliseconds to ticks - beware rounding */ +#define TK2SEC(t) ((t)/HZ) /* ticks to seconds */ + +/* + * Fundamental addresses + */ +#define IDTADDR 0x80000800 /* idt */ +#define APBOOTSTRAP 0x80001000 /* AP bootstrap code */ +#define CONFADDR 0x80001200 /* info passed from boot loader */ +#define CPU0PDB 0x80002000 /* bootstrap processor PDB */ +#define CPU0PTE 0x80003000 /* bootstrap processor PTE's for 0-2MB */ +#define CPU0MACHPTE 0x80004000 /* bootstrap processor PTE for MACHADDR */ +#define CPU0MACH 0x80005000 /* Mach for bootstrap processor */ + +#define KZERO 0x80000000 /* base of kernel address space */ +#define KTZERO 0x80100000 /* first address in kernel text */ + +#define MACHSIZE 4096 + +/* + * known 80386 segments (in GDT) and their selectors + */ +#define NULLSEG 0 /* null segment */ +#define KDSEG 1 /* kernel data/stack */ +#define KESEG 2 /* kernel executable */ +#define UDSEG 3 /* user data/stack */ +#define UESEG 4 /* user executable */ +#define TSSSEG 5 /* task segment */ +#define N386SEG 6 /* number of segments */ + +#define SELGDT (0<<3) /* selector is in gdt */ +#define SELLDT (1<<3) /* selector is in ldt */ + +#define SELECTOR(i, t, p) (((i)<<3) | (t) | (p)) + +#define NULLSEL SELECTOR(NULLSEG, SELGDT, 0) +#define KESEL SELECTOR(KESEG, SELGDT, 0) +#define KDSEL SELECTOR(KDSEG, SELGDT, 0) +#define UESEL SELECTOR(UESEG, SELGDT, 3) +#define UDSEL SELECTOR(UDSEG, SELGDT, 3) +#define TSSSEL SELECTOR(TSSSEG, SELGDT, 0) + +/* + * fields in segment descriptors + */ +#define SEGDATA (0x10<<8) /* data/stack segment */ +#define SEGEXEC (0x18<<8) /* executable segment */ +#define SEGTSS (0x9<<8) /* TSS segment */ +#define SEGCG (0x0C<<8) /* call gate */ +#define SEGIG (0x0E<<8) /* interrupt gate */ +#define SEGTG (0x0F<<8) /* task gate */ +#define SEGTYPE (0x1F<<8) + +#define SEGP (1<<15) /* segment present */ +#define SEGPL(x) ((x)<<13) /* priority level */ +#define SEGB (1<<22) /* granularity 1==4k (for expand-down) */ +#define SEGG (1<<23) /* granularity 1==4k (for other) */ +#define SEGE (1<<10) /* expand down */ +#define SEGW (1<<9) /* writable (for data/stack) */ +#define SEGR (1<<9) /* readable (for code) */ +#define SEGD (1<<22) /* default 1==32bit (for code) */ + +/* + * physical MMU + */ +#define PTEVALID (1<<0) +#define PTEUNCACHED (1<<4) +#define PTEWRITE (1<<1) +#define PTERONLY (0<<1) +#define PTEKERNEL (0<<2) +#define PTEUSER (1<<2) +#define PTESIZE (1<<7) + +#define MACHADDR ((ulong)&mach0) /* hack number 1 */ + +#define IFLAG 0x200 /* psw: interrupt enable, to be accurate */ diff -Nru /sys/src/fs/fs/mkfile /sys/src/fs/fs/mkfile --- /sys/src/fs/fs/mkfile Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/fs/mkfile Tue Nov 1 00:00:00 2011 @@ -0,0 +1,147 @@ +CONF=fs +p=9 + +objtype=386 +text = "scp"; + synccopy(); +} + +void +localconfinit(void) +{ + /* conf.nfile = 60000; */ /* from emelie */ + conf.nodump = 0; + conf.dumpreread = 1; + conf.firstsb = 0; /* time- & jukebox-dependent optimisation */ + conf.recovsb = 0; /* 971531 is 24 june 2003, before w3 died */ + conf.ripoff = 1; + conf.nlgmsg = 1100; /* @8576 bytes, for packets */ + conf.nsmmsg = 500; /* @128 bytes */ + + conf.minuteswest = 8*60; + conf.dsttime = 1; +} + +int (*fsprotocol[])(Msgbuf*) = { + /* 64-bit file servers can't serve 9P1 correctly: NAMELEN is too big */ + serve9p2, + nil, +}; diff -Nru /sys/src/fs/fs64/dat.h /sys/src/fs/fs64/dat.h --- /sys/src/fs/fs64/dat.h Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/fs64/dat.h Tue Nov 1 00:00:00 2011 @@ -0,0 +1,35 @@ +/* + * The most fundamental constant. + * The code will not compile with RBUFSIZE made a variable; + * for one thing, RBUFSIZE determines FEPERBUF, which determines + * the number of elements in a free-list-block array. + */ +#define RBUFSIZE (8*1024) /* raw buffer size */ + +#include "../port/portdat.h" + +extern Mach mach0; + +typedef struct Segdesc Segdesc; +struct Segdesc +{ + ulong d0; + ulong d1; +}; + +typedef struct Mbank { + ulong base; + ulong limit; +} Mbank; + +#define MAXBANK 8 + +typedef struct Mconf { + Lock; + Mbank bank[MAXBANK]; + int nbank; + ulong topofmem; +} Mconf; +extern Mconf mconf; + +extern char nvrfile[128]; diff -Nru /sys/src/fs/fs64/fns.h /sys/src/fs/fs64/fns.h --- /sys/src/fs/fs64/fns.h Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/fs64/fns.h Tue Nov 1 00:00:00 2011 @@ -0,0 +1,92 @@ +ulong strtoul(char*, char**, int); +vlong strtoll(char*, char**, int); + +#include "../port/portfns.h" + +void aamloop(int); +void cgaputc(int); +void cgaputs(char*, int); +int cistrcmp(char*, char*); +int cistrncmp(char*, char*, int); +void (*coherence)(void); +void etherinit(void); +void etherstart(void); +int floppyinit(void); +void floppyproc(void); +Off floppyread(int, void*, long); +Devsize floppyseek(int, Devsize); +Off floppywrite(int, void*, long); +void fpinit(void); +char* getconf(char*); +ulong getcr0(void); +ulong getcr2(void); +ulong getcr4(void); +int getfields(char*, char**, int, int, char*); +ulong getstatus(void); +int atainit(void); +Off ataread(int, void*, long); +Devsize ataseek(int, Devsize); +Off atawrite(int, void*, long); +void i8042a20(void); +void i8042reset(void); +int inb(int); +void insb(int, void*, int); +ushort ins(int); +void inss(int, void*, int); +ulong inl(int); +void insl(int, void*, int); +void kbdinit(void); +int kbdintr0(void); +int kbdgetc(void); +long* mapaddr(ulong); +void microdelay(int); +void mmuinit(void); +uchar nvramread(int); +void outb(int, int); +void outsb(int, void*, int); +void outs(int, ushort); +void outss(int, void*, int); +void outl(int, ulong); +void outsl(int, void*, int); +void printcpufreq(void); +void putgdt(Segdesc*, int); +void putidt(Segdesc*, int); +void putcr3(ulong); +void putcr4(ulong); +void puttr(ulong); +void rdmsr(int, vlong*); +void wrmsr(int, vlong); +void (*cycles)(uvlong*); +void scsiinit(void); +Off scsiread(int, void*, long); +Devsize scsiseek(int, Devsize); +Off scsiwrite(int, void*, long); +int setatapart(int, char*); +int setscsipart(int, char*); +void setvec(int, void (*)(Ureg*, void*), void*); +int tas(Lock*); +void trapinit(void); +void uartspecial(int, void (*)(int), int (*)(void), int); +int uartgetc(void); +void uartputc(int); +void wbflush(void); +void cpuid(char*, int*, int*); + +#define PADDR(a) ((ulong)(a)&~KZERO) + +/* pata */ +void ideinit(Device *d); +Devsize idesize(Device *d); +int ideread(Device *d, Devsize, void*); +int idewrite(Device *d, Devsize, void*); + +/* marvell sata */ +void mvideinit(Device *d); +Devsize mvidesize(Device *d); +int mvideread(Device *d, Devsize, void*); +int mvidewrite(Device *d, Devsize, void*); +int mvsatainit(void); +Off mvsataread(int, void*, long); +Devsize mvsataseek(int, Devsize); +Off mvsatawrite(int, void*, long); +int setmv50part(int, char*); diff -Nru /sys/src/fs/fs64/io.h /sys/src/fs/fs64/io.h --- /sys/src/fs/fs64/io.h Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/fs64/io.h Tue Nov 1 00:00:00 2011 @@ -0,0 +1,247 @@ +/* + * programmable interrupt vectors (for the 8259's) + */ +enum +{ + Bptvec= 3, /* breakpoints */ + Mathemuvec= 7, /* math coprocessor emulation interrupt */ + Mathovervec= 9, /* math coprocessor overrun interrupt */ + Matherr1vec= 16, /* math coprocessor error interrupt */ + Faultvec= 14, /* page fault */ + + Int0vec= 24, /* first 8259 */ + Clockvec= Int0vec+0, /* clock interrupts */ + Kbdvec= Int0vec+1, /* keyboard interrupts */ + Uart1vec= Int0vec+3, /* modem line */ + Uart0vec= Int0vec+4, /* serial line */ + PCMCIAvec= Int0vec+5, /* PCMCIA card change */ + Floppyvec= Int0vec+6, /* floppy interrupts */ + Parallelvec= Int0vec+7, /* parallel port interrupts */ + Int1vec= Int0vec+8, + Ethervec= Int0vec+10, /* ethernet interrupt */ + Mousevec= Int0vec+12, /* mouse interrupt */ + Matherr2vec= Int0vec+13, /* math coprocessor */ + ATA0vec= Int0vec+14, /* hard disk */ + + Syscallvec= 64, +}; + +/* + * 8259 interrupt controllers + */ +enum +{ + Int0ctl= 0x20, /* control port (ICW1, OCW2, OCW3) */ + Int0aux= 0x21, /* everything else (ICW2, ICW3, ICW4, OCW1) */ + Int1ctl= 0xA0, /* control port */ + Int1aux= 0xA1, /* everything else (ICW2, ICW3, ICW4, OCW1) */ + + Icw1= 0x10, /* select bit in ctl register */ + Ocw2= 0x00, + Ocw3= 0x08, + + EOI= 0x20, /* non-specific end of interrupt */ + + Elcr1= 0x4D0, /* Edge/Level Triggered Register */ + Elcr2= 0x4D1, +}; + +extern int int0mask; /* interrupts enabled for first 8259 */ +extern int int1mask; /* interrupts enabled for second 8259 */ + +#define NVRAUTHADDR 0 +#define LINESIZE 0 + +enum { + MaxEISA = 16, + EISAconfig = 0xC80, + + MaxScsi = 4, + NTarget = 16, + + MaxEther = 4, +}; + +#define DMAOK(x, l) ((ulong)(((ulong)(x))+(l)) < (ulong)(KZERO|16*1024*1024)) + +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 */ +}; + +#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) + +/* + * PCI support code. + */ +enum { /* type 0 and type 1 pre-defined header */ + PciVID = 0x00, /* vendor ID */ + PciDID = 0x02, /* device ID */ + PciPCR = 0x04, /* command */ + PciPSR = 0x06, /* status */ + PciRID = 0x08, /* revision ID */ + PciCCRp = 0x09, /* programming interface class code */ + PciCCRu = 0x0A, /* sub-class code */ + PciCCRb = 0x0B, /* base class code */ + PciCLS = 0x0C, /* cache line size */ + PciLTR = 0x0D, /* latency timer */ + PciHDT = 0x0E, /* header type */ + PciBST = 0x0F, /* BIST */ + + PciBAR0 = 0x10, /* base address */ + PciBAR1 = 0x14, + + PciINTL = 0x3C, /* interrupt line */ + PciINTP = 0x3D, /* interrupt pin */ +}; + +enum { /* type 0 pre-defined header */ + PciBAR2 = 0x18, + PciBAR3 = 0x1C, + PciBAR4 = 0x20, + PciBAR5 = 0x24, + PciCIS = 0x28, /* cardbus CIS pointer */ + PciSVID = 0x2C, /* subsystem vendor ID */ + PciSID = 0x2E, /* cardbus CIS pointer */ + PciEBAR0 = 0x30, /* expansion ROM base address */ + PciMGNT = 0x3E, /* burst period length */ + PciMLT = 0x3F, /* maximum latency between bursts */ +}; + +enum { /* type 1 pre-defined header */ + PciPBN = 0x18, /* primary bus number */ + PciSBN = 0x19, /* secondary bus number */ + PciUBN = 0x1A, /* subordinate bus number */ + PciSLTR = 0x1B, /* secondary latency timer */ + PciIBR = 0x1C, /* I/O base */ + PciILR = 0x1D, /* I/O limit */ + PciSPSR = 0x1E, /* secondary status */ + PciMBR = 0x20, /* memory base */ + PciMLR = 0x22, /* memory limit */ + PciPMBR = 0x24, /* prefetchable memory base */ + PciPMLR = 0x26, /* prefetchable memory limit */ + PciPUBR = 0x28, /* prefetchable base upper 32 bits */ + PciPULR = 0x2C, /* prefetchable limit upper 32 bits */ + PciIUBR = 0x30, /* I/O base upper 16 bits */ + PciIULR = 0x32, /* I/O limit upper 16 bits */ + PciEBAR1 = 0x28, /* expansion ROM base address */ + PciBCR = 0x3E, /* bridge control register */ +}; + +typedef struct Pcidev Pcidev; +typedef struct Pcidev { + int tbdf; /* type+bus+device+function */ + ushort vid; /* vendor ID */ + ushort did; /* device ID */ + + struct { + ulong bar; /* base address */ + int size; + } mem[6]; + + uchar rid; + uchar ccrp; + uchar ccrb; + uchar intl; /* interrupt line */ + ushort ccru; /* is uchar in cpu kernel */ + ulong pcr; + + Pcidev* list; + Pcidev* bridge; /* down a bus */ + Pcidev* link; /* next device on this bno */ +} Pcidev; + +extern int pcicfgr8(Pcidev*, int); +extern int pcicfgr16(Pcidev*, int); +extern int pcicfgr32(Pcidev*, int); +extern void pcicfgw8(Pcidev*, int, int); +extern void pcicfgw16(Pcidev*, int, int); +extern void pcicfgw32(Pcidev*, int, int); +extern void pciclrmwi(Pcidev*); +extern void pcihinv(Pcidev*, ulong); +extern Pcidev* pcimatch(Pcidev*, int, int); +extern Pcidev* pcimatchtbdf(int); +extern void pcireset(void); +extern void pcisetbme(Pcidev*); +extern void pciclrbme(Pcidev*); + +/* + * a parsed plan9.ini line + */ +#define ISAOPTLEN 16 +#define NISAOPT 8 + +typedef struct ISAConf { + char type[NAMELEN]; + ulong port; + ulong irq; + ulong dma; + ulong mem; + ulong size; + ulong freq; + + int nopt; + char opt[NISAOPT][ISAOPTLEN]; +} ISAConf; + +extern int isaconfig(char*, int, ISAConf*); + +/* + * SCSI support code. + */ +enum { + STblank =-6, /* blank block */ + STnomem =-5, /* buffer allocation failed */ + STtimeout =-4, /* bus timeout */ + STownid =-3, /* playing with myself */ + STharderr =-2, /* controller error of some kind */ + STinit =-1, /* */ + STok = 0, /* good */ + STcheck = 0x02, /* check condition */ + STcondmet = 0x04, /* condition met/good */ + STbusy = 0x08, /* busy */ + STintok = 0x10, /* intermediate/good */ + STintcondmet = 0x14, /* intermediate/condition met/good */ + STresconf = 0x18, /* reservation conflict */ + STterminated = 0x22, /* command terminated */ + STqfull = 0x28, /* queue full */ +}; + +typedef struct Target { + int ctlrno; + int targetno; + uchar* inquiry; + uchar* sense; + + QLock; + char id[NAMELEN]; + int ok; + + char fflag; + Filter work[3]; + Filter rate[3]; +} Target; + +typedef int (*Scsiio)(Target*, int, uchar*, int, void*, int*); diff -Nru /sys/src/fs/fs64/mem.h /sys/src/fs/fs64/mem.h --- /sys/src/fs/fs64/mem.h Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/fs64/mem.h Tue Nov 1 00:00:00 2011 @@ -0,0 +1,89 @@ +#define BY2WD 4 /* bytes per word */ +#define BY2V 8 /* bytes per vlong */ +#define BY2PG 4096 /* bytes per page */ +#define PGSHIFT 12 /* log(BY2PG) */ +#define PGROUND(s) (((s)+(BY2PG-1))&~(BY2PG-1)) +#define MB (1024*1024) + +#define HZ (82) /* clock frequency */ +#define TK2MS(t) (((ulong)(t)*1000)/HZ) /* ticks to milliseconds - beware rounding */ +#define MS2TK(t) (((ulong)(t)*HZ)/1000) /* milliseconds to ticks - beware rounding */ +#define TK2SEC(t) ((t)/HZ) /* ticks to seconds */ + +/* + * Fundamental addresses + */ +/* + * KZERO == 0 or 0x80000000 will work fine. 0x10000000 will work but limit + * usable memory to 256MB. + */ +#define KZERO 0x80000000 /* base of kernel address space */ +#define KTZERO (KZERO+0x100000) /* first address in kernel text */ + +#define IDTADDR (KZERO+0x800) /* idt */ +#define APBOOTSTRAP (KZERO+0x1000) /* AP bootstrap code */ +#define CONFADDR (KZERO+0x1200) /* info passed from boot loader */ +#define CPU0PDB (KZERO+0x2000) /* bootstrap processor PDB */ +#define CPU0PTE (KZERO+0x3000) /* bootstrap processor PTE's for 0-2MB */ +#define CPU0MACHPTE (KZERO+0x4000) /* bootstrap processor PTE for MACHADDR */ +#define CPU0MACH (KZERO+0x5000) /* Mach for bootstrap processor */ + +#define MACHSIZE 4096 + +/* + * known 80386 segments (in GDT) and their selectors + */ +#define NULLSEG 0 /* null segment */ +#define KDSEG 1 /* kernel data/stack */ +#define KESEG 2 /* kernel executable */ +#define UDSEG 3 /* user data/stack */ +#define UESEG 4 /* user executable */ +#define TSSSEG 5 /* task segment */ +#define N386SEG 6 /* number of segments */ + +#define SELGDT (0<<3) /* selector is in gdt */ +#define SELLDT (1<<3) /* selector is in ldt */ + +#define SELECTOR(i, t, p) (((i)<<3) | (t) | (p)) + +#define NULLSEL SELECTOR(NULLSEG, SELGDT, 0) +#define KESEL SELECTOR(KESEG, SELGDT, 0) +#define KDSEL SELECTOR(KDSEG, SELGDT, 0) +#define UESEL SELECTOR(UESEG, SELGDT, 3) +#define UDSEL SELECTOR(UDSEG, SELGDT, 3) +#define TSSSEL SELECTOR(TSSSEG, SELGDT, 0) + +/* + * fields in segment descriptors + */ +#define SEGDATA (0x10<<8) /* data/stack segment */ +#define SEGEXEC (0x18<<8) /* executable segment */ +#define SEGTSS (0x9<<8) /* TSS segment */ +#define SEGCG (0x0C<<8) /* call gate */ +#define SEGIG (0x0E<<8) /* interrupt gate */ +#define SEGTG (0x0F<<8) /* task gate */ +#define SEGTYPE (0x1F<<8) + +#define SEGP (1<<15) /* segment present */ +#define SEGPL(x) ((x)<<13) /* priority level */ +#define SEGB (1<<22) /* granularity 1==4k (for expand-down) */ +#define SEGG (1<<23) /* granularity 1==4k (for other) */ +#define SEGE (1<<10) /* expand down */ +#define SEGW (1<<9) /* writable (for data/stack) */ +#define SEGR (1<<9) /* readable (for code) */ +#define SEGD (1<<22) /* default 1==32bit (for code) */ + +/* + * physical MMU + */ +#define PTEVALID (1<<0) +#define PTEUNCACHED (1<<4) +#define PTEWRITE (1<<1) +#define PTERONLY (0<<1) +#define PTEKERNEL (0<<2) +#define PTEUSER (1<<2) +#define PTESIZE (1<<7) + +#define MACHADDR ((ulong)&mach0) /* hack number 1 */ + +#define IFLAG 0x200 /* psw: interrupt enable, to be accurate */ diff -Nru /sys/src/fs/fs64/mkfile /sys/src/fs/fs64/mkfile --- /sys/src/fs/fs64/mkfile Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/fs64/mkfile Tue Nov 1 00:00:00 2011 @@ -0,0 +1,146 @@ +CONF=fs +p=9 + +objtype=386 +>8; + p[1] = x; +} + +void +hnputl(uchar *p, long x) +{ + p[0] = x>>24; + p[1] = x>>16; + p[2] = x>>8; + p[3] = x; +} + +void +arpstart(void) +{ + if(arpcache.start == 0) { + lock(&arpcache); + if(arpcache.start == 0) { + cmd_install("arp", "subcommand -- arp protocol", cmd_arp); + arpcache.flag = flag_install("arp", "-- verbose"); + arpcache.start = 1; + iprouteinit(); + } + unlock(&arpcache); + } +} + +void +arpreceive(Enpkt *ep, int l, Ifc *ifc) +{ + Ilp* ilp; + Arppkt *p, *q; + Msgbuf *mb, **mbp; + Arpe *a; + uchar *tpa; + int type, i, h; + Timet t; + + if(l < Ensize+Arpsize) + return; + + p = (Arppkt*)ep; + + if(nhgets(p->pro) != Iptype || + nhgets(p->hrd) != 1 || + p->pln != Pasize || + p->hln != Easize) + return; + + type = nhgets(p->op); + switch(type) { + case Arprequest: + /* update entry for this source */ + h = ipahash(p->spa); + a = arpcache.abkt[h].arpe; + lock(&arpcache); + for(i=0; itpa, p->spa, Pasize) == 0) { + memmove(a->tha, p->sha, Easize); + break; + } + } + unlock(&arpcache); + + if(memcmp(p->tpa, ifc->ipa, Pasize) != 0) + break; + + DEBUG("rcv arp req for %I from %I\n", p->tpa, p->spa); + + mb = mballoc(Ensize+Arpsize, 0, Mbarp1); + q = (Arppkt*)mb->data; + + memmove(q, p, Ensize+Arpsize); + + hnputs(q->op, Arpreply); + memmove(q->tha, p->sha, Easize); + memmove(q->tpa, p->spa, Pasize); + memmove(q->sha, ifc->ea, Easize); + memmove(q->spa, ifc->ipa, Pasize); + memmove(q->d, q->s, Easize); + + send(ifc->reply, mb); + break; + + case Arpreply: + DEBUG("rcv arp rpl for %I is %E\n", p->spa, p->sha); + + h = ipahash(p->spa); + a = arpcache.abkt[h].arpe; + lock(&arpcache); + for(i=0; itpa, p->spa, Pasize) == 0) { + memmove(a->tha, p->sha, Easize); + goto out; + } + } + + i = arpcache.abkt[h].laste + 1; + if(i < 0 || i >= Ne) + i = 0; + arpcache.abkt[h].laste = i; + + a = &arpcache.abkt[h].arpe[i]; + memmove(a->tpa, p->spa, Pasize); + memmove(a->tha, p->sha, Easize); + + /* + * go thru unresolved queue + */ + out: + t = toytime(); + mbp = &arpcache.unresol; + for(mb = *mbp; mb; mb = *mbp) { + if(t >= mb->param) { + *mbp = mb->next; + unlock(&arpcache); + mbfree(mb); + lock(&arpcache); + goto out; + } + ilp = mb->chan->pdata; + tpa = ilp->ipgate; + if(memcmp(a->tpa, tpa, Pasize) == 0) { + *mbp = mb->next; + mb->next = 0; + unlock(&arpcache); + ipsend(mb); + lock(&arpcache); + goto out; + } + mbp = &mb->next; + } + unlock(&arpcache); + break; + } +} + +static +int +ipahash(uchar *p) +{ + ulong h; + + h = p[0]; + h = h*7 + p[1]; + h = h*7 + p[2]; + h = h*7 + p[3]; + return h%Nb; +} + +void +ipsend1(Msgbuf *mb, Ifc *ifc, uchar *ipgate) +{ + Msgbuf **mbp, *m; + Ippkt *p; + Arppkt *q; + Arpe *a; + int i, id, len, dlen, off; + Timet t; + + p = (Ippkt*)mb->data; + + a = arpcache.abkt[ipahash(ipgate)].arpe; + lock(&arpcache); + for(i=0; itpa, ipgate, Pasize) == 0) + goto found; + + /* + * queue ip pkt to be resolved later + */ +again: + i = 0; // q length + t = toytime(); + mbp = &arpcache.unresol; + for(m = *mbp; m; m = *mbp) { + if(t >= m->param) { + *mbp = m->next; + unlock(&arpcache); + mbfree(m); + lock(&arpcache); + goto again; + } + mbp = &m->next; + i++; + } + if(mb->chan && i < 10) { + mb->param = t + SECOND(10); + mb->next = 0; + *mbp = mb; + unlock(&arpcache); + } else { + unlock(&arpcache); + mbfree(mb); + } + + /* + * send an arp request + */ + + m = mballoc(Ensize+Arpsize, 0, Mbarp2); + q = (Arppkt*)m->data; + + DEBUG("snd arp req target %I ip dest %I\n", ipgate, p->dst); + + memset(q->d, 0xff, Easize); /* broadcast */ + hnputs(q->type, Arptype); + hnputs(q->hrd, 1); + hnputs(q->pro, Iptype); + q->hln = Easize; + q->pln = Pasize; + hnputs(q->op, Arprequest); + memmove(q->sha, ifc->ea, Easize); + memmove(q->spa, ifc->ipa, Pasize); + memset(q->tha, 0, Easize); + memmove(q->tpa, ipgate, Pasize); + + send(ifc->reply, m); + + return; + +found: + len = mb->count; /* includes Ensize+Ipsize+Ilsize */ + memmove(p->d, a->tha, Easize); + p->vihl = IP_VER|IP_HLEN; + p->tos = 0; + p->ttl = 255; + id = arpcache.idgen; + if(id == 0) + id = toytime() * 80021; + arpcache.idgen = id+1; + unlock(&arpcache); + hnputs(p->id, id); + hnputs(p->type, Iptype); + + /* + * If we dont need to fragment just send it + */ + if(len <= ETHERMAXTU) { + hnputs(p->length, len-Ensize); + p->frag[0] = 0; + p->frag[1] = 0; + p->cksum[0] = 0; + p->cksum[1] = 0; + hnputs(p->cksum, ipcsum(&p->vihl)); + + send(ifc->reply, mb); + return; + } + + off = 0; + len -= Ensize+Ipsize; /* just ip data */ + + while(len > 0) { + dlen = (ETHERMAXTU-(Ensize+Ipsize)) & ~7; + if(dlen > len) + dlen = len; + len -= dlen; + + /* + * use first frag in place, + * make copies of subsequent frags + * this saves a copy of a MTU-size buffer + */ + if(ORDER && off == 0) { + m = 0; + mb->count = (Ensize+Ipsize)+dlen; + p = (Ippkt*)mb->data; + } else { + m = mballoc((Ensize+Ipsize)+dlen, 0, Mbip1); + p = (Ippkt*)m->data; + + memmove(m->data, mb->data, Ensize+Ipsize); + memmove(m->data+(Ensize+Ipsize), + mb->data+(Ensize+Ipsize)+off, dlen); + } + + hnputs(p->length, dlen+Ipsize); + if(len == 0) + hnputs(p->frag, off>>3); + else + hnputs(p->frag, (off>>3)|IP_MF); + p->cksum[0] = 0; + p->cksum[1] = 0; + hnputs(p->cksum, ipcsum(&p->vihl)); + + if(m) + send(ifc->reply, m); + + off += dlen; + } + if(ORDER) + send(ifc->reply, mb); + else + mbfree(mb); +} + +void +ipsend(Msgbuf *mb) +{ + Ilp *ilp; + Chan *cp; + + cp = mb->chan; + if(cp == 0) { + print("cp = 0\n"); + mbfree(mb); + return; + } + ilp = cp->pdata; + ipsend1(mb, cp->ifc, ilp->ipgate); +} + +int +ipforme(uchar addr[Pasize], Ifc *ifc) +{ + ulong haddr; + + if(memcmp(addr, ifc->ipa, Pasize) == 0) + return 1; + + haddr = nhgetl(addr); + + /* My subnet broadcast */ + if((haddr&ifc->mask) == (ifc->ipaddr&ifc->mask)) + return 1; + + /* Real ip broadcast */ + if(haddr == 0) + return 1; + + /* Old style 255.255.255.255 address */ + if(haddr == ~0) + return 1; + + return 0; +} + +/* + * ipcsum - Compute internet header checksums + */ +int +ipcsum(uchar *addr) +{ + int len; + ulong sum = 0; + + len = (addr[0]&0xf) << 2; + + while(len > 0) { + sum += (addr[0]<<8) | addr[1] ; + len -= 2; + addr += 2; + } + + sum = (sum & 0xffff) + (sum >> 16); + sum = (sum & 0xffff) + (sum >> 16); + return sum^0xffff; +} + +/* + * protcol checksum routine + */ + +static short endian = 1; +static char* aendian = (char*)&endian; +#define LITTLE *aendian + +int +ptclcsum(uchar *addr, int len) +{ + ulong losum, hisum, mdsum, x; + ulong t1, t2; + + losum = 0; + hisum = 0; + mdsum = 0; + + x = 0; + if((ulong)addr & 1) { + if(len) { + hisum += addr[0]; + len--; + addr++; + } + x = 1; + } + while(len >= 16) { + t1 = *(ushort*)(addr+0); + t2 = *(ushort*)(addr+2); mdsum += t1; + t1 = *(ushort*)(addr+4); mdsum += t2; + t2 = *(ushort*)(addr+6); mdsum += t1; + t1 = *(ushort*)(addr+8); mdsum += t2; + t2 = *(ushort*)(addr+10); mdsum += t1; + t1 = *(ushort*)(addr+12); mdsum += t2; + t2 = *(ushort*)(addr+14); mdsum += t1; + mdsum += t2; + len -= 16; + addr += 16; + } + while(len >= 2) { + mdsum += *(ushort*)addr; + len -= 2; + addr += 2; + } + if(x) { + if(len) + losum += addr[0]; + if(LITTLE) + losum += mdsum; + else + hisum += mdsum; + } else { + if(len) + hisum += addr[0]; + if(LITTLE) + hisum += mdsum; + else + losum += mdsum; + } + + losum += hisum >> 8; + losum += (hisum & 0xff) << 8; + while(hisum = losum>>16) + losum = hisum + (losum & 0xffff); + + return ~losum & 0xffff; +} + +static +void +cmd_arp(int argc, char *argv[]) +{ + int h, i, j; + Arpe *a; + + if(argc <= 1) { + print("arp flush -- clear cache\n"); + print("arp print -- print cache\n"); + return; + } + for(i=1; itpa, Pasize) == 0) + continue; + print("%-15I %E\n", a->tpa, a->tha); + prflush(); + } + } + continue; + } + } +} diff -Nru /sys/src/fs/ip/icmp.c /sys/src/fs/ip/icmp.c --- /sys/src/fs/ip/icmp.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/ip/icmp.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,65 @@ +#include "all.h" +#include "mem.h" + +#include "../ip/ip.h" + +enum /* Packet Types */ +{ + EchoReply = 0, + Unreachable = 3, + SrcQuench = 4, + EchoRequest = 8, + TimeExceed = 11, + Timestamp = 13, + TimestampReply = 14, + InfoRequest = 15, + InfoReply = 16, +}; + +void +icmprecv(Msgbuf *mb, Ifc *ifc) +{ + Icmppkt *p; + uchar tmp[Pasize]; + + p = (Icmppkt*)mb->data; + + switch(p->icmptype) { + default: + goto drop; + + case EchoRequest: + memmove(tmp, p->src, Pasize); + memmove(p->src, p->dst, Pasize); + memmove(p->dst, tmp, Pasize); + p->icmptype = EchoReply; + p->icmpsum[0] = 0; + p->icmpsum[1] = 0; + hnputs(p->icmpsum, + ptclcsum((uchar*)mb->data+(Ensize+Ipsize), + mb->count-(Ensize+Ipsize))); + + /* note that tmp contains dst */ + if((nhgetl(ifc->ipa)&ifc->mask) != (nhgetl(p->dst)&ifc->mask)) + iproute(tmp, p->dst, ifc->netgate); + ipsend1(mb, ifc, tmp); + break; + } + return; + +drop: + mbfree(mb); +} + +void +igmprecv(Msgbuf *mb, Ifc*) +{ + mbfree(mb); +} + +void +tcprecv(Msgbuf *mb, Ifc*) +{ + mbfree(mb); +} + diff -Nru /sys/src/fs/ip/il.c /sys/src/fs/ip/il.c --- /sys/src/fs/ip/il.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/ip/il.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,1069 @@ +#include "all.h" +#include "mem.h" + +#include "../ip/ip.h" + +#define DEBUG if(cons.flags&ilflag)print +#define msec (MACHP(0)->ticks * (1000/HZ)) + +enum +{ + Ilsync = 0, /* Packet types */ + Ildata, + Ildataquery, + Ilack, + Ilquery, + Ilstate, + Ilclose, + + Ilclosed = 0, /* Connection state */ + Ilsyncer, + Ilsyncee, + Ilestablished, + Illistening, + Ilclosing, + Ilopening, + + Seconds = 1000, + Iltickms = 50, /* time base */ + AckDelay = (Timet)(2*Iltickms), /* max time twixt message rcvd & ack sent */ + MaxTimeout = (Timet)(4*Seconds), /* max time between rexmit */ + QueryTime = (Timet)(10*Seconds), /* time between subsequent queries */ + DeathTime = (Timet)(30*QueryTime), + + MaxRexmit = 16, /* max retransmissions before hangup */ + DefWin = 20, + + LogAGain = 3, + AGain = 1<type != Devil) + return; + + ilp = cp->pdata; + t = MACHP(0)->ticks * (1000/HZ); + print(" (%d,%d)", ilp->alloc, ilp->state); + print(" (%ld,%ld,%ld)", + ilp->timeout-t, ilp->querytime-t, + ilp->lastrecv-t); + print(" (%ld,%ld,%ld,%ld)", ilp->rate, ilp->delay, + ilp->mdev, ilp->unackedbytes); +} + +static +void +ilpinit(Ilp *ilp) +{ + ilp->start = (toytime() * 80021) & 0x3fffffffUL; + ilp->next = ilp->start + 1; + ilp->rstart = 0; + ilp->recvd = 0; + ilp->window = DefWin; + ilp->unackedbytes = 0; + ilp->unacked = nil; + ilp->unackedtail = nil; + ilp->outoforder = nil; + ilp->rexmit = 0; + + /* timers */ + ilp->delay = DefRtt<mdev = DefRtt<rate = DefByteRate<querytime = msec + QueryTime; + ilp->lastrecv = msec; /* to avoid immediate timeout */ + ilsettimeout(ilp); +} + +static +Chan* +getchan(Ifc *ifc, Ilpkt *p, Msgbuf *mb) +{ + Ilp *ilp; + Chan *cp, *xcp; + int srcp, dstp; + + srcp = nhgets(p->ilsrc); + dstp = nhgets(p->ildst); + + lock(&il); + xcp = 0; + for(cp = il.chan; cp; cp = ilp->chan) { + ilp = cp->pdata; + if(ilp->alloc == 0) { + xcp = cp; + continue; + } + if(srcp == ilp->srcp) + if(dstp == ilp->dstp) + if(memcmp(p->src, ilp->iphis, Pasize) == 0) + if(memcmp(p->dst, ifc->ipa, Pasize) == 0){ + unlock(&il); + return cp; + } + } + + if(il.reply == 0) { + il.reply = newqueue(Nqueue); + userinit(ilout, &il, "ilo"); + userinit(iltimer, &il, "ilt"); + ilflag = flag_install("il", "-- on errors"); + } + + if(dstp != Ilfsport) { + ilgoaway(mb, ifc); + unlock(&il); + DEBUG("open not fsport %I.%d -> %I.%d\n", p->src, srcp, p->dst, dstp); + return nil; + } + + if(p->iltype != Ilsync) { + ilgoaway(mb, ifc); + unlock(&il); + DEBUG("open not sync %I.%d -> %I.%d\n", p->src, srcp, p->dst, dstp); + return nil; + } + + cp = xcp; + if(cp == 0) { + cp = chaninit(Devil, 1, sizeof(Ilp)); + ilp = cp->pdata; + ilp->chan = il.chan; + il.chan = cp; + } + + + cp->ifc = ifc; + ilp = cp->pdata; + memmove(ilp->iphis, p->src, Pasize); + memmove(ifc->ipa, p->dst, Pasize); + ilp->srcp = srcp; + ilp->dstp = dstp; + ilp->state = Ilopening; + + ilpinit(ilp); + + memmove(ilp->ipgate, ilp->iphis, Pasize); + if((nhgetl(ifc->ipa)&ifc->mask) != (nhgetl(p->src)&ifc->mask)) + iproute(ilp->ipgate, p->src, ifc->netgate); + + cp->send = serveq; + cp->reply = il.reply; + ilp->reply = ifc->reply; + cp->protocol = nil; + cp->msize = 0; + cp->whotime = 0; + sprint(cp->whochan, "il!%I!%d", p->src, srcp); + cp->whoprint = ilwhoprint; + ilp->alloc = 1; + + unlock(&il); + return cp; +} + +void +ilrecv(Msgbuf *mb, Ifc *ifc) +{ + Ilpkt *ih; + Chan *cp; + Ilp *ilp; + int illen, plen; + + ih = (Ilpkt*)mb->data; + + plen = mb->count; + if(plen < Ensize+Ipsize+Ilsize) + goto drop; + + illen = nhgets(ih->illen); + if(illen+Ilsize > plen) + goto drop; + + if(ptclcsum((uchar*)ih+(Ensize+Ipsize), illen) != 0) { + print("il: cksum error %E %I\n", ih->s, ih->src); + ifc->sumerr++; + goto drop; + } + + cp = getchan(ifc, ih, mb); + if(cp == nil) + goto drop; + mb->chan = cp; + ilp = cp->pdata; + + if(ilp->state == Ilopening) { + ilp->state = Ilsyncee; + ilpinit(ilp); + ilp->rstart = nhgetl(ih->ilid); + print("il: allocating %s\n", cp->whochan); + } + + ilprocess(cp, mb); + return; + +drop: + mbfree(mb); +} + +/* + * process to convert p9 to il/ip + */ +static +void +ilout(void) +{ + Ifc *ifc; + Msgbuf *mb; + Ilp *ilp; + Ilpkt *ih; + Chan *cp; + int dlen; + ulong id, ack; + + for (;;) { + while ((mb = recv(il.reply, 0)) == nil) + continue; + + cp = mb->chan; + ilp = cp->pdata; + + switch(ilp->state) { + case Ilclosed: + case Illistening: + case Ilclosing: + print("ilout: error\n"); + mbfree(mb); + continue; + } + + dlen = mb->count; + mb->data -= Ensize+Ipsize+Ilsize; /* make room for header */ + mb->count += Ensize+Ipsize+Ilsize; + if(mb->data < mb->xdata) + panic("ilout: no room for header"); + ih = (Ilpkt*)mb->data; + + /* + * Ip fields + */ + ifc = cp->ifc; + memmove(ih->src, ifc->ipa, Pasize); + memmove(ih->dst, ilp->iphis, Pasize); + ih->proto = Ilproto; + + /* + * Il fields + */ + hnputs(ih->illen, Ilsize+dlen); + hnputs(ih->ilsrc, ilp->dstp); + hnputs(ih->ildst, ilp->srcp); + id = ilp->next++; + hnputl(ih->ilid, id); + ack = ilp->recvd; + hnputl(ih->ilack, ack); + ilp->acksent = ack; + ilp->acktime = msec + AckDelay; + ih->iltype = Ildata; + ih->ilspec = 0; + ih->ilsum[0] = 0; + ih->ilsum[1] = 0; + + /* + * checksum + */ + hnputs(ih->ilsum, ptclcsum((uchar*)ih+(Ensize+Ipsize), + dlen+Ilsize)); + + ilackq(cp, mb); + + /* + * Start the round trip timer for this packet if the timer + * is free. + */ + if(ilp->rttack == 0) { + ilp->rttack = id; + ilp->rttstart = msec; + ilp->rttlen = dlen+Ipsize+Ilsize; + } + + if(ilp->timeout <= msec) + ilsettimeout(ilp); + ipsend(mb); + } +} + +static +void +ilackq(Chan *cp, Msgbuf *mb) +{ + Msgbuf *nmb; + Ilp *ilp; + + /* + * Enqueue a copy on the unacked queue in case this one gets lost + */ +/* botch -- a reference count will save this copy */ + nmb = mballoc(mb->count, cp, Mbil2); + memmove(nmb->data, mb->data, mb->count); + nmb->next = 0; + + ilp = cp->pdata; + lock(ilp); + if(ilp->unacked) + ilp->unackedtail->next = nmb; + else + ilp->unacked = nmb; + ilp->unackedtail = nmb; + ilp->unackedbytes += nmb->count; + unlock(ilp); +} + +static +void +ilprocess(Chan *cp, Msgbuf *mb) +{ + ulong id, ack; + Ilp* ilp; + Ilpkt *h; + + ilp = cp->pdata; + h = (Ilpkt*)mb->data; + + id = nhgetl(h->ilid); + ack = nhgetl(h->ilack); + + ilp->lastrecv = msec; + + switch(ilp->state) { + default: + print("il unknown state\n"); + case Ilclosed: + mbfree(mb); + break; + case Ilsyncer: + switch(h->iltype) { + default: + break; + case Ilsync: + if(ack != ilp->start) { + ilp->state = Ilclosed; + ilhangup(cp, "connection rejected", 1); + } else { + ilp->recvd = id; + ilp->rstart = id; + ilsendctl(cp, 0, Ilack, ilp->next, ilp->recvd, 0); + ilp->state = Ilestablished; + wakeup(&ilp->syn); + ilpullup(cp); + } + break; + case Ilclose: + if(ack == ilp->start) { + ilp->state = Ilclosed; + ilhangup(cp, "remote close-1", 1); + } + break; + } + mbfree(mb); + break; + + case Ilsyncee: + switch(h->iltype) { + default: + break; + case Ilsync: + if(id != ilp->rstart || ack != 0) + ilp->state = Ilclosed; + else { + ilp->recvd = id; + ilsendctl(cp, 0, Ilsync, ilp->start, ilp->recvd, 0); + } + break; + case Ilack: + if(ack == ilp->start) { + ilp->state = Ilestablished; + ilpullup(cp); + } + break; + case Ildata: + if(ack == ilp->start) { + ilp->state = Ilestablished; + goto established; + } + break; + case Ilclose: + if(id == ilp->next) { + ilp->state = Ilclosed; + ilhangup(cp, "remote close-2", 1); + } + break; + } + mbfree(mb); + break; + + case Ilestablished: + established: + switch(h->iltype) { + default: + mbfree(mb); + break; + case Ilsync: + if(id != ilp->rstart) { + ilp->state = Ilclosed; + ilhangup(cp, "remote close-3", 1); + } else + ilsendctl(cp, 0, Ilack, ilp->next, ilp->rstart, 0); + mbfree(mb); + break; + case Ildata: + ilackto(cp, ack, mb); + iloutoforder(cp, h, mb); + ilpullup(cp); + break; + case Ildataquery: + ilackto(cp, ack, mb); + iloutoforder(cp, h, mb); + ilpullup(cp); + ilsendctl(cp, 0, Ilstate, ilp->next, ilp->recvd, h->ilspec); + break; + case Ilack: + ilackto(cp, ack, mb); + mbfree(mb); + break; + case Ilquery: + ilackto(cp, ack, mb); + ilsendctl(cp, 0, Ilstate, ilp->next, ilp->recvd, h->ilspec); + mbfree(mb); + break; + case Ilstate: + if(ack >= ilp->rttack) + ilp->rttack = 0; + ilackto(cp, ack, mb); + if(h->ilspec > Nqt) + h->ilspec = 0; + if(ilp->qt[h->ilspec] > ack){ + ilrexmit(ilp); + ilsettimeout(ilp); + } + mbfree(mb); + break; + case Ilclose: + mbfree(mb); + if(ack < ilp->start || ack > ilp->next) + break; + ilp->recvd = id; + ilsendctl(cp, 0, Ilclose, ilp->next, ilp->recvd, 0); + ilp->state = Ilclosing; + ilfreeq(cp); + break; + } + break; + + case Illistening: + mbfree(mb); + break; + + case Ilclosing: + switch(h->iltype) { + case Ilclose: + ilp->recvd = id; + ilsendctl(cp, 0, Ilclose, ilp->next, ilp->recvd, 0); + if(ack == ilp->next) { + ilp->state = Ilclosed; + ilhangup(cp, "closed", 1); + } + break; + } + mbfree(mb); + break; + } +} + +static +void +ilsendctl(Chan *cp, Ilpkt *inih, int type, ulong id, ulong ack, int ilspec) +{ + Ifc *ifc; + Ilpkt *ih; + Msgbuf *mb; + Ilp *ilp; + + ilp = cp->pdata; + mb = mballoc(Ensize+Ipsize+Ilsize, cp, Mbil3); + + ih = (Ilpkt*)mb->data; + + ih->proto = Ilproto; + ifc = cp->ifc; + memmove(ih->src, ifc->ipa, Pasize); + hnputs(ih->illen, Ilsize); + if(inih) { + memmove(ih->dst, inih->src, Pasize); + memmove(ih->ilsrc, inih->ildst, sizeof(ih->ilsrc)); + memmove(ih->ildst, inih->ilsrc, sizeof(ih->ildst)); + memmove(ih->ilid, inih->ilack, sizeof(ih->ilid)); + memmove(ih->ilack, inih->ilid, sizeof(ih->ilack)); + } else { + memmove(ih->dst, ilp->iphis, Pasize); + hnputs(ih->ilsrc, ilp->dstp); + hnputs(ih->ildst, ilp->srcp); + hnputl(ih->ilid, id); + hnputl(ih->ilack, ack); + ilp->acksent = ack; + ilp->acktime = msec; + } + ih->iltype = type; + ih->ilspec = ilspec; + ih->ilsum[0] = 0; + ih->ilsum[1] = 0; + + hnputs(ih->ilsum, ptclcsum((uchar*)mb->data+(Ensize+Ipsize), Ilsize)); + + ipsend(mb); +} + +static +void +ilhangup(Chan *cp, char *msg, int dolock) +{ + Ilp *ilp; + int s; + + ilp = cp->pdata; + s = ilp->state; + ilp->state = Ilclosed; + if(s == Ilsyncer) + wakeup(&ilp->syn); + + if(msg != nil) + print("hangup! %s %d/%I.%d\n", msg, ilp->srcp, + ilp->iphis, ilp->dstp); + ilfreeq(cp); + + fileinit(cp); + cp->whotime = 0; + strcpy(cp->whoname, ""); + + if(dolock) + lock(&il); + ilp->alloc = 0; + ilp->srcp = 0; + ilp->dstp = 0; + memset(ilp->iphis, 0, sizeof(ilp->iphis)); + if(dolock) + unlock(&il); +} + +static +void +ilpullup(Chan *cp) +{ + Ilpkt *oh; + Msgbuf *mb; + Ilp *ilp; + ulong oid, dlen; + + ilp = cp->pdata; + lock(ilp); + + while(ilp->outoforder) { + mb = ilp->outoforder; + oh = (Ilpkt*)mb->data; + oid = nhgetl(oh->ilid); + if(oid <= ilp->recvd) { + ilp->outoforder = mb->next; + mbfree(mb); + continue; + } + if(oid != ilp->recvd+1) + break; + + ilp->recvd = oid; + ilp->outoforder = mb->next; + + /* + * strip off the header + */ + dlen = nhgets(oh->illen)-Ilsize; + mb->data += Ensize+Ipsize+Ilsize; + mb->count = dlen; + send(cp->send, mb); + } + unlock(ilp); +} + +static +void +iloutoforder(Chan *cp, Ilpkt *h, Msgbuf *mb) +{ + Msgbuf **l, *f; + Ilp *ilp; + ulong id, ilid; + uchar *lid; + + ilp = cp->pdata; + id = nhgetl(h->ilid); + + /* + * Window checks + */ + if(id <= ilp->recvd || id > ilp->recvd+ilp->window) { + mbfree(mb); + return; + } + + /* + * Packet is acceptable so + * sort onto receive queue for pullup + */ + mb->next = 0; + lock(ilp); + if(ilp->outoforder == 0) { + ilp->outoforder = mb; + } else { + l = &ilp->outoforder; + for(f = *l; f; f = f->next) { + lid = ((Ilpkt*)(f->data))->ilid; + ilid = nhgetl(lid); + if(id <= ilid) { + if(id == ilid) { + mbfree(mb); + unlock(ilp); + return; + } + mb->next = f; + break; + } + l = &f->next; + } + *l = mb; + } + unlock(ilp); +} + +static +void +ilrttcalc(Ilp *ilp, Msgbuf *mb) +{ + int rtt, tt, pt, delay, rate; + + rtt = msec - ilp->rttstart + TK2MS(1) - 1; + delay = ilp->delay; + rate = ilp->rate; + + /* Guard against zero wrap */ + if(rtt > 120000 || rtt < 0) + return; + + /* this block had to be transmitted after the one acked so count its size */ + ilp->rttlen += mb->count+Ipsize+Ilsize; + + if(ilp->rttlen < 256){ + /* guess fixed delay as rtt of small packets */ + delay += rtt - (delay>>LogAGain); + if(delay < AGain) + delay = AGain; + ilp->delay = delay; + } else { + /* if packet took longer than avg rtt delay, recalc rate */ + tt = rtt - (delay>>LogAGain); + if(tt > 0){ + rate += ilp->rttlen/tt - (rate>>LogAGain); + if(rate < AGain) + rate = AGain; + ilp->rate = rate; + } + } + + /* mdev */ + pt = ilp->rttlen/(rate>>LogAGain) + (delay>>LogAGain); + ilp->mdev += abs(rtt-pt) - (ilp->mdev>>LogDGain); + + if(rtt > ilp->maxrtt) + ilp->maxrtt = rtt; +} + +static +void +ilackto(Chan *cp, ulong ackto, Msgbuf *mb) +{ + Ilpkt *h; + Ilp *ilp; + ulong id; + + ilp = cp->pdata; + if(ilp->rttack == ackto) + ilrttcalc(ilp, mb); + + /* Cancel if we lost the packet we were interested in */ + if(ilp->rttack <= ackto) + ilp->rttack = 0; + + lock(ilp); + while(ilp->unacked) { + h = (Ilpkt*)ilp->unacked->data; + id = nhgetl(h->ilid); + if(ackto < id) + break; + + mb = ilp->unacked; + ilp->unacked = mb->next; + mb->next = 0; + ilp->unackedbytes -= mb->count; + mbfree(mb); + ilp->rexmit = 0; + ilsettimeout(ilp); + } + unlock(ilp); +} + +static +void +ilrexmit(Ilp *ilp) +{ + Msgbuf *omb, *mb; + Ilpkt *h; + + lock(ilp); + omb = ilp->unacked; + if(omb == 0) { + unlock(ilp); + return; + } + +/* botch -- a reference count will save this copy */ + mb = mballoc(omb->count, omb->chan, Mbil4); + memmove(mb->data, omb->data, omb->count); + unlock(ilp); + + h = (Ilpkt*)mb->data; + + h->iltype = Ildataquery; + hnputl(h->ilack, ilp->recvd); + h->ilspec = ilnextqt(ilp); + h->ilsum[0] = 0; + h->ilsum[1] = 0; + hnputs(h->ilsum, ptclcsum((uchar*)mb->data+(Ensize+Ipsize), nhgets(h->illen))); + + ilbackoff(ilp); + + ipsend(mb); +} + +static +void +ilfreeq(Chan *cp) +{ + Ilp *ilp; + Msgbuf *mb, *next; + + ilp = cp->pdata; + lock(ilp); + for(mb = ilp->unacked; mb; mb = next) { + next = mb->next; + mbfree(mb); + } + ilp->unacked = 0; + + for(mb = ilp->outoforder; mb; mb = next) { + next = mb->next; + mbfree(mb); + } + ilp->outoforder = 0; + unlock(ilp); +} + +static +void +ilsettimeout(Ilp *ilp) +{ + Timet pt; + + pt = (ilp->delay>>LogAGain) + + ilp->unackedbytes/(ilp->rate>>LogAGain) + + (ilp->mdev>>(LogDGain-1)) + + AckDelay; + if(pt > MaxTimeout) + pt = MaxTimeout; + ilp->timeout = msec + pt; +} + +static +void +ilbackoff(Ilp *ilp) +{ + Timet pt; + int i; + + pt = (ilp->delay>>LogAGain) + + ilp->unackedbytes/(ilp->rate>>LogAGain) + + (ilp->mdev>>(LogDGain-1)) + + AckDelay; + for(i = 0; i < ilp->rexmit; i++) + pt = pt + (pt>>1); + if(pt > MaxTimeout) + pt = MaxTimeout; + ilp->timeout = msec + pt; + + ilp->rexmit++; +} + +/* + * il timer + * every 100ms + */ +static Rendez ilt; + +static +void +callil(Alarm *a, void *) +{ + cancel(a); + wakeup(&ilt); +} + +// complain if two numbers not within an hour of each other +#define Tfuture (1000*60*60) + +int +later(Timet t1, Timet t2, char *x) +{ + Timet dt; + + dt = t1 - t2; + if(dt > 0) { + if(dt > Tfuture) + print("%s: way future %ld\n", x, dt); + return 1; + } + if(dt < -Tfuture) { + print("%s: way past %ld\n", x, -dt); + return 1; + } + return 0; +} + + +static +void +iltimer(void) +{ + Chan *cp; + Ilp *ilp; + +loop: + lock(&il); + for(cp = il.chan; cp; cp = ilp->chan) { + ilp = cp->pdata; + if(ilp->alloc == 0) + continue; + + switch(ilp->state) { + case Ilclosed: + case Illistening: + break; + + case Ilclosing: + if(later(msec, ilp->timeout, "timeout")){ + if(ilp->rexmit > MaxRexmit){ + ilp->state = Ilclosed; + ilhangup(cp, "connection timed out-0", 0); + break; + } + ilsendctl(cp, 0, Ilclose, ilp->next, ilp->recvd, 0); + ilbackoff(ilp); + } + break; + + case Ilsyncee: + case Ilsyncer: + if(later(msec, ilp->timeout, "timeout")){ + if(ilp->rexmit > MaxRexmit){ + ilp->state = Ilclosed; + ilhangup(cp, "connection timed out-1", 0); + break; + } + ilsendctl(cp, 0, Ilsync, ilp->start, ilp->recvd, 0); + ilbackoff(ilp); + } + break; + + case Ilestablished: + if(ilp->recvd != ilp->acksent) + if(later(msec, ilp->acktime, "acktime")) + ilsendctl(cp, 0, Ilack, ilp->next, ilp->recvd, 0); + + if(later(msec, ilp->querytime, "querytime")){ + if(later(msec, ilp->lastrecv+DeathTime, "deathtime")){ + ilhangup(cp, "connection timed out-2", 0); + break; + } + ilsendctl(cp, 0, Ilquery, ilp->next, ilp->recvd, ilnextqt(ilp)); + ilp->querytime = msec + QueryTime; + } + if(ilp->unacked != nil) + if(later(msec, ilp->timeout, "timeout")) { + if(ilp->rexmit > MaxRexmit) { + ilp->state = Ilclosed; + ilhangup(cp, "connection timed out-3", 0); + break; + } + ilsendctl(cp, 0, Ilquery, ilp->next, ilp->recvd, ilnextqt(ilp)); + ilbackoff(ilp); + } + break; + } + } + unlock(&il); + alarm(Iltickms, callil, 0); + sleep(&ilt, no, 0); + goto loop; +} + +static +int +notsyncer(void *ic) +{ + return ((Ilp*)ic)->state != Ilsyncer; +} + +static +void +callildial(Alarm *a, void*) +{ + + cancel(a); + wakeup(&ild); +} + +static +int +ilnextqt(Ilp *ilp) +{ + int x; + + lock(ilp); + x = ilp->qtx; + if(++x > Nqt) + x = 1; + ilp->qtx = x; + ilp->qt[x] = ilp->next-1; /* highest xmitted packet */ + ilp->qt[0] = ilp->qt[x]; /* compatibility with old implementations */ + unlock(ilp); + + return x; +} + +#define HOWMANY(x, y) (((x)+((y)-1))/(y)) +#define ROUNDUP(x, y) (HOWMANY((x), (y))*(y)) + +static void +ilgoaway(Msgbuf *inmb, Ifc *ifc) +{ + Chan *cp; + int size; + Ilpkt *ih, *inih; + Msgbuf *mb; + Ilp *ilp; + uchar *p; + + inih = (Ilpkt*)inmb->data; + + /* allocate a temporary message, channel, and Ilp structure */ + size = ROUNDUP(Ensize+Ipsize+Ilsize, BY2WD)+sizeof(Chan)+sizeof(Ilp); + mb = mballoc(size, nil, Mbil3); + p = mb->data; + p += ROUNDUP(Ensize+Ipsize+Ilsize, BY2WD); + cp = (Chan*)p; + p += sizeof(Chan); + ilp = (Ilp*)p; + + /* link them together */ + cp->ifc = ifc; + mb->chan = cp; + cp->pdata = ilp; + + /* figure out next hop */ + memmove(ilp->ipgate, inih->src, Pasize); + if((nhgetl(ifc->ipa)&ifc->mask) != (nhgetl(inih->src)&ifc->mask)) + iproute(ilp->ipgate, inih->src, ifc->netgate); + + /* create a close message */ + ih = (Ilpkt*)mb->data; + ih->proto = Ilproto; + hnputs(ih->illen, Ilsize); + memmove(ih->src, ifc->ipa, Pasize); + memmove(ih->dst, inih->src, Pasize); + memmove(ih->ilsrc, inih->ildst, sizeof(ih->ilsrc)); + memmove(ih->ildst, inih->ilsrc, sizeof(ih->ildst)); + memmove(ih->ilid, inih->ilack, sizeof(ih->ilid)); + memmove(ih->ilack, inih->ilid, sizeof(ih->ilack)); + ih->iltype = Ilclose; + ih->ilspec = 0; + ih->ilsum[0] = 0; + ih->ilsum[1] = 0; + hnputs(ih->ilsum, ptclcsum((uchar*)mb->data+(Ensize+Ipsize), Ilsize)); + + ipsend(mb); +} diff -Nru /sys/src/fs/ip/ip.c /sys/src/fs/ip/ip.c --- /sys/src/fs/ip/ip.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/ip/ip.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,235 @@ +#include "all.h" + +#include "../ip/ip.h" + +#define DEBUG if(1||cons.flags&Fip)print + +typedef struct Rock Rock; +typedef struct Frag Frag; + +struct Frag +{ + int start; + int end; +}; + +struct Rock +{ + uchar src[Pasize]; + uchar dst[Pasize]; + int id; /* src,dst,id are address of the rock */ + Msgbuf* mb; /* reassembly. if 0, the rock is empty */ + Timet age; /* timeout to throw away */ + int last; /* set to data size when last frag arrives */ + int nfrag; + Frag frag[Nfrag]; +}; + +static +struct +{ + Lock; + Rock rock[Nrock]; +} ip; + +void +ipreceive(Enpkt *ep, int l, Ifc *ifc) +{ + Ippkt *p; + Msgbuf *mb; + Rock *r, *or; + Frag *f; + int len, id, frag, off, loff, i, n; + Ippkt pkt; + Timet t; + + p = (Ippkt*)ep; + if(l < Ensize+Ipsize) { + ifc->sumerr++; + print("ip: en too small\n"); + return; + } + if(l > LARGEBUF) { + ifc->sumerr++; + print("ip: en too large\n"); + return; + } + + memmove(&pkt, p, Ensize+Ipsize); /* copy pkt to 'real' memory */ + if(pkt.vihl != (IP_VER|IP_HLEN)) + return; + if(!ipforme(pkt.dst, ifc)) + return; + if(ipcsum(&pkt.vihl)) { + ifc->sumerr++; + print("ip: checksum error (from %I)\n", pkt.src); + return; + } + + frag = nhgets(pkt.frag); + len = nhgets(pkt.length) - Ipsize; + id = nhgets(pkt.id); + + /* + * total ip msg fits into one frag + */ + if((frag & ~IP_DF) == 0) { + mb = mballoc(l, 0, Mbip3); + memmove(mb->data, &pkt, Ensize+Ipsize); + memmove(mb->data + (Ensize+Ipsize), + (uchar*)p + (Ensize+Ipsize), l-(Ensize+Ipsize)); + goto send; + } + + /* + * throw away old rocks. + */ + t = toytime(); + lock(&ip); + r = ip.rock; + for(i=0; imb && t >= r->age) { + mbfree(r->mb); + r->mb = 0; + } + + /* + * reassembly of fragments + * look up rock by src, dst, id. + */ + or = 0; + r = ip.rock; + for(i=0; imb == 0) { + if(or == 0) + or = r; + continue; + } + if(id == r->id) + if(memcmp(r->src, pkt.src, Pasize) == 0) + if(memcmp(r->dst, pkt.dst, Pasize) == 0) + goto found; + } + r = or; + if(r == 0) { + /* no available rocks */ + r = ip.rock; + for(i=0; imb) { + mbfree(r->mb); + r->mb = 0; + } + r = ip.rock; + } + r->id = id; + r->mb = mballoc(LARGEBUF, 0, Mbip2); + memmove(r->src, pkt.src, Pasize); + memmove(r->dst, pkt.dst, Pasize); + r->nfrag = 0; + r->last = 0; + +found: + mb = r->mb; + r->age = t + SECOND(30); + + off = (frag & ~(IP_DF|IP_MF)) << 3; + if(len+off+Ensize+Ipsize > mb->count) { + /* ip pkt too big */ + mbfree(mb); + r->mb = 0; + goto uout; + } + if(!(frag & IP_MF)) + r->last = off+len; /* found the end */ + + memmove(mb->data+(Ensize+Ipsize)+off, + (uchar*)p + (Ensize+Ipsize), len); + + /* + * frag algorithm: + * first entry is easy + */ + n = r->nfrag; + if(n == 0) { + r->frag[0].start = off; + r->frag[0].end = off+len; + r->nfrag = 1; + goto span; + } + + /* + * two in a row is easy + */ + if(r->frag[n-1].end == off) { + r->frag[n-1].end += len; + goto span; + } + + /* + * add this frag + */ + if(n >= Nfrag) { + /* too many frags */ + mbfree(mb); + r->mb = 0; + goto uout; + } + r->frag[n].start = off; + r->frag[n].end = off+len; + n++; + r->nfrag = n; + +span: + /* + * see if we span the whole list + * can be O(n**2), but usually much smaller + */ + if(r->last == 0) + goto uout; + off = 0; + +spanloop: + loff = off; + f = r->frag; + for(i=0; i= f->start && off < f->end) + off = f->end; + if(loff == off) + goto uout; + if(off < r->last) + goto spanloop; + + memmove(mb->data, &pkt, Ensize+Ipsize); + p = (Ippkt*)mb->data; + hnputs(p->length, r->last+Ipsize); + l = r->last + (Ensize+Ipsize); + mb->count = l; + r->mb = 0; + unlock(&ip); + +send: + switch(pkt.proto) { + default: + mbfree(mb); + break; + case Ilproto: + ilrecv(mb, ifc); + break; + case Udpproto: + udprecv(mb, ifc); + break; + case Icmpproto: + icmprecv(mb, ifc); + break; + case Igmpproto: + igmprecv(mb, ifc); + break; + case Tcpproto: + tcprecv(mb, ifc); + break; + } + return; + +uout: + unlock(&ip); +} diff -Nru /sys/src/fs/ip/ip.h /sys/src/fs/ip/ip.h --- /sys/src/fs/ip/ip.h Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/ip/ip.h Tue Nov 1 00:00:00 2011 @@ -0,0 +1,286 @@ +typedef struct Enpkt Enpkt; +typedef struct Arppkt Arppkt; +typedef struct Ippkt Ippkt; +typedef struct Ilpkt Ilpkt; +typedef struct Udppkt Udppkt; +typedef struct Icmppkt Icmppkt; +typedef struct Ifc Ifc; + +enum +{ + Easize = 6, /* Ether address size */ + Pasize = 4, /* IP protocol address size */ +}; + +enum +{ + Nqt= 8, +}; + +typedef +struct Ilp +{ + Queue* reply; /* ethernet output */ + uchar iphis[Pasize]; /* his ip address (index) */ + uchar ipgate[Pasize]; /* his ip/gateway address */ + Chan* chan; /* list of il channels */ + + int alloc; /* 1 means allocated */ + int srcp; /* source port (index) */ + int dstp; /* dest port (index) */ + int state; /* connection state */ + + Lock; + + Msgbuf* unacked; + Msgbuf* unackedtail; + + Msgbuf* outoforder; + + ulong next; /* id of next to send */ + ulong recvd; /* last packet received */ + ulong start; /* local start id */ + ulong rstart; /* remote start id */ + ulong acksent; /* Last packet acked */ + + Timet lastxmit; /* time of last xmit */ + Timet lastrecv; /* time of last recv */ + Timet timeout; /* time out counter */ + Timet acktime; /* acknowledge timer */ + Timet querytime; /* Query timer */ + + ulong delay; /* Average of the fixed rtt delay */ + ulong rate; /* Average byte rate */ + ulong mdev; /* Mean deviation of predicted to real rtt */ + ulong maxrtt; /* largest rtt seen */ + ulong rttack; /* The ack we are waiting for */ + int rttlen; /* Length of rttack packet */ + ulong rttstart; /* Time we issued rttack packet */ + ulong unackedbytes; + int rexmit; /* number of rexmits of *unacked */ + + ulong qt[Nqt+1]; /* state table for query messages */ + int qtx; /* ... index into qt */ + + int window; /* maximum receive window */ + + Rendez syn; /* connect hang out */ +} Ilp; + +/* + * Ethernet header + */ +enum +{ + ETHERMINTU = 60, /* minimum transmit size */ + ETHERMAXTU = 1514, /* maximum transmit size */ + + Arptype = 0x0806, + Iptype = 0x0800, + + Icmpproto = 1, + Igmpproto = 2, + Tcpproto = 6, + Udpproto = 17, + Ilproto = 40, + + Nqueue = 20, + Nfrag = 6, /* max number of non-contig ip fragments */ + Nrock = 20, /* number of partial ip assembly stations */ + Nb = 211, /* number of arp hash buckets */ + Ne = 10, /* number of entries in each arp hash bucket */ + + Ensize = 14, /* ether header size */ + Ipsize = 20, /* ip header size -- doesnt include Ensize */ + Arpsize = 28, /* arp header size -- doesnt include Ensize */ + Ilsize = 18, /* il header size -- doesnt include Ipsize/Ensize */ + Udpsize = 8, /* il header size -- doesnt include Ipsize/Ensize */ + Udpphsize = 12, /* udp pseudo ip header size */ + + IP_VER = 0x40, /* Using IP version 4 */ + IP_HLEN = Ipsize/4, /* Header length in longs */ + IP_DF = 0x4000, /* Don't fragment */ + IP_MF = 0x2000, /* More fragments */ + + Arprequest = 1, + Arpreply, + + Ilfsport = 17008, + Ilauthport = 17020, + Ilfsout = 5000, + SNTP = 123, + SNTP_LOCAL = 6001, +}; + +struct Enpkt +{ + uchar d[Easize]; /* destination address */ + uchar s[Easize]; /* source address */ + uchar type[2]; /* packet type */ + + uchar data[ETHERMAXTU-(6+6+2)]; + uchar crc[4]; +}; + +struct Arppkt +{ + uchar d[Easize]; /* ether header */ + uchar s[Easize]; + uchar type[2]; + + uchar hrd[2]; /* hardware type, must be ether==1 */ + uchar pro[2]; /* protocol, must be ip */ + uchar hln; /* hardware address len, must be Easize */ + uchar pln; /* protocol address len, must be Pasize */ + uchar op[2]; + uchar sha[Easize]; + uchar spa[Pasize]; + uchar tha[Easize]; + uchar tpa[Pasize]; +}; + +struct Ippkt +{ + uchar d[Easize]; /* ether header */ + uchar s[Easize]; + uchar type[2]; + + uchar vihl; /* Version and header length */ + uchar tos; /* Type of service */ + uchar length[2]; /* packet length */ + uchar id[2]; /* Identification */ + uchar frag[2]; /* Fragment information */ + uchar ttl; /* Time to live */ + uchar proto; /* Protocol */ + uchar cksum[2]; /* Header checksum */ + uchar src[Pasize]; /* Ip source */ + uchar dst[Pasize]; /* Ip destination */ +}; + +struct Ilpkt +{ + uchar d[Easize]; /* ether header */ + uchar s[Easize]; + uchar type[2]; + + uchar vihl; /* ip header */ + uchar tos; + uchar length[2]; + uchar id[2]; + uchar frag[2]; + uchar ttl; + uchar proto; + uchar cksum[2]; + uchar src[Pasize]; + uchar dst[Pasize]; + + uchar ilsum[2]; /* Checksum including header */ + uchar illen[2]; /* Packet length */ + uchar iltype; /* Packet type */ + uchar ilspec; /* Special */ + uchar ilsrc[2]; /* Src port */ + uchar ildst[2]; /* Dst port */ + uchar ilid[4]; /* Sequence id */ + uchar ilack[4]; /* Acked sequence */ +}; + +struct Udppkt +{ + uchar d[Easize]; /* ether header */ + uchar s[Easize]; + uchar type[2]; + + uchar vihl; /* ip header */ + uchar tos; + uchar length[2]; + uchar id[2]; + uchar frag[2]; + uchar ttl; + uchar proto; + uchar cksum[2]; + uchar src[Pasize]; + uchar dst[Pasize]; + + uchar udpsrc[2]; /* Src port */ + uchar udpdst[2]; /* Dst port */ + uchar udplen[2]; /* Packet length */ + uchar udpsum[2]; /* Checksum including header */ +}; + +struct Icmppkt +{ + uchar d[Easize]; /* ether header */ + uchar s[Easize]; + uchar type[2]; + + uchar vihl; /* ip header */ + uchar tos; + uchar length[2]; + uchar id[2]; + uchar frag[2]; + uchar ttl; + uchar proto; + uchar cksum[2]; + uchar src[Pasize]; + uchar dst[Pasize]; + + uchar icmptype; /* Src port */ + uchar icmpcode; /* Dst port */ + uchar icmpsum[2]; /* Checksum including header */ + + uchar icmpbody[10]; /* Depends on type */ +}; + +struct Ifc +{ + Lock; + Queue* reply; + Filter work[3]; + Filter rate[3]; + ulong rcverr; + ulong txerr; + ulong sumerr; + ulong rxpkt; + ulong txpkt; + uchar ea[Easize]; /* my ether address */ + uchar ipa[Pasize]; /* my ip address, pulled from netdb */ + uchar netgate[Pasize]; /* my ip gateway, pulled from netdb */ + ulong ipaddr; + ulong mask; + ulong cmask; + Ifc *next; /* List of configured interfaces */ +}; + +Ifc* enets; /* List of configured interfaces */ + +void riprecv(Msgbuf*, Ifc*); +void sntprecv(Msgbuf *mb, Ifc *ifc); + +void arpreceive(Enpkt*, int, Ifc*); +void ipreceive(Enpkt*, int, Ifc*); +void ilrecv(Msgbuf*, Ifc*); +void udprecv(Msgbuf*, Ifc*); +void ilrecv(Msgbuf*, Ifc*); +void icmprecv(Msgbuf*, Ifc*); +void igmprecv(Msgbuf*, Ifc*); +void tcprecv(Msgbuf*, Ifc*); +void iprouteinit(void); +long ipclassmask(uchar*); +void iproute(uchar*, uchar*, uchar*); + +void getipa(Ifc*, int); +int ipforme(uchar*, Ifc*); +int ipcsum(uchar*); + +int ptclcsum(uchar*, int); +void ipsend(Msgbuf*); +void ipsend1(Msgbuf*, Ifc*, uchar*); + +uchar authip[Pasize]; /* ip address of server - from config block */ +uchar sntpip[Pasize]; /* ip address of sntp server */ +struct +{ + uchar sysip[Pasize]; /* my ip - from config block */ + uchar defmask[Pasize];/* ip mask - from config block */ + uchar defgwip[Pasize];/* gateway ip - from config block */ +} ipaddr[10]; diff -Nru /sys/src/fs/ip/ipaux.c /sys/src/fs/ip/ipaux.c --- /sys/src/fs/ip/ipaux.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/ip/ipaux.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,76 @@ +#include "all.h" + +#include "../ip/ip.h" + +int +chartoea(uchar *ea, char *cp) +{ + int i, h, c; + + h = 0; + for(i=0; i= '0' && c <= '9') + c = c - '0'; + else + if(c >= 'a' && c <= 'f') + c = c - 'a' + 10; + else + if(c >= 'A' && c <= 'F') + c = c - 'A' + 10; + else + return 1; + h = (h*16) + c; + if(i & 1) { + *ea++ = h; + h = 0; + } + } + if(*cp != 0) + return 1; + return 0; +} + +int +chartoip(uchar *pa, char *cp) +{ + int i, c, h; + + for(i=0;;) { + h = 0; + for(;;) { + c = *cp++; + if(c < '0' || c > '9') + break; + h = (h*10) + (c-'0'); + } + *pa++ = h; + i++; + if(i == Pasize) { + if(c != 0) + return 1; + return 0; + } + if(c != '.') + return 1; + } +} + +void +getipa(Ifc *ifc, int a) +{ + + memmove(ifc->ipa, ipaddr[a].sysip, Pasize); + memmove(ifc->netgate, ipaddr[a].defgwip, Pasize); + ifc->ipaddr = nhgetl(ifc->ipa); + ifc->mask = nhgetl(ipaddr[a].defmask); + ifc->cmask = ipclassmask(ifc->ipa); +} + +int +isvalidip(uchar ip[Pasize]) +{ + if(ip[0] || ip[1] || ip[2] || ip[3]) + return 1; + return 0; +} diff -Nru /sys/src/fs/ip/iproute.c /sys/src/fs/ip/iproute.c --- /sys/src/fs/ip/iproute.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/ip/iproute.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,425 @@ +#include "all.h" + +#include "../ip/ip.h" + +#define DEBUG if(cons.flags&ralloc.flag)print + +enum +{ + Version= 1, + + /* + * definitions that are innately tied to BSD + */ + AF_INET= 2, + AF_UNSPEC= 0, + + /* + * Packet types. + */ + Request= 1, + Response= 2, + Traceon= 3, + Traceoff= 4, + + Infinity= 16, /* infinite hop count */ + Maxpacket= 488, /* largest packet body */ +}; + + +/* + * network info + */ +typedef struct Rip Rip; +struct Rip +{ + uchar family[2]; + uchar port[2]; + uchar addr[Pasize]; + uchar pad[8]; + uchar metric[4]; +}; +typedef struct Ripmsg Ripmsg; +struct Ripmsg +{ + uchar type; + uchar vers; + uchar pad[2]; + Rip rip[1]; /* the rest of the packet consists of routes */ +}; + +enum +{ + Maxroutes= (Maxpacket-4)/sizeof(Ripmsg), +}; + +/* + * internal route info + */ +enum +{ + Nroute = 2*1024, + Nhash = 256, /* routing hash buckets */ + Nifc = 16, +}; + +typedef struct Route Route; +struct Route +{ + Route *next; + + uchar dest[Pasize]; + uchar mask[Pasize]; + uchar gate[Pasize]; + int metric; + int inuse; + Timet time; +}; +struct +{ + Lock; + Route route[Nroute]; + Route *hash[Nhash]; + int nroute; + ulong flag; + int dorip; +} ralloc; + +uchar classmask[4][4] = +{ + 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x00, +}; + +#define CLASS(p) ((*(uchar*)(p))>>6) + +static void considerroute(Route*); +static void deleteroute(Route*); +static void printroute(Route*); +static void printroutes(void); +static void installroute(Route*); +static void getmask(uchar*, uchar*); +static void maskip(uchar*, uchar*, uchar*); +static int equivip(uchar*, uchar*); +static void cmd_route(int, char*[]); +static ulong rhash(uchar*); + +void +iprouteinit(void) +{ + cmd_install("route", "subcommand -- ip routes", cmd_route); + ralloc.flag = flag_install("route", "-- verbose"); + if(!conf.ripoff) + ralloc.dorip = 1; +} + +static void +cmd_route(int argc, char *argv[]) +{ + Route r; + + if(argc < 2) { +usage: + print("route add dest gate [mask] -- add a route\n"); + print("route delete dest -- remote a route\n"); + print("route print [dest] -- print routes\n"); + print("route ripon -- listen to RIP packets\n"); + print("route ripoff -- ignore RIP packets\n"); + return; + } + if(strcmp(argv[1], "ripoff") == 0) + ralloc.dorip = 0; + else + if(strcmp(argv[1], "ripon") == 0) + ralloc.dorip = 1; + else + if(strcmp(argv[1], "add") == 0) { + switch(argc){ + default: + goto usage; + case 4: + memmove(r.mask, classmask[CLASS(r.dest)], Pasize); + break; + case 5: + if(chartoip(r.mask, argv[4])) + goto usage; + break; + } + if(chartoip(r.dest, argv[2]) || chartoip(r.gate, argv[3])) + goto usage; + r.metric = 0; /* rip can't nuke these */ + deleteroute(&r); + considerroute(&r); + } else + if(strcmp(argv[1], "delete") == 0) { + if(argc != 3 || chartoip(r.dest, argv[2])) + goto usage; + deleteroute(&r); + } else + if(strcmp(argv[1], "print") == 0) { + if(argc == 3) { + if(chartoip(r.dest, argv[2])) + goto usage; + printroute(&r); + } else + printroutes(); + } +} + +/* + * consider installing a route. Do so only if it is better than what + * we have. + */ +static void +considerroute(Route *r) +{ + ulong h, i; + ulong m, nm; + Route *hp, **l; + + r->next = 0; + r->time = time(); + r->inuse = 1; + + lock(&ralloc); + h = rhash(r->dest); + for(hp = ralloc.hash[h]; hp; hp = hp->next) { + if(equivip(hp->dest, r->dest) && equivip(hp->mask, r->mask)) { + /* + * found a match, replace if better (or much newer) + */ + if(r->metric < hp->metric || time()-hp->time > 10*60) { + memmove(hp->gate, r->gate, Pasize); + hp->metric = r->metric; + DEBUG("route: replacement %I & %I -> %I (%d)\n", + hp->dest, hp->mask, hp->dest, hp->metric); + } + if(equivip(r->gate, hp->gate)) + hp->time = time(); + goto done; + } + } + + /* + * no match, look for space + */ + for(hp = ralloc.route; hp < &ralloc.route[Nroute]; hp++) + if(hp->inuse == 0) + break; + if(hp == &ralloc.route[Nroute]) + hp = 0; + + /* + * look for an old entry + */ + for(i = 0; hp == 0 && i < Nhash; i++) { + l = &ralloc.hash[i]; + for(hp = *l; hp; hp = *l) { + if(time() - hp->time > 10*60 && hp->metric > 0){ + *l = hp->next; + break; + } + l = &hp->next; + } + } + + if(hp == 0) { + print("no more routes"); + goto done; + } + + memmove(hp, r, sizeof(Route)); + + /* + * insert largest mask first + */ + m = nhgetl(hp->mask); + for(l = &ralloc.hash[h]; *l; l = &(*l)->next){ + nm = nhgetl((*l)->mask); + if(nm < m) + break; + } + hp->next = *l; + *l = hp; + DEBUG("route: new %I & %I -> %I (%d)\n", hp->dest, hp->mask, + hp->dest, hp->metric); +done: + unlock(&ralloc); +} + +static void +deleteroute(Route *r) +{ + int h; + Route *hp, **l; + + lock(&ralloc); + for(h = 0; h < Nhash; h++) { + l = &ralloc.hash[h]; + for(hp = *l; hp; hp = *l){ + if(equivip(r->dest, hp->dest)) { + *l = hp->next; + hp->next = 0; + hp->inuse = 0; + break; + } + l = &hp->next; + } + } + unlock(&ralloc); +} + +static void +printroutes(void) +{ + Ifc *i; + int h; + Route *hp; + uchar mask[Pasize]; + + lock(&ralloc); + for(h = 0; h < Nhash; h++) + for(hp = ralloc.hash[h]; hp; hp = hp->next) + print("%I & %I -> %I\n", hp->dest, hp->mask, hp->gate); + unlock(&ralloc); + + print("\nifc's\n"); + for(i = enets; i; i = i->next) { + hnputl(mask, i->mask); + print("addr %I mask %I defgate %I\n", i->ipa, mask, i->netgate); + } +} + +static void +printroute(Route *r) +{ + int h; + Route *hp; + uchar net[Pasize]; + + h = rhash(r->dest); + for(hp = ralloc.hash[h]; hp; hp = hp->next){ + maskip(r->dest, hp->mask, net); + if(equivip(hp->dest, net)){ + print("%I & %I -> %I\n", hp->dest, hp->mask, hp->gate); + return; + } + } + print("default * -> %I\n", enets[0].netgate); +} + +void +iproute(uchar *to, uchar *dst, uchar *def) +{ + int h; + Route *hp; + uchar net[Pasize]; + + h = rhash(dst); + for(hp = ralloc.hash[h]; hp; hp = hp->next) { + maskip(dst, hp->mask, net); + if(equivip(hp->dest, net)) { + def = hp->gate; + break; + } + } + memmove(to, def, Pasize); +} + +void +riprecv(Msgbuf *mb, Ifc*) +{ + int n; + Rip *r; + Ripmsg *m; + Udppkt *uh; + Route route; + + if(ralloc.dorip == 0) + goto drop; + + uh = (Udppkt*)mb->data; + m = (Ripmsg*)(mb->data + Ensize + Ipsize + Udpsize); + if(m->type != Response || m->vers != Version) + goto drop; + + n = nhgets(uh->udplen); + n -= Udpsize; + n = n/sizeof(Rip); + + DEBUG("%d routes from %I\n", n, uh->src); + + memmove(route.gate, uh->src, Pasize); + for(r = m->rip; r < &m->rip[n]; r++){ + getmask(route.mask, r->addr); + maskip(r->addr, route.mask, route.dest); + route.metric = nhgetl(r->metric) + 1; + if(route.metric < 1) + continue; + considerroute(&route); + } +drop: + mbfree(mb); +} + +/* + * route's hashed by net, not subnet + */ +static ulong +rhash(uchar *d) +{ + ulong h; + uchar net[Pasize]; + + maskip(d, classmask[CLASS(d)], net); + h = net[0] + net[1] + net[2]; + return h % Nhash; +} + +/* + * figure out what mask to use, if we have a direct connected network + * with the same class net use its subnet mask. + */ +static void +getmask(uchar *mask, uchar *dest) +{ + Ifc *i; + long ip; + + ip = nhgetl(dest); + for(i = enets; i; i = i->next) + if((i->ipaddr & i->cmask) == (ip & i->cmask)) { + hnputl(mask, i->mask); + return; + } + + memmove(mask, classmask[CLASS(dest)], Pasize); +} + +static void +maskip(uchar *a, uchar *m, uchar *n) +{ + int i; + + for(i = 0; i < 4; i++) + n[i] = a[i] & m[i]; +} + +static int +equivip(uchar *a, uchar *b) +{ + int i; + + for(i = 0; i < 4; i++) + if(a[i] != b[i]) + return 0; + return 1; +} + +long +ipclassmask(uchar *ip) +{ + return nhgetl(classmask[CLASS(ip)]); +} diff -Nru /sys/src/fs/ip/mkfile /sys/src/fs/ip/mkfile --- /sys/src/fs/ip/mkfile Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/ip/mkfile Tue Nov 1 00:00:00 2011 @@ -0,0 +1,3 @@ +IPFILES=`{builtin cd ../ip;echo *.c | sed 's/ /|/g; s/\.c//g'} +^($IPFILES)\.$O:R: '../ip/\1.c' + $CC $CFLAGS -I. ../ip/$stem1.c diff -Nru /sys/src/fs/ip/sntp.c /sys/src/fs/ip/sntp.c --- /sys/src/fs/ip/sntp.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/ip/sntp.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,247 @@ +#include "all.h" + +#include "../ip/ip.h" + +typedef struct Sntppkt { + uchar d[Easize]; /* ether header */ + uchar s[Easize]; + uchar type[2]; + + uchar vihl; /* ip header */ + uchar tos; + uchar length[2]; + uchar id[2]; + uchar frag[2]; + uchar ttl; + uchar proto; + uchar cksum[2]; + uchar src[Pasize]; + uchar dst[Pasize]; + + uchar udpsrc[2]; /* Src port */ + uchar udpdst[2]; /* Dst port */ + uchar udplen[2]; /* Packet length */ + uchar udpsum[2]; /* Checksum including header */ + uchar mode; /* li:2, vn:3, mode:3 */ + uchar stratum; /* level of local clock */ + signed char poll; /* log2(max interval between polls) */ + signed char precision; /* log2(clock precision) -6 => mains, + -18 => us */ + uchar rootdelay[4]; /* round trip delay to reference + 16.16 fraction */ + uchar dispersion[4]; /* max error to reference */ + uchar clockid[4]; /* reference clock identifier */ + uchar reftime[8]; /* local time when clock set */ + uchar orgtime[8]; /* local time when client sent request */ + uchar rcvtime[8]; /* time request arrived */ + uchar xmttime[8]; /* time reply sent */ +} Sntppkt; + +enum { + Sntpsize = 4 + 3 * 4 + 4 * 8, + Version = 1, + Stratum = 0, + Poll = 0, + LI = 0, + Symmetric = 2, + ClientMode = 3, + ServerMode = 4, + Epoch = 86400 * (365 * 70 + 17), /* 1900 to 1970 in seconds */ +}; + +#define DEBUG if(cons.flags&sntp.flag)print + +static struct { + Lock; + int flag; + int gotreply; + int kicked; + Rendez r; + Rendez doze; +} sntp; + +static int +done(void*) +{ + return sntp.gotreply != 0; +} + +static int +kicked(void*) +{ + return sntp.kicked != 0; +} + +void +sntprecv(Msgbuf *mb, Ifc *ifc) +{ + Udppkt *uh; + Sntppkt *sh; + int v, li, m, now; + + USED(ifc); + uh = (Udppkt *)mb->data; + DEBUG("sntp: receive from %I\n", uh->src); + if (memcmp(uh->src, sntpip, 4) != 0) { + DEBUG("sntp: wrong IP address\n"); + goto overandout; + } + if (nhgets(uh->udplen) < Sntpsize) { + DEBUG("sntp: packet too small\n"); + goto overandout; + } + sh = (Sntppkt *)mb->data; + v = (sh->mode >> 3) & 7; + li = (sh->mode >> 6); + m = sh->mode & 7; + /* + * if reply from right place and contains time set gotreply + * and wakeup r + */ + DEBUG("sntp: LI %d Version %d Mode %d\n", li, v, m); + if (sh->stratum == 1) { + char buf[5]; + memmove(buf, sh->clockid, 4); + buf[4] = 0; + DEBUG("sntp: Stratum 1 (%s)\n", buf); + } + else { + DEBUG("sntp: Stratum %d\n", sh->stratum); + } + DEBUG("Poll %d Precision %d\n", sh->poll, sh->precision); + DEBUG("RootDelay %ld Dispersion %ld\n", + nhgetl(sh->rootdelay), nhgetl(sh->dispersion)); + if (v == 0 || v > 3) { + DEBUG("sntp: unsupported version\n"); + goto overandout; + } + if (m >= 6 || m == ClientMode) { + DEBUG("sntp: wrong mode\n"); + goto overandout; + } + now = nhgetl(sh->xmttime) - Epoch; + if (li == 3 || now == 0 || sh->stratum == 0) { + /* unsynchronized */ + print("sntp: time server not synchronized\n"); + goto overandout; + } + settime(now); + setrtc(now); + print("sntp: %d\n", now); + sntp.gotreply = 1; + wakeup(&sntp.r); +overandout: + mbfree(mb); +} + +void +sntpsend(void) +{ + ushort sum; + Msgbuf *mb; + Sntppkt *s; + uchar tmp[Pasize]; + Ifc *ifc; + + /* find an interface on the same subnet as sntpip, if any */ + for(ifc = enets; ifc; ifc = ifc->next) { + if(isvalidip(ifc->ipa) && + (nhgetl(ifc->ipa)&ifc->mask) == (nhgetl(sntpip)&ifc->mask)) + break; + } + /* if none, find an interface with a default gateway */ + if(ifc == nil) + for(ifc = enets; ifc; ifc = ifc->next) + if(isvalidip(ifc->ipa) && isvalidip(ifc->netgate)) + break; + if(ifc == nil) { + DEBUG("sntp: can't send to %I; no ifc on same subnet or with default route\n", sntpip); + return; + } + + /* compose a UDP sntp request */ + DEBUG("sntp: sending to %I on ifc %I\n", sntpip, ifc->ipa); + mb = mballoc(Ensize+Ipsize+Udpsize+Sntpsize, 0, Mbsntp); + s = (Sntppkt *)mb->data; + /* IP fields */ + memmove(s->src, ifc->ipa, Pasize); + memmove(s->dst, sntpip, Pasize); + s->proto = Udpproto; + s->ttl = 0; + /* Udp fields */ + hnputs(s->udpsrc, SNTP_LOCAL); + hnputs(s->udpdst, SNTP); + hnputs(s->udplen, Sntpsize + Udpsize); + /* Sntp fields */ + memset(mb->data + Ensize+Ipsize+Udpsize, 0, Sntpsize); + s->mode = 010 | ClientMode; + s->poll = 6; + hnputl(s->orgtime, rtctime() + Epoch); /* leave 0 fraction */ + /* Compute the UDP sum - form psuedo header */ + hnputs(s->cksum, Udpsize + Sntpsize); + hnputs(s->udpsum, 0); + sum = ptclcsum((uchar *)mb->data + Ensize + Ipsize - Udpphsize, + Udpsize + Udpphsize + Sntpsize); + hnputs(s->udpsum, sum); + /* + * now try to send it - cribbed from icmp.c + */ + memmove(tmp, s->dst, Pasize); + if((nhgetl(ifc->ipa)&ifc->mask) != (nhgetl(s->dst)&ifc->mask)) + iproute(tmp, s->dst, ifc->netgate); + ipsend1(mb, ifc, tmp); +} + +#define TRIES 3 +#define INTERVAL (60 * 60 * 1000) +#define TIMO 1000 + +void +sntptask(void) +{ + DEBUG("sntp: running\n"); + tsleep(&sntp.doze, kicked, 0, 2 * 60 * 1000); + for (;;) { + sntp.kicked = 0; + DEBUG("sntp: poll time!\n"); + if (isvalidip(sntpip)) { + int i; + sntp.gotreply = 0; + for (i = 0; i < TRIES; i++) + { + sntpsend(); + tsleep(&sntp.r, done, 0, TIMO); + if (sntp.gotreply) + break; + } + /* clock has been set */ + } + tsleep(&sntp.doze, kicked, 0, INTERVAL); + } +} + +void +cmd_sntp(int argc, char *argv[]) +{ + int i; + + if(argc <= 1) { + print("sntp kick -- check time now\n"); + return; + } + for(i=1; idata; + + plen = mb->count; + if(plen < Ensize+Ipsize+Udpsize) + goto drop; + + udplen = nhgets(uh->udplen); + if(udplen+Ensize+Ipsize > plen) + goto drop; + + /* construct pseudo hdr and check sum */ + uh->ttl = 0; + hnputs(uh->cksum, udplen); + if(nhgets(uh->udpsum) + && ptclcsum((uchar*)uh+(Ensize+Ipsize-Udpphsize), udplen + Udpphsize) != 0) { + if(ifc->sumerr < 3) + print("udp: cksum error %I\n", uh->src); + ifc->sumerr++; + goto drop; + } + + switch(nhgets(uh->udpdst)) { + case 520: + riprecv(mb, ifc); + break; + case SNTP_LOCAL: + sntprecv(mb, ifc); + break; + default: + mbfree(mb); + break; + } + return; + +drop: + mbfree(mb); +} diff -Nru /sys/src/fs/mkfile /sys/src/fs/mkfile --- /sys/src/fs/mkfile Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/mkfile Tue Nov 1 00:00:00 2011 @@ -0,0 +1,25 @@ +ARCH=\ + fs\ + fs64\ + 9netics32.16k\ + 9netics64.8k\ + choline\ + emelie\ + +all:V: + for(i in $ARCH)@{ + cd $i + mk + } + +installall install:V: + for(i in $ARCH) @{ + cd $i + mk install + } + +clean:V: + for(i in $ARCH) @{ + cd $i + mk clean + } diff -Nru /sys/src/fs/pc/8250.c /sys/src/fs/pc/8250.c --- /sys/src/fs/pc/8250.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/pc/8250.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,382 @@ +#include "all.h" +#include "mem.h" +#include "ureg.h" +#include "io.h" + +enum { + Development = 1, /* i.e., debugging */ + DLE = 0x10, /* ^p == DLE */ + Asciimask = 0x7f, +}; + +/* + * INS8250 uart + */ +enum +{ + /* + * register numbers + */ + Data= 0, /* xmit/rcv buffer */ + Iena= 1, /* interrupt enable */ + Ircv= (1<<0), /* for char rcv'd */ + Ixmt= (1<<1), /* for xmit buffer empty */ + Irstat=(1<<2), /* for change in rcv'er status */ + Imstat=(1<<3), /* for change in modem status */ + Istat= 2, /* interrupt flag (read) */ + Fenabd=(3<<6), /* on if fifo's enabled */ + Fifoctl=2, /* fifo control (write) */ + Fena= (1<<0), /* enable xmit/rcv fifos */ + Ftrig= (1<<6), /* trigger after 4 input characters */ + Fclear=(3<<1), /* clear xmit & rcv fifos */ + Format= 3, /* byte format */ + Bits8= (3<<0), /* 8 bits/byte */ + Stop2= (1<<2), /* 2 stop bits */ + Pena= (1<<3), /* generate parity */ + Peven= (1<<4), /* even parity */ + Pforce=(1<<5), /* force parity */ + Break= (1<<6), /* generate a break */ + Dra= (1<<7), /* address the divisor */ + Mctl= 4, /* modem control */ + Dtr= (1<<0), /* data terminal ready */ + Rts= (1<<1), /* request to send */ + Ri= (1<<2), /* ring */ + Inton= (1<<3), /* turn on interrupts */ + Loop= (1<<4), /* loop back */ + Lstat= 5, /* line status */ + Inready=(1<<0), /* receive buffer full */ + Oerror=(1<<1), /* receiver overrun */ + Perror=(1<<2), /* receiver parity error */ + Ferror=(1<<3), /* rcv framing error */ + Outready=(1<<5), /* output buffer empty */ + Mstat= 6, /* modem status */ + Ctsc= (1<<0), /* clear to send changed */ + Dsrc= (1<<1), /* data set ready changed */ + Rire= (1<<2), /* rising edge of ring indicator */ + Dcdc= (1<<3), /* data carrier detect changed */ + Cts= (1<<4), /* complement of clear to send line */ + Dsr= (1<<5), /* complement of data set ready line */ + Ring= (1<<6), /* complement of ring indicator line */ + Dcd= (1<<7), /* complement of data carrier detect line */ + Scratch=7, /* scratchpad */ + Dlsb= 0, /* divisor lsb */ + Dmsb= 1, /* divisor msb */ + + Serial= 0, + Modem= 1, +}; + +typedef struct Uart Uart; +struct Uart +{ + int port; + uchar sticky[8]; /* sticky write register values */ + int nofifo; + + void (*rx)(int); /* routine to take a received character */ + int (*tx)(void); /* routine to get a character to transmit */ + + ulong frame; + ulong overrun; +}; + +/* externally-visible console-on-a-uart flag */ +int uartcons; + +Uart uart[2]; + +#define UartFREQ 1843200 + +#define uartwrreg(u,r,v) outb((u)->port + r, (u)->sticky[r] | (v)) +#define uartrdreg(u,r) inb((u)->port + r) + +/* + * set the baud rate by calculating and setting the baudrate + * generator constant. This will work with fairly non-standard + * baud rates. + */ +static void +uartsetbaud(Uart *up, int rate) +{ + ulong brconst; + + brconst = (UartFREQ+8*rate-1)/(16*rate); + + uartwrreg(up, Format, Dra); + outb(up->port+Dmsb, (brconst>>8) & 0xff); + outb(up->port+Dlsb, brconst & 0xff); + uartwrreg(up, Format, 0); +} + +/* + * toggle DTR + */ +static void +uartdtr(Uart *up, int n) +{ + if(n) + up->sticky[Mctl] |= Dtr; + else + up->sticky[Mctl] &= ~Dtr; + uartwrreg(up, Mctl, 0); +} + +/* + * toggle RTS + */ +static void +uartrts(Uart *up, int n) +{ + if(n) + up->sticky[Mctl] |= Rts; + else + up->sticky[Mctl] &= ~Rts; + uartwrreg(up, Mctl, 0); +} + +/* + * Enable/disable FIFOs (if possible). + */ +static void +uartfifo(Uart *up, int n) +{ + int i, s; + + if(up->nofifo) + return; + + s = splhi(); + + /* reset fifos */ + uartwrreg(up, Fifoctl, Fclear); + + /* empty buffer and interrupt conditions */ + for(i = 0; i < 16; i++){ + uartrdreg(up, Istat); + uartrdreg(up, Data); + } + + /* turn on fifo */ + if(n){ + uartwrreg(up, Fifoctl, Fena|Ftrig); + + if((uartrdreg(up, Istat) & Fenabd) == 0){ + /* didn't work, must be an earlier chip type */ + up->nofifo = 1; + } + } + + splx(s); +} + +static void +uartintr(Ureg *ur, void *arg) +{ + Uart *up; + int ch; + int s, l, loops; + + USED(ur); + + up = arg; + for(loops = 0; loops < 1024; loops++){ + s = uartrdreg(up, Istat); + switch(s & 0x3F){ + case 6: /* receiver line status */ + l = uartrdreg(up, Lstat); + if(l & Ferror) + up->frame++; + if(l & Oerror) + up->overrun++; + break; + + case 4: /* received data available */ + case 12: + ch = inb(up->port+Data); + if (Development && (ch & Asciimask) == DLE) + firmware(); + if(up->rx) + (*up->rx)(ch & Asciimask); + break; + + case 2: /* transmitter empty */ + ch = -1; + if(up->tx) + ch = (*up->tx)(); + if(ch != -1) + outb(up->port+Data, ch); + break; + + case 0: /* modem status */ + uartrdreg(up, Mstat); + break; + + default: + if(s&1) + return; + print("weird modem interrupt #%2.2ux\n", s); + break; + } + } + panic("uartintr: 0x%2.2ux\n", uartrdreg(up, Istat)); +} + +/* + * turn on a port's interrupts. set DTR and RTS + */ +static void +uartenable(Uart *up) +{ + /* + * turn on interrupts + */ + up->sticky[Iena] = 0; + if(up->tx) + up->sticky[Iena] |= Ixmt; + if(up->rx) + up->sticky[Iena] |= Ircv|Irstat; + + /* + * turn on DTR and RTS + */ + uartdtr(up, 1); + uartrts(up, 1); + uartfifo(up, 1); + + uartwrreg(up, Iena, 0); +} + +void +uartspecial(int port, void (*rx)(int), int (*tx)(void), int baud) +{ + Uart *up = &uart[0]; + + if(up->port) + return; + + switch(port){ + + case 0: + up->port = 0x3F8; + setvec(Uart0vec, uartintr, up); + break; + + case 1: + up->port = 0x2F8; + setvec(Uart1vec, uartintr, up); + break; + + default: + return; + } + + /* + * set rate to 9600 baud. + * 8 bits/character. + * 1 stop bit. + * interrupts enabled. + */ + uartsetbaud(up, 9600); + up->sticky[Format] = Bits8; + uartwrreg(up, Format, 0); + up->sticky[Mctl] |= Inton; + uartwrreg(up, Mctl, 0x0); + + up->rx = rx; + up->tx = tx; + uartenable(up); + if(baud) + uartsetbaud(up, baud); + uartcons = 1; +} + +int +uartgetc(void) +{ + Uart *up = &uart[0]; + + if(uartrdreg(up, Lstat) & Inready) + return inb(up->port+Data); + return 0; +} + +void +uartputc(int c) +{ + Uart *up = &uart[0]; + int i; + + for(i = 0; i < 100; i++){ + if(uartrdreg(up, Lstat) & Outready) + break; + delay(1); + } + outb(up->port+Data, c); +} + +void +uartspecial1(int port, void (*rx)(int), int (*tx)(void), int baud) +{ + Uart *up = &uart[1]; + + if(up->port) + return; + + switch(port){ + + case 0: + up->port = 0x3F8; + setvec(Uart0vec, uartintr, up); + break; + + case 1: + up->port = 0x2F8; + setvec(Uart1vec, uartintr, up); + break; + + default: + return; + } + + /* + * set rate to 9600 baud. + * 8 bits/character. + * 1 stop bit. + * interrupts enabled. + */ + uartsetbaud(up, 9600); + up->sticky[Format] = Bits8; + uartwrreg(up, Format, 0); + up->sticky[Mctl] |= Inton; + uartwrreg(up, Mctl, 0x0); + + up->rx = rx; + up->tx = tx; + uartenable(up); + if(baud) + uartsetbaud(up, baud); +} + +int +uartgetc1(void) +{ + Uart *up = &uart[1]; + + if(uartrdreg(up, Lstat) & Inready) + return inb(up->port+Data); + return 0; +} + +void +uartputc1(int c) +{ + Uart *up = &uart[1]; + int i; + + for(i = 0; i < 100; i++){ + if(uartrdreg(up, Lstat) & Outready) + break; + delay(1); + } + outb(up->port+Data, c); +} diff -Nru /sys/src/fs/pc/8253.c /sys/src/fs/pc/8253.c --- /sys/src/fs/pc/8253.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/pc/8253.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,351 @@ +#include "all.h" +#include "mem.h" +#include "ureg.h" +#include "io.h" + +/* + * 8253 timer + */ +enum +{ + T0cntr = 0x40, /* counter ports */ + T1cntr = 0x41, /* ... */ + T2cntr = 0x42, /* ... */ + Tmode = 0x43, /* mode port */ + + /* commands */ + Latch0 = 0x00, /* latch counter 0's value */ + Load0 = 0x30, /* load counter 0 with 2 bytes */ + + /* modes */ + Square = 0x36, /* periodic square wave */ + Trigger = 0x30, /* interrupt on terminal count */ + + Freq = 1193182, /* Real clock frequency */ +}; + +static uvlong cpufreq = 66000000; +static uvlong cpuhz; +static int cpumhz = 66; +static int loopconst = 100; +/*static*/ int cpuidax, cpuiddx; +static char cpuidid[16]; +static int havetsc; + +static void +clockintr(Ureg *ur, void *v) +{ + USED(v); + clock(0, ur->pc); +} + +#define STEPPING(x) ((x)&0xf) +#define MODEL(x) (((x)>>4)&0xf) +#define FAMILY(x) (((x)>>8)&0xf) + +enum +{ + /* flags */ + CpuidFPU = 0x001, /* on-chip floating point unit */ + CpuidMCE = 0x080, /* machine check exception */ + CpuidCX8 = 0x100, /* CMPXCHG8B instruction */ +}; + +typedef struct +{ + int family; + int model; + int aalcycles; + char *name; +} X86type; + +static X86type x86intel[] = +{ + { 4, 0, 22, "486DX", }, /* known chips */ + { 4, 1, 22, "486DX50", }, + { 4, 2, 22, "486SX", }, + { 4, 3, 22, "486DX2", }, + { 4, 4, 22, "486SL", }, + { 4, 5, 22, "486SX2", }, + { 4, 7, 22, "DX2WB", }, /* P24D */ + { 4, 8, 22, "DX4", }, /* P24C */ + { 4, 9, 22, "DX4WB", }, /* P24CT */ + { 5, 0, 23, "P5", }, + { 5, 1, 23, "P5", }, + { 5, 2, 23, "P54C", }, + { 5, 3, 23, "P24T", }, + { 5, 4, 23, "P55C MMX", }, + { 5, 7, 23, "P54C VRT", }, + { 6, 1, 16, "PentiumPro", },/* trial and error */ + { 6, 3, 16, "PentiumII", }, + { 6, 5, 16, "PentiumII/Xeon", }, + { 6, 6, 16, "Celeron", }, + { 6, 7, 16, "PentiumIII/Xeon", }, + { 6, 8, 16, "PentiumIII/Xeon", }, + { 6, 0xB, 16, "PentiumIII/Xeon", }, + { 0xF, 1, 16, "P4", }, /* P4 */ + { 0xF, 2, 16, "PentiumIV/Xeon", }, + + { 3, -1, 32, "386", }, /* family defaults */ + { 4, -1, 22, "486", }, + { 5, -1, 23, "P5", }, + { 6, -1, 16, "P6", }, + { 0xF, -1, 16, "P4", }, /* P4 */ + + { -1, -1, 16, "unknown", }, /* total default */ +}; + +/* + * The AMD processors all implement the CPUID instruction. + * The later ones also return the processor name via functions + * 0x80000002, 0x80000003 and 0x80000004 in registers AX, BX, CX + * and DX: + * K5 "AMD-K5(tm) Processor" + * K6 "AMD-K6tm w/ multimedia extensions" + * K6 3D "AMD-K6(tm) 3D processor" + * K6 3D+ ? + */ +static X86type x86amd[] = +{ + { 5, 0, 23, "AMD-K5", }, /* guesswork */ + { 5, 1, 23, "AMD-K5", }, /* guesswork */ + { 5, 2, 23, "AMD-K5", }, /* guesswork */ + { 5, 3, 23, "AMD-K5", }, /* guesswork */ + { 5, 6, 11, "AMD-K6", }, /* trial and error */ + { 5, 7, 11, "AMD-K6", }, /* trial and error */ + { 5, 8, 11, "AMD-K6-2", }, /* trial and error */ + { 5, 9, 11, "AMD-K6-III", },/* trial and error */ + + { 6, 1, 11, "AMD-Athlon", },/* trial and error */ + { 6, 2, 11, "AMD-Athlon", },/* trial and error */ + + { 4, -1, 22, "Am486", }, /* guesswork */ + { 5, -1, 23, "AMD-K5/K6", }, /* guesswork */ + { 6, -1, 11, "AMD-Athlon", },/* guesswork */ + { 0xF, -1, 11, "AMD64", }, /* guesswork */ + + { -1, -1, 23, "unknown", }, /* total default */ +}; + +/* + * WinChip 240MHz + */ +static X86type x86winchip[] = +{ + {5, 4, 23, "Winchip",}, /* guesswork */ + {6, 7, 23, "Via C3 Samuel 2 or Ezra",}, + {6, 8, 23, "Via C3 Ezra-T",}, + { -1, -1, 23, "unknown", }, /* total default */ +}; + +/* + * SiS 55x + */ +static X86type x86sis[] = +{ + {5, 0, 23, "SiS 55x",}, /* guesswork */ + { -1, -1, 23, "unknown", }, /* total default */ +}; + +static X86type *cputype; + +static void +simplecycles(uvlong*x) +{ + *x = m->ticks; +} + +void (*cycles)(uvlong*) = simplecycles; +void _cycles(uvlong*); /* in l.s */ + +static void +nop(void) +{ +} + +void (*coherence)(void) = nop; + +/* + * delay for l milliseconds more or less. delayloop is set by + * clockinit() to match the actual CPU speed. + */ +void +delay(int l) +{ + l *= loopconst; + if(l <= 0) + l = 1; + aamloop(l); +} + +/* + * microsecond delay + */ +void +microdelay(int l) +{ + l *= loopconst; + l /= 1000; + if(l <= 0) + l = 1; + aamloop(l); +} + +void +printcpufreq(void) +{ + int i; + char buf[128]; + + i = sprint(buf, "cpu%d: %dMHz ", 0, cpumhz); + if(cpuidid[0]) + i += sprint(buf+i, "%s ", cpuidid); + sprint(buf+i, "%s (cpuid: AX 0x%4.4ux DX 0x%4.4ux)\n", + cputype->name, cpuidax, cpuiddx); + print(buf); +} + +void +clockinit(void) +{ + int x, y; /* change in counter */ + int family, model, loops, incr; + X86type *t; + uvlong a, b; + + /* + * set vector for clock interrupts + */ + setvec(Clockvec, clockintr, 0); + + /* + * figure out what we are + */ + cpuid(cpuidid, &cpuidax, &cpuiddx); + if(strncmp(cpuidid, "AuthenticAMD", 12) == 0) + t = x86amd; + else if(strncmp(cpuidid, "CentaurHauls", 12) == 0) + t = x86winchip; + else if(strncmp(cpuidid, "SiS SiS SiS ", 12) == 0) + t = x86sis; + else + t = x86intel; + family = FAMILY(cpuidax); + model = MODEL(cpuidax); + while(t->name){ + if((t->family == family && t->model == model) + || (t->family == family && t->model == -1) + || (t->family == -1)) + break; + t++; + } + cputype = t; + + if(family >= 5) + coherence = wbflush; + + /* + * if there is one, set tsc to a known value + */ + if(cpuiddx & 0x10){ + havetsc = 1; + cycles = _cycles; + if(cpuiddx & 0x20) + wrmsr(0x10, 0); + } + + /* + * set clock for 1/HZ seconds + */ + outb(Tmode, Load0|Square); + outb(T0cntr, (Freq/HZ)); /* low byte */ + outb(T0cntr, (Freq/HZ)>>8); /* high byte */ + + /* + * Introduce a little delay to make sure the count is + * latched and the timer is counting down; with a fast + * enough processor this may not be the case. + * The i8254 (which this probably is) has a read-back + * command which can be used to make sure the counting + * register has been written into the counting element. + */ + x = (Freq/HZ); + for(loops = 0; loops < 100000 && x >= (Freq/HZ); loops++){ + outb(Tmode, Latch0); + x = inb(T0cntr); + x |= inb(T0cntr)<<8; + } + + /* find biggest loop that doesn't wrap */ + incr = 16000000/(t->aalcycles*HZ*2); + x = 2000; + for(loops = incr; loops < 64*1024; loops += incr) { + + /* + * measure time for the loop + * + * MOVL loops,CX + * aaml1: AAM + * LOOP aaml1 + * + * the time for the loop should be independent of external + * cache and memory system since it fits in the execution + * prefetch buffer. + * + */ + outb(Tmode, Latch0); + cycles(&a); + x = inb(T0cntr); + x |= inb(T0cntr)<<8; + aamloop(loops); + outb(Tmode, Latch0); + cycles(&b); + y = inb(T0cntr); + y |= inb(T0cntr)<<8; + x -= y; + + if(x < 0) + x += Freq/HZ; + + if(x > Freq/(3*HZ)) + break; + } + + /* + * figure out clock frequency and a loop multiplier for delay(). + * n.b. counter goes up by 2*Freq + */ + cpufreq = (vlong)loops*((t->aalcycles*2*Freq)/x); + loopconst = (cpufreq/1000)/t->aalcycles; /* AAM+LOOP's for 1 ms */ + if (0) + print("loops %d x %d cpufreq %,lld loopconst %d\n", loops, x, cpufreq, loopconst); + + if(havetsc){ + /* counter goes up by 2*Freq */ + b = (b-a)<<1; + b *= Freq; + b /= x; + + /* + * round to the nearest megahz + */ + cpumhz = (b+500000)/1000000L; + cpuhz = b; + } else { + /* + * add in possible 0.5% error and convert to MHz + */ + cpumhz = (cpufreq + cpufreq/200)/1000000; + cpuhz = cpufreq; + } + if (0) { + print("cpuhz %,lld cpumhz %d\n", cpuhz, cpumhz); + delay(10*1000); + } +} + +void +clockreload(Timet n) +{ + USED(n); +} diff -Nru /sys/src/fs/pc/cga.c /sys/src/fs/pc/cga.c --- /sys/src/fs/pc/cga.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/pc/cga.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,101 @@ +#include "all.h" +#include "mem.h" + +enum { + Width = 160, + Height = 25, + + Attr = 7, /* white on black */ +}; + +#define CGASCREENBASE ((uchar*)(KZERO|0xB8000)) + +static int pos; +static int screeninitdone; +static Lock screenlock; + +static uchar +cgaregr(int index) +{ + outb(0x3D4, index); + return inb(0x3D4+1) & 0xFF; +} + +static void +cgaregw(int index, int data) +{ + outb(0x3D4, index); + outb(0x3D4+1, data); +} + +static void +movecursor(void) +{ + cgaregw(0x0E, (pos/2>>8) & 0xFF); + cgaregw(0x0F, pos/2 & 0xFF); + CGASCREENBASE[pos+1] = Attr; +} + +static void +cgascreenputc(int c) +{ + int i; + + if(c == '\r') + return; + if(c == '\n'){ + pos = pos/Width; + pos = (pos+1)*Width; + } + else if(c == '\t'){ + i = 4 - ((pos/2)&3); + while(i-->0) + cgascreenputc(' '); + } + else if(c == '\b'){ + if(pos >= 2) + pos -= 2; + cgascreenputc(' '); + pos -= 2; + } + else{ + CGASCREENBASE[pos++] = c; + CGASCREENBASE[pos++] = Attr; + } + if(pos >= Width*Height){ + memmove(CGASCREENBASE, &CGASCREENBASE[Width], Width*(Height-1)); + memset(&CGASCREENBASE[Width*(Height-1)], 0, Width); + pos = Width*(Height-1); + } + movecursor(); +} + +static void +screeninit(void) +{ + lock(&screenlock); + if(screeninitdone == 0){ + pos = cgaregr(0x0E)<<8; + pos |= cgaregr(0x0F); + pos *= 2; + screeninitdone = 1; + } + unlock(&screenlock); +} + +void +cgaputs(char* s, int n) +{ + if(screeninitdone == 0) + screeninit(); + while(n-- > 0) + cgascreenputc(*s++); +} + +void +cgaputc(int c) +{ + if(screeninitdone == 0) + screeninit(); + cgascreenputc(c); +} diff -Nru /sys/src/fs/pc/compat.c /sys/src/fs/pc/compat.c --- /sys/src/fs/pc/compat.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/pc/compat.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,107 @@ +/* + * fs kernel compatibility hacks for drivers from the cpu/terminal kernel + */ +#include "all.h" +#include "io.h" +#include "mem.h" +#include "../ip/ip.h" /* for Ether */ +#include "etherif.h" /* for Ether */ +#include "compat.h" + +enum { + VectorPIC = 24, /* external [A]PIC interrupts */ +}; + +void +free(void *p) /* there's a struct member named "free". sigh. */ +{ + USED(p); +} + +void * +mallocz(ulong sz, int clr) +{ + void *p = malloc(sz); + + if (clr && p != nil) + memset(p, '\0', sz); + return p; +} + +void +freeb(Block *b) +{ + mbfree(b); +} + +/* + * free a list of blocks + */ +void +freeblist(Block *b) +{ + Block *next; + + for(; b != 0; b = next){ + next = b->next; + b->next = 0; + freeb(b); + } +} + +int +readstr(vlong, void *, int, char *) +{ + return 0; +} + +void +addethercard(char *, int (*)(struct Ether *)) +{ +} + +void +kproc(char *name, void (*f)(void), void *arg) +{ + userinit(f, arg, strdup(name)); +} + +void +intrenable(int irq, void (*f)(Ureg*, void*), void* a, int tbdf, char *name) +{ + setvec(irq+VectorPIC, f, a); + USED(tbdf, name); +} + +int +intrdisable(int irq, void (*f)(Ureg *, void *), void *a, int tbdf, char *name) +{ + USED(irq, f, a, tbdf, name); + return -1; +} + +/* + * Atomically replace *p with copy of s + */ +void +kstrdup(char **p, char *s) +{ + int n; + char *t, *prev; + static Lock l; + + n = strlen(s)+1; + /* if it's a user, we can wait for memory; if not, something's very wrong */ + if(0 && u){ + t = /* s */malloc(n); + // setmalloctag(t, getcallerpc(&p)); + }else{ + t = malloc(n); + if(t == nil) + panic("kstrdup: no memory"); + } + memmove(t, s, n); + prev = *p; + *p = t; + free(prev); +} diff -Nru /sys/src/fs/pc/compat.h /sys/src/fs/pc/compat.h --- /sys/src/fs/pc/compat.h Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/pc/compat.h Tue Nov 1 00:00:00 2011 @@ -0,0 +1,86 @@ +/* + * fs kernel compatibility hacks for drivers from the cpu/terminal kernel + */ +#define ETHERIQ(a, b, c) etheriq((a), (b)) +/* + * cpu kernel uses bp->rp to point to start of packet and bp->wp to point + * just past valid data in the packet. + * fs kernel uses bp->data to point to start of packet and bp->data+bp->count + * points just past valid data. + * except beware that mballoc(count, ...) sets bp->count = count(!) + */ +#define BLEN(bp) (bp)->count +#define SETWPCNT(bp, cnt) (bp)->count = (cnt) +/* mballoc does: mb->data = mb->xdata+256; */ +#define BLKRESET(bp) ((bp)->data = (bp)->xdata +256, (bp)->count = 0) +#define INCRPTR(bp, incr) (bp)->count += (incr) +#define ENDDATA(bp) ((bp)->data + (bp)->count) + +#define ROUND(s, sz) (((s)+((sz)-1))&~((sz)-1)) + +#define Block Msgbuf +#define rp data /* Block member → Msgbuf member */ +#define Etherpkt Enpkt +#define Eaddrlen Easize +#define ETHERHDRSIZE Ensize + +#ifndef CACHELINESZ +#define CACHELINESZ 32 /* pentium & later */ +#endif + +#define KNAMELEN NAMELEN +#define READSTR 128 + +#define KADDR(a) ((void*)((ulong)(a)|KZERO)) +#define PCIWINDOW 0 +#define PCIWADDR(va) (PADDR(va)+PCIWINDOW) + +#define iprint print + +/* buffers */ +#define allocb(sz) mballoc((sz), 0, Maeth1) +#define iallocb(sz) mballoc((sz), 0, Mbeth1) + +/* other memory */ +#define malloc(sz) ialloc((sz), 0) +#define xspanalloc(sz, align, span) ialloc((sz)+(align)+(span), (align)) +/* offset==0 in all uses in fs */ +#define mallocalign(sz, align, offset, span) \ + ialloc((sz)+(align)+(span), (align)) +/* sleazy hacks; really need better allocators */ +#define xalloc(sz) malloc(sz) +#define xfree(p) +#define smalloc(sz) malloc(sz) + +#define waserror() 0 +#define poperror() +#define nexterror() return +#define error(x) goto err + +#define qsetlimit(q, lim) +#define ioalloc(a, b, c, d) 0 +#define iofree(p) +#define strtol strtoul +#define PROCARG(arg) +#define GETARG(arg) getarg() + +#define vmap(bar, size) upamalloc(bar, size, 0) + +/* see portdat.h for Msgbuf flags */ +void freeb(Block *b); +void freeblist(Block *b); +void free(void *p); +void *mallocz(ulong sz, int clr); +char *strdup(char *); /* port/config.c */ +void kstrdup(char **p, char *s); + +/* header files mysteriously fail to declare this */ +ulong upamalloc(ulong addr, int size, int align); + +int readstr(vlong, void *, int, char *); +void addethercard(char *, int (*)(struct Ether *)); +void kproc(char *text, void (*f)(void), void *arg); + +/* pc-specific? */ +int intrdisable(int irq, void (*f)(Ureg *, void *), void *a, int, char *); +void intrenable(int irq, void (*f)(Ureg*, void*), void* a, int, char *name); diff -Nru /sys/src/fs/pc/dosfs.c /sys/src/fs/pc/dosfs.c --- /sys/src/fs/pc/dosfs.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/pc/dosfs.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,858 @@ +#include "all.h" +#include "io.h" +#include "mem.h" + +#include "dosfs.h" + +#define GSHORT(p) (((p)[1]<<8)|(p)[0]) +#define GLONG(p) ((GSHORT(p+2)<<16)|GSHORT(p)) +#define GLSHORT(p) (((p)[0]<<8)|(p)[1]) +#define GLLONG(p) ((GLSHORT(p)<<16)|GLSHORT(p+2)) + +/* + * debugging + */ +#define chatty 0 +#define chat if(chatty)print + +/* + * block io buffers + */ +typedef struct Clustbuf Clustbuf; + +struct Clustbuf +{ + int flags; + int age; + Devsize sector; + uchar * iobuf; + Dos * dos; + int size; + int bufsize; +}; + +enum +{ + Nbio= 16, + LOCKED= 1, + MOD= 2, + IMMED= 4, +}; + +static void puttime(Dosdir*); + +static Clustbuf bio[Nbio]; + +/* + * write an io buffer and update its flags + */ +static void +writeclust(Clustbuf *p) +{ + Dos *dos; + Off addr; + + dos = p->dos; + addr = (p->sector+dos->start)*dos->sectbytes; + chat("writeclust @ %lld addr %lld...", (Wideoff)p->sector, + (Wideoff)addr); + if((*dos->seek)(dos->dev, addr) < 0) + panic("writeclust: seek"); + if((*dos->write)(dos->dev, p->iobuf, p->size) != p->size) + panic("writeclust: write"); + p->flags &= ~(MOD|IMMED); + chat("OK\n"); +} + +/* + * write any dirty buffers + */ +static void +syncclust(void) +{ + Clustbuf *p; + + for(p = bio; p < &bio[Nbio]; p++){ + if(p->flags & LOCKED) + panic("syncclust"); + if(p->flags & MOD) + writeclust(p); + } +} + +/* + * get an io buffer, possibly with valid data + */ +static Clustbuf* +getclust0(Dos *dos, Off sector) +{ + Clustbuf *p, *oldest; + + chat("getclust0 @ %lld\n", (Wideoff)sector); + + /* + * if we have it, just return it + * otherwise, reuse the oldest unlocked entry + */ + oldest = 0; + for(p = bio; p < &bio[Nbio]; p++){ + if(sector == p->sector && dos == p->dos){ + if(p->flags & LOCKED) + panic("getclust0 locked"); + chat("getclust0 %lld in cache\n", (Wideoff)sector); + p->flags |= LOCKED; + return p; + } + if(p->flags & LOCKED) + continue; + if(oldest == 0 || p->age <= oldest->age) + oldest = p; + } + p = oldest; + if(p == 0) + panic("getclust0 all locked"); + p->flags |= LOCKED; + if(p->flags & MOD) + writeclust(p); + + /* + * make sure the buffer is big enough + */ + if(p->iobuf==0 || p->bufsize < dos->clustbytes){ + p->bufsize = dos->clustbytes; + p->iobuf = ialloc(p->bufsize, 0); + } + if(sector >= dos->dataaddr) + p->size = dos->clustbytes; + else + p->size = dos->sectbytes; + p->dos = 0; /* make it invalid */ + return p; +} + +/* + * get an io block from an io buffer + */ +static Clustbuf* +getclust(Dos *dos, Off sector) +{ + Clustbuf *p; + Off addr; + + p = getclust0(dos, sector); + if(p->dos){ + p->age = MACHP(0)->ticks; + return p; + } + addr = (sector+dos->start)*dos->sectbytes; + chat("getclust seek addr %lld\n", (Wideoff)addr); + if((*dos->seek)(dos->dev, addr) < 0){ + chat("can't seek block\n"); + return 0; + } + chat("getclust read addr %lld\n", (Wideoff)addr); + if((*dos->read)(dos->dev, p->iobuf, p->size) != p->size){ + chat("can't read block\n"); + return 0; + } + + p->age = MACHP(0)->ticks; + p->dos = dos; + p->sector = sector; + chat("getclust %lld read\n", (Wideoff)sector); + return p; +} + +/* + * get an io block from an io buffer; + * any current data is discarded. + */ +static Clustbuf* +getclustz(Dos *dos, Off sector) +{ + Clustbuf *p; + + p = getclust0(dos, sector); + p->age = MACHP(0)->ticks; + p->dos = dos; + p->sector = sector; + memset(p->iobuf, 0, p->size); + p->flags |= MOD; + chat("getclustz %lld\n", (Wideoff)sector); + return p; +} + +/* + * release an io buffer + */ +static void +putclust(Clustbuf *p) +{ + if(!(p->flags & LOCKED)) + panic("putclust lock"); + if((p->flags & (MOD|IMMED)) == (MOD|IMMED)) + writeclust(p); + p->flags &= ~LOCKED; + chat("putclust @ sector %lld...", (Wideoff)p->sector); +} + +/* + * walk the fat one level ( n is a current cluster number ). + * return the new cluster number or -1 if no more. + */ +static long +fatwalk(Dos *dos, int n) +{ + ulong k, sect; + Clustbuf *p; + int o; + + chat("fatwalk %d\n", n); + + if(n < 2 || n >= dos->fatclusters) + return -1; + + switch(dos->fatbits){ + case 12: + k = (3*n)/2; break; + case 16: + k = 2*n; break; + default: + return -1; + } + if(k >= dos->fatbytes) + panic("getfat"); + + sect = k/dos->sectbytes + dos->fataddr; + o = k%dos->sectbytes; + p = getclust(dos, sect); + k = p->iobuf[o++]; + if(o >= dos->sectbytes){ + putclust(p); + p = getclust(dos, sect+1); + o = 0; + } + k |= p->iobuf[o]<<8; + putclust(p); + if(dos->fatbits == 12){ + if(n&1) + k >>= 4; + else + k &= 0xfff; + if(k >= 0xff8) + k |= 0xf000; + } + k = k < 0xfff8 ? k : -1; + chat("fatwalk %d -> %lud\n", n, k); + return k; +} + +/* + * write a value into each copy of the fat. + */ +static void +fatwrite(Dos *dos, int n, int val) +{ + Off k, sect; + Clustbuf *p; + int i, o; + + chat("fatwrite %d %d...", n, val); + + if(n < 2 || n >= dos->fatclusters) + panic("fatwrite n"); + + switch(dos->fatbits){ + case 12: + k = (3*n)/2; break; + case 16: + k = 2*n; break; + default: + panic("fatwrite fatbits"); + return; + } + if(k >= dos->fatbytes) + panic("fatwrite k"); + + for(i=0; infats; i++, k+=dos->fatbytes){ + sect = k/dos->sectbytes + dos->fataddr; + o = k%dos->sectbytes; + p = getclust(dos, sect); + if(p == 0) + panic("fatwrite getclust"); + switch(dos->fatbits){ + case 12: + if(n&1){ + p->iobuf[o] &= 0x0f; + p->iobuf[o++] |= val<<4; + }else + p->iobuf[o++] = val; + if(o >= dos->sectbytes){ + p->flags |= MOD; + putclust(p); + p = getclust(dos, sect+1); + if(p == 0) + panic("fatwrite getclust"); + o = 0; + } + if(n&1) + p->iobuf[o] = val>>4; + else{ + p->iobuf[o] &= 0xf0; + p->iobuf[o] |= (val>>8)&0x0f; + } + break; + case 16: + p->iobuf[o++] = val; + p->iobuf[o] = val>>8; + break; + } + p->flags |= MOD; + putclust(p); + } + chat("OK\n"); +} + +/* + * allocate a free cluster from the fat. + */ +static int +fatalloc(Dos *dos) +{ + Clustbuf *p; + int n; + + n = dos->freeptr; + for(;;){ + if(fatwalk(dos, n) == 0) + break; + if(++n >= dos->fatclusters) + n = 2; + if(n == dos->freeptr) + return -1; + } + dos->freeptr = n+1; + if(dos->freeptr >= dos->fatclusters) + dos->freeptr = 2; + fatwrite(dos, n, 0xffff); + p = getclustz(dos, dos->dataaddr + (n-2)*dos->clustsize); + putclust(p); + return n; +} + +/* + * map a file's logical sector address to a physical sector address + */ +static long +fileaddr(Dosfile *fp, Off ltarget, Clustbuf *pdir) +{ + Dos *dos = fp->dos; + Dosdir *dp; + Off p; + + chat("fileaddr %8.8s %lld\n", fp->name, (Wideoff)ltarget); + /* + * root directory is contiguous and easy + */ + if(fp->pdir == 0){ + if(ltarget*dos->sectbytes >= dos->rootsize*sizeof(Dosdir)) + return -1; + p = dos->rootaddr + ltarget; + chat("fileaddr %lld -> %lld\n", (Wideoff)ltarget, (Wideoff)p); + return p; + } + if(fp->pstart == 0){ /* empty file */ + if(!pdir) + return -1; + p = fatalloc(dos); + if(p <= 0) + return -1; + chat("fileaddr initial alloc %lld\n", (Wideoff)p); + dp = (Dosdir *)(pdir->iobuf + fp->odir); + puttime(dp); + dp->start[0] = p; + dp->start[1] = p>>8; + pdir->flags |= MOD; + fp->pstart = p; + fp->pcurrent = p; + fp->lcurrent = 0; + } + /* + * anything else requires a walk through the fat + * [lp]current will point to the last cluster if we run off the end + */ + ltarget /= dos->clustsize; + if(fp->pcurrent == 0 || fp->lcurrent > ltarget){ + /* go back to the beginning */ + fp->lcurrent = 0; + fp->pcurrent = fp->pstart; + } + while(fp->lcurrent < ltarget){ + /* walk the fat */ + p = fatwalk(dos, fp->pcurrent); + if(p < 0){ + if(!pdir) + return -1; + p = fatalloc(dos); + if(p < 0){ + print("file system full\n"); + return -1; + } + fatwrite(dos, fp->pcurrent, p); + } + fp->pcurrent = p; + ++fp->lcurrent; + } + + /* + * clusters start at 2 instead of 0 (why? - presotto) + */ + p = dos->dataaddr + (fp->pcurrent-2)*dos->clustsize; + chat("fileaddr %lld -> %lld\n", (Wideoff)ltarget, (Wideoff)p); + return p; +} + +/* + * set up a dos file name + */ +static void +setname(char *name, char *ext, char *from) +{ + char *to; + + memset(name, ' ', 8); + memset(ext, ' ', 3); + + to = name; + for(; *from && to-name < 8; from++, to++){ + if(*from == '.'){ + from++; + break; + } + if(*from >= 'a' && *from <= 'z') + *to = *from + 'A' - 'a'; + else + *to = *from; + } + to = ext; + for(; *from && to-ext < 3; from++, to++){ + if(*from >= 'a' && *from <= 'z') + *to = *from + 'A' - 'a'; + else + *to = *from; + } + + chat("name is %8.8s %3.3s\n", name, ext); +} + +/* + * walk a directory returns + * -1 if something went wrong + * 0 if not found + * 1 if found + */ +static int +doswalk(Dosfile *fp, char *name) +{ + char dname[8], dext[3]; + Clustbuf *p; + Dosdir *dp; + Off o, addr; + + if((fp->attr & DOSDIR) == 0){ + chat("walking non-directory!\n"); + return -1; + } + + setname(dname, dext, name); + + fp->offset = 0; /* start at the beginning */ + for(;;){ + addr = fileaddr(fp, fp->offset/fp->dos->sectbytes, 0); + if(addr < 0) + return 0; + p = getclust(fp->dos, addr); + if(p == 0) + return -1; + for(o=0; osize; o += sizeof(Dosdir)){ + dp = (Dosdir *)(p->iobuf + o); + chat("comparing to %8.8s.%3.3s\n", (char*)dp->name, (char*)dp->ext); + if(memcmp(dname, dp->name, sizeof(dp->name)) != 0) + continue; + if(memcmp(dext, dp->ext, sizeof(dp->ext)) == 0) + goto Found; + } + fp->offset += p->size; + putclust(p); + } + +Found: + fp->pdir = p->sector; + fp->odir = o; + putclust(p); + memmove(fp->name, dname, sizeof(fp->name)); + memmove(fp->ext, dext, sizeof(fp->ext)); + fp->attr = dp->attr; + fp->length = GLONG(dp->length); + fp->pstart = GSHORT(dp->start); + fp->pcurrent = 0; + fp->lcurrent = 0; + fp->offset = 0; + return 1; +} + +static void +bootdump(Dosboot *b) +{ + if(chatty == 0) + return; + print("magic: 0x%2.2x 0x%2.2x 0x%2.2x\n", + b->magic[0], b->magic[1], b->magic[2]); + print("version: \"%8.8s\"\n", (char*)b->version); + print("sectbytes: %d\n", GSHORT(b->sectbytes)); + print("allocsize: %d\n", b->clustsize); + print("nresrv: %d\n", GSHORT(b->nresrv)); + print("nfats: %d\n", b->nfats); + print("rootsize: %d\n", GSHORT(b->rootsize)); + print("volsize: %d\n", GSHORT(b->volsize)); + print("mediadesc: 0x%2.2x\n", b->mediadesc); + print("fatsize: %d\n", GSHORT(b->fatsize)); + print("trksize: %d\n", GSHORT(b->trksize)); + print("nheads: %d\n", GSHORT(b->nheads)); + print("nhidden: %d\n", GLONG(b->nhidden)); + print("bigvolsize: %d\n", GLONG(b->bigvolsize)); + print("driveno: %d\n", b->driveno); + print("reserved0: 0x%2.2x\n", b->reserved0); + print("bootsig: 0x%2.2x\n", b->bootsig); + print("volid: 0x%8.8x\n", GLONG(b->volid)); + print("label: \"%11.11s\"\n", (char*)b->label); +} + +/* + * instructions that boot blocks can start with + */ +#define JMPSHORT 0xeb +#define JMPNEAR 0xe9 + +/* + * read dos file system properties + */ +int +dosinit(Dos *dos) +{ + Clustbuf *p; + Dospart *dp; + Dosboot *b; + int i; + + + /* defaults till we know better */ + dos->start = 0; + dos->sectbytes = 512; + dos->clustsize = 1; + dos->clustbytes = 512; + + /* get first sector */ + p = getclust(dos, 0); + if(p == 0){ + chat("can't read boot block\n"); + return -1; + } + p->dos = 0; + + /* if a hard disk format, look for an active partition */ + b = (Dosboot *)p->iobuf; + if(b->magic[0] != JMPNEAR && (b->magic[0] != JMPSHORT || b->magic[2] != 0x90)){ + if(p->iobuf[0x1fe] != 0x55 || p->iobuf[0x1ff] != 0xaa){ + print("no dos file system or partition table\n"); + putclust(p); + return -1; + } + dp = (Dospart*)&p->iobuf[0x1be]; + for(i = 0; i < 4; i++, dp++) + if(dp->type && dp->flag == 0x80) + break; + if(i == 4){ + putclust(p); + return -1; + } + dos->start = GLONG(dp->start); + putclust(p); + p = getclust(dos, 0); + if(p == 0){ + chat("can't read boot block\n"); + putclust(p); + return -1; + } + p->dos = 0; + } + + b = (Dosboot *)p->iobuf; + if(b->magic[0] != JMPNEAR && (b->magic[0] != JMPSHORT || b->magic[2] != 0x90)){ + print("no dos file system\n"); + putclust(p); + return -1; + } + + if(chatty) + bootdump(b);/**/ + + /* + * determine the systems' wondersous properties + */ + dos->sectbytes = GSHORT(b->sectbytes); + dos->clustsize = b->clustsize; + dos->clustbytes = dos->sectbytes*dos->clustsize; + dos->nresrv = GSHORT(b->nresrv); + dos->nfats = b->nfats; + dos->rootsize = GSHORT(b->rootsize); + dos->volsize = GSHORT(b->volsize); + if(dos->volsize == 0) + dos->volsize = GLONG(b->bigvolsize); + dos->mediadesc = b->mediadesc; + dos->fatsize = GSHORT(b->fatsize); + dos->fatbytes = dos->sectbytes*dos->fatsize; + dos->fataddr = dos->nresrv; + dos->rootaddr = dos->fataddr + dos->nfats*dos->fatsize; + i = dos->rootsize*sizeof(Dosdir) + dos->sectbytes - 1; + i = i/dos->sectbytes; + dos->dataaddr = dos->rootaddr + i; + dos->fatclusters = 2+(dos->volsize - dos->dataaddr)/dos->clustsize; + if(dos->fatclusters < 4087) + dos->fatbits = 12; + else + dos->fatbits = 16; + dos->freeptr = 2; + putclust(p); + + /* + * set up the root + */ + dos->root.dos = dos; + dos->root.pdir = 0; + dos->root.odir = 0; + memmove(dos->root.name, " ", 8); + memmove(dos->root.ext, " ", 3); + dos->root.attr = DOSDIR; + dos->root.length = dos->rootsize*sizeof(Dosdir); + dos->root.pstart = 0; + dos->root.lcurrent = 0; + dos->root.pcurrent = 0; + dos->root.offset = 0; + + syncclust(); + return 0; +} + +static char * +nextelem(char *path, char *elem) +{ + int i; + + while(*path == '/') + path++; + if(*path==0 || *path==' ') + return 0; + for(i=0; *path && *path != '/' && *path != ' '; i++){ + if(i >= NAMELEN){ + print("name component too long\n"); + return 0; + } + *elem++ = *path++; + } + *elem = 0; + return path; +} + +static void +puttime(Dosdir *d) +{ + Timet secs; + Rtc rtc; + ushort x; + + secs = rtctime(); + sec2rtc(secs, &rtc); + x = (rtc.hour<<11) | (rtc.min<<5) | (rtc.sec>>1); + d->time[0] = x; + d->time[1] = x>>8; + x = ((rtc.year-80)<<9) | ((rtc.mon+1)<<5) | rtc.mday; + d->date[0] = x; + d->date[1] = x>>8; +} + +Dosfile* +dosopen(Dos *dos, char *path, Dosfile *fp) +{ + char element[NAMELEN]; + + *fp = dos->root; + while(path = nextelem(path, element)){ + switch(doswalk(fp, element)){ + case -1: + print("error walking to %s\n", element); + return 0; + case 0: + print("%s not found\n", element); + return 0; + case 1: + print("found %s attr 0x%ux start 0x%llux len %lld\n", + element, fp->attr, (Wideoff)fp->pstart, + (Wideoff)fp->length); + break; + } + } + + syncclust(); + return fp; +} + +/* + * read from a dos file + */ +long +dosread(Dosfile *fp, void *a, long n) +{ + Off addr, k, o; + Clustbuf *p; + uchar *to; + + if((fp->attr & DOSDIR) == 0){ + if(fp->offset >= fp->length) + return 0; + if(fp->offset+n > fp->length) + n = fp->length - fp->offset; + } + to = a; + while(n > 0){ + /* + * read the data; sectors below dos->dataaddr + * are read one at a time. + */ + addr = fileaddr(fp, fp->offset/fp->dos->sectbytes, 0); + if(addr < 0) + return -1; + p = getclust(fp->dos, addr); + if(p == 0) + return -1; + /* + * copy the bytes we need + */ + o = fp->offset % p->size; + k = p->size - o; + if(k > n) + k = n; + memmove(to, p->iobuf+o, k); + putclust(p); + to += k; + fp->offset += k; + n -= k; + } + syncclust(); + return to - (uchar *)a; +} + +/* + * write to a dos file + */ +long +doswrite(Dosfile *fp, void *a, long n) +{ + Off blksize, addr, k, o; + Clustbuf *p, *pdir; + Dosdir *dp; + uchar *from; + + if(fp->attr & DOSDIR){ + print("write dir\n"); + return -1; + } + if(fp->pdir){ + pdir = getclust(fp->dos, fp->pdir); + /* + * should do consistency check if + * concurrent access is possible. + */ + if(pdir == 0) + panic("doswrite"); + }else + pdir = 0; + blksize = pdir ? fp->dos->clustbytes : fp->dos->sectbytes; + from = a; + while(n > 0){ + addr = fileaddr(fp, fp->offset/fp->dos->sectbytes, pdir); + if(addr < 0) + return -1; + o = fp->offset % blksize; + if(o == 0 && n >= blksize) + p = getclustz(fp->dos, addr); + else + p = getclust(fp->dos, addr); + if(p == 0) + return -1; + /* + * copy the bytes we need + */ + k = p->size - o; + if(k > n) + k = n; + memmove(p->iobuf+o, from, k); + p->flags |= MOD; + putclust(p); + from += k; + fp->offset += k; + n -= k; + } + if(pdir){ + dp = (Dosdir *)(pdir->iobuf + fp->odir); + puttime(dp); + if(fp->offset > fp->length){ + fp->length = fp->offset; + dp->length[0] = fp->length; + dp->length[1] = fp->length>>8; + dp->length[2] = fp->length>>16; + dp->length[3] = fp->length>>24; + } + pdir->flags |= MOD; + putclust(pdir); + } + syncclust(); + return from - (uchar *)a; +} + +/* + * truncate a dos file to zero length + */ +int +dostrunc(Dosfile *fp) +{ + Clustbuf *pdir; + Dosdir *dp; + Off p, np; + + if(fp->attr & DOSDIR){ + print("trunc dir\n"); + return -1; + } + pdir = getclust(fp->dos, fp->pdir); + if(pdir == 0) + panic("dostrunc"); + p = fatwalk(fp->dos, fp->pstart); + fatwrite(fp->dos, fp->pstart, 0xffff); + while(p >= 0){ + np = fatwalk(fp->dos, p); + fatwrite(fp->dos, p, 0); + p = np; + } + fp->length = 0; + dp = (Dosdir *)(pdir->iobuf + fp->odir); + puttime(dp); + dp->length[0] = 0; + dp->length[1] = 0; + dp->length[2] = 0; + dp->length[3] = 0; + pdir->flags |= MOD; + putclust(pdir); + syncclust(); + return 0; +} diff -Nru /sys/src/fs/pc/dosfs.h /sys/src/fs/pc/dosfs.h --- /sys/src/fs/pc/dosfs.h Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/pc/dosfs.h Tue Nov 1 00:00:00 2011 @@ -0,0 +1,107 @@ +typedef struct Dosboot Dosboot; +typedef struct Dos Dos; +typedef struct Dosdir Dosdir; +typedef struct Dosfile Dosfile; +typedef struct Dospart Dospart; + +struct Dospart +{ + uchar flag; /* active flag */ + uchar shead; /* starting head */ + uchar scs[2]; /* starting cylinder/sector */ + uchar type; /* partition type */ + uchar ehead; /* ending head */ + uchar ecs[2]; /* ending cylinder/sector */ + uchar start[4]; /* starting sector */ + uchar len[4]; /* length in sectors */ +}; + +struct Dosboot{ + uchar magic[3]; + uchar version[8]; + uchar sectbytes[2]; + uchar clustsize; + uchar nresrv[2]; + uchar nfats; + uchar rootsize[2]; + uchar volsize[2]; + uchar mediadesc; + uchar fatsize[2]; + uchar trksize[2]; + uchar nheads[2]; + uchar nhidden[4]; + uchar bigvolsize[4]; + uchar driveno; + uchar reserved0; + uchar bootsig; + uchar volid[4]; + uchar label[11]; + uchar reserved1[8]; +}; + +struct Dosfile{ + Dos * dos; /* owning dos file system */ + int pdir; /* sector containing directory entry */ + int odir; /* offset to same */ + char name[8]; + char ext[3]; + uchar attr; + Devsize length; + Devsize pstart; /* physical start cluster address */ + Devsize pcurrent; /* physical current cluster address */ + Devsize lcurrent; /* logical current cluster address */ + Devsize offset; +}; + +struct Dos{ + int dev; /* device id */ + Off (*read)(int, void*, long); /* read routine */ + Devsize (*seek)(int, Devsize); /* seek routine */ + Off (*write)(int, void*, long); /* write routine */ + + int start; /* start of file system (sector no.) */ + int sectbytes; /* size of a sector */ + int clustsize; /* size of a cluster (in sectors) */ + int clustbytes; /* size of a cluster (in bytes) */ + int nresrv; /* sectors */ + int nfats; /* usually 2 */ + int rootsize; /* number of entries */ + int volsize; /* in sectors */ + int mediadesc; + int fatsize; /* size of a fat (in sectors) */ + int fatbytes; /* size of a fat (in bytes) */ + int fatclusters; /* no. of clusters governed by fat */ + int fatbits; /* 12 or 16 */ + Devsize fataddr; /* sector address of first fat */ + Devsize rootaddr; /* sector address of root directory */ + Devsize dataaddr; /* sector address of first data block */ + Devsize freeptr; /* for cluster allocation */ + + Dosfile root; +}; + +struct Dosdir{ + uchar name[8]; + uchar ext[3]; + uchar attr; + uchar reserved[10]; + uchar time[2]; + uchar date[2]; + uchar start[2]; + uchar length[4]; +}; + +#define DRONLY 0x01 +#define DHIDDEN 0x02 +#define DSYSTEM 0x04 +#define DVLABEL 0x08 +#define DOSDIR 0x10 +#define DARCH 0x20 + +extern int dosinit(Dos*); +extern Dosfile* dosopen(Dos*, char*, Dosfile*); +extern int dostrunc(Dosfile*); +extern long dosread(Dosfile*, void*, long); +extern long doswrite(Dosfile*, void*, long); + +extern Dos dos; diff -Nru /sys/src/fs/pc/ether2114x.c /sys/src/fs/pc/ether2114x.c --- /sys/src/fs/pc/ether2114x.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/pc/ether2114x.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,1426 @@ +/* + * Digital Semiconductor DECchip 2114x PCI Fast Ethernet LAN Controller. + * To do: + * thresholds; + * ring sizing; + * handle more error conditions; + * tidy setup packet mess; + * push initialisation back to attach; + * full SROM decoding. + */ +#include "all.h" +#include "io.h" +#include "mem.h" + +#include "../ip/ip.h" +#include "etherif.h" + +#define DEBUG (0) +#define debug if(DEBUG)print + +enum { + Nrde = 64, + Ntde = 64, +}; + +#define Rbsz ROUNDUP(sizeof(Enpkt)+4, 4) + +enum { /* CRS0 - Bus Mode */ + Swr = 0x00000001, /* Software Reset */ + Bar = 0x00000002, /* Bus Arbitration */ + Dsl = 0x0000007C, /* Descriptor Skip Length (field) */ + Ble = 0x00000080, /* Big/Little Endian */ + Pbl = 0x00003F00, /* Programmable Burst Length (field) */ + Cal = 0x0000C000, /* Cache Alignment (field) */ + Cal8 = 0x00004000, /* 8 longword boundary alignment */ + Cal16 = 0x00008000, /* 16 longword boundary alignment */ + Cal32 = 0x0000C000, /* 32 longword boundary alignment */ + Tap = 0x000E0000, /* Transmit Automatic Polling (field) */ + Dbo = 0x00100000, /* Descriptor Byte Ordering Mode */ + Rml = 0x00200000, /* Read Multiple */ +}; + +enum { /* CSR[57] - Status and Interrupt Enable */ + Ti = 0x00000001, /* Transmit Interrupt */ + Tps = 0x00000002, /* Transmit Process Stopped */ + Tu = 0x00000004, /* Transmit buffer Unavailable */ + Tjt = 0x00000008, /* Transmit Jabber Timeout */ + Unf = 0x00000020, /* transmit UNderFlow */ + Ri = 0x00000040, /* Receive Interrupt */ + Ru = 0x00000080, /* Receive buffer Unavailable */ + Rps = 0x00000100, /* Receive Process Stopped */ + Rwt = 0x00000200, /* Receive Watchdog Timeout */ + Eti = 0x00000400, /* Early Transmit Interrupt */ + Gte = 0x00000800, /* General purpose Timer Expired */ + Fbe = 0x00002000, /* Fatal Bit Error */ + Ais = 0x00008000, /* Abnormal Interrupt Summary */ + Nis = 0x00010000, /* Normal Interrupt Summary */ + Rs = 0x000E0000, /* Receive process State (field) */ + Ts = 0x00700000, /* Transmit process State (field) */ + Eb = 0x03800000, /* Error bits */ +}; + +enum { /* CSR6 - Operating Mode */ + Hp = 0x00000001, /* Hash/Perfect receive filtering mode */ + Sr = 0x00000002, /* Start/stop Receive */ + Ho = 0x00000004, /* Hash-Only filtering mode */ + Pb = 0x00000008, /* Pass Bad frames */ + If = 0x00000010, /* Inverse Filtering */ + Sb = 0x00000020, /* Start/stop Backoff counter */ + Pr = 0x00000040, /* Promiscuous Mode */ + Pm = 0x00000080, /* Pass all Multicast */ + Fd = 0x00000200, /* Full Duplex mode */ + Om = 0x00000C00, /* Operating Mode (field) */ + Fc = 0x00001000, /* Force Collision */ + St = 0x00002000, /* Start/stop Transmission Command */ + Tr = 0x0000C000, /* ThReshold control bits (field) */ + Tr128 = 0x00000000, + Tr256 = 0x00004000, + Tr512 = 0x00008000, + Tr1024 = 0x0000C000, + Ca = 0x00020000, /* CApture effect enable */ + Ps = 0x00040000, /* Port Select */ + Hbd = 0x00080000, /* HeartBeat Disable */ + Imm = 0x00100000, /* IMMediate mode */ + Sf = 0x00200000, /* Store and Forward */ + Ttm = 0x00400000, /* Transmit Threshold Mode */ + Pcs = 0x00800000, /* PCS function */ + Scr = 0x01000000, /* SCRambler mode */ + Mbo = 0x02000000, /* Must Be One */ + Ra = 0x40000000, /* Receive All */ + Sc = 0x80000000, /* Special Capture effect enable */ + + TrMODE = Tr512, /* default transmission threshold */ +}; + +enum { /* CSR9 - ROM and MII Management */ + Scs = 0x00000001, /* serial ROM chip select */ + Sclk = 0x00000002, /* serial ROM clock */ + Sdi = 0x00000004, /* serial ROM data in */ + Sdo = 0x00000008, /* serial ROM data out */ + Ss = 0x00000800, /* serial ROM select */ + Wr = 0x00002000, /* write */ + Rd = 0x00004000, /* read */ + + Mdc = 0x00010000, /* MII management clock */ + Mdo = 0x00020000, /* MII management write data */ + Mii = 0x00040000, /* MII management operation mode (W) */ + Mdi = 0x00080000, /* MII management data in */ +}; + +enum { /* CSR12 - General-Purpose Port */ + Gpc = 0x00000100, /* General Purpose Control */ +}; + +typedef struct Des { + int status; + int control; + ulong addr; + Msgbuf* mb; +} Des; + +enum { /* status */ + Of = 0x00000001, /* Rx: OverFlow */ + Ce = 0x00000002, /* Rx: CRC Error */ + Db = 0x00000004, /* Rx: Dribbling Bit */ + Re = 0x00000008, /* Rx: Report on MII Error */ + Rw = 0x00000010, /* Rx: Receive Watchdog */ + Ft = 0x00000020, /* Rx: Frame Type */ + Cs = 0x00000040, /* Rx: Collision Seen */ + Tl = 0x00000080, /* Rx: Frame too Long */ + Ls = 0x00000100, /* Rx: Last deScriptor */ + Fs = 0x00000200, /* Rx: First deScriptor */ + Mf = 0x00000400, /* Rx: Multicast Frame */ + Rf = 0x00000800, /* Rx: Runt Frame */ + Dt = 0x00003000, /* Rx: Data Type (field) */ + De = 0x00004000, /* Rx: Descriptor Error */ + Fl = 0x3FFF0000, /* Rx: Frame Length (field) */ + Ff = 0x40000000, /* Rx: Filtering Fail */ + + Def = 0x00000001, /* Tx: DEFerred */ + Uf = 0x00000002, /* Tx: UnderFlow error */ + Lf = 0x00000004, /* Tx: Link Fail report */ + Cc = 0x00000078, /* Tx: Collision Count (field) */ + Hf = 0x00000080, /* Tx: Heartbeat Fail */ + Ec = 0x00000100, /* Tx: Excessive Collisions */ + Lc = 0x00000200, /* Tx: Late Collision */ + Nc = 0x00000400, /* Tx: No Carrier */ + Lo = 0x00000800, /* Tx: LOss of carrier */ + To = 0x00004000, /* Tx: Transmission jabber timeOut */ + + Es = 0x00008000, /* [RT]x: Error Summary */ + Own = 0x80000000, /* [RT]x: OWN bit */ +}; + +enum { /* control */ + Bs1 = 0x000007FF, /* [RT]x: Buffer 1 Size */ + Bs2 = 0x003FF800, /* [RT]x: Buffer 2 Size */ + + Ch = 0x01000000, /* [RT]x: second address CHained */ + Er = 0x02000000, /* [RT]x: End of Ring */ + + Ft0 = 0x00400000, /* Tx: Filtering Type 0 */ + Dpd = 0x00800000, /* Tx: Disabled PaDding */ + Ac = 0x04000000, /* Tx: Add CRC disable */ + Set = 0x08000000, /* Tx: SETup packet */ + Ft1 = 0x10000000, /* Tx: Filtering Type 1 */ + Fseg = 0x20000000, /* Tx: First SEGment */ + Lseg = 0x40000000, /* Tx: Last SEGment */ + Ic = 0x80000000, /* Tx: Interrupt on Completion */ +}; + +enum { /* PHY registers */ + Bmcr = 0, /* Basic Mode Control */ + Bmsr = 1, /* Basic Mode Status */ + Phyidr1 = 2, /* PHY Identifier #1 */ + Phyidr2 = 3, /* PHY Identifier #2 */ + Anar = 4, /* Auto-Negotiation Advertisment */ + Anlpar = 5, /* Auto-Negotiation Link Partner Ability */ + Aner = 6, /* Auto-Negotiation Expansion */ +}; + +enum { /* Variants */ + Tulip0 = (0x0009<<16)|0x1011, + Tulip3 = (0x0019<<16)|0x1011, + Pnic = (0x0002<<16)|0x11AD, + Pnic2 = (0xC115<<16)|0x11AD, +}; + +typedef struct Ctlr Ctlr; +typedef struct Ctlr { + int port; + Pcidev* pcidev; + Ctlr* next; + int active; + int id; /* (pcidev->did<<16)|pcidev->vid */ + + uchar srom[128]; + uchar* sromea; /* MAC address */ + uchar* leaf; + int sct; /* selected connection type */ + int k; /* info block count */ + uchar* infoblock[16]; + int sctk; /* sct block index */ + int curk; /* current block index */ + uchar* type5block; + + int phy[32]; /* logical to physical map */ + int phyreset; /* reset bitmap */ + int curphyad; + int fdx; + int ttm; + + uchar fd; /* option */ + int medium; /* option */ + + int csr6; /* CSR6 - operating mode */ + int mask; /* CSR[57] - interrupt mask */ + int mbps; + + Lock lock; + + Des* rdr; /* receive descriptor ring */ + int nrdr; /* size of rdr */ + int rdrx; /* index into rdr */ + + Lock tlock; + Des* tdr; /* transmit descriptor ring */ + int ntdr; /* size of tdr */ + int tdrh; /* host index into tdr */ + int tdri; /* interface index into tdr */ + int ntq; /* descriptors active */ + int ntqmax; + Msgbuf* setupmb; + + ulong of; /* receive statistics */ + ulong ce; + ulong cs; + ulong tl; + ulong rf; + ulong de; + + ulong ru; + ulong rps; + ulong rwt; + + ulong uf; /* transmit statistics */ + ulong ec; + ulong lc; + ulong nc; + ulong lo; + ulong to; + + ulong tps; + ulong tu; + ulong tjt; + ulong unf; +} Ctlr; + +static Ctlr* ctlrhead; +static Ctlr* ctlrtail; + +#define csr32r(c, r) (inl((c)->port+((r)*8))) +#define csr32w(c, r, l) (outl((c)->port+((r)*8), (ulong)(l))) + +static void +attach(Ether* ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(&ctlr->lock); + if(!(ctlr->csr6 & Sr)){ + ctlr->csr6 |= Sr; + csr32w(ctlr, 6, ctlr->csr6); + } + iunlock(&ctlr->lock); +} + +static void +txstart(Ether* ether) +{ + Ctlr *ctlr; + Msgbuf *mb; + Des *des; + int control; + + ctlr = ether->ctlr; + while(ctlr->ntq < (ctlr->ntdr-1)){ + if(ctlr->setupmb){ + mb = ctlr->setupmb; + ctlr->setupmb = 0; + control = Ic|Set|mb->count; + } + else{ + mb = etheroq(ether); + if(mb == nil) + break; + control = Ic|Lseg|Fseg|mb->count; + } + + ctlr->tdr[PREV(ctlr->tdrh, ctlr->ntdr)].control &= ~Ic; + des = &ctlr->tdr[ctlr->tdrh]; + des->mb = mb; + des->addr = PADDR(mb->data); + des->control |= control; + ctlr->ntq++; + coherence(); + des->status = Own; + csr32w(ctlr, 1, 0); + ctlr->tdrh = NEXT(ctlr->tdrh, ctlr->ntdr); + } + + if(ctlr->ntq > ctlr->ntqmax) + ctlr->ntqmax = ctlr->ntq; +} + +static void +transmit(Ether* ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(&ctlr->tlock); + txstart(ether); + iunlock(&ctlr->tlock); +} + +static void +interrupt(Ureg*, void* arg) +{ + Ctlr *ctlr; + Ether *ether; + int len, status; + Des *des; + Msgbuf *mb, *rmb; + + ether = arg; + ctlr = ether->ctlr; + + while((status = csr32r(ctlr, 5)) & (Nis|Ais)){ + /* + * Acknowledge the interrupts and mask-out + * the ones that are implicitly handled. + */ + csr32w(ctlr, 5, status); + status &= (ctlr->mask & ~(Nis|Ti)); + + if(status & Ais){ + if(status & Tps) + ctlr->tps++; + if(status & Tu) + ctlr->tu++; + if(status & Tjt) + ctlr->tjt++; + if(status & Ru) + ctlr->ru++; + if(status & Rps) + ctlr->rps++; + if(status & Rwt) + ctlr->rwt++; + status &= ~(Ais|Rwt|Rps|Ru|Tjt|Tu|Tps); + } + + /* + * Received packets. + */ + if(status & Ri){ + des = &ctlr->rdr[ctlr->rdrx]; + while(!(des->status & Own)){ + if(des->status & Es){ + if(des->status & Of) + ctlr->of++; + if(des->status & Ce) + ctlr->ce++; + if(des->status & Cs) + ctlr->cs++; + if(des->status & Tl) + ctlr->tl++; + if(des->status & Rf) + ctlr->rf++; + if(des->status & De) + ctlr->de++; + } + else if(mb = mballoc(Rbsz, 0, Mbeth1)){ + rmb = des->mb; + rmb->count = ((des->status & Fl)>>16)-4; + etheriq(ether, rmb); + des->mb = mb; + des->addr = PADDR(mb->data); + } + + des->control &= Er; + des->control |= Rbsz; + coherence(); + des->status = Own; + + ctlr->rdrx = NEXT(ctlr->rdrx, ctlr->nrdr); + des = &ctlr->rdr[ctlr->rdrx]; + } + status &= ~Ri; + } + + /* + * Check the transmit side: + * check for Transmit Underflow and Adjust + * the threshold upwards; + * free any transmitted buffers and try to + * top-up the ring. + */ + if(status & Unf){ + ctlr->unf++; + ilock(&ctlr->lock); + csr32w(ctlr, 6, ctlr->csr6 & ~St); + switch(ctlr->csr6 & Tr){ + case Tr128: + len = Tr256; + break; + case Tr256: + len = Tr512; + break; + case Tr512: + len = Tr1024; + break; + default: + case Tr1024: + len = Sf; + break; + } + ctlr->csr6 = (ctlr->csr6 & ~Tr)|len; + csr32w(ctlr, 6, ctlr->csr6); + iunlock(&ctlr->lock); + csr32w(ctlr, 5, Tps); + status &= ~(Unf|Tps); + } + + ilock(&ctlr->tlock); + while(ctlr->ntq){ + des = &ctlr->tdr[ctlr->tdri]; + if(des->status & Own) + break; + + if(des->status & Es){ + if(des->status & Uf) + ctlr->uf++; + if(des->status & Ec) + ctlr->ec++; + if(des->status & Lc) + ctlr->lc++; + if(des->status & Nc) + ctlr->nc++; + if(des->status & Lo) + ctlr->lo++; + if(des->status & To) + ctlr->to++; + } + + mbfree(des->mb); + des->control &= Er; + + ctlr->ntq--; + ctlr->tdri = NEXT(ctlr->tdri, ctlr->ntdr); + } + txstart(ether); + iunlock(&ctlr->tlock); + + /* + * Anything left not catered for? + */ + if(status) + panic("#l%d: status %8.8uX\n", ether->ctlrno, status); + } +} + +static void +ctlrinit(Ether* ether) +{ + Ctlr *ctlr; + Des *des; + Msgbuf *mb; + int i; + uchar bi[Easize*2]; + + ctlr = ether->ctlr; + + /* + * Allocate and initialise the receive ring; + * allocate and initialise the transmit ring; + * unmask interrupts and start the transmit side; + * create and post a setup packet to initialise + * the physical ethernet address. + */ + ctlr->rdr = ialloc(ctlr->nrdr*sizeof(Des), 8*sizeof(ulong)); + for(des = ctlr->rdr; des < &ctlr->rdr[ctlr->nrdr]; des++){ + des->mb = mballoc(Rbsz, 0, Mbeth2); + des->status = Own; + des->control = Rbsz; + des->addr = PADDR(des->mb->data); + } + ctlr->rdr[ctlr->nrdr-1].control |= Er; + ctlr->rdrx = 0; + csr32w(ctlr, 3, PADDR(ctlr->rdr)); + + ctlr->tdr = ialloc(ctlr->ntdr*sizeof(Des), 8*sizeof(ulong)); + ctlr->tdr[ctlr->ntdr-1].control |= Er; + ctlr->tdrh = 0; + ctlr->tdri = 0; + csr32w(ctlr, 4, PADDR(ctlr->tdr)); + + /* + * Clear any bits in the Status Register (CSR5) as + * the PNIC has a different reset value from a true 2114x. + */ + ctlr->mask = Nis|Ais|Fbe|Rwt|Rps|Ru|Ri|Unf|Tjt|Tps|Ti; + csr32w(ctlr, 5, ctlr->mask); + csr32w(ctlr, 7, ctlr->mask); + ctlr->csr6 |= St; + csr32w(ctlr, 6, ctlr->csr6); + + for(i = 0; i < Easize/2; i++){ + bi[i*4] = ether->ea[i*2]; + bi[i*4+1] = ether->ea[i*2+1]; + bi[i*4+2] = ether->ea[i*2+1]; + bi[i*4+3] = ether->ea[i*2]; + } + mb = mballoc(Easize*2*16, 0, Mbeth3); + memset(mb->data, 0xFF, sizeof(bi)); + for(i = sizeof(bi); i < sizeof(bi)*16; i += sizeof(bi)) + memmove(mb->data+i, bi, sizeof(bi)); + mb->count = sizeof(bi)*16; + + ctlr->setupmb = mb; + transmit(ether); +} + +static void +csr9w(Ctlr* ctlr, int data) +{ + csr32w(ctlr, 9, data); + microdelay(1); +} + +static int +miimdi(Ctlr* ctlr, int n) +{ + int data, i; + + /* + * Read n bits from the MII Management Register. + */ + data = 0; + for(i = n-1; i >= 0; i--){ + if(csr32r(ctlr, 9) & Mdi) + data |= (1<= 0; i--){ + if(bits & (1<id == Pnic){ + i = 1000; + csr32w(ctlr, 20, 0x60020000|(phyad<<23)|(regad<<18)); + do{ + microdelay(1); + data = csr32r(ctlr, 20); + }while((data & 0x80000000) && --i); + + if(i == 0) + return -1; + return data & 0xFFFF; + } + + /* + * Preamble; + * ST+OP+PHYAD+REGAD; + * TA + 16 data bits. + */ + miimdo(ctlr, 0xFFFFFFFF, 32); + miimdo(ctlr, 0x1800|(phyad<<5)|regad, 14); + data = miimdi(ctlr, 18); + + if(data & 0x10000) + return -1; + + return data & 0xFFFF; +} + +static void +miiw(Ctlr* ctlr, int phyad, int regad, int data) +{ + /* + * Preamble; + * ST+OP+PHYAD+REGAD+TA + 16 data bits; + * Z. + */ + miimdo(ctlr, 0xFFFFFFFF, 32); + data &= 0xFFFF; + data |= (0x05<<(5+5+2+16))|(phyad<<(5+2+16))|(regad<<(2+16))|(0x02<<16); + miimdo(ctlr, data, 32); + csr9w(ctlr, Mdc); + csr9w(ctlr, 0); +} + +static int +sromr(Ctlr* ctlr, int r) +{ + int i, op, data; + + if(ctlr->id == Pnic){ + i = 1000; + csr32w(ctlr, 19, 0x600|r); + do{ + microdelay(1); + data = csr32r(ctlr, 19); + }while((data & 0x80000000) && --i); + + return csr32r(ctlr, 9) & 0xFFFF; + } + + /* + * This sequence for reading a 16-bit register 'r' + * in the EEPROM is taken straight from Section + * 7.4 of the 21140 Hardware Reference Manual. + */ + csr9w(ctlr, Rd|Ss); + csr9w(ctlr, Rd|Ss|Scs); + csr9w(ctlr, Rd|Ss|Sclk|Scs); + csr9w(ctlr, Rd|Ss); + + op = 0x06; + for(i = 3-1; i >= 0; i--){ + data = Rd|Ss|(((op>>i) & 0x01)<<2)|Scs; + csr9w(ctlr, data); + csr9w(ctlr, data|Sclk); + csr9w(ctlr, data); + } + + for(i = 6-1; i >= 0; i--){ + data = Rd|Ss|(((r>>i) & 0x01)<<2)|Scs; + csr9w(ctlr, data); + csr9w(ctlr, data|Sclk); + csr9w(ctlr, data); + } + + data = 0; + for(i = 16-1; i >= 0; i--){ + csr9w(ctlr, Rd|Ss|Sclk|Scs); + if(csr32r(ctlr, 9) & Sdo) + data |= (1<= 50 PCI cycles (2×S @ 25MHz). + */ + csr32w(ctlr, 0, Swr); + microdelay(10); + csr32w(ctlr, 0, Rml|Cal16); + delay(1); +} + +static int +type5block(Ctlr* ctlr, uchar* block) +{ + int csr15, i, len; + + /* + * Reset or GPR sequence. Reset should be once only, + * before the GPR sequence. + * Note 'block' is not a pointer to the block head but + * a pointer to the data in the block starting at the + * reset length value so type5block can be used for the + * sequences contained in type 1 and type 3 blocks. + * The SROM docs state the 21140 type 5 block is the + * same as that for the 21143, but the two controllers + * use different registers and sequence-element lengths + * so the 21140 code here is a guess for a real type 5 + * sequence. + */ + len = *block++; + if(ctlr->id != Tulip3){ + for(i = 0; i < len; i++){ + csr32w(ctlr, 12, *block); + block++; + } + return len; + } + + for(i = 0; i < len; i++){ + csr15 = *block++<<16; + csr15 |= *block++<<24; + csr32w(ctlr, 15, csr15); + debug("%8.8uX ", csr15); + } + return 2*len; +} + +static int +typephylink(Ctlr* ctlr, uchar*) +{ + int an, bmcr, bmsr, csr6, x; + + /* + * Fail if + * auto-negotiataion enabled but not complete; + * no valid link established. + */ + bmcr = miir(ctlr, ctlr->curphyad, Bmcr); + miir(ctlr, ctlr->curphyad, Bmsr); + bmsr = miir(ctlr, ctlr->curphyad, Bmsr); + debug("bmcr 0x%2.2uX bmsr 0x%2.2uX\n", bmcr, bmsr); + if(((bmcr & 0x1000) && !(bmsr & 0x0020)) || !(bmsr & 0x0004)) + return 0; + + if(bmcr & 0x1000){ + an = miir(ctlr, ctlr->curphyad, Anar); + an &= miir(ctlr, ctlr->curphyad, Anlpar) & 0x3E0; + debug("an 0x%2.uX 0x%2.2uX 0x%2.2uX\n", + miir(ctlr, ctlr->curphyad, Anar), + miir(ctlr, ctlr->curphyad, Anlpar), + an); + + if(an & 0x0100) + x = 0x4000; + else if(an & 0x0080) + x = 0x2000; + else if(an & 0x0040) + x = 0x1000; + else if(an & 0x0020) + x = 0x0800; + else + x = 0; + } + else if((bmcr & 0x2100) == 0x2100) + x = 0x4000; + else if(bmcr & 0x2000){ + /* + * If FD capable, force it if necessary. + */ + if((bmsr & 0x4000) && ctlr->fd){ + miiw(ctlr, ctlr->curphyad, Bmcr, 0x2100); + x = 0x4000; + } + else + x = 0x2000; + } + else if(bmcr & 0x0100) + x = 0x1000; + else + x = 0x0800; + + csr6 = Sc|Mbo|Hbd|Ps|Ca|Sb|TrMODE; + if(ctlr->fdx & x) + csr6 |= Fd; + if(ctlr->ttm & x) + csr6 |= Ttm; + debug("csr6 0x%8.8uX 0x%8.8uX 0x%8.8luX\n", + csr6, ctlr->csr6, csr32r(ctlr, 6)); + if(csr6 != ctlr->csr6){ + ctlr->csr6 = csr6; + csr32w(ctlr, 6, csr6); + } + + return 1; +} + +static int +typephymode(Ctlr* ctlr, uchar* block, int wait) +{ + uchar *p; + int len, mc, nway, phyx, timeo; + + if(DEBUG){ + int i; + + len = (block[0] & ~0x80)+1; + for(i = 0; i < len; i++) + debug("%2.2uX ", block[i]); + debug("\n"); + } + + if(block[1] == 1) + len = 1; + else if(block[1] == 3) + len = 2; + else + return -1; + + /* + * Snarf the media capabilities, nway advertisment, + * FDX and TTM bitmaps. + */ + p = &block[5+len*block[3]+len*block[4+len*block[3]]]; + mc = *p++; + mc |= *p++<<8; + nway = *p++; + nway |= *p++<<8; + ctlr->fdx = *p++; + ctlr->fdx |= *p++<<8; + ctlr->ttm = *p++; + ctlr->ttm |= *p<<8; + debug("mc %4.4uX nway %4.4uX fdx %4.4uX ttm %4.4uX\n", + mc, nway, ctlr->fdx, ctlr->ttm); + USED(mc); + + phyx = block[2]; + ctlr->curphyad = ctlr->phy[phyx]; + + ctlr->csr6 = 0;//Sc|Mbo|Hbd|Ps|Ca|Sb|TrMODE; + //csr32w(ctlr, 6, ctlr->csr6); + if(typephylink(ctlr, block)) + return 0; + + if(!(ctlr->phyreset & (1<type5block) + type5block(ctlr, &ctlr->type5block[2]); + else + type5block(ctlr, &block[4+len*block[3]]); + debug("\n"); + ctlr->phyreset |= (1<csr6 = 0;//Sc|Mbo|Hbd|Ps|Ca|Sb|TrMODE; + //csr32w(ctlr, 6, ctlr->csr6); + if(typephylink(ctlr, block)) + return 0; + + /* + * Turn off auto-negotiation, set the auto-negotiation + * advertisment register then start the auto-negotiation + * process again. + */ + miiw(ctlr, ctlr->curphyad, Bmcr, 0); + miiw(ctlr, ctlr->curphyad, Anar, nway|1); + miiw(ctlr, ctlr->curphyad, Bmcr, 0x1000); + + if(!wait) + return 0; + + for(timeo = 0; timeo < 30; timeo++){ + if(typephylink(ctlr, block)) + return 0; + delay(100); + } + + return -1; +} + +static int +type0link(Ctlr* ctlr, uchar* block) +{ + int m, polarity, sense; + + m = (block[3]<<8)|block[2]; + sense = 1<<((m & 0x000E)>>1); + if(m & 0x0080) + polarity = sense; + else + polarity = 0; + + return (csr32r(ctlr, 12) & sense)^polarity; +} + +static int +type0mode(Ctlr* ctlr, uchar* block, int wait) +{ + int csr6, m, timeo; + + csr6 = Sc|Mbo|Hbd|Ca|Sb|TrMODE; +debug("type0: medium 0x%uX, fd %d: 0x%2.2uX 0x%2.2uX 0x%2.2uX 0x%2.2uX\n", + ctlr->medium, ctlr->fd, block[0], block[1], block[2], block[3]); + switch(block[0]){ + default: + break; + + case 0x04: /* 10BASE-TFD */ + case 0x05: /* 100BASE-TXFD */ + case 0x08: /* 100BASE-FXFD */ + /* + * Don't attempt full-duplex + * unless explicitly requested. + */ + if(!ctlr->fd) + return -1; + csr6 |= Fd; + break; + } + + m = (block[3]<<8)|block[2]; + if(m & 0x0001) + csr6 |= Ps; + if(m & 0x0010) + csr6 |= Ttm; + if(m & 0x0020) + csr6 |= Pcs; + if(m & 0x0040) + csr6 |= Scr; + + csr32w(ctlr, 12, block[1]); + microdelay(10); + csr32w(ctlr, 6, csr6); + ctlr->csr6 = csr6; + + if(!wait) + return 0; + + for(timeo = 0; timeo < 30; timeo++){ + if(type0link(ctlr, block)) + return 0; + delay(100); + } + + return -1; +} + +static int +mediaxx(Ether* ether, int wait) +{ + Ctlr* ctlr; + uchar *block; + + ctlr = ether->ctlr; + block = ctlr->infoblock[ctlr->curk]; + if(block[0] & 0x80){ + switch(block[1]){ + default: + return -1; + case 0: + if(ctlr->medium >= 0 && block[2] != ctlr->medium) + return 0; +/* need this test? */ if(ctlr->sct != 0x0800 && (ctlr->sct & 0x3F) != block[2]) + return 0; + if(type0mode(ctlr, block+2, wait)) + return 0; + break; + case 1: + if(typephymode(ctlr, block, wait)) + return 0; + break; + case 3: + if(typephymode(ctlr, block, wait)) + return 0; + break; + } + } + else{ + if(ctlr->medium >= 0 && block[0] != ctlr->medium) + return 0; +/* need this test? */if(ctlr->sct != 0x0800 && (ctlr->sct & 0x3F) != block[0]) + return 0; + if(type0mode(ctlr, block, wait)) + return 0; + } + + if(ctlr->csr6){ + if(!(ctlr->csr6 & Ps) || (ctlr->csr6 & Ttm)) + return 10; + return 100; + } + + return 0; +} + +static int +media(Ether* ether, int wait) +{ + Ctlr* ctlr; + int k, mbps; + + ctlr = ether->ctlr; + for(k = 0; k < ctlr->k; k++){ + mbps = mediaxx(ether, wait); + if(mbps > 0) + return mbps; + if(ctlr->curk == 0) + ctlr->curk = ctlr->k-1; + else + ctlr->curk--; + } + + return 0; +} + +static char* mediatable[9] = { + "10BASE-T", /* TP */ + "10BASE-2", /* BNC */ + "10BASE-5", /* AUI */ + "100BASE-TX", + "10BASE-TFD", + "100BASE-TXFD", + "100BASE-T4", + "100BASE-FX", + "100BASE-FXFD", +}; + +static uchar en1207[] = { /* Accton EN1207-COMBO */ + 0x00, 0x00, 0xE8, /* [0] vendor ethernet code */ + 0x00, /* [3] spare */ + + 0x00, 0x08, /* [4] connection (LSB+MSB = 0x0800) */ + 0x1F, /* [6] general purpose control */ + 2, /* [7] block count */ + + 0x00, /* [8] media code (10BASE-TX) */ + 0x0B, /* [9] general purpose port data */ + 0x9E, 0x00, /* [10] command (LSB+MSB = 0x009E) */ + + 0x03, /* [8] media code (100BASE-TX) */ + 0x1B, /* [9] general purpose port data */ + 0x6D, 0x00, /* [10] command (LSB+MSB = 0x006D) */ + + /* There is 10BASE-2 as well, but... */ +}; + +static uchar ana6910fx[] = { /* Adaptec (Cogent) ANA-6910FX */ + 0x00, 0x00, 0x92, /* [0] vendor ethernet code */ + 0x00, /* [3] spare */ + + 0x00, 0x08, /* [4] connection (LSB+MSB = 0x0800) */ + 0x3F, /* [6] general purpose control */ + 1, /* [7] block count */ + + 0x07, /* [8] media code (100BASE-FX) */ + 0x03, /* [9] general purpose port data */ + 0x2D, 0x00 /* [10] command (LSB+MSB = 0x000D) */ +}; + +static uchar smc9332[] = { /* SMC 9332 */ + 0x00, 0x00, 0xC0, /* [0] vendor ethernet code */ + 0x00, /* [3] spare */ + + 0x00, 0x08, /* [4] connection (LSB+MSB = 0x0800) */ + 0x1F, /* [6] general purpose control */ + 2, /* [7] block count */ + + 0x00, /* [8] media code (10BASE-TX) */ + 0x00, /* [9] general purpose port data */ + 0x9E, 0x00, /* [10] command (LSB+MSB = 0x009E) */ + + 0x03, /* [8] media code (100BASE-TX) */ + 0x09, /* [9] general purpose port data */ + 0x6D, 0x00, /* [10] command (LSB+MSB = 0x006D) */ +}; + +static uchar* leaf21140[] = { + en1207, /* Accton EN1207-COMBO */ + ana6910fx, /* Adaptec (Cogent) ANA-6910FX */ + smc9332, /* SMC 9332 */ + nil, +}; + +/* + * Copied to ctlr->srom at offset 20. + */ +static uchar leafpnic[] = { + 0x00, 0x00, 0x00, 0x00, /* MAC address */ + 0x00, 0x00, + 0x00, /* controller 0 device number */ + 0x1E, 0x00, /* controller 0 info leaf offset */ + 0x00, /* reserved */ + 0x00, 0x08, /* selected connection type */ + 0x00, /* general purpose control */ + 0x01, /* block count */ + + 0x8C, /* format indicator and count */ + 0x01, /* block type */ + 0x00, /* PHY number */ + 0x00, /* GPR sequence length */ + 0x00, /* reset sequence length */ + 0x00, 0x78, /* media capabilities */ + 0xE0, 0x01, /* Nway advertisment */ + 0x00, 0x50, /* FDX bitmap */ + 0x00, 0x18, /* TTM bitmap */ +}; + +static int +srom(Ctlr* ctlr) +{ + int i, k, oui, phy, x; + uchar *p; + + /* + * This is a partial decoding of the SROM format described in + * 'Digital Semiconductor 21X4 Serial ROM Format, Version 4.05, + * 2-Mar-98'. Only the 2114[03] are handled, support for other + * controllers can be added as needed. + */ + for(i = 0; i < sizeof(ctlr->srom)/2; i++){ + x = sromr(ctlr, i); + ctlr->srom[2*i] = x; + ctlr->srom[2*i+1] = x>>8; + } + + /* + * There are 2 SROM layouts: + * e.g. Digital EtherWORKS station address at offset 20; + * this complies with the 21140A SROM + * application note from Digital; + * e.g. SMC9332 station address at offset 0 followed by + * 2 additional bytes, repeated at offset + * 6; the 8 bytes are also repeated in + * reverse order at offset 8. + * To check which it is, read the SROM and check for the repeating + * patterns of the non-compliant cards; if that fails use the one at + * offset 20. + */ + ctlr->sromea = ctlr->srom; + for(i = 0; i < 8; i++){ + x = ctlr->srom[i]; + if(x != ctlr->srom[15-i] || x != ctlr->srom[16+i]){ + ctlr->sromea = &ctlr->srom[20]; + break; + } + } + + /* + * Fake up the SROM for the PNIC. + * It looks like a 21140 with a PHY. + * The MAC address is byte-swapped in the orginal SROM data. + */ + if(ctlr->id == Pnic){ + memmove(&ctlr->srom[20], leafpnic, sizeof(leafpnic)); + for(i = 0; i < Easize; i += 2){ + ctlr->srom[20+i] = ctlr->srom[i+1]; + ctlr->srom[20+i+1] = ctlr->srom[i]; + } + } + + /* + * Next, try to find the info leaf in the SROM for media detection. + * If it's a non-conforming card try to match the vendor ethernet code + * and point p at a fake info leaf with compact 21140 entries. + */ + if(ctlr->sromea == ctlr->srom){ + p = nil; + for(i = 0; leaf21140[i] != nil; i++){ + if(memcmp(leaf21140[i], ctlr->sromea, 3) == 0){ + p = &leaf21140[i][4]; + break; + } + } + if(p == nil) + return -1; + } + else + p = &ctlr->srom[(ctlr->srom[28]<<8)|ctlr->srom[27]]; + + /* + * Set up the info needed for later media detection. + * For the 21140, set the general-purpose mask in CSR12. + * The info block entries are stored in order of increasing + * precedence, so detection will work backwards through the + * stored indexes into ctlr->srom. + * If an entry is found which matches the selected connection + * type, save the index. Otherwise, start at the last entry. + * If any MII entries are found (type 1 and 3 blocks), scan + * for PHYs. + */ + ctlr->leaf = p; + ctlr->sct = *p++; + ctlr->sct |= *p++<<8; + if(ctlr->id != Tulip3){ + csr32w(ctlr, 12, Gpc|*p++); + delay(200); + } + ctlr->k = *p++; + if(ctlr->k >= nelem(ctlr->infoblock)) + ctlr->k = nelem(ctlr->infoblock)-1; + ctlr->sctk = ctlr->k-1; + phy = 0; + for(k = 0; k < ctlr->k; k++){ + ctlr->infoblock[k] = p; + /* + * The RAMIX PMC665 has a badly-coded SROM, + * hence the test for 21143 and type 3. + */ + if((*p & 0x80) || (ctlr->id == Tulip3 && *(p+1) == 3)){ + *p |= 0x80; + if(*(p+1) == 1 || *(p+1) == 3) + phy = 1; + if(*(p+1) == 5) + ctlr->type5block = p; + p += (*p & ~0x80)+1; + } + else{ + debug("type0: 0x%2.2uX 0x%2.2uX 0x%2.2uX 0x%2.2uX\n", + p[0], p[1], p[2], p[3]); + if(ctlr->sct != 0x0800 && *p == (ctlr->sct & 0xFF)) + ctlr->sctk = k; + p += 4; + } + } + ctlr->curk = ctlr->sctk; + debug("sct 0x%uX medium 0x%uX k %d curk %d phy %d\n", + ctlr->sct, ctlr->medium, ctlr->k, ctlr->curk, phy); + + if(phy){ + x = 0; + for(k = 0; k < nelem(ctlr->phy); k++){ + if((oui = miir(ctlr, k, 2)) == -1 || oui == 0) + continue; + if(DEBUG){ + oui = (oui & 0x3FF)<<6; + oui |= miir(ctlr, k, 3)>>10; + miir(ctlr, k, 1); + debug("phy%d: index %d oui %uX reg1 %uX\n", + x, k, oui, miir(ctlr, k, 1)); + USED(oui); + } + ctlr->phy[x] = k; + } + } + + ctlr->fd = 0; + ctlr->medium = -1; + + return 0; +} + +static void +dec2114xpci(void) +{ + Ctlr *ctlr; + Pcidev *p; + int x; + + p = nil; + while(p = pcimatch(p, 0, 0)){ + if(p->ccru != ((0x02<<8)|0x00)) + continue; + switch((p->did<<16)|p->vid){ + default: + continue; + + case Tulip3: /* 21143 */ + /* + * Exit sleep mode. + */ + x = pcicfgr32(p, 0x40); + x &= ~0xc0000000; + pcicfgw32(p, 0x40, x); + /*FALLTHROUGH*/ + + case Pnic: /* PNIC */ + case Pnic2: /* PNIC-II */ + case Tulip0: /* 21140 */ + break; + } + + /* + * bar[0] is the I/O port register address and + * bar[1] is the memory-mapped register address. + */ + ctlr = ialloc(sizeof(Ctlr), 0); + ctlr->port = p->mem[0].bar & ~0x01; + ctlr->pcidev = p; + ctlr->id = (p->did<<16)|p->vid; + + /* + * Some cards (e.g. ANA-6910FX) seem to need the Ps bit + * set or they don't always work right after a hardware + * reset. + */ + csr32w(ctlr, 6, Mbo|Ps); + softreset(ctlr); + + if(srom(ctlr)){ + //free(ctlr); + break; + } + + switch(ctlr->id){ + default: + break; + + case Pnic: /* PNIC */ + /* + * Turn off the jabber timer. + */ + csr32w(ctlr, 15, 0x00000001); + break; + } + + if(ctlrhead != nil) + ctlrtail->next = ctlr; + else + ctlrhead = ctlr; + ctlrtail = ctlr; + } +} + +int +ether21140reset(Ether* ether) +{ + Ctlr *ctlr; + int i, x; + uchar ea[Easize]; + static int scandone; + + if(scandone == 0){ + dec2114xpci(); + scandone = 1; + } + + /* + * Any adapter matches if no ether->port is supplied, + * otherwise the ports must match. + */ + for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + if(ether->port == 0 || ether->port == ctlr->port){ + ctlr->active = 1; + break; + } + } + if(ctlr == nil) + return -1; + + ether->ctlr = ctlr; + ether->port = ctlr->port; + ether->irq = ctlr->pcidev->intl; + ether->tbdf = ctlr->pcidev->tbdf; + + /* + * Check if the adapter's station address is to be overridden. + * If not, read it from the EEPROM and set in ether->ea prior to + * loading the station address in the hardware. + */ + memset(ea, 0, Easize); + if(memcmp(ea, ether->ea, Easize) == 0) + memmove(ether->ea, ctlr->sromea, Easize); + + /* + * Look for a medium override in case there's no autonegotiation + * (no MII) or the autonegotiation fails. + */ + for(i = 0; i < ether->nopt; i++){ + if(cistrcmp(ether->opt[i], "FD") == 0){ + ctlr->fd = 1; + continue; + } + for(x = 0; x < nelem(mediatable); x++){ + debug("compare <%s> <%s>\n", mediatable[x], + ether->opt[i]); + if(cistrcmp(mediatable[x], ether->opt[i])) + continue; + ctlr->medium = x; + + switch(ctlr->medium){ + default: + ctlr->fd = 0; + break; + + case 0x04: /* 10BASE-TFD */ + case 0x05: /* 100BASE-TXFD */ + case 0x08: /* 100BASE-FXFD */ + ctlr->fd = 1; + break; + } + break; + } + } + + ether->mbps = media(ether, 1); + + /* + * Initialise descriptor rings, ethernet address. + */ + ctlr->nrdr = Nrde; + ctlr->ntdr = Ntde; + pcisetbme(ctlr->pcidev); + ctlrinit(ether); + + /* + * Linkage to the generic ethernet driver. + */ + ether->attach = attach; + ether->transmit = transmit; + ether->interrupt = interrupt; + + return 0; +} diff -Nru /sys/src/fs/pc/ether8139.c /sys/src/fs/pc/ether8139.c --- /sys/src/fs/pc/ether8139.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/pc/ether8139.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,775 @@ +/* + * Realtek 8139 (but not the 8129). + * Error recovery for the various over/under -flow conditions + * may need work. + */ +#ifdef FS +#include "all.h" +#include "io.h" +#include "mem.h" +#include "../ip/ip.h" + +#else + +#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" +#endif /* FS */ + +#include "etherif.h" + +#include "compat.h" + +enum { /* registers */ + Idr0 = 0x0000, /* MAC address */ + Mar0 = 0x0008, /* Multicast address */ + Tsd0 = 0x0010, /* Transmit Status Descriptor0 */ + Tsad0 = 0x0020, /* Transmit Start Address Descriptor0 */ + Rbstart = 0x0030, /* Receive Buffer Start Address */ + Erbcr = 0x0034, /* Early Receive Byte Count */ + Ersr = 0x0036, /* Early Receive Status */ + Cr = 0x0037, /* Command Register */ + Capr = 0x0038, /* Current Address of Packet Read */ + Cbr = 0x003A, /* Current Buffer Address */ + Imr = 0x003C, /* Interrupt Mask */ + Isr = 0x003E, /* Interrupt Status */ + Tcr = 0x0040, /* Transmit Configuration */ + Rcr = 0x0044, /* Receive Configuration */ + Tctr = 0x0048, /* Timer Count */ + Mpc = 0x004C, /* Missed Packet Counter */ + Cr9346 = 0x0050, /* 9346 Command Register */ + Config0 = 0x0051, /* Configuration Register 0 */ + Config1 = 0x0052, /* Configuration Register 1 */ + TimerInt = 0x0054, /* Timer Interrupt */ + Msr = 0x0058, /* Media Status */ + Config3 = 0x0059, /* Configuration Register 3 */ + Config4 = 0x005A, /* Configuration Register 4 */ + Mulint = 0x005C, /* Multiple Interrupt Select */ + RerID = 0x005E, /* PCI Revision ID */ + Tsad = 0x0060, /* Transmit Status of all Descriptors */ + + Bmcr = 0x0062, /* Basic Mode Control */ + Bmsr = 0x0064, /* Basic Mode Status */ + Anar = 0x0066, /* Auto-Negotiation Advertisment */ + Anlpar = 0x0068, /* Auto-Negotiation Link Partner */ + Aner = 0x006A, /* Auto-Negotiation Expansion */ + Dis = 0x006C, /* Disconnect Counter */ + Fcsc = 0x006E, /* False Carrier Sense Counter */ + Nwaytr = 0x0070, /* N-way Test */ + Rec = 0x0072, /* RX_ER Counter */ + Cscr = 0x0074, /* CS Configuration */ + Phy1parm = 0x0078, /* PHY Parameter 1 */ + Twparm = 0x007C, /* Twister Parameter */ + Phy2parm = 0x0080, /* PHY Parameter 2 */ +}; + +enum { /* Cr */ + Bufe = 0x01, /* Rx Buffer Empty */ + Te = 0x04, /* Transmitter Enable */ + Re = 0x08, /* Receiver Enable */ + Rst = 0x10, /* Software Reset */ +}; + +enum { /* Imr/Isr */ + Rok = 0x0001, /* Receive OK */ + Rer = 0x0002, /* Receive Error */ + Tok = 0x0004, /* Transmit OK */ + Ter = 0x0008, /* Transmit Error */ + Rxovw = 0x0010, /* Receive Buffer Overflow */ + PunLc = 0x0020, /* Packet Underrun or Link Change */ + Fovw = 0x0040, /* Receive FIFO Overflow */ + Clc = 0x2000, /* Cable Length Change */ + Timerbit = 0x4000, /* Timer */ + Serr = 0x8000, /* System Error */ +}; + +enum { /* Tcr */ + Clrabt = 0x00000001, /* Clear Abort */ + TxrrSHIFT = 4, /* Transmit Retry Count */ + TxrrMASK = 0x000000F0, + MtxdmaSHIFT = 8, /* Max. DMA Burst Size */ + MtxdmaMASK = 0x00000700, + Mtxdma2048 = 0x00000700, + Acrc = 0x00010000, /* Append CRC (not) */ + LbkSHIFT = 17, /* Loopback Test */ + LbkMASK = 0x00060000, + Rtl8139ArevG = 0x00800000, /* RTL8139A Rev. G ID */ + IfgSHIFT = 24, /* Interframe Gap */ + IfgMASK = 0x03000000, + HwveridSHIFT = 26, /* Hardware Version ID */ + HwveridMASK = 0x7C000000, +}; + +enum { /* Rcr */ + Aap = 0x00000001, /* Accept All Packets */ + Apm = 0x00000002, /* Accept Physical Match */ + Am = 0x00000004, /* Accept Multicast */ + Ab = 0x00000008, /* Accept Broadcast */ + Ar = 0x00000010, /* Accept Runt */ + Aer = 0x00000020, /* Accept Error */ + Sel9356 = 0x00000040, /* 9356 EEPROM used */ + Wrap = 0x00000080, /* Rx Buffer Wrap Control */ + MrxdmaSHIFT = 8, /* Max. DMA Burst Size */ + MrxdmaMASK = 0x00000700, + Mrxdmaunlimited = 0x00000700, + RblenSHIFT = 11, /* Receive Buffer Length */ + RblenMASK = 0x00001800, + Rblen8K = 0x00000000, /* 8KB+16 */ + Rblen16K = 0x00000800, /* 16KB+16 */ + Rblen32K = 0x00001000, /* 32KB+16 */ + Rblen64K = 0x00001800, /* 64KB+16 */ + RxfthSHIFT = 13, /* Receive Buffer Length */ + RxfthMASK = 0x0000E000, + Rxfth256 = 0x00008000, + Rxfthnone = 0x0000E000, + Rer8 = 0x00010000, /* Accept Error Packets > 8 bytes */ + MulERINT = 0x00020000, /* Multiple Early Interrupt Select */ + ErxthSHIFT = 24, /* Early Rx Threshold */ + ErxthMASK = 0x0F000000, + Erxthnone = 0x00000000, +}; + +enum { /* Received Packet Status */ + Rcok = 0x0001, /* Receive Completed OK */ + Fae = 0x0002, /* Frame Alignment Error */ + Crc = 0x0004, /* CRC Error */ + Long = 0x0008, /* Long Packet */ + Runt = 0x0010, /* Runt Packet Received */ + Ise = 0x0020, /* Invalid Symbol Error */ + Bar = 0x2000, /* Broadcast Address Received */ + Pam = 0x4000, /* Physical Address Matched */ + Mar = 0x8000, /* Multicast Address Received */ +}; + +enum { /* Media Status Register */ + Rxpf = 0x01, /* Pause Flag */ + Txpf = 0x02, /* Pause Flag */ + Linkb = 0x04, /* Inverse of Link Status */ + Speed10 = 0x08, /* 10Mbps */ + Auxstatus = 0x10, /* Aux. Power Present Status */ + Rxfce = 0x40, /* Receive Flow Control Enable */ + Txfce = 0x80, /* Transmit Flow Control Enable */ +}; + +typedef struct { /* Soft Transmit Descriptor */ + int tsd; + int tsad; + uchar* data; + Block* bp; +} Td; + +enum { /* Tsd0 */ + SizeSHIFT = 0, /* Descriptor Size */ + SizeMASK = 0x00001FFF, + Own = 0x00002000, + Tun = 0x00004000, /* Transmit FIFO Underrun */ + Tcok = 0x00008000, /* Transmit COmpleted OK */ + EtxthSHIFT = 16, /* Early Tx Threshold */ + EtxthMASK = 0x001F0000, + NccSHIFT = 24, /* Number of Collisions Count */ + NccMASK = 0x0F000000, + Cdh = 0x10000000, /* CD Heartbeat */ + Owc = 0x20000000, /* Out of Window Collision */ + Tabt = 0x40000000, /* Transmit Abort */ + Crs = 0x80000000, /* Carrier Sense Lost */ +}; + +enum { + Rblen = Rblen64K, /* Receive Buffer Length */ + Ntd = 4, /* Number of Transmit Descriptors */ + Tdbsz = ROUNDUP(sizeof(Etherpkt), 4), +}; + +typedef struct Ctlr Ctlr; +typedef struct Ctlr { + int port; + Pcidev* pcidev; + Ctlr* next; + int active; + int id; + + QLock alock; /* attach */ + Lock ilock; /* init */ + void* alloc; /* base of per-Ctlr allocated data */ + + int rcr; /* receive configuration register */ + uchar* rbstart; /* receive buffer */ + int rblen; /* receive buffer length */ + int ierrs; /* receive errors */ + + Lock tlock; /* transmit */ + Td td[Ntd]; + int ntd; /* descriptors active */ + int tdh; /* host index into td */ + int tdi; /* interface index into td */ + int etxth; /* early transmit threshold */ + int taligned; /* packet required no alignment */ + int tunaligned; /* packet required alignment */ + + int dis; /* disconnect counter */ + int fcsc; /* false carrier sense counter */ + int rec; /* RX_ER counter */ +} Ctlr; + +static Ctlr* ctlrhead; +static Ctlr* ctlrtail; + +#define csr8r(c, r) (inb((c)->port+(r))) +#define csr16r(c, r) (ins((c)->port+(r))) +#define csr32r(c, r) (inl((c)->port+(r))) +#define csr8w(c, r, b) (outb((c)->port+(r), (int)(b))) +#define csr16w(c, r, w) (outs((c)->port+(r), (ushort)(w))) +#define csr32w(c, r, l) (outl((c)->port+(r), (ulong)(l))) + +static void +rtl8139promiscuous(void* arg, int on) +{ + Ether *edev; + Ctlr * ctlr; + + edev = arg; + ctlr = edev->ctlr; + ilock(&ctlr->ilock); + + if(on) + ctlr->rcr |= Aap; + else + ctlr->rcr &= ~Aap; + csr32w(ctlr, Rcr, ctlr->rcr); + iunlock(&ctlr->ilock); +} + +static long +rtl8139ifstat(Ether* edev, void* a, long n, ulong offset) +{ + int l; + char *p; + Ctlr *ctlr; + + ctlr = edev->ctlr; + p = malloc(READSTR); + l = snprint(p, READSTR, "rcr %8.8uX\n", ctlr->rcr); + l += snprint(p+l, READSTR-l, "ierrs %d\n", ctlr->ierrs); + l += snprint(p+l, READSTR-l, "etxth %d\n", ctlr->etxth); + l += snprint(p+l, READSTR-l, "taligned %d\n", ctlr->taligned); + l += snprint(p+l, READSTR-l, "tunaligned %d\n", ctlr->tunaligned); + ctlr->dis += csr16r(ctlr, Dis); + l += snprint(p+l, READSTR-l, "dis %d\n", ctlr->dis); + ctlr->fcsc += csr16r(ctlr, Fcsc); + l += snprint(p+l, READSTR-l, "fcscnt %d\n", ctlr->fcsc); + ctlr->rec += csr16r(ctlr, Rec); + l += snprint(p+l, READSTR-l, "rec %d\n", ctlr->rec); + + l += snprint(p+l, READSTR-l, "Tcr %8.8luX\n", csr32r(ctlr, Tcr)); + l += snprint(p+l, READSTR-l, "Config0 %2.2uX\n", csr8r(ctlr, Config0)); + l += snprint(p+l, READSTR-l, "Config1 %2.2uX\n", csr8r(ctlr, Config1)); + l += snprint(p+l, READSTR-l, "Msr %2.2uX\n", csr8r(ctlr, Msr)); + l += snprint(p+l, READSTR-l, "Config3 %2.2uX\n", csr8r(ctlr, Config3)); + l += snprint(p+l, READSTR-l, "Config4 %2.2uX\n", csr8r(ctlr, Config4)); + + l += snprint(p+l, READSTR-l, "Bmcr %4.4uX\n", csr16r(ctlr, Bmcr)); + l += snprint(p+l, READSTR-l, "Bmsr %4.4uX\n", csr16r(ctlr, Bmsr)); + l += snprint(p+l, READSTR-l, "Anar %4.4uX\n", csr16r(ctlr, Anar)); + l += snprint(p+l, READSTR-l, "Anlpar %4.4uX\n", csr16r(ctlr, Anlpar)); + l += snprint(p+l, READSTR-l, "Aner %4.4uX\n", csr16r(ctlr, Aner)); + l += snprint(p+l, READSTR-l, "Nwaytr %4.4uX\n", csr16r(ctlr, Nwaytr)); + snprint(p+l, READSTR-l, "Cscr %4.4uX\n", csr16r(ctlr, Cscr)); + n = readstr(offset, a, n, p); + free(p); + + return n; +} + +static int +rtl8139reset(Ctlr* ctlr) +{ + int timeo; + + /* + * Soft reset the controller. + */ + csr8w(ctlr, Cr, Rst); + for(timeo = 0; timeo < 1000; timeo++){ + if(!(csr8r(ctlr, Cr) & Rst)) + return 0; + delay(1); + } + + return -1; +} + +static void +rtl8139halt(Ctlr* ctlr) +{ + int i; + + csr8w(ctlr, Cr, 0); + csr16w(ctlr, Imr, 0); + csr16w(ctlr, Isr, ~0); + + for(i = 0; i < Ntd; i++){ + if(ctlr->td[i].bp == nil) + continue; + freeb(ctlr->td[i].bp); + ctlr->td[i].bp = nil; + } +} + +static void +rtl8139init(Ether* edev) +{ + int i; + ulong r; + Ctlr *ctlr; + uchar *alloc; + + ctlr = edev->ctlr; + ilock(&ctlr->ilock); + + rtl8139halt(ctlr); + + /* + * MAC Address. + */ + r = (edev->ea[3]<<24)|(edev->ea[2]<<16)|(edev->ea[1]<<8)|edev->ea[0]; + csr32w(ctlr, Idr0, r); + r = (edev->ea[5]<<8)|edev->ea[4]; + csr32w(ctlr, Idr0+4, r); + + /* + * Receiver + */ + alloc = (uchar*)ROUNDUP((ulong)ctlr->alloc, 32); + ctlr->rbstart = alloc; + alloc += ctlr->rblen+16; + memset(ctlr->rbstart, 0, ctlr->rblen+16); + csr32w(ctlr, Rbstart, PCIWADDR(ctlr->rbstart)); + ctlr->rcr = Rxfth256|Rblen|Mrxdmaunlimited|Ab|Apm; + + /* + * Transmitter. + */ + for(i = 0; i < Ntd; i++){ + ctlr->td[i].tsd = Tsd0+i*4; + ctlr->td[i].tsad = Tsad0+i*4; + ctlr->td[i].data = alloc; + alloc += Tdbsz; + ctlr->td[i].bp = nil; + } + ctlr->ntd = ctlr->tdh = ctlr->tdi = 0; + ctlr->etxth = 128/32; + + /* + * Interrupts. + */ + csr32w(ctlr, TimerInt, 0); + csr16w(ctlr, Imr, Serr|Timerbit|Fovw|PunLc|Rxovw|Ter|Tok|Rer|Rok); + csr32w(ctlr, Mpc, 0); + + /* + * Enable receiver/transmitter. + * Need to enable before writing the Rcr or it won't take. + */ + csr8w(ctlr, Cr, Te|Re); + csr32w(ctlr, Tcr, Mtxdma2048); + csr32w(ctlr, Rcr, ctlr->rcr); + + iunlock(&ctlr->ilock); +} + +static void +rtl8139attach(Ether* edev) +{ + Ctlr *ctlr; + + ctlr = edev->ctlr; + qlock(&ctlr->alock); + if(ctlr->alloc == nil){ + ctlr->rblen = 1<<((Rblen>>RblenSHIFT)+13); + ctlr->alloc = mallocz(ctlr->rblen+16 + Ntd*Tdbsz + 32, 0); + rtl8139init(edev); + } + qunlock(&ctlr->alock); +} + +static void +rtl8139txstart(Ether* edev) +{ + Td *td; + int size; + Block *bp; + Ctlr *ctlr; + + ctlr = edev->ctlr; + while(ctlr->ntd < Ntd){ + bp = etheroq(edev); + if(bp == nil) + break; + size = BLEN(bp); + + td = &ctlr->td[ctlr->tdh]; + if(((int)bp->rp) & 0x03){ + memmove(td->data, bp->rp, size); + freeb(bp); + csr32w(ctlr, td->tsad, PCIWADDR(td->data)); + ctlr->tunaligned++; + } + else{ + td->bp = bp; + csr32w(ctlr, td->tsad, PCIWADDR(bp->rp)); + ctlr->taligned++; + } + csr32w(ctlr, td->tsd, (ctlr->etxth<ntd++; + ctlr->tdh = NEXT(ctlr->tdh, Ntd); + } +} + +static void +rtl8139transmit(Ether* edev) +{ + Ctlr *ctlr; + + ctlr = edev->ctlr; + ilock(&ctlr->tlock); + rtl8139txstart(edev); + iunlock(&ctlr->tlock); +} + +static void +rtl8139receive(Ether* edev) +{ + Block *bp; + Ctlr *ctlr; + ushort capr; + uchar cr, *p; + int l, length, status; + + ctlr = edev->ctlr; + + /* + * Capr is where the host is reading from, + * Cbr is where the NIC is currently writing. + */ + capr = (csr16r(ctlr, Capr)+16) % ctlr->rblen; + while(!(csr8r(ctlr, Cr) & Bufe)){ + p = ctlr->rbstart+capr; + + /* + * Apparently the packet length may be 0xFFF0 if + * the NIC is still copying the packet into memory. + */ + length = (*(p+3)<<8)|*(p+2); + if(length == 0xFFF0) + break; + status = (*(p+1)<<8)|*p; + + if(!(status & Rcok)){ +#ifndef FS + if(status & (Ise|Fae)) + edev->frames++; + if(status & Crc) + edev->crcs++; + if(status & (Runt|Long)) + edev->buffs++; +#endif + /* + * Reset the receiver. + * Also may have to restore the multicast list + * here too if it ever gets used. + */ + cr = csr8r(ctlr, Cr); + csr8w(ctlr, Cr, cr & ~Re); + csr32w(ctlr, Rbstart, PCIWADDR(ctlr->rbstart)); + csr8w(ctlr, Cr, cr); + csr32w(ctlr, Rcr, ctlr->rcr); + + continue; + } + + /* + * Receive Completed OK. + * Very simplistic; there are ways this could be done + * without copying, but the juice probably isn't worth + * the squeeze. + * The packet length includes a 4 byte CRC on the end. + */ + capr = (capr+4) % ctlr->rblen; + p = ctlr->rbstart+capr; + capr = (capr+length) % ctlr->rblen; + + if((bp = iallocb(length)) != nil){ + SETWPCNT(bp, 0); + if(p+length >= ctlr->rbstart+ctlr->rblen){ + l = ctlr->rbstart+ctlr->rblen - p; + memmove(ENDDATA(bp), p, l); + INCRPTR(bp, l); + length -= l; + p = ctlr->rbstart; + } + if(length > 0){ + memmove(ENDDATA(bp), p, length); + INCRPTR(bp, length); + } + INCRPTR(bp, -4); + if (BLEN(bp) < 0) { + print("rtl8139receive: input packet of negative length\n"); + SETWPCNT(bp, 0); + freeb(bp); + } else + ETHERIQ(edev, bp, 1); + } + + capr = ROUNDUP(capr, 4); + csr16w(ctlr, Capr, capr-16); + } +} + +static void +rtl8139interrupt(Ureg*, void* arg) +{ + Td *td; + Ctlr *ctlr; + Ether *edev; + int isr, msr, tsd; + + edev = arg; + ctlr = edev->ctlr; + + while((isr = csr16r(ctlr, Isr)) != 0){ + csr16w(ctlr, Isr, isr); + if(isr & (Fovw|PunLc|Rxovw|Rer|Rok)){ + rtl8139receive(edev); + if(!(isr & Rok)) + ctlr->ierrs++; + isr &= ~(Fovw|Rxovw|Rer|Rok); + } + + if(isr & (Ter|Tok)){ + ilock(&ctlr->tlock); + while(ctlr->ntd){ + td = &ctlr->td[ctlr->tdi]; + tsd = csr32r(ctlr, td->tsd); + if(!(tsd & (Tabt|Tun|Tcok))) + break; + + if(!(tsd & Tcok)){ + if(tsd & Tun){ + if(ctlr->etxth < ETHERMAXTU/32) + ctlr->etxth++; + } +#ifndef FS + edev->oerrs++; +#endif + } + + if(td->bp != nil){ + freeb(td->bp); + td->bp = nil; + } + + ctlr->ntd--; + ctlr->tdi = NEXT(ctlr->tdi, Ntd); + } + rtl8139txstart(edev); + iunlock(&ctlr->tlock); + isr &= ~(Ter|Tok); + } + + if(isr & PunLc){ + /* + * Maybe the link changed - do we care very much? + */ + msr = csr8r(ctlr, Msr); + if(!(msr & Linkb)){ + if(!(msr & Speed10) && edev->mbps != 100){ + edev->mbps = 100; + qsetlimit(edev->oq, 256*1024); + } + else if((msr & Speed10) && edev->mbps != 10){ + edev->mbps = 10; + qsetlimit(edev->oq, 65*1024); + } + } + isr &= ~(Clc|PunLc); + } + + /* + * Only Serr|Timerbit should be left by now. + * Should anything be done to tidy up? TimerInt isn't + * used so that can be cleared. A PCI bus error is indicated + * by Serr, that's pretty serious; is there anyhing to do + * other than try to reinitialise the chip? + */ + if(isr != 0){ + iprint("rtl8139interrupt: imr %4.4ux isr %4.4ux\n", + csr16r(ctlr, Imr), isr); + if(isr & Timerbit) + csr32w(ctlr, TimerInt, 0); + if(isr & Serr) + rtl8139init(edev); + } + } +} + +static Ctlr* +rtl8139match(Ether* edev, int id) +{ + int port; + Pcidev *p; + Ctlr *ctlr; + + /* + * Any adapter matches if no edev->port is supplied, + * otherwise the ports must match. + */ + for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + p = ctlr->pcidev; + if(((p->did<<16)|p->vid) != id) + continue; + port = p->mem[0].bar & ~0x01; + if(edev->port != 0 && edev->port != port) + continue; + + if(ioalloc(port, p->mem[0].size, 0, "rtl8139") < 0){ + print("rtl8139: port 0x%ux in use\n", port); + continue; + } + + ctlr->port = port; + if(rtl8139reset(ctlr)) + continue; + pcisetbme(p); + + ctlr->active = 1; + return ctlr; + } + return nil; +} + +static struct { + char* name; + int id; +} rtl8139pci[] = { + { "rtl8139", (0x8139<<16)|0x10EC, }, /* generic */ + { "smc1211", (0x1211<<16)|0x1113, }, /* SMC EZ-Card */ + { "dfe-538tx", (0x1300<<16)|0x1186, }, /* D-Link DFE-538TX */ + { "dfe-560txd", (0x1340<<16)|0x1186, }, /* D-Link DFE-560TXD */ + { nil }, +}; + +int +rtl8139pnp(Ether* edev) +{ + int i, id; + Pcidev *p; + Ctlr *ctlr; + uchar ea[Eaddrlen]; + + /* + * Make a list of all ethernet controllers + * if not already done. + */ + if(ctlrhead == nil){ + p = nil; + while(p = pcimatch(p, 0, 0)){ +#ifdef FS + if(p->ccru != ((0x02<<8)|0x00)) +#else + if(p->ccrb != 0x02 || p->ccru != 0) +#endif + continue; + ctlr = malloc(sizeof(Ctlr)); + ctlr->pcidev = p; + ctlr->id = (p->did<<16)|p->vid; + + if(ctlrhead != nil) + ctlrtail->next = ctlr; + else + ctlrhead = ctlr; + ctlrtail = ctlr; + } + } + + /* + * Is it an RTL8139 under a different name? + * Normally a search is made through all the found controllers + * for one which matches any of the known vid+did pairs. + * If a vid+did pair is specified a search is made for that + * specific controller only. + */ + id = 0; + for(i = 0; i < edev->nopt; i++){ + if(cistrncmp(edev->opt[i], "id=", 3) == 0) + id = strtol(&edev->opt[i][3], nil, 0); + } + + ctlr = nil; + if(id != 0) + ctlr = rtl8139match(edev, id); + else for(i = 0; rtl8139pci[i].name; i++){ + if((ctlr = rtl8139match(edev, rtl8139pci[i].id)) != nil) + break; + } + if(ctlr == nil) + return -1; + + edev->ctlr = ctlr; + edev->port = ctlr->port; + edev->irq = ctlr->pcidev->intl; + edev->tbdf = ctlr->pcidev->tbdf; + + /* + * Check if the adapter's station address is to be overridden. + * If not, read it from the device and set in edev->ea. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, edev->ea, Eaddrlen) == 0){ + i = csr32r(ctlr, Idr0); + edev->ea[0] = i; + edev->ea[1] = i>>8; + edev->ea[2] = i>>16; + edev->ea[3] = i>>24; + i = csr32r(ctlr, Idr0+4); + edev->ea[4] = i; + edev->ea[5] = i>>8; + } + + edev->attach = rtl8139attach; + edev->transmit = rtl8139transmit; + edev->interrupt = rtl8139interrupt; +#ifndef FS + edev->ifstat = rtl8139ifstat; + + edev->arg = edev; + edev->promiscuous = rtl8139promiscuous; +#endif + + /* + * This should be much more dynamic but will do for now. + */ + if((csr8r(ctlr, Msr) & (Speed10|Linkb)) == 0) + edev->mbps = 100; + + return 0; +} + +void +ether8139link(void) +{ + addethercard("rtl8139", rtl8139pnp); +} + +void +ether8139bothlink(void) +{ + ether8139link(); +} diff -Nru /sys/src/fs/pc/ether8169.c /sys/src/fs/pc/ether8169.c --- /sys/src/fs/pc/ether8169.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/pc/ether8169.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,1133 @@ +/* + * Realtek RTL8110S/8169S. + * Mostly there. There are some magic register values used + * which are not described in any datasheet or driver but seem + * to be necessary. + * No tuning has been done. Only tested on an RTL8110S, there + * are slight differences between the chips in the series so some + * tweaks may be needed. + */ +#include "etherdat.h" +#include "etherif.h" +#include "ethermii.h" +#include "compat.h" + +#define dprint(...) print("ether 8169: " __VA_ARGS__); + +enum { /* registers */ + Idr0 = 0x00, /* MAC address */ + Mar0 = 0x08, /* Multicast address */ + Dtccr = 0x10, /* Dump Tally Counter Command */ + Tnpds = 0x20, /* Transmit Normal Priority Descriptors */ + Thpds = 0x28, /* Transmit High Priority Descriptors */ + Flash = 0x30, /* Flash Memory Read/Write */ + Erbcr = 0x34, /* Early Receive Byte Count */ + Ersr = 0x36, /* Early Receive Status */ + Cr = 0x37, /* Command Register */ + Tppoll = 0x38, /* Transmit Priority Polling */ + Imr = 0x3C, /* Interrupt Mask */ + Isr = 0x3E, /* Interrupt Status */ + Tcr = 0x40, /* Transmit Configuration */ + Rcr = 0x44, /* Receive Configuration */ + Tctr = 0x48, /* Timer Count */ + Mpc = 0x4C, /* Missed Packet Counter */ + Cr9346 = 0x50, /* 9346 Command Register */ + Config0 = 0x51, /* Configuration Register 0 */ + Config1 = 0x52, /* Configuration Register 1 */ + Config2 = 0x53, /* Configuration Register 2 */ + Config3 = 0x54, /* Configuration Register 3 */ + Config4 = 0x55, /* Configuration Register 4 */ + Config5 = 0x56, /* Configuration Register 5 */ + Timerint = 0x58, /* Timer Interrupt */ + Mulint = 0x5C, /* Multiple Interrupt Select */ + Phyar = 0x60, /* PHY Access */ + Tbicsr0 = 0x64, /* TBI Control and Status */ + Tbianar = 0x68, /* TBI Auto-Negotiation Advertisment */ + Tbilpar = 0x6A, /* TBI Auto-Negotiation Link Partner */ + Phystatus = 0x6C, /* PHY Status */ + + Rms = 0xDA, /* Receive Packet Maximum Size */ + Cplusc = 0xE0, /* C+ Command */ + Rdsar = 0xE4, /* Receive Descriptor Start Address */ + Mtps = 0xEC, /* Max. Transmit Packet Size */ +}; + +enum { /* Dtccr */ + Cmd = 0x00000008, /* Command */ +}; + +enum { /* Cr */ + Te = 0x04, /* Transmitter Enable */ + Re = 0x08, /* Receiver Enable */ + Rst = 0x10, /* Software Reset */ +}; + +enum { /* Tppoll */ + Fswint = 0x01, /* Forced Software Interrupt */ + Npq = 0x40, /* Normal Priority Queue polling */ + Hpq = 0x80, /* High Priority Queue polling */ +}; + +enum { /* Imr/Isr */ + Rok = 0x0001, /* Receive OK */ + Rer = 0x0002, /* Receive Error */ + Tok = 0x0004, /* Transmit OK */ + Ter = 0x0008, /* Transmit Error */ + Rdu = 0x0010, /* Receive Descriptor Unavailable */ + Punlc = 0x0020, /* Packet Underrun or Link Change */ + Fovw = 0x0040, /* Receive FIFO Overflow */ + Tdu = 0x0080, /* Transmit Descriptor Unavailable */ + Swint = 0x0100, /* Software Interrupt */ + Timeout = 0x4000, /* Timer */ + Serr = 0x8000, /* System Error */ +}; + +enum { /* Tcr */ + MtxdmaSHIFT = 8, /* Max. DMA Burst Size */ + MtxdmaMASK = 0x00000700, + Mtxdmaunlimited = 0x00000700, + Acrc = 0x00010000, /* Append CRC (not) */ + Lbk0 = 0x00020000, /* Loopback Test 0 */ + Lbk1 = 0x00040000, /* Loopback Test 1 */ + Ifg2 = 0x00080000, /* Interframe Gap 2 */ + HwveridSHIFT = 23, /* Hardware Version ID */ + HwveridMASK = 0x7C800000, + Macv01 = 0x00000000, /* RTL8169 */ + Macv02 = 0x00800000, /* RTL8169S/8110S */ + Macv03 = 0x04000000, /* RTL8169S/8110S */ + Macv04 = 0x10000000, /* RTL8169SB/8110SB */ + Macv05 = 0x18000000, /* RTL8169SC/8110SC */ + Macv11 = 0x30000000, /* RTL8168B/8111B */ + Macv12 = 0x38000000, /* RTL8169B/8111B */ + Macv13 = 0x34000000, /* RTL8101E */ + Macv14 = 0x30800000, /* RTL8100E */ + Macv15 = 0x38800000, /* RTL8100E */ + Ifg0 = 0x01000000, /* Interframe Gap 0 */ + Ifg1 = 0x02000000, /* Interframe Gap 1 */ +}; + +enum { /* Rcr */ + Aap = 0x00000001, /* Accept All Packets */ + Apm = 0x00000002, /* Accept Physical Match */ + Am = 0x00000004, /* Accept Multicast */ + Ab = 0x00000008, /* Accept Broadcast */ + Ar = 0x00000010, /* Accept Runt */ + Aer = 0x00000020, /* Accept Error */ + Sel9356 = 0x00000040, /* 9356 EEPROM used */ + MrxdmaSHIFT = 8, /* Max. DMA Burst Size */ + MrxdmaMASK = 0x00000700, + Mrxdmaunlimited = 0x00000700, + RxfthSHIFT = 13, /* Receive Buffer Length */ + RxfthMASK = 0x0000E000, + Rxfth256 = 0x00008000, + Rxfthnone = 0x0000E000, + Rer8 = 0x00010000, /* Accept Error Packets > 8 bytes */ + MulERINT = 0x01000000, /* Multiple Early Interrupt Select */ +}; + +enum { /* Cr9346 */ + Eedo = 0x01, /* */ + Eedi = 0x02, /* */ + Eesk = 0x04, /* */ + Eecs = 0x08, /* */ + Eem0 = 0x40, /* Operating Mode */ + Eem1 = 0x80, +}; + +enum { /* Phyar */ + DataMASK = 0x0000FFFF, /* 16-bit GMII/MII Register Data */ + DataSHIFT = 0, + RegaddrMASK = 0x001F0000, /* 5-bit GMII/MII Register Address */ + RegaddrSHIFT = 16, + PhyFlag = 0x80000000, /* */ +}; + +enum { /* Phystatus */ + Fd = 0x01, /* Full Duplex */ + Linksts = 0x02, /* Link Status */ + Speed10 = 0x04, /* */ + Speed100 = 0x08, /* */ + Speed1000 = 0x10, /* */ + Rxflow = 0x20, /* */ + Txflow = 0x40, /* */ + Entbi = 0x80, /* */ +}; + +enum { /* Cplusc */ + Mulrw = 0x0008, /* PCI Multiple R/W Enable */ + Dac = 0x0010, /* PCI Dual Address Cycle Enable */ + Rxchksum = 0x0020, /* Receive Checksum Offload Enable */ + Rxvlan = 0x0040, /* Receive VLAN De-tagging Enable */ + Endian = 0x0200, /* Endian Mode */ +}; + +typedef struct D D; /* Transmit/Receive Descriptor */ +struct D { + u32int control; + u32int vlan; + u32int addrlo; + u32int addrhi; +}; + +enum { /* Transmit Descriptor control */ + TxflMASK = 0x0000FFFF, /* Transmit Frame Length */ + TxflSHIFT = 0, + Tcps = 0x00010000, /* TCP Checksum Offload */ + Udpcs = 0x00020000, /* UDP Checksum Offload */ + Ipcs = 0x00040000, /* IP Checksum Offload */ + Lgsen = 0x08000000, /* Large Send */ +}; + +enum { /* Receive Descriptor control */ + RxflMASK = 0x00003FFF, /* Receive Frame Length */ + RxflSHIFT = 0, + Tcpf = 0x00004000, /* TCP Checksum Failure */ + Udpf = 0x00008000, /* UDP Checksum Failure */ + Ipf = 0x00010000, /* IP Checksum Failure */ + Pid0 = 0x00020000, /* Protocol ID0 */ + Pid1 = 0x00040000, /* Protocol ID1 */ + Crce = 0x00080000, /* CRC Error */ + Runt = 0x00100000, /* Runt Packet */ + Res = 0x00200000, /* Receive Error Summary */ + Rwt = 0x00400000, /* Receive Watchdog Timer Expired */ + Fovf = 0x00800000, /* FIFO Overflow */ + Bovf = 0x01000000, /* Buffer Overflow */ + Bar = 0x02000000, /* Broadcast Address Received */ + Pam = 0x04000000, /* Physical Address Matched */ + Mar = 0x08000000, /* Multicast Address Received */ +}; + +enum { /* General Descriptor control */ + Ls = 0x10000000, /* Last Segment Descriptor */ + Fs = 0x20000000, /* First Segment Descriptor */ + Eor = 0x40000000, /* End of Descriptor Ring */ + Own = 0x80000000, /* Ownership */ +}; + +/* + */ +enum { /* Ring sizes (<= 1024) */ + Ntd = 32, /* Transmit Ring */ + Nrd = 128, /* Receive Ring */ + + Mps = ROUNDUP(ETHERMAXTU+4, 128), +}; + +typedef struct Dtcc Dtcc; +struct Dtcc { + u64int txok; + u64int rxok; + u64int txer; + u32int rxer; + u16int misspkt; + u16int fae; + u32int tx1col; + u32int txmcol; + u64int rxokph; + u64int rxokbrd; + u32int rxokmu; + u16int txabt; + u16int txundrn; +}; + +enum { /* Variants */ + Rtl8100e = (0x8136<<16)|0x10EC, /* RTL810[01]E ? */ + Rtl8169c = (0x0116<<16)|0x16EC, /* RTL8169C+ (USR997902) */ + Rtl8169sc = (0x8167<<16)|0x10EC, /* RTL8169SC */ + Rtl8168b = (0x8168<<16)|0x10EC, /* RTL8168B */ + Rtl8169 = (0x8169<<16)|0x10EC, /* RTL8169 */ +}; + +typedef struct Ctlr Ctlr; +typedef struct Ctlr { + int port; + Pcidev* pcidev; + Ctlr* next; + int active; + + QLock alock; /* attach */ + Lock ilock; /* init */ + int init; /* */ + + int pciv; /* */ + int macv; /* MAC version */ + int phyv; /* PHY version */ + + Mii* mii; + + Lock tlock; /* transmit */ + D* td; /* descriptor ring */ + Block** tb; /* transmit buffers */ + int ntd; + + int tdh; /* head - producer index (host) */ + int tdt; /* tail - consumer index (NIC) */ + int ntdfree; + int ntq; + + int mtps; /* Max. Transmit Packet Size */ + + Lock rlock; /* receive */ + D* rd; /* descriptor ring */ + Block** rb; /* receive buffers */ + int nrd; + + int rdh; /* head - producer index (NIC) */ + int rdt; /* tail - consumer index (host) */ + int nrdfree; + + int tcr; /* transmit configuration register */ + int rcr; /* receive configuration register */ + int imr; + + QLock slock; /* statistics */ + Dtcc* dtcc; + uint txdu; + uint tcpf; + uint udpf; + uint ipf; + uint fovf; + uint ierrs; + uint rer; + uint rdu; + uint punlc; + uint fovw; +} Ctlr; + +static Ctlr* rtl8169ctlrhead; +static Ctlr* rtl8169ctlrtail; + +#define csr8r(c, r) (inb((c)->port+(r))) +#define csr16r(c, r) (ins((c)->port+(r))) +#define csr32r(c, r) (inl((c)->port+(r))) +#define csr8w(c, r, b) (outb((c)->port+(r), (u8int)(b))) +#define csr16w(c, r, w) (outs((c)->port+(r), (u16int)(w))) +#define csr32w(c, r, l) (outl((c)->port+(r), (u32int)(l))) + +static int +rtl8169miimir(Mii* mii, int pa, int ra) +{ + uint r; + int timeo; + Ctlr *ctlr; + + if(pa != 1) + return -1; + ctlr = mii->ctlr; + + r = (ra<<16) & RegaddrMASK; + csr32w(ctlr, Phyar, r); + delay(1); + for(timeo = 0; timeo < 2000; timeo++){ + if((r = csr32r(ctlr, Phyar)) & PhyFlag) + break; + microdelay(100); + } + if(!(r & PhyFlag)) + return -1; + + return (r & DataMASK)>>DataSHIFT; +} + +static int +rtl8169miimiw(Mii* mii, int pa, int ra, int data) +{ + uint r; + int timeo; + Ctlr *ctlr; + + if(pa != 1) + return -1; + ctlr = mii->ctlr; + + r = PhyFlag|((ra<<16) & RegaddrMASK)|((data<mii = malloc(sizeof(Mii))) == nil) + return -1; + ctlr->mii->mir = rtl8169miimir; + ctlr->mii->miw = rtl8169miimiw; + ctlr->mii->ctlr = ctlr; + + /* + * Get rev number out of Phyidr2 so can config properly. + * There's probably more special stuff for Macv0[234] needed here. + */ + ctlr->phyv = rtl8169miimir(ctlr->mii, 1, Phyidr2) & 0x0F; + if(ctlr->macv == Macv02){ + csr8w(ctlr, 0x82, 1); /* magic */ + rtl8169miimiw(ctlr->mii, 1, 0x0B, 0x0000); /* magic */ + } + + if(mii(ctlr->mii, (1<<1)) == 0 || (phy = ctlr->mii->curphy) == nil){ + free(ctlr->mii); + ctlr->mii = nil; + return -1; + } + print("oui %#ux phyno %d, macv = %#8.8ux phyv = %#4.4ux\n", + phy->oui, phy->phyno, ctlr->macv, ctlr->phyv); + + miiane(ctlr->mii, ~0, ~0, ~0); + + return 0; +} + +void +rtl8169promiscuous(void* arg, int on) +{ + Ether *edev; + Ctlr * ctlr; + + edev = arg; + ctlr = edev->ctlr; + ilock(&ctlr->ilock); + + if(on) + ctlr->rcr |= Aap; + else + ctlr->rcr &= ~Aap; + csr32w(ctlr, Rcr, ctlr->rcr); + iunlock(&ctlr->ilock); +} + +#ifndef FS +static long +rtl8169ifstat(Ether* edev, void* a, long n, ulong offset) +{ + char *p; + Ctlr *ctlr; + Dtcc *dtcc; + int i, l, r, timeo; + + ctlr = edev->ctlr; + qlock(&ctlr->slock); + + p = nil; + if(waserror()){ + qunlock(&ctlr->slock); + free(p); + nexterror(); + } + + csr32w(ctlr, Dtccr+4, 0); + csr32w(ctlr, Dtccr, PCIWADDR(ctlr->dtcc)|Cmd); + for(timeo = 0; timeo < 1000; timeo++){ + if(!(csr32r(ctlr, Dtccr) & Cmd)) + break; + delay(1); + } + if(csr32r(ctlr, Dtccr) & Cmd) + error(Eio); + dtcc = ctlr->dtcc; + + edev->oerrs = dtcc->txer; + edev->crcs = dtcc->rxer; + edev->frames = dtcc->fae; + edev->buffs = dtcc->misspkt; + edev->overflows = ctlr->txdu+ctlr->rdu; + + if(n == 0){ + qunlock(&ctlr->slock); + poperror(); + return 0; + } + + if((p = malloc(READSTR)) == nil) + error(Enomem); + + l = snprint(p, READSTR, "TxOk: %llud\n", dtcc->txok); + l += snprint(p+l, READSTR-l, "RxOk: %llud\n", dtcc->rxok); + l += snprint(p+l, READSTR-l, "TxEr: %llud\n", dtcc->txer); + l += snprint(p+l, READSTR-l, "RxEr: %ud\n", dtcc->rxer); + l += snprint(p+l, READSTR-l, "MissPkt: %ud\n", dtcc->misspkt); + l += snprint(p+l, READSTR-l, "FAE: %ud\n", dtcc->fae); + l += snprint(p+l, READSTR-l, "Tx1Col: %ud\n", dtcc->tx1col); + l += snprint(p+l, READSTR-l, "TxMCol: %ud\n", dtcc->txmcol); + l += snprint(p+l, READSTR-l, "RxOkPh: %llud\n", dtcc->rxokph); + l += snprint(p+l, READSTR-l, "RxOkBrd: %llud\n", dtcc->rxokbrd); + l += snprint(p+l, READSTR-l, "RxOkMu: %ud\n", dtcc->rxokmu); + l += snprint(p+l, READSTR-l, "TxAbt: %ud\n", dtcc->txabt); + l += snprint(p+l, READSTR-l, "TxUndrn: %ud\n", dtcc->txundrn); + + l += snprint(p+l, READSTR-l, "txdu: %ud\n", ctlr->txdu); + l += snprint(p+l, READSTR-l, "tcpf: %ud\n", ctlr->tcpf); + l += snprint(p+l, READSTR-l, "udpf: %ud\n", ctlr->udpf); + l += snprint(p+l, READSTR-l, "ipf: %ud\n", ctlr->ipf); + l += snprint(p+l, READSTR-l, "fovf: %ud\n", ctlr->fovf); + l += snprint(p+l, READSTR-l, "ierrs: %ud\n", ctlr->ierrs); + l += snprint(p+l, READSTR-l, "rer: %ud\n", ctlr->rer); + l += snprint(p+l, READSTR-l, "rdu: %ud\n", ctlr->rdu); + l += snprint(p+l, READSTR-l, "punlc: %ud\n", ctlr->punlc); + l += snprint(p+l, READSTR-l, "fovw: %ud\n", ctlr->fovw); + + l += snprint(p+l, READSTR-l, "tcr: %#8.8ux\n", ctlr->tcr); + l += snprint(p+l, READSTR-l, "rcr: %#8.8ux\n", ctlr->rcr); + + if(ctlr->mii != nil && ctlr->mii->curphy != nil){ + l += snprint(p+l, READSTR, "phy: "); + for(i = 0; i < NMiiPhyr; i++){ + if(i && ((i & 0x07) == 0)) + l += snprint(p+l, READSTR-l, "\n "); + r = miimir(ctlr->mii, i); + l += snprint(p+l, READSTR-l, " %4.4ux", r); + } + snprint(p+l, READSTR-l, "\n"); + } + + n = readstr(offset, a, n, p); + + qunlock(&ctlr->slock); + poperror(); + free(p); + + return n; +} +#endif + +static void +rtl8169halt(Ctlr* ctlr) +{ + csr8w(ctlr, Cr, 0); + csr16w(ctlr, Imr, 0); + csr16w(ctlr, Isr, ~0); +} + +static int +rtl8169reset(Ctlr* ctlr) +{ + u32int r; + int timeo; + + /* + * Soft reset the controller. + */ + csr8w(ctlr, Cr, Rst); + for(r = timeo = 0; timeo < 1000; timeo++){ + r = csr8r(ctlr, Cr); + if(!(r & Rst)) + break; + delay(1); + } + rtl8169halt(ctlr); + + if(r & Rst) + return -1; + return 0; +} + +static void +rtl8169replenish(Ctlr* ctlr) +{ + D *d; + int rdt; + Block *bp; + + rdt = ctlr->rdt; + while(NEXT(rdt, ctlr->nrd) != ctlr->rdh){ + d = &ctlr->rd[rdt]; + if(ctlr->rb[rdt] == nil){ + /* + * Simple allocation for now. + * This better be aligned on 8. + */ + bp = iallocb(Mps); + if(bp == nil){ + iprint("no available buffers\n"); + break; + } + ctlr->rb[rdt] = bp; + d->addrlo = PCIWADDR(bp->rp); + d->addrhi = 0; + } + coherence(); + d->control |= Own|Mps; + rdt = NEXT(rdt, ctlr->nrd); + ctlr->nrdfree++; + } + ctlr->rdt = rdt; +} + +static int +rtl8169init(Ether* edev) +{ + int i; + u32int r; + Block *bp; + Ctlr *ctlr; + u8int cplusc; + + ctlr = edev->ctlr; + ilock(&ctlr->ilock); + + rtl8169halt(ctlr); + + /* + * MAC Address. + * Must put chip into config register write enable mode. + */ + csr8w(ctlr, Cr9346, Eem1|Eem0); + r = (edev->ea[3]<<24)|(edev->ea[2]<<16)|(edev->ea[1]<<8)|edev->ea[0]; + csr32w(ctlr, Idr0, r); + r = (edev->ea[5]<<8)|edev->ea[4]; + csr32w(ctlr, Idr0+4, r); + + /* + * Transmitter. + */ + memset(ctlr->td, 0, sizeof(D)*ctlr->ntd); + ctlr->tdh = ctlr->tdt = 0; + ctlr->td[ctlr->ntd-1].control = Eor; + + /* + * Receiver. + * Need to do something here about the multicast filter. + */ + memset(ctlr->rd, 0, sizeof(D)*ctlr->nrd); + ctlr->nrdfree = ctlr->rdh = ctlr->rdt = 0; + ctlr->rd[ctlr->nrd-1].control = Eor; + + for(i = 0; i < ctlr->nrd; i++){ + if((bp = ctlr->rb[i]) != nil){ + ctlr->rb[i] = nil; + freeb(bp); + } + } + rtl8169replenish(ctlr); + ctlr->rcr = Rxfthnone|Mrxdmaunlimited|Ab|Apm; + + /* + * Mtps is in units of 128 except for the RTL8169 + * where is is 32. If using jumbo frames should be + * set to 0x3F. + * Setting Mulrw in Cplusc disables the Tx/Rx DMA burst + * settings in Tcr/Rcr; the (1<<14) is magic. + */ + ctlr->mtps = HOWMANY(Mps, 128); + cplusc = csr16r(ctlr, Cplusc) & ~(1<<14); + cplusc |= /*Rxchksum|*/Mulrw; + dprint("mac = %.2ux\n", ctlr->macv); + switch(ctlr->macv){ + default: + print("bad mac %.2ux\n", ctlr->macv); + return -1; + case Macv01: + ctlr->mtps = HOWMANY(Mps, 32); + break; + case Macv02: + case Macv03: + cplusc |= (1<<14); /* magic */ + break; + case Macv05: + /* + * This is interpreted from clearly bogus code + * in the manufacturer-supplied driver, it could + * be wrong. Untested. + */ + r = csr8r(ctlr, Config2) & 0x07; + if(r == 0x01) /* 66MHz PCI */ + csr32w(ctlr, 0x7C, 0x0007FFFF); /* magic */ + else + csr32w(ctlr, 0x7C, 0x0007FF00); /* magic */ + pciclrmwi(ctlr->pcidev); + break; + case Macv13: + /* + * This is interpreted from clearly bogus code + * in the manufacturer-supplied driver, it could + * be wrong. Untested. + */ + pcicfgw8(ctlr->pcidev, 0x68, 0x00); /* magic */ + pcicfgw8(ctlr->pcidev, 0x69, 0x08); /* magic */ + break; + case Macv04: + case Macv11: + case Macv12: + case Macv14: + case Macv15: + break; + } + + /* + * Enable receiver/transmitter. + * Need to do this first or some of the settings below + * won't take. + */ + switch(ctlr->pciv){ + default: + csr8w(ctlr, Cr, Te|Re); + csr32w(ctlr, Tcr, Ifg1|Ifg0|Mtxdmaunlimited); + csr32w(ctlr, Rcr, ctlr->rcr); + case Rtl8169sc: + case Rtl8168b: + break; + } + + /* + * Interrupts. + * Disable Tdu|Tok for now, the transmit routine will tidy. + * Tdu means the NIC ran out of descriptors to send, so it + * doesn't really need to ever be on. + */ + csr32w(ctlr, Timerint, 0); + ctlr->imr = Serr|Timeout|Fovw|Punlc|Rdu|Ter|Rer|Rok; + csr16w(ctlr, Imr, ctlr->imr); + + /* + * Clear missed-packet counter; + * initial early transmit threshold value; + * set the descriptor ring base addresses; + * set the maximum receive packet size; + * no early-receive interrupts. + */ + csr32w(ctlr, Mpc, 0); + csr8w(ctlr, Mtps, ctlr->mtps); + csr32w(ctlr, Tnpds+4, 0); + csr32w(ctlr, Tnpds, PCIWADDR(ctlr->td)); + csr32w(ctlr, Rdsar+4, 0); + csr32w(ctlr, Rdsar, PCIWADDR(ctlr->rd)); + csr16w(ctlr, Rms, Mps); + r = csr16r(ctlr, Mulint) & 0xF000; + csr16w(ctlr, Mulint, r); + csr16w(ctlr, Cplusc, cplusc); + + /* + * Set configuration. + */ + switch(ctlr->pciv){ + default: + break; + case Rtl8169sc: + csr16w(ctlr, 0xE2, 0); /* magic */ + csr8w(ctlr, Cr, Te|Re); + csr32w(ctlr, Tcr, Ifg1|Ifg0|Mtxdmaunlimited); + csr32w(ctlr, Rcr, ctlr->rcr); + break; + case Rtl8168b: + case Rtl8169c: + csr16w(ctlr, 0xE2, 0); /* magic */ + csr16w(ctlr, Cplusc, 0x2000); /* magic */ + csr8w(ctlr, Cr, Te|Re); + csr32w(ctlr, Tcr, Ifg1|Ifg0|Mtxdmaunlimited); + csr32w(ctlr, Rcr, ctlr->rcr); + csr16w(ctlr, Rms, 0x0800); + csr8w(ctlr, Mtps, 0x3F); + break; + } + ctlr->tcr = csr32r(ctlr, Tcr); + csr8w(ctlr, Cr9346, 0); + + iunlock(&ctlr->ilock); + +// rtl8169mii(ctlr); + + return 0; +} + +static void +rtl8169attach(Ether* edev) +{ + int timeo; + Ctlr *ctlr; + + ctlr = edev->ctlr; + qlock(&ctlr->alock); + if(ctlr->init == 0){ + /* + * Handle allocation/init errors here. + */ + ctlr->td = mallocalign(sizeof(D)*Ntd, 256, 0, 0); + ctlr->tb = malloc(Ntd*sizeof(Block*)); + ctlr->ntd = Ntd; + ctlr->rd = mallocalign(sizeof(D)*Nrd, 256, 0, 0); + ctlr->rb = malloc(Nrd*sizeof(Block*)); + ctlr->nrd = Nrd; + ctlr->dtcc = mallocalign(sizeof(Dtcc), 64, 0, 0); + rtl8169init(edev); + ctlr->init = 1; + } + qunlock(&ctlr->alock); + + /* + * Wait for link to be ready. + */ + for(timeo = 0; timeo < 3500; timeo++){ + if(miistatus(ctlr->mii) == 0) + break; + delay(10); + } +} + +static void +rtl8169link(Ether* edev) +{ + uint r; + Ctlr *ctlr; + + ctlr = edev->ctlr; + + /* + * Maybe the link changed - do we care very much? + * Could stall transmits if no link, maybe? + */ + if(!((r = csr8r(ctlr, Phystatus)) & Linksts)) + return; + + if(r & Speed10) + edev->mbps = 10; + else if(r & Speed100) + edev->mbps = 100; + else if(r & Speed1000) + edev->mbps = 1000; +} + +static void +rtl8169transmit(Ether* edev) +{ + D *d; + Block *bp; + Ctlr *ctlr; + int control, x; + + ctlr = edev->ctlr; + + ilock(&ctlr->tlock); + for(x = ctlr->tdh; ctlr->ntq > 0; x = NEXT(x, ctlr->ntd)){ + d = &ctlr->td[x]; + if((control = d->control) & Own) + break; + + /* + * Check errors and log here. + */ + USED(control); + + /* + * Free it up. + * Need to clean the descriptor here? Not really. + * Simple freeb for now (no chain and freeblist). + * Use ntq count for now. + */ + freeb(ctlr->tb[x]); + ctlr->tb[x] = nil; + d->control &= Eor; + + ctlr->ntq--; + } + ctlr->tdh = x; + + x = ctlr->tdt; + while(ctlr->ntq < (ctlr->ntd-1)){ + if((bp = etheroq(edev)) == nil) + break; + + d = &ctlr->td[x]; + d->addrlo = PCIWADDR(bp->rp); + d->addrhi = 0; + ctlr->tb[x] = bp; + coherence(); + d->control |= Own|Fs|Ls|((BLEN(bp)<ntd); + ctlr->ntq++; + } + if(x != ctlr->tdt){ + ctlr->tdt = x; + csr8w(ctlr, Tppoll, Npq); + } + else if(ctlr->ntq >= (ctlr->ntd-1)) + ctlr->txdu++; + + iunlock(&ctlr->tlock); +} + +static void +rtl8169receive(Ether* edev) +{ + D *d; + int rdh; + Block *bp; + Ctlr *ctlr; + u32int control; + + ctlr = edev->ctlr; + + rdh = ctlr->rdh; + for(;;){ + d = &ctlr->rd[rdh]; + + if(d->control & Own) + break; + + control = d->control; + if((control & (Fs|Ls|Res)) == (Fs|Ls)){ + bp = ctlr->rb[rdh]; + ctlr->rb[rdh] = nil; + SETWPCNT(bp, ((control & RxflMASK)>>RxflSHIFT)-4); + bp->next = nil; + +#ifndef FS + if(control & Fovf) + ctlr->fovf++; +#endif + + switch(control & (Pid1|Pid0)){ + default: + break; + case Pid0: + if(control & Tcpf){ + ctlr->tcpf++; + break; + } +#ifndef FS + bp->flag |= Btcpck; +#endif + break; + case Pid1: + if(control & Udpf){ + ctlr->udpf++; + break; + } +#ifndef FS + bp->flag |= Budpck; +#endif + break; + case Pid1|Pid0: + if(control & Ipf){ + ctlr->ipf++; + break; + } +#ifndef FS + bp->flag |= Bipck; +#endif + break; + } + ETHERIQ(edev, bp, 1); + } + else{ + /* + * Error stuff here. + print("control %#8.8ux\n", control); + */ + } + d->control &= Eor; + ctlr->nrdfree--; + rdh = NEXT(rdh, ctlr->nrd); + + if(ctlr->nrdfree < ctlr->nrd/2) + rtl8169replenish(ctlr); + } + ctlr->rdh = rdh; +} + +static void +rtl8169interrupt(Ureg*, void* arg) +{ + Ctlr *ctlr; + Ether *edev; + u32int isr; + + edev = arg; + ctlr = edev->ctlr; + + while((isr = csr16r(ctlr, Isr)) != 0 && isr != 0xFFFF){ + csr16w(ctlr, Isr, isr); + if((isr & ctlr->imr) == 0) + break; + if(isr & (Fovw|Punlc|Rdu|Rer|Rok)){ + rtl8169receive(edev); + if(!(isr & (Punlc|Rok))) + ctlr->ierrs++; + if(isr & Rer) + ctlr->rer++; + if(isr & Rdu) + ctlr->rdu++; + if(isr & Punlc) + ctlr->punlc++; + if(isr & Fovw) + ctlr->fovw++; + isr &= ~(Fovw|Rdu|Rer|Rok); + } + + if(isr & (Tdu|Ter|Tok)){ + rtl8169transmit(edev); + isr &= ~(Tdu|Ter|Tok); + } + + if(isr & Punlc){ + rtl8169link(edev); + isr &= ~Punlc; + } + + /* + * Some of the reserved bits get set sometimes... + */ + if(isr & (Serr|Timeout|Tdu|Fovw|Punlc|Rdu|Ter|Tok|Rer|Rok)) + panic("rtl8169interrupt: imr %#4.4ux isr %#4.4ux\n", + csr16r(ctlr, Imr), isr); + } +} + +static void +rtl8169pci(void) +{ + Pcidev *p; + Ctlr *ctlr; + int i, port; + + p = nil; + while(p = pcimatch(p, 0, 0)){ +#ifdef FS + if(p->ccru != ((0x02<<8)|0x00)) +#else + if(p->ccrb != 0x02 || p->ccru != 0) +#endif + continue; + + dprint(" pci: found vid %ux did %ux\n", p->vid, p->did); + switch(i = ((p->did<<16)|p->vid)){ + default: + continue; + case Rtl8100e: /* RTL810[01]E ? */ + case Rtl8169c: /* RTL8169C */ + case Rtl8169sc: /* RTL8169SC */ + case Rtl8168b: /* RTL8168B */ + case Rtl8169: /* RTL8169 */ + break; + case (0xC107<<16)|0x1259: /* Corega CG-LAPCIGT */ + i = Rtl8169; + break; + } + + port = p->mem[0].bar & ~0x01; + if(ioalloc(port, p->mem[0].size, 0, "rtl8169") < 0){ + print("rtl8169: port %#ux in use\n", port); + continue; + } + + ctlr = malloc(sizeof(Ctlr)); + ctlr->port = port; + ctlr->pcidev = p; + ctlr->pciv = i; + +#ifndef FS + if(pcigetpms(p) > 0){ + pcisetpms(p, 0); + + for(i = 0; i < 6; i++) + pcicfgw32(p, PciBAR0+i*4, p->mem[i].bar); + pcicfgw8(p, PciINTL, p->intl); + pcicfgw8(p, PciLTR, p->ltr); + pcicfgw8(p, PciCLS, p->cls); + pcicfgw16(p, PciPCR, p->pcr); + } +#endif + + if(rtl8169reset(ctlr)){ + iofree(port); + free(ctlr); + continue; + } + + /* + * Extract the chip hardware version, + * needed to configure each properly. + */ + ctlr->macv = csr32r(ctlr, Tcr) & HwveridMASK; + + rtl8169mii(ctlr); + + pcisetbme(p); + + if(rtl8169ctlrhead != nil) + rtl8169ctlrtail->next = ctlr; + else + rtl8169ctlrhead = ctlr; + rtl8169ctlrtail = ctlr; + } +} + +int +rtl8169pnp(Ether* edev) +{ + u32int r; + Ctlr *ctlr; + uchar ea[Eaddrlen]; + + if(rtl8169ctlrhead == nil) + rtl8169pci(); + + /* + * Any adapter matches if no edev->port is supplied, + * otherwise the ports must match. + */ + for(ctlr = rtl8169ctlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + if(edev->port == 0 || edev->port == ctlr->port){ + ctlr->active = 1; + break; + } + } + if(ctlr == nil) + return -1; + + edev->ctlr = ctlr; + edev->port = ctlr->port; + edev->irq = ctlr->pcidev->intl; + edev->tbdf = ctlr->pcidev->tbdf; + edev->mbps = 100; + + /* + * Check if the adapter's station address is to be overridden. + * If not, read it from the device and set in edev->ea. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, edev->ea, Eaddrlen) == 0){ + r = csr32r(ctlr, Idr0); + edev->ea[0] = r; + edev->ea[1] = r>>8; + edev->ea[2] = r>>16; + edev->ea[3] = r>>24; + r = csr32r(ctlr, Idr0+4); + edev->ea[4] = r; + edev->ea[5] = r>>8; + } + + edev->attach = rtl8169attach; + edev->transmit = rtl8169transmit; + edev->interrupt = rtl8169interrupt; +#ifndef FS + edev->ifstat = rtl8169ifstat; + + edev->arg = edev; + edev->promiscuous = rtl8169promiscuous; +#endif + rtl8169link(edev); + + return 0; +} + +void +ether8169link(void) +{ + addethercard("rtl8169", rtl8169pnp); +} diff -Nru /sys/src/fs/pc/ether82557.c /sys/src/fs/pc/ether82557.c --- /sys/src/fs/pc/ether82557.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/pc/ether82557.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,1363 @@ +/* + * Intel 82557 Fast Ethernet PCI Bus LAN Controller + * as found on the Intel EtherExpress PRO/100B. This chip is full + * of smarts, unfortunately they're not all in the right place. + * To do: + * the PCI scanning code could be made common to other adapters; + * auto-negotiation, full-duplex; + * optionally use memory-mapped registers; + * detach for PCI reset problems (also towards loadable drivers). + */ +#ifdef FS +#include "all.h" +#include "io.h" +#include "mem.h" +#include "../ip/ip.h" + +#else + +#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" +#endif /* FS */ + +#include "etherif.h" +#include "ethermii.h" +#include "compat.h" + +enum { + Nrfd = 64, /* receive frame area */ + Ncb = 64, /* maximum control blocks queued */ + + NullPointer = 0xFFFFFFFF, /* 82557 NULL pointer */ +}; + +enum { /* CSR */ + Status = 0x00, /* byte or word (word includes Ack) */ + Ack = 0x01, /* byte */ + CommandR = 0x02, /* byte or word (word includes Interrupt) */ + Interrupt = 0x03, /* byte */ + General = 0x04, /* dword */ + Port = 0x08, /* dword */ + Fcr = 0x0C, /* Flash control register */ + Ecr = 0x0E, /* EEPROM control register */ + Mcr = 0x10, /* MDI control register */ + Gstatus = 0x1D, /* General status register */ +}; + +enum { /* Status */ + RUidle = 0x0000, + RUsuspended = 0x0004, + RUnoresources = 0x0008, + RUready = 0x0010, + RUrbd = 0x0020, /* bit */ + RUstatus = 0x003F, /* mask */ + + CUidle = 0x0000, + CUsuspended = 0x0040, + CUactive = 0x0080, + CUstatus = 0x00C0, /* mask */ + + StatSWI = 0x0400, /* SoftWare generated Interrupt */ + StatMDI = 0x0800, /* MDI r/w done */ + StatRNR = 0x1000, /* Receive unit Not Ready */ + StatCNA = 0x2000, /* Command unit Not Active (Active->Idle) */ + StatFR = 0x4000, /* Finished Receiving */ + StatCX = 0x8000, /* Command eXecuted */ + StatTNO = 0x8000, /* Transmit NOT OK */ +}; + +enum { /* Command (byte) */ + CUnop = 0x00, + CUstart = 0x10, + CUresume = 0x20, + LoadDCA = 0x40, /* Load Dump Counters Address */ + DumpSC = 0x50, /* Dump Statistical Counters */ + LoadCUB = 0x60, /* Load CU Base */ + ResetSA = 0x70, /* Dump and Reset Statistical Counters */ + + RUstart = 0x01, + RUresume = 0x02, + RUabort = 0x04, + LoadHDS = 0x05, /* Load Header Data Size */ + LoadRUB = 0x06, /* Load RU Base */ + RBDresume = 0x07, /* Resume frame reception */ +}; + +enum { /* Interrupt (byte) */ + InterruptM = 0x01, /* interrupt Mask */ + InterruptSI = 0x02, /* Software generated Interrupt */ +}; + +enum { /* Ecr */ + EEsk = 0x01, /* serial clock */ + EEcs = 0x02, /* chip select */ + EEdi = 0x04, /* serial data in */ + EEdo = 0x08, /* serial data out */ + + EEstart = 0x04, /* start bit */ + EEread = 0x02, /* read opcode */ +}; + +enum { /* Mcr */ + MDIread = 0x08000000, /* read opcode */ + MDIwrite = 0x04000000, /* write opcode */ + MDIready = 0x10000000, /* ready bit */ + MDIie = 0x20000000, /* interrupt enable */ +}; + +typedef struct Rfd { + int field; + ulong link; + ulong rbd; + ushort count; + ushort size; + + uchar data[1700]; +} Rfd; + +enum { /* field */ + RfdCollision = 0x00000001, + RfdIA = 0x00000002, /* IA match */ + RfdRxerr = 0x00000010, /* PHY character error */ + RfdType = 0x00000020, /* Type frame */ + RfdRunt = 0x00000080, + RfdOverrun = 0x00000100, + RfdBuffer = 0x00000200, + RfdAlignment = 0x00000400, + RfdCRC = 0x00000800, + + RfdOK = 0x00002000, /* frame received OK */ + RfdC = 0x00008000, /* reception Complete */ + RfdSF = 0x00080000, /* Simplified or Flexible (1) Rfd */ + RfdH = 0x00100000, /* Header RFD */ + + RfdI = 0x20000000, /* Interrupt after completion */ + RfdS = 0x40000000, /* Suspend after completion */ + RfdEL = 0x80000000, /* End of List */ +}; + +enum { /* count */ + RfdF = 0x4000, + RfdEOF = 0x8000, +}; + +typedef struct Cb Cb; +typedef struct Cb { + ushort status; + ushort command; + ulong link; + union { + uchar data[24]; /* CbIAS + CbConfigure */ + struct { + ulong tbd; + ushort count; + uchar threshold; + uchar number; + + ulong tba; + ushort tbasz; + ushort pad; + }; + }; + + Block* bp; + Cb* next; +} Cb; + +enum { /* action command */ + CbU = 0x1000, /* transmit underrun */ + CbOK = 0x2000, /* DMA completed OK */ + CbC = 0x8000, /* execution Complete */ + + CbNOP = 0x0000, + CbIAS = 0x0001, /* Individual Address Setup */ + CbConfigure = 0x0002, + CbMAS = 0x0003, /* Multicast Address Setup */ + CbTransmit = 0x0004, + CbDump = 0x0006, + CbDiagnose = 0x0007, + CbCommand = 0x0007, /* mask */ + + CbSF = 0x0008, /* Flexible-mode CbTransmit */ + + CbI = 0x2000, /* Interrupt after completion */ + CbS = 0x4000, /* Suspend after completion */ + CbEL = 0x8000, /* End of List */ +}; + +enum { /* CbTransmit count */ + CbEOF = 0x8000, +}; + +typedef struct Ctlr Ctlr; +typedef struct Ctlr { + Lock slock; /* attach */ + int state; + + int port; + Pcidev* pcidev; + Ctlr* next; + int active; + + int eepromsz; /* address size in bits */ + ushort* eeprom; + + Lock miilock; + + int tick; + + Lock rlock; /* registers */ + int command; /* last command issued */ + + Block* rfdhead; /* receive side */ + Block* rfdtail; + int nrfd; + + Lock cblock; /* transmit side */ + int action; + int nop; + uchar configdata[24]; + int threshold; + int ncb; + Cb* cbr; + Cb* cbhead; + Cb* cbtail; + int cbq; + int cbqmax; + int cbqmaxhw; + + Rendez timer; /* for watchdog */ + + Lock dlock; /* dump statistical counters */ + ulong dump[17]; +} Ctlr; + +static Ctlr* ctlrhead; +static Ctlr* ctlrtail; + +static uchar configdata[24] = { + 0x16, /* byte count */ + 0x08, /* Rx/Tx FIFO limit */ + 0x00, /* adaptive IFS */ + 0x00, + 0x00, /* Rx DMA maximum byte count */ +// 0x80, /* Tx DMA maximum byte count */ + 0x00, /* Tx DMA maximum byte count */ + 0x32, /* !late SCB, CNA interrupts */ + 0x03, /* discard short Rx frames */ + 0x00, /* 503/MII */ + + 0x00, + 0x2E, /* normal operation, NSAI */ + 0x00, /* linear priority */ + 0x60, /* inter-frame spacing */ + 0x00, + 0xF2, + 0xC8, /* 503, promiscuous mode off */ + 0x00, + 0x40, + 0xF3, /* transmit padding enable */ + 0x80, /* full duplex pin enable */ + 0x3F, /* no Multi IA */ + 0x05, /* no Multi Cast ALL */ +}; + +#define csr8r(c, r) (inb((c)->port+(r))) +#define csr16r(c, r) (ins((c)->port+(r))) +#define csr32r(c, r) (inl((c)->port+(r))) +#define csr8w(c, r, b) (outb((c)->port+(r), (int)(b))) +#define csr16w(c, r, w) (outs((c)->port+(r), (ushort)(w))) +#define csr32w(c, r, l) (outl((c)->port+(r), (ulong)(l))) + +static void +command(Ctlr* ctlr, int c, int v) +{ + int timeo; + + ilock(&ctlr->rlock); + + /* + * Only back-to-back CUresume can be done + * without waiting for any previous command to complete. + * This should be the common case. + * Unfortunately there's a chip errata where back-to-back + * CUresumes can be lost, the fix is to always wait. + if(c == CUresume && ctlr->command == CUresume){ + csr8w(ctlr, CommandR, c); + iunlock(&ctlr->rlock); + return; + } + */ + + for(timeo = 0; timeo < 100; timeo++){ + if(!csr8r(ctlr, CommandR)) + break; + microdelay(1); + } + if(timeo >= 100){ + ctlr->command = -1; + iunlock(&ctlr->rlock); + iprint("i82557: command %#ux %#ux timeout\n", c, v); + return; + } + + switch(c){ + + case CUstart: + case LoadDCA: + case LoadCUB: + case RUstart: + case LoadHDS: + case LoadRUB: + csr32w(ctlr, General, v); + break; + + /* + case CUnop: + case CUresume: + case DumpSC: + case ResetSA: + case RUresume: + case RUabort: + */ + default: + break; + } + csr8w(ctlr, CommandR, c); + ctlr->command = c; + + iunlock(&ctlr->rlock); +} + +static Block* +rfdalloc(ulong link) +{ + Block *bp; + Rfd *rfd; + + if(bp = iallocb(sizeof(Rfd))){ + rfd = (Rfd*)bp->rp; + rfd->field = 0; + rfd->link = link; + rfd->rbd = NullPointer; + rfd->count = 0; + rfd->size = sizeof(Etherpkt); + } + + return bp; +} + +#ifdef FS +static int +return0(void*) +{ + return 0; +} +#endif + +static void +watchdog(PROCARG(void* arg)) +{ + Ether *ether; + Ctlr *ctlr; + static void txstart(Ether*); + static Rendez timer; /* for FS */ + + ether = GETARG(arg); + for(;;){ + tsleep(&timer, return0, 0, 4000); + + /* + * Hmmm. This doesn't seem right. Currently + * the device can't be disabled but it may be in + * the future. + */ + ctlr = ether->ctlr; + if(ctlr == nil || ctlr->state == 0){ +#ifdef FS + print("i82557: watchdog: exiting\n"); + for (;;) + tsleep(&timer, return0, 0, 10000); +#else + print("%s: exiting\n", up->text); + pexit("disabled", 0); +#endif + } + + ilock(&ctlr->cblock); + if(ctlr->tick++){ + ctlr->action = CbMAS; + txstart(ether); + } + iunlock(&ctlr->cblock); + } +} + +static void +attach(Ether* ether) +{ + Ctlr *ctlr; + char name[KNAMELEN]; + + ctlr = ether->ctlr; + lock(&ctlr->slock); + if(ctlr->state == 0){ + ilock(&ctlr->rlock); + csr8w(ctlr, Interrupt, 0); + iunlock(&ctlr->rlock); + command(ctlr, RUstart, PADDR(ctlr->rfdhead->rp)); + ctlr->state = 1; + + /* + * Start the watchdog timer for the receive lockup errata + * unless the EEPROM compatibility word indicates it may be + * omitted. + */ + if((ctlr->eeprom[0x03] & 0x0003) != 0x0003){ + snprint(name, KNAMELEN, "#l%dwatchdog", ether->ctlrno); + kproc(name, watchdog, ether); + } + } + unlock(&ctlr->slock); +} + +#ifndef FS +static long +ifstat(Ether* ether, void* a, long n, ulong offset) +{ + char *p; + int i, len, phyaddr; + Ctlr *ctlr; + ulong dump[17]; + + ctlr = ether->ctlr; + lock(&ctlr->dlock); + + /* + * Start the command then + * wait for completion status, + * should be 0xA005. + */ + ctlr->dump[16] = 0; + command(ctlr, DumpSC, 0); + while(ctlr->dump[16] == 0) + ; + + ether->oerrs = ctlr->dump[1]+ctlr->dump[2]+ctlr->dump[3]; + ether->crcs = ctlr->dump[10]; + ether->frames = ctlr->dump[11]; + ether->buffs = ctlr->dump[12]+ctlr->dump[15]; + ether->overflows = ctlr->dump[13]; + + if(n == 0){ + unlock(&ctlr->dlock); + return 0; + } + + memmove(dump, ctlr->dump, sizeof(dump)); + unlock(&ctlr->dlock); + + p = malloc(READSTR); + len = snprint(p, READSTR, "transmit good frames: %lud\n", dump[0]); + len += snprint(p+len, READSTR-len, "transmit maximum collisions errors: %lud\n", dump[1]); + len += snprint(p+len, READSTR-len, "transmit late collisions errors: %lud\n", dump[2]); + len += snprint(p+len, READSTR-len, "transmit underrun errors: %lud\n", dump[3]); + len += snprint(p+len, READSTR-len, "transmit lost carrier sense: %lud\n", dump[4]); + len += snprint(p+len, READSTR-len, "transmit deferred: %lud\n", dump[5]); + len += snprint(p+len, READSTR-len, "transmit single collisions: %lud\n", dump[6]); + len += snprint(p+len, READSTR-len, "transmit multiple collisions: %lud\n", dump[7]); + len += snprint(p+len, READSTR-len, "transmit total collisions: %lud\n", dump[8]); + len += snprint(p+len, READSTR-len, "receive good frames: %lud\n", dump[9]); + len += snprint(p+len, READSTR-len, "receive CRC errors: %lud\n", dump[10]); + len += snprint(p+len, READSTR-len, "receive alignment errors: %lud\n", dump[11]); + len += snprint(p+len, READSTR-len, "receive resource errors: %lud\n", dump[12]); + len += snprint(p+len, READSTR-len, "receive overrun errors: %lud\n", dump[13]); + len += snprint(p+len, READSTR-len, "receive collision detect errors: %lud\n", dump[14]); + len += snprint(p+len, READSTR-len, "receive short frame errors: %lud\n", dump[15]); + len += snprint(p+len, READSTR-len, "nop: %d\n", ctlr->nop); + if(ctlr->cbqmax > ctlr->cbqmaxhw) + ctlr->cbqmaxhw = ctlr->cbqmax; + len += snprint(p+len, READSTR-len, "cbqmax: %d\n", ctlr->cbqmax); + ctlr->cbqmax = 0; + len += snprint(p+len, READSTR-len, "threshold: %d\n", ctlr->threshold); + + len += snprint(p+len, READSTR-len, "eeprom:"); + for(i = 0; i < (1<eepromsz); i++){ + if(i && ((i & 0x07) == 0)) + len += snprint(p+len, READSTR-len, "\n "); + len += snprint(p+len, READSTR-len, " %4.4ux", ctlr->eeprom[i]); + } + + if((ctlr->eeprom[6] & 0x1F00) && !(ctlr->eeprom[6] & 0x8000)){ + phyaddr = ctlr->eeprom[6] & 0x00FF; + len += snprint(p+len, READSTR-len, "\nphy %2d:", phyaddr); + for(i = 0; i < 6; i++){ + static int miir(Ctlr*, int, int); + + len += snprint(p+len, READSTR-len, " %4.4ux", + miir(ctlr, phyaddr, i)); + } + } + + snprint(p+len, READSTR-len, "\n"); + n = readstr(offset, a, n, p); + free(p); + + return n; +} +#endif + +static void +txstart(Ether* ether) +{ + Ctlr *ctlr; + Block *bp; + Cb *cb; + + ctlr = ether->ctlr; + while(ctlr->cbq < (ctlr->ncb-1)){ + cb = ctlr->cbhead->next; + if(ctlr->action == 0){ + bp = etheroq(ether); + if(bp == nil) + break; + + cb->command = CbS|CbSF|CbTransmit; + cb->tbd = PADDR(&cb->tba); + cb->count = 0; + cb->threshold = ctlr->threshold; + cb->number = 1; + cb->tba = PADDR(bp->rp); + cb->bp = bp; + cb->tbasz = BLEN(bp); + } + else if(ctlr->action == CbConfigure){ + cb->command = CbS|CbConfigure; + memmove(cb->data, ctlr->configdata, sizeof(ctlr->configdata)); + ctlr->action = 0; + } + else if(ctlr->action == CbIAS){ + cb->command = CbS|CbIAS; + memmove(cb->data, ether->ea, Eaddrlen); + ctlr->action = 0; + } + else if(ctlr->action == CbMAS){ + cb->command = CbS|CbMAS; + memset(cb->data, 0, sizeof(cb->data)); + ctlr->action = 0; + } + else{ + print("#l%d: action %#ux\n", ether->ctlrno, ctlr->action); + ctlr->action = 0; + break; + } + cb->status = 0; + + coherence(); + ctlr->cbhead->command &= ~CbS; + ctlr->cbhead = cb; + ctlr->cbq++; + } + + /* + * Workaround for some broken HUB chips + * when connected at 10Mb/s half-duplex. + */ + if(ctlr->nop){ + command(ctlr, CUnop, 0); + microdelay(1); + } + command(ctlr, CUresume, 0); + + if(ctlr->cbq > ctlr->cbqmax) + ctlr->cbqmax = ctlr->cbq; +} + +static void +configure(Ether* ether, int promiscuous) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(&ctlr->cblock); + if(promiscuous){ + ctlr->configdata[6] |= 0x80; /* Save Bad Frames */ + //ctlr->configdata[6] &= ~0x40; /* !Discard Overrun Rx Frames */ + ctlr->configdata[7] &= ~0x01; /* !Discard Short Rx Frames */ + ctlr->configdata[15] |= 0x01; /* Promiscuous mode */ + ctlr->configdata[18] &= ~0x01; /* (!Padding enable?), !stripping enable */ + ctlr->configdata[21] |= 0x08; /* Multi Cast ALL */ + } + else{ + ctlr->configdata[6] &= ~0x80; + //ctlr->configdata[6] |= 0x40; + ctlr->configdata[7] |= 0x01; + ctlr->configdata[15] &= ~0x01; + ctlr->configdata[18] |= 0x01; /* 0x03? */ + ctlr->configdata[21] &= ~0x08; + } + ctlr->action = CbConfigure; + txstart(ether); + iunlock(&ctlr->cblock); +} + +static void +promiscuous(void* arg, int on) +{ + configure(arg, on); +} + +static void +multicast(void* arg, uchar *addr, int on) +{ + USED(addr, on); + configure(arg, 1); +} + +static void +transmit(Ether* ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(&ctlr->cblock); + txstart(ether); + iunlock(&ctlr->cblock); +} + +static void +receive(Ether* ether) +{ + Rfd *rfd; + Ctlr *ctlr; + int count; + Block *bp, *pbp, *xbp; + + ctlr = ether->ctlr; + bp = ctlr->rfdhead; + for(rfd = (Rfd*)bp->rp; rfd->field & RfdC; rfd = (Rfd*)bp->rp){ + /* + * If it's an OK receive frame + * 1) save the count + * 2) if it's small, try to allocate a block and copy + * the data, then adjust the necessary fields for reuse; + * 3) if it's big, try to allocate a new Rfd and if + * successful + * adjust the received buffer pointers for the + * actual data received; + * initialise the replacement buffer to point to + * the next in the ring; + * initialise bp to point to the replacement; + * 4) if there's a good packet, pass it on for disposal. + */ + if(rfd->field & RfdOK){ + pbp = nil; + count = rfd->count & 0x3FFF; + if((count < ETHERMAXTU/4) && (pbp = iallocb(count))){ + memmove(pbp->rp, bp->rp+offsetof(Rfd, data[0]), count); + SETWPCNT(pbp, count); + rfd->count = 0; + rfd->field = 0; + } + else if(xbp = rfdalloc(rfd->link)){ + bp->rp += offsetof(Rfd, data[0]); + SETWPCNT(bp, count); + + xbp->next = bp->next; + bp->next = 0; + + pbp = bp; + bp = xbp; + } + if(pbp != nil) + ETHERIQ(ether, pbp, 1); + } + else{ + rfd->count = 0; + rfd->field = 0; + } + + /* + * The ring tail pointer follows the head with with one + * unused buffer in between to defeat hardware prefetch; + * once the tail pointer has been bumped on to the next + * and the new tail has the Suspend bit set, it can be + * removed from the old tail buffer. + * As a replacement for the current head buffer may have + * been allocated above, ensure that the new tail points + * to it (next and link). + */ + rfd = (Rfd*)ctlr->rfdtail->rp; + ctlr->rfdtail = ctlr->rfdtail->next; + ctlr->rfdtail->next = bp; + ((Rfd*)ctlr->rfdtail->rp)->link = PADDR(bp->rp); + ((Rfd*)ctlr->rfdtail->rp)->field |= RfdS; + coherence(); + rfd->field &= ~RfdS; + + /* + * Finally done with the current (possibly replaced) + * head, move on to the next and maintain the sentinel + * between tail and head. + */ + ctlr->rfdhead = bp->next; + bp = ctlr->rfdhead; + } +} + +static void +interrupt(Ureg*, void* arg) +{ + Cb* cb; + Ctlr *ctlr; + Ether *ether; + int status; + + ether = arg; + ctlr = ether->ctlr; + + for(;;){ + ilock(&ctlr->rlock); + status = csr16r(ctlr, Status); + csr8w(ctlr, Ack, (status>>8) & 0xFF); + iunlock(&ctlr->rlock); + + if(!(status & (StatCX|StatFR|StatCNA|StatRNR|StatMDI|StatSWI))) + break; + + /* + * If the watchdog timer for the receiver lockup errata is running, + * let it know the receiver is active. + */ + if(status & (StatFR|StatRNR)){ + ilock(&ctlr->cblock); + ctlr->tick = 0; + iunlock(&ctlr->cblock); + } + + if(status & StatFR){ + receive(ether); + status &= ~StatFR; + } + + if(status & StatRNR){ + command(ctlr, RUresume, 0); + status &= ~StatRNR; + } + + if(status & StatCNA){ + ilock(&ctlr->cblock); + + cb = ctlr->cbtail; + while(ctlr->cbq){ + if(!(cb->status & CbC)) + break; + if(cb->bp){ + freeb(cb->bp); + cb->bp = nil; + } + if((cb->status & CbU) && ctlr->threshold < 0xE0) + ctlr->threshold++; + + ctlr->cbq--; + cb = cb->next; + } + ctlr->cbtail = cb; + + txstart(ether); + iunlock(&ctlr->cblock); + + status &= ~StatCNA; + } + + if(status & (StatCX|StatFR|StatCNA|StatRNR|StatMDI|StatSWI)) + panic("#l%d: status %#ux\n", ether->ctlrno, status); + } +} + +static void +ctlrinit(Ctlr* ctlr) +{ + int i; + Block *bp; + Rfd *rfd; + ulong link; + + /* + * Create the Receive Frame Area (RFA) as a ring of allocated + * buffers. + * A sentinel buffer is maintained between the last buffer in + * the ring (marked with RfdS) and the head buffer to defeat the + * hardware prefetch of the next RFD and allow dynamic buffer + * allocation. + */ + link = NullPointer; + for(i = 0; i < Nrfd; i++){ + bp = rfdalloc(link); + if(ctlr->rfdhead == nil) + ctlr->rfdtail = bp; + bp->next = ctlr->rfdhead; + ctlr->rfdhead = bp; + link = PADDR(bp->rp); + } + ctlr->rfdtail->next = ctlr->rfdhead; + rfd = (Rfd*)ctlr->rfdtail->rp; + rfd->link = PADDR(ctlr->rfdhead->rp); + rfd->field |= RfdS; + ctlr->rfdhead = ctlr->rfdhead->next; + + /* + * Create a ring of control blocks for the + * transmit side. + */ + ilock(&ctlr->cblock); + ctlr->cbr = malloc(ctlr->ncb*sizeof(Cb)); + for(i = 0; i < ctlr->ncb; i++){ + ctlr->cbr[i].status = CbC|CbOK; + ctlr->cbr[i].command = CbS|CbNOP; + ctlr->cbr[i].link = PADDR(&ctlr->cbr[NEXT(i, ctlr->ncb)].status); + ctlr->cbr[i].next = &ctlr->cbr[NEXT(i, ctlr->ncb)]; + } + ctlr->cbhead = ctlr->cbr; + ctlr->cbtail = ctlr->cbr; + ctlr->cbq = 0; + + memmove(ctlr->configdata, configdata, sizeof(configdata)); + ctlr->threshold = 80; + ctlr->tick = 0; + + iunlock(&ctlr->cblock); +} + +static int +miir(Ctlr* ctlr, int phyadd, int regadd) +{ + int mcr, timo; + + lock(&ctlr->miilock); + csr32w(ctlr, Mcr, MDIread|(phyadd<<21)|(regadd<<16)); + mcr = 0; + for(timo = 64; timo; timo--){ + mcr = csr32r(ctlr, Mcr); + if(mcr & MDIready) + break; + microdelay(1); + } + unlock(&ctlr->miilock); + + if(mcr & MDIready) + return mcr & 0xFFFF; + + return -1; +} + +static int +miiw(Ctlr* ctlr, int phyadd, int regadd, int data) +{ + int mcr, timo; + + lock(&ctlr->miilock); + csr32w(ctlr, Mcr, MDIwrite|(phyadd<<21)|(regadd<<16)|(data & 0xFFFF)); + mcr = 0; + for(timo = 64; timo; timo--){ + mcr = csr32r(ctlr, Mcr); + if(mcr & MDIready) + break; + microdelay(1); + } + unlock(&ctlr->miilock); + + if(mcr & MDIready) + return 0; + + return -1; +} + +static int +hy93c46r(Ctlr* ctlr, int r) +{ + int data, i, op, size; + + /* + * Hyundai HY93C46 or equivalent serial EEPROM. + * This sequence for reading a 16-bit register 'r' + * in the EEPROM is taken straight from Section + * 3.3.4.2 of the Intel 82557 User's Guide. + */ +reread: + csr16w(ctlr, Ecr, EEcs); + op = EEstart|EEread; + for(i = 2; i >= 0; i--){ + data = (((op>>i) & 0x01)<<2)|EEcs; + csr16w(ctlr, Ecr, data); + csr16w(ctlr, Ecr, data|EEsk); + microdelay(1); + csr16w(ctlr, Ecr, data); + microdelay(1); + } + + /* + * First time through must work out the EEPROM size. + */ + if((size = ctlr->eepromsz) == 0) + size = 8; + + for(size = size-1; size >= 0; size--){ + data = (((r>>size) & 0x01)<<2)|EEcs; + csr16w(ctlr, Ecr, data); + csr16w(ctlr, Ecr, data|EEsk); + delay(1); + csr16w(ctlr, Ecr, data); + microdelay(1); + if(!(csr16r(ctlr, Ecr) & EEdo)) + break; + } + + data = 0; + for(i = 15; i >= 0; i--){ + csr16w(ctlr, Ecr, EEcs|EEsk); + microdelay(1); + if(csr16r(ctlr, Ecr) & EEdo) + data |= (1<eepromsz == 0){ + ctlr->eepromsz = 8-size; + ctlr->eeprom = malloc((1<eepromsz)*sizeof(ushort)); + goto reread; + } + + return data; +} + +static void +i82557pci(void) +{ + Pcidev *p; + Ctlr *ctlr; + int nop, port; + + p = nil; + nop = 0; + while(p = pcimatch(p, 0x8086, 0)){ + switch(p->did){ + default: + continue; + case 0x1031: /* Intel 82562EM */ + case 0x1050: /* Intel 82562EZ */ + case 0x1039: /* Intel 82801BD PRO/100 VE */ + case 0x103A: /* Intel 82562 PRO/100 VE */ + case 0x103D: /* Intel 82562 PRO/100 VE */ + case 0x1064: /* Intel 82562 PRO/100 VE */ + case 0x2449: /* Intel 82562ET */ + nop = 1; + /*FALLTHROUGH*/ + case 0x1209: /* Intel 82559ER */ + case 0x1229: /* Intel 8255[789] */ + case 0x1030: /* Intel 82559 InBusiness 10/100 */ + break; + } +#ifndef FS + if(pcigetpms(p) > 0){ + int i; + + pcisetpms(p, 0); + + for(i = 0; i < 6; i++) + pcicfgw32(p, PciBAR0+i*4, p->mem[i].bar); + pcicfgw8(p, PciINTL, p->intl); + pcicfgw8(p, PciLTR, p->ltr); + pcicfgw8(p, PciCLS, p->cls); + pcicfgw16(p, PciPCR, p->pcr); + } +#endif + /* + * bar[0] is the memory-mapped register address (4KB), + * bar[1] is the I/O port register address (32 bytes) and + * bar[2] is for the flash ROM (1MB). + */ + port = p->mem[1].bar & ~0x01; + if(ioalloc(port, p->mem[1].size, 0, "i82557") < 0){ + print("i82557: port %#ux in use\n", port); + continue; + } + + ctlr = malloc(sizeof(Ctlr)); + ctlr->port = port; + ctlr->pcidev = p; + ctlr->nop = nop; + + if(ctlrhead != nil) + ctlrtail->next = ctlr; + else + ctlrhead = ctlr; + ctlrtail = ctlr; + + pcisetbme(p); + } +} + +static char* mediatable[9] = { + "10BASE-T", /* TP */ + "10BASE-2", /* BNC */ + "10BASE-5", /* AUI */ + "100BASE-TX", + "10BASE-TFD", + "100BASE-TXFD", + "100BASE-T4", + "100BASE-FX", + "100BASE-FXFD", +}; + +static int +scanphy(Ctlr* ctlr) +{ + int i, oui, x; + + for(i = 0; i < 32; i++){ + if((oui = miir(ctlr, i, 2)) == -1 || oui == 0 || oui == 0xFFFF) + continue; + oui <<= 6; + x = miir(ctlr, i, 3); + oui |= x>>10; + //print("phy%d: oui %#ux reg1 %#ux\n", i, oui, miir(ctlr, i, 1)); + + ctlr->eeprom[6] = i; + if(oui == 0xAA00) + ctlr->eeprom[6] |= 0x07<<8; + else if(oui == 0x80017){ + if(x & 0x01) + ctlr->eeprom[6] |= 0x0A<<8; + else + ctlr->eeprom[6] |= 0x04<<8; + } + return i; + } + return -1; +} + +static void +shutdown(Ether* ether) +{ + Ctlr *ctlr = ether->ctlr; + +print("ether82557 shutting down\n"); + csr32w(ctlr, Port, 0); + delay(1); + csr8w(ctlr, Interrupt, InterruptM); +} + + +int +etheri82557reset(Ether* ether) +{ + int anar, anlpar, bmcr, bmsr, i, k, medium, phyaddr, x; + unsigned short sum; + uchar ea[Eaddrlen]; + Ctlr *ctlr; + + if(ctlrhead == nil) + i82557pci(); + + /* + * Any adapter matches if no ether->port is supplied, + * otherwise the ports must match. + */ + for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + if(ether->port == 0 || ether->port == ctlr->port){ + ctlr->active = 1; + break; + } + } + if(ctlr == nil) + return -1; + + /* + * Initialise the Ctlr structure. + * Perform a software reset after which should ensure busmastering + * is still enabled. The EtherExpress PRO/100B appears to leave + * the PCI configuration alone (see the 'To do' list above) so punt + * for now. + * Load the RUB and CUB registers for linear addressing (0). + */ + ether->ctlr = ctlr; + ether->port = ctlr->port; + ether->irq = ctlr->pcidev->intl; + ether->tbdf = ctlr->pcidev->tbdf; + + ilock(&ctlr->rlock); + csr32w(ctlr, Port, 0); + delay(1); + csr8w(ctlr, Interrupt, InterruptM); + iunlock(&ctlr->rlock); + + command(ctlr, LoadRUB, 0); + command(ctlr, LoadCUB, 0); + command(ctlr, LoadDCA, PADDR(ctlr->dump)); + + /* + * Initialise the receive frame, transmit ring and configuration areas. + */ + ctlr->ncb = Ncb; + ctlrinit(ctlr); + + /* + * Read the EEPROM. + * Do a dummy read first to get the size + * and allocate ctlr->eeprom. + */ + hy93c46r(ctlr, 0); + sum = 0; + for(i = 0; i < (1<eepromsz); i++){ + x = hy93c46r(ctlr, i); + ctlr->eeprom[i] = x; + sum += x; + } + if(sum != 0xBABA) + print("#l%d: EEPROM checksum - %#4.4ux\n", ether->ctlrno, sum); + + /* + * Eeprom[6] indicates whether there is a PHY and whether + * it's not 10Mb-only, in which case use the given PHY address + * to set any PHY specific options and determine the speed. + * Unfortunately, sometimes the EEPROM is blank except for + * the ether address and checksum; in this case look at the + * controller type and if it's am 82558 or 82559 it has an + * embedded PHY so scan for that. + * If no PHY, assume 82503 (serial) operation. + */ + if((ctlr->eeprom[6] & 0x1F00) && !(ctlr->eeprom[6] & 0x8000)) + phyaddr = ctlr->eeprom[6] & 0x00FF; + else + switch(ctlr->pcidev->rid){ + case 0x01: /* 82557 A-step */ + case 0x02: /* 82557 B-step */ + case 0x03: /* 82557 C-step */ + default: + phyaddr = -1; + break; + case 0x04: /* 82558 A-step */ + case 0x05: /* 82558 B-step */ + case 0x06: /* 82559 A-step */ + case 0x07: /* 82559 B-step */ + case 0x08: /* 82559 C-step */ + case 0x09: /* 82559ER A-step */ + phyaddr = scanphy(ctlr); + break; + } + if(phyaddr >= 0){ + /* + * Resolve the highest common ability of the two + * link partners. In descending order: + * 0x0100 100BASE-TX Full Duplex + * 0x0200 100BASE-T4 + * 0x0080 100BASE-TX + * 0x0040 10BASE-T Full Duplex + * 0x0020 10BASE-T + */ + anar = miir(ctlr, phyaddr, 0x04); + anlpar = miir(ctlr, phyaddr, 0x05) & 0x03E0; + anar &= anlpar; + bmcr = 0; + if(anar & 0x380) + bmcr = 0x2000; + if(anar & 0x0140) + bmcr |= 0x0100; + + switch((ctlr->eeprom[6]>>8) & 0x001F){ + + case 0x04: /* DP83840 */ + case 0x0A: /* DP83840A */ + /* + * The DP83840[A] requires some tweaking for + * reliable operation. + * The manual says bit 10 should be unconditionally + * set although it supposedly only affects full-duplex + * operation (an & 0x0140). + */ + x = miir(ctlr, phyaddr, 0x17) & ~0x0520; + x |= 0x0420; + for(i = 0; i < ether->nopt; i++){ + if(cistrcmp(ether->opt[i], "congestioncontrol")) + continue; + x |= 0x0100; + break; + } + miiw(ctlr, phyaddr, 0x17, x); + + /* + * If the link partner can't autonegotiate, determine + * the speed from elsewhere. + */ + if(anlpar == 0){ + miir(ctlr, phyaddr, 0x01); + bmsr = miir(ctlr, phyaddr, 0x01); + x = miir(ctlr, phyaddr, 0x19); + if((bmsr & 0x0004) && !(x & 0x0040)) + bmcr = 0x2000; + } + break; + + case 0x07: /* Intel 82555 */ + /* + * Auto-negotiation may fail if the other end is + * a DP83840A and the cable is short. + */ + miir(ctlr, phyaddr, 0x01); + bmsr = miir(ctlr, phyaddr, 0x01); + if((miir(ctlr, phyaddr, 0) & 0x1000) && !(bmsr & 0x0020)){ + miiw(ctlr, phyaddr, 0x1A, 0x2010); + x = miir(ctlr, phyaddr, 0); + miiw(ctlr, phyaddr, 0, 0x0200|x); + for(i = 0; i < 3000; i++){ + delay(1); + if(miir(ctlr, phyaddr, 0x01) & 0x0020) + break; + } + miiw(ctlr, phyaddr, 0x1A, 0x2000); + + anar = miir(ctlr, phyaddr, 0x04); + anlpar = miir(ctlr, phyaddr, 0x05) & 0x03E0; + anar &= anlpar; + bmcr = 0; + if(anar & 0x380) + bmcr = 0x2000; + if(anar & 0x0140) + bmcr |= 0x0100; + } + break; + } + + /* + * Force speed and duplex if no auto-negotiation. + */ + if(anlpar == 0){ + medium = -1; + for(i = 0; i < ether->nopt; i++){ + for(k = 0; k < nelem(mediatable); k++){ + if(cistrcmp(mediatable[k], ether->opt[i])) + continue; + medium = k; + break; + } + + switch(medium){ + default: + break; + + case 0x00: /* 10BASE-T */ + case 0x01: /* 10BASE-2 */ + case 0x02: /* 10BASE-5 */ + bmcr &= ~(0x2000|0x0100); + ctlr->configdata[19] &= ~0x40; + break; + + case 0x03: /* 100BASE-TX */ + case 0x06: /* 100BASE-T4 */ + case 0x07: /* 100BASE-FX */ + ctlr->configdata[19] &= ~0x40; + bmcr |= 0x2000; + break; + + case 0x04: /* 10BASE-TFD */ + bmcr = (bmcr & ~0x2000)|0x0100; + ctlr->configdata[19] |= 0x40; + break; + + case 0x05: /* 100BASE-TXFD */ + case 0x08: /* 100BASE-FXFD */ + bmcr |= 0x2000|0x0100; + ctlr->configdata[19] |= 0x40; + break; + } + } + if(medium != -1) + miiw(ctlr, phyaddr, 0x00, bmcr); + } + + if(bmcr & 0x2000) + ether->mbps = 100; + + ctlr->configdata[8] = 1; + ctlr->configdata[15] &= ~0x80; + } + else{ + ctlr->configdata[8] = 0; + ctlr->configdata[15] |= 0x80; + } + + /* + * Workaround for some broken HUB chips when connected at 10Mb/s + * half-duplex. + * This is a band-aid, but as there's no dynamic auto-negotiation + * code at the moment, only deactivate the workaround code in txstart + * if the link is 100Mb/s. + */ + if(ether->mbps != 10) + ctlr->nop = 0; + + /* + * Load the chip configuration and start it off. + */ +#ifndef FS + if(ether->oq == 0) + ether->oq = qopen(256*1024, Qmsg, 0, 0); +#endif + configure(ether, 0); + command(ctlr, CUstart, PADDR(&ctlr->cbr->status)); + + /* + * Check if the adapter's station address is to be overridden. + * If not, read it from the EEPROM and set in ether->ea prior to loading + * the station address with the Individual Address Setup command. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, ether->ea, Eaddrlen) == 0){ + for(i = 0; i < Eaddrlen/2; i++){ + x = ctlr->eeprom[i]; + ether->ea[2*i] = x; + ether->ea[2*i+1] = x>>8; + } + } + + ilock(&ctlr->cblock); + ctlr->action = CbIAS; + txstart(ether); + iunlock(&ctlr->cblock); + + /* + * Linkage to the generic ethernet driver. + */ + ether->attach = attach; + ether->transmit = transmit; + ether->interrupt = interrupt; +#ifndef FS + ether->ifstat = ifstat; + + ether->arg = ether; + ether->promiscuous = promiscuous; + ether->shutdown = shutdown; + ether->multicast = multicast; +#endif + return 0; +} + +#ifndef FS +void +ether82557bothlink(void) +{ + addethercard("i82557", etheri82557reset); +} +#endif diff -Nru /sys/src/fs/pc/ether82563.c /sys/src/fs/pc/ether82563.c --- /sys/src/fs/pc/ether82563.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/pc/ether82563.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,1112 @@ +/* + * Intel 82563 Gigabit Ethernet Controller + */ +#include "all.h" +#include "io.h" +#include "../ip/ip.h" +#include "etherif.h" +#include "portfns.h" +#include "mem.h" + +extern ulong upamalloc(ulong, int, int); + +#define PCIWADDR(x) PADDR(x)+0 +#define ROUND(s, sz) (((s)+((sz)-1)) & ~((sz)-1)) + +/* + * these are in the order they appear in the manual, not numeric order. + * It was too hard to find them in the book. Ref 21489, rev 2.6 + */ + +enum { + /* General */ + + Ctrl = 0x00000000, /* Device Control */ + Status = 0x00000008, /* Device Status */ + Eec = 0x00000010, /* EEPROM/Flash Control/Data */ + Eerd = 0x00000014, /* EEPROM Read */ + Ctrlext = 0x00000018, /* Extended Device Control */ + Fla = 0x0000001c, /* Flash Access */ + Mdic = 0x00000020, /* MDI Control */ + Seresctl = 0x00000024, /* Serdes ana */ + Fcal = 0x00000028, /* Flow Control Address Low */ + Fcah = 0x0000002C, /* Flow Control Address High */ + Fct = 0x00000030, /* Flow Control Type */ + Kumctrlsta = 0x00000034, /* Kumeran Control and Status Register */ + Vet = 0x00000038, /* VLAN EtherType */ + Fcttv = 0x00000170, /* Flow Control Transmit Timer Value */ + Txcw = 0x00000178, /* Transmit Configuration Word */ + Rxcw = 0x00000180, /* Receive Configuration Word */ + Ledctl = 0x00000E00, /* LED control */ + Pba = 0x00001000, /* Packet Buffer Allocation */ + + /* Interrupt */ + + Icr = 0x000000C0, /* Interrupt Cause Read */ + Ics = 0x000000C8, /* Interrupt Cause csr32w */ + Ims = 0x000000D0, /* Interrupt Mask csr32w/Read */ + Imc = 0x000000D8, /* Interrupt mask Clear */ + Iam = 0x000000E0, /* Interrupt acknowledge Auto Mask */ + + /* Receive */ + + Rctl = 0x00000100, /* Control */ + Ert = 0x00002008, /* Early Receive Threshold (573[EVL] only) */ + Fcrtl = 0x00002160, /* Flow Control Rx Threshold Low */ + Fcrth = 0x00002168, /* Flow Control Rx Threshold High */ + Psrctl = 0x00002170, /* Packet Split Receive Control */ + Rdbal = 0x00002800, /* Rdesc Base Address Low Queue 0 */ + Rdbah = 0x00002804, /* Rdesc Base Address High Queue 0 */ + Rdlen = 0x00002808, /* Descriptor Length Queue 0 */ + Rdh = 0x00002810, /* Descriptor Head Queue 0 */ + Rdt = 0x00002818, /* Descriptor Tail Queue 0 */ + Rdtr = 0x00002820, /* Descriptor Timer Ring */ + Rxdctl = 0x00002828, /* Descriptor Control */ + Radv = 0x0000282C, /* Interrupt Absolute Delay Timer */ + Rdbal1 = 0x00002900, /* Rdesc Base Address Low Queue 1 */ + Rdbah1 = 0x00002804, /* Rdesc Base Address High Queue 1 */ + Rdlen1 = 0x00002908, /* Descriptor Length Queue 1 */ + Rdh1 = 0x00002910, /* Descriptor Head Queue 1 */ + Rdt1 = 0x00002918, /* Descriptor Tail Queue 1 */ + Rxdctl1 = 0x00002928, /* Descriptor Control Queue 1 */ + Rsrpd = 0x00002c00, /* Small Packet Detect */ + Raid = 0x00002c08, /* ACK interrupt delay */ + Cpuvec = 0x00002c10, /* CPU Vector */ + Rxcsum = 0x00005000, /* Checksum Control */ + Rfctl = 0x00005008, /* Filter Control */ + Mta = 0x00005200, /* Multicast Table Array */ + Ral = 0x00005400, /* Address Low */ + Rah = 0x00005404, /* Address High */ + Vfta = 0x00005600, /* VLAN Filter Table Array */ + Mrqc = 0x00005818, /* Multiple Receive Queues Command */ + Rssim = 0x00005864, /* RSS Interrupt Mask */ + Rssir = 0x00005868, /* RSS Interrupt Request */ + Reta = 0x00005c00, /* Redirection Table */ + Rssrk = 0x00005c80, /* RSS Random Key */ + + /* Transmit */ + + Tctl = 0x00000400, /* Control */ + Tipg = 0x00000410, /* IPG */ + Tdbal = 0x00003800, /* Tdesc Base Address Low */ + Tdbah = 0x00003804, /* Tdesc Base Address High */ + Tdlen = 0x00003808, /* Descriptor Length */ + Tdh = 0x00003810, /* Descriptor Head */ + Tdt = 0x00003818, /* Descriptor Tail */ + Tidv = 0x00003820, /* Interrupt Delay Value */ + Txdctl = 0x00003828, /* Descriptor Control */ + Tadv = 0x0000382C, /* Interrupt Absolute Delay Timer */ + Tarc0 = 0x00003840, /* Arbitration Counter Queue 0 */ + Tdbal1 = 0x00003900, /* Descriptor Base Low Queue 1 */ + Tdbah1 = 0x00003904, /* Descriptor Base High Queue 1 */ + Tdlen1 = 0x00003908, /* Descriptor Length Queue 1 */ + Tdh1 = 0x00003910, /* Descriptor Head Queue 1 */ + Tdt1 = 0x00003918, /* Descriptor Tail Queue 1 */ + Txdctl1 = 0x00003928, /* Descriptor Control 1 */ + Tarc1 = 0x00003940, /* Arbitration Counter Queue 1 */ + + /* Statistics */ + + Statistics = 0x00004000, /* Start of Statistics Area */ + Gorcl = 0x88/4, /* Good Octets Received Count */ + Gotcl = 0x90/4, /* Good Octets Transmitted Count */ + Torl = 0xC0/4, /* Total Octets Received */ + Totl = 0xC8/4, /* Total Octets Transmitted */ + Nstatistics = 64, + +}; + +enum { /* Ctrl */ + GIOmd = (1<<2), /* BIO master disable */ + Lrst = (1<<3), /* link reset */ + Slu = (1<<6), /* csr32w Link Up */ + SspeedMASK = (3<<8), /* Speed Selection */ + SspeedSHIFT = 8, + Sspeed10 = 0x00000000, /* 10Mb/s */ + Sspeed100 = 0x00000100, /* 100Mb/s */ + Sspeed1000 = 0x00000200, /* 1000Mb/s */ + Frcspd = (1<<11), /* Force Speed */ + Frcdplx = (1<<12), /* Force Duplex */ + SwdpinsloMASK = 0x003C0000, /* Software Defined Pins - lo nibble */ + SwdpinsloSHIFT = 18, + SwdpioloMASK = 0x03C00000, /* Software Defined Pins - I or O */ + SwdpioloSHIFT = 22, + Devrst = (1<<26), /* Device Reset */ + Rfce = (1<<27), /* Receive Flow Control Enable */ + Tfce = (1<<28), /* Transmit Flow Control Enable */ + Vme = (1<<30), /* VLAN Mode Enable */ + Phy_rst = (1<<31), /* Phy Reset */ +}; + +enum { /* Status */ + Lu = (1<<1), /* Link Up */ + Lanid = (3<<2), /* mask for Lan ID. + Txoff = (1<<4), /* Transmission Paused */ + Tbimode = (1<<5), /* TBI Mode Indication */ + SpeedMASK = 0x000000C0, + Speed10 = 0x00000000, /* 10Mb/s */ + Speed100 = 0x00000040, /* 100Mb/s */ + Speed1000 = 0x00000080, /* 1000Mb/s */ + Phyra = (1<<10), /* PHY Reset Asserted */ + GIOme = (1<<19), /* GIO Master Enable Status */ +}; + +enum { /* Ctrl and Status */ + Fd = 0x00000001, /* Full-Duplex */ + AsdvMASK = 0x00000300, + Asdv10 = 0x00000000, /* 10Mb/s */ + Asdv100 = 0x00000100, /* 100Mb/s */ + Asdv1000 = 0x00000200, /* 1000Mb/s */ +}; + +enum { /* Eec */ + Sk = (1<<0), /* Clock input to the EEPROM */ + Cs = (1<<1), /* Chip Select */ + Di = (1<<2), /* Data Input to the EEPROM */ + Do = (1<<3), /* Data Output from the EEPROM */ + Areq = (1<<6), /* EEPROM Access Request */ + Agnt = (1<<7), /* EEPROM Access Grant */ +}; + +enum { /* Eerd */ + ee_start = (1<<0), /* Start Read */ + ee_done = (1<<1), /* Read done */ + ee_addr = (0xfff8<<2), /* Read address [15:2] */ + ee_data = (0xffff<<16), /* Read Data; Data returned from eeprom/nvm */ +}; + +enum { /* Ctrlext */ + Asdchk = (1<<12), /* ASD Check */ + Eerst = (1<<13), /* EEPROM Reset */ + Spdbyps = (1<<15), /* Speed Select Bypass */ +}; + +enum { /* EEPROM content offsets */ + Ea = 0x00, /* Ethernet Address */ + Cf = 0x03, /* Compatibility Field */ + xIcw1 = 0x0A, /* Initialization Control Word 1 */ + Sid = 0x0B, /* Subsystem ID */ + Svid = 0x0C, /* Subsystem Vendor ID */ + Did = 0x0D, /* Device ID */ + Vid = 0x0E, /* Vendor ID */ + Icw2 = 0x0F, /* Initialization Control Word 2 */ +}; + +enum { /* Mdic */ + MDIdMASK = 0x0000FFFF, /* Data */ + MDIdSHIFT = 0, + MDIrMASK = 0x001F0000, /* PHY Register Address */ + MDIrSHIFT = 16, + MDIpMASK = 0x03E00000, /* PHY Address */ + MDIpSHIFT = 21, + MDIwop = 0x04000000, /* Write Operation */ + MDIrop = 0x08000000, /* Read Operation */ + MDIready = 0x10000000, /* End of Transaction */ + MDIie = 0x20000000, /* Interrupt Enable */ + MDIe = 0x40000000, /* Error */ +}; + +enum { /* Icr, Ics, Ims, Imc */ + Txdw = 0x00000001, /* Transmit Descriptor Written Back */ + Txqe = 0x00000002, /* Transmit Queue Empty */ + Lsc = 0x00000004, /* Link Status Change */ + Rxseq = 0x00000008, /* Receive Sequence Error */ + Rxdmt0 = 0x00000010, /* Rdesc Minimum Threshold Reached */ + Rxo = 0x00000040, /* Receiver Overrun */ + Rxt0 = 0x00000080, /* Receiver Timer Interrupt */ + Mdac = 0x00000200, /* MDIO Access Completed */ + Rxcfg = 0x00000400, /* Receiving /C/ ordered sets */ + Gpi0 = 0x00000800, /* General Purpose Interrupts */ + Gpi1 = 0x00001000, + Gpi2 = 0x00002000, + Gpi3 = 0x00004000, +}; + +enum { /* Txcw */ + TxcwFd = 0x00000020, /* Full Duplex */ + TxcwHd = 0x00000040, /* Half Duplex */ + TxcwPauseMASK = 0x00000180, /* Pause */ + TxcwPauseSHIFT = 7, + TxcwPs = (1<reply */ + Filter rate; + Filter work; +}Ctlr; + +enum{ + Nether = 8, +}; + +#define csr32r(c, r) (*((c)->nic+((r)/4))) +#define csr32w(c, r, v) (*((c)->nic+((r)/4)) = (v)) + +static Ctlr ports[Nether]; +static int nports; + +static Lock i82563rblock; /* free receive Blocks */ +static Msgbuf *i82563rbpool; + +#ifdef notdef +static char* statistics[Nstatistics] = { + "CRC Error", + "Alignment Error", + "Symbol Error", + "RX Error", + "Missed Packets", + "Single Collision", + "Excessive Collisions", + "Multiple Collision", + "Late Collisions", + nil, + "Collision", + "Transmit Underrun", + "Defer", + "Transmit - No CRS", + "Sequence Error", + "Carrier Extension Error", + "Receive Error Length", + nil, + "XON Received", + "XON Transmitted", + "XOFF Received", + "XOFF Transmitted", + "FC Received Unsupported", + "Packets Received (64 Bytes)", + "Packets Received (65-127 Bytes)", + "Packets Received (128-255 Bytes)", + "Packets Received (256-511 Bytes)", + "Packets Received (512-1023 Bytes)", + "Packets Received (1024-1522 Bytes)", + "Good Packets Received", + "Broadcast Packets Received", + "Multicast Packets Received", + "Good Packets Transmitted", + nil, + "Good Octets Received", + nil, + "Good Octets Transmitted", + nil, + nil, + nil, + "Receive No Buffers", + "Receive Undersize", + "Receive Fragment", + "Receive Oversize", + "Receive Jabber", + nil, + nil, + nil, + "Total Octets Received", + nil, + "Total Octets Transmitted", + nil, + "Total Packets Received", + "Total Packets Transmitted", + "Packets Transmitted (64 Bytes)", + "Packets Transmitted (65-127 Bytes)", + "Packets Transmitted (128-255 Bytes)", + "Packets Transmitted (256-511 Bytes)", + "Packets Transmitted (512-1023 Bytes)", + "Packets Transmitted (1024-1522 Bytes)", + "Multicast Packets Transmitted", + "Broadcast Packets Transmitted", + "TCP Segmentation Context Transmitted", + "TCP Segmentation Context Fail", +}; + +static void +i82563ifstat(Ctlr *c) +{ + char *s; + int i, r; + uvlong tuvl, ruvl; + + for(i = 0; i < Nstatistics; i++){ + r = csr32r(c, Statistics+i*4); + if((s = statistics[i]) == nil) + continue; + switch(i){ + case Gorcl: + case Gotcl: + case Torl: + case Totl: + ruvl = r; + ruvl += ((uvlong)csr32r(c, Statistics+(i+1)*4))<<32; + tuvl = ruvl; + tuvl += c->statistics[i]; + tuvl += ((uvlong)c->statistics[i+1])<<32; + if(tuvl == 0) + continue; + c->statistics[i] = tuvl; + c->statistics[i+1] = tuvl>>32; + print("%s: %llud %llud\n", s, tuvl, ruvl); + i++; + break; + + default: + c->statistics[i] += r; + if(c->statistics[i] == 0) + continue; + print("%s: %ud %ud\n", s, c->statistics[i], r); + break; + } + } + + print("lintr: %ud %ud\n", c->lintr, c->lsleep); + print("rintr: %ud %ud\n", c->rintr, c->rsleep); + print("tintr: %ud %ud\n", c->tintr, c->txdw); + print("ixcs: %ud %ud %ud\n", c->ixsm, c->ipcs, c->tcpcs); + print("rdtr: %ud\n", c->rdtr); + print("radv: %ud\n", c->radv); + print("Ctrlext: %08x\n", csr32r(c, Ctrlext)); + + print("eeprom:"); + for(i = 0; i < 0x40; i++){ + if(i && ((i & 0x07) == 0)) + print("\n "); + print(" %4.4uX", c->eeprom[i]); + } +} +#endif + +static Msgbuf* +i82563rballoc(void) +{ + Msgbuf *m; + + ilock(&i82563rblock); + if((m = i82563rbpool) != nil){ + i82563rbpool = m->next; + m->next = nil; + } + iunlock(&i82563rblock); + m->flags &= ~FREE; + m->count = 0; + m->data = (uchar*)PGROUND((uintptr)m->xdata); + return m; +} + +static void +i82563rbfree(Msgbuf *m) +{ + m->flags |= FREE; + ilock(&i82563rblock); + m->next = i82563rbpool; + i82563rbpool = m; + iunlock(&i82563rblock); +} + +static void +i82563im(Ctlr* c, int im) +{ + ilock(&c->imlock); + c->im |= im; + csr32w(c, Ims, c->im); + iunlock(&c->imlock); +} + +static void +i82563txinit(Ctlr* c) +{ + int i, r; + Msgbuf *m; + + csr32w(c, Tctl, 0x0F<tdba)); + csr32w(c, Tdbah, 0); + csr32w(c, Tdlen, c->ntd*sizeof(Td)); + c->tdh = PREV(0, c->ntd); + csr32w(c, Tdh, 0); + c->tdt = 0; + csr32w(c, Tdt, 0); + for(i = 0; i < c->ntd; i++){ + if((m = c->tb[i]) != nil){ + c->tb[i] = nil; + mbfree(m); + } + memset(&c->tdba[i], 0, sizeof(Td)); + } + c->tdfree = c->ntd; + csr32w(c, Tidv, 128); + r = csr32r(c, Txdctl); + r &= ~WthreshMASK; + r |= Gran | 4<ctlr; + ilock(&c->txlock); + /* + * Free any completed packets + */ + tdh = c->tdh; + ctdh = csr32r(c, Tdh); + while(NEXT(tdh, c->ntd) != ctdh){ + if((m = c->tb[tdh]) != nil){ + c->tb[tdh] = nil; + mbfree(m); + } + memset(&c->tdba[tdh], 0, sizeof(Td)); + tdh = NEXT(tdh, c->ntd); + } + c->tdh = tdh; + + /* + * Try to fill the ring back up. + */ + tdt = c->tdt; + while(NEXT(tdt, c->ntd) != tdh){ + if((m = etheroq(e)) == nil) + break; + td = &c->tdba[tdt]; + td->addr[0] = PCIWADDR(m->data); + td->control = (m->count & LenMASK) << LenSHIFT; + td->control |= Ifcs | Teop | DtypeDD; + c->tb[tdt] = m; + tdt = NEXT(tdt, c->ntd); + c->tdt = tdt; + if(NEXT(tdt, c->ntd) == tdh){ + td->control |= Rs; + c->txdw++; + i82563im(c, Txdw); + break; + } + } + csr32w(c, Tdt, tdt); + iunlock(&c->txlock); +} + +static void +i82563replenish(Ctlr* c) +{ + int rdt; + Msgbuf *m; + Rd *rd; + + rdt = c->rdt; + while(NEXT(rdt, c->nrd) != c->rdh){ + rd = &c->rdba[rdt]; + if(c->rb[rdt] == nil){ + if((m = i82563rballoc()) == nil){ + print("no available buffers\n"); + break; + } + c->rb[rdt] = m; + rd->addr[0] = PCIWADDR(m->data); + rd->addr[1] = 0; + } + rd->status = 0; + rdt = NEXT(rdt, c->nrd); + c->rdfree++; + } + c->rdt = rdt; + csr32w(c, Rdt, rdt); +} + +static void +i82563rxinit(Ctlr* c) +{ + int i; + Msgbuf *m; + +// csr32w(c, Rctl, Dpf | Bsize2048 | Bam | RdtmsHALF); +// csr32w(c, Rctl, Lpe| Dpf | Bsize16384 | Bam | RdtmsHALF | Bsex | Secrc); + csr32w(c, Rctl, Lpe| Dpf | Bsize8192 | Bam | RdtmsHALF | Bsex | Secrc); + + csr32w(c, Rdbal, PCIWADDR(c->rdba)); + csr32w(c, Rdbah, 0); + csr32w(c, Rdlen, c->nrd*sizeof(Rd)); + c->rdh = 0; + csr32w(c, Rdh, 0); + c->rdt = 0; + csr32w(c, Rdt, 0); + c->rdtr = 0; + c->radv = 0; + csr32w(c, Rdtr, Fpd | 0); + csr32w(c, Radv, 0); + + for(i = 0; i < c->nrd; i++){ + if((m = c->rb[i]) != nil){ + c->rb[i] = nil; + mbfree(m); + } + } + i82563replenish(c); + csr32w(c, Radv, 64); +// csr32w(c, Rxdctl, 8<arg; + c = e->ctlr; + i82563rxinit(c); + r = csr32r(c, Rctl); + r |= Ren; + csr32w(c, Rctl, r); + + for(;;){ + i82563im(c, Rxt0|Rxo|Rxdmt0|Rxseq); + c->rsleep++; + coherence(); + sleep(&c->rxrendez, rim0, &c->rim); + + rdh = c->rdh; + for(;;){ + rd = &c->rdba[rdh]; + rim = c->rim; + c->rim = 0; + if(!(rd->status & Rdd)) + break; + + /* + * Accept eop packets with no errors. + * With no errors and the Ixsm bit set, + * the descriptor status Tpcs and Ipcs bits give + * an indication of whether the checksums were + * calculated and valid. + */ + if (m = c->rb[rdh]) { + if((rd->status & Reop) && rd->errors == 0){ + m->count = rd->length; + m->next = nil; + etheriq(e, m); + } else + mbfree(m); + c->rb[rdh] = nil; + } + memset(rd, 0, sizeof(Rd)); + c->rdfree--; + c->rdh = rdh = NEXT(rdh, c->nrd); + coherence(); + if(c->rdfree < (c->nrd/4)*3 || (rim&Rxdmt0)) + i82563replenish(c); + } + } +} + +static void +i82563attach(Ether *e) +{ + char name[NAMELEN]; + Ctlr *c; + Msgbuf *m; + + c = e->ctlr; + c->nrd = ROUND(Nrd, 8); + c->ntd = ROUND(Ntd, 8); + c->alloc = ialloc(c->nrd*sizeof(Rd)+c->ntd*sizeof(Td) + 255, 0); + c->rdba = (Rd*)ROUNDUP((ulong)c->alloc, 256); + c->tdba = (Td*)(c->rdba+c->nrd); + + c->rb = ialloc(c->nrd*sizeof m, 0); + c->tb = ialloc(c->ntd*sizeof m, 0); + + for(c->nrb = 0; c->nrb < Nrb; c->nrb++){ + m = mballoc(Rbsz+BY2PG, 0, Mbeth1); + m->free = i82563rbfree; + mbfree(m); + } + snprint(name, sizeof name, "82563rx%ld", c-ports); + userinit(i82563rxproc, e, name); + i82563txinit(c); +} + +static void +i82563interrupt(Ureg*, void* v) +{ + int icr, im, txdw; + Ether *e; + Ctlr *c; + + e = v; + c = e->ctlr; + + ilock(&c->imlock); + csr32w(c, Imc, ~0); + im = c->im; + txdw = 0; + while(icr = csr32r(c, Icr) & c->im){ + if(icr & Lsc){ + im &= ~Lsc; + c->lim = icr & Lsc; + c->lintr++; + } + if(icr & (Rxt0|Rxo|Rxdmt0|Rxseq)){ + c->rim = icr & (Rxt0|Rxo|Rxdmt0|Rxseq); + im &= ~(Rxt0|Rxo|Rxdmt0|Rxseq); + wakeup(&c->rxrendez); + c->rintr++; + } + if(icr & Txdw){ + im &= ~Txdw; + txdw++; + c->tintr++; + } + } + c->im = im; + csr32w(c, Ims, im); + iunlock(&c->imlock); + if(txdw) + i82563transmit(e); +} + +static int +i82563detach(Ctlr* c) +{ + int r, timeo; + + /* + * Perform a device reset to get the chip back to the + * power-on state, followed by an EEPROM reset to read + * the defaults for some internal registers. + */ + csr32w(c, Imc, ~0); + csr32w(c, Rctl, 0); + csr32w(c, Tctl, 0); + + delay(10); + + csr32w(c, Ctrl, Devrst); + delay(1); + for(timeo = 0; timeo < 1000; timeo++){ + if(!(csr32r(c, Ctrl) & Devrst)) + break; + delay(1); + } + if(csr32r(c, Ctrl) & Devrst) + return -1; + r = csr32r(c, Ctrlext); + csr32w(c, Ctrlext, r | Eerst); + delay(1); + for(timeo = 0; timeo < 1000; timeo++){ + if(!(csr32r(c, Ctrlext) & Eerst)) + break; + delay(1); + } + if(csr32r(c, Ctrlext) & Eerst) + return -1; + + csr32w(c, Imc, ~0); + delay(1); + for(timeo = 0; timeo < 1000; timeo++){ + if(!csr32r(c, Icr)) + break; + delay(1); + } + if(csr32r(c, Icr)) + return -1; + + return 0; +} + +static ushort +eeread(Ctlr* c, int adr) +{ + csr32w(c, Eerd, ee_start | adr<<2); + while ((csr32r(c, Eerd) & ee_done) == 0) + ; + return csr32r(c, Eerd) >> 16; +} + +static int +eeload(Ctlr* c) +{ + int data, adr; + ushort sum; + + sum = 0; + for (adr = 0; adr < 0x40; adr++) { + data = eeread(c, adr); + c->eeprom[adr] = data; + sum += data; + } + return sum; +} + +static uchar* +etheradd(uchar *u, uint n) +{ + int i; + uint j; + + for(i = 5; n != 0 && i >= 0; i--){ + j = n + u[i]; + u[i] = j; + n = j >> 8; + } + return u; +} + +typedef struct { + uchar ea[Easize]; + int n; +} Basetab; + +static Basetab btab[Nether]; +static int nbase; + +int +nthether(uchar *ea) +{ + int i; + + for(i = 0; i < nelem(btab); i++) + if(btab[i].n == 0 || memcmp(btab[i].ea, ea, Easize) == 0) { + memmove(btab[i].ea, ea, Easize); + return btab[i].n++; + } + return -1; +} + +static int +reset(Ctlr *c) +{ + int i, r; + + if(i82563detach(c)) + return -1; + r = eeload(c); + if (r != 0 && r != 0xbaba){ + print("i82563: bad EEPROM checksum - 0x%4.4ux\n", r); + return -1; + } + + for(i = Ea; i < Easize/2; i++){ + c->ra[2*i] = c->eeprom[i]; + c->ra[2*i+1] = c->eeprom[i] >> 8; + } + etheradd(c->ra, nthether(c->ra)); + r = c->ra[3]<<24 | c->ra[2]<<16 | c->ra[1]<<8 | c->ra[0]; + csr32w(c, Ral, r); + r = 0x80000000 | c->ra[5]<<8 | c->ra[4]; + csr32w(c, Rah, r); + for(i = 1; i < 16; i++){ + csr32w(c, Ral+i*8, 0); + csr32w(c, Rah+i*8, 0); + } + memset(c->mta, 0, sizeof c->mta); + for(i = 0; i < 128; i++) + csr32w(c, Mta+i*4, 0); + csr32w(c, Fcal, 0x00C28001); + csr32w(c, Fcah, 0x00000100); + csr32w(c, Fct, 0x00008808); + csr32w(c, Fcttv, 0x00000100); + csr32w(c, Fcrtl, c->fcrtl); + csr32w(c, Fcrth, c->fcrth); + return 0; +} + +static void +i82563init(Ether *) +{ + Ctlr *c; + Pcidev *p; + + print("i82563init\n"); + p = 0; + while(nports < nelem(ports) && (p = pcimatch(p, 0x8086, 0x1096))){ + c = ports + nports; + memset(c, 0, sizeof *c); + c->pcidev = p; + c->id = p->did<<16 | p->vid; + + c->port = p->mem[0].bar & ~0xf; + c->nic = (int*)upamalloc(c->port, p->mem[0].size, 0); + c->cls = pcicfgr8(p, PciCLS) << 2; + switch(c->cls){ + default: + print("i82563: unexpected CLS - %d\n", c->cls); + case 0x08<<2: + case 0x10<<2: + break; + case 0x00<<2: + case 0xFF<<2: + print("i82563: unusable CLS\n"); + continue; + } + if(reset(c)) + continue; + pcisetbme(p); + print("82563 %d irq %d Ea %E\n", nports, p->intl, + ports[nports].ra); + nports++; + } +} + +int +i82563reset(Ether *e) +{ + int i; + static int once; + + if(once++ == 0) + i82563init(e); + for(i = 0; i < nports; i++) + if (!ports[i].active && + (e->port == 0 || e->port == ports[i].port)) + break; + if(i == nports) + return -1; + ports[i].active = 1; + e->ctlr = ports+i; + e->port = ports[i].port; + e->irq = ports[i].pcidev->intl; + e->tbdf = ports[i].pcidev->tbdf; + e->mbps = 1000; + memmove(e->ea, ports[i].ra, Easize); + e->attach = i82563attach; + e->transmit = i82563transmit; + e->interrupt = i82563interrupt; + + return 0; +} diff -Nru /sys/src/fs/pc/ether83815.c /sys/src/fs/pc/ether83815.c --- /sys/src/fs/pc/ether83815.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/pc/ether83815.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,1136 @@ +/* + * National Semiconductor DP83815 + * + * Supports only internal PHY and has been tested on: + * Netgear FA311TX (using Netgear DS108 10/100 hub) + * SiS 900 (works under light load only) + * To do: + * check Ethernet address; + * test autonegotiation on 10 Mbit, and 100 Mbit full duplex; + * external PHY via MII (should be common code for MII); + * thresholds; + * ring sizing; + * physical link changes/disconnect; + * push initialisation back to attach. + * + * C H Forsyth, forsyth@vitanuova.com, 18th June 2001. + */ + +#ifdef FS +#include "all.h" +#include "io.h" +#include "mem.h" +#include "../ip/ip.h" +#else /* FS */ +#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" +#endif /* FS */ +#include "etherif.h" +#include "compat.h" + +#define DEBUG (0) +#define debug if(DEBUG)print + +enum { + Nrde = 64, + Ntde = 64, +}; + +#define Rbsz ROUNDUP(sizeof(Etherpkt)+4, 4) + +typedef struct Des { + ulong next; + int cmdsts; + ulong addr; + Block* bp; +} Des; + +enum { /* cmdsts */ + Own = 1<<31, /* set by data producer to hand to consumer */ + More = 1<<30, /* more of packet in next descriptor */ + Intr = 1<<29, /* interrupt when device is done with it */ + Supcrc = 1<<28, /* suppress crc on transmit */ + Inccrc = 1<<28, /* crc included on receive (always) */ + Ok = 1<<27, /* packet ok */ + Size = 0xFFF, /* packet size in bytes */ + + /* transmit */ + Txa = 1<<26, /* transmission aborted */ + Tfu = 1<<25, /* transmit fifo underrun */ + Crs = 1<<24, /* carrier sense lost */ + Td = 1<<23, /* transmission deferred */ + Ed = 1<<22, /* excessive deferral */ + Owc = 1<<21, /* out of window collision */ + Ec = 1<<20, /* excessive collisions */ + /* 19-16 collision count */ + + /* receive */ + Rxa = 1<<26, /* receive aborted (same as Rxo) */ + Rxo = 1<<25, /* receive overrun */ + Dest = 3<<23, /* destination class */ + Drej= 0<<23, /* packet was rejected */ + Duni= 1<<23, /* unicast */ + Dmulti= 2<<23, /* multicast */ + Dbroad= 3<<23, /* broadcast */ + Long = 1<<22, /* too long packet received */ + Runt = 1<<21, /* packet less than 64 bytes */ + Ise = 1<<20, /* invalid symbol */ + Crce = 1<<19, /* invalid crc */ + Fae = 1<<18, /* frame alignment error */ + Lbp = 1<<17, /* loopback packet */ + Col = 1<<16, /* collision during receive */ +}; + +enum { /* PCI vendor & device IDs */ + Nat83815 = (0x0020<<16)|0x100B, + SiS = 0x1039, + SiS900 = (0x0900<<16)|SiS, + SiS7016 = (0x7016<<16)|SiS, + + SiS630bridge = 0x0008, + + /* SiS 900 PCI revision codes */ + SiSrev630s = 0x81, + SiSrev630e = 0x82, + SiSrev630ea1 = 0x83, + + SiSeenodeaddr = 8, /* short addr of SiS eeprom mac addr */ + SiS630eenodeaddr = 9, /* likewise for the 630 */ + Nseenodeaddr = 6, /* " for NS eeprom */ +}; + +typedef struct Ctlr Ctlr; +typedef struct Ctlr { + int port; + Pcidev* pcidev; + Ctlr* next; + int active; + int id; /* (pcidev->did<<16)|pcidev->vid */ + + ushort srom[0xB+1]; + uchar sromea[Eaddrlen]; /* MAC address */ + + uchar fd; /* option or auto negotiation */ + + int mbps; + + Lock lock; + + Des* rdr; /* receive descriptor ring */ + int nrdr; /* size of rdr */ + int rdrx; /* index into rdr */ + + Lock tlock; + Des* tdr; /* transmit descriptor ring */ + int ntdr; /* size of tdr */ + int tdrh; /* host index into tdr */ + int tdri; /* interface index into tdr */ + int ntq; /* descriptors active */ + int ntqmax; + + ulong rxa; /* receive statistics */ + ulong rxo; + ulong rlong; + ulong runt; + ulong ise; + ulong crce; + ulong fae; + ulong lbp; + ulong col; + ulong rxsovr; + ulong rxorn; + + ulong txa; /* transmit statistics */ + ulong tfu; + ulong crs; + ulong td; + ulong ed; + ulong owc; + ulong ec; + ulong txurn; + + ulong dperr; /* system errors */ + ulong rmabt; + ulong rtabt; + ulong sserr; + ulong rxsover; +} Ctlr; + +static Ctlr* ctlrhead; +static Ctlr* ctlrtail; + +enum { + /* registers (could memory map) */ + Rcr= 0x00, /* command register */ + Rst= 1<<8, + Rxr= 1<<5, /* receiver reset */ + Txr= 1<<4, /* transmitter reset */ + Rxd= 1<<3, /* receiver disable */ + Rxe= 1<<2, /* receiver enable */ + Txd= 1<<1, /* transmitter disable */ + Txe= 1<<0, /* transmitter enable */ + Rcfg= 0x04, /* configuration */ + Lnksts= 1<<31, /* link good */ + Speed100= 1<<30, /* 100 Mb/s link */ + Fdup= 1<<29, /* full duplex */ + Pol= 1<<28, /* polarity reversal (10baseT) */ + Aneg_dn= 1<<27, /* autonegotiation done */ + Pint_acen= 1<<17, /* PHY interrupt auto clear enable */ + Pause_adv= 1<<16, /* advertise pause during auto neg */ + Paneg_ena= 1<<13, /* auto negotiation enable */ + Paneg_all= 7<<13, /* auto negotiation enable 10/100 half & full */ + Ext_phy= 1<<12, /* enable MII for external PHY */ + Phy_rst= 1<<10, /* reset internal PHY */ + Phy_dis= 1<<9, /* disable internal PHY (eg, low power) */ + Req_alg= 1<<7, /* PCI bus request: set means less aggressive */ + Sb= 1<<6, /* single slot back-off not random */ + Pow= 1<<5, /* out of window timer selection */ + Exd= 1<<4, /* disable excessive deferral timer */ + Pesel= 1<<3, /* parity error algorithm selection */ + Brom_dis= 1<<2, /* disable boot rom interface */ + Bem= 1<<0, /* big-endian mode */ + Rmear= 0x08, /* eeprom access */ + Mdc= 1<<6, /* MII mangement check */ + Mddir= 1<<5, /* MII management direction */ + Mdio= 1<<4, /* MII mangement data */ + Eesel= 1<<3, /* EEPROM chip select */ + Eeclk= 1<<2, /* EEPROM clock */ + Eedo= 1<<1, /* EEPROM data out (from chip) */ + Eedi= 1<<0, /* EEPROM data in (to chip) */ + Rptscr= 0x0C, /* pci test control */ + Risr= 0x10, /* interrupt status */ + Txrcmp= 1<<25, /* transmit reset complete */ + Rxrcmp= 1<<24, /* receiver reset complete */ + Dperr= 1<<23, /* detected parity error */ + Sserr= 1<<22, /* signalled system error */ + Rmabt= 1<<21, /* received master abort */ + Rtabt= 1<<20, /* received target abort */ + Rxsovr= 1<<16, /* RX status FIFO overrun */ + Hiberr= 1<<15, /* high bits error set (OR of 25-16) */ + Phy= 1<<14, /* PHY interrupt */ + Pme= 1<<13, /* power management event (wake online) */ + Swi= 1<<12, /* software interrupt */ + Mib= 1<<11, /* MIB service */ + Txurn= 1<<10, /* TX underrun */ + Txidle= 1<<9, /* TX idle */ + Txerr= 1<<8, /* TX packet error */ + Txdesc= 1<<7, /* TX descriptor (with Intr bit done) */ + Txok= 1<<6, /* TX ok */ + Rxorn= 1<<5, /* RX overrun */ + Rxidle= 1<<4, /* RX idle */ + Rxearly= 1<<3, /* RX early threshold */ + Rxerr= 1<<2, /* RX packet error */ + Rxdesc= 1<<1, /* RX descriptor (with Intr bit done) */ + Rxok= 1<<0, /* RX ok */ + Rimr= 0x14, /* interrupt mask */ + Rier= 0x18, /* interrupt enable */ + Ie= 1<<0, /* interrupt enable */ + Rtxdp= 0x20, /* transmit descriptor pointer */ + Rtxcfg= 0x24, /* transmit configuration */ + Csi= 1<<31, /* carrier sense ignore (needed for full duplex) */ + Hbi= 1<<30, /* heartbeat ignore (needed for full duplex) */ + Atp= 1<<28, /* automatic padding of runt packets */ + Mxdma= 7<<20, /* maximum dma transfer field */ + Mxdma32= 4<<20, /* 4x32-bit words (32 bytes) */ + Mxdma64= 5<<20, /* 8x32-bit words (64 bytes) */ + Flth= 0x3F<<8,/* Tx fill threshold, units of 32 bytes (must be > Mxdma) */ + Drth= 0x3F<<0,/* Tx drain threshold (units of 32 bytes) */ + Flth128= 4<<8, /* fill at 128 bytes */ + /* seems to be the same on SiS 900; maybe use larger value @ 100Mb/s */ + Drth512= 16<<0, /* drain at 512 bytes */ + Rrxdp= 0x30, /* receive descriptor pointer */ + Rrxcfg= 0x34, /* receive configuration */ + Atx= 1<<28, /* accept transmit packets (needed for full duplex) */ + Rdrth= 0x1F<<1,/* Rx drain threshold (units of 32 bytes) */ + Rdrth64= 2<<1, /* drain at 64 bytes */ + Rccsr= 0x3C, /* CLKRUN control/status */ + Pmests= 1<<15, /* PME status */ + Rwcsr= 0x40, /* wake on lan control/status */ + Rpcr= 0x44, /* pause control/status */ + /* TODO: different on SiS, but does it matter? Rfen - Aau are same. */ + Rrfcr= 0x48, /* receive filter/match control */ + Rfen= 1<<31, /* receive filter enable */ + Aab= 1<<30, /* accept all broadcast */ + Aam= 1<<29, /* accept all multicast */ + Aau= 1<<28, /* accept all unicast */ + Apm= 1<<27, /* accept on perfect match */ + Apat= 0xF<<23,/* accept on pattern match */ + Aarp= 1<<22, /* accept ARP */ + Mhen= 1<<21, /* multicast hash enable */ + Uhen= 1<<20, /* unicast hash enable */ + Ulm= 1<<19, /* U/L bit mask */ + /* bits 0-9 are rfaddr */ + Rrfdr= 0x4C, /* receive filter/match data */ + Rbrar= 0x50, /* boot rom address */ + Rbrdr= 0x54, /* boot rom data */ + Rsrr= 0x58, /* silicon revision */ + Rmibc= 0x5C, /* MIB control */ + /* 60-78 MIB data */ + + /* PHY registers */ + Rbmcr= 0x80, /* basic mode configuration */ + Reset= 1<<15, + Sel100= 1<<13, /* select 100Mb/sec if no auto neg */ + Anena= 1<<12, /* auto negotiation enable */ + Anrestart= 1<<9, /* restart auto negotiation */ + Selfdx= 1<<8, /* select full duplex if no auto neg */ + Rbmsr= 0x84, /* basic mode status */ + Ancomp= 1<<5, /* autonegotiation complete */ + Rphyidr1= 0x88, + Rphyidr2= 0x8C, + Ranar= 0x90, /* autonegotiation advertisement */ + Ranlpar= 0x94, /* autonegotiation link partner ability */ + Raner= 0x98, /* autonegotiation expansion */ + Rannptr= 0x9C, /* autonegotiation next page TX */ + Rphysts= 0xC0, /* PHY status */ + Rmicr= 0xC4, /* MII control */ + Inten= 1<<1, /* PHY interrupt enable */ + Rmisr= 0xC8, /* MII status */ + Rfcscr= 0xD0, /* false carrier sense counter */ + Rrecr= 0xD4, /* receive error counter */ + Rpcsr= 0xD8, /* 100Mb config/status */ + Rphycr= 0xE4, /* PHY control */ + Rtbscr= 0xE8, /* 10BaseT status/control */ +}; + +/* + * eeprom addresses + * 7 to 9 (16 bit words): mac address, shifted and reversed + */ + +#define csr32r(c, r) (inl((c)->port+(r))) +#define csr32w(c, r, l) (outl((c)->port+(r), (ulong)(l))) +#define csr16r(c, r) (ins((c)->port+(r))) +#define csr16w(c, r, l) (outs((c)->port+(r), (ulong)(l))) + +static void +dumpcregs(Ctlr *ctlr) +{ + int i; + + for(i=0; i<=0x5C; i+=4) + print("%2.2ux %8.8lux\n", i, csr32r(ctlr, i)); +} + +static void +promiscuous(void* arg, int on) +{ + Ctlr *ctlr; + ulong w; + + ctlr = ((Ether*)arg)->ctlr; + ilock(&ctlr->lock); + w = csr32r(ctlr, Rrfcr); + if(on != ((w&Aau)!=0)){ + csr32w(ctlr, Rrfcr, w & ~Rfen); + csr32w(ctlr, Rrfcr, Rfen | (w ^ Aau)); + } + iunlock(&ctlr->lock); +} + +static void +attach(Ether* ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(&ctlr->lock); + if(0) + dumpcregs(ctlr); + csr32w(ctlr, Rcr, Rxe); + iunlock(&ctlr->lock); +} + +#ifndef FS +static long +ifstat(Ether* ether, void* a, long n, ulong offset) +{ + Ctlr *ctlr; + char *buf, *p; + int i, l, len; + + ctlr = ether->ctlr; + + ether->crcs = ctlr->crce; + ether->frames = ctlr->runt+ctlr->ise+ctlr->rlong+ctlr->fae; + ether->buffs = ctlr->rxorn+ctlr->tfu; + ether->overflows = ctlr->rxsovr; + + if(n == 0) + return 0; + + p = malloc(READSTR); + l = snprint(p, READSTR, "Rxa: %lud\n", ctlr->rxa); + l += snprint(p+l, READSTR-l, "Rxo: %lud\n", ctlr->rxo); + l += snprint(p+l, READSTR-l, "Rlong: %lud\n", ctlr->rlong); + l += snprint(p+l, READSTR-l, "Runt: %lud\n", ctlr->runt); + l += snprint(p+l, READSTR-l, "Ise: %lud\n", ctlr->ise); + l += snprint(p+l, READSTR-l, "Fae: %lud\n", ctlr->fae); + l += snprint(p+l, READSTR-l, "Lbp: %lud\n", ctlr->lbp); + l += snprint(p+l, READSTR-l, "Tfu: %lud\n", ctlr->tfu); + l += snprint(p+l, READSTR-l, "Txa: %lud\n", ctlr->txa); + l += snprint(p+l, READSTR-l, "CRC Error: %lud\n", ctlr->crce); + l += snprint(p+l, READSTR-l, "Collision Seen: %lud\n", ctlr->col); + l += snprint(p+l, READSTR-l, "Frame Too Long: %lud\n", ctlr->rlong); + l += snprint(p+l, READSTR-l, "Runt Frame: %lud\n", ctlr->runt); + l += snprint(p+l, READSTR-l, "Rx Underflow Error: %lud\n", ctlr->rxorn); + l += snprint(p+l, READSTR-l, "Tx Underrun: %lud\n", ctlr->txurn); + l += snprint(p+l, READSTR-l, "Excessive Collisions: %lud\n", ctlr->ec); + l += snprint(p+l, READSTR-l, "Late Collision: %lud\n", ctlr->owc); + l += snprint(p+l, READSTR-l, "Loss of Carrier: %lud\n", ctlr->crs); + l += snprint(p+l, READSTR-l, "Parity: %lud\n", ctlr->dperr); + l += snprint(p+l, READSTR-l, "Aborts: %lud\n", ctlr->rmabt+ctlr->rtabt); + l += snprint(p+l, READSTR-l, "RX Status overrun: %lud\n", ctlr->rxsover); + snprint(p+l, READSTR-l, "ntqmax: %d\n", ctlr->ntqmax); + ctlr->ntqmax = 0; + buf = a; + len = readstr(offset, buf, n, p); + if(offset > l) + offset -= l; + else + offset = 0; + buf += len; + n -= len; + + l = snprint(p, READSTR, "srom:"); + for(i = 0; i < nelem(ctlr->srom); i++){ + if(i && ((i & 0x0F) == 0)) + l += snprint(p+l, READSTR-l, "\n "); + l += snprint(p+l, READSTR-l, " %4.4uX", ctlr->srom[i]); + } + + snprint(p+l, READSTR-l, "\n"); + len += readstr(offset, buf, n, p); + free(p); + + return len; +} +#endif + +static void +txstart(Ether* ether) +{ + Ctlr *ctlr; + Block *bp; + Des *des; + int started; + + ctlr = ether->ctlr; + started = 0; + while(ctlr->ntq < ctlr->ntdr-1){ + bp = etheroq(ether); + if(bp == nil) + break; + des = &ctlr->tdr[ctlr->tdrh]; + des->bp = bp; + des->addr = PADDR(bp->rp); + ctlr->ntq++; + coherence(); + des->cmdsts = Own | BLEN(bp); + ctlr->tdrh = NEXT(ctlr->tdrh, ctlr->ntdr); + started = 1; + } + if(started){ + coherence(); + csr32w(ctlr, Rcr, Txe); /* prompt */ + } + + if(ctlr->ntq > ctlr->ntqmax) + ctlr->ntqmax = ctlr->ntq; +} + +static void +transmit(Ether* ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(&ctlr->tlock); + txstart(ether); + iunlock(&ctlr->tlock); +} + +static void +txrxcfg(Ctlr *ctlr, int txdrth) +{ + ulong rx, tx; + + rx = csr32r(ctlr, Rrxcfg); + tx = csr32r(ctlr, Rtxcfg); + if(ctlr->fd){ + rx |= Atx; + tx |= Csi | Hbi; + }else{ + rx &= ~Atx; + tx &= ~(Csi | Hbi); + } + tx &= ~(Mxdma|Drth|Flth); + tx |= Mxdma64 | Flth128 | txdrth; + csr32w(ctlr, Rtxcfg, tx); + rx &= ~(Mxdma|Rdrth); + rx |= Mxdma64 | Rdrth64; + csr32w(ctlr, Rrxcfg, rx); +} + +static void +interrupt(Ureg*, void* arg) +{ + Ctlr *ctlr; + Ether *ether; + int len, status, cmdsts; + Des *des; + Block *bp; + + ether = arg; + ctlr = ether->ctlr; + + while((status = csr32r(ctlr, Risr)) != 0){ + + status &= ~(Pme|Mib); + + if(status & Hiberr){ + if(status & Rxsovr) + ctlr->rxsover++; + if(status & Sserr) + ctlr->sserr++; + if(status & Dperr) + ctlr->dperr++; + if(status & Rmabt) + ctlr->rmabt++; + if(status & Rtabt) + ctlr->rtabt++; + status &= ~(Hiberr|Txrcmp|Rxrcmp|Rxsovr|Dperr|Sserr|Rmabt|Rtabt); + } + + /* + * Received packets. + */ + if(status & (Rxdesc|Rxok|Rxerr|Rxearly|Rxorn)){ + des = &ctlr->rdr[ctlr->rdrx]; + while((cmdsts = des->cmdsts) & Own){ + if((cmdsts&Ok) == 0){ + if(cmdsts & Rxa) + ctlr->rxa++; + if(cmdsts & Rxo) + ctlr->rxo++; + if(cmdsts & Long) + ctlr->rlong++; + if(cmdsts & Runt) + ctlr->runt++; + if(cmdsts & Ise) + ctlr->ise++; + if(cmdsts & Crce) + ctlr->crce++; + if(cmdsts & Fae) + ctlr->fae++; + if(cmdsts & Lbp) + ctlr->lbp++; + if(cmdsts & Col) + ctlr->col++; + } + else if(bp = iallocb(Rbsz)){ + len = (cmdsts&Size)-4; + if(len <= 0){ + debug("ns83815: packet len %d <=0\n", len); + freeb(des->bp); + }else{ + SETWPCNT(des->bp, len); + ETHERIQ(ether, des->bp, 1); + } + des->bp = bp; + des->addr = PADDR(bp->rp); + coherence(); + }else{ + debug("ns83815: interrupt: iallocb for input buffer failed\n"); + des->bp->next = 0; + } + + des->cmdsts = Rbsz; + coherence(); + + ctlr->rdrx = NEXT(ctlr->rdrx, ctlr->nrdr); + des = &ctlr->rdr[ctlr->rdrx]; + } + status &= ~(Rxdesc|Rxok|Rxerr|Rxearly|Rxorn); + } + + /* + * Check the transmit side: + * check for Transmit Underflow and Adjust + * the threshold upwards; + * free any transmitted buffers and try to + * top-up the ring. + */ + if(status & Txurn){ + ctlr->txurn++; + ilock(&ctlr->lock); + /* change threshold */ + iunlock(&ctlr->lock); + status &= ~(Txurn); + } + + ilock(&ctlr->tlock); + while(ctlr->ntq){ + des = &ctlr->tdr[ctlr->tdri]; + cmdsts = des->cmdsts; + if(cmdsts & Own) + break; + + if((cmdsts & Ok) == 0){ + if(cmdsts & Txa) + ctlr->txa++; + if(cmdsts & Tfu) + ctlr->tfu++; + if(cmdsts & Td) + ctlr->td++; + if(cmdsts & Ed) + ctlr->ed++; + if(cmdsts & Owc) + ctlr->owc++; + if(cmdsts & Ec) + ctlr->ec++; +#ifndef FS + ether->oerrs++; +#endif + } + + freeb(des->bp); + des->bp = nil; + des->cmdsts = 0; + + ctlr->ntq--; + ctlr->tdri = NEXT(ctlr->tdri, ctlr->ntdr); + } + txstart(ether); + iunlock(&ctlr->tlock); + + status &= ~(Txurn|Txidle|Txerr|Txdesc|Txok); + + /* + * Anything left not catered for? + */ + if(status) + print("#l%d: status %8.8uX\n", ether->ctlrno, status); + } +} + +static void +ctlrinit(Ether* ether) +{ + Ctlr *ctlr; + Des *des, *last; + + ctlr = ether->ctlr; + + /* + * Allocate suitable aligned descriptors + * for the transmit and receive rings; + * initialise the receive ring; + * initialise the transmit ring; + * unmask interrupts and start the transmit side. + */ + des = xspanalloc((ctlr->nrdr+ctlr->ntdr)*sizeof(Des), 32, 0); + if(des == nil) { + print("ns83815: ctlrinit: iallocb of descs. failed\n"); + return; + } + ctlr->tdr = des; + ctlr->rdr = des+ctlr->ntdr; + + last = nil; + for(des = ctlr->rdr; des < &ctlr->rdr[ctlr->nrdr]; des++){ + des->bp = iallocb(Rbsz); + if(des->bp == nil) + error(Enomem); + des->cmdsts = Rbsz; + des->addr = PADDR(des->bp->rp); + if(last != nil) + last->next = PADDR(des); + last = des; + } + ctlr->rdr[ctlr->nrdr-1].next = PADDR(ctlr->rdr); + ctlr->rdrx = 0; + csr32w(ctlr, Rrxdp, PADDR(ctlr->rdr)); + + last = nil; + for(des = ctlr->tdr; des < &ctlr->tdr[ctlr->ntdr]; des++){ + des->cmdsts = 0; + des->bp = nil; + des->addr = ~0; + if(last != nil) + last->next = PADDR(des); + last = des; + } + ctlr->tdr[ctlr->ntdr-1].next = PADDR(ctlr->tdr); + ctlr->tdrh = 0; + ctlr->tdri = 0; + csr32w(ctlr, Rtxdp, PADDR(ctlr->tdr)); + + txrxcfg(ctlr, Drth512); + + csr32w(ctlr, Rimr, Dperr|Sserr|Rmabt|Rtabt|Rxsovr|Hiberr|Txurn|Txerr|Txdesc|Txok|Rxorn|Rxerr|Rxdesc|Rxok); /* Phy|Pme|Mib */ + csr32r(ctlr, Risr); /* clear status */ + csr32w(ctlr, Rier, Ie); +err: + ; +} + +static void +eeclk(Ctlr *ctlr, int clk) +{ + csr32w(ctlr, Rmear, Eesel | clk); + microdelay(2); +} + +static void +eeidle(Ctlr *ctlr) +{ + int i; + + eeclk(ctlr, 0); + eeclk(ctlr, Eeclk); + for(i=0; i<25; i++){ + eeclk(ctlr, 0); + eeclk(ctlr, Eeclk); + } + eeclk(ctlr, 0); + csr32w(ctlr, Rmear, 0); + microdelay(2); +} + +static int +eegetw(Ctlr *ctlr, int a) +{ + int d, i, w, v; + + eeidle(ctlr); + eeclk(ctlr, 0); + eeclk(ctlr, Eeclk); + d = 0x180 | a; + for(i=0x400; i; i>>=1){ + v = (d & i) ? Eedi : 0; + eeclk(ctlr, v); + eeclk(ctlr, Eeclk|v); + } + eeclk(ctlr, 0); + + w = 0; + for(i=0x8000; i; i >>= 1){ + eeclk(ctlr, Eeclk); + if(csr32r(ctlr, Rmear) & Eedo) + w |= i; + microdelay(2); + eeclk(ctlr, 0); + } + eeidle(ctlr); + return w; +} + +static void +resetctlr(Ctlr *ctlr) +{ + int i; + + csr32w(ctlr, Rcr, Rst); + for(i=0;; i++){ + if(i > 100) + panic("ns83815: soft reset did not complete"); + microdelay(250); + if((csr32r(ctlr, Rcr) & Rst) == 0) + break; + delay(1); + } +} + +static void +shutdown(Ether* ether) +{ + Ctlr *ctlr = ether->ctlr; + +print("ether83815 shutting down\n"); + csr32w(ctlr, Rcr, Rxd|Txd); /* disable transceiver */ + resetctlr(ctlr); +} + +static void +softreset(Ctlr* ctlr, int resetphys) +{ + int i, w; + + /* + * Soft-reset the controller + */ + resetctlr(ctlr); + csr32w(ctlr, Rccsr, Pmests); + csr32w(ctlr, Rccsr, 0); + csr32w(ctlr, Rcfg, csr32r(ctlr, Rcfg) | Pint_acen); + + if(resetphys){ + /* + * Soft-reset the PHY + */ + csr32w(ctlr, Rbmcr, Reset); + for(i=0;; i++){ + if(i > 100) + panic("ns83815: PHY soft reset time out"); + if((csr32r(ctlr, Rbmcr) & Reset) == 0) + break; + delay(1); + } + } + + /* + * Initialisation values, in sequence (see 4.4 Recommended Registers Configuration) + */ + csr16w(ctlr, 0xCC, 0x0001); /* PGSEL */ + csr16w(ctlr, 0xE4, 0x189C); /* PMCCSR */ + csr16w(ctlr, 0xFC, 0x0000); /* TSTDAT */ + csr16w(ctlr, 0xF4, 0x5040); /* DSPCFG */ + csr16w(ctlr, 0xF8, 0x008C); /* SDCFG */ + + /* + * Auto negotiate + */ + w = csr16r(ctlr, Rbmsr); /* clear latched bits */ + debug("anar: %4.4ux\n", csr16r(ctlr, Ranar)); + csr16w(ctlr, Rbmcr, Anena); + if(csr16r(ctlr, Ranar) == 0 || (csr32r(ctlr, Rcfg) & Aneg_dn) == 0){ + csr16w(ctlr, Rbmcr, Anena|Anrestart); + for(i=0;; i++){ + if(i > 6000){ + print("ns83815: auto neg timed out\n"); + break; + } + if((w = csr16r(ctlr, Rbmsr)) & Ancomp) + break; + delay(1); + } + debug("%d ms\n", i); + w &= 0xFFFF; + debug("bmsr: %4.4ux\n", w); + } + USED(w); + debug("anar: %4.4ux\n", csr16r(ctlr, Ranar)); + debug("anlpar: %4.4ux\n", csr16r(ctlr, Ranlpar)); + debug("aner: %4.4ux\n", csr16r(ctlr, Raner)); + debug("physts: %4.4ux\n", csr16r(ctlr, Rphysts)); + debug("tbscr: %4.4ux\n", csr16r(ctlr, Rtbscr)); +} + +static int +media(Ether* ether) +{ + Ctlr* ctlr; + ulong cfg; + + ctlr = ether->ctlr; + cfg = csr32r(ctlr, Rcfg); + ctlr->fd = (cfg & Fdup) != 0; + if(cfg & Speed100) + return 100; + if((cfg & Lnksts) == 0) + return 100; /* no link: use 100 to ensure larger queues */ + return 10; +} + +static char* mediatable[9] = { + "10BASE-T", /* TP */ + "10BASE-2", /* BNC */ + "10BASE-5", /* AUI */ + "100BASE-TX", + "10BASE-TFD", + "100BASE-TXFD", + "100BASE-T4", + "100BASE-FX", + "100BASE-FXFD", +}; + +static int +is630(ulong id, Pcidev *p) +{ + if(id == SiS900) + switch (p->rid) { + case SiSrev630s: + case SiSrev630e: + case SiSrev630ea1: + return 1; + } + return 0; +} + +enum { + MagicReg = 0x48, + MagicRegSz = 1, + Magicrden = 0x40, /* read enable, apparently */ + Paddr= 0x70, /* address port */ + Pdata= 0x71, /* data port */ + Pcinetctlr = 2, +}; + +/* rcmos() originally from LANL's SiS 900 driver's rcmos() */ +static int +sisrdcmos(Ctlr *ctlr) +{ + int i; + unsigned reg; + ulong port; + Pcidev *p; + + debug("ns83815: SiS 630 rev. %ux reading mac address from cmos\n", ctlr->pcidev->rid); + p = pcimatch(nil, SiS, SiS630bridge); + if(p == nil) { + print("ns83815: no SiS 630 rev. %ux bridge for mac addr\n", + ctlr->pcidev->rid); + return 0; + } + port = p->mem[0].bar & ~0x01; + debug("ns83815: SiS 630 rev. %ux reading mac addr from cmos via bridge at port 0x%lux\n", ctlr->pcidev->rid, port); + + reg = pcicfgr8(p, MagicReg); + pcicfgw8(p, MagicReg, reg|Magicrden); + + for (i = 0; i < Eaddrlen; i++) { + outb(port+Paddr, SiS630eenodeaddr + i); + ctlr->sromea[i] = inb(port+Pdata); + } + + pcicfgw8(p, MagicReg, reg & ~Magicrden); + return 1; +} + +/* + * If this is a SiS 630E chipset with an embedded SiS 900 controller, + * we have to read the MAC address from the APC CMOS RAM. - sez freebsd. + * However, CMOS *is* NVRAM normally. See devrtc.c:440, memory.c:88. + */ +static void +sissrom(Ctlr *ctlr) +{ + union { + uchar eaddr[Eaddrlen]; + ushort alignment; + } ee; + int i, off = SiSeenodeaddr, cnt = sizeof ee.eaddr / sizeof(short); + ushort *shp = (ushort *)ee.eaddr; + + if(!is630(ctlr->id, ctlr->pcidev) || !sisrdcmos(ctlr)) { + for (i = 0; i < cnt; i++) + *shp++ = eegetw(ctlr, off++); + memmove(ctlr->sromea, ee.eaddr, sizeof ctlr->sromea); + } +} + +static void +nssrom(Ctlr* ctlr) +{ + int i, j; + + for(i = 0; i < nelem(ctlr->srom); i++) + ctlr->srom[i] = eegetw(ctlr, i); + + /* + * the MAC address is reversed, straddling word boundaries + */ + j = Nseenodeaddr*16 + 15; + for(i=0; i<48; i++){ + ctlr->sromea[i>>3] |= ((ctlr->srom[j>>4] >> (15-(j&0xF))) & 1) << (i&7); + j++; + } +} + +static void +srom(Ctlr* ctlr) +{ + memset(ctlr->sromea, 0, sizeof(ctlr->sromea)); + switch (ctlr->id) { + case SiS900: + case SiS7016: + sissrom(ctlr); + break; + case Nat83815: + nssrom(ctlr); + break; + default: + print("ns83815: srom: unknown id 0x%ux\n", ctlr->id); + break; + } +} + +static void +scanpci83815(void) +{ + Ctlr *ctlr; + Pcidev *p; + ulong id; + + p = nil; + while(p = pcimatch(p, 0, 0)){ + /* ccru is a short in the FS kernel, thus the cast to uchar */ + if (p->ccrb != Pcinetctlr || (uchar)p->ccru != 0) + continue; /* not a nic */ + id = (p->did<<16)|p->vid; + switch(id){ + default: + continue; + case Nat83815: + case SiS900: + break; + } + + /* + * bar[0] is the I/O port register address and + * bar[1] is the memory-mapped register address. + */ + ctlr = mallocz(sizeof(Ctlr), 1); + ctlr->port = p->mem[0].bar & ~0x01; + ctlr->pcidev = p; + ctlr->id = id; + + if(ioalloc(ctlr->port, p->mem[0].size, 0, "ns83815") < 0){ + print("ns83815: port 0x%uX in use\n", ctlr->port); + free(ctlr); + continue; + } + + softreset(ctlr, 0); + srom(ctlr); + + if(ctlrhead != nil) + ctlrtail->next = ctlr; + else + ctlrhead = ctlr; + ctlrtail = ctlr; + } +} + +/* multicast already on, don't need to do anything */ +static void +multicast(void*, uchar*, int) +{ +} + +int +dp83815reset(Ether* ether) +{ + Ctlr *ctlr; + int i, x; + ulong ctladdr; + uchar ea[Eaddrlen]; + static int scandone; + + if(scandone == 0){ + scanpci83815(); + scandone = 1; + } + + /* + * Any adapter matches if no ether->port is supplied, + * otherwise the ports must match. + */ + for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + if(ether->port == 0 || ether->port == ctlr->port){ + ctlr->active = 1; + break; + } + } + if(ctlr == nil) + return -1; + + ether->ctlr = ctlr; + ether->port = ctlr->port; + ether->irq = ctlr->pcidev->intl; + ether->tbdf = ctlr->pcidev->tbdf; + + /* + * Check if the adapter's station address is to be overridden. + * If not, read it from the EEPROM and set in ether->ea prior to + * loading the station address in the hardware. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, ether->ea, Eaddrlen) == 0) + memmove(ether->ea, ctlr->sromea, Eaddrlen); + for(i=0; iea[i] | (ether->ea[i+1]<<8); + ctladdr = (ctlr->id == Nat83815? i: i<<15); + csr32w(ctlr, Rrfcr, ctladdr); + csr32w(ctlr, Rrfdr, x); + } + csr32w(ctlr, Rrfcr, Rfen|Apm|Aab|Aam); + + ether->mbps = media(ether); + + /* + * Look for a medium override in case there's no autonegotiation + * the autonegotiation fails. + */ + + for(i = 0; i < ether->nopt; i++){ + if(cistrcmp(ether->opt[i], "FD") == 0){ + ctlr->fd = 1; + continue; + } + for(x = 0; x < nelem(mediatable); x++){ + debug("compare <%s> <%s>\n", mediatable[x], + ether->opt[i]); + if(cistrcmp(mediatable[x], ether->opt[i]) == 0){ + if(x != 4 && x >= 3) + ether->mbps = 100; + else + ether->mbps = 10; + switch(x){ + default: + ctlr->fd = 0; + break; + + case 0x04: /* 10BASE-TFD */ + case 0x05: /* 100BASE-TXFD */ + case 0x08: /* 100BASE-FXFD */ + ctlr->fd = 1; + break; + } + break; + } + } + } + + /* + * Initialise descriptor rings, ethernet address. + */ + ctlr->nrdr = Nrde; + ctlr->ntdr = Ntde; + pcisetbme(ctlr->pcidev); + ctlrinit(ether); + + /* + * Linkage to the generic ethernet driver. + */ + ether->attach = attach; + ether->transmit = transmit; + ether->interrupt = interrupt; +#ifndef FS + ether->ifstat = ifstat; + + ether->arg = ether; + ether->promiscuous = promiscuous; + ether->multicast = multicast; + ether->shutdown = shutdown; +#endif + debug("ns83815: dp83815reset: done\n"); + return 0; +} + +#ifndef FS +void +ether83815link(void) +{ + addethercard("83815", dp83815reset); +} +#endif diff -Nru /sys/src/fs/pc/ether83815.mii.c /sys/src/fs/pc/ether83815.mii.c --- /sys/src/fs/pc/ether83815.mii.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/pc/ether83815.mii.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,1363 @@ +/* + * National Semiconductor DP83815 + * + * Supports only internal PHY and has been tested on: + * Netgear FA311TX (using Netgear DS108 10/100 hub, and using switches) + * SiS 900 within SiS 630 (works under light load only) + * To do: + * check Ethernet address; + * test autonegotiation on 10 Mbit, and 100 Mbit full duplex; + * thresholds; + * ring sizing; + * push initialisation back to attach. + * + * C H Forsyth, forsyth@vitanuova.com, 18th June 2001. + * made to drive the SiS 900, including using common MII/PHY code, + * by Geoff Collyer, March 2003. + */ + +#ifdef FS +#include "all.h" +#include "io.h" +#include "mem.h" +#include "../ip/ip.h" +#else /* FS */ +#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" +#endif /* FS */ + +#include "etherif.h" +#include "ethermii.h" + +#include "compat.h" + +enum { + Debug = 0, + Nrde = 64, + Ntde = 64, + Rbsz = ROUNDUP(sizeof(Etherpkt)+4, 4), +}; + +#define debug if(Debug)print + +typedef struct Des { + ulong next; + int cmdsts; + ulong addr; + Block* bp; +} Des; + +enum { /* cmdsts */ + Own = 1<<31, /* set by data producer to hand to consumer */ + More = 1<<30, /* more of packet in next descriptor */ + Intr = 1<<29, /* interrupt when device is done with it */ + Supcrc = 1<<28, /* suppress crc on transmit */ + Inccrc = 1<<28, /* crc included on receive (always) */ + Ok = 1<<27, /* packet ok */ + Size = 0xFFF, /* packet size in bytes */ + + /* transmit */ + Txa = 1<<26, /* transmission aborted */ + Tfu = 1<<25, /* transmit fifo underrun */ + Crs = 1<<24, /* carrier sense lost */ + Td = 1<<23, /* transmission deferred */ + Ed = 1<<22, /* excessive deferral */ + Owc = 1<<21, /* out of window collision */ + Ec = 1<<20, /* excessive collisions */ + /* 19-16 collision count */ + + /* receive */ + Rxa = 1<<26, /* receive aborted (same as Rxo) */ + Rxo = 1<<25, /* receive overrun */ + Dest = 3<<23, /* destination class */ + Drej= 0<<23, /* packet was rejected */ + Duni= 1<<23, /* unicast */ + Dmulti= 2<<23, /* multicast */ + Dbroad= 3<<23, /* broadcast */ + Long = 1<<22, /* too long packet received */ + Runt = 1<<21, /* packet less than 64 bytes */ + Ise = 1<<20, /* invalid symbol */ + Crce = 1<<19, /* invalid crc */ + Fae = 1<<18, /* frame alignment error */ + Lbp = 1<<17, /* loopback packet */ + Col = 1<<16, /* collision during receive */ +}; + +enum { + // PCI vendor & device IDs + NatSemi = 0x100B, + Nat83815 = (0x20<<16)|NatSemi, + SiS = 0x1039, + SiS900 = (0x900<<16)|SiS, + SiS7016 = (0x7016<<16)|SiS, + SiS630bridge = 0x0008, + + // SiS 900 PCI revision codes + SiSrev630s = 0x81, + SiSrev630e = 0x82, + SiSrev630ea1 = 0x83, + + SiSeenodeaddr = 8, // short addr of SiS eeprom mac addr + SiS630eenodeaddr = 9, // likewise for the 630 + Nseenodeaddr = 6, // " for NS eeprom +}; + +typedef struct Ctlr Ctlr; +typedef struct Ctlr { + int port; + Pcidev* pcidev; + Ctlr* next; + int active; + int id; /* (pcidev->did<<16)|pcidev->vid */ + + ushort srom[0xB+1]; + uchar sromea[Eaddrlen]; /* MAC address */ + + uchar fd; /* option or auto negotiation */ + + int mbps; + + Lock lock; + Mii* mii; + + Des* rdr; /* receive descriptor ring */ + int nrdr; /* size of rdr */ + int rdrx; /* index into rdr */ + + Lock tlock; + Des* tdr; /* transmit descriptor ring */ + int ntdr; /* size of tdr */ + int tdrh; /* host index into tdr */ + int tdri; /* interface index into tdr */ + int ntq; /* descriptors active */ + int ntqmax; + + ulong rxa; /* receive statistics */ + ulong rxo; + ulong rlong; + ulong runt; + ulong ise; + ulong crce; + ulong fae; + ulong lbp; + ulong col; + ulong rxsovr; + ulong rxorn; + + ulong txa; /* transmit statistics */ + ulong tfu; + ulong crs; + ulong td; + ulong ed; + ulong owc; + ulong ec; + ulong txurn; + + ulong dperr; /* system errors */ + ulong rmabt; + ulong rtabt; + ulong sserr; + ulong rxsover; +} Ctlr; + +static Ctlr* ctlrhead; +static Ctlr* ctlrtail; + +enum { + /* registers (could memory map) */ + Rcr= 0x00, /* command register */ + Rst= 1<<8, + Rxr= 1<<5, /* receiver reset */ + Txr= 1<<4, /* transmitter reset */ + Rxd= 1<<3, /* receiver disable */ + Rxe= 1<<2, /* receiver enable */ + Txd= 1<<1, /* transmitter disable */ + Txe= 1<<0, /* transmitter enable */ + Rcfg= 0x04, /* configuration */ + Lnksts= 1<<31, /* link good */ + Speed100= 1<<30, /* 100 Mb/s link */ + Fdup= 1<<29, /* full duplex */ + Pol= 1<<28, /* polarity reversal (10baseT) */ + Aneg_dn= 1<<27, /* autonegotiation done */ + Pint_acen= 1<<17, /* PHY interrupt auto clear enable */ + Pause_adv= 1<<16, /* advertise pause during auto neg */ + Paneg_ena= 1<<13, /* auto negotiation enable */ + Paneg_all= 7<<13, /* auto negotiation enable 10/100 half & full */ + Ext_phy= 1<<12, /* enable MII for external PHY */ + Phy_rst= 1<<10, /* reset internal PHY */ + Phy_dis= 1<<9, /* disable internal PHY (eg, low power) */ + Req_alg= 1<<7, /* PCI bus request: set means less aggressive */ + Sb= 1<<6, /* single slot back-off not random */ + Pow= 1<<5, /* out of window timer selection */ + Exd= 1<<4, /* disable excessive deferral timer */ + Pesel= 1<<3, /* parity error algorithm selection */ + Brom_dis= 1<<2, /* disable boot rom interface */ + Bem= 1<<0, /* big-endian mode */ + Rmear= 0x08, /* eeprom access */ + Mdc= 1<<6, /* MII mangement check */ + Mddir= 1<<5, /* MII management direction */ + Mdio= 1<<4, /* MII mangement data */ + Eesel= 1<<3, /* EEPROM chip select */ + Eeclk= 1<<2, /* EEPROM clock */ + Eedo= 1<<1, /* EEPROM data out (from chip) */ + Eedi= 1<<0, /* EEPROM data in (to chip) */ + Rptscr= 0x0C, /* pci test control */ + Risr= 0x10, /* interrupt status */ + Txrcmp= 1<<25, /* transmit reset complete */ + Rxrcmp= 1<<24, /* receiver reset complete */ + Dperr= 1<<23, /* detected parity error */ + Sserr= 1<<22, /* signalled system error */ + Rmabt= 1<<21, /* received master abort */ + Rtabt= 1<<20, /* received target abort */ + Rxsovr= 1<<16, /* RX status FIFO overrun */ + Hiberr= 1<<15, /* high bits error set (OR of 25-16) */ + Phy= 1<<14, /* PHY interrupt */ + Pme= 1<<13, /* power management event (wake online) */ + Swi= 1<<12, /* software interrupt */ + Mib= 1<<11, /* MIB service */ + Txurn= 1<<10, /* TX underrun */ + Txidle= 1<<9, /* TX idle */ + Txerr= 1<<8, /* TX packet error */ + Txdesc= 1<<7, /* TX descriptor (with Intr bit done) */ + Txok= 1<<6, /* TX ok */ + Rxorn= 1<<5, /* RX overrun */ + Rxidle= 1<<4, /* RX idle */ + Rxearly= 1<<3, /* RX early threshold */ + Rxerr= 1<<2, /* RX packet error */ + Rxdesc= 1<<1, /* RX descriptor (with Intr bit done) */ + Rxok= 1<<0, /* RX ok */ + Rimr= 0x14, /* interrupt mask */ + Rier= 0x18, /* interrupt enable */ + Ie= 1<<0, /* interrupt enable */ + Rtxdp= 0x20, /* transmit descriptor pointer */ + Rtxcfg= 0x24, /* transmit configuration */ + Csi= 1<<31, /* carrier sense ignore (needed for full duplex) */ + Hbi= 1<<30, /* heartbeat ignore (needed for full duplex) */ + Atp= 1<<28, /* automatic padding of runt packets */ + Mxdma= 7<<20, /* maximum dma transfer field */ + Mxdma32= 4<<20, /* 4x32-bit words (32 bytes) */ + Mxdma64= 5<<20, /* 8x32-bit words (64 bytes) */ + Flth= 0x3F<<8, /* Tx fill threshold, units of 32 bytes (must be > Mxdma) */ + Drth= 0x3F<<0, /* Tx drain threshold (units of 32 bytes) */ + Flth128= 4<<8, /* fill at 128 bytes */ + // seems to be the same on SiS 900; maybe use larger value @ 100Mb/s + Drth512= 16<<0, /* drain at 512 bytes */ + Rrxdp= 0x30, /* receive descriptor pointer */ + Rrxcfg= 0x34, /* receive configuration */ + Atx= 1<<28, /* accept transmit packets (needed for full duplex) */ + Rdrth= 0x1F<<1, /* Rx drain threshold (units of 32 bytes) */ + Rdrth64= 2<<1, /* drain at 64 bytes */ + Rccsr= 0x3C, /* CLKRUN control/status */ + Pmests= 1<<15, /* PME status */ + Rwcsr= 0x40, /* wake on lan control/status */ + Rpcr= 0x44, /* pause control/status */ + // TODO: different on SiS, but does it matter? + Rrfcr= 0x48, /* receive filter/match control */ + Rfen= 1<<31, /* receive filter enable */ + Aab= 1<<30, /* accept all broadcast */ + Aam= 1<<29, /* accept all multicast */ + Aau= 1<<28, /* accept all unicast */ + Apm= 1<<27, /* accept on perfect match */ + Apat= 0xF<<23, /* accept on pattern match */ + Aarp= 1<<22, /* accept ARP */ + Mhen= 1<<21, /* multicast hash enable */ + Uhen= 1<<20, /* unicast hash enable */ + Ulm= 1<<19, /* U/L bit mask */ + /* bits 0-9 are rfaddr */ + Rrfdr= 0x4C, /* receive filter/match data */ + Rbrar= 0x50, /* boot rom address */ + Rbrdr= 0x54, /* boot rom data */ + Rsrr= 0x58, /* silicon revision */ + Rmibc= 0x5C, /* MIB control */ + /* 60-78 MIB data */ + + /* PHY registers */ + Rbmcr= 0x80, /* basic mode configuration */ + Reset= 1<<15, + Sel100= 1<<13, /* select 100Mb/sec if no auto neg */ + Anena= 1<<12, /* auto negotiation enable */ + Anrestart= 1<<9, /* restart auto negotiation */ + Selfdx= 1<<8, /* select full duplex if no auto neg */ + Rbmsr= 0x84, /* basic mode status */ + Ancomp= 1<<5, /* autonegotiation complete */ + Rphyidr1= 0x88, + Rphyidr2= 0x8C, + Ranar= 0x90, /* autonegotiation advertisement */ + Ranlpar= 0x94, /* autonegotiation link partner ability */ + Raner= 0x98, /* autonegotiation expansion */ + Rannptr= 0x9C, /* autonegotiation next page TX */ + Rphysts= 0xC0, /* PHY status */ + Rmicr= 0xC4, /* MII control */ + Inten= 1<<1, /* PHY interrupt enable */ + Rmisr= 0xC8, /* MII status */ + Rfcscr= 0xD0, /* false carrier sense counter */ + Rrecr= 0xD4, /* receive error counter */ + Rpcsr= 0xD8, /* 100Mb config/status */ + Rphycr= 0xE4, /* PHY control */ + Rtbscr= 0xE8, /* 10BaseT status/control */ +}; + +/* + * eeprom addresses + * 7 to 9 (16 bit words): mac address, shifted and reversed + */ + +#define csr32r(c, r) (inl((c)->port+(r))) +#define csr32w(c, r, l) (outl((c)->port+(r), (ulong)(l))) +#define csr16r(c, r) (ins((c)->port+(r))) +#define csr16w(c, r, l) (outs((c)->port+(r), (ulong)(l))) + +static void +dumpcregs(Ctlr *ctlr) +{ + int i; + + for(i=0; i<=0x5C; i+=4) + print("%2.2ux %8.8lux\n", i, csr32r(ctlr, i)); +} + +// TODO: finish this +static void +promiscuous(void* arg, int on) +{ + Ctlr *ctlr; + ulong w; + + ctlr = ((Ether*)arg)->ctlr; + ilock(&ctlr->lock); + w = csr32r(ctlr, Rrfcr); + if(on != ((w&Aau)!=0)){ + csr32w(ctlr, Rrfcr, w & ~Rfen); + csr32w(ctlr, Rrfcr, Rfen | (w ^ Aau)); + } + iunlock(&ctlr->lock); +} + +static void +attach(Ether* ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(&ctlr->lock); + if(0) + dumpcregs(ctlr); + csr32w(ctlr, Rcr, Rxe); + iunlock(&ctlr->lock); +} + +#ifndef FS +static long +ifstat(Ether* ether, void* a, long n, ulong offset) +{ + Ctlr *ctlr; + char *buf, *p; + int i, l, len; + + ctlr = ether->ctlr; + + ether->crcs = ctlr->crce; + ether->frames = ctlr->runt+ctlr->ise+ctlr->rlong+ctlr->fae; + ether->buffs = ctlr->rxorn+ctlr->tfu; + ether->overflows = ctlr->rxsovr; + + if(n == 0) + return 0; + + p = malloc(READSTR); + l = snprint(p, READSTR, "Rxa: %lud\n", ctlr->rxa); + l += snprint(p+l, READSTR-l, "Rxo: %lud\n", ctlr->rxo); + l += snprint(p+l, READSTR-l, "Rlong: %lud\n", ctlr->rlong); + l += snprint(p+l, READSTR-l, "Runt: %lud\n", ctlr->runt); + l += snprint(p+l, READSTR-l, "Ise: %lud\n", ctlr->ise); + l += snprint(p+l, READSTR-l, "Fae: %lud\n", ctlr->fae); + l += snprint(p+l, READSTR-l, "Lbp: %lud\n", ctlr->lbp); + l += snprint(p+l, READSTR-l, "Tfu: %lud\n", ctlr->tfu); + l += snprint(p+l, READSTR-l, "Txa: %lud\n", ctlr->txa); + l += snprint(p+l, READSTR-l, "CRC Error: %lud\n", ctlr->crce); + l += snprint(p+l, READSTR-l, "Collision Seen: %lud\n", ctlr->col); + l += snprint(p+l, READSTR-l, "Frame Too Long: %lud\n", ctlr->rlong); + l += snprint(p+l, READSTR-l, "Runt Frame: %lud\n", ctlr->runt); + l += snprint(p+l, READSTR-l, "Rx Underflow Error: %lud\n", ctlr->rxorn); + l += snprint(p+l, READSTR-l, "Tx Underrun: %lud\n", ctlr->txurn); + l += snprint(p+l, READSTR-l, "Excessive Collisions: %lud\n", ctlr->ec); + l += snprint(p+l, READSTR-l, "Late Collision: %lud\n", ctlr->owc); + l += snprint(p+l, READSTR-l, "Loss of Carrier: %lud\n", ctlr->crs); + l += snprint(p+l, READSTR-l, "Parity: %lud\n", ctlr->dperr); + l += snprint(p+l, READSTR-l, "Aborts: %lud\n", ctlr->rmabt+ctlr->rtabt); + l += snprint(p+l, READSTR-l, "RX Status overrun: %lud\n", ctlr->rxsover); + snprint(p+l, READSTR-l, "ntqmax: %d\n", ctlr->ntqmax); + ctlr->ntqmax = 0; + buf = a; + len = readstr(offset, buf, n, p); + if(offset > l) + offset -= l; + else + offset = 0; + buf += len; + n -= len; + + l = snprint(p, READSTR, "srom:"); + for(i = 0; i < nelem(ctlr->srom); i++){ + if(i && ((i & 0x0F) == 0)) + l += snprint(p+l, READSTR-l, "\n "); + l += snprint(p+l, READSTR-l, " %4.4uX", ctlr->srom[i]); + } + + snprint(p+l, READSTR-l, "\n"); + len += readstr(offset, buf, n, p); + free(p); + + return len; +} +#endif + +/* always called under ilock(ðer->ctlr->tlock) */ +static void +txstart(Ether* ether) +{ + Ctlr *ctlr; + Block *bp; + Des *des; + int started; + + ctlr = ether->ctlr; + started = 0; + while(ctlr->ntq < ctlr->ntdr-1){ + bp = etheroq(ether); + if(bp == nil) + break; + des = &ctlr->tdr[ctlr->tdrh]; + des->bp = bp; + des->addr = PADDR(bp->rp); + // debug("ns83815: txstart: des->addr %lux\n", des->addr); + ctlr->ntq++; + coherence(); + des->cmdsts = Own | BLEN(bp); + // debug("ns83815: txstart: des->cmdsts %ux\n", des->cmdsts); + ctlr->tdrh = NEXT(ctlr->tdrh, ctlr->ntdr); + started = 1; + } + if(started){ + coherence(); + csr32w(ctlr, Rcr, Txe); /* prompt */ + } + + if(ctlr->ntq > ctlr->ntqmax) + ctlr->ntqmax = ctlr->ntq; +} + +static void +transmit(Ether* ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(&ctlr->tlock); + txstart(ether); + iunlock(&ctlr->tlock); +} + +// TODO: finish this; set speed too +static void +txrxcfg(Ctlr *ctlr, int txdrth) +{ + ulong rx, tx; + + rx = csr32r(ctlr, Rrxcfg); + tx = csr32r(ctlr, Rtxcfg); + if(ctlr->fd){ + rx |= Atx; + tx |= Csi | Hbi; + }else{ + rx &= ~Atx; + tx &= ~(Csi | Hbi); + } + tx &= ~(Mxdma|Drth|Flth); + tx |= Mxdma64 | Flth128 | txdrth; + csr32w(ctlr, Rtxcfg, tx); + rx &= ~(Mxdma|Rdrth); + rx |= Mxdma64 | Rdrth64; + csr32w(ctlr, Rrxcfg, rx); +} + +static void +interrupt(Ureg*, void* arg) +{ + Ctlr *ctlr; + Ether *ether; + int len, status, cmdsts, iter = 0; + Des *des; + Block *bp; + + ether = arg; + ctlr = ether->ctlr; + while((status = csr32r(ctlr, Risr)) != 0){ + if (iter++ >= 100) + print("ns83815: interrupt: >= 100 iterations\n"); + status &= ~(Pme|Mib); + if(status & Hiberr){ + if(status & Rxsovr) + ctlr->rxsover++; + if(status & Sserr) + ctlr->sserr++; + if(status & Dperr) + ctlr->dperr++; + if(status & Rmabt) + ctlr->rmabt++; + if(status & Rtabt) + ctlr->rtabt++; + status &= ~(Hiberr|Txrcmp|Rxrcmp|Rxsovr|Dperr|Sserr|Rmabt|Rtabt); + } + + /* + * Received packets. + */ + if(status & (Rxdesc|Rxok|Rxerr|Rxearly|Rxorn)){ + des = &ctlr->rdr[ctlr->rdrx]; + while((cmdsts = des->cmdsts) & Own){ + if((cmdsts&Ok) == 0){ + if(cmdsts & Rxa) + ctlr->rxa++; + if(cmdsts & Rxo) + ctlr->rxo++; + if(cmdsts & Long) + ctlr->rlong++; + if(cmdsts & Runt) + ctlr->runt++; + if(cmdsts & Ise) + ctlr->ise++; + if(cmdsts & Crce) + ctlr->crce++; + if(cmdsts & Fae) + ctlr->fae++; + if(cmdsts & Lbp) + ctlr->lbp++; + if(cmdsts & Col) + ctlr->col++; + } + else if(bp = iallocb(Rbsz)){ + len = (cmdsts&Size)-4; + if (len <= 0) { + print( + "ns83815: interrupt: packet len %d <= 0\n", + len); + freeb(des->bp); /* toss it */ + } else { + SETWPCNT(des->bp, len); + ETHERIQ(ether, des->bp, 1); + } + /* replace just-queued/freed packet */ + des->bp = bp; + des->addr = PADDR(bp->rp); +// debug( +// "ns83815: interrupt: packet into input q, new des->addr %lux\n", +// des->addr); + coherence(); + } else { + print( + "ns83815: interrupt: iallocb for input buffer failed\n"); + /* + * prevent accidents & ignore this + * packet. it will be overwritten. + */ + des->bp->next = 0; + } + + des->cmdsts = Rbsz; + coherence(); + + ctlr->rdrx = NEXT(ctlr->rdrx, ctlr->nrdr); + des = &ctlr->rdr[ctlr->rdrx]; + } + status &= ~(Rxdesc|Rxok|Rxerr|Rxearly|Rxorn); + } + + /* + * Check the transmit side: + * check for Transmit Underflow and Adjust + * the threshold upwards; + * free any transmitted buffers and try to + * top-up the ring. + */ + if(status & Txurn){ + ctlr->txurn++; + ilock(&ctlr->lock); + /* change threshold */ + iunlock(&ctlr->lock); + status &= ~(Txurn); + } + + ilock(&ctlr->tlock); + while(ctlr->ntq > 0){ + des = &ctlr->tdr[ctlr->tdri]; + cmdsts = des->cmdsts; + if(cmdsts & Own) + break; + + if((cmdsts & Ok) == 0){ + if(cmdsts & Txa) + ctlr->txa++; + if(cmdsts & Tfu) + ctlr->tfu++; + if(cmdsts & Td) + ctlr->td++; + if(cmdsts & Ed) + ctlr->ed++; + if(cmdsts & Owc) + ctlr->owc++; + if(cmdsts & Ec) + ctlr->ec++; +#ifndef FS + ether->oerrs++; +#endif + } + +// debug("ns83815: interrupt: done output for des->addr %lux\n", +// des->addr); + if (des->bp == nil) + panic("83815 interrupt: nil des->bp"); + freeb(des->bp); + des->bp = nil; + des->cmdsts = 0; + + ctlr->ntq--; + ctlr->tdri = NEXT(ctlr->tdri, ctlr->ntdr); + } + txstart(ether); + iunlock(&ctlr->tlock); + + if((status & Phy) && ctlr->mii != nil){ + ctlr->mii->mir(ctlr->mii, 1, Bmsr); + print("phy: cfg %8.8lux bmsr %4.4ux\n", + csr32r(ctlr, Rcfg), + ctlr->mii->mir(ctlr->mii, 1, Bmsr)); + /* TODO: set speed too; can't do it yet */ + txrxcfg(ctlr, Drth512); /* set duplicity */ + status &= ~Phy; + } + + status &= ~(Txurn|Txidle|Txerr|Txdesc|Txok); + + /* + * Anything left not catered for? + */ + if(status) + print("#l%d: status %8.8ux\n", ether->ctlrno, status); + } +} + +static void +ctlrinit(Ether* ether) +{ + Ctlr *ctlr; + Des *des, *last; + + ctlr = ether->ctlr; + + /* + * Allocate and initialise the receive ring; + * allocate and initialise the transmit ring; + * unmask interrupts and start the transmit side + */ + ctlr->rdr = malloc(ctlr->nrdr*sizeof(Des)); + if(ctlr->rdr == nil) { + print("ns83815: ctlrinit: iallocb of rcv. descs. failed\n"); + return; + } + last = nil; + for(des = ctlr->rdr; des < &ctlr->rdr[ctlr->nrdr]; des++){ + des->bp = iallocb(Rbsz); + if (des->bp == nil) { + print("ns83815: ctlrinit: iallocb(%d) failed\n", Rbsz); + return; + } + des->cmdsts = Rbsz; + des->addr = PADDR(des->bp->rp); + if(last != nil) + last->next = PADDR(des); + last = des; + } + ctlr->rdr[ctlr->nrdr-1].next = PADDR(ctlr->rdr); + ctlr->rdrx = 0; + csr32w(ctlr, Rrxdp, PADDR(ctlr->rdr)); + + ctlr->tdr = xspanalloc(ctlr->ntdr*sizeof(Des), 8*sizeof(ulong), 0); + last = nil; + for(des = ctlr->tdr; des < &ctlr->tdr[ctlr->ntdr]; des++){ + des->cmdsts = 0; + des->bp = nil; + des->addr = ~0; + if(last != nil) + last->next = PADDR(des); + last = des; + } + ctlr->tdr[ctlr->ntdr-1].next = PADDR(ctlr->tdr); + ctlr->tdrh = 0; + ctlr->tdri = 0; + csr32w(ctlr, Rtxdp, PADDR(ctlr->tdr)); + + txrxcfg(ctlr, Drth512); + +// TODO: finish this +// MII + csr32w(ctlr, Rimr, Dperr|Sserr|Rmabt|Rtabt|Rxsovr|Hiberr|Txurn|Txerr| + Txdesc|Txok|Rxorn|Rxerr|Rxdesc|Rxok); /* Phy|Pme|Mib */ + csr32r(ctlr, Risr); /* clear status */ + csr32w(ctlr, Rier, Ie); + debug("ns83815: ctlrinit: set Ie, done\n"); +} + +/* + * EEPROM + */ + +static void +eeclk(Ctlr *ctlr, int clk) +{ + csr32w(ctlr, Rmear, Eesel | clk); + microdelay(2); +} + +static void +eeidle(Ctlr *ctlr) +{ + int i; + + eeclk(ctlr, 0); + eeclk(ctlr, Eeclk); + for(i=0; i<25; i++){ + eeclk(ctlr, 0); + eeclk(ctlr, Eeclk); + } + eeclk(ctlr, 0); + csr32w(ctlr, Rmear, 0); + microdelay(2); +} + +static int +eegetw(Ctlr *ctlr, int a) +{ + int d, i, w; + + eeidle(ctlr); + eeclk(ctlr, 0); + eeclk(ctlr, Eeclk); + d = 0x180 | a; // read EEPROM at address `a' + for(i=0x400; i; i>>=1){ + if(d & i) + csr32w(ctlr, Rmear, Eesel|Eedi); + else + csr32w(ctlr, Rmear, Eesel); + eeclk(ctlr, Eeclk); + eeclk(ctlr, 0); + microdelay(2); + } + w = 0; + for(i=0x8000; i; i >>= 1){ + eeclk(ctlr, Eeclk); + if(csr32r(ctlr, Rmear) & Eedo) + w |= i; + microdelay(2); + eeclk(ctlr, 0); + } + eeidle(ctlr); + return w; +} + + +static void +softreset(Ctlr* ctlr, int resetphys) +{ + int i, w; + + /* + * Soft-reset the controller + */ + csr32w(ctlr, Rcr, Rst); + for(i=0;; i++){ + if(i > 100) + panic("ns83815: soft reset did not complete"); + microdelay(250); + if((csr32r(ctlr, Rcr) & Rst) == 0) + break; + delay(1); + } + + csr32w(ctlr, Rccsr, Pmests); + csr32w(ctlr, Rccsr, 0); + csr32w(ctlr, Rcfg, csr32r(ctlr, Rcfg) | Pint_acen); +// MII +// TODO: finish this + if(resetphys){ + /* + * Soft-reset the PHY + */ + csr32w(ctlr, Rbmcr, Reset); + for(i=0;; i++){ + if(i > 100) + panic("ns83815: PHY soft reset time out"); + if((csr32r(ctlr, Rbmcr) & Reset) == 0) + break; + delay(1); + } + } + + /* + * Initialisation values, in sequence (see 4.4 Recommended Registers Configuration) + */ + csr16w(ctlr, 0xCC, 0x0001); /* PGSEL */ + csr16w(ctlr, 0xE4, 0x189C); /* PMCCSR */ + csr16w(ctlr, 0xFC, 0x0000); /* TSTDAT */ + csr16w(ctlr, 0xF4, 0x5040); /* DSPCFG */ + csr16w(ctlr, 0xF8, 0x008C); /* SDCFG */ + + /* + * Auto negotiate + */ + w = csr16r(ctlr, Rbmsr); /* clear latched bits */ + debug("anar: %4.4ux\n", csr16r(ctlr, Ranar)); + csr16w(ctlr, Rbmcr, Anena); + if(csr16r(ctlr, Ranar) == 0 || (csr32r(ctlr, Rcfg) & Aneg_dn) == 0){ + csr16w(ctlr, Rbmcr, Anena|Anrestart); + /* TODO: this times out on the SiS900. why? */ +// TODO: finish this + for(i=0;; i++){ + if(i > 6000){ + print("ns83815: speed auto neg. timed out\n"); + break; + } + if((w = csr16r(ctlr, Rbmsr)) & Ancomp) + break; + delay(1); + } + debug("%d ms\n", i); + w &= 0xFFFF; + debug("bmsr: %4.4ux\n", w); + USED(w); + } + debug("anar: %4.4ux\n", csr16r(ctlr, Ranar)); + debug("anlpar: %4.4ux\n", csr16r(ctlr, Ranlpar)); + debug("aner: %4.4ux\n", csr16r(ctlr, Raner)); + debug("physts: %4.4ux\n", csr16r(ctlr, Rphysts)); + debug("tbscr: %4.4ux\n", csr16r(ctlr, Rtbscr)); +} + +/* + * MII/PHY + */ + +// TODO: finish this +static int +mdior(Ctlr* ctlr, int n) +{ + int data, i, mear, r; + +// mear = csr32r(ctlr, Mear); +// r = ~(Mdc|Mddir) & mear; + data = 0; + for(i = n-1; i >= 0; i--){ +// if(csr32r(ctlr, Mear) & Mdio) + data |= (1<= 0; i--){ +// if(bits & (1<ctlr; + + /* + * MII Management Interface Read. + * + * Preamble; + * ST+OP+PA+RA; + * LT + 16 data bits. + */ + mdiow(ctlr, 0xFFFFFFFF, 32); + mdiow(ctlr, 0x1800|(pa<<5)|ra, 14); + data = mdior(ctlr, 18); + + if(data & 0x10000) + return -1; + + return data & 0xFFFF; +} + +static int +ns83815miimiw(Mii* mii, int pa, int ra, int data) +{ + Ctlr *ctlr; + + ctlr = mii->ctlr; + + /* + * MII Management Interface Write. + * + * Preamble; + * ST+OP+PA+RA+LT + 16 data bits; + * Z. + */ + mdiow(ctlr, 0xFFFFFFFF, 32); + data &= 0xFFFF; + data |= (0x05<<(5+5+2+16))|(pa<<(5+2+16))|(ra<<(2+16))|(0x02<<16); + mdiow(ctlr, data, 32); + return data & 0xFFFF; /* TODO: what's the caller expect here? */ +} + +// TODO: finish this +static int +sismiimir(Mii* mii, int pa, int ra) +{ + int data; + Ctlr *ctlr; + + ctlr = mii->ctlr; + + /* + * MII Management Interface Read. + * + * Preamble; + * ST+OP+PA+RA; + * LT + 16 data bits. + */ + mdiow(ctlr, 0xFFFFFFFF, 32); + mdiow(ctlr, 0x1800|(pa<<5)|ra, 14); + data = mdior(ctlr, 18); + + if(data & 0x10000) + return -1; + + return data & 0xFFFF; +} + +// TODO: finish this +static int +sismiimiw(Mii* mii, int pa, int ra, int data) +{ + Ctlr *ctlr; + + ctlr = mii->ctlr; + + /* + * MII Management Interface Write. + * + * Preamble; + * ST+OP+PA+RA+LT + 16 data bits; + * Z. + */ + mdiow(ctlr, 0xFFFFFFFF, 32); + data &= 0xFFFF; + data |= (0x05<<(5+5+2+16))|(pa<<(5+2+16))|(ra<<(2+16))|(0x02<<16); + mdiow(ctlr, data, 32); + return data & 0xFFFF; /* TODO: what's the caller expect here? */ +} + +// MII +// TODO: finish this +static int +media(Ether* ether) +{ + Ctlr* ctlr; + ulong cfg; + + ctlr = ether->ctlr; + cfg = csr32r(ctlr, Rcfg); + + if(waserror()){ +err: + if(ctlr->mii != nil){ + free(ctlr->mii); + ctlr->mii = nil; + } +#ifdef FS + return 10; +#else + nexterror(); +#endif + } + + switch (ctlr->id) { + case SiS900: + case SiS7016: + if (1){ + if((ctlr->mii = malloc(sizeof(Mii))) == nil) + error(Enomem); + ctlr->mii->ctlr = ctlr; + ctlr->mii->mir = sismiimir; + ctlr->mii->miw = sismiimiw; + if(mii(ctlr->mii, ~0) == 0) + error("no PHY"); + // ctlr->cfg |= Dupstsien|Lnkstsien|Spdstsien; + // ctlr->imr |= Phy; + } + break; + case Nat83815: + if (1){ + if((ctlr->mii = malloc(sizeof(Mii))) == nil) + error(Enomem); + ctlr->mii->ctlr = ctlr; + ctlr->mii->mir = ns83815miimir; + ctlr->mii->miw = ns83815miimiw; + if(mii(ctlr->mii, ~0) == 0) + error("no PHY"); + // ctlr->cfg |= Dupstsien|Lnkstsien|Spdstsien; + // ctlr->imr |= Phy; + } + break; + } + + ctlr->fd = (cfg & Fdup) != 0; + if(cfg & Speed100) + return 100; + if((cfg & Lnksts) == 0) + return 100; /* no link: use 100 to ensure larger queues */ + return 10; +} + + +static char* mediatable[9] = { + "10BASE-T", /* TP */ + "10BASE-2", /* BNC */ + "10BASE-5", /* AUI */ + "100BASE-TX", + "10BASE-TFD", + "100BASE-TXFD", + "100BASE-T4", + "100BASE-FX", + "100BASE-FXFD", +}; + +enum { + MagicReg = 0x48, + MagicRegSz = 1, + Magicrden = 0x40, /* read enable, apparently */ + Paddr= 0x70, /* address port */ + Pdata= 0x71, /* data port */ +}; + +/* rcmos() originally from LANL's SiS 900 driver's rcmos() */ +static int +sisrdcmos(Ctlr *ctlr) +{ + int i; + unsigned reg; + ulong port; + Pcidev *p; + + p = pcimatch(nil, SiS, SiS630bridge); + if (p == nil) { + print("ns83815: no SiS 630 rev. %ux bridge for mac addr\n", + ctlr->pcidev->rid); + return 0; + } + port = p->mem[0].bar & ~0x01; + print( +"ns83815: SiS 630 rev. %ux reading mac addr from cmos via bridge at port 0x%lux\n", + ctlr->pcidev->rid, port); + + reg = pcicfgr8(p, MagicReg); + pcicfgw8(p, MagicReg, reg|Magicrden); + + for (i = 0; i < Eaddrlen; i++) { + outb(port+Paddr, SiS630eenodeaddr + i); + ctlr->sromea[i] = inb(port+Pdata); + } + + pcicfgw8(p, MagicReg, reg & ~Magicrden); + return 1; +} + +static int +is630(ulong id, Pcidev *p) +{ + if (id == SiS900) + switch (p->rid) { + case SiSrev630s: + case SiSrev630e: + case SiSrev630ea1: + return 1; + } + return 0; +} + +/* + * If this is a SiS 630E chipset with an embedded SiS 900 controller, + * we have to read the MAC address from the APC CMOS RAM. - sez freebsd. + * However, CMOS *is* NVRAM normally. See devrtc.c:440, memory.c:88. + */ +static void +sissrom(Ctlr *ctlr) +{ + union { + uchar eaddr[Eaddrlen]; + ushort alignment; + } ee; + int i, off = SiSeenodeaddr, cnt = sizeof ee.eaddr / sizeof(short); + ushort *shp = (ushort *)ee.eaddr; + + if (!is630(ctlr->id, ctlr->pcidev) || !sisrdcmos(ctlr)) { + print("ns83815: reading SiS mac address from eeprom\n"); + for (i = 0; i < cnt; i++) + *shp++ = eegetw(ctlr, off++); + memmove(ctlr->sromea, ee.eaddr, sizeof ctlr->sromea); + } +} + +static void +nssrom(Ctlr *ctlr) +{ + int i, j; + + debug("ns83815: fa311: reading mac address from eeprom & swizzling\n"); + for(i = 0; i < nelem(ctlr->srom); i++) + ctlr->srom[i] = eegetw(ctlr, i); + + /* + * the MAC address is reversed, straddling word boundaries + */ + j = Nseenodeaddr*16 + 15; // bit offset to read + for (i=0; i<48; i++, j++) + ctlr->sromea[i>>3] |= + ((ctlr->srom[j>>4] >> (15-(j&0xF))) & 1) << (i&7); +} + +static void +srom(Ctlr* ctlr) +{ + memset(ctlr->sromea, 0, sizeof(ctlr->sromea)); + switch (ctlr->id) { + case SiS900: + case SiS7016: + sissrom(ctlr); + break; + case Nat83815: + nssrom(ctlr); + break; + default: + print("ns83815: srom: unknown id 0x%ux\n", ctlr->id); + break; + } +} + +// from pci.c +enum { + Pcinetctlr = 0x02, /* network controller */ +// PciCCRp = 0x09, /* programming interface class code */ +// PciCCRu = 0x0A, /* sub-class code */ +// PciCCRb = 0x0B, /* base class code */ +}; + +static void +scanpci83815(void) +{ + typedef struct { + ulong bar; /* base address */ + int size; + } Bar; + int port; + ulong id; + Bar *portbarp; + Ctlr *ctlr; + Pcidev *p = nil; + + while(p = pcimatch(p, 0, 0)){ + /* ccru is a short in the FS kernel, thus the cast to uchar */ + if (p->ccrb != Pcinetctlr || (uchar)p->ccru != 0) + continue; // not a nic + id = (p->did<<16)|p->vid; + // print("ns83815: id 0x%lux on pci bus\n", id); + switch(id){ + case Nat83815: + print("ns83815: FA31[12] found\n"); + break; + case SiS900: + print("ns83815: SiS900"); + if (is630(id, p)) + print(" (within SiS630)"); + print(" found\n"); + break; + case SiS7016: + print("ns83815: SiS7016 found\n"); + break; + default: + continue; // unrecognised + } + portbarp = &p->mem[0]; + port = portbarp->bar & ~1; + + /* + * bar[0] is the I/O port register address and + * bar[1] is the memory-mapped register address. + */ + ctlr = mallocz(sizeof(Ctlr), 1); + ctlr->port = port; + ctlr->pcidev = p; + ctlr->id = id; + + if(ioalloc(ctlr->port, portbarp->size, 0, "ns83815") < 0){ + print("ns83815: port 0x%ux in use\n", ctlr->port); + free(ctlr); + continue; + } + + softreset(ctlr, 0); + srom(ctlr); + + if(ctlrhead != nil) + ctlrtail->next = ctlr; + else + ctlrhead = ctlr; + ctlrtail = ctlr; + } +} + +int +dp83815reset(Ether* ether) +{ + Ctlr *ctlr; + int i, x; + ulong ctladdr; + uchar ea[Eaddrlen]; + static int scandone; + + if(scandone == 0){ + scanpci83815(); + scandone = 1; + } + + /* + * Any adapter matches if no ether->port is supplied, + * otherwise the ports must match. + */ + for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + if(ether->port == 0 || ether->port == ctlr->port){ + ctlr->active = 1; + break; + } + } + if(ctlr == nil) + return -1; + + ether->ctlr = ctlr; + ether->port = ctlr->port; + ether->irq = ctlr->pcidev->intl; + ether->tbdf = ctlr->pcidev->tbdf; + + /* + * Check if the adapter's station address is to be overridden. + * If not, read it from the EEPROM and set in ether->ea prior to + * loading the station address in the hardware. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, ether->ea, Eaddrlen) == 0) + memmove(ether->ea, ctlr->sromea, Eaddrlen); + print("ns83815: setting mac addr\n"); /* DEBUG */ + for(i=0; iea[i] | (ether->ea[i+1]<<8); + ctladdr = (ctlr->id == Nat83815? i: i<<15); + csr32w(ctlr, Rrfcr, ctladdr); + csr32w(ctlr, Rrfdr, x); + } + csr32w(ctlr, Rrfcr, Rfen|Apm|Aab|Aam); + + ether->mbps = media(ether); + + /* + * Look for a medium override in case there's no autonegotiation + * or the autonegotiation fails. + */ + + for(i = 0; i < ether->nopt; i++){ + if(cistrcmp(ether->opt[i], "FD") == 0){ + ctlr->fd = 1; + continue; + } + for(x = 0; x < nelem(mediatable); x++){ + debug("compare <%s> <%s>\n", mediatable[x], + ether->opt[i]); + if(cistrcmp(mediatable[x], ether->opt[i]) == 0){ + if(x != 4 && x >= 3) + ether->mbps = 100; + else + ether->mbps = 10; + switch(x){ + default: + ctlr->fd = 0; + break; + + case 0x04: /* 10BASE-TFD */ + case 0x05: /* 100BASE-TXFD */ + case 0x08: /* 100BASE-FXFD */ + ctlr->fd = 1; + break; + } + break; + } + } + } + + /* + * Initialise descriptor rings, ethernet address. + */ + ctlr->nrdr = Nrde; + ctlr->ntdr = Ntde; + pcisetbme(ctlr->pcidev); + ctlrinit(ether); + + /* + * Linkage to the generic ethernet driver. + */ + ether->attach = attach; + ether->transmit = transmit; + ether->interrupt = interrupt; +#ifndef FS + ether->ifstat = ifstat; + ether->arg = ether; + ether->promiscuous = promiscuous; +#endif + debug("ns83815: dp83815reset: done\n"); + return 0; +} + +#ifndef FS +void +ether83815link(void) +{ + addethercard("83815", dp83815reset); +} +#endif diff -Nru /sys/src/fs/pc/etherdat.h /sys/src/fs/pc/etherdat.h --- /sys/src/fs/pc/etherdat.h Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/pc/etherdat.h Tue Nov 1 00:00:00 2011 @@ -0,0 +1,4 @@ +#include "all.h" +#include "io.h" +#include "mem.h" +#include "../ip/ip.h" diff -Nru /sys/src/fs/pc/etherdp83820.c /sys/src/fs/pc/etherdp83820.c --- /sys/src/fs/pc/etherdp83820.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/pc/etherdp83820.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,1322 @@ +/* + * National Semiconductor DP83820 & DP83821 + * 10/100/1000 Mb/s Ethernet Network Interface Controller + * (Gig-NIC). + * Driver assumes little-endian and 32-bit host throughout. + */ +#ifdef FS +#include "all.h" +#include "io.h" +#include "mem.h" +#include "../ip/ip.h" + +#else + +#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" +#endif /* FS */ + +#include "etherif.h" +#include "ethermii.h" + +#include "compat.h" + +enum { /* Registers */ + Cr = 0x00, /* Command */ + Cfg = 0x04, /* Configuration and Media Status */ + Mear = 0x08, /* MII/EEPROM Access */ + Ptscr = 0x0C, /* PCI Test Control */ + Isr = 0x10, /* Interrupt Status */ + Imr = 0x14, /* Interrupt Mask */ + Ier = 0x18, /* Interrupt Enable */ + Ihr = 0x1C, /* Interrupt Holdoff */ + Txdp = 0x20, /* Transmit Descriptor Pointer */ + Txdphi = 0x24, /* Transmit Descriptor Pointer Hi */ + Txcfg = 0x28, /* Transmit Configuration */ + Gpior = 0x2C, /* General Purpose I/O Control */ + Rxdp = 0x30, /* Receive Descriptor Pointer */ + Rxdphi = 0x34, /* Receive Descriptor Pointer Hi */ + Rxcfg = 0x38, /* Receive Configuration */ + Pqcr = 0x3C, /* Priority Queueing Control */ + Wcsr = 0x40, /* Wake on LAN Control/Status */ + Pcr = 0x44, /* Pause Control/Status */ + Rfcr = 0x48, /* Receive Filter/Match Control */ + Rfdr = 0x4C, /* Receive Filter/Match Data */ + Brar = 0x50, /* Boot ROM Address */ + Brdr = 0x54, /* Boot ROM Data */ + Srr = 0x58, /* Silicon Revision */ + Mibc = 0x5C, /* MIB Control */ + Mibd = 0x60, /* MIB Data */ + Txdp1 = 0xA0, /* Txdp Priority 1 */ + Txdp2 = 0xA4, /* Txdp Priority 2 */ + Txdp3 = 0xA8, /* Txdp Priority 3 */ + Rxdp1 = 0xB0, /* Rxdp Priority 1 */ + Rxdp2 = 0xB4, /* Rxdp Priority 2 */ + Rxdp3 = 0xB8, /* Rxdp Priority 3 */ + Vrcr = 0xBC, /* VLAN/IP Receive Control */ + Vtcr = 0xC0, /* VLAN/IP Transmit Control */ + Vdr = 0xC4, /* VLAN Data */ + Ccsr = 0xCC, /* Clockrun Control/Status */ + Tbicr = 0xE0, /* TBI Control */ + Tbisr = 0xE4, /* TBI Status */ + Tanar = 0xE8, /* TBI ANAR */ + Tanlpar = 0xEC, /* TBI ANLPAR */ + Taner = 0xF0, /* TBI ANER */ + Tesr = 0xF4, /* TBI ESR */ +}; + +enum { /* Cr */ + Txe = 0x00000001, /* Transmit Enable */ + Txd = 0x00000002, /* Transmit Disable */ + Rxe = 0x00000004, /* Receiver Enable */ + Rxd = 0x00000008, /* Receiver Disable */ + Txr = 0x00000010, /* Transmitter Reset */ + Rxr = 0x00000020, /* Receiver Reset */ + Swien = 0x00000080, /* Software Interrupt Enable */ + Rst = 0x00000100, /* Reset */ + TxpriSHFT = 9, /* Tx Priority Queue Select */ + TxpriMASK = 0x00001E00, + RxpriSHFT = 13, /* Rx Priority Queue Select */ + RxpriMASK = 0x0001E000, +}; + +enum { /* Configuration and Media Status */ + Bem = 0x00000001, /* Big Endian Mode */ + Ext125 = 0x00000002, /* External 125MHz reference Select */ + Bromdis = 0x00000004, /* Disable Boot ROM interface */ + Pesel = 0x00000008, /* Parity Error Detection Action */ + Exd = 0x00000010, /* Excessive Deferral Abort */ + Pow = 0x00000020, /* Program Out of Window Timer */ + Sb = 0x00000040, /* Single Back-off */ + Reqalg = 0x00000080, /* PCI Bus Request Algorithm */ + Extstsen = 0x00000100, /* Extended Status Enable */ + Phydis = 0x00000200, /* Disable PHY */ + Phyrst = 0x00000400, /* Reset PHY */ + M64addren = 0x00000800, /* Master 64-bit Addressing Enable */ + Data64en = 0x00001000, /* 64-bit Data Enable */ + Pci64det = 0x00002000, /* PCI 64-bit Bus Detected */ + T64addren = 0x00004000, /* Target 64-bit Addressing Enable */ + Mwidis = 0x00008000, /* MWI Disable */ + Mrmdis = 0x00010000, /* MRM Disable */ + Tmrtest = 0x00020000, /* Timer Test Mode */ + Spdstsien = 0x00040000, /* PHY Spdsts Interrupt Enable */ + Lnkstsien = 0x00080000, /* PHY Lnksts Interrupt Enable */ + Dupstsien = 0x00100000, /* PHY Dupsts Interrupt Enable */ + Mode1000 = 0x00400000, /* 1000Mb/s Mode Control */ + Tbien = 0x01000000, /* Ten-Bit Interface Enable */ + Dupsts = 0x10000000, /* Full Duplex Status */ + Spdsts100 = 0x20000000, /* SPEED100 Input Pin Status */ + Spdsts1000 = 0x40000000, /* SPEED1000 Input Pin Status */ + Lnksts = 0x80000000, /* Link Status */ +}; + +enum { /* MII/EEPROM Access */ + Eedi = 0x00000001, /* EEPROM Data In */ + Eedo = 0x00000002, /* EEPROM Data Out */ + Eeclk = 0x00000004, /* EEPROM Serial Clock */ + Eesel = 0x00000008, /* EEPROM Chip Select */ + Mdio = 0x00000010, /* MII Management Data */ + Mddir = 0x00000020, /* MII Management Direction */ + Mdc = 0x00000040, /* MII Management Clock */ +}; + +enum { /* Interrupts */ + Rxok = 0x00000001, /* Rx OK */ + Rxdesc = 0x00000002, /* Rx Descriptor */ + Rxerr = 0x00000004, /* Rx Packet Error */ + Rxearly = 0x00000008, /* Rx Early Threshold */ + Rxidle = 0x00000010, /* Rx Idle */ + Rxorn = 0x00000020, /* Rx Overrun */ + Txok = 0x00000040, /* Tx Packet OK */ + Txdesc = 0x00000080, /* Tx Descriptor */ + Txerr = 0x00000100, /* Tx Packet Error */ + Txidle = 0x00000200, /* Tx Idle */ + Txurn = 0x00000400, /* Tx Underrun */ + Mib = 0x00000800, /* MIB Service */ + Swi = 0x00001000, /* Software Interrupt */ + Pme = 0x00002000, /* Power Management Event */ + Phy = 0x00004000, /* PHY Interrupt */ + Hibint = 0x00008000, /* High Bits Interrupt Set */ + Rxsovr = 0x00010000, /* Rx Status FIFO Overrun */ + Rtabt = 0x00020000, /* Received Target Abort */ + Rmabt = 0x00040000, /* Received Master Abort */ + Sserr = 0x00080000, /* Signalled System Error */ + Dperr = 0x00100000, /* Detected Parity Error */ + Rxrcmp = 0x00200000, /* Receive Reset Complete */ + Txrcmp = 0x00400000, /* Transmit Reset Complete */ + Rxdesc0 = 0x00800000, /* Rx Descriptor for Priority Queue 0 */ + Rxdesc1 = 0x01000000, /* Rx Descriptor for Priority Queue 1 */ + Rxdesc2 = 0x02000000, /* Rx Descriptor for Priority Queue 2 */ + Rxdesc3 = 0x04000000, /* Rx Descriptor for Priority Queue 3 */ + Txdesc0 = 0x08000000, /* Tx Descriptor for Priority Queue 0 */ + Txdesc1 = 0x10000000, /* Tx Descriptor for Priority Queue 1 */ + Txdesc2 = 0x20000000, /* Tx Descriptor for Priority Queue 2 */ + Txdesc3 = 0x40000000, /* Tx Descriptor for Priority Queue 3 */ +}; + +enum { /* Interrupt Enable */ + Ien = 0x00000001, /* Interrupt Enable */ +}; + +enum { /* Interrupt Holdoff */ + IhSHFT = 0, /* Interrupt Holdoff */ + IhMASK = 0x000000FF, + Ihctl = 0x00000100, /* Interrupt Holdoff Control */ +}; + +enum { /* Transmit Configuration */ + TxdrthSHFT = 0, /* Tx Drain Threshold */ + TxdrthMASK = 0x000000FF, + FlthSHFT = 16, /* Tx Fill Threshold */ + FlthMASK = 0x0000FF00, + Brstdis = 0x00080000, /* 1000Mb/s Burst Disable */ + MxdmaSHFT = 20, /* Max Size per Tx DMA Burst */ + MxdmaMASK = 0x00700000, + Ecretryen = 0x00800000, /* Excessive Collision Retry Enable */ + Atp = 0x10000000, /* Automatic Transmit Padding */ + Mlb = 0x20000000, /* MAC Loopback */ + Hbi = 0x40000000, /* Heartbeat Ignore */ + Csi = 0x80000000, /* Carrier Sense Ignore */ +}; + +enum { /* Receive Configuration */ + RxdrthSHFT = 1, /* Rx Drain Threshold */ + RxdrthMASK = 0x0000003E, + Airl = 0x04000000, /* Accept In-Range Length Errored */ + Alp = 0x08000000, /* Accept Long Packets */ + Rxfd = 0x10000000, /* Receive Full Duplex */ + Stripcrc = 0x20000000, /* Strip CRC */ + Arp = 0x40000000, /* Accept Runt Packets */ + Aep = 0x80000000, /* Accept Errored Packets */ +}; + +enum { /* Priority Queueing Control */ + Txpqen = 0x00000001, /* Transmit Priority Queuing Enable */ + Txfairen = 0x00000002, /* Transmit Fairness Enable */ + RxpqenSHFT = 2, /* Receive Priority Queue Enable */ + RxpqenMASK = 0x0000000C, +}; + +enum { /* Pause Control/Status */ + PscntSHFT = 0, /* Pause Counter Value */ + PscntMASK = 0x0000FFFF, + Pstx = 0x00020000, /* Transmit Pause Frame */ + PsffloSHFT = 18, /* Rx Data FIFO Lo Threshold */ + PsffloMASK = 0x000C0000, + PsffhiSHFT = 20, /* Rx Data FIFO Hi Threshold */ + PsffhiMASK = 0x00300000, + PsstloSHFT = 22, /* Rx Stat FIFO Hi Threshold */ + PsstloMASK = 0x00C00000, + PssthiSHFT = 24, /* Rx Stat FIFO Hi Threshold */ + PssthiMASK = 0x03000000, + Psrcvd = 0x08000000, /* Pause Frame Received */ + Psact = 0x10000000, /* Pause Active */ + Psda = 0x20000000, /* Pause on Destination Address */ + Psmcast = 0x40000000, /* Pause on Multicast */ + Psen = 0x80000000, /* Pause Enable */ +}; + +enum { /* Receive Filter/Match Control */ + RfaddrSHFT = 0, /* Extended Register Address */ + RfaddrMASK = 0x000003FF, + Ulm = 0x00080000, /* U/L bit mask */ + Uhen = 0x00100000, /* Unicast Hash Enable */ + Mhen = 0x00200000, /* Multicast Hash Enable */ + Aarp = 0x00400000, /* Accept ARP Packets */ + ApatSHFT = 23, /* Accept on Pattern Match */ + ApatMASK = 0x07800000, + Apm = 0x08000000, /* Accept on Perfect Match */ + Aau = 0x10000000, /* Accept All Unicast */ + Aam = 0x20000000, /* Accept All Multicast */ + Aab = 0x40000000, /* Accept All Broadcast */ + Rfen = 0x80000000, /* Rx Filter Enable */ +}; + +enum { /* Receive Filter/Match Data */ + RfdataSHFT = 0, /* Receive Filter Data */ + RfdataMASK = 0x0000FFFF, + BmaskSHFT = 16, /* Byte Mask */ + BmaskMASK = 0x00030000, +}; + +enum { /* MIB Control */ + Wrn = 0x00000001, /* Warning Test Indicator */ + Frz = 0x00000002, /* Freeze All Counters */ + Aclr = 0x00000004, /* Clear All Counters */ + Mibs = 0x00000008, /* MIB Counter Strobe */ +}; + +enum { /* MIB Data */ + Nmibd = 11, /* Number of MIB Data Registers */ +}; + +enum { /* VLAN/IP Receive Control */ + Vtden = 0x00000001, /* VLAN Tag Detection Enable */ + Vtren = 0x00000002, /* VLAN Tag Removal Enable */ + Dvtf = 0x00000004, /* Discard VLAN Tagged Frames */ + Dutf = 0x00000008, /* Discard Untagged Frames */ + Ipen = 0x00000010, /* IP Checksum Enable */ + Ripe = 0x00000020, /* Reject IP Checksum Errors */ + Rtcpe = 0x00000040, /* Reject TCP Checksum Errors */ + Rudpe = 0x00000080, /* Reject UDP Checksum Errors */ +}; + +enum { /* VLAN/IP Transmit Control */ + Vgti = 0x00000001, /* VLAN Global Tag Insertion */ + Vppti = 0x00000002, /* VLAN Per-Packet Tag Insertion */ + Gchk = 0x00000004, /* Global Checksum Generation */ + Ppchk = 0x00000008, /* Per-Packet Checksum Generation */ +}; + +enum { /* VLAN Data */ + VtypeSHFT = 0, /* VLAN Type Field */ + VtypeMASK = 0x0000FFFF, + VtciSHFT = 16, /* VLAN Tag Control Information */ + VtciMASK = 0xFFFF0000, +}; + +enum { /* Clockrun Control/Status */ + Clkrunen = 0x00000001, /* CLKRUN Enable */ + Pmeen = 0x00000100, /* PME Enable */ + Pmests = 0x00008000, /* PME Status */ +}; + +typedef struct { + u32int link; /* Link to the next descriptor */ + u32int bufptr; /* pointer to data Buffer */ + int cmdsts; /* Command/Status */ + int extsts; /* optional Extended Status */ + + Block* bp; /* Block containing bufptr */ + u32int unused; /* pad to 64-bit */ +} Desc; + +enum { /* Common cmdsts bits */ + SizeMASK = 0x0000FFFF, /* Descriptor Byte Count */ + SizeSHFT = 0, + Ok = 0x08000000, /* Packet OK */ + Crc = 0x10000000, /* Suppress/Include CRC */ + Intr = 0x20000000, /* Interrupt on ownership transfer */ + More = 0x40000000, /* not last descriptor in a packet */ + Own = 0x80000000, /* Descriptor Ownership */ +}; + +enum { /* Transmit cmdsts bits */ + CcntMASK = 0x000F0000, /* Collision Count */ + CcntSHFT = 16, + Ec = 0x00100000, /* Excessive Collisions */ + Owc = 0x00200000, /* Out of Window Collision */ + Ed = 0x00400000, /* Excessive Deferral */ + Td = 0x00800000, /* Transmit Deferred */ + Crs = 0x01000000, /* Carrier Sense Lost */ + Tfu = 0x02000000, /* Transmit FIFO Underrun */ + Txa = 0x04000000, /* Transmit Abort */ +}; + +enum { /* Receive cmdsts bits */ + Irl = 0x00010000, /* In-Range Length Error */ + Lbp = 0x00020000, /* Loopback Packet */ + Fae = 0x00040000, /* Frame Alignment Error */ + Crce = 0x00080000, /* CRC Error */ + Ise = 0x00100000, /* Invalid Symbol Error */ + Runt = 0x00200000, /* Runt Packet Received */ + Long = 0x00400000, /* Too Long Packet Received */ + DestMASK = 0x01800000, /* Destination Class */ + DestSHFT = 23, + Rxo = 0x02000000, /* Receive Overrun */ + Rxa = 0x04000000, /* Receive Aborted */ +}; + +enum { /* extsts bits */ + EvtciMASK = 0x0000FFFF, /* VLAN Tag Control Information */ + EvtciSHFT = 0, + Vpkt = 0x00010000, /* VLAN Packet */ + /* Ippkt, Udppkt are struct types in the fs kernel */ + Ippacket = 0x00020000, /* IP Packet */ + Iperr = 0x00040000, /* IP Checksum Error */ + Tcppkt = 0x00080000, /* TCP Packet */ + Tcperr = 0x00100000, /* TCP Checksum Error */ + Udppacket = 0x00200000, /* UDP Packet */ + Udperr = 0x00400000, /* UDP Checksum Error */ +}; + +/* + * adjust these, keeping in mind that at 1Gb/s, a 1514-byte packet is + * sent or received in 12µs. Ignoring mandatory inter-packet gaps, + * a ring of 32 buffers will take 388µs to fill or empty; 64 buffers + * will take 775µs; 256 will take 3.1ms. + */ +enum { + Nrd = 64, // was 64 then 450 + Nrb = 4*Nrd, + Rbsz = ROUNDUP(sizeof(Etherpkt)+8, 8), + Ntd = Nrd, // was 32 then 450 +}; + +typedef struct Ctlr Ctlr; +typedef struct Ctlr { + int port; + Pcidev* pcidev; + Ctlr* next; + int active; + int id; + + int eepromsz; /* address size in bits */ + ushort* eeprom; + + int* nic; + int cfg; + int imr; + + QLock alock; /* attach */ + Lock ilock; /* init */ + void* alloc; /* base of per-Ctlr allocated data */ + + Mii* mii; + + Lock rdlock; /* receive */ + Desc* rd; + int nrd; + int nrb; + int rdx; + int rxcfg; + + Lock tlock; /* transmit */ + Desc* td; + int ntd; + int tdh; + int tdt; + int ntq; + int txcfg; + + int rxidle; + + uint mibd[Nmibd]; + + int ec; + int owc; + int ed; + int crs; + int tfu; + int txa; +} Ctlr; + +#define csr32r(c, r) (*((c)->nic+((r)/4))) +#define csr32w(c, r, v) (*((c)->nic+((r)/4)) = (v)) + +static Ctlr* dp83820ctlrhead; +static Ctlr* dp83820ctlrtail; + +static Lock dp83820rblock; /* free receive Blocks */ +static Block* dp83820rbpool; + +static char* dp83820mibs[Nmibd] = { + "RXErroredPkts", + "RXFCSErrors", + "RXMsdPktErrors", + "RXFAErrors", + "RXSymbolErrors", + "RXFrameToLong", + "RXIRLErrors", + "RXBadOpcodes", + "RXPauseFrames", + "TXPauseFrames", + "TXSQEErrors", +}; + +static int +mdior(Ctlr* ctlr, int n) +{ + int data, i, mear, r; + + mear = csr32r(ctlr, Mear); + r = ~(Mdc|Mddir) & mear; + data = 0; + for(i = n-1; i >= 0; i--){ + if(csr32r(ctlr, Mear) & Mdio) + data |= (1<= 0; i--){ + if(bits & (1<ctlr; + + /* + * MII Management Interface Read. + * + * Preamble; + * ST+OP+PA+RA; + * LT + 16 data bits. + */ + mdiow(ctlr, 0xFFFFFFFF, 32); + mdiow(ctlr, 0x1800|(pa<<5)|ra, 14); + data = mdior(ctlr, 18); + + if(data & 0x10000) + return -1; + + return data & 0xFFFF; +} + +static int +dp83820miimiw(Mii* mii, int pa, int ra, int data) +{ + Ctlr *ctlr; + + ctlr = mii->ctlr; + + /* + * MII Management Interface Write. + * + * Preamble; + * ST+OP+PA+RA+LT + 16 data bits; + * Z. + */ + mdiow(ctlr, 0xFFFFFFFF, 32); + data &= 0xFFFF; + data |= (0x05<<(5+5+2+16))|(pa<<(5+2+16))|(ra<<(2+16))|(0x02<<16); + mdiow(ctlr, data, 32); + return data & 0xFFFF; /* TODO: what's the caller expect here? */ +} + +/* + * return free Block+buffer ready for input of up to MTU bytes. + * desc->bp is also set to the return value. + */ +static Block * +dp83820rballoc(Desc* desc) +{ + Block *bp; + + if(desc->bp == nil){ + ilock(&dp83820rblock); + if((bp = dp83820rbpool) == nil){ + iunlock(&dp83820rblock); + desc->bp = nil; + desc->cmdsts = Own; + iprint("dp83820rballoc: out of buffers\n"); + return nil; + } + dp83820rbpool = bp->next; + bp->next = nil; + iunlock(&dp83820rblock); + + desc->bufptr = PCIWADDR(bp->rp); + desc->bp = bp; + } + else{ + bp = desc->bp; + BLKRESET(bp); + } + + coherence(); + desc->cmdsts = Intr|Rbsz; + return bp; +} + +/* should be called via freeb() */ +static void +dp83820rbfree(Block *bp) +{ + BLKRESET(bp); + + ilock(&dp83820rblock); + bp->next = dp83820rbpool; + dp83820rbpool = bp; + iunlock(&dp83820rblock); +} + +static void +dp83820halt(Ctlr* ctlr) +{ + int i, timeo; + + ilock(&ctlr->ilock); + csr32w(ctlr, Imr, 0); + csr32w(ctlr, Ier, 0); + csr32w(ctlr, Cr, Rxd|Txd); + for(timeo = 0; timeo < 1000; timeo++){ + if(!(csr32r(ctlr, Cr) & (Rxe|Txe))) + break; + microdelay(1); + } + csr32w(ctlr, Mibc, Frz); + iunlock(&ctlr->ilock); + + if(ctlr->rd != nil){ + for(i = 0; i < ctlr->nrd; i++){ + if(ctlr->rd[i].bp == nil) + continue; + freeb(ctlr->rd[i].bp); + ctlr->rd[i].bp = nil; + } + } + if(ctlr->td != nil){ + for(i = 0; i < ctlr->ntd; i++){ + if(ctlr->td[i].bp == nil) + continue; + freeb(ctlr->td[i].bp); + ctlr->td[i].bp = nil; + } + } +} + +static void +dp83820cfg(Ctlr* ctlr) +{ + int cfg; + + /* + * Don't know how to deal with a TBI yet. + */ + if(ctlr->mii == nil) { + iprint("83820: got tbi, not phy\n"); + return; + } + + /* + * The polarity of these bits is at the mercy + * of the board designer. + * The correct answer for all speed and duplex questions + * should be to query the phy. + */ + cfg = csr32r(ctlr, Cfg); + if(!(cfg & Dupsts)){ + ctlr->rxcfg |= Rxfd; + ctlr->txcfg |= Csi|Hbi; + iprint("83820: full duplex, "); + } + else{ + ctlr->rxcfg &= ~Rxfd; + ctlr->txcfg &= ~(Csi|Hbi); + iprint("83820: half duplex, "); + } + csr32w(ctlr, Rxcfg, ctlr->rxcfg); + csr32w(ctlr, Txcfg, ctlr->txcfg); + + switch(cfg & (Spdsts1000|Spdsts100)){ + case Spdsts1000: /* 100Mbps */ + default: /* 10Mbps */ + ctlr->cfg &= ~Mode1000; + if ((cfg & (Spdsts1000|Spdsts100)) == Spdsts1000) + iprint("100Mb/s\n"); + else + iprint("10Mb/s\n"); + break; + case Spdsts100: /* 1Gbps */ + ctlr->cfg |= Mode1000; + iprint("1Gb/s\n"); + break; + } + csr32w(ctlr, Cfg, ctlr->cfg); +} + +static void +dp83820init(Ether* edev) +{ + int i; + Ctlr *ctlr; + Desc *desc; + uchar *alloc; + + ctlr = edev->ctlr; + + dp83820halt(ctlr); + + /* + * Receiver + */ + alloc = (uchar*)ROUNDUP((ulong)ctlr->alloc, 8); + ctlr->rd = (Desc*)alloc; + alloc += ctlr->nrd*sizeof(Desc); + memset(ctlr->rd, 0, ctlr->nrd*sizeof(Desc)); + ctlr->rdx = 0; + for(i = 0; i < ctlr->nrd; i++){ + desc = &ctlr->rd[i]; + desc->link = PCIWADDR(&ctlr->rd[NEXT(i, ctlr->nrd)]); + if(dp83820rballoc(desc) == nil) { + print("dp83820init: out of buffers\n"); + continue; + } + } + csr32w(ctlr, Rxdphi, 0); + csr32w(ctlr, Rxdp, PCIWADDR(ctlr->rd)); + + for(i = 0; i < Eaddrlen; i += 2){ + csr32w(ctlr, Rfcr, i); + csr32w(ctlr, Rfdr, (edev->ea[i+1]<<8)|edev->ea[i]); + } + /* for now, accept all multicast packets */ + csr32w(ctlr, Rfcr, Rfen|Aab|Apm|Aam); + + ctlr->rxcfg = Stripcrc|(((2*(ETHERMINTU+4))/8)<imr |= Rxorn|Rxidle|Rxearly|Rxdesc|Rxok; + + /* + * Transmitter. + */ + ctlr->td = (Desc*)alloc; + memset(ctlr->td, 0, ctlr->ntd*sizeof(Desc)); + ctlr->tdh = ctlr->tdt = ctlr->ntq = 0; + for(i = 0; i < ctlr->ntd; i++){ + desc = &ctlr->td[i]; + desc->link = PCIWADDR(&ctlr->td[NEXT(i, ctlr->ntd)]); + } + csr32w(ctlr, Txdphi, 0); + csr32w(ctlr, Txdp, PCIWADDR(ctlr->td)); + + ctlr->txcfg = Atp|(((2*(ETHERMINTU+4))/32)<imr |= Txurn|Txidle|Txdesc|Txok; + + ilock(&ctlr->ilock); + + dp83820cfg(ctlr); + + csr32w(ctlr, Mibc, Aclr); + ctlr->imr |= Mib; + + csr32w(ctlr, Imr, ctlr->imr); + + /* try coalescing adjacent interrupts; use hold-off interval of 100µs */ + csr32w(ctlr, Ihr, Ihctl|(1<ilock); +} + +/* multicast already on, don't need to do anything */ +static void +multicast(void*, uchar*, int) +{ +} + +static void +dp83820attach(Ether* edev) +{ + Block *bp; + Ctlr *ctlr; + + ctlr = edev->ctlr; + qlock(&ctlr->alock); + if(ctlr->alloc != nil){ + qunlock(&ctlr->alock); + return; + } + + if(waserror()){ +err: + if(ctlr->mii != nil){ + free(ctlr->mii); + ctlr->mii = nil; + } + if(ctlr->alloc != nil){ + free(ctlr->alloc); + ctlr->alloc = nil; + } + qunlock(&ctlr->alock); + nexterror(); + } + + if(!(ctlr->cfg & Tbien)){ + if((ctlr->mii = malloc(sizeof(Mii))) == nil) + error(Enomem); + ctlr->mii->ctlr = ctlr; + ctlr->mii->mir = dp83820miimir; + ctlr->mii->miw = dp83820miimiw; + if(mii(ctlr->mii, ~0) == 0) + error("no PHY"); + ctlr->cfg |= Dupstsien|Lnkstsien|Spdstsien; + ctlr->imr |= Phy; + } + + /* allocate all Descs */ + ctlr->nrd = Nrd; + ctlr->nrb = Nrb; + ctlr->ntd = Ntd; + ctlr->alloc = mallocz((ctlr->nrd+ctlr->ntd)*sizeof(Desc) + 7, 0); + if(ctlr->alloc == nil) + error(Enomem); + + /* + * allocate receive Blocks+buffers, add all to receive Block+buffer pool + */ + for(ctlr->nrb = 0; ctlr->nrb < Nrb; ctlr->nrb++){ + if((bp = iallocb(Rbsz)) == nil) { + print( + "dp83820attach: iallocb failed with %d rcv bufs allocated\n", + ctlr->nrb); + error(Enomem); + } +#ifdef FS + bp->flags |= Mbrcvbuf; +#endif + bp->free = dp83820rbfree; /* to be called via freeb() */ + dp83820rbfree(bp); + } + + /* attaches a receive Block+buffer to each receive Desc */ + dp83820init(edev); + + qunlock(&ctlr->alock); + poperror(); +} + +static void +dp83820transmit(Ether* edev) +{ + Block *bp; + Ctlr *ctlr; + Desc *desc; + int cmdsts, r, x; + + ctlr = edev->ctlr; + + ilock(&ctlr->tlock); + + bp = nil; + for(x = ctlr->tdh; ctlr->ntq; x = NEXT(x, ctlr->ntd)){ + desc = &ctlr->td[x]; + if((cmdsts = desc->cmdsts) & Own) + break; + if(!(cmdsts & Ok)){ + if(cmdsts & Ec) + ctlr->ec++; + if(cmdsts & Owc) + ctlr->owc++; + if(cmdsts & Ed) + ctlr->ed++; + if(cmdsts & Crs) + ctlr->crs++; + if(cmdsts & Tfu) + ctlr->tfu++; + if(cmdsts & Txa) + ctlr->txa++; +#ifndef FS + edev->oerrs++; +#endif + } + desc->bp->next = bp; /* chain transmitted Blocks together */ + bp = desc->bp; + desc->bp = nil; /* unlink them from Descs */ + + ctlr->ntq--; + } + /* free Blocks+buffers comprising the packets just sent */ + ctlr->tdh = x; + if(bp != nil) + freeblist(bp); + + x = ctlr->tdt; + while(ctlr->ntq < (ctlr->ntd-1)){ + bp = etheroq(edev); /* get head of transmit q */ + if(bp == nil) + break; + + desc = &ctlr->td[x]; + desc->bufptr = PCIWADDR(bp->rp); + desc->bp = bp; /* attach to Desc */ + ctlr->ntq++; + coherence(); + desc->cmdsts = Own|Intr|BLEN(bp); /* fire! */ + + x = NEXT(x, ctlr->ntd); + } + if (ctlr->ntq >= ctlr->ntd-1) + iprint("83820: xmit q full, Ntd=%d\n", Ntd); + if(x != ctlr->tdt){ + ctlr->tdt = x; + r = csr32r(ctlr, Cr); + csr32w(ctlr, Cr, Txe|r); + } + + iunlock(&ctlr->tlock); +} + +static void +dp83820interrupt(Ureg*, void* arg) +{ + Block *bp; + Ctlr *ctlr; + Desc *desc; + Ether *edev; + int cmdsts, i, isr, r, x, rcvd = 0; + + edev = arg; + ctlr = edev->ctlr; + + for(isr = csr32r(ctlr, Isr); isr & ctlr->imr; isr = csr32r(ctlr, Isr)){ + if(isr & (Rxorn|Rxidle|Rxearly|Rxerr|Rxdesc|Rxok)){ + x = ctlr->rdx; + desc = &ctlr->rd[x]; + while((cmdsts = desc->cmdsts) & Own){ + if((cmdsts & Ok) && desc->bp != nil){ + /* unlink rcv. Block from Desc */ + bp = desc->bp; + desc->bp = nil; + /* store its length, add to input q */ + INCRPTR(bp, desc->cmdsts & SizeMASK); + ETHERIQ(edev, bp, 1); + rcvd++; + } + /* replace rcv. Block just detached from Desc */ + if (dp83820rballoc(desc) == nil) + iprint( + "dp83820interrupt: rballoc failed\n"); + + x = NEXT(x, ctlr->nrd); + desc = &ctlr->rd[x]; + } + ctlr->rdx = x; + if (rcvd >= ctlr->nrd-1) + iprint("83820: rcv q full, Nrd=%d\n", Nrd); + + if(isr & Rxidle){ + /* resume reading packets */ + r = csr32r(ctlr, Cr); + csr32w(ctlr, Cr, Rxe|r); + ctlr->rxidle++; + } + + isr &= ~(Rxorn|Rxidle|Rxearly|Rxerr|Rxdesc|Rxok); + } + + if(isr & Txurn){ + x = (ctlr->txcfg & TxdrthMASK)>>TxdrthSHFT; + r = (ctlr->txcfg & FlthMASK)>>FlthSHFT; + if(x < ((TxdrthMASK)>>TxdrthSHFT) + && x < (2048/32 - r)){ + ctlr->txcfg &= ~TxdrthMASK; + x++; + ctlr->txcfg |= x<txcfg); + } + } + + if(isr & (Txurn|Txidle|Txdesc|Txok)){ + /* toss completed packets, q new ones, fire */ + dp83820transmit(edev); + isr &= ~(Txurn|Txidle|Txdesc|Txok); + } + + if(isr & Mib){ + for(i = 0; i < Nmibd; i++){ + r = csr32r(ctlr, Mibd+(i*sizeof(int))); + ctlr->mibd[i] += r & 0xFFFF; + } + isr &= ~Mib; + } + + if((isr & Phy) && ctlr->mii != nil){ + ctlr->mii->mir(ctlr->mii, 1, Bmsr); + print("phy: cfg %8.8ux bmsr %4.4ux\n", + csr32r(ctlr, Cfg), + ctlr->mii->mir(ctlr->mii, 1, Bmsr)); + dp83820cfg(ctlr); + isr &= ~Phy; + } + if(isr) + iprint("dp83820: isr %8.8ux\n", isr); + } +} + +#ifndef FS +static long +dp83820ifstat(Ether* edev, void* a, long n, ulong offset) +{ + char *p; + Ctlr *ctlr; + int i, l, r; + MiiPhy *phy; + + ctlr = edev->ctlr; + + edev->crcs = ctlr->mibd[1]; + edev->frames = ctlr->mibd[3]; + edev->buffs = ctlr->mibd[5]; + edev->overflows = ctlr->mibd[2]; + + if(n == 0) + return 0; + + p = malloc(READSTR); + l = 0; + for(i = 0; i < Nmibd; i++){ + r = csr32r(ctlr, Mibd+(i*sizeof(int))); + ctlr->mibd[i] += r & 0xFFFF; + if(ctlr->mibd[i] != 0 && dp83820mibs[i] != nil) + l += snprint(p+l, READSTR-l, "%s: %ud %ud\n", + dp83820mibs[i], ctlr->mibd[i], r); + } + l += snprint(p+l, READSTR-l, "rxidle %d\n", ctlr->rxidle); + l += snprint(p+l, READSTR-l, "ec %d\n", ctlr->ec); + l += snprint(p+l, READSTR-l, "owc %d\n", ctlr->owc); + l += snprint(p+l, READSTR-l, "ed %d\n", ctlr->ed); + l += snprint(p+l, READSTR-l, "crs %d\n", ctlr->crs); + l += snprint(p+l, READSTR-l, "tfu %d\n", ctlr->tfu); + l += snprint(p+l, READSTR-l, "txa %d\n", ctlr->txa); + + l += snprint(p+l, READSTR, "rom:"); + for(i = 0; i < 0x10; i++){ + if(i && ((i & 0x07) == 0)) + l += snprint(p+l, READSTR-l, "\n "); + l += snprint(p+l, READSTR-l, " %4.4uX", ctlr->eeprom[i]); + } + l += snprint(p+l, READSTR-l, "\n"); + USED(l); + if(0 && ctlr->mii != nil && (phy = ctlr->mii->curphy) != nil){ + l += snprint(p+l, READSTR, "phy:"); + for(i = 0; i < NMiiPhyr; i++){ + if(i && ((i & 0x07) == 0)) + l += snprint(p+l, READSTR-l, "\n "); + /* phy->r no longer exists */ + // l += snprint(p+l, READSTR-l, " %4.4uX", phy->r[i]); + } + snprint(p+l, READSTR-l, "\n"); + } + + n = readstr(offset, a, n, p); + free(p); + + return n; +} +#endif /* FS */ + +static void +dp83820promiscuous(void* arg, int on) +{ + USED(arg, on); +} + +static int +atc93c46r(Ctlr* ctlr, int address) +{ + int data, i, mear, r, size; + + /* + * Analog Technology, Inc. ATC93C46 + * or equivalent serial EEPROM. + */ + mear = csr32r(ctlr, Mear); + mear &= ~(Eesel|Eeclk|Eedo|Eedi); + r = Eesel|mear; + +reread: + csr32w(ctlr, Mear, r); + data = 0x06; + for(i = 3-1; i >= 0; i--){ + if(data & (1<eepromsz) == 0) + size = 8; + + for(size = size-1; size >= 0; size--){ + if(address & (1<= 0; i--){ + csr32w(ctlr, Mear, Eeclk|r); + microdelay(1); + if(csr32r(ctlr, Mear) & Eedo) + data |= (1<eepromsz == 0){ + ctlr->eepromsz = 8-size; + ctlr->eeprom = malloc((1<eepromsz)*sizeof(ushort)); + goto reread; + } + + return data; +} + +static void +resetctlr(Ctlr *ctlr) +{ + csr32w(ctlr, Cr, Rst); + delay(1); + /* TODO: limit this; don't wait forever */ + while(csr32r(ctlr, Cr) & Rst) + delay(1); + + atc93c46r(ctlr, 0); +} + +static void +shutdown(Ether* ether) +{ + Ctlr *ctlr = ether->ctlr; + +print("ether83820 shutting down\n"); + csr32w(ctlr, Cr, Txd|Rxd); /* disable transceiver */ + resetctlr(ctlr); +} + +int +dp83820reset(Ctlr* ctlr) +{ + int i, r; + unsigned char sum; + + /* + * Soft reset the controller; + * read the EEPROM to get the initial settings + * of the Cfg and Gpior bits which should be cleared by + * the reset. + */ + resetctlr(ctlr); + sum = 0; + for(i = 0; i < 0x0E; i++){ + r = atc93c46r(ctlr, i); + ctlr->eeprom[i] = r; + sum += r; + sum += r>>8; + } + + if(sum != 0){ + print("dp83820reset: bad EEPROM checksum\n"); + return -1; + } + +#ifdef notdef + csr32w(ctlr, Gpior, ctlr->eeprom[4]); + + cfg = Extstsen|Exd; + r = csr32r(ctlr, Cfg); + if(ctlr->eeprom[5] & 0x0001) + cfg |= Ext125; + if(ctlr->eeprom[5] & 0x0002) + cfg |= M64addren; + if((ctlr->eeprom[5] & 0x0004) && (r & Pci64det)) + cfg |= Data64en; + if(ctlr->eeprom[5] & 0x0008) + cfg |= T64addren; + if(!(pcicfgr16(ctlr->pcidev, PciPCR) & 0x10)) + cfg |= Mwidis; + if(ctlr->eeprom[5] & 0x0020) + cfg |= Mrmdis; + if(ctlr->eeprom[5] & 0x0080) + cfg |= Mode1000; + if(ctlr->eeprom[5] & 0x0200) + cfg |= Tbien|Mode1000; + /* + * What about RO bits we might have destroyed with Rst? + * What about Exd, Tmrtest, Extstsen, Pintctl? + * Why does it think it has detected a 64-bit bus when + * it hasn't? + */ +#else + //r = csr32r(ctlr, Cfg); + //r &= ~(Mode1000|T64addren|Data64en|M64addren); + //csr32w(ctlr, Cfg, r); + //csr32w(ctlr, Cfg, 0x2000); +#endif /* notdef */ + ctlr->cfg = csr32r(ctlr, Cfg); +print("cfg %8.8ux pcicfg %8.8ux\n", ctlr->cfg, pcicfgr32(ctlr->pcidev, PciPCR)); + ctlr->cfg &= ~(T64addren|Data64en|M64addren); + csr32w(ctlr, Cfg, ctlr->cfg); + csr32w(ctlr, Mibc, Aclr|Frz); + + return 0; +} + +// from pci.c +enum { + Pcinetctlr = 0x02, /* network controller */ +// PciCCRp = 0x09, /* programming interface class code */ +// PciCCRu = 0x0A, /* sub-class code */ +// PciCCRb = 0x0B, /* base class code */ +}; + +static void +dp83820pci(void) +{ + void *mem; + Pcidev *p; + Ctlr *ctlr; + + p = nil; + while(p = pcimatch(p, 0, 0)){ + /* ccru is a short in the FS kernel, thus the cast to uchar */ + if(p->ccrb != Pcinetctlr || (uchar)p->ccru != 0) + continue; + switch((p->did<<16)|p->vid){ + default: + continue; + case (0x0022<<16)|0x100B: /* NS DP83820 (Gig-NIC) */ +/* case (0x1032<<16)|0x1737: /* linksys eg1032 */ + break; + } + + /* cast for FS */ + mem = (void *)vmap(p->mem[1].bar & ~0x0F, p->mem[1].size); + if(mem == 0){ + print("DP83820: can't map %8.8lux\n", p->mem[1].bar); + continue; + } + /* malloc only zeroes storage if Npadlong!=0, so use mallocz */ + ctlr = mallocz(sizeof(Ctlr), 1); + ctlr->port = p->mem[1].bar & ~0x0F; + ctlr->pcidev = p; + ctlr->id = (p->did<<16)|p->vid; + +#ifdef notdef + /* + * bar[0] is the I/O port register address and + * bar[1] is the memory-mapped register address. + */ + if (ioalloc(p->mem[0].bar & ~0x0F, p->mem[0].size, 0, "dp83820") + < 0) { + print("dp83820: port 0x%uX in use\n", ctlr->port); + free(ctlr); + continue; + } +#endif + ctlr->nic = mem; + if(dp83820reset(ctlr)){ + free(ctlr); + continue; + } + pcisetbme(p); + + if(dp83820ctlrhead != nil) + dp83820ctlrtail->next = ctlr; + else + dp83820ctlrhead = ctlr; + dp83820ctlrtail = ctlr; + } +} + +int +dp83820pnp(Ether* edev) +{ + int i; + Ctlr *ctlr; + uchar ea[Eaddrlen]; + + if(dp83820ctlrhead == nil) + dp83820pci(); + + /* + * Any adapter matches if no edev->port is supplied, + * otherwise the ports must match. + */ + for(ctlr = dp83820ctlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + if(edev->port == 0 || edev->port == ctlr->port){ + ctlr->active = 1; + break; + } + } + if(ctlr == nil) + return -1; + + edev->ctlr = ctlr; + edev->port = ctlr->port; + edev->irq = ctlr->pcidev->intl; + edev->tbdf = ctlr->pcidev->tbdf; + edev->mbps = 1000; + + /* + * Check if the adapter's station address is to be overridden. + * If not, read it from the EEPROM and set in ether->ea prior to + * loading the station address in the hardware. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, edev->ea, Eaddrlen) == 0){ + for(i = 0; i < Eaddrlen/2; i++){ + edev->ea[2*i] = ctlr->eeprom[0x0C-i]; + edev->ea[2*i+1] = ctlr->eeprom[0x0C-i]>>8; + } + } + + edev->attach = dp83820attach; + edev->transmit = dp83820transmit; + edev->interrupt = dp83820interrupt; +#ifndef FS + edev->ifstat = dp83820ifstat; + edev->arg = edev; + edev->shutdown = shutdown; + edev->multicast = multicast; + edev->promiscuous = dp83820promiscuous; +#endif + return 0; +} + +#ifndef FS +void +etherdp83820link(void) +{ + addethercard("DP83820", dp83820pnp); +} + +void +etherdp83820bothlink(void) +{ + etherdp83820link(); +} +#endif diff -Nru /sys/src/fs/pc/etherelnk3.c /sys/src/fs/pc/etherelnk3.c --- /sys/src/fs/pc/etherelnk3.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/pc/etherelnk3.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,1848 @@ +/* + * Etherlink III, Fast EtherLink and Fast EtherLink XL adapters. + * To do: + * check robustness in the face of errors (e.g. busmaster & rxUnderrun); + * RxEarly and busmaster; + * autoSelect; + * PCI latency timer and master enable; + * errata list; + * rewrite all initialisation; + * handle the cyclone adapter. + * + * Product ID: + * 9150 ISA 3C509[B] + * 9050 ISA 3C509[B]-TP + * 9450 ISA 3C509[B]-COMBO + * 9550 ISA 3C509[B]-TPO + * + * 9350 EISA 3C579 + * 9250 EISA 3C579-TP + * + * 5920 EISA 3C592-[TP|COMBO|TPO] + * 5970 EISA 3C597-TX Fast Etherlink 10BASE-T/100BASE-TX + * 5971 EISA 3C597-T4 Fast Etherlink 10BASE-T/100BASE-T4 + * 5972 EISA 3C597-MII Fast Etherlink 10BASE-T/MII + * + * 5900 PCI 3C590-[TP|COMBO|TPO] + * 5950 PCI 3C595-TX Fast Etherlink Shared 10BASE-T/100BASE-TX + * 5951 PCI 3C595-T4 Fast Etherlink Shared 10BASE-T/100BASE-T4 + * 5952 PCI 3C595-MII Fast Etherlink 10BASE-T/MII + * + * 9000 PCI 3C900-TPO Etherlink III XL PCI 10BASE-T + * 9001 PCI 3C900-COMBO Etherlink III XL PCI 10BASE-T/10BASE-2/AUI + * 9005 PCI 3C900B-COMBO Etherlink III XL PCI 10BASE-T/10BASE-2/AUI + * 9050 PCI 3C905-TX Fast Etherlink XL Shared 10BASE-T/100BASE-TX + * 9051 PCI 3C905-T4 Fast Etherlink Shared 10BASE-T/100BASE-T4 + * 9055 PCI 3C905B-TX Fast Etherlink Shared 10BASE-T/100BASE-TX + * 9200 PCI 3C905C-TX Fast Etherlink Shared 10BASE-T/100BASE-TX + * + * 9058 PCMCIA 3C589[B]-[TP|COMBO] + * + * 627C MCA 3C529 + * 627D MCA 3C529-TP + */ +#include "all.h" +#include "io.h" +#include "mem.h" + +#include "../ip/ip.h" +#include "etherif.h" + +#define XCVRDEBUG 0 +#define xcvrdebug if(XCVRDEBUG)print + +enum { + IDport = 0x0110, /* anywhere between 0x0100 and 0x01F0 */ +}; + +enum { /* all windows */ + CommandR = 0x000E, + IntStatusR = 0x000E, +}; + +enum { /* Commands */ + GlobalReset = 0x0000, + SelectRegisterWindow = 0x0001, + EnableDcConverter = 0x0002, + RxDisable = 0x0003, + RxEnable = 0x0004, + RxReset = 0x0005, + Stall = 0x0006, /* 3C90x */ + TxDone = 0x0007, + RxDiscard = 0x0008, + TxEnable = 0x0009, + TxDisable = 0x000A, + TxReset = 0x000B, + RequestInterrupt = 0x000C, + AcknowledgeInterrupt = 0x000D, + SetInterruptEnable = 0x000E, + SetIndicationEnable = 0x000F, /* SetReadZeroMask */ + SetRxFilter = 0x0010, + SetRxEarlyThresh = 0x0011, + SetTxAvailableThresh = 0x0012, + SetTxStartThresh = 0x0013, + StartDma = 0x0014, /* initiate busmaster operation */ + StatisticsEnable = 0x0015, + StatisticsDisable = 0x0016, + DisableDcConverter = 0x0017, + SetTxReclaimThresh = 0x0018, /* PIO-only adapters */ + PowerUp = 0x001B, /* not all adapters */ + PowerDownFull = 0x001C, /* not all adapters */ + PowerAuto = 0x001D, /* not all adapters */ +}; + +enum { /* (Global|Rx|Tx)Reset command bits */ + tpAuiReset = 0x0001, /* 10BaseT and AUI transceivers */ + endecReset = 0x0002, /* internal Ethernet encoder/decoder */ + networkReset = 0x0004, /* network interface logic */ + fifoReset = 0x0008, /* FIFO control logic */ + aismReset = 0x0010, /* autoinitialise state-machine logic */ + hostReset = 0x0020, /* bus interface logic */ + dmaReset = 0x0040, /* bus master logic */ + vcoReset = 0x0080, /* on-board 10Mbps VCO */ + updnReset = 0x0100, /* upload/download (Rx/TX) logic */ + + resetMask = 0x01FF, +}; + +enum { /* Stall command bits */ + upStall = 0x0000, + upUnStall = 0x0001, + dnStall = 0x0002, + dnUnStall = 0x0003, +}; + +enum { /* SetRxFilter command bits */ + receiveIndividual = 0x0001, /* match station address */ + receiveMulticast = 0x0002, + receiveBroadcast = 0x0004, + receiveAllFrames = 0x0008, /* promiscuous */ +}; + +enum { /* StartDma command bits */ + Upload = 0x0000, /* transfer data from adapter to memory */ + Download = 0x0001, /* transfer data from memory to adapter */ +}; + +enum { /* IntStatus bits */ + interruptLatch = 0x0001, + hostError = 0x0002, /* Adapter Failure */ + txComplete = 0x0004, + txAvailable = 0x0008, + rxComplete = 0x0010, + rxEarly = 0x0020, + intRequested = 0x0040, + updateStats = 0x0080, + transferInt = 0x0100, /* Bus Master Transfer Complete */ + dnComplete = 0x0200, + upComplete = 0x0400, + busMasterInProgress = 0x0800, + commandInProgress = 0x1000, + + interruptMask = 0x07FE, +}; + +#define COMMAND(port, cmd, a) outs((port)+CommandR, ((cmd)<<11)|(a)) +#define STATUS(port) ins((port)+IntStatusR) + +enum { /* Window 0 - setup */ + Wsetup = 0x0000, + /* registers */ + ManufacturerID = 0x0000, /* 3C5[08]*, 3C59[27] */ + ProductID = 0x0002, /* 3C5[08]*, 3C59[27] */ + ConfigControl = 0x0004, /* 3C5[08]*, 3C59[27] */ + AddressConfig = 0x0006, /* 3C5[08]*, 3C59[27] */ + ResourceConfig = 0x0008, /* 3C5[08]*, 3C59[27] */ + EepromCommand = 0x000A, + EepromData = 0x000C, + /* AddressConfig Bits */ + autoSelect9 = 0x0080, + xcvrMask9 = 0xC000, + /* ConfigControl bits */ + Ena = 0x0001, + base10TAvailable9 = 0x0200, + coaxAvailable9 = 0x1000, + auiAvailable9 = 0x2000, + /* EepromCommand bits */ + EepromReadRegister = 0x0080, + EepromBusy = 0x8000, +}; + +#define EEPROMCMD(port, cmd, a) outs((port)+EepromCommand, (cmd)|(a)) +#define EEPROMBUSY(port) (ins((port)+EepromCommand) & EepromBusy) +#define EEPROMDATA(port) ins((port)+EepromData) + +enum { /* Window 1 - operating set */ + Wop = 0x0001, + /* registers */ + Fifo = 0x0000, + RxError = 0x0004, /* 3C59[0257] only */ + RxStatus = 0x0008, + Timer = 0x000A, + TxStatus = 0x000B, + TxFree = 0x000C, + /* RxError bits */ + rxOverrun = 0x0001, + runtFrame = 0x0002, + alignmentError = 0x0004, /* Framing */ + crcError = 0x0008, + oversizedFrame = 0x0010, + dribbleBits = 0x0080, + /* RxStatus bits */ + rxBytes = 0x1FFF, /* 3C59[0257] mask */ + rxBytes9 = 0x07FF, /* 3C5[078]9 mask */ + rxError9 = 0x3800, /* 3C5[078]9 error mask */ + rxOverrun9 = 0x0000, + oversizedFrame9 = 0x0800, + dribbleBits9 = 0x1000, + runtFrame9 = 0x1800, + alignmentError9 = 0x2000, /* Framing */ + crcError9 = 0x2800, + rxError = 0x4000, + rxIncomplete = 0x8000, + /* TxStatus Bits */ + txStatusOverflow = 0x0004, + maxCollisions = 0x0008, + txUnderrun = 0x0010, + txJabber = 0x0020, + interruptRequested = 0x0040, + txStatusComplete = 0x0080, +}; + +enum { /* Window 2 - station address */ + Wstation = 0x0002, + + ResetOp905B = 0x000C, +}; + +enum { /* Window 3 - FIFO management */ + Wfifo = 0x0003, + /* registers */ + InternalConfig = 0x0000, /* 3C509B, 3C589, 3C59[0257] */ + OtherInt = 0x0004, /* 3C59[0257] */ + RomControl = 0x0006, /* 3C509B, 3C59[27] */ + MacControl = 0x0006, /* 3C59[0257] */ + ResetOptions = 0x0008, /* 3C59[0257] */ + MediaOptions = 0x0008, /* 3C905B */ + RxFree = 0x000A, + /* InternalConfig bits */ + disableBadSsdDetect = 0x00000100, + ramLocation = 0x00000200, /* 0 external, 1 internal */ + ramPartition5to3 = 0x00000000, + ramPartition3to1 = 0x00010000, + ramPartition1to1 = 0x00020000, + ramPartition3to5 = 0x00030000, + ramPartitionMask = 0x00030000, + xcvr10BaseT = 0x00000000, + xcvrAui = 0x00100000, /* 10BASE5 */ + xcvr10Base2 = 0x00300000, + xcvr100BaseTX = 0x00400000, + xcvr100BaseFX = 0x00500000, + xcvrMii = 0x00600000, + xcvrMask = 0x00700000, + autoSelect = 0x01000000, + /* MacControl bits */ + deferExtendEnable = 0x0001, + deferTimerSelect = 0x001E, /* mask */ + fullDuplexEnable = 0x0020, + allowLargePackets = 0x0040, + extendAfterCollision = 0x0080, /* 3C90xB */ + flowControlEnable = 0x0100, /* 3C90xB */ + vltEnable = 0x0200, /* 3C90xB */ + /* ResetOptions bits */ + baseT4Available = 0x0001, + baseTXAvailable = 0x0002, + baseFXAvailable = 0x0004, + base10TAvailable = 0x0008, + coaxAvailable = 0x0010, + auiAvailable = 0x0020, + miiConnector = 0x0040, +}; + +enum { /* Window 4 - diagnostic */ + Wdiagnostic = 0x0004, + /* registers */ + VcoDiagnostic = 0x0002, + FifoDiagnostic = 0x0004, + NetworkDiagnostic = 0x0006, + PhysicalMgmt = 0x0008, + MediaStatus = 0x000A, + BadSSD = 0x000C, + UpperBytesOk = 0x000D, + /* FifoDiagnostic bits */ + txOverrun = 0x0400, + rxUnderrun = 0x2000, + receiving = 0x8000, + /* PhysicalMgmt bits */ + mgmtClk = 0x0001, + mgmtData = 0x0002, + mgmtDir = 0x0004, + cat5LinkTestDefeat = 0x8000, + /* MediaStatus bits */ + dataRate100 = 0x0002, + crcStripDisable = 0x0004, + enableSqeStats = 0x0008, + collisionDetect = 0x0010, + carrierSense = 0x0020, + jabberGuardEnable = 0x0040, + linkBeatEnable = 0x0080, + jabberDetect = 0x0200, + polarityReversed = 0x0400, + linkBeatDetect = 0x0800, + txInProg = 0x1000, + dcConverterEnabled = 0x4000, + auiDisable = 0x8000, /* 10BaseT transceiver selected */ +}; + +enum { /* Window 5 - internal state */ + Wstate = 0x0005, + /* registers */ + TxStartThresh = 0x0000, + TxAvailableThresh = 0x0002, + RxEarlyThresh = 0x0006, + RxFilter = 0x0008, + InterruptEnable = 0x000A, + IndicationEnable = 0x000C, +}; + +enum { /* Window 6 - statistics */ + Wstatistics = 0x0006, + /* registers */ + CarrierLost = 0x0000, + SqeErrors = 0x0001, + MultipleColls = 0x0002, + SingleCollFrames = 0x0003, + LateCollisions = 0x0004, + RxOverruns = 0x0005, + FramesXmittedOk = 0x0006, + FramesRcvdOk = 0x0007, + FramesDeferred = 0x0008, + UpperFramesOk = 0x0009, + BytesRcvdOk = 0x000A, + BytesXmittedOk = 0x000C, +}; + +enum { /* Window 7 - bus master operations */ + Wmaster = 0x0007, + /* registers */ + MasterAddress = 0x0000, + MasterLen = 0x0006, + MasterStatus = 0x000C, + /* MasterStatus bits */ + masterAbort = 0x0001, + targetAbort = 0x0002, + targetRetry = 0x0004, + targetDisc = 0x0008, + masterDownload = 0x1000, + masterUpload = 0x4000, + masterInProgress = 0x8000, + + masterMask = 0xD00F, +}; + +enum { /* 3C90x extended register set */ + Timer905 = 0x001A, /* 8-bits */ + TxStatus905 = 0x001B, /* 8-bits */ + PktStatus = 0x0020, /* 32-bits */ + DnListPtr = 0x0024, /* 32-bits, 8-byte aligned */ + FragAddr = 0x0028, /* 32-bits */ + FragLen = 0x002C, /* 16-bits */ + ListOffset = 0x002E, /* 8-bits */ + TxFreeThresh = 0x002F, /* 8-bits */ + UpPktStatus = 0x0030, /* 32-bits */ + FreeTimer = 0x0034, /* 16-bits */ + UpListPtr = 0x0038, /* 32-bits, 8-byte aligned */ + + /* PktStatus bits */ + fragLast = 0x00000001, + dnCmplReq = 0x00000002, + dnStalled = 0x00000004, + upCompleteX = 0x00000008, + dnCompleteX = 0x00000010, + upRxEarlyEnable = 0x00000020, + armCountdown = 0x00000040, + dnInProg = 0x00000080, + counterSpeed = 0x00000010, /* 0 3.2uS, 1 320nS */ + countdownMode = 0x00000020, + /* UpPktStatus bits (dpd->control) */ + upPktLenMask = 0x00001FFF, + upStalled = 0x00002000, + upError = 0x00004000, + upPktComplete = 0x00008000, + upOverrun = 0x00010000, /* RxError<<16 */ + upRuntFrame = 0x00020000, + upAlignmentError = 0x00040000, + upCRCError = 0x00080000, + upOversizedFrame = 0x00100000, + upDribbleBits = 0x00800000, + upOverflow = 0x01000000, + + dnIndicate = 0x80000000, /* FrameStartHeader (dpd->control) */ + + updnLastFrag = 0x80000000, /* (dpd->len) */ + + Nup = 32, + Ndn = 64, +}; + +/* + * Up/Dn Packet Descriptors. + * The hardware info (np, control, addr, len) must be 8-byte aligned + * and this structure size must be a multiple of 8. + */ +typedef struct Pd Pd; +typedef struct Pd { + ulong np; /* next pointer */ + ulong control; /* FSH or UpPktStatus */ + ulong addr; + ulong len; + + Pd* next; + Msgbuf* mb; +} Pd; + +typedef struct Ctlr Ctlr; +typedef struct Ctlr { + Lock wlock; /* window access */ + + int port; + int irq; + int tbdf; + Ctlr* next; + int active; + int busmaster; + int xcvr; /* transceiver type */ + int rxstatus9; /* old-style RxStatus */ + int rxearly; /* RxEarlyThreshold */ + int ts; /* threshold shift */ + int upenabled; + int dnenabled; + + int attached; + + Msgbuf* rmb; /* receive buffer */ + + Msgbuf* txmb; /* FIFO -based transmission */ + int txthreshold; + int txbusy; + + int nup; /* full-busmaster -based reception */ + void* upbase; + Pd* upr; + Pd* uphead; + + int ndn; /* full-busmaster -based transmission */ + void* dnbase; + Pd* dnr; + Pd* dnhead; + Pd* dntail; + int dnq; + + long interrupts; /* statistics */ + long timer[2]; + long stats[BytesRcvdOk+3]; + + int upqmax; + int upqmaxhw; + ulong upinterrupts; + ulong upqueued; + ulong upstalls; + int dnqmax; + int dnqmaxhw; + ulong dninterrupts; + ulong dnqueued; +} Ctlr; + +static Ctlr* ctlrhead; +static Ctlr* ctlrtail; + +static void +init905(Ctlr* ctlr) +{ + Msgbuf *mb; + Pd *pd, *prev; + + /* + * Create rings for the receive and transmit sides. + * Take care with alignment: + * make sure ring base is 8-byte aligned; + * make sure each entry is 8-byte aligned. + */ + ctlr->upbase = ialloc((ctlr->nup+1)*sizeof(Pd), 8); + ctlr->upr = (Pd*)ROUNDUP((ulong)ctlr->upbase, 8); + + prev = ctlr->upr; + for(pd = &ctlr->upr[ctlr->nup-1]; pd >= ctlr->upr; pd--){ + pd->np = PADDR(&prev->np); + pd->control = 0; + mb = mballoc(sizeof(Enpkt), 0, Mbeth2); + pd->addr = PADDR(mb->data); + pd->len = updnLastFrag|sizeof(Enpkt); + + pd->next = prev; + prev = pd; + pd->mb = mb; + } + ctlr->uphead = ctlr->upr; + + ctlr->dnbase = ialloc((ctlr->ndn+1)*sizeof(Pd), 8); + ctlr->dnr = (Pd*)ROUNDUP((ulong)ctlr->dnbase, 8); + + prev = ctlr->dnr; + for(pd = &ctlr->dnr[ctlr->ndn-1]; pd >= ctlr->dnr; pd--){ + pd->next = prev; + prev = pd; + } + ctlr->dnhead = ctlr->dnr; + ctlr->dntail = ctlr->dnr; + ctlr->dnq = 0; +} + +static Msgbuf* +allocrmb(void) +{ + /* + * The receive buffers must be on a 32-byte + * boundary for EISA busmastering. + * Fortunately Msgbufs are aligned OK. + */ + return mballoc(ROUNDUP(sizeof(Enpkt), 4) + 31, 0, Mbeth1); +} + +static uchar* +startdma(Ether* ether, ulong address) +{ + int port, status, w; + uchar *wp; + + port = ether->port; + + w = (STATUS(port)>>13) & 0x07; + COMMAND(port, SelectRegisterWindow, Wmaster); + + wp = (uchar*)(KZERO|(inl(port+MasterAddress))); + status = ins(port+MasterStatus); + if(status & (masterInProgress|targetAbort|masterAbort)) + print("#l%d: BM status 0x%ux\n", ether->ctlrno, status); + outs(port+MasterStatus, masterMask); + outl(port+MasterAddress, address); + outs(port+MasterLen, sizeof(Enpkt)); + COMMAND(port, StartDma, Upload); + + COMMAND(port, SelectRegisterWindow, w); + return wp; +} + +static void +attach(Ether* ether) +{ + int port, x; + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(&ctlr->wlock); + if(ctlr->attached){ + iunlock(&ctlr->wlock); + return; + } + + port = ether->port; + + /* + * Set the receiver packet filter for this and broadcast addresses, + * set the interrupt masks for all interrupts, enable the receiver + * and transmitter. + */ + COMMAND(port, SetRxFilter, receiveBroadcast|receiveIndividual); + + x = interruptMask; + if(ctlr->busmaster == 1) + x &= ~(rxEarly|rxComplete); + else{ + if(ctlr->dnenabled) + x &= ~transferInt; + if(ctlr->upenabled) + x &= ~(rxEarly|rxComplete); + } + COMMAND(port, SetIndicationEnable, x); + COMMAND(port, SetInterruptEnable, x); + + COMMAND(port, RxEnable, 0); + COMMAND(port, TxEnable, 0); + + /* + * Prime the busmaster channel for receiving directly into a + * receive packet buffer if necessary. + */ + if(ctlr->busmaster == 1) + startdma(ether, PADDR(ctlr->rmb->data)); + else{ + if(ctlr->upenabled) + outl(port+UpListPtr, PADDR(&ctlr->uphead->np)); + } + + ctlr->attached = 1; + iunlock(&ctlr->wlock); +} + +static void +statistics(Ether* ether) +{ + int port, i, u, w; + Ctlr *ctlr; + + port = ether->port; + ctlr = ether->ctlr; + + /* + * 3C59[27] require a read between a PIO write and + * reading a statistics register. + */ + w = (STATUS(port)>>13) & 0x07; + COMMAND(port, SelectRegisterWindow, Wstatistics); + STATUS(port); + + for(i = 0; i < UpperFramesOk; i++) + ctlr->stats[i] += inb(port+i) & 0xFF; + u = inb(port+UpperFramesOk) & 0xFF; + ctlr->stats[FramesXmittedOk] += (u & 0x30)<<4; + ctlr->stats[FramesRcvdOk] += (u & 0x03)<<8; + ctlr->stats[BytesRcvdOk] += ins(port+BytesRcvdOk) & 0xFFFF; + ctlr->stats[BytesRcvdOk+1] += ins(port+BytesXmittedOk) & 0xFFFF; + + switch(ctlr->xcvr){ + + case xcvrMii: + case xcvr100BaseTX: + case xcvr100BaseFX: + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + STATUS(port); + ctlr->stats[BytesRcvdOk+2] += inb(port+BadSSD); + break; + } + + COMMAND(port, SelectRegisterWindow, w); +} + +static void +txstart(Ether* ether) +{ + int port, len; + Ctlr *ctlr; + Msgbuf *mb; + + port = ether->port; + ctlr = ether->ctlr; + + /* + * Attempt to top-up the transmit FIFO. If there's room simply + * stuff in the packet length (unpadded to a dword boundary), the + * packet data (padded) and remove the packet from the queue. + * If there's no room post an interrupt for when there is. + * This routine is called both from the top level and from interrupt + * level and expects to be called with ctlr->wlock already locked + * and the correct register window (Wop) in place. + */ + for(;;){ + if(ctlr->txmb != nil){ + mb = ctlr->txmb; + ctlr->txmb = nil; + } + else{ + mb = etheroq(ether); + if(mb == nil) + break; + } + + len = ROUNDUP(mb->count, 4); + if(len+4 <= ins(port+TxFree)){ + outl(port+Fifo, mb->count); + outsl(port+Fifo, mb->data, len/4); + mbfree(mb); + } + else{ + ctlr->txmb = mb; + if(ctlr->txbusy == 0){ + ctlr->txbusy = 1; + COMMAND(port, SetTxAvailableThresh, len>>ctlr->ts); + } + break; + } + } +} + +static void +txstart905(Ether* ether) +{ + Ctlr *ctlr; + int port, stalled, timeo; + Msgbuf *mb; + Pd *pd; + + ctlr = ether->ctlr; + port = ether->port; + + /* + * Free any completed packets. + */ + pd = ctlr->dntail; + while(ctlr->dnq){ + if(PADDR(&pd->np) == inl(port+DnListPtr)) + break; + if(pd->mb != nil){ + mbfree(pd->mb); + pd->mb = nil; + } + ctlr->dnq--; + pd = pd->next; + } + ctlr->dntail = pd; + + stalled = 0; + while(ctlr->dnq < (ctlr->ndn-1)){ + mb = etheroq(ether); + if(mb == nil) + break; + + pd = ctlr->dnhead->next; + pd->np = 0; + pd->control = dnIndicate|mb->count; + pd->addr = PADDR(mb->data); + pd->len = updnLastFrag|mb->count; + pd->mb = mb; + + if(stalled == 0 && ctlr->dnq && inl(port+DnListPtr)){ + COMMAND(port, Stall, dnStall); + for(timeo = 100; (STATUS(port) & commandInProgress) && timeo; timeo--) + ; + if(timeo == 0) + print("#l%d: dnstall %d\n", ether->ctlrno, timeo); + stalled = 1; + } + + coherence(); + ctlr->dnhead->np = PADDR(&pd->np); + ctlr->dnhead->control &= ~dnIndicate; + ctlr->dnhead = pd; + if(ctlr->dnq == 0) + ctlr->dntail = pd; + ctlr->dnq++; + + ctlr->dnqueued++; + } + + if(ctlr->dnq > ctlr->dnqmax) + ctlr->dnqmax = ctlr->dnq; + + /* + * If the adapter is not currently processing anything + * and there is something on the queue, start it processing. + */ + if(inl(port+DnListPtr) == 0 && ctlr->dnq) + outl(port+DnListPtr, PADDR(&ctlr->dnhead->np)); + if(stalled) + COMMAND(port, Stall, dnUnStall); +} + +static void +transmit(Ether* ether) +{ + Ctlr *ctlr; + int port, w; + + port = ether->port; + ctlr = ether->ctlr; + + ilock(&ctlr->wlock); + if(ctlr->dnenabled) + txstart905(ether); + else{ + w = (STATUS(port)>>13) & 0x07; + COMMAND(port, SelectRegisterWindow, Wop); + txstart(ether); + COMMAND(port, SelectRegisterWindow, w); + } + iunlock(&ctlr->wlock); +} + +static void +receive905(Ether* ether) +{ + Ctlr *ctlr; + int len, port, q; + Pd *pd; + Msgbuf *mb; + + ctlr = ether->ctlr; + port = ether->port; + + if(inl(port+UpPktStatus) & upStalled) + ctlr->upstalls++; + q = 0; + for(pd = ctlr->uphead; pd->control & upPktComplete; pd = pd->next){ + if(pd->control & upError){ + if(pd->control & upOverrun) + ether->ifc.rcverr++; + else if(pd->control & (upOversizedFrame|upRuntFrame)) + ether->ifc.rcverr++; + else if(pd->control & upAlignmentError) + ether->ifc.rcverr++; + if(pd->control & upCRCError) + ether->ifc.sumerr++; + } + else if(mb = mballoc(sizeof(Enpkt)+4, 0, Mbeth1)){ + len = pd->control & rxBytes; + pd->mb->count = len; + etheriq(ether, pd->mb); + pd->mb = mb; + pd->addr = PADDR(mb->data); + coherence(); + } + + pd->control = 0; + COMMAND(port, Stall, upUnStall); + + q++; + } + ctlr->uphead = pd; + + ctlr->upqueued += q; + if(q > ctlr->upqmax) + ctlr->upqmax = q; +} + +static void +receive(Ether* ether) +{ + int len, port, rxerror, rxstatus; + Ctlr *ctlr; + Msgbuf *mb, *rmb; + + port = ether->port; + ctlr = ether->ctlr; + + while(((rxstatus = ins(port+RxStatus)) & rxIncomplete) == 0){ + if(ctlr->busmaster == 1 && (STATUS(port) & busMasterInProgress)) + break; + + /* + * If there was an error, log it and continue. + * Unfortunately the 3C5[078]9 has the error info in the status + * register and the 3C59[0257] implement a separate RxError + * register. + */ + if(rxstatus & rxError){ + if(ctlr->rxstatus9){ + switch(rxstatus & rxError9){ + + case rxOverrun9: + case oversizedFrame9: + case runtFrame9: + case alignmentError9: + ether->ifc.rcverr++; + break; + case crcError9: + ether->ifc.sumerr++; + break; + + } + } + else{ + rxerror = inb(port+RxError); + if(rxerror & (alignmentError|oversizedFrame|runtFrame|rxOverrun)) + ether->ifc.rcverr++; + if(rxerror & crcError) + ether->ifc.sumerr++; + } + } + + /* + * If there was an error or a new receive buffer can't be + * allocated, discard the packet and go on to the next. + */ + rmb = ctlr->rmb; + if((rxstatus & rxError) || (mb = allocrmb()) == nil){ + COMMAND(port, RxDiscard, 0); + while(STATUS(port) & commandInProgress) + ; + + if(ctlr->busmaster == 1) + startdma(ether, PADDR(rmb->data)); + + continue; + } + + /* + * A valid receive packet awaits: + * if using PIO, read it into the buffer; + * discard the packet from the FIFO; + * if using busmastering, start a new transfer for + * the next packet and as a side-effect get the + * end-pointer of the one just received; + * pass the packet on to whoever wants it. + */ + if(ctlr->busmaster == 0 || ctlr->busmaster == 2){ + len = (rxstatus & rxBytes9); + rmb->count = len; + insl(port+Fifo, rmb->data, HOWMANY(len, 4)); + } + + COMMAND(port, RxDiscard, 0); + while(STATUS(port) & commandInProgress) + ; + + if(ctlr->busmaster == 1) + rmb->count = startdma(ether, PADDR(mb->data))-rmb->data; + + etheriq(ether, rmb); + ctlr->rmb = mb; + } +} + +static void +interrupt(Ureg*, void* arg) +{ + Ether *ether; + int port, status, s, txstatus, w, x; + Ctlr *ctlr; + + ether = arg; + port = ether->port; + ctlr = ether->ctlr; + + ilock(&ctlr->wlock); + status = STATUS(port); + if(!(status & (interruptMask|interruptLatch))){ + iunlock(&ctlr->wlock); + return; + } + w = (status>>13) & 0x07; + COMMAND(port, SelectRegisterWindow, Wop); + + ctlr->interrupts++; + if(ctlr->busmaster == 2) + ctlr->timer[0] += inb(port+Timer905) & 0xFF; + else + ctlr->timer[0] += inb(port+Timer) & 0xFF; + + do{ + if(status & hostError){ + /* + * Adapter failure, try to find out why, reset if + * necessary. What happens if Tx is active and a reset + * occurs, need to retransmit? This probably isn't right. + */ + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + x = ins(port+FifoDiagnostic); + COMMAND(port, SelectRegisterWindow, Wop); + print("#l%d: status 0x%ux, diag 0x%ux\n", + ether->ctlrno, status, x); + + if(x & txOverrun){ + if(ctlr->busmaster == 0) + COMMAND(port, TxReset, 0); + else + COMMAND(port, TxReset, (updnReset|dmaReset)); + COMMAND(port, TxEnable, 0); + } + + if(x & rxUnderrun){ + /* + * This shouldn't happen... + * Reset the receiver and restore the filter and RxEarly + * threshold before re-enabling. + * Need to restart any busmastering? + */ + COMMAND(port, SelectRegisterWindow, Wstate); + s = (port+RxFilter) & 0x000F; + COMMAND(port, SelectRegisterWindow, Wop); + COMMAND(port, RxReset, 0); + while(STATUS(port) & commandInProgress) + ; + COMMAND(port, SetRxFilter, s); + COMMAND(port, SetRxEarlyThresh, ctlr->rxearly>>ctlr->ts); + COMMAND(port, RxEnable, 0); + } + + status &= ~hostError; + } + + if(status & (transferInt|rxComplete)){ + receive(ether); + status &= ~(transferInt|rxComplete); + } + + if(status & (upComplete)){ + COMMAND(port, AcknowledgeInterrupt, upComplete); + receive905(ether); + status &= ~upComplete; + ctlr->upinterrupts++; + } + + if(status & txComplete){ + /* + * Pop the TxStatus stack, accumulating errors. + * Adjust the TX start threshold if there was an underrun. + * If there was a Jabber or Underrun error, reset + * the transmitter, taking care not to reset the dma logic + * as a busmaster receive may be in progress. + * For all conditions enable the transmitter. + */ + if(ctlr->busmaster == 2) + txstatus = port+TxStatus905; + else + txstatus = port+TxStatus; + s = 0; + do{ + if(x = inb(txstatus)) + outb(txstatus, 0); + s |= x; + }while(STATUS(port) & txComplete); + + if(s & txUnderrun){ + if(ctlr->dnenabled){ + while(inl(port+PktStatus) & dnInProg) + ; + } + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + while(ins(port+MediaStatus) & txInProg) + ; + COMMAND(port, SelectRegisterWindow, Wop); + if(ctlr->txthreshold < ETHERMAXTU) + ctlr->txthreshold += ETHERMINTU; + } + + /* + * According to the manual, maxCollisions does not require + * a TxReset, merely a TxEnable. However, evidence points to + * it being necessary on the 3C905. The jury is still out. + * On busy or badly configured networks maxCollisions can + * happen frequently enough for messages to be annoying so + * keep quiet about them by popular request. + */ + if(s & (txJabber|txUnderrun|maxCollisions)){ + if(ctlr->busmaster == 0) + COMMAND(port, TxReset, 0); + else + COMMAND(port, TxReset, (updnReset|dmaReset)); + while(STATUS(port) & commandInProgress) + ; + COMMAND(port, SetTxStartThresh, ctlr->txthreshold>>ctlr->ts); + if(ctlr->busmaster == 2) + outl(port+TxFreeThresh, HOWMANY(ETHERMAXTU, 256)); + if(ctlr->dnenabled) + status |= dnComplete; + } + + if(s & ~(txStatusComplete|maxCollisions)) + print("#l%d: txstatus 0x%ux, threshold %d\n", + ether->ctlrno, s, ctlr->txthreshold); + COMMAND(port, TxEnable, 0); + ether->ifc.txerr++; + status &= ~txComplete; + status |= txAvailable; + } + + if(status & txAvailable){ + COMMAND(port, AcknowledgeInterrupt, txAvailable); + ctlr->txbusy = 0; + txstart(ether); + status &= ~txAvailable; + } + + if(status & dnComplete){ + COMMAND(port, AcknowledgeInterrupt, dnComplete); + txstart905(ether); + status &= ~dnComplete; + ctlr->dninterrupts++; + } + + if(status & updateStats){ + statistics(ether); + status &= ~updateStats; + } + + /* + * Currently, this shouldn't happen. + */ + if(status & rxEarly){ + COMMAND(port, AcknowledgeInterrupt, rxEarly); + status &= ~rxEarly; + } + + /* + * Panic if there are any interrupts not dealt with. + */ + if(status & interruptMask) + panic("#l%d: interrupt mask 0x%ux\n", ether->ctlrno, status); + + COMMAND(port, AcknowledgeInterrupt, interruptLatch); + }while((status = STATUS(port)) & (interruptMask|interruptLatch)); + + if(ctlr->busmaster == 2) + ctlr->timer[1] += inb(port+Timer905) & 0xFF; + else + ctlr->timer[1] += inb(port+Timer) & 0xFF; + + COMMAND(port, SelectRegisterWindow, w); + iunlock(&ctlr->wlock); +} + +static void +txrxreset(int port) +{ + COMMAND(port, TxReset, 0); + while(STATUS(port) & commandInProgress) + ; + COMMAND(port, RxReset, 0); + while(STATUS(port) & commandInProgress) + ; +} + +static void +tcmadapter(int port, int irq, int tbdf) +{ + Ctlr *ctlr; + + ctlr = ialloc(sizeof(Ctlr), 0); + ctlr->port = port; + ctlr->irq = irq; + ctlr->tbdf = tbdf; + + if(ctlrhead != nil) + ctlrtail->next = ctlr; + else + ctlrhead = ctlr; + ctlrtail = ctlr; +} + +/* + * Write two 0 bytes to identify the IDport and then reset the + * ID sequence. Then send the ID sequence to the card to get + * the card into command state. + */ +static void +idseq(void) +{ + int i; + uchar al; + static int reset, untag; + + /* + * One time only: + * reset any adapters listening + */ + if(reset == 0){ + outb(IDport, 0); + outb(IDport, 0); + outb(IDport, 0xC0); + delay(20); + reset = 1; + } + + outb(IDport, 0); + outb(IDport, 0); + for(al = 0xFF, i = 0; i < 255; i++){ + outb(IDport, al); + if(al & 0x80){ + al <<= 1; + al ^= 0xCF; + } + else + al <<= 1; + } + + /* + * One time only: + * write ID sequence to get the attention of all adapters; + * untag all adapters. + * If a global reset is done here on all adapters it will confuse + * any ISA cards configured for EISA mode. + */ + if(untag == 0){ + outb(IDport, 0xD0); + untag = 1; + } +} + +static ulong +activate(void) +{ + int i; + ushort x, acr; + + /* + * Do the little configuration dance: + * + * 2. write the ID sequence to get to command state. + */ + idseq(); + + /* + * 3. Read the Manufacturer ID from the EEPROM. + * This is done by writing the IDPort with 0x87 (0x80 + * is the 'read EEPROM' command, 0x07 is the offset of + * the Manufacturer ID field in the EEPROM). + * The data comes back 1 bit at a time. + * A delay seems necessary between reading the bits. + * + * If the ID doesn't match, there are no more adapters. + */ + outb(IDport, 0x87); + delay(20); + for(x = 0, i = 0; i < 16; i++){ + delay(20); + x <<= 1; + x |= inb(IDport) & 0x01; + } + if(x != 0x6D50) + return 0; + + /* + * 3. Read the Address Configuration from the EEPROM. + * The Address Configuration field is at offset 0x08 in the EEPROM). + */ + outb(IDport, 0x88); + for(acr = 0, i = 0; i < 16; i++){ + delay(20); + acr <<= 1; + acr |= inb(IDport) & 0x01; + } + + return (acr & 0x1F)*0x10 + 0x200; +} + +static void +tcm509isa(void) +{ + int irq, port; + + /* + * Attempt to activate all adapters. If adapter is set for + * EISA mode (0x3F0), tag it and ignore. Otherwise, activate + * it fully. + */ + while(port = activate()){ + /* + * 6. Tag the adapter so it won't respond in future. + */ + outb(IDport, 0xD1); + if(port == 0x3F0) + continue; + + /* + * 6. Activate the adapter by writing the Activate command + * (0xFF). + */ + outb(IDport, 0xFF); + delay(20); + + /* + * 8. Can now talk to the adapter's I/O base addresses. + * Use the I/O base address from the acr just read. + * + * Enable the adapter and clear out any lingering status + * and interrupts. + */ + while(STATUS(port) & commandInProgress) + ; + COMMAND(port, SelectRegisterWindow, Wsetup); + outs(port+ConfigControl, Ena); + + txrxreset(port); + COMMAND(port, AcknowledgeInterrupt, 0xFF); + + irq = (ins(port+ResourceConfig)>>12) & 0x0F; + tcmadapter(port, irq, BUSUNKNOWN); + } +} + +static void +tcm5XXeisa(void) +{ + ushort x; + int irq, port, slot; + + /* + * Check if this is an EISA machine. + * If not, nothing to do. + */ + if(strncmp((char*)(KZERO|0xFFFD9), "EISA", 4)) + return; + + /* + * Continue through the EISA slots looking for a match on both + * 3COM as the manufacturer and 3C579-* or 3C59[27]-* as the product. + * If an adapter is found, select window 0, enable it and clear + * out any lingering status and interrupts. + */ + for(slot = 1; slot < MaxEISA; slot++){ + port = slot*0x1000; + if(ins(port+0xC80+ManufacturerID) != 0x6D50) + continue; + x = ins(port+0xC80+ProductID); + if((x & 0xF0FF) != 0x9050 && (x & 0xFF00) != 0x5900) + continue; + + COMMAND(port, SelectRegisterWindow, Wsetup); + outs(port+ConfigControl, Ena); + + txrxreset(port); + COMMAND(port, AcknowledgeInterrupt, 0xFF); + + irq = (ins(port+ResourceConfig)>>12) & 0x0F; + tcmadapter(port, irq, BUSUNKNOWN); + } +} + +static void +tcm59Xpci(void) +{ + Pcidev *p; + int irq, port; + + p = nil; + while(p = pcimatch(p, 0x10B7, 0)){ + port = p->mem[0].bar & ~0x01; + irq = p->intl; + COMMAND(port, GlobalReset, 0); + while(STATUS(port) & commandInProgress) + ; + + tcmadapter(port, irq, p->tbdf); + pcisetbme(p); + } +} + +static void +setxcvr(int port, int xcvr, int is9) +{ + int x; + + if(is9){ + COMMAND(port, SelectRegisterWindow, Wsetup); + x = ins(port+AddressConfig) & ~xcvrMask9; + x |= (xcvr>>20)<<14; + outs(port+AddressConfig, x); + } + else{ + COMMAND(port, SelectRegisterWindow, Wfifo); + x = inl(port+InternalConfig) & ~xcvrMask; + x |= xcvr; + outl(port+InternalConfig, x); + } + + txrxreset(port); +} + +static void +setfullduplex(int port) +{ + int x; + + COMMAND(port, SelectRegisterWindow, Wfifo); + x = ins(port+MacControl); + outs(port+MacControl, fullDuplexEnable|x); + + txrxreset(port); +} + +static int +miimdi(int port, int n) +{ + int data, i; + + /* + * Read n bits from the MII Management Register. + */ + data = 0; + for(i = n-1; i >= 0; i--){ + if(ins(port) & mgmtData) + data |= (1<= 0; i--){ + if(bits & (1<>13) & 0x07; + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + port += PhysicalMgmt; + + /* + * Preamble; + * ST+OP+PHYAD+REGAD; + * TA + 16 data bits. + */ + miimdo(port, 0xFFFFFFFF, 32); + miimdo(port, 0x1800|(phyad<<5)|regad, 14); + data = miimdi(port, 18); + + port -= PhysicalMgmt; + COMMAND(port, SelectRegisterWindow, w); + + if(data & 0x10000) + return -1; + + return data & 0xFFFF; +} + +static void +scanphy(int port) +{ + int i, x; + + for(i = 0; i < 32; i++){ + if((x = miir(port, i, 2)) == -1 || x == 0) + continue; + x <<= 6; + x |= miir(port, i, 3)>>10; + xcvrdebug("phy%d: oui %ux reg1 %ux\n", i, x, miir(port, i, 1)); + USED(x); + } +} + +static struct { + char *name; + int avail; + int xcvr; +} media[] = { + "10BaseT", base10TAvailable, xcvr10BaseT, + "10Base2", coaxAvailable, xcvr10Base2, + "100BaseTX", baseTXAvailable, xcvr100BaseTX, + "100BaseFX", baseFXAvailable, xcvr100BaseFX, + "aui", auiAvailable, xcvrAui, + "mii", miiConnector, xcvrMii +}; + +static int +autoselect(int port, int xcvr, int is9) +{ + int media, x; + USED(xcvr); + + /* + * Pathetic attempt at automatic media selection. + * Really just to get the Fast Etherlink 10BASE-T/100BASE-TX + * cards operational. + * It's a bonus if it works for anything else. + */ + if(is9){ + COMMAND(port, SelectRegisterWindow, Wsetup); + x = ins(port+ConfigControl); + media = 0; + if(x & base10TAvailable9) + media |= base10TAvailable; + if(x & coaxAvailable9) + media |= coaxAvailable; + if(x & auiAvailable9) + media |= auiAvailable; + } + else{ + COMMAND(port, SelectRegisterWindow, Wfifo); + media = ins(port+ResetOptions); + } + xcvrdebug("autoselect: media %ux\n", media); + + if(media & miiConnector) + return xcvrMii; + + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + xcvrdebug("autoselect: media status %ux\n", ins(port+MediaStatus)); + + if(media & baseTXAvailable){ + /* + * Must have InternalConfig register. + */ + setxcvr(port, xcvr100BaseTX, is9); + + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + x = ins(port+MediaStatus) & ~(dcConverterEnabled|jabberGuardEnable); + outs(port+MediaStatus, linkBeatEnable|x); + delay(10); + + if(ins(port+MediaStatus) & linkBeatDetect) + return xcvr100BaseTX; + outs(port+MediaStatus, x); + } + + if(media & base10TAvailable){ + setxcvr(port, xcvr10BaseT, is9); + + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + x = ins(port+MediaStatus) & ~dcConverterEnabled; + outs(port+MediaStatus, linkBeatEnable|jabberGuardEnable|x); + delay(100); + + xcvrdebug("autoselect: 10BaseT media status %ux\n", ins(port+MediaStatus)); + if(ins(port+MediaStatus) & linkBeatDetect) + return xcvr10BaseT; + outs(port+MediaStatus, x); + } + + /* + * Botch. + */ + return autoSelect; +} + +static int +eepromdata(int port, int offset) +{ + COMMAND(port, SelectRegisterWindow, Wsetup); + while(EEPROMBUSY(port)) + ; + EEPROMCMD(port, EepromReadRegister, offset); + while(EEPROMBUSY(port)) + ; + return EEPROMDATA(port); +} + +int +etherelnk3reset(Ether* ether) +{ + int anar, anlpar, did, i, j, phyaddr, phystat, port, timeo, x; + uchar ea[Easize]; + Ctlr *ctlr; + char *p; + + /* + * Scan for adapter on PCI, EISA and finally + * using the little ISA configuration dance. + */ + if(ctlrhead == nil){ + tcm59Xpci(); + tcm5XXeisa(); + tcm509isa(); + } + + /* + * Any adapter matches if no ether->port is supplied, + * otherwise the ports must match. + */ + for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + if(ether->port == 0 || ether->port == ctlr->port){ + ctlr->active = 1; + break; + } + } + if(ctlr == nil) + return -1; + + ether->ctlr = ctlr; + ether->port = ctlr->port; + ether->irq = ctlr->irq; + ether->tbdf = ctlr->tbdf; + port = ctlr->port; + + /* + * Read the DeviceID from the EEPROM, it's at offset 0x03, + * and do something depending on capabilities. + */ + switch(did = eepromdata(port, 0x03)){ + + case 0x9000: + case 0x9001: + case 0x9005: + case 0x9050: + case 0x9051: + case 0x9055: + case 0x9200: + if(BUSTYPE(ether->tbdf) != BusPCI) + goto buggery; + ctlr->busmaster = 2; + goto vortex; + + case 0x5900: + case 0x5920: + case 0x5950: + case 0x5951: + case 0x5952: + case 0x5970: + case 0x5971: + case 0x5972: + ctlr->busmaster = 1; + vortex: + COMMAND(port, SelectRegisterWindow, Wfifo); + ctlr->xcvr = inl(port+InternalConfig) & (autoSelect|xcvrMask); + ctlr->rxstatus9 = 0; + ctlr->rxearly = 8188; + break; + + buggery: + default: + ctlr->busmaster = 0; + COMMAND(port, SelectRegisterWindow, Wsetup); + x = ins(port+AddressConfig); + ctlr->xcvr = ((x & xcvrMask9)>>14)<<20; + if(x & autoSelect9) + ctlr->xcvr |= autoSelect; + ctlr->rxstatus9 = 1; + ctlr->rxearly = 2044; + break; + } + + /* + * Check if the adapter's station address is to be overridden. + * If not, read it from the EEPROM and set in ether->ea prior to loading the + * station address in Wstation. The EEPROM returns 16-bits at a time. + */ + memset(ea, 0, Easize); + if(memcmp(ea, ether->ea, Easize) == 0){ + for(i = 0; i < Easize/2; i++){ + x = eepromdata(port, i); + ether->ea[2*i] = x>>8; + ether->ea[2*i+1] = x; + } + } + + COMMAND(port, SelectRegisterWindow, Wstation); + for(i = 0; i < Easize; i++) + outb(port+i, ether->ea[i]); + + /* + * Enable the transceiver if necessary and determine whether + * busmastering can be used. Due to bugs in the first revision + * of the 3C59[05], don't use busmastering at 10Mbps. + */ + xcvrdebug("reset: xcvr %ux\n", ctlr->xcvr); + + /* + * Allow user to specify desired media in plan9.ini + */ + for(i = 0; i < ether->nopt; i++){ + if(cistrncmp(ether->opt[i], "media=", 6) != 0) + continue; + p = ether->opt[i]+6; + for(j = 0; j < nelem(media); j++) + if(cistrcmp(p, media[j].name) == 0) + ctlr->xcvr = media[j].xcvr; + } + +/* + * forgive me, but i am weak + */ +if(did == 0x9055 || did == 0x9200){ + ctlr->xcvr = xcvrMii; + xcvrdebug("9055 reset ops 0x%ux\n", ins(port+ResetOp905B)); +} +else + if(ctlr->xcvr & autoSelect) + ctlr->xcvr = autoselect(port, ctlr->xcvr, ctlr->rxstatus9); + xcvrdebug("autoselect returns: xcvr %ux, did 0x%ux\n", ctlr->xcvr, did); + ether->mbps = 10; + switch(ctlr->xcvr){ + + case xcvrMii: + /* + * Quick hack. + scanphy(port); + */ + phyaddr = 24; + + for(i = 0; i < 7; i++) + xcvrdebug(" %2.2ux", miir(port, phyaddr, i)); + xcvrdebug("\n"); + + for(timeo = 0; timeo < 30; timeo++){ + phystat = miir(port, phyaddr, 0x01); + if(phystat & 0x20) + break; + xcvrdebug(" %2.2ux", phystat); + delay(100); + } + xcvrdebug(" %2.2ux\n", miir(port, phyaddr, 0x01)); + + anar = miir(port, phyaddr, 0x04); + anlpar = miir(port, phyaddr, 0x05) & 0x03E0; + anar &= anlpar; + miir(port, phyaddr, 0x00); + xcvrdebug("mii an: %ux anlp: %ux r0:%ux r1:%ux\n", + anar, anlpar, miir(port, phyaddr, 0x00), + miir(port, phyaddr, 0x01)); + for(i = 0; i < ether->nopt; i++){ + if(cistrcmp(ether->opt[i], "fullduplex") == 0) + anar |= 0x0100; + else if(cistrcmp(ether->opt[i], "100BASE-TXFD") == 0) + anar |= 0x0100; + else if(cistrcmp(ether->opt[i], "force100") == 0) + anar |= 0x0080; + } + xcvrdebug("mii anar: %ux\n", anar); + if(anar & 0x0100){ /* 100BASE-TXFD */ + ether->mbps = 100; + setfullduplex(port); + } + else if(anar & 0x0200){ /* 100BASE-T4 */ + /* nothing to do */ + } + else if(anar & 0x0080) /* 100BASE-TX */ + ether->mbps = 100; + else if(anar & 0x0040) /* 10BASE-TFD */ + setfullduplex(port); + else{ /* 10BASE-T */ + /* nothing to do */ + } + break; + + case xcvr100BaseTX: + case xcvr100BaseFX: + COMMAND(port, SelectRegisterWindow, Wfifo); + x = inl(port+InternalConfig) & ~ramPartitionMask; + outl(port+InternalConfig, x|ramPartition1to1); + + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + x = ins(port+MediaStatus) & ~(dcConverterEnabled|jabberGuardEnable); + x |= linkBeatEnable; + outs(port+MediaStatus, x); + + if(x & dataRate100) + ether->mbps = 100; + break; + + case xcvr10BaseT: + /* + * Enable Link Beat and Jabber to start the + * transceiver. + */ + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + x = ins(port+MediaStatus) & ~dcConverterEnabled; + x |= linkBeatEnable|jabberGuardEnable; + outs(port+MediaStatus, x); + + if((did & 0xFF00) == 0x5900) + ctlr->busmaster = 0; + break; + + case xcvr10Base2: + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + x = ins(port+MediaStatus) & ~(linkBeatEnable|jabberGuardEnable); + outs(port+MediaStatus, x); + + /* + * Start the DC-DC converter. + * Wait > 800 microseconds. + */ + COMMAND(port, EnableDcConverter, 0); + delay(1); + break; + } + + /* + * Wop is the normal operating register set. + * The 3C59[0257] adapters allow access to more than one register window + * at a time, but there are situations where switching still needs to be + * done, so just do it. + * Clear out any lingering Tx status. + */ + COMMAND(port, SelectRegisterWindow, Wop); + if(ctlr->busmaster == 2) + x = port+TxStatus905; + else + x = port+TxStatus; + while(inb(x)) + outb(x, 0); + + /* + * Clear out the adapter statistics, clear the statistics + * logged into ctlr and enable statistics collection. + */ + ilock(&ctlr->wlock); + statistics(ether); + memset(ctlr->stats, 0, sizeof(ctlr->stats)); + + if(ctlr->rxearly >= 2048) + ctlr->ts = 2; + + COMMAND(port, StatisticsEnable, 0); + + /* + * Allocate any receive buffers. + */ + switch(ctlr->busmaster){ + + case 2: + ctlr->dnenabled = 1; + + /* + * 10MUpldBug. + * Disabling is too severe, can use receive busmastering at + * 100Mbps OK, but how to tell which rate is actually being used + * - the 3c905 always seems to have dataRate100 set? + * Believe the bug doesn't apply if upRxEarlyEnable is set + * and the threshold is set such that uploads won't start + * until the whole packet has been received. + */ + ctlr->upenabled = 1; + x = eepromdata(port, 0x0F); + if(!(x & 0x01)) + outl(port+PktStatus, upRxEarlyEnable); + + if(ctlr->upenabled || ctlr->dnenabled){ + ctlr->nup = Nup; + ctlr->ndn = Ndn; + init905(ctlr); + } + else + ctlr->rmb = allocrmb(); + outl(port+TxFreeThresh, HOWMANY(ETHERMAXTU, 256)); + break; + + default: + ctlr->rmb = allocrmb(); + break; + } + + /* + * Set a base TxStartThresh which will be incremented + * if any txUnderrun errors occur and ensure no RxEarly + * interrupts happen. + */ + ctlr->txthreshold = ETHERMAXTU/2; + COMMAND(port, SetTxStartThresh, ctlr->txthreshold>>ctlr->ts); + COMMAND(port, SetRxEarlyThresh, ctlr->rxearly>>ctlr->ts); + + iunlock(&ctlr->wlock); + + /* + * Linkage to the generic ethernet driver. + */ + ether->attach = attach; + ether->transmit = transmit; + ether->interrupt = interrupt; + + return 0; +} diff -Nru /sys/src/fs/pc/etherga620.c /sys/src/fs/pc/etherga620.c --- /sys/src/fs/pc/etherga620.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/pc/etherga620.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,1040 @@ +/* + * Netgear GA620 Gigabit Ethernet Card. + * Specific for the Alteon Tigon 2 and Intel Pentium or later. + * To Do: + * cache alignment for PCI Write-and-Invalidate + * mini ring (what size)? + * tune coalescing values + * statistics formatting + * don't update Spi if nothing to send + * receive ring alignment + * watchdog for link management? + */ +#include "all.h" +#include "io.h" +#include "mem.h" +#include "../ip/ip.h" + +#define malign(n) ialloc((n), 32) +#define KADDR(a) ((void*)((ulong)(a)|KZERO)) +#define PCIWINDOW 0 +#define PCIWADDR(va) (PADDR(va)+PCIWINDOW) + +#include "etherif.h" +#include "etherga620fw.h" + +enum { /* Compile-time options */ + DoMhcCheck = 1, + DoCoalUpdateOnly= 1, + DoHardwareCksum = 0, + DoCountTicks = 1, +}; + +enum { + Mhc = 0x0040, /* Miscellaneous Host Control */ + Mlc = 0x0044, /* Miscellaneous Local Control */ + Mc = 0x0050, /* Miscellaneous Configuration */ + Ps = 0x005C, /* PCI State */ + Wba = 0x0068, /* Window Base Address */ + Wd = 0x006C, /* Window Data */ + + DMAas = 0x011C, /* DMA Assist State */ + + CPUAstate = 0x0140, /* CPU A State */ + CPUApc = 0x0144, /* CPU A Programme Counter */ + + CPUBstate = 0x0240, /* CPU B State */ + + Hi = 0x0504, /* Host In Interrupt Handler */ + Cpi = 0x050C, /* Command Producer Index */ + Spi = 0x0514, /* Send Producer Index */ + Rspi = 0x051C, /* Receive Standard Producer Index */ + Rjpi = 0x0524, /* Receive Jumbo Producer Index */ + Rmpi = 0x052C, /* Receive Mini Producer Index */ + + Mac = 0x0600, /* MAC Address */ + Gip = 0x0608, /* General Information Pointer */ + Om = 0x0618, /* Operating Mode */ + DMArc = 0x061C, /* DMA Read Configuration */ + DMAwc = 0x0620, /* DMA Write Configuration */ + Tbr = 0x0624, /* Transmit Buffer Ratio */ + Eci = 0x0628, /* Event Consumer Index */ + Cci = 0x062C, /* Command Consumer Index */ + + Rct = 0x0630, /* Receive Coalesced Ticks */ + Sct = 0x0634, /* Send Coalesced Ticks */ + St = 0x0638, /* Stat Ticks */ + SmcBD = 0x063C, /* Send Max. Coalesced BDs */ + RmcBD = 0x0640, /* Receive Max. Coalesced BDs */ + Nt = 0x0644, /* NIC Tracing */ + Gln = 0x0648, /* Gigabit Link Negotiation */ + Fln = 0x064C, /* 10/100 Link Negotiation */ + Ifx = 0x065C, /* Interface Index */ + IfMTU = 0x0660, /* Interface MTU */ + Mi = 0x0664, /* Mask Interrupts */ + Gls = 0x0668, /* Gigabit Link State */ + Fls = 0x066C, /* 10/100 Link State */ + + Cr = 0x0700, /* Command Ring */ + + Lmw = 0x0800, /* Local Memory Window */ +}; + +enum { /* Mhc */ + Is = 0x00000001, /* Interrupt State */ + Ci = 0x00000002, /* Clear Interrupt */ + Hr = 0x00000008, /* Hard Reset */ + Eebs = 0x00000010, /* Enable Endian Byte Swap */ + Eews = 0x00000020, /* Enable Endian Word (64-bit) swap */ + Mpio = 0x00000040, /* Mask PCI Interrupt Output */ +}; + +enum { /* Mlc */ + SRAM512 = 0x00000200, /* SRAM Bank Size of 512KB */ + SRAMmask = 0x00000300, + EEclk = 0x00100000, /* Serial EEPROM Clock Output */ + EEdoe = 0x00200000, /* Serial EEPROM Data Out Enable */ + EEdo = 0x00400000, /* Serial EEPROM Data Out Value */ + EEdi = 0x00800000, /* Serial EEPROM Data Input */ +}; + +enum { /* Mc */ + SyncSRAM = 0x00100000, /* Set Synchronous SRAM Timing */ +}; + +enum { /* Ps */ + PCIwm32 = 0x000000C0, /* Write Max DMA 32 */ + PCImrm = 0x00020000, /* Use Memory Read Multiple Command */ + PCI66 = 0x00080000, + PCI32 = 0x00100000, + PCIrcmd = 0x06000000, /* PCI Read Command */ + PCIwcmd = 0x70000000, /* PCI Write Command */ +}; + +enum { /* CPUAstate */ + CPUrf = 0x00000010, /* ROM Fail */ + CPUhalt = 0x00010000, /* Halt the internal CPU */ + CPUhie = 0x00040000, /* HALT instruction executed */ +}; + +enum { /* Om */ + BswapBD = 0x00000002, /* Byte Swap Buffer Descriptors */ + WswapBD = 0x00000004, /* Word Swap Buffer Descriptors */ + Warn = 0x00000008, + BswapDMA = 0x00000010, /* Byte Swap DMA Data */ + Only1DMA = 0x00000040, /* Only One DMA Active at a time */ + NoJFrag = 0x00000200, /* Don't Fragment Jumbo Frames */ + Fatal = 0x40000000, +}; + +enum { /* Lmw */ + Lmwsz = 2*1024, /* Local Memory Window Size */ + + Sr = 0x3800, /* Send Ring (accessed via Lmw) */ +}; + +enum { /* Link */ + Lpref = 0x00008000, /* Preferred Link */ + L10MB = 0x00010000, + L100MB = 0x00020000, + L1000MB = 0x00040000, + Lfd = 0x00080000, /* Full Duplex */ + Lhd = 0x00100000, /* Half Duplex */ + Lefc = 0x00200000, /* Emit Flow Control Packets */ + Lofc = 0x00800000, /* Obey Flow Control Packets */ + Lean = 0x20000000, /* Enable Autonegotiation/Sensing */ + Le = 0x40000000, /* Link Enable */ +}; + +typedef struct Host64 { + uint hi; + uint lo; +} Host64; + +typedef struct Ere { /* Event Ring Element */ + int event; /* (event<<24)|(code<<12)|index */ + int unused; +} Ere; + +typedef int Cmd; /* (cmd<<24)|(flags<<12)|index */ + +typedef struct Rbd { /* Receive Buffer Descriptor */ + Host64 addr; + int indexlen; /* (ring-index<<16)|buffer-length */ + int flags; /* only lower 16-bits */ + int checksum; /* (ip<<16)|tcp/udp */ + int error; /* only upper 16-bits */ + int reserved; + void* opaque; /* passed to receive return ring */ +} Rbd; + +typedef struct Sbd { /* Send Buffer Descriptor */ + Host64 addr; + int lenflags; /* (len<<16)|flags */ + int reserved; +} Sbd; + +enum { /* Buffer Descriptor Flags */ + Fend = 0x00000004, /* Frame Ends in this Buffer */ + Frjr = 0x00000010, /* Receive Jumbo Ring Buffer */ + Funicast = 0x00000020, /* Unicast packet (2-bit field) */ + Fmulticast = 0x00000040, /* Multicast packet */ + Fbroadcast = 0x00000060, /* Broadcast packet */ + Ferror = 0x00000400, /* Frame Has Error */ + Frmr = 0x00001000, /* Receive Mini Ring Buffer */ +}; + +enum { /* Buffer Error Flags */ + Ecrc = 0x00010000, /* bad CRC */ + Ecollision = 0x00020000, /* collision */ + Elink = 0x00040000, /* link lost */ + Ephy = 0x00080000, /* unspecified PHY frame decode error */ + Eodd = 0x00100000, /* odd number of nibbles */ + Emac = 0x00200000, /* unspecified MAC abort */ + Elen64 = 0x00400000, /* short packet */ + Eresources = 0x00800000, /* MAC out of internal resources */ + Egiant = 0x01000000, /* packet too big */ +}; + +typedef struct Rcb { /* Ring Control Block */ + Host64 addr; /* points to the Rbd ring */ + int control; /* (max_len<<16)|flags */ + int unused; +} Rcb; + +enum { + TcpUdpCksum = 0x0001, /* Perform TCP or UDP checksum */ + IpCksum = 0x0002, /* Perform IP checksum */ + NoPseudoHdrCksum= 0x0008, /* Don't include the pseudo header */ + VlanAssist = 0x0010, /* Enable VLAN tagging */ + CoalUpdateOnly = 0x0020, /* Coalesce transmit interrupts */ + HostRing = 0x0040, /* Sr in host memory */ + SnapCksum = 0x0080, /* Parse + offload 802.3 SNAP frames */ + UseExtRxBd = 0x0100, /* Extended Rbd for Jumbo frames */ + RingDisabled = 0x0200, /* Jumbo or Mini RCB only */ +}; + +typedef struct Gib { /* General Information Block */ + int statistics[256]; /* Statistics */ + Rcb ercb; /* Event Ring */ + Rcb crcb; /* Command Ring */ + Rcb srcb; /* Send Ring */ + Rcb rsrcb; /* Receive Standard Ring */ + Rcb rjrcb; /* Receive Jumbo Ring */ + Rcb rmrcb; /* Receive Mini Ring */ + Rcb rrrcb; /* Receive Return Ring */ + Host64 epp; /* Event Producer */ + Host64 rrrpp; /* Receive Return Ring Producer */ + Host64 scp; /* Send Consumer */ + Host64 rsp; /* Refresh Stats */ +} Gib; + +enum { /* Host/NIC Interface ring sizes */ + Ner = 256, /* event ring */ + Ncr = 64, /* command ring */ + Nsr = 512, /* send ring */ + Nrsr = 512, /* receive standard ring */ + Nrjr = 256, /* receive jumbo ring */ + Nrmr = 1024, /* receive mini ring */ + Nrrr = 2048, /* receive return ring */ +}; + +enum { + NrsrHI = 72, /* Fill-level of Rsr (m.b. < Nrsr) */ + NrsrLO = 54, /* Level at which to top-up ring */ + NrjrHI = 0, /* Fill-level of Rjr (m.b. < Nrjr) */ + NrjrLO = 0, /* Level at which to top-up ring */ + NrmrHI = 0, /* Fill-level of Rmr (m.b. < Nrmr) */ + NrmrLO = 0, /* Level at which to top-up ring */ +}; + +typedef struct Ctlr Ctlr; +typedef struct Ctlr { + int port; + Pcidev* pcidev; + Ctlr* next; + int active; + int id; + + uchar ea[Easize]; + + int interrupts; + uvlong ticks; + + int* nic; + Gib* gib; + + Ere* er; + + Lock srlock; + Sbd* sr; + Msgbuf** srb; + int nsr; /* currently in send ring */ + + Rbd* rsr; + int nrsr; /* currently in Receive Standard Ring */ + Rbd* rjr; + int nrjr; /* currently in Receive Jumbo Ring */ + Rbd* rmr; + int nrmr; /* currently in Receive Mini Ring */ + Rbd* rrr; + int rrrci; /* Receive Return Ring Consumer Index */ + + int epi[2]; /* Event Producer Index */ + int rrrpi[2]; /* Receive Return Ring Producer Index */ + int sci[3]; /* Send Consumer Index ([2] is host) */ +} Ctlr; + +static Ctlr* ctlrhead; +static Ctlr* ctlrtail; + +#define csr32r(c, r) (*((c)->nic+((r)/4))) +#define csr32w(c, r, v) (*((c)->nic+((r)/4)) = (v)) + +static void +sethost64(Host64* host64, void* addr) +{ + uvlong uvl; + + uvl = PCIWADDR(addr); + host64->hi = uvl>>32; + host64->lo = uvl & 0xFFFFFFFFL; +} + +static void +ga620command(Ctlr* ctlr, int cmd, int flags, int index) +{ + int cpi; + + cpi = csr32r(ctlr, Cpi); + csr32w(ctlr, Cr+(cpi*4), (cmd<<24)|(flags<<12)|index); + cpi = NEXT(cpi, Ncr); + csr32w(ctlr, Cpi, cpi); +} + +static void +ga620attach(Ether* edev) +{ + Ctlr *ctlr; + + ctlr = edev->ctlr; + USED(ctlr); +} + + + + +static void +ga620transmit(Ether* edev) +{ + Sbd *sbd; + Msgbuf *bp; + Ctlr *ctlr; + int sci, spi; + + /* + * For now there are no smarts here, just empty the + * ring and try to fill it back up. Tuning comes later. + */ + ctlr = edev->ctlr; + ilock(&ctlr->srlock); + + /* + * Free any completed packets. + * Ctlr->sci[0] is where the NIC has got to consuming the ring. + * Ctlr->sci[2] is where the host has got to tidying up after the + * NIC has done with the packets. + */ + for(sci = ctlr->sci[2]; sci != ctlr->sci[0]; sci = NEXT(sci, Nsr)){ + if(ctlr->srb[sci] == nil) + continue; + mbfree(ctlr->srb[sci]); + ctlr->srb[sci] = nil; + } + ctlr->sci[2] = sci; + + sci = PREV(sci, Nsr); + for(spi = csr32r(ctlr, Spi); spi != sci; spi = NEXT(spi, Nsr)){ + if((bp = etheroq(edev)) == nil) + break; + + sbd = &ctlr->sr[spi]; + sethost64(&sbd->addr, bp->data); + sbd->lenflags = (bp->count<<16)|Fend; + + ctlr->srb[spi] = bp; + } + csr32w(ctlr, Spi, spi); + + iunlock(&ctlr->srlock); +} + +static void +ga620replenish(Ctlr* ctlr) +{ + Rbd *rbd; + int rspi; + Msgbuf *bp; + + rspi = csr32r(ctlr, Rspi); + while(ctlr->nrsr < NrsrHI){ + if((bp = mballoc(ETHERMAXTU+4, 0, Mbeth1)) == nil) + break; + rbd = &ctlr->rsr[rspi]; + sethost64(&rbd->addr, bp->data); + rbd->indexlen = (rspi<<16)|(ETHERMAXTU+4); + rbd->flags = 0; + rbd->opaque = bp; + + rspi = NEXT(rspi, Nrsr); + ctlr->nrsr++; + } + csr32w(ctlr, Rspi, rspi); +} + +static void +ga620event(Ctlr* ctlr, int eci, int epi) +{ + int event; + + while(eci != epi){ + event = ctlr->er[eci].event; + switch(event>>24){ + case 0x01: /* firmware operational */ + ga620command(ctlr, 0x01, 0x01, 0x00); + ga620command(ctlr, 0x0B, 0x00, 0x00); +print("%8.8uX: %8.8uX\n", ctlr->port, event); + break; + case 0x04: /* statistics updated */ + break; + case 0x06: /* link state changed */ +print("%8.8uX: %8.8uX %8.8uX %8.8uX\n", + ctlr->port, event, csr32r(ctlr, Gls), csr32r(ctlr, Fls)); + break; + case 0x07: /* event error */ + default: + print("er[%d] = %8.8uX\n", eci, event); + break; + } + eci = NEXT(eci, Ner); + } + csr32w(ctlr, Eci, eci); +} + +static void +ga620receive(Ether* edev) +{ + int len; + Rbd *rbd; + Msgbuf *bp; + Ctlr* ctlr; + + ctlr = edev->ctlr; + while(ctlr->rrrci != ctlr->rrrpi[0]){ + rbd = &ctlr->rrr[ctlr->rrrci]; + /* + * Errors are collected in the statistics block so + * no need to tally them here, let ifstat do the work. + */ + len = rbd->indexlen & 0xFFFF; + if(!(rbd->flags & Ferror) && len != 0){ + bp = rbd->opaque; + bp->count = len; + etheriq(edev, bp); + } + else + mbfree(rbd->opaque); + rbd->opaque = nil; + + if(rbd->flags & Frjr) + ctlr->nrjr--; + else if(rbd->flags & Frmr) + ctlr->nrmr--; + else + ctlr->nrsr--; + + ctlr->rrrci = NEXT(ctlr->rrrci, Nrrr); + } +} + +static void +ga620interrupt(Ureg*, void* arg) +{ + int csr; + Ctlr *ctlr; + Ether *edev; + uvlong tsc0, tsc1; + + edev = arg; + ctlr = edev->ctlr; + + if(DoMhcCheck){ + if(!(csr32r(ctlr, Mhc) & Is)) + return; + } + if(DoCountTicks) + cycles(&tsc0); + + ctlr->interrupts++; + csr32w(ctlr, Hi, 1); + + if(ctlr->rrrci != ctlr->rrrpi[0]) + ga620receive(edev); + + ga620transmit(edev); + + csr = csr32r(ctlr, Eci); + if(csr != ctlr->epi[0]) + ga620event(ctlr, csr, ctlr->epi[0]); + + if(ctlr->nrsr <= NrsrLO) + ga620replenish(ctlr); + + csr32w(ctlr, Hi, 0); + + if(DoCountTicks){ + cycles(&tsc1); + ctlr->ticks += tsc1-tsc0; + } +} + +static void +ga620lmw(Ctlr* ctlr, int addr, int* data, int len) +{ + int i, l, lmw, v; + + /* + * Write to or clear ('data' == nil) 'len' bytes of the NIC + * local memory at address 'addr'. + * The destination address and count should be 32-bit aligned. + */ + v = 0; + while(len > 0){ + /* + * 1) Set the window. The (Lmwsz-1) bits are ignored + * in Wba when accessing through the local memory window; + * 2) Find the minimum of how many bytes still to + * transfer and how many left in this window; + * 3) Create the offset into the local memory window in the + * shared memory space then copy (or zero) the data; + * 4) Bump the counts. + */ + csr32w(ctlr, Wba, addr); + + l = ROUNDUP(addr+1, Lmwsz) - addr; + if(l > len) + l = len; + + lmw = Lmw + (addr & (Lmwsz-1)); + for(i = 0; i < l; i += 4){ + if(data != nil) + v = *data++; + csr32w(ctlr, lmw+i, v); + } + + len -= l; + addr += l; + } +} + +static int +ga620init(Ether* edev) +{ + Ctlr *ctlr; + Host64 host64; + int csr, ea, i, flags; + + ctlr = edev->ctlr; + + /* + * Load the MAC address. + */ + ea = (edev->ea[0]<<8)|edev->ea[1]; + csr32w(ctlr, Mac, ea); + ea = (edev->ea[2]<<24)|(edev->ea[3]<<16)|(edev->ea[4]<<8)|edev->ea[5]; + csr32w(ctlr, Mac+4, ea); + + /* + * General Information Block. + */ + ctlr->gib = ialloc(sizeof(Gib), 0); + sethost64(&host64, ctlr->gib); + csr32w(ctlr, Gip, host64.hi); + csr32w(ctlr, Gip+4, host64.lo); + + /* + * Event Ring. + * This is located in host memory. Allocate the ring, + * tell the NIC where it is and initialise the indices. + */ + ctlr->er = malign(sizeof(Ere)*Ner); + sethost64(&ctlr->gib->ercb.addr, ctlr->er); + sethost64(&ctlr->gib->epp, ctlr->epi); + csr32w(ctlr, Eci, 0); + + /* + * Command Ring. + * This is located in the General Communications Region + * and so the value placed in the Rcb is unused, the NIC + * knows where it is. Stick in the value according to + * the datasheet anyway. + * Initialise the ring and indices. + */ + ctlr->gib->crcb.addr.lo = Cr-0x400; + for(i = 0; i < Ncr*4; i += 4) + csr32w(ctlr, Cr+i, 0); + csr32w(ctlr, Cpi, 0); + csr32w(ctlr, Cci, 0); + + /* + * Send Ring. + * This ring is either in NIC memory at a fixed location depending + * on how big the ring is or it is in host memory. If in NIC + * memory it is accessed via the Local Memory Window; with a send + * ring size of 128 the window covers the whole ring and then need + * only be set once: + * ctlr->sr = KADDR(ctlr->port+Lmw); + * ga620lmw(ctlr, Sr, nil, sizeof(Sbd)*Nsr); + * ctlr->gib->srcb.addr.lo = Sr; + * There is nowhere in the Sbd to hold the Block* associated + * with this entry so an external array must be kept. + */ + ctlr->sr = malign(sizeof(Sbd)*Nsr); + sethost64(&ctlr->gib->srcb.addr, ctlr->sr); + if(DoHardwareCksum) + flags = TcpUdpCksum|NoPseudoHdrCksum|HostRing; + else + flags = HostRing; + if(DoCoalUpdateOnly) + flags |= CoalUpdateOnly; + ctlr->gib->srcb.control = (Nsr<<16)|flags; + sethost64(&ctlr->gib->scp, ctlr->sci); + csr32w(ctlr, Spi, 0); + ctlr->srb = ialloc(sizeof(Msgbuf*)*Nsr, 0); + + /* + * Receive Standard Ring. + */ + ctlr->rsr = malign(sizeof(Rbd)*Nrsr); + sethost64(&ctlr->gib->rsrcb.addr, ctlr->rsr); + if(DoHardwareCksum) + flags = TcpUdpCksum|NoPseudoHdrCksum; + else + flags = 0; + ctlr->gib->rsrcb.control = ((ETHERMAXTU+4)<<16)|flags; + csr32w(ctlr, Rspi, 0); + + /* + * Jumbo and Mini Rings. Unused for now. + */ + ctlr->gib->rjrcb.control = RingDisabled; + ctlr->gib->rmrcb.control = RingDisabled; + + /* + * Receive Return Ring. + * This is located in host memory. Allocate the ring, + * tell the NIC where it is and initialise the indices. + */ + ctlr->rrr = malign(sizeof(Rbd)*Nrrr); + sethost64(&ctlr->gib->rrrcb.addr, ctlr->rrr); + ctlr->gib->rrrcb.control = (Nrrr<<16)|0; + sethost64(&ctlr->gib->rrrpp, ctlr->rrrpi); + ctlr->rrrci = 0; + + /* + * Refresh Stats Pointer. + * For now just point it at the existing statistics block. + */ + sethost64(&ctlr->gib->rsp, ctlr->gib->statistics); + + /* + * DMA configuration. + * Use the recommended values. + */ + csr32w(ctlr, DMArc, 0x80); + csr32w(ctlr, DMAwc, 0x80); + + /* + * Transmit Buffer Ratio. + * Set to 1/3 of available buffer space (units are 1/64ths) + * if using Jumbo packets, ~64KB otherwise (assume 1MB on NIC). + */ + if(NrjrHI > 0 || Nsr > 128) + csr32w(ctlr, Tbr, 64/3); + else + csr32w(ctlr, Tbr, 4); + + /* + * Tuneable parameters. + * These defaults are based on the tuning hints in the Alteon + * Host/NIC Software Interface Definition and example software. + */ + csr32w(ctlr, Rct, 120); + csr32w(ctlr, Sct, 400); + csr32w(ctlr, St, 1000000); + csr32w(ctlr, SmcBD, 60); + csr32w(ctlr, RmcBD, 25); + + /* + * Enable DMA Assist Logic. + */ + csr = csr32r(ctlr, DMAas) & ~0x03; + csr32w(ctlr, DMAas, csr|0x01); + + /* + * Link negotiation. + * The bits are set here but the NIC must be given a command + * once it is running to set negotiation in motion. + */ + csr32w(ctlr, Gln, Le|Lean|Lofc|Lfd|L1000MB|Lpref); + csr32w(ctlr, Fln, Le|Lean|Lhd|Lfd|L100MB|L10MB); + + /* + * A unique index for this controller and the maximum packet + * length expected. + * For now only standard packets are expected. + */ + csr32w(ctlr, Ifx, 1); + csr32w(ctlr, IfMTU, ETHERMAXTU+4); + + /* + * Enable Interrupts. + * There are 3 ways to mask interrupts - a bit in the Mhc (which + * is already cleared), the Mi register and the Hi mailbox. + * Writing to the Hi mailbox has the side-effect of clearing the + * PCI interrupt. + */ + csr32w(ctlr, Mi, 0); + csr32w(ctlr, Hi, 0); + + /* + * Start the firmware. + */ + csr32w(ctlr, CPUApc, tigon2FwStartAddr); + csr = csr32r(ctlr, CPUAstate) & ~CPUhalt; + csr32w(ctlr, CPUAstate, csr); + + return 0; +} + +static int +at24c32io(Ctlr* ctlr, char* op, int data) +{ + char *lp, *p; + int i, loop, mlc, r; + + mlc = csr32r(ctlr, Mlc); + + r = 0; + loop = -1; + lp = nil; + for(p = op; *p != '\0'; p++){ + switch(*p){ + default: + return -1; + case ' ': + continue; + case ':': /* start of 8-bit loop */ + if(lp != nil) + return -1; + lp = p; + loop = 7; + continue; + case ';': /* end of 8-bit loop */ + if(lp == nil) + return -1; + loop--; + if(loop >= 0) + p = lp; + else + lp = nil; + continue; + case 'C': /* assert clock */ + mlc |= EEclk; + break; + case 'c': /* deassert clock */ + mlc &= ~EEclk; + break; + case 'D': /* next bit in 'data' byte */ + if(loop < 0) + return -1; + if(data & (1<= 0) + r |= (i<= 0) + return -1; + return r; +} + +static int +at24c32r(Ctlr* ctlr, int addr) +{ + int data; + + /* + * Read a byte at address 'addr' from the Atmel AT24C32 + * Serial EEPROM. The 2-wire EEPROM access is controlled + * by 4 bits in Mlc. See the AT24C32 datasheet for + * protocol details. + */ + /* + * Start condition - a high to low transition of data + * with the clock high must precede any other command. + */ + at24c32io(ctlr, "OECoc", 0); + + /* + * Perform a random read at 'addr'. A dummy byte + * write sequence is performed to clock in the device + * and data word addresses (0 and 'addr' respectively). + */ + data = -1; + if(at24c32io(ctlr, "oE :DCc; oeCIc", 0xA0) != 0) + goto stop; + if(at24c32io(ctlr, "oE :DCc; oeCIc", addr>>8) != 0) + goto stop; + if(at24c32io(ctlr, "oE :DCc; oeCIc", addr) != 0) + goto stop; + + /* + * Now send another start condition followed by a + * request to read the device. The EEPROM responds + * by clocking out the data. + */ + at24c32io(ctlr, "OECoc", 0); + if(at24c32io(ctlr, "oE :DCc; oeCIc", 0xA1) != 0) + goto stop; + data = at24c32io(ctlr, ":CIc;", 0xA1); + +stop: + /* + * Stop condition - a low to high transition of data + * with the clock high is a stop condition. After a read + * sequence, the stop command will place the EEPROM in + * a standby power mode. + */ + at24c32io(ctlr, "oECOc", 0); + + return data; +} + +static int +ga620detach(Ctlr* ctlr) +{ + int timeo; + + /* + * Hard reset (don't know which endian so catch both); + * enable for little-endian mode; + * wait for code to be loaded from serial EEPROM or flash; + * make sure CPU A is halted. + */ + csr32w(ctlr, Mhc, (Hr<<24)|Hr); + csr32w(ctlr, Mhc, ((Eews|Ci)<<24)|(Eews|Ci)); + + microdelay(1); + for(timeo = 0; timeo < 500000; timeo++){ + if((csr32r(ctlr, CPUAstate) & (CPUhie|CPUrf)) == CPUhie) + break; + microdelay(1); + } + if((csr32r(ctlr, CPUAstate) & (CPUhie|CPUrf)) != CPUhie) + return -1; + csr32w(ctlr, CPUAstate, CPUhalt); + + /* + * After reset, CPU B seems to be stuck in 'CPUrf'. + * Worry about it later. + */ + csr32w(ctlr, CPUBstate, CPUhalt); + + return 0; +} + +static int +ga620reset(Ctlr* ctlr) +{ + int cls, csr, data, i; + + if(ga620detach(ctlr) < 0) + return -1; + + /* + * Tigon 2 PCI NICs have 512KB SRAM per bank. + * Clear out any lingering serial EEPROM state + * bits. + */ + csr = csr32r(ctlr, Mlc) & ~(EEdi|EEdo|EEdoe|EEclk|SRAMmask); + csr32w(ctlr, Mlc, SRAM512|csr); + csr = csr32r(ctlr, Mc); + csr32w(ctlr, Mc, SyncSRAM|csr); + + /* + * Initialise PCI State register. + * If PCI Write-and-Invalidate is enabled set the max write DMA + * value to the host cache-line size (32 on Pentium or later). + */ + csr = csr32r(ctlr, Ps) & (PCI32|PCI66); + csr |= PCIwcmd|PCIrcmd|PCImrm; + if(pcicfgr8(ctlr->pcidev, PciPCR) & 0x0010){ + cls = pcicfgr8(ctlr->pcidev, PciCLS) * 4; + if(cls != 32) + pcicfgw8(ctlr->pcidev, PciCLS, 32/4); + csr |= PCIwm32; + } + csr32w(ctlr, Ps, csr); + + /* + * Operating Mode. + */ + csr32w(ctlr, Om, Fatal|NoJFrag|BswapDMA|WswapBD); + + /* + * Snarf the MAC address from the serial EEPROM. + */ + for(i = 0; i < Easize; i++){ + if((data = at24c32r(ctlr, 0x8E+i)) == -1) + return -1; + ctlr->ea[i] = data & 0xFF; + } + + /* + * Load the firmware. + */ + ga620lmw(ctlr, tigon2FwTextAddr, tigon2FwText, tigon2FwTextLen); + ga620lmw(ctlr, tigon2FwRodataAddr, tigon2FwRodata, tigon2FwRodataLen); + ga620lmw(ctlr, tigon2FwDataAddr, tigon2FwData, tigon2FwDataLen); + ga620lmw(ctlr, tigon2FwSbssAddr, nil, tigon2FwSbssLen); + ga620lmw(ctlr, tigon2FwBssAddr, nil, tigon2FwBssLen); + + return 0; +} + + +static void +ga620pci(void) +{ + int port; + Pcidev *p; + Ctlr *ctlr; + extern ulong upamalloc(ulong, int, int); + + p = nil; + while(p = pcimatch(p, 0, 0)){ + if(p->ccru != ((0x02<<8)|0x00)) + continue; + + switch((p->did<<16)|p->vid){ + default: + continue; + case (0x620A<<16)|0x1385: /* Netgear GA620 */ + case (0x630A<<16)|0x1385: /* Netgear GA620T */ + case (0x0001<<16)|0x12AE: /* Alteon Acenic fiber + * and DEC DEGPA-SA */ + case (0x0002<<16)|0x12AE: /* Alteon Acenic copper */ + case (0x0009<<16)|0x10A9: /* SGI Acenic */ + break; + } + + port = upamalloc(p->mem[0].bar & ~0x0F, p->mem[0].size, 0); + if(port == 0){ + print("ga620: can't map %8.8luX\n", p->mem[0].bar); + continue; + } + + ctlr = ialloc(sizeof(Ctlr), 0); + ctlr->port = port; + ctlr->pcidev = p; + ctlr->id = (p->did<<16)|p->vid; + + ctlr->nic = KADDR(ctlr->port); + if(ga620reset(ctlr)){ + //free(ctlr); + continue; + } + + if(ctlrhead != nil) + ctlrtail->next = ctlr; + else + ctlrhead = ctlr; + ctlrtail = ctlr; + } +} + + +int +etherga620reset(Ether* edev) +{ + Ctlr *ctlr; + uchar ea[Easize]; + + if(ctlrhead == nil) + ga620pci(); + + /* + * Any adapter matches if no edev->port is supplied, + * otherwise the ports must match. + */ + for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + if(edev->port == 0 || edev->port == ctlr->port){ + ctlr->active = 1; + break; + } + } + if(ctlr == nil) + return -1; + + edev->ctlr = ctlr; + edev->port = ctlr->port; + edev->irq = ctlr->pcidev->intl; + edev->tbdf = ctlr->pcidev->tbdf; + edev->mbps = 1000; + + /* + * Check if the adapter's station address is to be overridden. + * If not, read it from the EEPROM and set in ether->ea prior to + * loading the station address in the hardware. + */ + memset(ea, 0, Easize); + if(memcmp(ea, edev->ea, Easize) == 0) + memmove(edev->ea, ctlr->ea, Easize); + + ga620init(edev); + + /* + * Linkage to the generic ethernet driver. + */ + edev->attach = ga620attach; + edev->transmit = ga620transmit; + edev->interrupt = ga620interrupt; + + return 0; +} + diff -Nru /sys/src/fs/pc/etherga620fw.h /sys/src/fs/pc/etherga620fw.h --- /sys/src/fs/pc/etherga620fw.h Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/pc/etherga620fw.h Tue Nov 1 00:00:00 2011 @@ -0,0 +1,4858 @@ +/* Generated by genfw.c */ +#define tigon2FwReleaseMajor 0xc +#define tigon2FwReleaseMinor 0x4 +#define tigon2FwReleaseFix 0xb +#define tigon2FwStartAddr 0x00004000 +#define tigon2FwTextAddr 0x00004000 +#define tigon2FwTextLen 0x11bc0 +#define tigon2FwRodataAddr 0x00015bc0 +#define tigon2FwRodataLen 0x10d0 +#define tigon2FwDataAddr 0x00016cc0 +#define tigon2FwDataLen 0x1c0 +#define tigon2FwSbssAddr 0x00016e80 +#define tigon2FwSbssLen 0xcc +#define tigon2FwBssAddr 0x00016f50 +#define tigon2FwBssLen 0x20c0 +static int tigon2FwText[/*(MAX_TEXT_LEN/4) + 1*/] = { +0x0, +0x10000003, 0x0, 0xd, 0xd, +0x3c1d0001, 0x8fbd6d20, 0x3a0f021, 0x3c100000, +0x26104000, 0xc0010c0, 0x0, 0xd, +0x3c1d0001, 0x8fbd6d24, 0x3a0f021, 0x3c100000, +0x26104000, 0xc0017e0, 0x0, 0xd, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x2000008, +0x0, 0x800172f, 0x3c0a0001, 0x800172f, +0x3c0a0002, 0x800172f, 0x0, 0x8002cac, +0x0, 0x8002c4f, 0x0, 0x800172f, +0x3c0a0004, 0x800328a, 0x0, 0x8001a52, +0x0, 0x800394d, 0x0, 0x80038f4, +0x0, 0x800172f, 0x3c0a0006, 0x80039bb, +0x3c0a0007, 0x800172f, 0x3c0a0008, 0x800172f, +0x3c0a0009, 0x8003a13, 0x0, 0x8002ea6, +0x0, 0x800172f, 0x3c0a000b, 0x800172f, +0x3c0a000c, 0x800172f, 0x3c0a000d, 0x80028fb, +0x0, 0x8002890, 0x0, 0x800172f, +0x3c0a000e, 0x800208c, 0x0, 0x8001964, +0x0, 0x8001a04, 0x0, 0x8003ca6, +0x0, 0x8003c94, 0x0, 0x800172f, +0x0, 0x800191a, 0x0, 0x800172f, +0x0, 0x800172f, 0x3c0a0013, 0x800172f, +0x3c0a0014, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x27bdffe0, +0x3c1cc000, 0xafbf001c, 0xafb00018, 0x8f820140, +0x24030003, 0xaf8300ec, 0x34420004, 0xc002b20, +0xaf820140, 0x3c0100c0, 0xc001763, 0xac203ffc, +0x401821, 0x3c020010, 0x3c010001, 0xac236e9c, +0x10620011, 0x43102b, 0x14400002, 0x3c020020, +0x3c020008, 0x1062000c, 0x24050100, 0x3c060001, +0x8cc66e9c, 0x3c040001, 0x24845c74, 0x3821, +0xafa00010, 0xc002b3b, 0xafa00014, 0x3c020020, +0x3c010001, 0xac226e9c, 0x24020008, 0x3c010001, +0xac226eb4, 0x2402001f, 0x3c010001, 0xac226ec4, +0x24020016, 0x3c010001, 0xac226e98, 0x3c05fffe, +0x34a56f08, 0x3c020001, 0x8c426e9c, 0x3c030002, +0x24639010, 0x3c040001, 0x8c846cc4, 0x431023, +0x14800002, 0x458021, 0x2610fa38, 0x2402f000, +0x2028024, 0xc001785, 0x2002021, 0x2022823, +0x3c040020, 0x821823, 0x651823, 0x247bb000, +0x3c03fffe, 0x3463bf08, 0x363b821, 0x3c0600bf, +0x34c6f000, 0x3c070001, 0x8ce76cc0, 0x3c0300bf, +0x3463e000, 0x852023, 0x3c010001, 0xac246ea8, +0x822023, 0x3c010001, 0xac256e90, 0x52842, +0x3c010001, 0xac226e84, 0x27620ffc, 0x3c010001, +0xac226d20, 0x27621ffc, 0xdb3023, 0x7b1823, +0x3c010001, 0xac246e88, 0x3c010001, 0xac256eac, +0x3c010001, 0xac226d24, 0xaf860150, 0x10e00011, +0xaf830250, 0x3c1d0001, 0x8fbd6ccc, 0x3a0f021, +0xc001749, 0x0, 0x3c020001, 0x8c426cd0, +0x3c030001, 0x8c636cd4, 0x2442fe00, 0x24630200, +0x3c010001, 0xac226cd0, 0x3c010001, 0x10000004, +0xac236cd4, 0x3c1d0001, 0x8fbd6d20, 0x3a0f021, +0x3c020001, 0x8c426cc4, 0x1040000d, 0x26fafa38, +0x3c020001, 0x8c426cd0, 0x3c030001, 0x8c636cd4, +0x3c1a0001, 0x8f5a6cd4, 0x2442fa38, 0x246305c8, +0x3c010001, 0xac226cd0, 0x3c010001, 0xac236cd4, +0x3c020001, 0x8c426cc8, 0x14400003, 0x0, +0x3c010001, 0xac206cd0, 0xc001151, 0x0, +0x8fbf001c, 0x8fb00018, 0x3e00008, 0x27bd0020, +0x3c020001, 0x8c426cd0, 0x3c030001, 0x8c636cd4, +0x27bdff98, 0xafb00048, 0x3c100001, 0x8e1066b8, +0xafb20050, 0x3c120000, 0x26524100, 0xafbf0060, +0xafbe005c, 0xafb50058, 0xafb30054, 0xafb1004c, +0xafa20034, 0xafa30030, 0xafa00010, 0xafa00014, +0x8f860040, 0x3c040001, 0x24845c80, 0x24050200, +0x3c010001, 0xac326e80, 0xc002b3b, 0x2003821, +0x8f830040, 0x3c02f000, 0x621824, 0x3c026000, +0x1062000b, 0xa3a0003f, 0x240e0001, 0x3c040001, +0x24845c88, 0xa3ae003f, 0xafa00010, 0xafa00014, +0x8f860040, 0x24050300, 0xc002b3b, 0x2003821, +0x8f820240, 0x3c030001, 0x431025, 0xaf820240, +0xaf800048, 0x8f820048, 0x14400005, 0x0, +0xaf800048, 0x8f820048, 0x10400004, 0x0, +0xaf800048, 0x10000003, 0x2e02021, 0xaf80004c, +0x2e02021, 0x3c050001, 0xc002ba8, 0x34a540f8, +0x3402021, 0xc002ba8, 0x240505c8, 0x3c020001, +0x8c426ea8, 0x3c0d0001, 0x8dad6e88, 0x3c030001, +0x8c636e84, 0x3c080001, 0x8d086e90, 0x3c090001, +0x8d296eac, 0x3c0a0001, 0x8d4a6eb4, 0x3c0b0001, +0x8d6b6ec4, 0x3c0c0001, 0x8d8c6e98, 0x3c040001, +0x24845c94, 0x24050400, 0xaf42013c, 0x8f42013c, +0x24060001, 0x24070001, 0xaf400000, 0xaf4d0138, +0xaf430144, 0xaf480148, 0xaf49014c, 0xaf4a0150, +0xaf4b0154, 0xaf4c0158, 0x2442ff80, 0xaf420140, +0x24020001, 0xafa20010, 0xc002b3b, 0xafa00014, +0x8f420138, 0xafa20010, 0x8f42013c, 0xafa20014, +0x8f460144, 0x8f470148, 0x3c040001, 0x24845ca0, +0xc002b3b, 0x24050500, 0xafb70010, 0xafba0014, +0x8f46014c, 0x8f470150, 0x3c040001, 0x24845cac, +0xc002b3b, 0x24050600, 0x3c020001, 0x8c426e9c, +0x3603821, 0x3c060002, 0x24c69010, 0x2448ffff, +0x1061824, 0xe81024, 0x43102b, 0x10400006, +0x24050900, 0x3c040001, 0x24845cb8, 0xafa80010, +0xc002b3b, 0xafa00014, 0x8f82000c, 0xafa20010, +0x8f82003c, 0xafa20014, 0x8f860000, 0x8f870004, +0x3c040001, 0x24845cc4, 0xc002b3b, 0x24051000, +0x8c020220, 0x8c030224, 0x8c060218, 0x8c07021c, +0x3c040001, 0x24845ccc, 0x24051100, 0xafa20010, +0xc002b3b, 0xafa30014, 0xaf800054, 0xaf80011c, +0x8c020218, 0x30420002, 0x10400009, 0x0, +0x8c020220, 0x3c030002, 0x34630004, 0x431025, +0xaf42000c, 0x8c02021c, 0x10000008, 0x34420004, +0x8c020220, 0x3c030002, 0x34630006, 0x431025, +0xaf42000c, 0x8c02021c, 0x34420006, 0xaf420014, +0x8c020218, 0x30420010, 0x1040000a, 0x0, +0x8c02021c, 0x34420004, 0xaf420010, 0x8c020220, +0x3c03000a, 0x34630004, 0x431025, 0x10000009, +0xaf420008, 0x8c020220, 0x3c03000a, 0x34630006, +0x431025, 0xaf420008, 0x8c02021c, 0x34420006, +0xaf420010, 0x24020001, 0xaf8200a0, 0xaf8200b0, +0x8f830054, 0x8f820054, 0xaf8000d0, 0xaf8000c0, +0x10000002, 0x24630064, 0x8f820054, 0x621023, +0x2c420065, 0x1440fffc, 0x0, 0x8c040208, +0x8c05020c, 0x26e20028, 0xaee20020, 0x24020490, +0xaee20010, 0xaee40008, 0xaee5000c, 0x26e40008, +0x8c820000, 0x8c830004, 0xaf820090, 0xaf830094, +0x8c820018, 0xaf8200b4, 0x9482000a, 0xaf82009c, +0x8f420014, 0xaf8200b0, 0x8f8200b0, 0x30420004, +0x1440fffd, 0x0, 0x8f8200b0, 0x3c03ef00, +0x431024, 0x10400021, 0x0, 0x8f8200b4, +0xafa20010, 0x8f820090, 0x8f830094, 0x3c040001, +0x24845cd4, 0xafa30014, 0x8f8600b0, 0x8f87009c, +0x3c050001, 0xc002b3b, 0x34a5200d, 0x3c040001, +0x24845ce0, 0x240203c0, 0xafa20010, 0xafa00014, +0x8f860144, 0x3c070001, 0x24e75ce8, 0xc002b3b, +0x3405dead, 0x8f82011c, 0x34420002, 0xaf82011c, +0x8f820220, 0x34420004, 0xaf820220, 0x8f820140, +0x3c030001, 0x431025, 0xaf820140, 0x96e20472, +0x96e60452, 0x96e70462, 0xafa20010, 0x96e20482, +0x3c040001, 0x24845d14, 0x24051200, 0xc002b3b, +0xafa20014, 0x96f00452, 0x32020001, 0x10400002, +0xb021, 0x24160001, 0x32020002, 0x54400001, +0x36d60002, 0x32020008, 0x54400001, 0x36d60004, +0x32020010, 0x54400001, 0x36d60008, 0x32020020, +0x54400001, 0x36d60010, 0x32020040, 0x54400001, +0x36d60020, 0x32020080, 0x54400001, 0x36d60040, +0x96e60482, 0x30c20200, 0x54400001, 0x36d64000, +0x96e30472, 0x30620200, 0x10400003, 0x30620100, +0x10000003, 0x36d62000, 0x54400001, 0x36d61000, +0x96f00462, 0x32c24000, 0x14400004, 0x3207009b, +0x30c2009b, 0x14e20007, 0x240e0001, 0x32c22000, +0x1440000d, 0x32020001, 0x3062009b, 0x10e20009, +0x240e0001, 0x3c040001, 0x24845d20, 0x24051300, +0x2003821, 0xa3ae003f, 0xafa30010, 0xc002b3b, +0xafa00014, 0x32020001, 0x54400001, 0x36d60080, +0x32020002, 0x54400001, 0x36d60100, 0x32020008, +0x54400001, 0x36d60200, 0x32020010, 0x54400001, +0x36d60400, 0x32020080, 0x54400001, 0x36d60800, +0x8c020218, 0x30420200, 0x10400002, 0x3c020008, +0x2c2b025, 0x8c020218, 0x30420800, 0x10400002, +0x3c020080, 0x2c2b025, 0x8c020218, 0x30420400, +0x10400002, 0x3c020100, 0x2c2b025, 0x8c020218, +0x30420100, 0x10400002, 0x3c020200, 0x2c2b025, +0x8c020218, 0x30420080, 0x10400002, 0x3c020400, +0x2c2b025, 0x8c020218, 0x30422000, 0x10400002, +0x3c020010, 0x2c2b025, 0x8c020218, 0x30424000, +0x10400002, 0x3c020020, 0x2c2b025, 0x8c020218, +0x30421000, 0x10400002, 0x3c020040, 0x2c2b025, +0x8ee20498, 0x8ee3049c, 0xaf420160, 0xaf430164, +0x8ee204a0, 0x8ee304a4, 0xaf420168, 0xaf43016c, +0x8ee204a8, 0x8ee304ac, 0xaf420170, 0xaf430174, +0x8ee20428, 0x8ee3042c, 0xaf420178, 0xaf43017c, +0x8ee20448, 0x8ee3044c, 0xaf420180, 0xaf430184, +0x8ee20458, 0x8ee3045c, 0xaf420188, 0xaf43018c, +0x8ee20468, 0x8ee3046c, 0xaf420190, 0xaf430194, +0x8ee20478, 0x8ee3047c, 0xaf420198, 0xaf43019c, +0x8ee20488, 0x8ee3048c, 0xaf4201a0, 0xaf4301a4, +0x8ee204b0, 0x8ee304b4, 0x24040080, 0xaf4201a8, +0xaf4301ac, 0xc002ba8, 0x24050080, 0x8c02025c, +0x27440224, 0xaf4201f0, 0x8c020260, 0x24050200, +0x24060008, 0xc002bbf, 0xaf4201f8, 0x3c043b9a, +0x3484ca00, 0x3821, 0x24020006, 0x24030002, +0xaf4201f4, 0x240203e8, 0xaf430204, 0xaf430200, +0xaf4401fc, 0xaf420294, 0x24020001, 0xaf430290, +0xaf42029c, 0x3c030001, 0x671821, 0x90636cd8, +0x3471021, 0x24e70001, 0xa043022c, 0x2ce2000f, +0x1440fff8, 0x3471821, 0x24e70001, 0x3c080001, +0x350840f8, 0x8f820040, 0x3c040001, 0x24845d2c, +0x24051400, 0x21702, 0x24420030, 0xa062022c, +0x3471021, 0xa040022c, 0x8c070218, 0x2c03021, +0x240205c8, 0xafa20010, 0xc002b3b, 0xafa80014, +0x3c040001, 0x24845d38, 0x3c050000, 0x24a55c80, +0x24060010, 0x27b10030, 0x2203821, 0x27b30034, +0xc0017a3, 0xafb30010, 0x3c030001, 0x8c636cc8, +0x1060000a, 0x408021, 0x8fa30030, 0x2405ff00, +0x8fa20034, 0x246400ff, 0x852024, 0x831823, +0x431023, 0xafa20034, 0xafa40030, 0x3c040001, +0x24845d44, 0x3c050000, 0x24a54100, 0x24060108, +0x2203821, 0xc0017a3, 0xafb30010, 0x409021, +0x32c20003, 0x3c010001, 0xac326e80, 0x10400045, +0x2203821, 0x8f820050, 0x3c030010, 0x431024, +0x10400016, 0x0, 0x8c020218, 0x30420040, +0x1040000f, 0x24020001, 0x8f820050, 0x8c030218, +0x240e0001, 0x3c040001, 0x24845d50, 0xa3ae003f, +0xafa20010, 0xafa30014, 0x8f870040, 0x24051500, +0xc002b3b, 0x2c03021, 0x10000004, 0x0, +0x3c010001, 0x370821, 0xa02240f4, 0x3c040001, +0x24845d5c, 0x3c050001, 0x24a55b40, 0x3c060001, +0x24c65bac, 0xc53023, 0x8f420010, 0x27b30030, +0x2603821, 0x27b10034, 0x34420a00, 0xaf420010, +0xc0017a3, 0xafb10010, 0x3c040001, 0x24845d70, +0x3c050001, 0x24a5b714, 0x3c060001, 0x24c6ba90, +0xc53023, 0x2603821, 0xaf420108, 0xc0017a3, +0xafb10010, 0x3c040001, 0x24845d8c, 0x3c050001, +0x24a5be58, 0x3c060001, 0x24c6c900, 0xc53023, +0x2603821, 0x3c010001, 0xac226ef4, 0xc0017a3, +0xafb10010, 0x3c040001, 0x24845da4, 0x10000024, +0x24051600, 0x3c040001, 0x24845dac, 0x3c050001, +0x24a5a10c, 0x3c060001, 0x24c6a238, 0xc53023, +0xc0017a3, 0xafb30010, 0x3c040001, 0x24845dbc, +0x3c050001, 0x24a5b2b0, 0x3c060001, 0x24c6b70c, +0xc53023, 0x2203821, 0xaf420108, 0xc0017a3, +0xafb30010, 0x3c040001, 0x24845dd0, 0x3c050001, +0x24a5ba98, 0x3c060001, 0x24c6be50, 0xc53023, +0x2203821, 0x3c010001, 0xac226ef4, 0xc0017a3, +0xafb30010, 0x3c040001, 0x24845de4, 0x24051650, +0x2c03021, 0x3821, 0x3c010001, 0xac226ef8, +0xafa00010, 0xc002b3b, 0xafa00014, 0x32c20020, +0x10400021, 0x27a70030, 0x3c040001, 0x24845df0, +0x3c050001, 0x24a5b13c, 0x3c060001, 0x24c6b2a8, +0xc53023, 0x24022000, 0xaf42001c, 0x27a20034, +0xc0017a3, 0xafa20010, 0x21900, 0x31982, +0x3c040800, 0x641825, 0xae430028, 0x24030010, +0xaf43003c, 0x96e30450, 0xaf430040, 0x8f430040, +0x3c040001, 0x24845e04, 0xafa00014, 0xafa30010, +0x8f47001c, 0x24051660, 0x3c010001, 0xac226ef0, +0x10000025, 0x32c60020, 0x8ee20448, 0x8ee3044c, +0xaf43001c, 0x8f42001c, 0x2442e000, 0x2c422001, +0x1440000a, 0x240e0001, 0x3c040001, 0x24845e10, +0xa3ae003f, 0xafa00010, 0xafa00014, 0x8f46001c, +0x24051700, 0xc002b3b, 0x3821, 0x3c020000, +0x24425cbc, 0x21100, 0x21182, 0x3c030800, +0x431025, 0xae420028, 0x24020008, 0xaf42003c, +0x96e20450, 0xaf420040, 0x8f420040, 0x3c040001, +0x24845e1c, 0xafa00014, 0xafa20010, 0x8f47001c, +0x24051800, 0x32c60020, 0xc002b3b, 0x0, +0x3c050fff, 0x3c030001, 0x8c636ef4, 0x34a5ffff, +0x2403021, 0x3c020001, 0x8c426ef8, 0x3c040800, +0x651824, 0x31882, 0x641825, 0x451024, +0x21082, 0x441025, 0xacc20080, 0x32c20180, +0x10400056, 0xacc30020, 0x8f82005c, 0x3c030080, +0x431024, 0x1040000d, 0x0, 0x8f820050, +0xafa20010, 0x8f82005c, 0x240e0001, 0x3c040001, +0x24845e28, 0xa3ae003f, 0xafa20014, 0x8f870040, +0x24051900, 0xc002b3b, 0x2c03021, 0x8f820050, +0x3c030010, 0x431024, 0x10400016, 0x0, +0x8c020218, 0x30420040, 0x1040000f, 0x24020001, +0x8f820050, 0x8c030218, 0x240e0001, 0x3c040001, +0x24845d50, 0xa3ae003f, 0xafa20010, 0xafa30014, +0x8f870040, 0x24052000, 0xc002b3b, 0x2c03021, +0x10000004, 0x0, 0x3c010001, 0x370821, +0xa02240f4, 0x3c040001, 0x24845e34, 0x3c050001, +0x24a55ac0, 0x3c060001, 0x24c65b38, 0xc53023, +0x8f420008, 0x27b30030, 0x2603821, 0x27b10034, +0x34420e00, 0xaf420008, 0xc0017a3, 0xafb10010, +0x3c040001, 0x24845e4c, 0x3c050001, 0x24a5d8b4, +0x3c060001, 0x24c6e3c8, 0xc53023, 0x2603821, +0xaf42010c, 0xc0017a3, 0xafb10010, 0x3c040001, +0x24845e64, 0x3c050001, 0x24a5e9ac, 0x3c060001, +0x24c6f0f0, 0xc53023, 0x2603821, 0x3c010001, +0xac226f04, 0xc0017a3, 0xafb10010, 0x3c040001, +0x24845e7c, 0x10000027, 0x24052100, 0x3c040001, +0x24845e84, 0x3c050001, 0x24a59fc8, 0x3c060001, +0x24c6a104, 0xc53023, 0x27b10030, 0x2203821, +0x27b30034, 0xc0017a3, 0xafb30010, 0x3c040001, +0x24845e94, 0x3c050001, 0x24a5cad4, 0x3c060001, +0x24c6d8ac, 0xc53023, 0x2203821, 0xaf42010c, +0xc0017a3, 0xafb30010, 0x3c040001, 0x24845ea4, +0x3c050001, 0x24a5e84c, 0x3c060001, 0x24c6e9a4, +0xc53023, 0x2203821, 0x3c010001, 0xac226f04, +0xc0017a3, 0xafb30010, 0x3c040001, 0x24845eb8, +0x24052150, 0x2c03021, 0x3821, 0x3c010001, +0xac226f10, 0xafa00010, 0xc002b3b, 0xafa00014, +0x3c110fff, 0x3c030001, 0x8c636f04, 0x3631ffff, +0x2409821, 0x3c020001, 0x8c426f10, 0x3c0e0800, +0x711824, 0x31882, 0x6e1825, 0x511024, +0x21082, 0x4e1025, 0xae630038, 0xae620078, +0x8c020218, 0x30420040, 0x14400004, 0x24020001, +0x3c010001, 0x370821, 0xa02240f4, 0x3c040001, +0x24845ec4, 0x3c050001, 0x24a5e3d0, 0x3c060001, +0x24c6e52c, 0xc53023, 0x27be0030, 0x3c03821, +0x27b50034, 0xc0017a3, 0xafb50010, 0x3c010001, +0xac226efc, 0x511024, 0x21082, 0x3c0e0800, +0x4e1025, 0xae620050, 0x32c22000, 0x10400006, +0x3c03821, 0x3c020000, 0x24425cbc, 0x2221024, +0x1000000f, 0x21082, 0x3c040001, 0x24845ed8, +0x3c050001, 0x24a5e534, 0x3c060001, 0x24c6e6e4, +0xc53023, 0xc0017a3, 0xafb50010, 0x3c010001, +0xac226f14, 0x511024, 0x21082, 0x3c0e0800, +0x4e1025, 0xae620048, 0x32c24000, 0x10400005, +0x27a70030, 0x3c020000, 0x24425cbc, 0x1000000e, +0x21100, 0x3c040001, 0x24845ef0, 0x3c050001, +0x24a5e6ec, 0x3c060001, 0x24c6e844, 0xc53023, +0x27a20034, 0xc0017a3, 0xafa20010, 0x3c010001, +0xac226f08, 0x21100, 0x21182, 0x3c030800, +0x431025, 0xae420060, 0x3c040001, 0x24845f08, +0x3c050001, 0x24a58230, 0x3c060001, 0x24c68650, +0xc53023, 0x27b10030, 0x2203821, 0x27b30034, +0xc0017a3, 0xafb30010, 0x3c0e0fff, 0x35ceffff, +0x3c040001, 0x24845f14, 0x3c050000, 0x24a56468, +0x3c060000, 0x24c66588, 0xc53023, 0x2203821, +0x240f021, 0x3c010001, 0xac226edc, 0x4e1024, +0x21082, 0x3c150800, 0x551025, 0xafae0044, +0xafc200b8, 0xc0017a3, 0xafb30010, 0x3c040001, +0x24845f20, 0x3c050000, 0x24a56590, 0x3c060000, +0x24c66808, 0x8fae0044, 0xc53023, 0x2203821, +0x3c010001, 0xac226ed0, 0x4e1024, 0x21082, +0x551025, 0xafc200e8, 0xc0017a3, 0xafb30010, +0x3c040001, 0x24845f38, 0x3c050000, 0x24a56810, +0x3c060000, 0x24c66940, 0x8fae0044, 0xc53023, +0x2203821, 0x3c010001, 0xac226ec8, 0x4e1024, +0x21082, 0x551025, 0xafc200c0, 0xc0017a3, +0xafb30010, 0x3c040001, 0x24845f50, 0x3c050001, +0x24a5fad0, 0x3c060001, 0x24c6fba8, 0x8fae0044, +0xc53023, 0x2203821, 0x3c010001, 0xac226ed4, +0x4e1024, 0x21082, 0x551025, 0xafc200c8, +0xc0017a3, 0xafb30010, 0x3c040001, 0x24845f5c, +0x3c050001, 0x24a5c93c, 0x3c060001, 0x24c6ca20, +0xc53023, 0x2203821, 0xaf420110, 0xc0017a3, +0xafb30010, 0x3c040001, 0x24845f6c, 0x3c050001, +0x24a5c910, 0x3c060001, 0x24c6c934, 0xc53023, +0x2203821, 0xaf420124, 0xc0017a3, 0xafb30010, +0x3c040001, 0x24845f7c, 0x3c050001, 0x24a55a80, +0x3c060001, 0x24c65aac, 0xc53023, 0x2203821, +0xaf420120, 0xaf420114, 0xc0017a3, 0xafb30010, +0x3c040001, 0x24845f88, 0x3c050001, 0x24a5f298, +0x3c060001, 0x24c6f6b4, 0xc53023, 0x2203821, +0xaf420118, 0xc0017a3, 0xafb30010, 0x8fae0044, +0x3c010001, 0xac226f18, 0x4e1024, 0x21082, +0x551025, 0xc003fc3, 0xafc200d0, 0xc003c40, +0x0, 0xc0027a8, 0x0, 0xac000228, +0xac00022c, 0x96e20450, 0x2442ffff, 0xaf420038, +0x96e20460, 0xaf420080, 0x32c24000, 0x14400003, +0x0, 0x96e20480, 0xaf420084, 0x96e70490, +0x50e00001, 0x24070800, 0x24e2ffff, 0xaf420088, +0xaf42007c, 0x24020800, 0x10e2000f, 0x32c24000, +0x10400003, 0x24020400, 0x10e2000b, 0x0, +0x240e0001, 0x3c040001, 0x24845f98, 0xa3ae003f, +0x96e60490, 0x24052170, 0x2c03821, 0xafa00010, +0xc002b3b, 0xafa00014, 0x8f430138, 0x8f440138, +0x24020001, 0xa34205c2, 0xaf430094, 0xaf440098, +0xafa00010, 0xafa00014, 0x8f460080, 0x8f470084, +0x3c040001, 0x24845fa4, 0xc002b3b, 0x24052200, +0xc0024a4, 0x3c110800, 0x3c1433d8, 0x3694cb58, +0x3c020800, 0x34420080, 0x3c040001, 0x24845fb0, +0x3c050000, 0x24a55d00, 0x3c060000, 0x24c65d1c, +0xc53023, 0x27a70030, 0xaf820060, 0x2402ffff, +0xaf820064, 0x27a20034, 0xc0017a3, 0xafa20010, +0x3c010001, 0xac226eb8, 0x21100, 0x21182, +0x511025, 0xc0018fc, 0xae420000, 0x8f820240, +0x3c030001, 0x431025, 0xaf820240, 0x3c020000, +0x24424034, 0xaf820244, 0xaf800240, 0x8f820060, +0x511024, 0x14400005, 0x3c030800, 0x8f820060, +0x431024, 0x1040fffd, 0x0, 0xc003c4d, +0x8821, 0x3c020100, 0xafa20020, 0x8f530018, +0x240200ff, 0x56620001, 0x26710001, 0x8c020228, +0x1622000e, 0x1330c0, 0x8f42033c, 0x24420001, +0xaf42033c, 0x8f42033c, 0x8c020228, 0x3c040001, +0x24845c24, 0x3c050009, 0xafa00014, 0xafa20010, +0x8fa60020, 0x1000003f, 0x34a50100, 0xd71021, +0x8fa30020, 0x8fa40024, 0xac4304c0, 0xac4404c4, +0xc01821, 0x8f440178, 0x8f45017c, 0x1021, +0x24070004, 0xafa70010, 0xafb10014, 0x8f48000c, +0x24c604c0, 0x2e63021, 0xafa80018, 0x8f48010c, +0x24070008, 0xa32821, 0xa3482b, 0x822021, +0x100f809, 0x892021, 0x1440000b, 0x24070008, +0x8f820120, 0xafa20010, 0x8f820124, 0x3c040001, +0x24845c2c, 0x3c050009, 0xafa20014, 0x8fa60020, +0x1000001c, 0x34a50200, 0x8f440160, 0x8f450164, +0x8f43000c, 0xaf510018, 0x8f860120, 0x24020010, +0xafa20010, 0xafb10014, 0xafa30018, 0x8f42010c, +0x40f809, 0x24c6001c, 0x14400010, 0x0, +0x8f420340, 0x24420001, 0xaf420340, 0x8f420340, +0x8f820120, 0xafa20010, 0x8f820124, 0x3c040001, +0x24845c34, 0x3c050009, 0xafa20014, 0x8fa60020, +0x34a50300, 0xc002b3b, 0x2603821, 0x8f4202e4, +0x24420001, 0xaf4202e4, 0x8f4202e4, 0x93a2003f, +0x10400069, 0x3c020700, 0x34423000, 0xafa20028, +0x8f530018, 0x240200ff, 0x12620002, 0x8821, +0x26710001, 0x8c020228, 0x1622000e, 0x1330c0, +0x8f42033c, 0x24420001, 0xaf42033c, 0x8f42033c, +0x8c020228, 0x3c040001, 0x24845c24, 0x3c050009, +0xafa00014, 0xafa20010, 0x8fa60028, 0x1000003f, +0x34a50100, 0xd71021, 0x8fa30028, 0x8fa4002c, +0xac4304c0, 0xac4404c4, 0xc01821, 0x8f440178, +0x8f45017c, 0x1021, 0x24070004, 0xafa70010, +0xafb10014, 0x8f48000c, 0x24c604c0, 0x2e63021, +0xafa80018, 0x8f48010c, 0x24070008, 0xa32821, +0xa3482b, 0x822021, 0x100f809, 0x892021, +0x1440000b, 0x24070008, 0x8f820120, 0xafa20010, +0x8f820124, 0x3c040001, 0x24845c2c, 0x3c050009, +0xafa20014, 0x8fa60028, 0x1000001c, 0x34a50200, +0x8f440160, 0x8f450164, 0x8f43000c, 0xaf510018, +0x8f860120, 0x24020010, 0xafa20010, 0xafb10014, +0xafa30018, 0x8f42010c, 0x40f809, 0x24c6001c, +0x14400010, 0x0, 0x8f420340, 0x24420001, +0xaf420340, 0x8f420340, 0x8f820120, 0xafa20010, +0x8f820124, 0x3c040001, 0x24845c34, 0x3c050009, +0xafa20014, 0x8fa60028, 0x34a50300, 0xc002b3b, +0x2603821, 0x8f4202f0, 0x24420001, 0xaf4202f0, +0x8f4202f0, 0x3c040001, 0x24845fc0, 0xafa00010, +0xafa00014, 0x8fa60028, 0x24052300, 0xc002b3b, +0x3821, 0x10000004, 0x0, 0x8c020264, +0x10400005, 0x0, 0x8f8200a0, 0x30420004, +0x1440fffa, 0x0, 0x8f820044, 0x34420004, +0xaf820044, 0x8f420308, 0x24420001, 0xaf420308, +0x8f420308, 0x8f8200d8, 0x8f8300d4, 0x431023, +0x2442ff80, 0xaf420090, 0x8f420090, 0x2842ff81, +0x10400006, 0x24020001, 0x8f420090, 0x8f430144, +0x431021, 0xaf420090, 0x24020001, 0xaf42008c, +0x32c20008, 0x10400006, 0x0, 0x8f820214, +0x3c038100, 0x3042ffff, 0x431025, 0xaf820214, +0x3c030001, 0x8c636d94, 0x30620002, 0x10400009, +0x30620001, 0x3c040001, 0x24845fcc, 0x3c050000, +0x24a56d50, 0x3c060000, 0x24c671c8, 0x10000012, +0xc53023, 0x10400009, 0x0, 0x3c040001, +0x24845fdc, 0x3c050000, 0x24a571d0, 0x3c060000, +0x24c67678, 0x10000008, 0xc53023, 0x3c040001, +0x24845fec, 0x3c050000, 0x24a56948, 0x3c060000, +0x24c66d48, 0xc53023, 0x27a70030, 0x27a20034, +0xc0017a3, 0xafa20010, 0x3c010001, 0xac226ecc, +0x3c020001, 0x8c426ecc, 0x3c030800, 0x21100, +0x21182, 0x431025, 0xae420040, 0x8f8200a0, +0xafa20010, 0x8f8200b0, 0xafa20014, 0x8f86005c, +0x8f87011c, 0x3c040001, 0x24845ffc, 0x3c010001, +0xac366ea4, 0x3c010001, 0xac206e94, 0x3c010001, +0xac3c6e8c, 0x3c010001, 0xac3b6ebc, 0x3c010001, +0xac376ec0, 0x3c010001, 0xac3a6ea0, 0xc002b3b, +0x24052400, 0x8f820200, 0xafa20010, 0x8f820220, +0xafa20014, 0x8f860044, 0x8f870050, 0x3c040001, +0x24846008, 0xc002b3b, 0x24052500, 0x8f830060, +0x74100b, 0x242000a, 0x200f821, 0x0, +0xd, 0x8fbf0060, 0x8fbe005c, 0x8fb50058, +0x8fb30054, 0x8fb20050, 0x8fb1004c, 0x8fb00048, +0x3e00008, 0x27bd0068, 0x27bdffe0, 0x3c040001, +0x24846014, 0x24052600, 0x3021, 0x3821, +0xafbf0018, 0xafa00010, 0xc002b3b, 0xafa00014, +0x8fbf0018, 0x3e00008, 0x27bd0020, 0x3e00008, +0x0, 0x3e00008, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x3e00008, 0x0, 0x3e00008, 0x0, +0x27bdfde0, 0x27a50018, 0x3c04dead, 0x3484beef, +0xafbf0218, 0x8f820150, 0x3c03001f, 0x3463ffff, +0xafa40018, 0xa22823, 0xa32824, 0x8ca20000, +0x1044000a, 0x0, 0xafa50010, 0x8ca20000, +0xafa20014, 0x8f860150, 0x8f870250, 0x3c040001, +0x2484601c, 0xc002b3b, 0x24052700, 0x8fbf0218, +0x3e00008, 0x27bd0220, 0x27bdffe0, 0x3c06abba, +0x34c6babe, 0xafb00018, 0x3c100004, 0x3c07007f, +0x34e7ffff, 0xafbf001c, 0x102840, 0x8e040000, +0x8ca30000, 0xaca00000, 0xae060000, 0x8ca20000, +0xaca30000, 0x10460005, 0xae040000, 0xa08021, +0xf0102b, 0x1040fff5, 0x102840, 0x3c040001, +0x24846028, 0x24052800, 0x2003021, 0x3821, +0xafa00010, 0xc002b3b, 0xafa00014, 0x2001021, +0x8fbf001c, 0x8fb00018, 0x3e00008, 0x27bd0020, +0x8c020224, 0x3047003f, 0x10e00010, 0x803021, +0x2821, 0x24030020, 0xe31024, 0x10400002, +0x63042, 0xa62821, 0x31842, 0x1460fffb, +0xe31024, 0x2402f000, 0xa22824, 0x3402ffff, +0x45102b, 0x14400003, 0x3c020001, 0x10000008, +0x3c020001, 0x3442ffff, 0x851823, 0x43102b, +0x14400003, 0xa01021, 0x3c02fffe, 0x821021, +0x3e00008, 0x0, 0x27bdffd0, 0xafb50028, +0x8fb50040, 0xafb20020, 0xa09021, 0xafb1001c, +0x24c60003, 0xafbf002c, 0xafb30024, 0xafb00018, +0x8ea20000, 0x2403fffc, 0xc38024, 0x50102b, +0x1440001b, 0xe08821, 0x8e330000, 0xafb00010, +0x8ea20000, 0xafa20014, 0x8e270000, 0x24053000, +0xc002b3b, 0x2403021, 0x8e230000, 0x702021, +0x64102b, 0x10400007, 0x2402821, 0x8ca20000, +0xac620000, 0x24630004, 0x64102b, 0x1440fffb, +0x24a50004, 0x8ea20000, 0x501023, 0xaea20000, +0x8e220000, 0x501021, 0x1000000b, 0xae220000, +0x2402002d, 0xa0820000, 0xafb00010, 0x8ea20000, +0x2409821, 0xafa20014, 0x8e270000, 0x24053100, +0xc002b3b, 0x2603021, 0x2601021, 0x8fbf002c, +0x8fb50028, 0x8fb30024, 0x8fb20020, 0x8fb1001c, +0x8fb00018, 0x3e00008, 0x27bd0030, 0x27bdffe8, +0x3c1cc000, 0x3c05fffe, 0x3c030001, 0x8c636e84, +0x3c040001, 0x8c846e90, 0x34a5bf08, 0x24021ffc, +0x3c010001, 0xac226cd0, 0x3c0200c0, 0x3c010001, +0xac226cd4, 0x3c020020, 0xafbf0010, 0x3c0100c0, +0xac201ffc, 0x431023, 0x441023, 0x245bb000, +0x365b821, 0x3c1d0001, 0x8fbd6ccc, 0x3a0f021, +0x3c0400c0, 0x34840200, 0x3c1a00c0, 0x3c0300c0, +0x346307c8, 0x24021dfc, 0x3c010001, 0xac226cd0, +0x24021834, 0x3c010001, 0xac246cd4, 0x3c010001, +0xac226cd0, 0x3c010001, 0xac236cd4, 0xc00180d, +0x375a0200, 0x8fbf0010, 0x3e00008, 0x27bd0018, +0x27bdffc8, 0x3c040001, 0x24846034, 0x24053200, +0x3c020001, 0x8c426cd0, 0x3c030001, 0x8c636cd4, +0x3021, 0x3603821, 0xafbf0030, 0xafb3002c, +0xafb20028, 0xafb10024, 0xafb00020, 0xafa2001c, +0xafa30018, 0xafb70010, 0xc002b3b, 0xafba0014, +0xc001916, 0x0, 0x8f820240, 0x34420004, +0xaf820240, 0x24020001, 0xaf420000, 0x3c020001, +0x571021, 0x904240f4, 0x10400092, 0x2403fffc, +0x3c100001, 0x2610ac73, 0x3c120001, 0x2652a84c, +0x2121023, 0x438024, 0x8fa3001c, 0x3c040001, +0x24846040, 0x70102b, 0x1440001a, 0x27b30018, +0x8fb10018, 0x24053000, 0x2403021, 0xafb00010, +0xafa30014, 0xc002b3b, 0x2203821, 0x8fa30018, +0x702021, 0x64102b, 0x10400007, 0x2403021, +0x8cc20000, 0xac620000, 0x24630004, 0x64102b, +0x1440fffb, 0x24c60004, 0x8fa2001c, 0x501023, +0xafa2001c, 0x8e620000, 0x501021, 0x1000000a, +0xae620000, 0x2408821, 0x24053100, 0xafb00010, +0xafa30014, 0x8fa70018, 0x2203021, 0x2402002d, +0xc002b3b, 0xa0820000, 0x24070020, 0x8fa3001c, +0x3c040001, 0x2484605c, 0x24120020, 0x3c010001, +0xac316eb0, 0x2c620020, 0x1440001d, 0x27b10018, +0x8fb00018, 0x24053000, 0x3c060001, 0x24c66f50, +0xafa70010, 0xafa30014, 0xc002b3b, 0x2003821, +0x8fa30018, 0x3c040001, 0x24846f50, 0x24650020, +0x65102b, 0x10400007, 0x0, 0x8c820000, +0xac620000, 0x24630004, 0x65102b, 0x1440fffb, +0x24840004, 0x8fa2001c, 0x521023, 0xafa2001c, +0x8e220000, 0x521021, 0x1000000b, 0xae220000, +0x3c100001, 0x26106f50, 0x24053100, 0xafa70010, +0xafa30014, 0x8fa70018, 0x2003021, 0x2402002d, +0xc002b3b, 0xa0820000, 0x24070020, 0x3c040001, +0x24846070, 0x8fa3001c, 0x24120020, 0x3c010001, +0xac306ee4, 0x2c620020, 0x1440001d, 0x27b10018, +0x8fb00018, 0x24053000, 0x3c060001, 0x24c66f70, +0xafa70010, 0xafa30014, 0xc002b3b, 0x2003821, +0x8fa30018, 0x3c040001, 0x24846f70, 0x24650020, +0x65102b, 0x10400007, 0x0, 0x8c820000, +0xac620000, 0x24630004, 0x65102b, 0x1440fffb, +0x24840004, 0x8fa2001c, 0x521023, 0xafa2001c, +0x8e220000, 0x521021, 0x1000000b, 0xae220000, +0x3c100001, 0x26106f70, 0x24053100, 0xafa70010, +0xafa30014, 0x8fa70018, 0x2003021, 0x2402002d, +0xc002b3b, 0xa0820000, 0x3c010001, 0x10000031, +0xac306ee0, 0x3c100001, 0x2610821f, 0x3c120001, +0x2652809c, 0x2121023, 0x438024, 0x8fa3001c, +0x3c040001, 0x24846084, 0x70102b, 0x1440001a, +0x27b30018, 0x8fb10018, 0x24053000, 0x2403021, +0xafb00010, 0xafa30014, 0xc002b3b, 0x2203821, +0x8fa30018, 0x702021, 0x64102b, 0x10400007, +0x2403021, 0x8cc20000, 0xac620000, 0x24630004, +0x64102b, 0x1440fffb, 0x24c60004, 0x8fa2001c, +0x501023, 0xafa2001c, 0x8e620000, 0x501021, +0x1000000a, 0xae620000, 0x2408821, 0x24053100, +0xafb00010, 0xafa30014, 0x8fa70018, 0x2203021, +0x2402002d, 0xc002b3b, 0xa0820000, 0x3c010001, +0xac316eb0, 0x3c030001, 0x8c636eb0, 0x24020400, +0x60f809, 0xaf820070, 0x8fbf0030, 0x8fb3002c, +0x8fb20028, 0x8fb10024, 0x8fb00020, 0x3e00008, +0x27bd0038, 0x0, 0x0, 0x8f820040, +0x3c03f000, 0x431024, 0x3c036000, 0x14430006, +0x0, 0x8f820050, 0x2403ff80, 0x431024, +0x34420055, 0xaf820050, 0x8f820054, 0x244203e8, +0xaf820058, 0x240201f4, 0xaf4200e0, 0x24020004, +0xaf4200e8, 0x24020002, 0xaf4001b0, 0xaf4000e4, +0xaf4200dc, 0xaf4000d8, 0xaf4000d4, 0x3e00008, +0xaf4000d0, 0x8f820054, 0x24420005, 0x3e00008, +0xaf820078, 0x27bdffe8, 0xafbf0010, 0x8f820054, +0x244203e8, 0xaf820058, 0x3c020800, 0x2c21024, +0x10400004, 0x3c02f7ff, 0x3442ffff, 0x2c2b024, +0x36940040, 0x3c020001, 0x8c426da8, 0x10400017, +0x3c020200, 0x3c030001, 0x8c636f1c, 0x10600016, +0x282a025, 0x3c020001, 0x8c426e44, 0x14400012, +0x3c020200, 0x3c020001, 0x8c426d94, 0x30420003, +0x1440000d, 0x3c020200, 0x8f830224, 0x3c020002, +0x8c428fec, 0x10620008, 0x3c020200, 0xc003daf, +0x0, 0x10000004, 0x3c020200, 0xc004196, +0x0, 0x3c020200, 0x2c21024, 0x10400003, +0x0, 0xc001f4b, 0x0, 0x8f4200d8, +0x8f4300dc, 0x24420001, 0xaf4200d8, 0x43102b, +0x14400003, 0x0, 0xaf4000d8, 0x36940080, +0x8c030238, 0x1060000c, 0x0, 0x8f4201b0, +0x244203e8, 0xaf4201b0, 0x43102b, 0x14400006, +0x0, 0x934205c5, 0x14400003, 0x0, +0xc001da0, 0x0, 0x8fbf0010, 0x3e00008, +0x27bd0018, 0x3e00008, 0x0, 0x27bdffd8, +0xafbf0020, 0x8f43002c, 0x8f420038, 0x10620059, +0x0, 0x3c020001, 0x571021, 0x904240f0, +0x10400026, 0x24070008, 0x8f440170, 0x8f450174, +0x8f48000c, 0x8f860120, 0x24020020, 0xafa20010, +0xafa30014, 0xafa80018, 0x8f42010c, 0x40f809, +0x24c6001c, 0x14400011, 0x24020001, 0x3c010001, +0x370821, 0xa02240f0, 0x8f820124, 0xafa20010, +0x8f820128, 0x3c040001, 0x24846128, 0xafa20014, +0x8f46002c, 0x8f870120, 0x3c050009, 0xc002b3b, +0x34a50900, 0x1000005c, 0x0, 0x8f420300, +0x24420001, 0xaf420300, 0x8f420300, 0x8f42002c, +0xa34005c1, 0x10000027, 0xaf420038, 0x8f440170, +0x8f450174, 0x8f43002c, 0x8f48000c, 0x8f860120, +0x24020080, 0xafa20010, 0xafa30014, 0xafa80018, +0x8f42010c, 0x40f809, 0x24c6001c, 0x14400011, +0x24020001, 0x3c010001, 0x370821, 0xa02240f1, +0x8f820124, 0xafa20010, 0x8f820128, 0x3c040001, +0x24846134, 0xafa20014, 0x8f46002c, 0x8f870120, +0x3c050009, 0xc002b3b, 0x34a51100, 0x10000036, +0x0, 0x8f420300, 0x8f43002c, 0x24420001, +0xaf420300, 0x8f420300, 0x24020001, 0xa34205c1, +0xaf430038, 0x3c010001, 0x370821, 0xa02040f1, +0x3c010001, 0x370821, 0xa02040f0, 0x10000026, +0xaf400034, 0x934205c1, 0x1040001d, 0x0, +0xa34005c1, 0x8f820040, 0x30420001, 0x14400008, +0x2021, 0x8c030104, 0x24020001, 0x50620005, +0x24040001, 0x8c020264, 0x10400003, 0x801021, +0x24040001, 0x801021, 0x10400006, 0x0, +0x8f42030c, 0x24420001, 0xaf42030c, 0x10000008, +0x8f42030c, 0x8f820044, 0x34420004, 0xaf820044, +0x8f420308, 0x24420001, 0xaf420308, 0x8f420308, +0x3c010001, 0x370821, 0xa02040f0, 0x3c010001, +0x370821, 0xa02040f1, 0x8f420000, 0x10400007, +0x0, 0xaf80004c, 0x8f82004c, 0x1040fffd, +0x0, 0x10000005, 0x0, 0xaf800048, +0x8f820048, 0x1040fffd, 0x0, 0x8f820060, +0x3c03ff7f, 0x3463ffff, 0x431024, 0xaf820060, +0x8f420000, 0x10400003, 0x0, 0x10000002, +0xaf80004c, 0xaf800048, 0x8fbf0020, 0x3e00008, +0x27bd0028, 0x3e00008, 0x0, 0x27bdffd8, +0xafbf0020, 0x8f430044, 0x8f42007c, 0x10620029, +0x24070008, 0x8f440168, 0x8f45016c, 0x8f48000c, +0x8f860120, 0x24020040, 0xafa20010, 0xafa30014, +0xafa80018, 0x8f42010c, 0x40f809, 0x24c6001c, +0x14400011, 0x24020001, 0x3c010001, 0x370821, +0xa02240f2, 0x8f820124, 0xafa20010, 0x8f820128, +0x3c040001, 0x2484613c, 0xafa20014, 0x8f460044, +0x8f870120, 0x3c050009, 0xc002b3b, 0x34a51300, +0x1000000f, 0x0, 0x8f420304, 0x24420001, +0xaf420304, 0x8f420304, 0x8f420044, 0xaf42007c, +0x3c010001, 0x370821, 0xa02040f2, 0x10000004, +0xaf400078, 0x3c010001, 0x370821, 0xa02040f2, +0x8f420000, 0x10400007, 0x0, 0xaf80004c, +0x8f82004c, 0x1040fffd, 0x0, 0x10000005, +0x0, 0xaf800048, 0x8f820048, 0x1040fffd, +0x0, 0x8f820060, 0x3c03feff, 0x3463ffff, +0x431024, 0xaf820060, 0x8f420000, 0x10400003, +0x0, 0x10000002, 0xaf80004c, 0xaf800048, +0x8fbf0020, 0x3e00008, 0x27bd0028, 0x3e00008, +0x0, 0x3c020001, 0x8c426da8, 0x27bdffa8, +0xafbf0050, 0xafbe004c, 0xafb50048, 0xafb30044, +0xafb20040, 0xafb1003c, 0xafb00038, 0x104000d5, +0x8f900044, 0x8f4200d0, 0x24430001, 0x2842000b, +0x144000e4, 0xaf4300d0, 0x8f420004, 0x30420002, +0x1440009c, 0xaf4000d0, 0x8f420004, 0x3c030001, +0x8c636d98, 0x34420002, 0xaf420004, 0x24020001, +0x14620003, 0x3c020600, 0x10000002, 0x34423000, +0x34421000, 0xafa20020, 0x8f4a0018, 0xafaa0034, +0x27aa0020, 0xafaa002c, 0x8faa0034, 0x240200ff, +0x11420002, 0x1821, 0x25430001, 0x8c020228, +0x609821, 0x1662000e, 0x3c050009, 0x8f42033c, +0x24420001, 0xaf42033c, 0x8f42033c, 0x8c020228, +0x8fa70034, 0x3c040001, 0x2484610c, 0xafa00014, +0xafa20010, 0x8fa60020, 0x10000070, 0x34a50500, +0x8faa0034, 0xa38c0, 0xf71021, 0x8fa30020, +0x8fa40024, 0xac4304c0, 0xac4404c4, 0x8f830054, +0x8f820054, 0x247103e8, 0x2221023, 0x2c4203e9, +0x1040001b, 0xa821, 0xe09021, 0x265e04c0, +0x8f440178, 0x8f45017c, 0x2401821, 0x240a0004, +0xafaa0010, 0xafb30014, 0x8f48000c, 0x1021, +0x2fe3021, 0xafa80018, 0x8f48010c, 0x24070008, +0xa32821, 0xa3482b, 0x822021, 0x100f809, +0x892021, 0x54400006, 0x24150001, 0x8f820054, +0x2221023, 0x2c4203e9, 0x1440ffe9, 0x0, +0x32a200ff, 0x54400018, 0xaf530018, 0x8f420378, +0x24420001, 0xaf420378, 0x8f420378, 0x8f820120, +0x8faa002c, 0x8fa70034, 0xafa20010, 0x8f820124, +0x3c040001, 0x24846118, 0xafa20014, 0x8d460000, +0x3c050009, 0x10000035, 0x34a50600, 0x8f420308, +0x24150001, 0x24420001, 0xaf420308, 0x8f420308, +0x1000001e, 0x32a200ff, 0x8f830054, 0x8f820054, +0x247103e8, 0x2221023, 0x2c4203e9, 0x10400016, +0xa821, 0x3c1e0020, 0x24120010, 0x8f42000c, +0x8f440160, 0x8f450164, 0x8f860120, 0xafb20010, +0xafb30014, 0x5e1025, 0xafa20018, 0x8f42010c, +0x24070008, 0x40f809, 0x24c6001c, 0x1440ffe3, +0x0, 0x8f820054, 0x2221023, 0x2c4203e9, +0x1440ffee, 0x0, 0x32a200ff, 0x14400011, +0x3c050009, 0x8f420378, 0x24420001, 0xaf420378, +0x8f420378, 0x8f820120, 0x8faa002c, 0x8fa70034, +0xafa20010, 0x8f820124, 0x3c040001, 0x24846120, +0xafa20014, 0x8d460000, 0x34a50700, 0xc002b3b, +0x0, 0x8f4202ec, 0x24420001, 0xaf4202ec, +0x8f4202ec, 0x8f420004, 0x30420001, 0x50400029, +0x36100040, 0x3c020400, 0x2c21024, 0x10400013, +0x2404ffdf, 0x8f420250, 0x8f430254, 0x8f4401b4, +0x14640006, 0x36100040, 0x8f420270, 0x8f430274, +0x8f4401b8, 0x10640007, 0x2402ffdf, 0x8f420250, +0x8f430254, 0x8f440270, 0x8f450274, 0x10000012, +0x3a100020, 0x1000002b, 0x2028024, 0x8f420250, +0x8f430254, 0x8f4501b4, 0x14650006, 0x2048024, +0x8f420270, 0x8f430274, 0x8f4401b8, 0x50640021, +0x36100040, 0x8f420250, 0x8f430254, 0x8f440270, +0x8f450274, 0x3a100040, 0xaf4301b4, 0x10000019, +0xaf4501b8, 0x8f4200d4, 0x24430001, 0x10000011, +0x28420033, 0x8f420004, 0x30420001, 0x10400009, +0x3c020400, 0x2c21024, 0x10400004, 0x2402ffdf, +0x2028024, 0x1000000b, 0x36100040, 0x10000009, +0x36100060, 0x8f4200d4, 0x36100040, 0x24430001, +0x284201f5, 0x14400003, 0xaf4300d4, 0xaf4000d4, +0x3a100020, 0xaf900044, 0x2402ff7f, 0x282a024, +0x8fbf0050, 0x8fbe004c, 0x8fb50048, 0x8fb30044, +0x8fb20040, 0x8fb1003c, 0x8fb00038, 0x3e00008, +0x27bd0058, 0x3e00008, 0x0, 0x3c020001, +0x8c426da8, 0x27bdffb0, 0xafbf0048, 0xafbe0044, +0xafb50040, 0xafb3003c, 0xafb20038, 0xafb10034, +0x104000c7, 0xafb00030, 0x8f4200d0, 0x24430001, +0x2842000b, 0x144000da, 0xaf4300d0, 0x8f420004, +0x30420002, 0x14400097, 0xaf4000d0, 0x8f420004, +0x3c030001, 0x8c636d98, 0x34420002, 0xaf420004, +0x24020001, 0x14620003, 0x3c020600, 0x10000002, +0x34423000, 0x34421000, 0xafa20020, 0x1821, +0x8f5e0018, 0x27aa0020, 0x240200ff, 0x13c20002, +0xafaa002c, 0x27c30001, 0x8c020228, 0x609021, +0x1642000e, 0x1e38c0, 0x8f42033c, 0x24420001, +0xaf42033c, 0x8f42033c, 0x8c020228, 0x3c040001, +0x2484610c, 0x3c050009, 0xafa00014, 0xafa20010, +0x8fa60020, 0x1000006d, 0x34a50500, 0xf71021, +0x8fa30020, 0x8fa40024, 0xac4304c0, 0xac4404c4, +0x8f830054, 0x8f820054, 0x247003e8, 0x2021023, +0x2c4203e9, 0x1040001b, 0x9821, 0xe08821, +0x263504c0, 0x8f440178, 0x8f45017c, 0x2201821, +0x240a0004, 0xafaa0010, 0xafb20014, 0x8f48000c, +0x1021, 0x2f53021, 0xafa80018, 0x8f48010c, +0x24070008, 0xa32821, 0xa3482b, 0x822021, +0x100f809, 0x892021, 0x54400006, 0x24130001, +0x8f820054, 0x2021023, 0x2c4203e9, 0x1440ffe9, +0x0, 0x326200ff, 0x54400017, 0xaf520018, +0x8f420378, 0x24420001, 0xaf420378, 0x8f420378, +0x8f820120, 0x8faa002c, 0xafa20010, 0x8f820124, +0x3c040001, 0x24846118, 0x3c050009, 0xafa20014, +0x8d460000, 0x10000035, 0x34a50600, 0x8f420308, +0x24130001, 0x24420001, 0xaf420308, 0x8f420308, +0x1000001e, 0x326200ff, 0x8f830054, 0x8f820054, +0x247003e8, 0x2021023, 0x2c4203e9, 0x10400016, +0x9821, 0x3c150020, 0x24110010, 0x8f42000c, +0x8f440160, 0x8f450164, 0x8f860120, 0xafb10010, +0xafb20014, 0x551025, 0xafa20018, 0x8f42010c, +0x24070008, 0x40f809, 0x24c6001c, 0x1440ffe3, +0x0, 0x8f820054, 0x2021023, 0x2c4203e9, +0x1440ffee, 0x0, 0x326200ff, 0x14400011, +0x0, 0x8f420378, 0x24420001, 0xaf420378, +0x8f420378, 0x8f820120, 0x8faa002c, 0xafa20010, +0x8f820124, 0x3c040001, 0x24846120, 0x3c050009, +0xafa20014, 0x8d460000, 0x34a50700, 0xc002b3b, +0x3c03821, 0x8f4202ec, 0x24420001, 0xaf4202ec, +0x8f4202ec, 0x8f420004, 0x30420001, 0x10400018, +0x24040001, 0x8f420250, 0x8f430254, 0x8f4501b4, +0x3c010001, 0x14650006, 0xa0246cf1, 0x8f420270, +0x8f430274, 0x8f4401b8, 0x10640021, 0x0, +0x8f420250, 0x8f430254, 0x3c040001, 0x90846cf0, +0x8f460270, 0x8f470274, 0x38840001, 0xaf4301b4, +0xaf4701b8, 0x3c010001, 0x10000025, 0xa0246cf0, +0x8f4200d4, 0x3c010001, 0xa0206cf0, 0x24430001, +0x28420033, 0x1440001e, 0xaf4300d4, 0x3c020001, +0x90426cf1, 0xaf4000d4, 0x10000017, 0x38420001, +0x8f420004, 0x30420001, 0x10400008, 0x0, +0xc00565a, 0x2021, 0x3c010001, 0xa0206cf1, +0x3c010001, 0x1000000e, 0xa0206cf0, 0x8f4200d4, +0x3c010001, 0xa0206cf0, 0x24430001, 0x284201f5, +0x14400007, 0xaf4300d4, 0x3c020001, 0x90426cf1, +0xaf4000d4, 0x421026, 0x3c010001, 0xa0226cf1, +0x3c030001, 0x8c636d98, 0x24020002, 0x1462000c, +0x3c030002, 0x3c030001, 0x90636cf1, 0x24020001, +0x5462001f, 0x2021, 0x3c020001, 0x90426cf0, +0x1443001b, 0x24040005, 0x10000019, 0x24040006, +0x3c020002, 0x8c428ff4, 0x431024, 0x1040000b, +0x24020001, 0x3c030001, 0x90636cf1, 0x54620010, +0x2021, 0x3c020001, 0x90426cf0, 0x1443000c, +0x24040003, 0x1000000a, 0x24040004, 0x3c030001, +0x90636cf1, 0x14620006, 0x2021, 0x3c020001, +0x90426cf0, 0x24040001, 0x50440001, 0x24040002, +0xc00565a, 0x0, 0x2402ff7f, 0x282a024, +0x8fbf0048, 0x8fbe0044, 0x8fb50040, 0x8fb3003c, +0x8fb20038, 0x8fb10034, 0x8fb00030, 0x3e00008, +0x27bd0050, 0x3e00008, 0x0, 0x3c020001, +0x8c426da8, 0x27bdffb0, 0xafbf0048, 0xafbe0044, +0xafb50040, 0xafb3003c, 0xafb20038, 0xafb10034, +0x104000de, 0xafb00030, 0x8f4200d0, 0x3c040001, +0x8c846d98, 0x24430001, 0x2842000b, 0xaf4400e8, +0x144000fe, 0xaf4300d0, 0x8f420004, 0x30420002, +0x14400095, 0xaf4000d0, 0x8f420004, 0x34420002, +0xaf420004, 0x24020001, 0x14820003, 0x3c020600, +0x10000002, 0x34423000, 0x34421000, 0xafa20020, +0x1821, 0x8f5e0018, 0x27aa0020, 0x240200ff, +0x13c20002, 0xafaa002c, 0x27c30001, 0x8c020228, +0x609021, 0x1642000e, 0x1e38c0, 0x8f42033c, +0x24420001, 0xaf42033c, 0x8f42033c, 0x8c020228, +0x3c040001, 0x2484610c, 0x3c050009, 0xafa00014, +0xafa20010, 0x8fa60020, 0x1000006d, 0x34a50500, +0xf71021, 0x8fa30020, 0x8fa40024, 0xac4304c0, +0xac4404c4, 0x8f830054, 0x8f820054, 0x247003e8, +0x2021023, 0x2c4203e9, 0x1040001b, 0x9821, +0xe08821, 0x263504c0, 0x8f440178, 0x8f45017c, +0x2201821, 0x240a0004, 0xafaa0010, 0xafb20014, +0x8f48000c, 0x1021, 0x2f53021, 0xafa80018, +0x8f48010c, 0x24070008, 0xa32821, 0xa3482b, +0x822021, 0x100f809, 0x892021, 0x54400006, +0x24130001, 0x8f820054, 0x2021023, 0x2c4203e9, +0x1440ffe9, 0x0, 0x326200ff, 0x54400017, +0xaf520018, 0x8f420378, 0x24420001, 0xaf420378, +0x8f420378, 0x8f820120, 0x8faa002c, 0xafa20010, +0x8f820124, 0x3c040001, 0x24846118, 0x3c050009, +0xafa20014, 0x8d460000, 0x10000035, 0x34a50600, +0x8f420308, 0x24130001, 0x24420001, 0xaf420308, +0x8f420308, 0x1000001e, 0x326200ff, 0x8f830054, +0x8f820054, 0x247003e8, 0x2021023, 0x2c4203e9, +0x10400016, 0x9821, 0x3c150020, 0x24110010, +0x8f42000c, 0x8f440160, 0x8f450164, 0x8f860120, +0xafb10010, 0xafb20014, 0x551025, 0xafa20018, +0x8f42010c, 0x24070008, 0x40f809, 0x24c6001c, +0x1440ffe3, 0x0, 0x8f820054, 0x2021023, +0x2c4203e9, 0x1440ffee, 0x0, 0x326200ff, +0x14400011, 0x0, 0x8f420378, 0x24420001, +0xaf420378, 0x8f420378, 0x8f820120, 0x8faa002c, +0xafa20010, 0x8f820124, 0x3c040001, 0x24846120, +0x3c050009, 0xafa20014, 0x8d460000, 0x34a50700, +0xc002b3b, 0x3c03821, 0x8f4202ec, 0x24420001, +0xaf4202ec, 0x8f4202ec, 0x8f420004, 0x30420001, +0x10400033, 0x3c020400, 0x2c21024, 0x10400017, +0x0, 0x934205c0, 0x8f440250, 0x8f450254, +0x8f4301b4, 0x34420020, 0x14a30006, 0xa34205c0, +0x8f420270, 0x8f430274, 0x8f4401b8, 0x10640008, +0x0, 0x8f420250, 0x8f430254, 0x934405c0, +0x8f460270, 0x8f470274, 0x10000016, 0x38840040, +0x934205c0, 0x10000048, 0x304200bf, 0x934205c0, +0x8f440250, 0x8f450254, 0x8f4301b4, 0x304200bf, +0x14a30006, 0xa34205c0, 0x8f420270, 0x8f430274, +0x8f4401b8, 0x1064000b, 0x0, 0x8f420250, +0x8f430254, 0x934405c0, 0x8f460270, 0x8f470274, +0x38840020, 0xaf4301b4, 0xaf4701b8, 0x10000033, +0xa34405c0, 0x934205c0, 0x1000002f, 0x34420020, +0x934205c0, 0x8f4300d4, 0x34420020, 0xa34205c0, +0x24620001, 0x10000023, 0x28630033, 0x8f4200e4, +0x8f4300e0, 0x24420001, 0xaf4200e4, 0x43102a, +0x14400006, 0x24030001, 0x8f4200e8, 0x14430002, +0xaf4000e4, 0x24030004, 0xaf4300e8, 0x8f420004, +0x30420001, 0x1040000d, 0x3c020400, 0x2c21024, +0x10400007, 0x0, 0x934205c0, 0x34420040, +0xa34205c0, 0x934205c0, 0x1000000f, 0x304200df, +0x934205c0, 0x1000000c, 0x34420060, 0x934205c0, +0x8f4300d4, 0x34420020, 0xa34205c0, 0x24620001, +0x286300fb, 0x14600005, 0xaf4200d4, 0x934205c0, +0xaf4000d4, 0x38420040, 0xa34205c0, 0x934205c0, +0x8f4300e8, 0x3042007f, 0xa34205c0, 0x24020001, +0x14620005, 0x0, 0x934405c0, 0x42102, +0x10000003, 0x348400f0, 0x934405c0, 0x3484000f, +0xc005640, 0x0, 0x2402ff7f, 0x282a024, +0x8fbf0048, 0x8fbe0044, 0x8fb50040, 0x8fb3003c, +0x8fb20038, 0x8fb10034, 0x8fb00030, 0x3e00008, +0x27bd0050, 0x3e00008, 0x0, 0x27bdffb0, +0x274401c0, 0x26e30028, 0x24650400, 0x65102b, +0xafbf0048, 0xafbe0044, 0xafb50040, 0xafb3003c, +0xafb20038, 0xafb10034, 0x10400007, 0xafb00030, +0x8c820000, 0xac620000, 0x24630004, 0x65102b, +0x1440fffb, 0x24840004, 0x8c020080, 0xaee20044, +0x8c0200c0, 0xaee20040, 0x8c020084, 0xaee20030, +0x8c020084, 0xaee2023c, 0x8c020088, 0xaee20240, +0x8c02008c, 0xaee20244, 0x8c020090, 0xaee20248, +0x8c020094, 0xaee2024c, 0x8c020098, 0xaee20250, +0x8c02009c, 0xaee20254, 0x8c0200a0, 0xaee20258, +0x8c0200a4, 0xaee2025c, 0x8c0200a8, 0xaee20260, +0x8c0200ac, 0xaee20264, 0x8c0200b0, 0xaee20268, +0x8c0200b4, 0xaee2026c, 0x8c0200b8, 0xaee20270, +0x8c0200bc, 0x24040001, 0xaee20274, 0xaee00034, +0x41080, 0x571021, 0x8ee30034, 0x8c42023c, +0x24840001, 0x621821, 0x2c82000f, 0xaee30034, +0x1440fff8, 0x41080, 0x8c0200cc, 0xaee20048, +0x8c0200d0, 0xaee2004c, 0x8c0200e0, 0xaee201f8, +0x8c0200e4, 0xaee201fc, 0x8c0200e8, 0xaee20200, +0x8c0200ec, 0xaee20204, 0x8c0200f0, 0xaee20208, +0x8ee400c0, 0x8ee500c4, 0x8c0200fc, 0x45102b, +0x1040000b, 0x0, 0x8ee200c0, 0x8ee300c4, +0x24040001, 0x24050000, 0x651821, 0x65302b, +0x441021, 0x461021, 0xaee200c0, 0xaee300c4, +0x8c0200fc, 0x8ee400c0, 0x8ee500c4, 0x2408ffff, +0x24090000, 0x401821, 0x1021, 0x882024, +0xa92824, 0x822025, 0xa32825, 0xaee400c0, +0xaee500c4, 0x8ee400d0, 0x8ee500d4, 0x8c0200f4, +0x45102b, 0x1040000b, 0x0, 0x8ee200d0, +0x8ee300d4, 0x24040001, 0x24050000, 0x651821, +0x65302b, 0x441021, 0x461021, 0xaee200d0, +0xaee300d4, 0x8c0200f4, 0x8ee400d0, 0x8ee500d4, +0x401821, 0x1021, 0x882024, 0xa92824, +0x822025, 0xa32825, 0xaee400d0, 0xaee500d4, +0x8ee400c8, 0x8ee500cc, 0x8c0200f8, 0x45102b, +0x1040000b, 0x0, 0x8ee200c8, 0x8ee300cc, +0x24040001, 0x24050000, 0x651821, 0x65302b, +0x441021, 0x461021, 0xaee200c8, 0xaee300cc, +0x8c0200f8, 0x8ee400c8, 0x8ee500cc, 0x401821, +0x1021, 0x882024, 0xa92824, 0x822025, +0xa32825, 0x24020008, 0xaee400c8, 0xaee500cc, +0xafa20010, 0xafa00014, 0x8f42000c, 0x8c040208, +0x8c05020c, 0xafa20018, 0x8f42010c, 0x26e60028, +0x40f809, 0x24070400, 0x104000f0, 0x3c020400, +0xafa20020, 0x934205c6, 0x10400089, 0x1821, +0x8f5e0018, 0x27aa0020, 0x240200ff, 0x13c20002, +0xafaa002c, 0x27c30001, 0x8c020228, 0x609021, +0x1642000e, 0x1e38c0, 0x8f42033c, 0x24420001, +0xaf42033c, 0x8f42033c, 0x8c020228, 0x3c040001, +0x2484610c, 0x3c050009, 0xafa00014, 0xafa20010, +0x8fa60020, 0x1000006b, 0x34a50500, 0xf71021, +0x8fa30020, 0x8fa40024, 0xac4304c0, 0xac4404c4, +0x8f830054, 0x8f820054, 0x247003e8, 0x2021023, +0x2c4203e9, 0x1040001b, 0x9821, 0xe08821, +0x263504c0, 0x8f440178, 0x8f45017c, 0x2201821, +0x240a0004, 0xafaa0010, 0xafb20014, 0x8f48000c, +0x1021, 0x2f53021, 0xafa80018, 0x8f48010c, +0x24070008, 0xa32821, 0xa3482b, 0x822021, +0x100f809, 0x892021, 0x54400006, 0x24130001, +0x8f820054, 0x2021023, 0x2c4203e9, 0x1440ffe9, +0x0, 0x326200ff, 0x54400017, 0xaf520018, +0x8f420378, 0x24420001, 0xaf420378, 0x8f420378, +0x8f820120, 0x8faa002c, 0xafa20010, 0x8f820124, +0x3c040001, 0x24846118, 0x3c050009, 0xafa20014, +0x8d460000, 0x10000033, 0x34a50600, 0x8f420308, +0x24130001, 0x24420001, 0xaf420308, 0x8f420308, +0x1000001c, 0x326200ff, 0x8f830054, 0x8f820054, +0x247003e8, 0x2021023, 0x2c4203e9, 0x10400014, +0x9821, 0x24110010, 0x8f42000c, 0x8f440160, +0x8f450164, 0x8f860120, 0xafb10010, 0xafb20014, +0xafa20018, 0x8f42010c, 0x24070008, 0x40f809, +0x24c6001c, 0x1440ffe5, 0x0, 0x8f820054, +0x2021023, 0x2c4203e9, 0x1440ffef, 0x0, +0x326200ff, 0x54400012, 0x24020001, 0x8f420378, +0x24420001, 0xaf420378, 0x8f420378, 0x8f820120, +0x8faa002c, 0xafa20010, 0x8f820124, 0x3c040001, +0x24846120, 0x3c050009, 0xafa20014, 0x8d460000, +0x34a50700, 0xc002b3b, 0x3c03821, 0x1021, +0x1440005b, 0x24020001, 0x10000065, 0x0, +0x8f510018, 0x240200ff, 0x12220002, 0x8021, +0x26300001, 0x8c020228, 0x1602000e, 0x1130c0, +0x8f42033c, 0x24420001, 0xaf42033c, 0x8f42033c, +0x8c020228, 0x3c040001, 0x248460f4, 0x3c050009, +0xafa00014, 0xafa20010, 0x8fa60020, 0x1000003f, +0x34a50100, 0xd71021, 0x8fa30020, 0x8fa40024, +0xac4304c0, 0xac4404c4, 0xc01821, 0x8f440178, +0x8f45017c, 0x1021, 0x24070004, 0xafa70010, +0xafb00014, 0x8f48000c, 0x24c604c0, 0x2e63021, +0xafa80018, 0x8f48010c, 0x24070008, 0xa32821, +0xa3482b, 0x822021, 0x100f809, 0x892021, +0x1440000b, 0x24070008, 0x8f820120, 0xafa20010, +0x8f820124, 0x3c040001, 0x248460fc, 0x3c050009, +0xafa20014, 0x8fa60020, 0x1000001c, 0x34a50200, +0x8f440160, 0x8f450164, 0x8f43000c, 0xaf500018, +0x8f860120, 0x24020010, 0xafa20010, 0xafb00014, +0xafa30018, 0x8f42010c, 0x40f809, 0x24c6001c, +0x54400011, 0x24020001, 0x8f420340, 0x24420001, +0xaf420340, 0x8f420340, 0x8f820120, 0xafa20010, +0x8f820124, 0x3c040001, 0x24846104, 0x3c050009, +0xafa20014, 0x8fa60020, 0x34a50300, 0xc002b3b, +0x2203821, 0x1021, 0x1040000d, 0x24020001, +0x8f4202e8, 0xa34005c6, 0xaf4001b0, 0x24420001, +0xaf4202e8, 0x8f4202e8, 0x8ee20150, 0x24420001, +0xaee20150, 0x10000003, 0x8ee20150, 0x24020001, +0xa34205c6, 0x8fbf0048, 0x8fbe0044, 0x8fb50040, +0x8fb3003c, 0x8fb20038, 0x8fb10034, 0x8fb00030, +0x3e00008, 0x27bd0050, 0x27bdffd8, 0xafbf0020, +0x8f8200b0, 0x30420004, 0x10400068, 0x0, +0x8f430128, 0x8f820104, 0x14620005, 0x0, +0x8f430130, 0x8f8200b4, 0x10620006, 0x0, +0x8f820104, 0xaf420128, 0x8f8200b4, 0x1000005b, +0xaf420130, 0x8f8200b0, 0x3c030080, 0x431024, +0x1040000d, 0x0, 0x8f82011c, 0x34420002, +0xaf82011c, 0x8f8200b0, 0x2403fffb, 0x431024, +0xaf8200b0, 0x8f82011c, 0x2403fffd, 0x431024, +0x1000004a, 0xaf82011c, 0x8f430128, 0x8f820104, +0x14620005, 0x0, 0x8f430130, 0x8f8200b4, +0x10620010, 0x0, 0x8f820104, 0xaf420128, +0x8f8200b4, 0x8f430128, 0xaf420130, 0xafa30010, +0x8f420130, 0x3c040001, 0x24846144, 0xafa20014, +0x8f86011c, 0x8f8700b0, 0x3c050005, 0x10000031, +0x34a50900, 0x8f420128, 0xafa20010, 0x8f420130, +0x3c040001, 0x24846150, 0xafa20014, 0x8f86011c, +0x8f8700b0, 0x3c050005, 0xc002b3b, 0x34a51000, +0x8f82011c, 0x34420002, 0xaf82011c, 0x8f830104, +0x8f8200b0, 0x34420001, 0xaf8200b0, 0x24020008, +0xaf830104, 0xafa20010, 0xafa00014, 0x8f42000c, +0x8c040208, 0x8c05020c, 0xafa20018, 0x8f42010c, +0x26e60028, 0x40f809, 0x24070400, 0x8f82011c, +0x2403fffd, 0x431024, 0xaf82011c, 0x8ee201dc, +0x24420001, 0xaee201dc, 0x8ee201dc, 0x8f420128, +0xafa20010, 0x8f420130, 0x3c040001, 0x2484615c, +0xafa20014, 0x8f86011c, 0x8f8700b0, 0x3c050005, +0x34a51100, 0xc002b3b, 0x0, 0x8f8200a0, +0x30420004, 0x10400069, 0x0, 0x8f43012c, +0x8f820124, 0x14620005, 0x0, 0x8f430134, +0x8f8200a4, 0x10620006, 0x0, 0x8f820124, +0xaf42012c, 0x8f8200a4, 0x1000005c, 0xaf420134, +0x8f8200a0, 0x3c030080, 0x431024, 0x1040000d, +0x0, 0x8f82011c, 0x34420002, 0xaf82011c, +0x8f8200a0, 0x2403fffb, 0x431024, 0xaf8200a0, +0x8f82011c, 0x2403fffd, 0x431024, 0x1000004b, +0xaf82011c, 0x8f43012c, 0x8f820124, 0x14620005, +0x0, 0x8f430134, 0x8f8200a4, 0x10620010, +0x0, 0x8f820124, 0xaf42012c, 0x8f8200a4, +0x8f43012c, 0xaf420134, 0xafa30010, 0x8f420134, +0x3c040001, 0x24846168, 0xafa20014, 0x8f86011c, +0x8f8700a0, 0x3c050005, 0x10000032, 0x34a51200, +0x8f42012c, 0xafa20010, 0x8f420134, 0x3c040001, +0x24846174, 0xafa20014, 0x8f86011c, 0x8f8700a0, +0x3c050005, 0xc002b3b, 0x34a51300, 0x8f82011c, +0x34420002, 0xaf82011c, 0x8f830124, 0x8f8200a0, +0x34420001, 0xaf8200a0, 0x24020080, 0xaf830124, +0xafa20010, 0xafa00014, 0x8f420014, 0x8c040208, +0x8c05020c, 0xafa20018, 0x8f420108, 0x3c060001, +0x24c66ed8, 0x40f809, 0x24070004, 0x8f82011c, +0x2403fffd, 0x431024, 0xaf82011c, 0x8ee201dc, +0x24420001, 0xaee201dc, 0x8ee201dc, 0x8f42012c, +0xafa20010, 0x8f420134, 0x3c040001, 0x24846180, +0xafa20014, 0x8f86011c, 0x8f8700a0, 0x3c050005, +0x34a51400, 0xc002b3b, 0x0, 0x8fbf0020, +0x3e00008, 0x27bd0028, 0x3c081000, 0x24070001, +0x3c060080, 0x3c050100, 0x8f820070, 0x481024, +0x1040fffd, 0x0, 0x8f820054, 0x24420005, +0xaf820078, 0x8c040234, 0x10800016, 0x1821, +0x3c020001, 0x571021, 0x8c4240e8, 0x24420005, +0x3c010001, 0x370821, 0xac2240e8, 0x3c020001, +0x571021, 0x8c4240e8, 0x44102b, 0x14400009, +0x0, 0x3c030080, 0x3c010001, 0x370821, +0xac2040e8, 0x3c010001, 0x370821, 0x1000000b, +0xa02740f0, 0x3c020001, 0x571021, 0x904240f0, +0x54400006, 0x661825, 0x3c020001, 0x571021, +0x904240f1, 0x54400001, 0x661825, 0x8c040230, +0x10800013, 0x0, 0x3c020001, 0x571021, +0x8c4240ec, 0x24420005, 0x3c010001, 0x370821, +0xac2240ec, 0x3c020001, 0x571021, 0x8c4240ec, +0x44102b, 0x14400006, 0x0, 0x3c010001, +0x370821, 0xac2040ec, 0x10000006, 0x651825, +0x3c020001, 0x571021, 0x904240f2, 0x54400001, +0x651825, 0x1060ffbc, 0x0, 0x8f420000, +0x10400007, 0x0, 0xaf80004c, 0x8f82004c, +0x1040fffd, 0x0, 0x10000005, 0x0, +0xaf800048, 0x8f820048, 0x1040fffd, 0x0, +0x8f820060, 0x431025, 0xaf820060, 0x8f420000, +0x10400003, 0x0, 0x1000ffa7, 0xaf80004c, +0x1000ffa5, 0xaf800048, 0x3e00008, 0x0, +0x0, 0x0, 0x0, 0x27bdffe0, +0xafbf0018, 0x8f860064, 0x30c20004, 0x10400025, +0x24040004, 0x8c020114, 0xaf420020, 0xaf840064, +0x8f4202fc, 0x24420001, 0xaf4202fc, 0x8f4202fc, +0x8f820064, 0x30420004, 0x14400005, 0x0, +0x8c030114, 0x8f420020, 0x1462fff2, 0x0, +0x8f420000, 0x10400007, 0x8f43003c, 0xaf80004c, +0x8f82004c, 0x1040fffd, 0x0, 0x10000005, +0x0, 0xaf800048, 0x8f820048, 0x1040fffd, +0x0, 0x8f820060, 0x431025, 0xaf820060, +0x8f420000, 0x10400073, 0x0, 0x1000006f, +0x0, 0x30c20008, 0x10400020, 0x24040008, +0x8c02011c, 0xaf420048, 0xaf840064, 0x8f4202a8, +0x24420001, 0xaf4202a8, 0x8f4202a8, 0x8f820064, +0x30420008, 0x14400005, 0x0, 0x8c03011c, +0x8f420048, 0x1462fff2, 0x0, 0x8f420000, +0x10400007, 0x0, 0xaf80004c, 0x8f82004c, +0x1040fffd, 0x0, 0x10000005, 0x0, +0xaf800048, 0x8f820048, 0x1040fffd, 0x0, +0x8f820060, 0x1000ffd9, 0x34420200, 0x30c20020, +0x10400023, 0x24040020, 0x8c02012c, 0xaf420068, +0xaf840064, 0x8f4202d8, 0x24420001, 0xaf4202d8, +0x8f4202d8, 0x8f820064, 0x30420020, 0x14400005, +0x32c24000, 0x8c03012c, 0x8f420068, 0x1462fff2, +0x32c24000, 0x14400002, 0x3c020001, 0x2c2b025, +0x8f420000, 0x10400007, 0x0, 0xaf80004c, +0x8f82004c, 0x1040fffd, 0x0, 0x10000005, +0x0, 0xaf800048, 0x8f820048, 0x1040fffd, +0x0, 0x8f820060, 0x1000ffb4, 0x34420800, +0x30c20010, 0x10400029, 0x24040010, 0x8c020124, +0xaf420058, 0xaf840064, 0x8f4202d4, 0x24420001, +0xaf4202d4, 0x8f4202d4, 0x8f820064, 0x30420010, +0x14400005, 0x32c22000, 0x8c030124, 0x8f420058, +0x1462fff2, 0x32c22000, 0x50400001, 0x36d68000, +0x8f420000, 0x10400007, 0x0, 0xaf80004c, +0x8f82004c, 0x1040fffd, 0x0, 0x10000005, +0x0, 0xaf800048, 0x8f820048, 0x1040fffd, +0x0, 0x8f820060, 0x34420100, 0xaf820060, +0x8f420000, 0x10400003, 0x0, 0x1000006c, +0xaf80004c, 0x1000006a, 0xaf800048, 0x30c20001, +0x10400004, 0x24020001, 0xaf820064, 0x10000064, +0x0, 0x30c20002, 0x1440000b, 0x3c050003, +0x3c040001, 0x24846244, 0x34a50500, 0x3821, +0xafa00010, 0xc002b3b, 0xafa00014, 0x2402ffc0, +0x10000057, 0xaf820064, 0x8c05022c, 0x8c02010c, +0x10a20048, 0x51080, 0x8c460300, 0x24a20001, +0x3045003f, 0x24020003, 0xac05022c, 0x61e02, +0x10620005, 0x24020010, 0x1062001d, 0x30c20fff, +0x10000039, 0x0, 0x8f4302a8, 0x8f440000, +0x30c20fff, 0xaf420048, 0x24630001, 0xaf4302a8, +0x10800007, 0x8f4202a8, 0xaf80004c, 0x8f82004c, +0x1040fffd, 0x0, 0x10000005, 0x0, +0xaf800048, 0x8f820048, 0x1040fffd, 0x0, +0x8f820060, 0x34420200, 0xaf820060, 0x8f420000, +0x1040001f, 0x0, 0x1000001b, 0x0, +0xaf420058, 0x32c22000, 0x50400001, 0x36d68000, +0x8f4202d4, 0x8f430000, 0x24420001, 0xaf4202d4, +0x10600007, 0x8f4202d4, 0xaf80004c, 0x8f82004c, +0x1040fffd, 0x0, 0x10000005, 0x0, +0xaf800048, 0x8f820048, 0x1040fffd, 0x0, +0x8f820060, 0x34420100, 0xaf820060, 0x8f420000, +0x10400003, 0x0, 0x10000006, 0xaf80004c, +0x10000004, 0xaf800048, 0xc002196, 0xc02021, +0x402821, 0x8c02010c, 0x14a20002, 0x24020002, +0xaf820064, 0x8f820064, 0x30420002, 0x14400004, +0x0, 0x8c02010c, 0x14a2ffac, 0x0, +0x8fbf0018, 0x3e00008, 0x27bd0020, 0x3e00008, +0x0, 0x27bdffa0, 0xafb00040, 0x808021, +0x101602, 0x2442ffff, 0x304300ff, 0x2c620013, +0xafbf0058, 0xafbe0054, 0xafb50050, 0xafb3004c, +0xafb20048, 0xafb10044, 0x104001f3, 0xafa50034, +0x31080, 0x3c010001, 0x220821, 0x8c226288, +0x400008, 0x0, 0x101302, 0x30440fff, +0x24020001, 0x10820005, 0x24020002, 0x1082000c, +0x2402fffe, 0x10000024, 0x3c050003, 0x8f430004, +0x3c020001, 0x8c426f04, 0xaf440200, 0xaf440204, +0x3c040001, 0x8c846e80, 0x10000009, 0x34630001, +0x8f430004, 0xaf440200, 0xaf440204, 0x3c040001, +0x8c846e80, 0x621824, 0x3c020001, 0x2442ca28, +0x21100, 0x21182, 0xaf430004, 0x3c030800, +0x431025, 0xac820038, 0x8f840054, 0x41442, +0x41c82, 0x431021, 0x41cc2, 0x431023, +0x41d02, 0x431021, 0x41d42, 0x431023, +0x10000009, 0xaf420208, 0x3c040001, 0x24846250, +0x34a51000, 0x2003021, 0x3821, 0xafa00010, +0xc002b3b, 0xafa00014, 0x8f4202a0, 0x24420001, +0xaf4202a0, 0x1000021f, 0x8f4202a0, 0x27b00028, +0x2002021, 0x24050210, 0xc002bbf, 0x24060008, +0xc002518, 0x2002021, 0x10000216, 0x0, +0x8faa0034, 0x27a40028, 0xa1880, 0x25420001, +0x3042003f, 0xafa20034, 0x8c650300, 0x8faa0034, +0x21080, 0x8c430300, 0x25420001, 0x3042003f, +0xafa20034, 0xac02022c, 0xafa50028, 0xc002518, +0xafa3002c, 0x10000203, 0x0, 0x27b00028, +0x2002021, 0x24050210, 0xc002bbf, 0x24060008, +0xc002657, 0x2002021, 0x100001fa, 0x0, +0x8faa0034, 0x27a40028, 0xa1880, 0x25420001, +0x3042003f, 0xafa20034, 0x8c650300, 0x8faa0034, +0x21080, 0x8c430300, 0x25420001, 0x3042003f, +0xafa20034, 0xac02022c, 0xafa50028, 0xc002657, +0xafa3002c, 0x100001e7, 0x0, 0x101302, +0x30430fff, 0x24020001, 0x10620005, 0x24020002, +0x1062001e, 0x3c020002, 0x10000033, 0x3c050003, +0x3c030002, 0x2c31024, 0x54400037, 0x2c3b025, +0x8f820228, 0x3c010001, 0x370821, 0xac2238d8, +0x8f82022c, 0x3c010001, 0x370821, 0xac2238dc, +0x8f820230, 0x3c010001, 0x370821, 0xac2238e0, +0x8f820234, 0x3c010001, 0x370821, 0xac2238e4, +0x2402ffff, 0xaf820228, 0xaf82022c, 0xaf820230, +0xaf820234, 0x10000020, 0x2c3b025, 0x2c21024, +0x10400012, 0x3c02fffd, 0x3c020001, 0x571021, +0x8c4238d8, 0xaf820228, 0x3c020001, 0x571021, +0x8c4238dc, 0xaf82022c, 0x3c020001, 0x571021, +0x8c4238e0, 0xaf820230, 0x3c020001, 0x571021, +0x8c4238e4, 0xaf820234, 0x3c02fffd, 0x3442ffff, +0x10000009, 0x2c2b024, 0x3c040001, 0x2484625c, +0x34a51100, 0x2003021, 0x3821, 0xafa00010, +0xc002b3b, 0xafa00014, 0x8f4202cc, 0x24420001, +0xaf4202cc, 0x1000019f, 0x8f4202cc, 0x101302, +0x30450fff, 0x24020001, 0x10a20005, 0x24020002, +0x10a2000d, 0x3c0408ff, 0x10000014, 0x3c050003, +0x3c0208ff, 0x3442ffff, 0x8f830220, 0x3c040004, +0x2c4b025, 0x621824, 0x34630008, 0xaf830220, +0x10000012, 0xaf450298, 0x3484fff7, 0x3c03fffb, +0x8f820220, 0x3463ffff, 0x2c3b024, 0x441024, +0xaf820220, 0x10000009, 0xaf450298, 0x3c040001, +0x24846268, 0x34a51200, 0x2003021, 0x3821, +0xafa00010, 0xc002b3b, 0xafa00014, 0x8f4202bc, +0x24420001, 0xaf4202bc, 0x10000176, 0x8f4202bc, +0x27840208, 0x24050200, 0xc002bbf, 0x24060008, +0x27440224, 0x24050200, 0xc002bbf, 0x24060008, +0x8f4202c4, 0x24420001, 0xaf4202c4, 0x10000169, +0x8f4202c4, 0x101302, 0x30430fff, 0x24020001, +0x10620011, 0x28620002, 0x50400005, 0x24020002, +0x10600007, 0x0, 0x10000017, 0x0, +0x1062000f, 0x0, 0x10000013, 0x0, +0x8c060248, 0x2021, 0xc005104, 0x24050004, +0x10000007, 0x0, 0x8c060248, 0x2021, +0xc005104, 0x24050004, 0x10000010, 0x0, +0x8c06024c, 0x2021, 0xc005104, 0x24050001, +0x1000000a, 0x0, 0x3c040001, 0x24846274, +0x3c050003, 0x34a51300, 0x2003021, 0x3821, +0xafa00010, 0xc002b3b, 0xafa00014, 0x8f4202c0, +0x24420001, 0xaf4202c0, 0x1000013a, 0x8f4202c0, +0xc002426, 0x0, 0x10000136, 0x0, +0x24020001, 0xa34205c5, 0x24100100, 0x8f4401a8, +0x8f4501ac, 0xafb00010, 0xafa00014, 0x8f420014, +0xafa20018, 0x8f420108, 0x26e60028, 0x40f809, +0x24070400, 0x1040fff5, 0x0, 0x10000125, +0x0, 0x3c03ffff, 0x34637fff, 0x8f420368, +0x8f440360, 0x2c3b024, 0x1821, 0xaf400058, +0xaf40005c, 0xaf400060, 0xaf400064, 0x441023, +0xaf420368, 0x3c020900, 0xaf400360, 0xafa20020, +0x8f5e0018, 0x27aa0020, 0x240200ff, 0x13c20002, +0xafaa003c, 0x27c30001, 0x8c020228, 0x609021, +0x1642000e, 0x1e38c0, 0x8f42033c, 0x24420001, +0xaf42033c, 0x8f42033c, 0x8c020228, 0x3c040001, +0x2484620c, 0x3c050009, 0xafa00014, 0xafa20010, +0x8fa60020, 0x1000006b, 0x34a50500, 0xf71021, +0x8fa30020, 0x8fa40024, 0xac4304c0, 0xac4404c4, +0x8f830054, 0x8f820054, 0x247003e8, 0x2021023, +0x2c4203e9, 0x1040001b, 0x9821, 0xe08821, +0x263504c0, 0x8f440178, 0x8f45017c, 0x2201821, +0x240a0004, 0xafaa0010, 0xafb20014, 0x8f48000c, +0x1021, 0x2f53021, 0xafa80018, 0x8f48010c, +0x24070008, 0xa32821, 0xa3482b, 0x822021, +0x100f809, 0x892021, 0x54400006, 0x24130001, +0x8f820054, 0x2021023, 0x2c4203e9, 0x1440ffe9, +0x0, 0x326200ff, 0x54400017, 0xaf520018, +0x8f420378, 0x24420001, 0xaf420378, 0x8f420378, +0x8f820120, 0x8faa003c, 0xafa20010, 0x8f820124, +0x3c040001, 0x24846218, 0x3c050009, 0xafa20014, +0x8d460000, 0x10000033, 0x34a50600, 0x8f420308, +0x24130001, 0x24420001, 0xaf420308, 0x8f420308, +0x1000001c, 0x326200ff, 0x8f830054, 0x8f820054, +0x247003e8, 0x2021023, 0x2c4203e9, 0x10400014, +0x9821, 0x24110010, 0x8f42000c, 0x8f440160, +0x8f450164, 0x8f860120, 0xafb10010, 0xafb20014, +0xafa20018, 0x8f42010c, 0x24070008, 0x40f809, +0x24c6001c, 0x1440ffe5, 0x0, 0x8f820054, +0x2021023, 0x2c4203e9, 0x1440ffef, 0x0, +0x326200ff, 0x14400011, 0x0, 0x8f420378, +0x24420001, 0xaf420378, 0x8f420378, 0x8f820120, +0x8faa003c, 0xafa20010, 0x8f820124, 0x3c040001, +0x24846220, 0x3c050009, 0xafa20014, 0x8d460000, +0x34a50700, 0xc002b3b, 0x3c03821, 0x8f4202b0, +0x24420001, 0xaf4202b0, 0x8f4202b0, 0x8f4202f8, +0x24420001, 0xaf4202f8, 0x1000008a, 0x8f4202f8, +0x8c02025c, 0x27440224, 0xaf4201f0, 0x8c020260, +0x24050200, 0x24060008, 0xc002bbf, 0xaf4201f8, +0x8f820220, 0x30420008, 0x14400002, 0x24020001, +0x24020002, 0xaf420298, 0x8f4202ac, 0x24420001, +0xaf4202ac, 0x10000077, 0x8f4202ac, 0x3c0200ff, +0x3442ffff, 0x2021824, 0x32c20180, 0x14400006, +0x3402fffb, 0x43102b, 0x14400003, 0x0, +0x1000006c, 0xaf4300bc, 0x3c040001, 0x24846280, +0x3c050003, 0x34a51500, 0x2003021, 0x3821, +0xafa00010, 0xc002b3b, 0xafa00014, 0x3c020700, +0x34421000, 0x101e02, 0x621825, 0xafa30020, +0x8f510018, 0x240200ff, 0x12220002, 0x8021, +0x26300001, 0x8c020228, 0x1602000e, 0x1130c0, +0x8f42033c, 0x24420001, 0xaf42033c, 0x8f42033c, +0x8c020228, 0x3c040001, 0x248461f4, 0x3c050009, +0xafa00014, 0xafa20010, 0x8fa60020, 0x1000003f, +0x34a50100, 0xd71021, 0x8fa30020, 0x8fa40024, +0xac4304c0, 0xac4404c4, 0xc01821, 0x8f440178, +0x8f45017c, 0x1021, 0x24070004, 0xafa70010, +0xafb00014, 0x8f48000c, 0x24c604c0, 0x2e63021, +0xafa80018, 0x8f48010c, 0x24070008, 0xa32821, +0xa3482b, 0x822021, 0x100f809, 0x892021, +0x1440000b, 0x24070008, 0x8f820120, 0xafa20010, +0x8f820124, 0x3c040001, 0x248461fc, 0x3c050009, +0xafa20014, 0x8fa60020, 0x1000001c, 0x34a50200, +0x8f440160, 0x8f450164, 0x8f43000c, 0xaf500018, +0x8f860120, 0x24020010, 0xafa20010, 0xafb00014, +0xafa30018, 0x8f42010c, 0x40f809, 0x24c6001c, +0x14400010, 0x0, 0x8f420340, 0x24420001, +0xaf420340, 0x8f420340, 0x8f820120, 0xafa20010, +0x8f820124, 0x3c040001, 0x24846204, 0x3c050009, +0xafa20014, 0x8fa60020, 0x34a50300, 0xc002b3b, +0x2203821, 0x8f4202e0, 0x24420001, 0xaf4202e0, +0x8f4202e0, 0x8f4202f0, 0x24420001, 0xaf4202f0, +0x8f4202f0, 0x8fa20034, 0x8fbf0058, 0x8fbe0054, +0x8fb50050, 0x8fb3004c, 0x8fb20048, 0x8fb10044, +0x8fb00040, 0x3e00008, 0x27bd0060, 0x27bdfff8, +0x2408ffff, 0x10a00014, 0x4821, 0x3c0aedb8, +0x354a8320, 0x90870000, 0x24840001, 0x3021, +0x1071026, 0x30420001, 0x10400002, 0x81842, +0x6a1826, 0x604021, 0x24c60001, 0x2cc20008, +0x1440fff7, 0x73842, 0x25290001, 0x125102b, +0x1440fff0, 0x0, 0x1001021, 0x3e00008, +0x27bd0008, 0x27bdffb0, 0xafbf0048, 0xafbe0044, +0xafb50040, 0xafb3003c, 0xafb20038, 0xafb10034, +0xafb00030, 0x8f870220, 0xafa70024, 0x8f870200, +0xafa7002c, 0x8f820220, 0x3c0308ff, 0x3463ffff, +0x431024, 0x34420004, 0xaf820220, 0x8f820200, +0x3c03c0ff, 0x3463ffff, 0x431024, 0x34420004, +0xaf820200, 0x8f530358, 0x8f55035c, 0x8f5e0360, +0x8f470364, 0xafa70014, 0x8f470368, 0xafa7001c, +0x8f4202d0, 0x274401c0, 0x24420001, 0xaf4202d0, +0x8f5002d0, 0x8f510204, 0x8f520200, 0xc002ba8, +0x24050400, 0xaf530358, 0xaf55035c, 0xaf5e0360, +0x8fa70014, 0xaf470364, 0x8fa7001c, 0xaf470368, +0xaf5002d0, 0xaf510204, 0xaf520200, 0x8c02025c, +0x27440224, 0xaf4201f0, 0x8c020260, 0x24050200, +0x24060008, 0xaf4201f8, 0x24020006, 0xc002bbf, +0xaf4201f4, 0x3c023b9a, 0x3442ca00, 0xaf4201fc, +0x240203e8, 0x24040002, 0x24030001, 0xaf420294, +0xaf440290, 0xaf43029c, 0x8f820220, 0x30420008, +0x10400004, 0x0, 0xaf430298, 0x10000003, +0x3021, 0xaf440298, 0x3021, 0x3c030001, +0x661821, 0x90636d00, 0x3461021, 0x24c60001, +0xa043022c, 0x2cc2000f, 0x1440fff8, 0x3461821, +0x24c60001, 0x8f820040, 0x24040080, 0x24050080, +0x21702, 0x24420030, 0xa062022c, 0x3461021, +0xc002ba8, 0xa040022c, 0x8fa70024, 0x30e20004, +0x14400006, 0x0, 0x8f820220, 0x3c0308ff, +0x3463fffb, 0x431024, 0xaf820220, 0x8fa7002c, +0x30e20004, 0x14400006, 0x0, 0x8f820200, +0x3c03c0ff, 0x3463fffb, 0x431024, 0xaf820200, +0x8fbf0048, 0x8fbe0044, 0x8fb50040, 0x8fb3003c, +0x8fb20038, 0x8fb10034, 0x8fb00030, 0x3e00008, +0x27bd0050, 0x0, 0x0, 0xaf400104, +0x24040001, 0x410c0, 0x2e21821, 0x24820001, +0x3c010001, 0x230821, 0xa42234d0, 0x402021, +0x2c820080, 0x1440fff8, 0x410c0, 0x24020001, +0x3c010001, 0x370821, 0xa42038d0, 0xaf420100, +0xaf800228, 0xaf80022c, 0xaf800230, 0xaf800234, +0x3e00008, 0x0, 0x27bdffe8, 0xafbf0014, +0xafb00010, 0x8f420104, 0x28420005, 0x10400026, +0x808021, 0x3c020001, 0x8f430104, 0x344230d0, +0x2e22021, 0x318c0, 0x621821, 0x2e31821, +0x83102b, 0x10400015, 0x1021, 0x96070000, +0x24840006, 0x24660006, 0x9482fffc, 0x14470009, +0x2821, 0x9483fffe, 0x96020002, 0x14620006, +0xa01021, 0x94820000, 0x96030004, 0x431026, +0x2c450001, 0xa01021, 0x14400009, 0x24840008, +0x86102b, 0x1440fff0, 0x1021, 0x304200ff, +0x14400030, 0x24020001, 0x1000002e, 0x1021, +0x1000fffa, 0x24020001, 0x2002021, 0xc00240c, +0x24050006, 0x3042007f, 0x218c0, 0x2e31021, +0x3c010001, 0x220821, 0x942230d0, 0x1040fff2, +0x2e31021, 0x3c060001, 0xc23021, 0x94c630d0, +0x10c0ffed, 0x3c080001, 0x350834d2, 0x96070000, +0x610c0, 0x572021, 0x882021, 0x94820000, +0x14470009, 0x2821, 0x94830002, 0x96020002, +0x14620006, 0xa01021, 0x94820004, 0x96030004, +0x431026, 0x2c450001, 0xa01021, 0x14400007, +0x610c0, 0x2e21021, 0x3c060001, 0xc23021, +0x94c634d0, 0x14c0ffeb, 0x610c0, 0x10c0ffd2, +0x24020001, 0x8fbf0014, 0x8fb00010, 0x3e00008, +0x27bd0018, 0x3e00008, 0x0, 0x27bdffb0, +0x801021, 0xafb00030, 0x24500002, 0x2002021, +0x24050006, 0xafb10034, 0x408821, 0xafbf0048, +0xafbe0044, 0xafb50040, 0xafb3003c, 0xc00240c, +0xafb20038, 0x3047007f, 0x710c0, 0x2e21021, +0x3c050001, 0xa22821, 0x94a530d0, 0x50a0001c, +0xa03021, 0x3c090001, 0x352934d2, 0x96280002, +0x510c0, 0x572021, 0x892021, 0x94820000, +0x14480009, 0x3021, 0x94830002, 0x96020002, +0x14620006, 0xc01021, 0x94820004, 0x96030004, +0x431026, 0x2c460001, 0xc01021, 0x14400007, +0x510c0, 0x2e21021, 0x3c050001, 0xa22821, +0x94a534d0, 0x14a0ffeb, 0x510c0, 0xa03021, +0x10c00014, 0x610c0, 0x571821, 0x3c010001, +0x230821, 0x8c2334d0, 0x571021, 0xafa30010, +0x3c010001, 0x220821, 0x8c2234d4, 0x3c040001, +0x24846394, 0xafa20014, 0x8e260000, 0x8e270004, +0x3c050004, 0xc002b3b, 0x34a50400, 0x10000063, +0x3c020800, 0x8f450100, 0x10a00006, 0x510c0, +0x2e21021, 0x3c010001, 0x220821, 0x942234d0, +0xaf420100, 0xa03021, 0x14c00011, 0x628c0, +0x710c0, 0x2e21021, 0xafa70010, 0x3c010001, +0x220821, 0x942230d0, 0x3c040001, 0x248463a0, +0xafa20014, 0x8e260000, 0x8e270004, 0x3c050004, +0xc002b3b, 0x34a50500, 0x10000048, 0x3c020800, +0xb71821, 0x3c020001, 0x96040000, 0x344234d2, +0x621821, 0xa4640000, 0x8e020002, 0x720c0, +0xac620002, 0x2e41021, 0x3c030001, 0x621821, +0x946330d0, 0x2e51021, 0x3c010001, 0x220821, +0xa42334d0, 0x2e41021, 0x3c010001, 0x220821, +0xa42630d0, 0x8f420104, 0x24420001, 0x28420080, +0x1040000f, 0x3c020002, 0x8f420104, 0x3c040001, +0x348430d2, 0x96030000, 0x210c0, 0x571021, +0x441021, 0xa4430000, 0x8e030002, 0xac430002, +0x8f420104, 0x24420001, 0xaf420104, 0x3c020002, +0x2c21024, 0x10400011, 0x72142, 0x3c030001, +0x346338d8, 0x24020003, 0x441023, 0x21080, +0x572021, 0x832021, 0x571021, 0x431021, +0x30e5001f, 0x8c430000, 0x24020001, 0xa21004, +0x621825, 0x1000000c, 0xac830000, 0x24020003, +0x441023, 0x21080, 0x5c2821, 0x5c1021, +0x30e4001f, 0x8c430228, 0x24020001, 0x821004, +0x621825, 0xaca30228, 0x3c020800, 0x34421000, +0x1821, 0xafa20020, 0x8f5e0018, 0x27aa0020, +0x240200ff, 0x13c20002, 0xafaa002c, 0x27c30001, +0x8c020228, 0x609021, 0x1642000e, 0x1e38c0, +0x8f42033c, 0x24420001, 0xaf42033c, 0x8f42033c, +0x8c020228, 0x3c040001, 0x2484635c, 0x3c050009, +0xafa00014, 0xafa20010, 0x8fa60020, 0x1000006b, +0x34a50500, 0xf71021, 0x8fa30020, 0x8fa40024, +0xac4304c0, 0xac4404c4, 0x8f830054, 0x8f820054, +0x247003e8, 0x2021023, 0x2c4203e9, 0x1040001b, +0x9821, 0xe08821, 0x263504c0, 0x8f440178, +0x8f45017c, 0x2201821, 0x240a0004, 0xafaa0010, +0xafb20014, 0x8f48000c, 0x1021, 0x2f53021, +0xafa80018, 0x8f48010c, 0x24070008, 0xa32821, +0xa3482b, 0x822021, 0x100f809, 0x892021, +0x54400006, 0x24130001, 0x8f820054, 0x2021023, +0x2c4203e9, 0x1440ffe9, 0x0, 0x326200ff, +0x54400017, 0xaf520018, 0x8f420378, 0x24420001, +0xaf420378, 0x8f420378, 0x8f820120, 0x8faa002c, +0xafa20010, 0x8f820124, 0x3c040001, 0x24846368, +0x3c050009, 0xafa20014, 0x8d460000, 0x10000033, +0x34a50600, 0x8f420308, 0x24130001, 0x24420001, +0xaf420308, 0x8f420308, 0x1000001c, 0x326200ff, +0x8f830054, 0x8f820054, 0x247003e8, 0x2021023, +0x2c4203e9, 0x10400014, 0x9821, 0x24110010, +0x8f42000c, 0x8f440160, 0x8f450164, 0x8f860120, +0xafb10010, 0xafb20014, 0xafa20018, 0x8f42010c, +0x24070008, 0x40f809, 0x24c6001c, 0x1440ffe5, +0x0, 0x8f820054, 0x2021023, 0x2c4203e9, +0x1440ffef, 0x0, 0x326200ff, 0x14400011, +0x0, 0x8f420378, 0x24420001, 0xaf420378, +0x8f420378, 0x8f820120, 0x8faa002c, 0xafa20010, +0x8f820124, 0x3c040001, 0x24846370, 0x3c050009, +0xafa20014, 0x8d460000, 0x34a50700, 0xc002b3b, +0x3c03821, 0x8f4202b4, 0x24420001, 0xaf4202b4, +0x8f4202b4, 0x8f4202f4, 0x24420001, 0xaf4202f4, +0x8f4202f4, 0x8fbf0048, 0x8fbe0044, 0x8fb50040, +0x8fb3003c, 0x8fb20038, 0x8fb10034, 0x8fb00030, +0x3e00008, 0x27bd0050, 0x27bdffa0, 0x801021, +0xafb00040, 0x24500002, 0x2002021, 0x24050006, +0xafb10044, 0x408821, 0xafbf0058, 0xafbe0054, +0xafb50050, 0xafb3004c, 0xc00240c, 0xafb20048, +0x3048007f, 0x810c0, 0x2e21021, 0x3c060001, +0xc23021, 0x94c630d0, 0x10c0001c, 0x3821, +0x3c0a0001, 0x354a34d2, 0x96290002, 0x610c0, +0x572021, 0x8a2021, 0x94820000, 0x14490009, +0x2821, 0x94830002, 0x96020002, 0x14620006, +0xa01021, 0x94820004, 0x96030004, 0x431026, +0x2c450001, 0xa01021, 0x14400008, 0x610c0, +0xc03821, 0x2e21021, 0x3c060001, 0xc23021, +0x94c634d0, 0x14c0ffea, 0x610c0, 0x14c00011, +0xafa70028, 0x810c0, 0x2e21021, 0xafa80010, +0x3c010001, 0x220821, 0x942230d0, 0x3c040001, +0x248463ac, 0xafa20014, 0x8e260000, 0x8e270004, +0x3c050004, 0xc002b3b, 0x34a50900, 0x10000075, +0x3c020800, 0x10e0000c, 0x610c0, 0x2e21021, +0x3c030001, 0x621821, 0x946334d0, 0x710c0, +0x2e21021, 0x3c010001, 0x220821, 0xa42334d0, +0x1000000b, 0x3c040001, 0x2e21021, 0x3c030001, +0x621821, 0x946334d0, 0x810c0, 0x2e21021, +0x3c010001, 0x220821, 0xa42330d0, 0x3c040001, +0x348430d0, 0x8f430100, 0x610c0, 0x2e21021, +0x3c010001, 0x220821, 0xa42334d0, 0x8f420104, +0x2e43821, 0x2821, 0x18400029, 0xaf460100, +0x24e60006, 0x94c3fffc, 0x96020000, 0x14620009, +0x2021, 0x94c3fffe, 0x96020002, 0x14620006, +0x801021, 0x94c20000, 0x96030004, 0x431026, +0x2c440001, 0x801021, 0x50400014, 0x24a50001, +0x8f420104, 0x2442ffff, 0xa2102a, 0x1040000b, +0x24e40004, 0x94820006, 0x8c830008, 0xa482fffe, +0xac830000, 0x8f420104, 0x24a50001, 0x2442ffff, +0xa2102a, 0x1440fff7, 0x24840008, 0x8f420104, +0x2442ffff, 0x10000006, 0xaf420104, 0x8f420104, +0x24c60008, 0xa2102a, 0x1440ffda, 0x24e70008, +0x810c0, 0x2e21021, 0x3c010001, 0x220821, +0x942230d0, 0x14400023, 0x3c020800, 0x3c020002, +0x2c21024, 0x10400012, 0x82142, 0x3c030001, +0x346338d8, 0x24020003, 0x441023, 0x21080, +0x572021, 0x832021, 0x571021, 0x431021, +0x3105001f, 0x24030001, 0x8c420000, 0xa31804, +0x31827, 0x431024, 0x1000000d, 0xac820000, +0x24020003, 0x441023, 0x21080, 0x5c2821, +0x5c1021, 0x3104001f, 0x24030001, 0x8c420228, +0x831804, 0x31827, 0x431024, 0xaca20228, +0x3c020800, 0x34422000, 0x1821, 0xafa20020, +0x8f5e0018, 0x27ab0020, 0x240200ff, 0x13c20002, +0xafab0034, 0x27c30001, 0x8c020228, 0x609021, +0x1642000e, 0x1e38c0, 0x8f42033c, 0x24420001, +0xaf42033c, 0x8f42033c, 0x8c020228, 0x3c040001, +0x2484635c, 0x3c050009, 0xafa00014, 0xafa20010, +0x8fa60020, 0x1000006b, 0x34a50500, 0xf71021, +0x8fa30020, 0x8fa40024, 0xac4304c0, 0xac4404c4, +0x8f830054, 0x8f820054, 0x247003e8, 0x2021023, +0x2c4203e9, 0x1040001b, 0x9821, 0xe08821, +0x263504c0, 0x8f440178, 0x8f45017c, 0x2201821, +0x240b0004, 0xafab0010, 0xafb20014, 0x8f48000c, +0x1021, 0x2f53021, 0xafa80018, 0x8f48010c, +0x24070008, 0xa32821, 0xa3482b, 0x822021, +0x100f809, 0x892021, 0x54400006, 0x24130001, +0x8f820054, 0x2021023, 0x2c4203e9, 0x1440ffe9, +0x0, 0x326200ff, 0x54400017, 0xaf520018, +0x8f420378, 0x24420001, 0xaf420378, 0x8f420378, +0x8f820120, 0x8fab0034, 0xafa20010, 0x8f820124, +0x3c040001, 0x24846368, 0x3c050009, 0xafa20014, +0x8d660000, 0x10000033, 0x34a50600, 0x8f420308, +0x24130001, 0x24420001, 0xaf420308, 0x8f420308, +0x1000001c, 0x326200ff, 0x8f830054, 0x8f820054, +0x247003e8, 0x2021023, 0x2c4203e9, 0x10400014, +0x9821, 0x24110010, 0x8f42000c, 0x8f440160, +0x8f450164, 0x8f860120, 0xafb10010, 0xafb20014, +0xafa20018, 0x8f42010c, 0x24070008, 0x40f809, +0x24c6001c, 0x1440ffe5, 0x0, 0x8f820054, +0x2021023, 0x2c4203e9, 0x1440ffef, 0x0, +0x326200ff, 0x14400011, 0x0, 0x8f420378, +0x24420001, 0xaf420378, 0x8f420378, 0x8f820120, +0x8fab0034, 0xafa20010, 0x8f820124, 0x3c040001, +0x24846370, 0x3c050009, 0xafa20014, 0x8d660000, +0x34a50700, 0xc002b3b, 0x3c03821, 0x8f4202b8, +0x24420001, 0xaf4202b8, 0x8f4202b8, 0x8f4202f4, +0x24420001, 0xaf4202f4, 0x8f4202f4, 0x8fbf0058, +0x8fbe0054, 0x8fb50050, 0x8fb3004c, 0x8fb20048, +0x8fb10044, 0x8fb00040, 0x3e00008, 0x27bd0060, +0x0, 0x0, 0x0, 0x27bdffe0, +0x27644000, 0xafbf0018, 0xc002ba8, 0x24051000, +0x3c030001, 0x34632cc0, 0x3c040001, 0x34842ec8, +0x24020020, 0xaf82011c, 0x2e31021, 0xaf800100, +0xaf800104, 0xaf800108, 0xaf800110, 0xaf800114, +0xaf800118, 0xaf800120, 0xaf800124, 0xaf800128, +0xaf800130, 0xaf800134, 0xaf800138, 0xaf4200ec, +0x2e31021, 0xaf4200f0, 0x2e41021, 0xaf4200f4, +0x2e41021, 0xaf4200f8, 0x3c020001, 0x571021, +0x904240f4, 0x1440001c, 0x3c050001, 0x8f82011c, +0x3c040001, 0x24846470, 0x3c050001, 0x34420001, +0xaf82011c, 0xafa00010, 0xafa00014, 0x8f86011c, +0x34a50100, 0xc002b3b, 0x3821, 0x8c020218, +0x30420040, 0x10400014, 0x0, 0x8f82011c, +0x3c040001, 0x2484647c, 0x3c050001, 0x34420004, +0xaf82011c, 0xafa00010, 0xafa00014, 0x8f86011c, +0x10000007, 0x34a50200, 0x3c040001, 0x24846484, +0xafa00010, 0xafa00014, 0x8f86011c, 0x34a50300, +0xc002b3b, 0x3821, 0x8fbf0018, 0x3e00008, +0x27bd0020, 0x8fa90010, 0x8f83012c, 0x8faa0014, +0x8fab0018, 0x1060000a, 0x27624fe0, 0x14620002, +0x24680020, 0x27684800, 0x8f820128, 0x11020004, +0x0, 0x8f820124, 0x15020007, 0x0, +0x8f430334, 0x1021, 0x24630001, 0xaf430334, +0x10000039, 0x8f430334, 0xac640000, 0xac650004, +0xac660008, 0xa467000e, 0xac690018, 0xac6a001c, +0xac6b0010, 0xac620014, 0xaf880120, 0x8f4200fc, +0x8f4400f4, 0x2442ffff, 0xaf4200fc, 0x8c820000, +0x10490005, 0x3042ff8f, 0x10400019, 0x3122ff8f, +0x10400018, 0x3c020001, 0x8c830004, 0x2c620010, +0x10400013, 0x3c020001, 0x24630001, 0xac830004, +0x8f4300f8, 0x344230c8, 0x2e21021, 0x54620004, +0x24620008, 0x3c020001, 0x34422ec8, 0x2e21021, +0x14440015, 0x24020001, 0x8f820128, 0x24420020, +0xaf820128, 0x8f820128, 0x1000000f, 0x24020001, +0x3c020001, 0x344230c8, 0x2e21021, 0x54820004, +0x24820008, 0x3c020001, 0x34422ec8, 0x2e21021, +0x402021, 0x24020001, 0xaf4400f4, 0xac890000, +0xac820004, 0x24020001, 0x3e00008, 0x0, +0x3e00008, 0x0, 0x8fa90010, 0x8f83010c, +0x8faa0014, 0x8fab0018, 0x1060000a, 0x276247e0, +0x14620002, 0x24680020, 0x27684000, 0x8f820108, +0x11020004, 0x0, 0x8f820104, 0x15020007, +0x0, 0x8f430338, 0x1021, 0x24630001, +0xaf430338, 0x10000035, 0x8f430338, 0xac640000, +0xac650004, 0xac660008, 0xa467000e, 0xac690018, +0xac6a001c, 0xac6b0010, 0xac620014, 0xaf880100, +0x8f4400ec, 0x8c820000, 0x30420006, 0x10400019, +0x31220006, 0x10400018, 0x3c020001, 0x8c830004, +0x2c620010, 0x10400013, 0x3c020001, 0x24630001, +0xac830004, 0x8f4300f0, 0x34422ec0, 0x2e21021, +0x54620004, 0x24620008, 0x3c020001, 0x34422cc0, +0x2e21021, 0x14440015, 0x24020001, 0x8f820108, +0x24420020, 0xaf820108, 0x8f820108, 0x1000000f, +0x24020001, 0x3c020001, 0x34422ec0, 0x2e21021, +0x54820004, 0x24820008, 0x3c020001, 0x34422cc0, +0x2e21021, 0x402021, 0x24020001, 0xaf4400ec, +0xac890000, 0xac820004, 0x24020001, 0x3e00008, +0x0, 0x3e00008, 0x0, 0x27bdffd8, +0x3c040001, 0x2484648c, 0x3c050001, 0xafbf0024, +0xafb20020, 0xafb1001c, 0xafb00018, 0x8f900104, +0x8f9100b0, 0x8f92011c, 0x34a52500, 0x8f820100, +0x2403021, 0x2203821, 0xafa20010, 0xc002b3b, +0xafb00014, 0x8e020008, 0xafa20010, 0x8e02000c, +0x3c040001, 0x24846498, 0xafa20014, 0x8e060000, +0x8e070004, 0x3c050001, 0xc002b3b, 0x34a52510, +0x8e020018, 0xafa20010, 0x8e02001c, 0x3c040001, +0x248464a4, 0xafa20014, 0x8e060010, 0x8e070014, +0x3c050001, 0xc002b3b, 0x34a52520, 0x3c027f00, +0x2221024, 0x3c030800, 0x54430016, 0x3c030200, +0x8f82009c, 0x3042ffff, 0x14400012, 0x3c030200, +0x3c040001, 0x248464b0, 0x3c050002, 0x34a5f030, +0x3021, 0x3821, 0x36420002, 0xaf82011c, +0x36220001, 0xaf8200b0, 0xaf900104, 0xaf92011c, +0xafa00010, 0xc002b3b, 0xafa00014, 0x10000024, +0x0, 0x2c31024, 0x1040000d, 0x2231024, +0x1040000b, 0x36420002, 0xaf82011c, 0x36220001, +0xaf8200b0, 0xaf900104, 0xaf92011c, 0x8f420330, +0x24420001, 0xaf420330, 0x10000015, 0x8f420330, +0x3c040001, 0x248464b8, 0x240202a9, 0xafa20010, +0xafa00014, 0x8f860144, 0x3c070001, 0x24e764c0, +0xc002b3b, 0x3405dead, 0x8f82011c, 0x34420002, +0xaf82011c, 0x8f820220, 0x34420004, 0xaf820220, +0x8f820140, 0x3c030001, 0x431025, 0xaf820140, +0x8fbf0024, 0x8fb20020, 0x8fb1001c, 0x8fb00018, +0x3e00008, 0x27bd0028, 0x27bdffd8, 0x3c040001, +0x248464e8, 0x3c050001, 0xafbf0024, 0xafb20020, +0xafb1001c, 0xafb00018, 0x8f900124, 0x8f9100a0, +0x8f92011c, 0x34a52600, 0x8f820120, 0x2403021, +0x2203821, 0xafa20010, 0xc002b3b, 0xafb00014, +0x8e020008, 0xafa20010, 0x8e02000c, 0x3c040001, +0x248464f4, 0xafa20014, 0x8e060000, 0x8e070004, +0x3c050001, 0xc002b3b, 0x34a52610, 0x8e020018, +0xafa20010, 0x8e02001c, 0x3c040001, 0x24846500, +0xafa20014, 0x8e060010, 0x8e070014, 0x3c050001, +0xc002b3b, 0x34a52620, 0x3c027f00, 0x2221024, +0x3c030800, 0x54430016, 0x3c030200, 0x8f8200ac, +0x3042ffff, 0x14400012, 0x3c030200, 0x3c040001, +0x2484650c, 0x3c050001, 0x34a5f030, 0x3021, +0x3821, 0x36420002, 0xaf82011c, 0x36220001, +0xaf8200a0, 0xaf900124, 0xaf92011c, 0xafa00010, +0xc002b3b, 0xafa00014, 0x10000024, 0x0, +0x2c31024, 0x1040000d, 0x2231024, 0x1040000b, +0x36420002, 0xaf82011c, 0x36220001, 0xaf8200a0, +0xaf900124, 0xaf92011c, 0x8f42032c, 0x24420001, +0xaf42032c, 0x10000015, 0x8f42032c, 0x3c040001, +0x248464b8, 0x240202e2, 0xafa20010, 0xafa00014, +0x8f860144, 0x3c070001, 0x24e764c0, 0xc002b3b, +0x3405dead, 0x8f82011c, 0x34420002, 0xaf82011c, +0x8f820220, 0x34420004, 0xaf820220, 0x8f820140, +0x3c030001, 0x431025, 0xaf820140, 0x8fbf0024, +0x8fb20020, 0x8fb1001c, 0x8fb00018, 0x3e00008, +0x27bd0028, 0x6021, 0x5021, 0x3021, +0x2821, 0x6821, 0x4821, 0x7821, +0x7021, 0x8f880124, 0x8f870104, 0x1580002e, +0x8f8b011c, 0x11a00014, 0x31620800, 0x8f820120, +0x10460029, 0x0, 0x3c040001, 0x8c846ee4, +0x8cc20000, 0x8cc30004, 0xac820000, 0xac830004, +0x8cc20008, 0xac820008, 0x94c2000e, 0xa482000e, +0x8cc20010, 0x240c0001, 0xac820010, 0x8cc20014, +0x10000012, 0x24c60020, 0x10400017, 0x0, +0x3c040001, 0x8c846ee4, 0x8d020000, 0x8d030004, +0xac820000, 0xac830004, 0x8d020008, 0xac820008, +0x9502000e, 0xa482000e, 0x8d020010, 0x25060020, +0xac820010, 0x8d020014, 0x240c0001, 0xc01821, +0xac820014, 0x27624fe0, 0x43102b, 0x54400001, +0x27634800, 0x603021, 0x1540002f, 0x31620100, +0x11200014, 0x31628000, 0x8f820100, 0x1045002a, +0x31620100, 0x3c040001, 0x8c846ee0, 0x8ca20000, +0x8ca30004, 0xac820000, 0xac830004, 0x8ca20008, +0xac820008, 0x94a2000e, 0xa482000e, 0x8ca20010, +0x240a0001, 0xac820010, 0x8ca20014, 0x10000012, +0x24a50020, 0x10400018, 0x31620100, 0x3c040001, +0x8c846ee0, 0x8ce20000, 0x8ce30004, 0xac820000, +0xac830004, 0x8ce20008, 0xac820008, 0x94e2000e, +0xa482000e, 0x8ce20010, 0x24e50020, 0xac820010, +0x8ce20014, 0x240a0001, 0xa01821, 0xac820014, +0x276247e0, 0x43102b, 0x54400001, 0x27634000, +0x602821, 0x31620100, 0x5440001d, 0x31621000, +0x11a00009, 0x31a20800, 0x10400004, 0x25020020, +0x8f8200a8, 0xa5e20000, 0x25020020, 0xaf820124, +0x8f880124, 0x6821, 0x11800011, 0x31621000, +0x3c040001, 0x8c846ee4, 0x8c820000, 0x8c830004, +0xaf820080, 0xaf830084, 0x8c820008, 0xaf8200a4, +0x9482000e, 0xaf8200ac, 0x8c820010, 0x6021, +0xaf8200a0, 0x8c8d0010, 0x8c8f0014, 0x31621000, +0x1440ff82, 0x0, 0x1120000f, 0x31220800, +0x10400004, 0x3c020002, 0x8f8200b8, 0xa5c20000, +0x3c020002, 0x1221024, 0x10400004, 0x24e20020, +0x8f8200b4, 0xaf8200d4, 0x24e20020, 0xaf820104, +0x8f870104, 0x4821, 0x1140ff70, 0x0, +0x3c040001, 0x8c846ee0, 0x8c820000, 0x8c830004, +0xaf820090, 0xaf830094, 0x8c820008, 0xaf8200b4, +0x9482000e, 0xaf82009c, 0x8c820010, 0x5021, +0xaf8200b0, 0x8c890010, 0x1000ff60, 0x8c8e0014, +0x3e00008, 0x0, 0x6021, 0x5821, +0x3021, 0x2821, 0x6821, 0x5021, +0x7821, 0x7021, 0x8f880124, 0x8f870104, +0x3c180100, 0x1580002e, 0x8f89011c, 0x11a00014, +0x31220800, 0x8f820120, 0x10460029, 0x0, +0x3c040001, 0x8c846ee4, 0x8cc20000, 0x8cc30004, +0xac820000, 0xac830004, 0x8cc20008, 0xac820008, +0x94c2000e, 0xa482000e, 0x8cc20010, 0x240c0001, +0xac820010, 0x8cc20014, 0x10000012, 0x24c60020, +0x10400017, 0x0, 0x3c040001, 0x8c846ee4, +0x8d020000, 0x8d030004, 0xac820000, 0xac830004, +0x8d020008, 0xac820008, 0x9502000e, 0xa482000e, +0x8d020010, 0x25060020, 0xac820010, 0x8d020014, +0x240c0001, 0xc01821, 0xac820014, 0x27624fe0, +0x43102b, 0x54400001, 0x27634800, 0x603021, +0x1560002f, 0x31220100, 0x11400014, 0x31228000, +0x8f820100, 0x1045002a, 0x31220100, 0x3c040001, +0x8c846ee0, 0x8ca20000, 0x8ca30004, 0xac820000, +0xac830004, 0x8ca20008, 0xac820008, 0x94a2000e, +0xa482000e, 0x8ca20010, 0x240b0001, 0xac820010, +0x8ca20014, 0x10000012, 0x24a50020, 0x10400018, +0x31220100, 0x3c040001, 0x8c846ee0, 0x8ce20000, +0x8ce30004, 0xac820000, 0xac830004, 0x8ce20008, +0xac820008, 0x94e2000e, 0xa482000e, 0x8ce20010, +0x24e50020, 0xac820010, 0x8ce20014, 0x240b0001, +0xa01821, 0xac820014, 0x276247e0, 0x43102b, +0x54400001, 0x27634000, 0x602821, 0x31220100, +0x5440001d, 0x31221000, 0x11a00009, 0x31a20800, +0x10400004, 0x25020020, 0x8f8200a8, 0xa5e20000, +0x25020020, 0xaf820124, 0x8f880124, 0x6821, +0x11800011, 0x31221000, 0x3c040001, 0x8c846ee4, +0x8c820000, 0x8c830004, 0xaf820080, 0xaf830084, +0x8c820008, 0xaf8200a4, 0x9482000e, 0xaf8200ac, +0x8c820010, 0x6021, 0xaf8200a0, 0x8c8d0010, +0x8c8f0014, 0x31221000, 0x14400022, 0x0, +0x1140000f, 0x31420800, 0x10400004, 0x3c020002, +0x8f8200b8, 0xa5c20000, 0x3c020002, 0x1421024, +0x10400004, 0x24e20020, 0x8f8200b4, 0xaf8200d4, +0x24e20020, 0xaf820104, 0x8f870104, 0x5021, +0x11600010, 0x0, 0x3c040001, 0x8c846ee0, +0x8c820000, 0x8c830004, 0xaf820090, 0xaf830094, +0x8c820008, 0xaf8200b4, 0x9482000e, 0xaf82009c, +0x8c820010, 0x5821, 0xaf8200b0, 0x8c8a0010, +0x8c8e0014, 0x8f820070, 0x3c031000, 0x431024, +0x1040ff5c, 0x0, 0x8f820054, 0x24420005, +0xaf820078, 0x8c040234, 0x10800016, 0x1821, +0x3c020001, 0x571021, 0x8c4240e8, 0x24420005, +0x3c010001, 0x370821, 0xac2240e8, 0x3c020001, +0x571021, 0x8c4240e8, 0x44102b, 0x14400009, +0x24020001, 0x3c030080, 0x3c010001, 0x370821, +0xac2040e8, 0x3c010001, 0x370821, 0x1000000c, +0xa02240f0, 0x3c020001, 0x571021, 0x904240f0, +0x14400006, 0x3c020080, 0x3c020001, 0x571021, +0x904240f1, 0x10400002, 0x3c020080, 0x621825, +0x8c040230, 0x10800013, 0x0, 0x3c020001, +0x571021, 0x8c4240ec, 0x24420005, 0x3c010001, +0x370821, 0xac2240ec, 0x3c020001, 0x571021, +0x8c4240ec, 0x44102b, 0x14400006, 0x0, +0x3c010001, 0x370821, 0xac2040ec, 0x10000006, +0x781825, 0x3c020001, 0x571021, 0x904240f2, +0x54400001, 0x781825, 0x1060ff1a, 0x0, +0x8f420000, 0x10400007, 0x0, 0xaf80004c, +0x8f82004c, 0x1040fffd, 0x0, 0x10000005, +0x0, 0xaf800048, 0x8f820048, 0x1040fffd, +0x0, 0x8f820060, 0x431025, 0xaf820060, +0x8f420000, 0x10400003, 0x0, 0x1000ff05, +0xaf80004c, 0x1000ff03, 0xaf800048, 0x3e00008, +0x0, 0x0, 0x0, 0x3c020001, +0x8c426d28, 0x27bdffe8, 0xafbf0014, 0x14400012, +0xafb00010, 0x3c100001, 0x26106f90, 0x2002021, +0xc002ba8, 0x24052000, 0x26021fe0, 0x3c010001, +0xac226eec, 0x3c010001, 0xac226ee8, 0xac020250, +0x24022000, 0xac100254, 0xac020258, 0x24020001, +0x3c010001, 0xac226d28, 0x8fbf0014, 0x8fb00010, +0x3e00008, 0x27bd0018, 0x3c090001, 0x8d296eec, +0x8c820000, 0x8fa30010, 0x8fa80014, 0xad220000, +0x8c820004, 0xad250008, 0xad220004, 0x8f820054, +0xad260010, 0xad270014, 0xad230018, 0xad28001c, +0xad22000c, 0x2529ffe0, 0x3c020001, 0x24426f90, +0x122102b, 0x10400003, 0x0, 0x3c090001, +0x8d296ee8, 0x3c020001, 0x8c426d10, 0xad220000, +0x3c020001, 0x8c426d10, 0x3c010001, 0xac296eec, +0xad220004, 0xac090250, 0x3e00008, 0x0, +0x27bdffd0, 0xafb00010, 0x3c100001, 0x8e106eec, +0x3c020001, 0x8c426d10, 0xafb10014, 0x808821, +0xafbe0024, 0x8fbe0040, 0x8fa40048, 0xafb20018, +0xa09021, 0xafbf0028, 0xafb50020, 0xafb3001c, +0xae020000, 0x3c020001, 0x8c426d10, 0xc09821, +0xe0a821, 0x10800006, 0xae020004, 0x26050008, +0xc002bb3, 0x24060018, 0x10000005, 0x2610ffe0, +0x26040008, 0xc002ba8, 0x24050018, 0x2610ffe0, +0x3c030001, 0x24636f90, 0x203102b, 0x10400003, +0x0, 0x3c100001, 0x8e106ee8, 0x8e220000, +0xae020000, 0x8e220004, 0xae120008, 0xae020004, +0x8f820054, 0xae130010, 0xae150014, 0xae1e0018, +0x8fa80044, 0xae08001c, 0xae02000c, 0x2610ffe0, +0x203102b, 0x10400003, 0x0, 0x3c100001, +0x8e106ee8, 0x3c020001, 0x8c426d10, 0xae020000, +0x3c020001, 0x8c426d10, 0x3c010001, 0xac306eec, +0xae020004, 0xac100250, 0x8fbf0028, 0x8fbe0024, +0x8fb50020, 0x8fb3001c, 0x8fb20018, 0x8fb10014, +0x8fb00010, 0x3e00008, 0x27bd0030, 0x851821, +0x83102b, 0x10400006, 0x0, 0xac800000, +0x24840004, 0x83102b, 0x5440fffd, 0xac800000, +0x3e00008, 0x0, 0xa61821, 0xa3102b, +0x10400007, 0x0, 0x8c820000, 0xaca20000, +0x24a50004, 0xa3102b, 0x1440fffb, 0x24840004, +0x3e00008, 0x0, 0x861821, 0x83102b, +0x10400007, 0x0, 0x8ca20000, 0xac820000, +0x24840004, 0x83102b, 0x1440fffb, 0x24a50004, +0x3e00008, 0x0, 0x63080, 0x861821, +0x83102b, 0x10400006, 0x0, 0xac850000, +0x24840004, 0x83102b, 0x5440fffd, 0xac850000, +0x3e00008, 0x0, 0x0, 0x26e50028, +0xa03021, 0x274301c0, 0x8f4d0358, 0x8f47035c, +0x8f480360, 0x8f490364, 0x8f4a0368, 0x8f4b0204, +0x8f4c0200, 0x24640400, 0x64102b, 0x10400008, +0x3c0208ff, 0x8cc20000, 0xac620000, 0x24630004, +0x64102b, 0x1440fffb, 0x24c60004, 0x3c0208ff, +0x3442ffff, 0x3c03c0ff, 0xaf4d0358, 0xaf47035c, +0xaf480360, 0xaf490364, 0xaf4a0368, 0xaf4b0204, +0xaf4c0200, 0x8f840220, 0x3463ffff, 0x8f860200, +0x821024, 0x34420004, 0xc31824, 0x34630004, +0xaf820220, 0xaf830200, 0x8ca20214, 0xac020084, +0x8ca20218, 0xac020088, 0x8ca2021c, 0xac02008c, +0x8ca20220, 0xac020090, 0x8ca20224, 0xac020094, +0x8ca20228, 0xac020098, 0x8ca2022c, 0xac02009c, +0x8ca20230, 0xac0200a0, 0x8ca20234, 0xac0200a4, +0x8ca20238, 0xac0200a8, 0x8ca2023c, 0xac0200ac, +0x8ca20240, 0xac0200b0, 0x8ca20244, 0xac0200b4, +0x8ca20248, 0xac0200b8, 0x8ca2024c, 0xac0200bc, +0x8ca2001c, 0xac020080, 0x8ca20018, 0xac0200c0, +0x8ca20020, 0xac0200cc, 0x8ca20024, 0xac0200d0, +0x8ca201d0, 0xac0200e0, 0x8ca201d4, 0xac0200e4, +0x8ca201d8, 0xac0200e8, 0x8ca201dc, 0xac0200ec, +0x8ca201e0, 0xac0200f0, 0x8ca20098, 0x8ca3009c, +0xac0300fc, 0x8ca200a8, 0x8ca300ac, 0xac0300f4, +0x8ca200a0, 0x8ca300a4, 0x30840004, 0xac0300f8, +0x14800007, 0x30c20004, 0x8f820220, 0x3c0308ff, +0x3463fffb, 0x431024, 0xaf820220, 0x30c20004, +0x14400006, 0x0, 0x8f820200, 0x3c03c0ff, +0x3463fffb, 0x431024, 0xaf820200, 0x8f4202dc, +0xa34005c5, 0x24420001, 0xaf4202dc, 0x8f4202dc, +0x3e00008, 0x0, 0x27bdffd8, 0xafbf0024, +0xafb00020, 0x8f430024, 0x8f420020, 0x10620038, +0x0, 0x8f430020, 0x8f420024, 0x622023, +0x4810003, 0x0, 0x8f420040, 0x822021, +0x8f430030, 0x8f420024, 0x43102b, 0x14400005, +0x0, 0x8f430040, 0x8f420024, 0x10000005, +0x621023, 0x8f420030, 0x8f430024, 0x431023, +0x2442ffff, 0x406021, 0x8c102a, 0x54400001, +0x806021, 0x8f4a0024, 0x8f490040, 0x8f480024, +0x8f440180, 0x8f450184, 0x8f460024, 0x8f4b001c, +0x24070001, 0xafa70010, 0x84100, 0x1001821, +0x14c5021, 0x2529ffff, 0x1498024, 0xafb00014, +0x8f470014, 0x1021, 0x63100, 0xafa70018, +0xa32821, 0xa3382b, 0x822021, 0x872021, +0x8f420108, 0x1663021, 0x40f809, 0xc3900, +0x54400001, 0xaf500024, 0x8f430024, 0x8f420020, +0x14620018, 0x0, 0x8f420000, 0x10400007, +0x0, 0xaf80004c, 0x8f82004c, 0x1040fffd, +0x0, 0x10000005, 0x0, 0xaf800048, +0x8f820048, 0x1040fffd, 0x0, 0x8f820060, +0x2403ffef, 0x431024, 0xaf820060, 0x8f420000, +0x10400003, 0x0, 0x10000002, 0xaf80004c, +0xaf800048, 0x8fbf0024, 0x8fb00020, 0x3e00008, +0x27bd0028, 0x3e00008, 0x0, 0x27bdffc0, +0x32c20020, 0xafbf0038, 0xafb30034, 0xafb20030, +0xafb1002c, 0x10400004, 0xafb00028, 0x8f530028, +0x10000002, 0x0, 0x8f530020, 0x8f420030, +0x105300eb, 0x21100, 0x8f43001c, 0x628021, +0x8e040000, 0x8e050004, 0x96120008, 0x8f420090, +0x9611000a, 0x3246ffff, 0x46102a, 0x10400017, +0x0, 0x8f8200d8, 0x8f430098, 0x431023, +0x2442dcbe, 0xaf420090, 0x8f420090, 0x2842dcbf, +0x10400005, 0x0, 0x8f420090, 0x8f430144, +0x431021, 0xaf420090, 0x8f420090, 0x46102a, +0x10400006, 0x0, 0x8f420348, 0x24420001, +0xaf420348, 0x100000e1, 0x8f420348, 0x8f8200fc, +0x14400006, 0x0, 0x8f420344, 0x24420001, +0xaf420344, 0x100000d9, 0x8f420344, 0x934205c2, +0x1040000b, 0x32c20008, 0x10400008, 0x32220200, +0x10400006, 0x3c034000, 0x9602000e, 0xaf4300ac, +0x21400, 0x10000002, 0xaf4200b0, 0xaf4000ac, +0x32220004, 0x1040007f, 0x32220800, 0x10400003, +0x3247ffff, 0x10000002, 0x24020020, 0x24020004, +0xafa20010, 0x8f420030, 0xafa20014, 0x8f420010, +0x3c030002, 0x431025, 0xafa20018, 0x8f460098, +0x8f420108, 0x40f809, 0x0, 0x104000b7, +0x0, 0x8f42009c, 0x8f430094, 0x2421021, +0xaf42009c, 0xae03000c, 0x8f4200ac, 0x10400008, +0x3c034000, 0x8f420094, 0x431025, 0xafa20020, +0x8f42009c, 0x8f4300b0, 0x10000004, 0x431025, +0x8f420094, 0xafa20020, 0x8f42009c, 0xafa20024, +0x8f8200fc, 0x8fa30020, 0x8fa40024, 0xac430000, +0xac440004, 0x24420008, 0xaf8200f0, 0x8f42009c, +0x8f440270, 0x8f450274, 0x401821, 0x1021, +0xa32821, 0xa3302b, 0x822021, 0x862021, +0x32230060, 0x24020040, 0xaf440270, 0xaf450274, +0x10620017, 0x2c620041, 0x10400005, 0x24020020, +0x10620008, 0x24020001, 0x10000026, 0x0, +0x24020060, 0x10620019, 0x24020001, 0x10000021, +0x0, 0x8f420278, 0x8f43027c, 0x24630001, +0x2c640001, 0x441021, 0xaf420278, 0xaf43027c, +0x8f420278, 0x8f43027c, 0x10000016, 0x24020001, +0x8f420280, 0x8f430284, 0x24630001, 0x2c640001, +0x441021, 0xaf420280, 0xaf430284, 0x8f420280, +0x8f430284, 0x1000000b, 0x24020001, 0x8f420288, +0x8f43028c, 0x24630001, 0x2c640001, 0x441021, +0xaf420288, 0xaf43028c, 0x8f420288, 0x8f43028c, +0x24020001, 0xa34205c2, 0x8f420098, 0x3244ffff, +0x2406fff8, 0x8f45013c, 0x441021, 0x24420007, +0x461024, 0x24840007, 0xaf420094, 0x8f420090, +0x8f430094, 0x862024, 0x441023, 0x65182b, +0x14600005, 0xaf420090, 0x8f420094, 0x8f430144, +0x431023, 0xaf420094, 0x8f420094, 0x10000023, +0xaf40009c, 0x3247ffff, 0x50e00022, 0x32c20020, +0x14400002, 0x24020010, 0x24020002, 0xafa20010, +0x8f420030, 0xafa20014, 0x8f420010, 0xafa20018, +0x8f460098, 0x8f420108, 0x40f809, 0x0, +0x1040003a, 0x3245ffff, 0x8f420098, 0x8f430090, +0x8f46013c, 0x451021, 0xaf420098, 0x8f42009c, +0x8f440098, 0xa34005c2, 0x651823, 0xaf430090, +0x451021, 0x86202b, 0x14800005, 0xaf42009c, +0x8f420098, 0x8f430144, 0x431023, 0xaf420098, +0x32c20020, 0x10400005, 0x0, 0x8f420358, +0x2442ffff, 0xaf420358, 0x8f420358, 0x8f420030, +0x8f430040, 0x24420001, 0x2463ffff, 0x431024, +0xaf420030, 0x8f420030, 0x14530018, 0x0, +0x8f420000, 0x10400007, 0x0, 0xaf80004c, +0x8f82004c, 0x1040fffd, 0x0, 0x10000005, +0x0, 0xaf800048, 0x8f820048, 0x1040fffd, +0x0, 0x8f820060, 0x2403fff7, 0x431024, +0xaf820060, 0x8f420000, 0x10400003, 0x0, +0x10000002, 0xaf80004c, 0xaf800048, 0x8fbf0038, +0x8fb30034, 0x8fb20030, 0x8fb1002c, 0x8fb00028, +0x3e00008, 0x27bd0040, 0x3e00008, 0x0, +0x27bdffd0, 0x32c20020, 0xafbf002c, 0xafb20028, +0xafb10024, 0x10400004, 0xafb00020, 0x8f520028, +0x10000002, 0x0, 0x8f520020, 0x8f420030, +0x105200b5, 0x21100, 0x8f43001c, 0x628021, +0x8e040000, 0x8e050004, 0x96110008, 0x8f420090, +0x9607000a, 0x3226ffff, 0x46102a, 0x10400017, +0x0, 0x8f8200d8, 0x8f430098, 0x431023, +0x2442dc46, 0xaf420090, 0x8f420090, 0x2842dc47, +0x10400005, 0x0, 0x8f420090, 0x8f430144, +0x431021, 0xaf420090, 0x8f420090, 0x46102a, +0x10400006, 0x0, 0x8f420348, 0x24420001, +0xaf420348, 0x100000ab, 0x8f420348, 0x8f8600fc, +0x10c0000c, 0x0, 0x8f8200f4, 0x2403fff8, +0x431024, 0x461023, 0x218c3, 0x58600001, +0x24630100, 0x8f42008c, 0x43102b, 0x14400006, +0x712c2, 0x8f420344, 0x24420001, 0xaf420344, +0x10000098, 0x8f420344, 0x934305c2, 0x1060000f, +0x30460001, 0x8f420010, 0x34480400, 0x32c20008, +0x10400008, 0x30e20200, 0x10400006, 0x3c034000, +0x9602000e, 0xaf4300ac, 0x21400, 0x10000004, +0xaf4200b0, 0x10000002, 0xaf4000ac, 0x8f480010, +0x30e20004, 0x10400045, 0x3227ffff, 0x8f4900ac, +0x11200005, 0x30c200ff, 0x14400006, 0x24020040, +0x10000004, 0x24020008, 0x14400002, 0x24020020, +0x24020004, 0xafa20010, 0x8f430030, 0x11200004, +0xafa30014, 0x8f4200b0, 0x621025, 0xafa20014, +0x3c020002, 0x1021025, 0xafa20018, 0x8f460098, +0x8f420108, 0x40f809, 0x0, 0x10400069, +0x3224ffff, 0x8f42008c, 0x8f430094, 0x24420001, +0xaf42008c, 0x24020001, 0xae03000c, 0xa34205c2, +0x8f420098, 0x2406fff8, 0x8f45013c, 0x441021, +0x24420007, 0x461024, 0x24840007, 0xaf420094, +0x8f420090, 0x8f430094, 0x862024, 0x441023, +0x65182b, 0x14600005, 0xaf420090, 0x8f420094, +0x8f430144, 0x431023, 0xaf420094, 0x8f430094, +0x8f420140, 0x43102b, 0x10400009, 0x0, +0x8f43013c, 0x8f440094, 0x8f420090, 0x8f450138, +0x641823, 0x431023, 0xaf420090, 0xaf450094, +0x8f420094, 0x1000001f, 0xaf420098, 0x10e0001d, +0x30c200ff, 0x14400002, 0x24020010, 0x24020002, +0xafa20010, 0x8f420030, 0xafa80018, 0xafa20014, +0x8f460098, 0x8f420108, 0x40f809, 0x0, +0x10400030, 0x3225ffff, 0x8f420098, 0x8f44013c, +0x451021, 0xaf420098, 0x8f420090, 0x8f430098, +0xa34005c2, 0x451023, 0x64182b, 0x14600005, +0xaf420090, 0x8f420098, 0x8f430144, 0x431023, +0xaf420098, 0x8f420030, 0x8f430040, 0x24420001, +0x2463ffff, 0x431024, 0xaf420030, 0x8f420030, +0x14520018, 0x0, 0x8f420000, 0x10400007, +0x0, 0xaf80004c, 0x8f82004c, 0x1040fffd, +0x0, 0x10000005, 0x0, 0xaf800048, +0x8f820048, 0x1040fffd, 0x0, 0x8f820060, +0x2403fff7, 0x431024, 0xaf820060, 0x8f420000, +0x10400003, 0x0, 0x10000002, 0xaf80004c, +0xaf800048, 0x8fbf002c, 0x8fb20028, 0x8fb10024, +0x8fb00020, 0x3e00008, 0x27bd0030, 0x3e00008, +0x0, 0x27bdffd8, 0x3c020001, 0x34422ec0, +0xafbf0020, 0x8f4300f0, 0x8f840108, 0x2e21021, +0x54620004, 0x24620008, 0x3c020001, 0x34422cc0, +0x2e21021, 0x401821, 0xaf4300f0, 0xac600000, +0x8f4200ec, 0x8c660004, 0x14620004, 0x3c020001, +0x24820020, 0x1000000f, 0xaf820108, 0x8f4300f0, +0x34422ec0, 0x2e21021, 0x54620004, 0x24620008, +0x3c020001, 0x34422cc0, 0x2e21021, 0x401821, +0x8c620004, 0x21140, 0x821021, 0xaf820108, +0xac600000, 0x8c850018, 0x30a20036, 0x1040006c, +0x30a20001, 0x8c82001c, 0x8f430040, 0x8f440034, +0x24420001, 0x2463ffff, 0x431024, 0x862021, +0xaf42002c, 0x30a20030, 0x14400006, 0xaf440034, +0x8f420034, 0x8c03023c, 0x43102b, 0x144000b4, +0x0, 0x32c20010, 0x10400028, 0x24070008, +0x8f440170, 0x8f450174, 0x8f43002c, 0x8f48000c, +0x8f860120, 0x24020080, 0xafa20010, 0xafa30014, +0xafa80018, 0x8f42010c, 0x40f809, 0x24c6001c, +0x14400011, 0x24020001, 0x3c010001, 0x370821, +0xa02240f1, 0x8f820124, 0xafa20010, 0x8f820128, +0x3c040001, 0x248467c4, 0xafa20014, 0x8f46002c, +0x8f870120, 0x3c050009, 0xc002b3b, 0x34a51100, +0x10000036, 0x0, 0x8f420300, 0x8f43002c, +0x24420001, 0xaf420300, 0x8f420300, 0x24020001, +0xa34205c1, 0x10000026, 0xaf430038, 0x8f440170, +0x8f450174, 0x8f43002c, 0x8f48000c, 0x8f860120, +0x24020020, 0xafa20010, 0xafa30014, 0xafa80018, +0x8f42010c, 0x40f809, 0x24c6001c, 0x14400011, +0x24020001, 0x3c010001, 0x370821, 0xa02240f0, +0x8f820124, 0xafa20010, 0x8f820128, 0x3c040001, +0x248467b8, 0xafa20014, 0x8f46002c, 0x8f870120, +0x3c050009, 0xc002b3b, 0x34a50900, 0x1000000f, +0x0, 0x8f420300, 0x24420001, 0xaf420300, +0x8f420300, 0x8f42002c, 0xa34005c1, 0xaf420038, +0x3c010001, 0x370821, 0xa02040f1, 0x3c010001, +0x370821, 0xa02040f0, 0xaf400034, 0x8f420314, +0x24420001, 0xaf420314, 0x10000059, 0x8f420314, +0x10400022, 0x30a27000, 0x8c85001c, 0x8f420028, +0xa22023, 0x4810003, 0x0, 0x8f420040, +0x822021, 0x8f420358, 0x8f430000, 0xaf450028, +0x441021, 0x10600007, 0xaf420358, 0xaf80004c, +0x8f82004c, 0x1040fffd, 0x0, 0x10000005, +0x0, 0xaf800048, 0x8f820048, 0x1040fffd, +0x0, 0x8f820060, 0x34420008, 0xaf820060, +0x8f420000, 0x10400003, 0x0, 0x10000038, +0xaf80004c, 0x10000036, 0xaf800048, 0x1040002f, +0x30a21000, 0x1040000c, 0x30a24000, 0x8c83001c, +0x8f420050, 0x622023, 0x4820001, 0x24840200, +0x8f42035c, 0x441021, 0xaf42035c, 0x8f420368, +0x1000001a, 0xaf430050, 0x1040000c, 0x32c28000, +0x8c83001c, 0x8f420070, 0x622023, 0x4820001, +0x24840400, 0x8f420364, 0x441021, 0xaf420364, +0x8f420368, 0x1000000d, 0xaf430070, 0x1040000e, +0x3c020800, 0x8c83001c, 0x8f420060, 0x622023, +0x4820001, 0x24840100, 0x8f420360, 0x441021, +0xaf420360, 0x8f420368, 0xaf430060, 0x441021, +0xaf420368, 0x3c020800, 0x2c21024, 0x50400008, +0x36940040, 0x10000006, 0x0, 0x30a20100, +0x10400003, 0x0, 0xc002bd8, 0x0, +0x8fbf0020, 0x3e00008, 0x27bd0028, 0x3e00008, +0x0, 0x27bdffa8, 0xafbf0050, 0xafbe004c, +0xafb50048, 0xafb30044, 0xafb20040, 0xafb1003c, +0xafb00038, 0x8f910108, 0x26220020, 0xaf820108, +0x8e320018, 0xa821, 0x32420024, 0x104001ba, +0xf021, 0x8e26001c, 0x8f43001c, 0x61100, +0x621821, 0x8c70000c, 0x9604000c, 0x962d0016, +0x9473000a, 0x2c8305dd, 0x38828870, 0x2c420001, +0x621825, 0x10600015, 0x2821, 0x32c20040, +0x10400015, 0x24020800, 0x96030014, 0x14620012, +0x3402aaaa, 0x9603000e, 0x14620007, 0x2021, +0x96030010, 0x24020300, 0x14620004, 0x801021, +0x96020012, 0x2c440001, 0x801021, 0x54400006, +0x24050016, 0x10000004, 0x0, 0x24020800, +0x50820001, 0x2405000e, 0x934205c3, 0x14400008, +0x5821, 0x240b0001, 0x32620180, 0xaf4500a8, +0xaf5000a0, 0x10400002, 0xaf4600a4, 0xa34b05c3, +0x10a00085, 0x2054021, 0x91020000, 0x3821, +0x3042000f, 0x25080, 0x32c20002, 0x10400012, +0x10a1821, 0x32620002, 0x10400010, 0x32c20001, +0x1002021, 0x94820000, 0x24840002, 0xe23821, +0x83102b, 0x1440fffb, 0x30e2ffff, 0x71c02, +0x623821, 0x71c02, 0x30e2ffff, 0x623821, +0x71027, 0xa502000a, 0x32c20001, 0x1040006a, +0x32620001, 0x10400068, 0x0, 0x8f4200a8, +0x10400065, 0x0, 0x8f4200a0, 0x8f4300a8, +0x431021, 0x904c0009, 0x318900ff, 0x39230006, +0x3182b, 0x39220011, 0x2102b, 0x621824, +0x1060000c, 0x3c050006, 0x8f4200a4, 0x3c040001, +0x248467d4, 0xafa20010, 0x8f4200a0, 0x34a54600, +0x1203821, 0xc002b3b, 0xafa20014, 0x1000004e, +0x0, 0x32c20004, 0x14400013, 0x2821, +0x316200ff, 0x14400004, 0x0, 0x95020002, +0x1000000d, 0x4a2823, 0x9505000c, 0x9502000e, +0x95030010, 0xa22821, 0xa32821, 0x95030012, +0x91040009, 0x95020002, 0xa32821, 0xa42821, +0x4a1023, 0xa22821, 0x2002021, 0x94820000, +0x24840002, 0xe23821, 0x88102b, 0x1440fffb, +0x71c02, 0x30e2ffff, 0x623821, 0x71c02, +0x30e2ffff, 0x623821, 0x1a52821, 0x51c02, +0x30a2ffff, 0x622821, 0x51c02, 0x30a2ffff, +0x622821, 0xa72823, 0x51402, 0xa22821, +0x30a5ffff, 0x50a00001, 0x3405ffff, 0x316200ff, +0x14400008, 0x318300ff, 0x8f4300a0, 0x8f4200a8, +0x624021, 0x91020000, 0x3042000f, 0x25080, +0x318300ff, 0x24020006, 0x14620003, 0x10a1021, +0x10000002, 0x24440010, 0x24440006, 0x316200ff, +0x14400006, 0x0, 0x94820000, 0xa22821, +0x51c02, 0x30a2ffff, 0x622821, 0x934205c3, +0x10400003, 0x32620100, 0x50400003, 0xa4850000, +0x52827, 0xa4850000, 0x9622000e, 0x8f43009c, +0x621821, 0x32a200ff, 0x10400007, 0xaf43009c, +0x3c024000, 0x2021025, 0xafa20020, 0x8f42009c, +0x10000003, 0x5e1025, 0xafb00020, 0x8f42009c, +0xafa20024, 0x32620080, 0x10400010, 0x32620100, +0x8f4200b4, 0x24430001, 0x210c0, 0x571021, +0xaf4300b4, 0x8fa30020, 0x8fa40024, 0x3c010001, +0x220821, 0xac2338e8, 0x3c010001, 0x220821, +0xac2438ec, 0x100000a5, 0x32c20020, 0x10400064, +0x0, 0x8f4200b4, 0x24430001, 0x210c0, +0x571021, 0xaf4300b4, 0x8fa30020, 0x8fa40024, +0x3c010001, 0x220821, 0xac2338e8, 0x3c010001, +0x220821, 0xac2438ec, 0x8f4200b4, 0x10400051, +0x3821, 0x3c090001, 0x352938e8, 0x3c08001f, +0x3508ffff, 0x240bffff, 0x340affff, 0x710c0, +0x571021, 0x491021, 0x8c430000, 0x8c440004, +0xafa30028, 0xafa4002c, 0x8f8200fc, 0x8fa30028, +0x8fa4002c, 0xac430000, 0xac440004, 0x24420008, +0xaf8200f0, 0x8f42008c, 0x2442ffff, 0xaf42008c, +0x97a2002e, 0x8f440270, 0x8f450274, 0x401821, +0x1021, 0xa32821, 0xa3302b, 0x822021, +0x862021, 0xaf440270, 0xaf450274, 0x8fa20028, +0x481024, 0x90430000, 0x30630001, 0x1460000b, +0x402021, 0x8f420278, 0x8f43027c, 0x24630001, +0x2c640001, 0x441021, 0xaf420278, 0xaf43027c, +0x8f420278, 0x1000001a, 0x8f43027c, 0x8c820000, +0x144b000e, 0x0, 0x94820004, 0x144a000b, +0x0, 0x8f420288, 0x8f43028c, 0x24630001, +0x2c640001, 0x441021, 0xaf420288, 0xaf43028c, +0x8f420288, 0x1000000a, 0x8f43028c, 0x8f420280, +0x8f430284, 0x24630001, 0x2c640001, 0x441021, +0xaf420280, 0xaf430284, 0x8f420280, 0x8f430284, +0x8f4200b4, 0x24e70001, 0xe2102b, 0x1440ffb8, +0x710c0, 0xa34005c3, 0x1000003f, 0xaf4000b4, +0x8f8200fc, 0x8fa30020, 0x8fa40024, 0xac430000, +0xac440004, 0x24420008, 0xaf8200f0, 0x8f42009c, +0x8f46008c, 0x8f440270, 0x8f450274, 0x401821, +0x1021, 0x24c6ffff, 0xaf46008c, 0xa32821, +0xa3302b, 0x822021, 0x862021, 0xaf440270, +0xaf450274, 0x92020000, 0x30420001, 0x1440000c, +0x2402ffff, 0x8f420278, 0x8f43027c, 0x24630001, +0x2c640001, 0x441021, 0xaf420278, 0xaf43027c, +0x8f420278, 0x8f43027c, 0x1000001c, 0x32c20020, +0x8e030000, 0x1462000f, 0x3402ffff, 0x96030004, +0x1462000c, 0x0, 0x8f420288, 0x8f43028c, +0x24630001, 0x2c640001, 0x441021, 0xaf420288, +0xaf43028c, 0x8f420288, 0x8f43028c, 0x1000000b, +0x32c20020, 0x8f420280, 0x8f430284, 0x24630001, +0x2c640001, 0x441021, 0xaf420280, 0xaf430284, +0x8f420280, 0x8f430284, 0x32c20020, 0x10400005, +0xaf40009c, 0x8f420358, 0x2442ffff, 0xaf420358, +0x8f420358, 0x8e22001c, 0x8f430040, 0x24420001, +0x2463ffff, 0x431024, 0xaf42002c, 0x32420060, +0x14400008, 0x32c20010, 0x8f420034, 0x24420001, +0xaf420034, 0x8c03023c, 0x43102b, 0x14400102, +0x32c20010, 0x10400018, 0x24070008, 0x8f440170, +0x8f450174, 0x8f43002c, 0x8f48000c, 0x8f860120, +0x24020080, 0xafa20010, 0xafa30014, 0xafa80018, +0x8f42010c, 0x40f809, 0x24c6001c, 0x10400047, +0x24020001, 0x8f420300, 0x8f43002c, 0x24420001, +0xaf420300, 0x8f420300, 0x24020001, 0xa34205c1, +0x1000007c, 0xaf430038, 0x8f440170, 0x8f450174, +0x8f43002c, 0x8f48000c, 0x8f860120, 0x24020020, +0xafa20010, 0xafa30014, 0xafa80018, 0x8f42010c, +0x40f809, 0x24c6001c, 0x10400057, 0x24020001, +0x10000065, 0x0, 0x32420012, 0x10400075, +0x32420001, 0x9622000e, 0x8f43009c, 0x621821, +0x32c20020, 0x10400005, 0xaf43009c, 0x8f420358, +0x2442ffff, 0xaf420358, 0x8f420358, 0x8e22001c, +0x8f430040, 0x24420001, 0x2463ffff, 0x431024, +0xaf42002c, 0x32420010, 0x14400008, 0x32c20010, +0x8f420034, 0x24420001, 0xaf420034, 0x8c03023c, +0x43102b, 0x144000bc, 0x32c20010, 0x10400028, +0x24070008, 0x8f440170, 0x8f450174, 0x8f43002c, +0x8f48000c, 0x8f860120, 0x24020080, 0xafa20010, +0xafa30014, 0xafa80018, 0x8f42010c, 0x40f809, +0x24c6001c, 0x14400011, 0x24020001, 0x3c010001, +0x370821, 0xa02240f1, 0x8f820124, 0xafa20010, +0x8f820128, 0x3c040001, 0x248467c4, 0xafa20014, +0x8f46002c, 0x8f870120, 0x3c050009, 0xc002b3b, +0x34a51100, 0x10000036, 0x0, 0x8f420300, +0x8f43002c, 0x24420001, 0xaf420300, 0x8f420300, +0x24020001, 0xa34205c1, 0x10000026, 0xaf430038, +0x8f440170, 0x8f450174, 0x8f43002c, 0x8f48000c, +0x8f860120, 0x24020020, 0xafa20010, 0xafa30014, +0xafa80018, 0x8f42010c, 0x40f809, 0x24c6001c, +0x14400011, 0x24020001, 0x3c010001, 0x370821, +0xa02240f0, 0x8f820124, 0xafa20010, 0x8f820128, +0x3c040001, 0x248467b8, 0xafa20014, 0x8f46002c, +0x8f870120, 0x3c050009, 0xc002b3b, 0x34a50900, +0x1000000f, 0x0, 0x8f420300, 0x24420001, +0xaf420300, 0x8f420300, 0x8f42002c, 0xa34005c1, +0xaf420038, 0x3c010001, 0x370821, 0xa02040f1, +0x3c010001, 0x370821, 0xa02040f0, 0xaf400034, +0x8f420314, 0x24420001, 0xaf420314, 0x10000062, +0x8f420314, 0x10400022, 0x32427000, 0x8e25001c, +0x8f420028, 0xa22023, 0x4810003, 0x0, +0x8f420040, 0x822021, 0x8f420358, 0x8f430000, +0xaf450028, 0x441021, 0x10600007, 0xaf420358, +0xaf80004c, 0x8f82004c, 0x1040fffd, 0x0, +0x10000005, 0x0, 0xaf800048, 0x8f820048, +0x1040fffd, 0x0, 0x8f820060, 0x34420008, +0xaf820060, 0x8f420000, 0x10400003, 0x0, +0x10000041, 0xaf80004c, 0x1000003f, 0xaf800048, +0x1040002f, 0x32421000, 0x1040000c, 0x32424000, +0x8e23001c, 0x8f420050, 0x622023, 0x4820001, +0x24840200, 0x8f42035c, 0x441021, 0xaf42035c, +0x8f420368, 0x1000001a, 0xaf430050, 0x1040000c, +0x32c28000, 0x8e23001c, 0x8f420070, 0x622023, +0x4820001, 0x24840400, 0x8f420364, 0x441021, +0xaf420364, 0x8f420368, 0x1000000d, 0xaf430070, +0x1040000e, 0x3c020800, 0x8e23001c, 0x8f420060, +0x622023, 0x4820001, 0x24840100, 0x8f420360, +0x441021, 0xaf420360, 0x8f420368, 0xaf430060, +0x441021, 0xaf420368, 0x3c020800, 0x2c21024, +0x50400011, 0x36940040, 0x1000000f, 0x0, +0x32420048, 0x10400007, 0x24150001, 0x8e22001c, +0x3c03ffff, 0x43f024, 0x3042ffff, 0x1000fd75, +0xae22001c, 0x32420100, 0x10400003, 0x0, +0xc002bd8, 0x0, 0x8fbf0050, 0x8fbe004c, +0x8fb50048, 0x8fb30044, 0x8fb20040, 0x8fb1003c, +0x8fb00038, 0x3e00008, 0x27bd0058, 0x3e00008, +0x0, 0x0, 0x0, 0x8f8300e4, +0x8f8200e0, 0x2404fff8, 0x441024, 0x621026, +0x2102b, 0x21023, 0x3e00008, 0x621024, +0x3e00008, 0x0, 0x27bdffe0, 0xafbf001c, +0xafb00018, 0x8f8600c4, 0x8f8400e0, 0x8f8500e4, +0x2402fff8, 0x821824, 0x10a30009, 0x27623ff8, +0x14a20002, 0x24a20008, 0x27623000, 0x408021, +0x16030005, 0x30820004, 0x10400004, 0xc02021, +0x10000022, 0x1021, 0x8e040000, 0x8f42011c, +0x14a20003, 0x0, 0x8f420120, 0xaf420114, +0x8ca30000, 0x8f420148, 0x831823, 0x43102b, +0x10400003, 0x0, 0x8f420148, 0x621821, +0x94a20006, 0x24420050, 0x62102b, 0x1440000f, +0xa01021, 0xafa40010, 0xafa30014, 0x8ca60000, +0x8ca70004, 0x3c040001, 0xc002b3b, 0x24846894, +0x8f42020c, 0x24420001, 0xaf42020c, 0x8f42020c, +0x1021, 0xaf9000e8, 0xaf9000e4, 0x8fbf001c, +0x8fb00018, 0x3e00008, 0x27bd0020, 0x3e00008, +0x0, 0x8f8400e0, 0x8f8800c4, 0x8f8300e8, +0x2402fff8, 0x823824, 0xe32023, 0x2c821000, +0x50400001, 0x24841000, 0x420c2, 0x801821, +0x8f440258, 0x8f45025c, 0x1021, 0xa32821, +0xa3302b, 0x822021, 0x862021, 0xaf440258, +0xaf45025c, 0x8f8300c8, 0x8f420148, 0x1032023, +0x82102b, 0x14400004, 0x801821, 0x8f420148, +0x822021, 0x801821, 0x8f440250, 0x8f450254, +0x1021, 0xa32821, 0xa3302b, 0x822021, +0x862021, 0xaf440250, 0xaf450254, 0xaf8800c8, +0xaf8700e4, 0xaf8700e8, 0x3e00008, 0x0, +0x27bdff30, 0x240a0001, 0xafbf00c8, 0xafbe00c4, +0xafb500c0, 0xafb300bc, 0xafb200b8, 0xafb100b4, +0xafb000b0, 0xa3a00097, 0xafa00044, 0xafaa005c, +0x934205c4, 0xa7a0008e, 0x1040000a, 0xa7a00086, +0x8f4b00c4, 0xafab0064, 0x8f4a00c0, 0xafaa006c, +0x8f4b00cc, 0xafab0074, 0x8f4a00c8, 0x10000129, +0xafaa007c, 0x8f420114, 0x40f809, 0x0, +0x403021, 0x10c0034f, 0x0, 0x8cc20000, +0x8cc30004, 0xafa20020, 0xafa30024, 0x8fab0024, +0x8faa0020, 0x3162ffff, 0x2442fffc, 0xafa2006c, +0x3c020006, 0x2c21024, 0xafab007c, 0x14400015, +0xafaa0064, 0x91420000, 0x30420001, 0x10400011, +0x2402ffff, 0x8d430000, 0x14620004, 0x3402ffff, +0x95430004, 0x1062000b, 0x0, 0xc0024bb, +0x8fa40064, 0x304200ff, 0x14400006, 0x0, +0x8f420118, 0x40f809, 0x0, 0x1000032d, +0x0, 0x8fa20024, 0x3c03ffbf, 0x3463ffff, +0x431024, 0x3c03ffff, 0x431824, 0x14600003, +0xafa20024, 0x10000040, 0x1821, 0x3c020080, +0x621024, 0x10400007, 0x0, 0x8f42038c, +0x24420001, 0xaf42038c, 0x8f42038c, 0x10000036, +0x24030001, 0x8f420210, 0x24420001, 0xaf420210, +0x8f420210, 0x3c020001, 0x621024, 0x10400006, +0x3c020002, 0x8f4201c4, 0x24420001, 0xaf4201c4, +0x8f4201c4, 0x3c020002, 0x621024, 0x10400006, +0x3c020004, 0x8f42037c, 0x24420001, 0xaf42037c, +0x8f42037c, 0x3c020004, 0x621024, 0x10400006, +0x3c020008, 0x8f420380, 0x24420001, 0xaf420380, +0x8f420380, 0x3c020008, 0x621024, 0x10400006, +0x3c020010, 0x8f420384, 0x24420001, 0xaf420384, +0x8f420384, 0x3c020010, 0x621024, 0x10400006, +0x3c020020, 0x8f4201c0, 0x24420001, 0xaf4201c0, +0x8f4201c0, 0x3c020020, 0x621024, 0x10400006, +0x24030001, 0x8f420388, 0x24420001, 0xaf420388, +0x8f420388, 0x24030001, 0x8c020260, 0x8fab006c, +0x4b102b, 0x10400014, 0x307000ff, 0x8f4201e8, +0x24420001, 0xaf4201e8, 0x8f4201e8, 0x8faa007c, +0x8f8200e0, 0x354a0100, 0xafaa007c, 0xafa20010, +0x8f8200e4, 0x24100001, 0x3c040001, 0x248468a0, +0xafa20014, 0x8fa60020, 0x8fa70024, 0x3c050007, +0xc002b3b, 0x34a50800, 0x12000010, 0x3c020080, +0x2c21024, 0x1440000e, 0x32c20400, 0x8fab007c, +0x3c020080, 0x34420100, 0x1621024, 0x10400005, +0x0, 0x8f42020c, 0x24420001, 0xaf42020c, +0x8f42020c, 0x100002b0, 0x8fa3006c, 0x32c20400, +0x10400015, 0x34028100, 0x8faa0064, 0x9543000c, +0x14620012, 0x3c020100, 0x240b0200, 0xa7ab008e, +0x9542000e, 0x8d430008, 0x8d440004, 0x8d450000, +0x8faa006c, 0x8fab0064, 0x254afffc, 0xafaa006c, +0xa7a20086, 0xad63000c, 0xad640008, 0xad650004, +0x256b0004, 0xafab0064, 0x3c020100, 0x2c21024, +0x10400004, 0x0, 0x8faa006c, 0x254a0004, +0xafaa006c, 0x8f4200bc, 0x5040000a, 0xafa00074, +0x8fab006c, 0x4b102b, 0x50400006, 0xafa00074, +0x8f4200bc, 0x1621023, 0xafa20074, 0x8f4a00bc, +0xafaa006c, 0x8f420080, 0x8fab006c, 0x4b102b, +0x10400056, 0x32c28000, 0x1040005e, 0x240a0003, +0x32c21000, 0x1040005b, 0xafaa005c, 0x10000058, +0x240b0004, 0x8f420350, 0x2403ffbf, 0x283a024, +0x24420001, 0xaf420350, 0x1000024f, 0x8f420350, +0x2c2b025, 0x2402ffbf, 0x282a024, 0x8f830128, +0x3c040001, 0x248468d0, 0x26620001, 0xafa20014, +0xafa30010, 0x8f860120, 0x8f870124, 0x3c050007, +0xc002b3b, 0x34a52250, 0x1000023f, 0x0, +0x2c2b025, 0x2402ffbf, 0x282a024, 0x8f830128, +0x3c040001, 0x248468d0, 0x24020002, 0xafa20014, +0xafa30010, 0x8f860120, 0x8f870124, 0x3c050007, +0xc002b3b, 0x34a52450, 0x1000022f, 0x0, +0x8ea20000, 0x8ea30004, 0x3c040001, 0x248468e8, +0xafb00010, 0xafbe0014, 0x8ea70018, 0x34a52800, +0xc002b3b, 0x603021, 0x10000223, 0x0, +0xa6b1000a, 0x8f820124, 0x3c040001, 0x248468f0, +0xafbe0014, 0xafa20010, 0x8f460044, 0x8f870120, +0x3c050007, 0xc002b3b, 0x34a53000, 0x10000216, +0x0, 0xa6b1000a, 0xa6b2000e, 0x8f820124, +0x3c040001, 0x248468fc, 0xafbe0014, 0xafa20010, +0x8f460044, 0x8f870120, 0x3c050007, 0xc002b3b, +0x34a53200, 0x10000208, 0x0, 0x8f420084, +0x8faa006c, 0x4a102b, 0x14400007, 0x3c020001, +0x2c21024, 0x10400004, 0x0, 0x240b0002, +0xafab005c, 0x8faa006c, 0x1140021b, 0x27ab0020, +0xafab00a4, 0x3c0a001f, 0x354affff, 0xafaa009c, +0x8fab005c, 0x240a0001, 0x556a0021, 0x240a0002, +0x8f430054, 0x8f420050, 0x1062000b, 0x274b0054, +0x8f5e0054, 0x3403ecc0, 0xafab004c, 0x27c20001, +0x304201ff, 0xafa20054, 0x1e1140, 0x431021, +0x1000006b, 0x2e2a821, 0x8f420044, 0x8faa006c, +0x3c040001, 0x248468ac, 0xafaa0014, 0xafa20010, +0x8f460054, 0x8f470050, 0x3c050007, 0xc002b3b, +0x34a51300, 0x8f430350, 0x2402ffbf, 0x282a024, +0x24630001, 0xaf430350, 0x100001d3, 0x8f420350, +0x156a001d, 0x0, 0x8f430074, 0x8f420070, +0x1062000a, 0x274b0074, 0x8f5e0074, 0xafab004c, +0x27c20001, 0x304203ff, 0xafa20054, 0x1e1140, +0x24426cc0, 0x1000004a, 0x2e2a821, 0x8f420044, +0x8faa006c, 0x3c040001, 0x248468b8, 0x3c050007, +0xafaa0014, 0xafa20010, 0x8f460074, 0x8f470070, +0x34a51500, 0x240b0001, 0xc002b3b, 0xafab005c, +0x1000ffc3, 0x0, 0x8f430064, 0x8f420060, +0x1062001a, 0x274a0064, 0x8f5e0064, 0x8fab005c, +0xafaa004c, 0x27c20001, 0x304200ff, 0xafa20054, +0x24020004, 0x1562000e, 0x1e1140, 0x1e1180, +0x24420cc0, 0x2e21021, 0xafa20044, 0x9442002a, +0x8faa0044, 0x8fab006c, 0x4b102b, 0x10400024, +0x25550020, 0x240a0001, 0x10000021, 0xa3aa0097, +0x24424cc0, 0x1000001e, 0x2e2a821, 0x8f420044, +0x8fab006c, 0x3c040001, 0x248468c4, 0xafab0014, +0xafa20010, 0x8f460064, 0x8f470060, 0x3c050007, +0xc002b3b, 0x34a51800, 0x3c020008, 0x2c21024, +0x1440ff34, 0x0, 0x8f420370, 0x240a0001, +0xafaa005c, 0x24420001, 0xaf420370, 0x1000ff90, +0x8f420370, 0x27a30036, 0x131040, 0x621821, +0x94620000, 0x441021, 0x10000020, 0xa4620000, +0x8fab0064, 0xaeab0018, 0x93a20097, 0x10400072, +0x9821, 0x8faa0044, 0x8fa4006c, 0x8fa300a4, +0x25420020, 0xafa20028, 0x25420008, 0xafa20030, +0x25420010, 0xafaa002c, 0xafa20034, 0x9542002a, +0xa7a20038, 0x95420018, 0xa7a2003a, 0x9542001a, +0xa7a2003c, 0x9542001c, 0xa7a2003e, 0x94620018, +0x24630002, 0x822023, 0x1880ffde, 0x26730001, +0x2e620004, 0x1440fff9, 0x0, 0x8f4200fc, +0x26650001, 0xa2102a, 0x1440002b, 0x24030001, +0x8f83012c, 0x10600023, 0x0, 0x8f820124, +0x431023, 0x22143, 0x58800001, 0x24840040, +0x8f820128, 0x431023, 0x21943, 0x58600001, +0x24630040, 0x64102a, 0x54400001, 0x602021, +0xaf4400fc, 0x8f4200fc, 0xa2102a, 0x10400011, +0x24030001, 0x10000015, 0x306200ff, 0x8fab0064, +0x96070018, 0xafab0010, 0x8e220008, 0x3c040001, +0x248468dc, 0x8c430004, 0x8c420000, 0x34a52400, +0x2403021, 0xc002b3b, 0xafa30014, 0x1000002b, +0x0, 0x8f420334, 0x1821, 0x24420001, +0xaf420334, 0x8f420334, 0x306200ff, 0x5040fedc, +0x3c020800, 0x12600021, 0x9021, 0x8fb100a4, +0x2208021, 0x8e220008, 0x96070018, 0x8fa60064, +0x8c440000, 0x8c450004, 0x240a0001, 0xafaa0010, +0xafbe0014, 0x8f420008, 0xafa20018, 0x8f42010c, +0x40f809, 0x0, 0x1040ffd8, 0x3c050007, +0x96020018, 0x8fab0064, 0x8faa009c, 0x1625821, +0x14b102b, 0x10400004, 0xafab0064, 0x8f420148, +0x1625823, 0xafab0064, 0x26100002, 0x26520001, +0x253102b, 0x1440ffe3, 0x26310004, 0x8fb0006c, +0x10000036, 0x97b10038, 0x8f4200fc, 0x24050002, +0xa2102a, 0x1440001b, 0x24030001, 0x8f83012c, +0x10600013, 0x0, 0x8f820124, 0x431023, +0x22143, 0x58800001, 0x24840040, 0x8f820128, +0x431023, 0x21943, 0x58600001, 0x24630040, +0x64102a, 0x54400001, 0x602021, 0xaf4400fc, +0x8f4200fc, 0xa2102a, 0x14400006, 0x24030001, +0x8f420334, 0x1821, 0x24420001, 0xaf420334, +0x8f420334, 0x306200ff, 0x1040fea5, 0x3c020800, +0x96b1000a, 0x8fb0006c, 0x3223ffff, 0x70102b, +0x54400001, 0x608021, 0x8ea40000, 0x8ea50004, +0x240b0001, 0xafab0010, 0xafbe0014, 0x8f420008, +0x8fa60064, 0xafa20018, 0x8f42010c, 0x40f809, +0x2003821, 0x1040fea2, 0x3c050007, 0x96a3000e, +0x97aa008e, 0x11400007, 0x609021, 0x934205c4, +0x14400004, 0x0, 0x97ab0086, 0x6a1825, +0xa6ab0016, 0x8faa007c, 0x3c02ffff, 0x1421024, +0x10400003, 0xa1402, 0x34630400, 0xa6a20014, +0x8fab006c, 0x560b0072, 0xa6a3000e, 0x34620004, +0xa6a2000e, 0x8faa0074, 0x16a1021, 0xa6a2000a, +0x8f430044, 0x8f4401a0, 0x8f4501a4, 0x34028000, +0xafa20010, 0x8f420044, 0x2a03021, 0x24070020, +0xafa20014, 0x8f42000c, 0x31940, 0x604821, +0xafa20018, 0x8f42010c, 0x4021, 0xa92821, +0xa9182b, 0x882021, 0x40f809, 0x832021, +0x5040fe7f, 0xa6b2000e, 0x8f420368, 0xafa0006c, +0xa34005c4, 0x2442ffff, 0xaf420368, 0x8fab005c, +0x240a0001, 0x8f420368, 0x156a0006, 0x240a0002, +0x8f42035c, 0x2442ffff, 0xaf42035c, 0x1000000c, +0x8f42035c, 0x156a0006, 0x0, 0x8f420364, +0x2442ffff, 0xaf420364, 0x10000005, 0x8f420364, +0x8f420360, 0x2442ffff, 0xaf420360, 0x8f420360, +0x8faa0054, 0x8fab004c, 0xad6a0000, 0x8f420044, +0x8f440088, 0x8f430078, 0x24420001, 0x441024, +0x24630001, 0xaf420044, 0xaf430078, 0x8c020240, +0x62182b, 0x14600075, 0x24070008, 0x8f440168, +0x8f45016c, 0x8f430044, 0x8f48000c, 0x8f860120, +0x24020040, 0xafa20010, 0xafa30014, 0xafa80018, +0x8f42010c, 0x40f809, 0x24c6001c, 0x14400011, +0x240b0001, 0x3c010001, 0x370821, 0xa02b40f2, +0x8f820124, 0xafa20010, 0x8f820128, 0x3c040001, +0x2484688c, 0xafa20014, 0x8f460044, 0x8f870120, +0x3c050009, 0xc002b3b, 0x34a51300, 0x1000000b, +0x0, 0x8f420304, 0x24420001, 0xaf420304, +0x8f420304, 0x8f420044, 0xaf42007c, 0x3c010001, +0x370821, 0xa02040f2, 0xaf400078, 0x8f420318, +0x24420001, 0xaf420318, 0x10000048, 0x8f420318, +0xa6b0000a, 0x8f430044, 0x8f4401a0, 0x8f4501a4, +0x34028000, 0xafa20010, 0x8f420044, 0x2a03021, +0x24070020, 0xafa20014, 0x8f42000c, 0x31940, +0x604821, 0xafa20018, 0x8f42010c, 0x4021, +0xa92821, 0xa9182b, 0x882021, 0x40f809, +0x832021, 0x1040fe1f, 0x240a0001, 0xa34a05c4, +0x8fab006c, 0x8faa0064, 0x1705823, 0xafab006c, +0x8fab009c, 0x1505021, 0x16a102b, 0x10400004, +0xafaa0064, 0x8f420148, 0x1425023, 0xafaa0064, +0x8f420368, 0x2442ffff, 0xaf420368, 0x8faa005c, +0x240b0001, 0x8f420368, 0x154b0006, 0x240b0002, +0x8f42035c, 0x2442ffff, 0xaf42035c, 0x1000000c, +0x8f42035c, 0x114b0006, 0x0, 0x8f420360, +0x2442ffff, 0xaf420360, 0x10000005, 0x8f420360, +0x8f420364, 0x2442ffff, 0xaf420364, 0x8f420364, +0x8fab0054, 0x8faa004c, 0xad4b0000, 0x8f420044, +0x8f440088, 0x8f430078, 0x24420001, 0x441024, +0x24630001, 0xaf420044, 0xaf430078, 0x8faa006c, +0x1540fe0b, 0x0, 0x8fab006c, 0x1160001e, +0x0, 0x934205c4, 0x10400009, 0x0, +0x8faa0064, 0xaf4a00c4, 0xaf4b00c0, 0x8fab007c, +0xaf4b00c8, 0x8faa0074, 0x1000000e, 0xaf4a00cc, +0x97ab008e, 0x1160000b, 0x34038100, 0x8fa20020, +0x8c46000c, 0xa443000c, 0x97aa0086, 0x8c440004, +0x8c450008, 0xa44a000e, 0xac440000, 0xac450004, +0xac460008, 0x8f42034c, 0x24420001, 0xaf42034c, +0x10000010, 0x8f42034c, 0x8fab007c, 0x3164ffff, +0x2484fffc, 0x801821, 0x8f440250, 0x8f450254, +0x8f460118, 0x1021, 0xa32821, 0xa3382b, +0x822021, 0x872021, 0xaf440250, 0xc0f809, +0xaf450254, 0x8fbf00c8, 0x8fbe00c4, 0x8fb500c0, +0x8fb300bc, 0x8fb200b8, 0x8fb100b4, 0x8fb000b0, +0x3e00008, 0x27bd00d0, 0x3e00008, 0x0, +0x27bdff38, 0x240b0001, 0xafbf00c0, 0xafbe00bc, +0xafb500b8, 0xafb300b4, 0xafb200b0, 0xafb100ac, +0xafb000a8, 0xa3a00087, 0xafa00044, 0xafab005c, +0x934205c4, 0xa7a00076, 0x10400007, 0xa7a0007e, +0x8f4c00c0, 0xafac0064, 0x8f4b00c8, 0x8f5e00c4, +0x10000130, 0xafab006c, 0x8f420114, 0x40f809, +0x0, 0x403021, 0x10c002a1, 0x0, +0x8cc20000, 0x8cc30004, 0xafa20020, 0xafa30024, +0x8fac0024, 0x8fbe0020, 0x3182ffff, 0x2442fffc, +0xafa20064, 0x3c020006, 0x2c21024, 0x14400015, +0xafac006c, 0x93c20000, 0x30420001, 0x10400011, +0x2402ffff, 0x8fc30000, 0x14620004, 0x3402ffff, +0x97c30004, 0x1062000b, 0x0, 0xc0024bb, +0x3c02021, 0x304200ff, 0x14400006, 0x0, +0x8f420118, 0x40f809, 0x0, 0x10000280, +0x0, 0x8fa20024, 0x3c03ffbf, 0x3463ffff, +0x431024, 0x3c03ffff, 0x431824, 0x14600003, +0xafa20024, 0x10000040, 0x8021, 0x3c020080, +0x621024, 0x10400007, 0x0, 0x8f42038c, +0x24420001, 0xaf42038c, 0x8f42038c, 0x10000036, +0x24100001, 0x8f420210, 0x24420001, 0xaf420210, +0x8f420210, 0x3c020001, 0x621024, 0x10400006, +0x3c020002, 0x8f4201c4, 0x24420001, 0xaf4201c4, +0x8f4201c4, 0x3c020002, 0x621024, 0x10400006, +0x3c020004, 0x8f42037c, 0x24420001, 0xaf42037c, +0x8f42037c, 0x3c020004, 0x621024, 0x10400006, +0x3c020008, 0x8f420380, 0x24420001, 0xaf420380, +0x8f420380, 0x3c020008, 0x621024, 0x10400006, +0x3c020010, 0x8f420384, 0x24420001, 0xaf420384, +0x8f420384, 0x3c020010, 0x621024, 0x10400006, +0x3c020020, 0x8f4201c0, 0x24420001, 0xaf4201c0, +0x8f4201c0, 0x3c020020, 0x621024, 0x10400006, +0x24100001, 0x8f420388, 0x24420001, 0xaf420388, +0x8f420388, 0x24100001, 0x8c020260, 0x8fab0064, +0x4b102b, 0x10400015, 0x320200ff, 0x8f4201e8, +0x24420001, 0xaf4201e8, 0x8f4201e8, 0x8fac006c, +0x8f8200e0, 0x358c0100, 0xafac006c, 0xafa20010, +0x8f8200e4, 0x24100001, 0x3c040001, 0x248468a0, +0xafa20014, 0x8fa60020, 0x8fa70024, 0x3c050007, +0xc002b3b, 0x34a53600, 0x320200ff, 0x10400010, +0x3c020080, 0x2c21024, 0x1440000e, 0x32c20400, +0x8fab006c, 0x3c020080, 0x34420100, 0x1621024, +0x10400005, 0x0, 0x8f42020c, 0x24420001, +0xaf42020c, 0x8f42020c, 0x10000202, 0x8fa30064, +0x32c20400, 0x10400012, 0x34028100, 0x97c3000c, +0x1462000f, 0x0, 0x240c0200, 0xa7ac0076, +0x97c2000e, 0x8fc30008, 0x8fc40004, 0x8fab0064, +0x8fc50000, 0x256bfffc, 0xafab0064, 0xa7a2007e, +0xafc3000c, 0xafc40008, 0xafc50004, 0x27de0004, +0x8fa70064, 0x320200ff, 0x14400034, 0x3c020100, +0x97c4000c, 0x2c8305dd, 0x38828870, 0x2c420001, +0x621825, 0x10600015, 0x2821, 0x32c20800, +0x10400015, 0x24020800, 0x97c30014, 0x14620012, +0x3402aaaa, 0x97c3000e, 0x14620007, 0x2021, +0x97c30010, 0x24020300, 0x14620004, 0x801021, +0x97c20012, 0x2c440001, 0x801021, 0x54400006, +0x24050016, 0x10000004, 0x0, 0x24020800, +0x50820001, 0x2405000e, 0x10a00013, 0x3c52021, +0x24830009, 0x3c02001f, 0x3442ffff, 0x43102b, +0x10400003, 0x0, 0x8f420148, 0x621823, +0x90620000, 0x38430006, 0x2c630001, 0x38420011, +0x2c420001, 0x621825, 0x10600004, 0x3c020100, +0x94820002, 0x453821, 0x3c020100, 0x2c21024, +0x5040000e, 0xafa70064, 0x8fac0064, 0x10ec0008, +0x3c050007, 0x3c040001, 0x24846908, 0x8fa60064, +0x34a54000, 0xafa00010, 0xc002b3b, 0xafa00014, +0x8fab0064, 0x256b0004, 0xafab0064, 0x8f420080, +0x8fac0064, 0x4c102b, 0x1040002c, 0x32c28000, +0x10400034, 0x240b0003, 0x32c21000, 0x10400031, +0xafab005c, 0x1000002e, 0x240c0004, 0x8f420350, +0x2403ffbf, 0x283a024, 0x24420001, 0xaf420350, +0x10000173, 0x8f420350, 0x3c020800, 0x2c2b025, +0x2402ffbf, 0x282a024, 0x8f830128, 0x3c040001, +0x248468d0, 0x26620001, 0xafa20014, 0xafa30010, +0x8f860120, 0x8f870124, 0x3c050007, 0xc002b3b, +0x34a55300, 0x10000162, 0x0, 0x8ea20000, +0x8ea30004, 0x3c040001, 0x248468e8, 0xafb00010, +0xafb10014, 0x8ea70018, 0x34a55900, 0xc002b3b, +0x603021, 0x10000156, 0x0, 0x8f420084, +0x8fab0064, 0x4b102b, 0x14400007, 0x3c020001, +0x2c21024, 0x10400004, 0x0, 0x240c0002, +0xafac005c, 0x8fab0064, 0x11600166, 0x27ac0020, +0xafac008c, 0x8fab005c, 0x240c0001, 0x556c0021, +0x240c0002, 0x8f430054, 0x8f420050, 0x1062000b, +0x274b0054, 0x8f510054, 0x3403ecc0, 0xafab004c, +0x26220001, 0x304201ff, 0xafa20054, 0x111140, +0x431021, 0x1000006b, 0x2e2a821, 0x8f420044, +0x8fac0064, 0x3c040001, 0x248468ac, 0xafac0014, +0xafa20010, 0x8f460054, 0x8f470050, 0x3c050007, +0xc002b3b, 0x34a54300, 0x8f430350, 0x2402ffbf, +0x282a024, 0x24630001, 0xaf430350, 0x10000124, +0x8f420350, 0x156c001d, 0x0, 0x8f430074, +0x8f420070, 0x1062000a, 0x274b0074, 0x8f510074, +0xafab004c, 0x26220001, 0x304203ff, 0xafa20054, +0x111140, 0x24426cc0, 0x1000004a, 0x2e2a821, +0x8f420044, 0x8fac0064, 0x3c040001, 0x248468b8, +0x3c050007, 0xafac0014, 0xafa20010, 0x8f460074, +0x8f470070, 0x34a54500, 0x240b0001, 0xc002b3b, +0xafab005c, 0x1000ffc3, 0x0, 0x8f430064, +0x8f420060, 0x1062001a, 0x274c0064, 0x8f510064, +0x8fab005c, 0xafac004c, 0x26220001, 0x304200ff, +0xafa20054, 0x24020004, 0x1562000e, 0x111140, +0x111180, 0x24420cc0, 0x2e21021, 0xafa20044, +0x9442002a, 0x8fac0044, 0x8fab0064, 0x4b102b, +0x10400024, 0x25950020, 0x240c0001, 0x10000021, +0xa3ac0087, 0x24424cc0, 0x1000001e, 0x2e2a821, +0x8f420044, 0x8fab0064, 0x3c040001, 0x248468c4, +0xafab0014, 0xafa20010, 0x8f460064, 0x8f470060, +0x3c050007, 0xc002b3b, 0x34a54800, 0x3c020008, +0x2c21024, 0x1440ff61, 0x0, 0x8f420370, +0x240c0001, 0xafac005c, 0x24420001, 0xaf420370, +0x1000ff90, 0x8f420370, 0x27a30036, 0x131040, +0x621821, 0x94620000, 0x441021, 0x1000001f, +0xa4620000, 0xaebe0018, 0x93a20087, 0x10400084, +0x9821, 0x8fab0044, 0x8fa40064, 0x8fa3008c, +0x25620020, 0xafa20028, 0x25620008, 0xafa20030, +0x25620010, 0xafab002c, 0xafa20034, 0x9562002a, +0xa7a20038, 0x95620018, 0xa7a2003a, 0x9562001a, +0xa7a2003c, 0x9562001c, 0xa7a2003e, 0x94620018, +0x24630002, 0x822023, 0x1880ffdf, 0x26730001, +0x2e620004, 0x1440fff9, 0x0, 0x8f4200fc, +0x262102a, 0x14400030, 0x24030001, 0x8f83012c, +0x10600028, 0x0, 0x8f820124, 0x431023, +0x22143, 0x58800001, 0x24840040, 0x8f820128, +0x431023, 0x21943, 0x58600001, 0x24630040, +0x64102a, 0x54400001, 0x602021, 0xaf4400fc, +0x8f4200fc, 0x262102a, 0x10400016, 0x24030001, +0x1000001a, 0x306200ff, 0x8fac008c, 0x101040, +0x4c1021, 0x94470018, 0x101080, 0x4c1021, +0xafbe0010, 0x8c420008, 0x3c040001, 0x248468dc, +0x3c050007, 0x8c430004, 0x8c420000, 0x34a55500, +0x2003021, 0xc002b3b, 0xafa30014, 0x10000039, +0x0, 0x8f420334, 0x1821, 0x24420001, +0xaf420334, 0x8f420334, 0x306200ff, 0x1040ff06, +0x8021, 0x8f430008, 0x2402fbff, 0x1260002d, +0x625024, 0x3c0b4000, 0x22b4025, 0x8fb1008c, +0x2669ffff, 0x2209021, 0x8e420008, 0x96270018, +0x8c440000, 0x8c450004, 0x56090004, 0x240b0001, +0x240c0002, 0x10000002, 0xafac0010, 0xafab0010, +0x16000004, 0xafa80014, 0x8f420008, 0x10000002, +0xafa20018, 0xafaa0018, 0x8f42010c, 0x3c03021, +0xafa80098, 0xafa9009c, 0x40f809, 0xafaa00a0, +0x8fa80098, 0x8fa9009c, 0x8faa00a0, 0x1040ffc2, +0x3c02001f, 0x96230018, 0x3442ffff, 0x3c3f021, +0x5e102b, 0x10400003, 0x26310002, 0x8f420148, +0x3c2f023, 0x26100001, 0x213102b, 0x1440ffda, +0x26520004, 0x8fb00064, 0x1000001a, 0x0, +0x96a3000a, 0x8fb00064, 0x70102b, 0x54400001, +0x608021, 0x8ea40000, 0x8ea50004, 0x8fab005c, +0x240c0002, 0xafac0010, 0x934305c4, 0xb1700, +0x10600003, 0x2223025, 0x3c020800, 0xc23025, +0xafa60014, 0x8f420008, 0xafa20018, 0x8f42010c, +0x3c03021, 0x40f809, 0x2003821, 0x1040fecb, +0x3c050007, 0x97ac0076, 0x11800007, 0x96a3000e, +0x934205c4, 0x14400004, 0x0, 0x97ab007e, +0x6c1825, 0xa6ab0016, 0x8fac006c, 0x3c02ffff, +0x1821024, 0x10400003, 0xc1402, 0x34630400, +0xa6a20014, 0xa6b0000a, 0x8fab0064, 0x560b0006, +0x3d0f021, 0x34620004, 0xafa00064, 0xa6a2000e, +0x1000000d, 0xa34005c4, 0x8fac0064, 0x3c02001f, +0x3442ffff, 0x5e102b, 0x1906023, 0xafac0064, +0xa6a3000e, 0x240b0001, 0x10400003, 0xa34b05c4, +0x8f420148, 0x3c2f023, 0x8fab0054, 0x8fac004c, +0xad8b0000, 0x8fac0064, 0x1580feba, 0x0, +0x8fab0064, 0x1160001b, 0x0, 0x934205c4, +0x10400006, 0x0, 0xaf5e00c4, 0xaf4b00c0, +0x8fac006c, 0x1000000e, 0xaf4c00c8, 0x97ab0076, +0x1160000b, 0x34038100, 0x8fa20020, 0x8c46000c, +0xa443000c, 0x97ac007e, 0x8c440004, 0x8c450008, +0xa44c000e, 0xac440000, 0xac450004, 0xac460008, +0x8f42034c, 0x24420001, 0xaf42034c, 0x10000010, +0x8f42034c, 0x8fab006c, 0x3164ffff, 0x2484fffc, +0x801821, 0x8f440250, 0x8f450254, 0x8f460118, +0x1021, 0xa32821, 0xa3382b, 0x822021, +0x872021, 0xaf440250, 0xc0f809, 0xaf450254, +0x8fbf00c0, 0x8fbe00bc, 0x8fb500b8, 0x8fb300b4, +0x8fb200b0, 0x8fb100ac, 0x8fb000a8, 0x3e00008, +0x27bd00c8, 0x3e00008, 0x0, 0x27bdffd8, +0xafbf0024, 0xafb00020, 0x8f43004c, 0x8f420048, +0x10620034, 0x0, 0x8f430048, 0x8f42004c, +0x622023, 0x4820001, 0x24840200, 0x8f430054, +0x8f42004c, 0x43102b, 0x14400004, 0x24020200, +0x8f43004c, 0x10000005, 0x431023, 0x8f420054, +0x8f43004c, 0x431023, 0x2442ffff, 0x405021, +0x8a102a, 0x54400001, 0x805021, 0x8f49004c, +0x8f48004c, 0x8f440188, 0x8f45018c, 0x8f46004c, +0x24071000, 0xafa70010, 0x84140, 0x1001821, +0x12a4821, 0x313001ff, 0xafb00014, 0x8f470014, +0x1021, 0x63140, 0xafa70018, 0xa32821, +0xa3382b, 0x822021, 0x872021, 0x3402ecc0, +0xc23021, 0x8f420108, 0x2e63021, 0x40f809, +0xa3940, 0x54400001, 0xaf50004c, 0x8f43004c, +0x8f420048, 0x14620018, 0x0, 0x8f420000, +0x10400007, 0x0, 0xaf80004c, 0x8f82004c, +0x1040fffd, 0x0, 0x10000005, 0x0, +0xaf800048, 0x8f820048, 0x1040fffd, 0x0, +0x8f820060, 0x2403fdff, 0x431024, 0xaf820060, +0x8f420000, 0x10400003, 0x0, 0x10000002, +0xaf80004c, 0xaf800048, 0x8fbf0024, 0x8fb00020, +0x3e00008, 0x27bd0028, 0x3e00008, 0x0, +0x27bdffd8, 0xafbf0024, 0xafb00020, 0x8f43005c, +0x8f420058, 0x10620049, 0x0, 0x8f430058, +0x8f42005c, 0x622023, 0x4820001, 0x24840100, +0x8f430064, 0x8f42005c, 0x43102b, 0x14400004, +0x24020100, 0x8f43005c, 0x10000005, 0x431023, +0x8f420064, 0x8f43005c, 0x431023, 0x2442ffff, +0x403821, 0x87102a, 0x54400001, 0x803821, +0x8f42005c, 0x471021, 0x305000ff, 0x32c21000, +0x10400015, 0x24082000, 0x8f49005c, 0x8f440190, +0x8f450194, 0x8f46005c, 0x73980, 0xafa80010, +0xafb00014, 0x8f480014, 0x94980, 0x1201821, +0x1021, 0xa32821, 0xa3482b, 0x822021, +0x892021, 0x63180, 0xafa80018, 0x8f420108, +0x10000014, 0x24c60cc0, 0x8f49005c, 0x8f440190, +0x8f450194, 0x8f46005c, 0x73940, 0xafa80010, +0xafb00014, 0x8f480014, 0x94940, 0x1201821, +0x1021, 0xa32821, 0xa3482b, 0x822021, +0x892021, 0x63140, 0xafa80018, 0x8f420108, +0x24c64cc0, 0x40f809, 0x2e63021, 0x54400001, +0xaf50005c, 0x8f43005c, 0x8f420058, 0x14620018, +0x0, 0x8f420000, 0x10400007, 0x0, +0xaf80004c, 0x8f82004c, 0x1040fffd, 0x0, +0x10000005, 0x0, 0xaf800048, 0x8f820048, +0x1040fffd, 0x0, 0x8f820060, 0x2403feff, +0x431024, 0xaf820060, 0x8f420000, 0x10400003, +0x0, 0x10000002, 0xaf80004c, 0xaf800048, +0x8fbf0024, 0x8fb00020, 0x3e00008, 0x27bd0028, +0x3e00008, 0x0, 0x27bdffd8, 0xafbf0024, +0xafb00020, 0x8f43006c, 0x8f420068, 0x10620033, +0x0, 0x8f430068, 0x8f42006c, 0x622023, +0x4820001, 0x24840400, 0x8f430074, 0x8f42006c, +0x43102b, 0x14400004, 0x24020400, 0x8f43006c, +0x10000005, 0x431023, 0x8f420074, 0x8f43006c, +0x431023, 0x2442ffff, 0x405021, 0x8a102a, +0x54400001, 0x805021, 0x8f49006c, 0x8f48006c, +0x8f440198, 0x8f45019c, 0x8f46006c, 0x24074000, +0xafa70010, 0x84140, 0x1001821, 0x12a4821, +0x313003ff, 0xafb00014, 0x8f470014, 0x1021, +0x63140, 0x24c66cc0, 0xafa70018, 0xa32821, +0xa3382b, 0x822021, 0x872021, 0x8f420108, +0x2e63021, 0x40f809, 0xa3940, 0x54400001, +0xaf50006c, 0x8f43006c, 0x8f420068, 0x14620018, +0x0, 0x8f420000, 0x10400007, 0x0, +0xaf80004c, 0x8f82004c, 0x1040fffd, 0x0, +0x10000005, 0x0, 0xaf800048, 0x8f820048, +0x1040fffd, 0x0, 0x8f820060, 0x2403f7ff, +0x431024, 0xaf820060, 0x8f420000, 0x10400003, +0x0, 0x10000002, 0xaf80004c, 0xaf800048, +0x8fbf0024, 0x8fb00020, 0x3e00008, 0x27bd0028, +0x3e00008, 0x0, 0x8f4200fc, 0x3c030001, +0x8f4400f8, 0x346330c8, 0x24420001, 0xaf4200fc, +0x8f850128, 0x2e31021, 0x54820004, 0x24820008, +0x3c020001, 0x34422ec8, 0x2e21021, 0x401821, +0xaf4300f8, 0xac600000, 0x8f4200f4, 0x14620004, +0x3c020001, 0x24a20020, 0x1000000f, 0xaf820128, +0x8f4300f8, 0x344230c8, 0x2e21021, 0x54620004, +0x24620008, 0x3c020001, 0x34422ec8, 0x2e21021, +0x401821, 0x8c620004, 0x21140, 0xa21021, +0xaf820128, 0xac600000, 0x8ca30018, 0x30620070, +0x1040002d, 0x30620020, 0x10400004, 0x3c020010, +0x2c21024, 0x1040000d, 0x0, 0x30620040, +0x10400004, 0x3c020020, 0x2c21024, 0x10400007, +0x0, 0x30620010, 0x1040001f, 0x3c020040, +0x2c21024, 0x1440001c, 0x0, 0x8f820040, +0x30420001, 0x14400008, 0x2021, 0x8c030104, +0x24020001, 0x50620005, 0x24040001, 0x8c020264, +0x10400003, 0x801021, 0x24040001, 0x801021, +0x10400006, 0x0, 0x8f42030c, 0x24420001, +0xaf42030c, 0x10000008, 0x8f42030c, 0x8f820044, +0x34420004, 0xaf820044, 0x8f420308, 0x24420001, +0xaf420308, 0x8f420308, 0x3e00008, 0x0, +0x3e00008, 0x0, 0x27bdff98, 0xafbf0060, +0xafbe005c, 0xafb50058, 0xafb30054, 0xafb20050, +0xafb1004c, 0xafb00048, 0x8f4200fc, 0x24420001, +0xaf4200fc, 0x8f880128, 0x25020020, 0xaf820128, +0x8d030018, 0x30620070, 0x1040002e, 0x30620020, +0x10400004, 0x3c020010, 0x2c21024, 0x1040000d, +0x0, 0x30620040, 0x10400004, 0x3c020020, +0x2c21024, 0x10400007, 0x0, 0x30620010, +0x104001a9, 0x3c020040, 0x2c21024, 0x144001a6, +0x0, 0x8f820040, 0x30420001, 0x14400008, +0x2021, 0x8c030104, 0x24020001, 0x50620005, +0x24040001, 0x8c020264, 0x10400003, 0x801021, +0x24040001, 0x801021, 0x10400006, 0x0, +0x8f42030c, 0x24420001, 0xaf42030c, 0x10000192, +0x8f42030c, 0x8f820044, 0x34420004, 0xaf820044, +0x8f420308, 0x24420001, 0xaf420308, 0x1000018a, +0x8f420308, 0x30620002, 0x1040014b, 0x3c020800, +0x8d1e001c, 0x1e5702, 0xafaa0034, 0x950a0016, +0x3c22024, 0xafaa0024, 0x8faa0034, 0x24020001, +0x15420006, 0x33deffff, 0x1e1140, 0x3403ecc0, +0x431021, 0x10000010, 0x2e2a821, 0x24020002, +0x15420005, 0x24020003, 0x1e1140, 0x24426cc0, +0x10000009, 0x2e2a821, 0x15420005, 0x1e1180, +0x1e1140, 0x24424cc0, 0x10000003, 0x2e2a821, +0x571021, 0x24550ce0, 0x96a2000e, 0x304afffc, +0x30420400, 0x10400003, 0xafaa002c, 0x100000e1, +0x8821, 0x10800004, 0x8821, 0x97b10026, +0x100000dd, 0xa6b10012, 0x8eb30018, 0x966a000c, +0xa7aa003e, 0x97a5003e, 0x2ca305dd, 0x38a28870, +0x2c420001, 0x621825, 0x10600015, 0x2021, +0x32c20800, 0x10400015, 0x24020800, 0x96630014, +0x14620012, 0x3402aaaa, 0x9663000e, 0x14620007, +0x2821, 0x96630010, 0x24020300, 0x14620004, +0xa01021, 0x96620012, 0x2c450001, 0xa01021, +0x54400006, 0x24040016, 0x10000004, 0x0, +0x24020800, 0x50a20001, 0x2404000e, 0x108000b9, +0x2649021, 0x92420000, 0x3042000f, 0x28080, +0x32c20100, 0x10400020, 0x2501821, 0x3c020020, +0x43102b, 0x1440000e, 0x2402021, 0x2821, +0x94820000, 0x24840002, 0xa22821, 0x83102b, +0x1440fffb, 0x30a2ffff, 0x51c02, 0x622821, +0x51c02, 0x30a2ffff, 0x10000009, 0x622821, +0x8f470148, 0x8f420110, 0x102842, 0x3c060020, +0x40f809, 0xafa80040, 0x3045ffff, 0x8fa80040, +0x50a00001, 0x3405ffff, 0x8faa002c, 0x354a0002, +0x10000002, 0xafaa002c, 0x2821, 0x32c20080, +0x10400090, 0xa6a50010, 0x26430009, 0x3c02001f, +0x3442ffff, 0x43102b, 0x10400003, 0x0, +0x8f420148, 0x621823, 0x90660000, 0x30c200ff, +0x38430006, 0x2c630001, 0x38420011, 0x2c420001, +0x621825, 0x1060007f, 0x24020800, 0x8821, +0x97a3003e, 0x1462000f, 0x2602021, 0x96710000, +0x96620002, 0x96630004, 0x96640006, 0x2228821, +0x2238821, 0x2248821, 0x96620008, 0x9663000a, +0x9664000c, 0x2228821, 0x2238821, 0x10000007, +0x2248821, 0x94820000, 0x24840002, 0x2228821, +0x92102b, 0x1440fffb, 0x0, 0x111c02, +0x3222ffff, 0x628821, 0x111c02, 0x3222ffff, +0x628821, 0x32c20200, 0x10400003, 0x26440006, +0x1000003e, 0x8021, 0x3c05001f, 0x34a5ffff, +0xa4102b, 0x10400003, 0x0, 0x8f420148, +0x822023, 0x94820000, 0x30421fff, 0x10400004, +0x2644000c, 0x96420002, 0x10000030, 0x508023, +0x96420002, 0x26430014, 0x508023, 0x3c020020, +0x43102b, 0x1440000a, 0xd08021, 0x9642000c, +0x2028021, 0x9642000e, 0x96430010, 0x96440012, +0x2028021, 0x2038021, 0x10000020, 0x2048021, +0xa4102b, 0x10400003, 0x0, 0x8f420148, +0x822023, 0x94820000, 0x24840002, 0x2028021, +0xa4102b, 0x10400003, 0x0, 0x8f420148, +0x822023, 0x94820000, 0x24840002, 0x2028021, +0xa4102b, 0x10400003, 0x0, 0x8f420148, +0x822023, 0x94820000, 0x24840002, 0x2028021, +0xa4102b, 0x10400003, 0x0, 0x8f420148, +0x822023, 0x94820000, 0x2028021, 0x3c020100, +0x2c21024, 0x1040000e, 0x0, 0x8faa002c, +0x31420004, 0x1040000a, 0x0, 0x9504000e, +0x2642021, 0xc003eec, 0x2484fffc, 0x3042ffff, +0x2228821, 0x111c02, 0x3222ffff, 0x628821, +0x8faa0024, 0x1518823, 0x111402, 0x2228821, +0x2308821, 0x111402, 0x2228821, 0x3231ffff, +0x52200001, 0x3411ffff, 0x8faa002c, 0x354a0001, +0xafaa002c, 0xa6b10012, 0x97aa002e, 0xa6aa000e, +0x8faa002c, 0x31420004, 0x10400002, 0x24091000, +0x34098000, 0x8f480044, 0x8f4401a0, 0x8f4501a4, +0xafa90010, 0x8f490044, 0x84140, 0x1001821, +0xafa90014, 0x8f48000c, 0x2a03021, 0x24070020, +0xafa80018, 0x8f48010c, 0x1021, 0xa32821, +0xa3482b, 0x822021, 0x100f809, 0x892021, +0x1440000b, 0x0, 0x8f820128, 0x3c040001, +0x24846914, 0xafbe0014, 0xafa20010, 0x8f860124, +0x8f870120, 0x3c050007, 0xc002b3b, 0x34a59920, +0x8f420368, 0x2442ffff, 0xaf420368, 0x8f420044, +0x8f430088, 0x24420001, 0x431024, 0xaf420044, +0x8faa0034, 0x8f440368, 0x24020001, 0x15420006, +0x24020002, 0x8f42035c, 0x2442ffff, 0xaf42035c, +0x10000049, 0x8f42035c, 0x15420006, 0x0, +0x8f420364, 0x2442ffff, 0xaf420364, 0x10000042, +0x8f420364, 0x8f420360, 0x2442ffff, 0xaf420360, +0x1000003d, 0x8f420360, 0x30621000, 0x10400005, +0x30628000, 0x8f420078, 0x24420001, 0x10000036, +0xaf420078, 0x10400034, 0x0, 0x8f420078, +0x24420001, 0xaf420078, 0x8c030240, 0x43102b, +0x1440002d, 0x24070008, 0x8f440168, 0x8f45016c, +0x8f430044, 0x8f48000c, 0x8f860120, 0x24020040, +0xafa20010, 0xafa30014, 0xafa80018, 0x8f42010c, +0x40f809, 0x24c6001c, 0x14400011, 0x24020001, +0x3c010001, 0x370821, 0xa02240f2, 0x8f820124, +0xafa20010, 0x8f820128, 0x3c040001, 0x2484688c, +0xafa20014, 0x8f460044, 0x8f870120, 0x3c050009, +0xc002b3b, 0x34a51300, 0x1000000b, 0x0, +0x8f420304, 0x24420001, 0xaf420304, 0x8f420304, +0x8f420044, 0xaf42007c, 0x3c010001, 0x370821, +0xa02040f2, 0xaf400078, 0x8f420318, 0x24420001, +0xaf420318, 0x8f420318, 0x8fbf0060, 0x8fbe005c, +0x8fb50058, 0x8fb30054, 0x8fb20050, 0x8fb1004c, +0x8fb00048, 0x3e00008, 0x27bd0068, 0x3e00008, +0x0, 0x0, 0x0, 0x8f42013c, +0xaf8200c0, 0x8f42013c, 0xaf8200c4, 0x8f42013c, +0xaf8200c8, 0x8f420138, 0xaf8200d0, 0x8f420138, +0xaf8200d4, 0x8f420138, 0x3e00008, 0xaf8200d8, +0x27bdffe0, 0x27840208, 0x24050200, 0xafbf0018, +0xc002bbf, 0x24060008, 0x8c020204, 0xc004012, +0xaf820210, 0x3c020001, 0x8c426d94, 0x30420002, +0x1040000e, 0x2021, 0x8c060248, 0x24020002, +0x3c010001, 0xac226d98, 0xc005104, 0x24050002, +0x2021, 0x8c060248, 0x24020001, 0x3c010001, +0xac226d98, 0x10000011, 0x24050001, 0x8c060248, +0x24020004, 0x3c010001, 0xac226d98, 0xc005104, +0x24050004, 0x3c020001, 0x8c426d94, 0x30420001, +0x10400008, 0x24020001, 0x3c010001, 0xac226d98, +0x2021, 0x24050001, 0x3c06601b, 0xc005104, +0x0, 0x3c040001, 0x248469d0, 0x8f420150, +0x8f430154, 0x3c050008, 0x8f460158, 0x21640, +0x31940, 0x34630403, 0x431025, 0x633c0, +0x461025, 0xaf82021c, 0xafa00010, 0xafa00014, +0x8f86021c, 0x34a50200, 0xc002b3b, 0x3821, +0x3c010001, 0xac206d90, 0x3c010001, 0xac206da8, +0x8fbf0018, 0x3e00008, 0x27bd0020, 0x27bdffe0, +0x3c050008, 0x34a50300, 0xafbf0018, 0xafa00010, +0xafa00014, 0x8f860200, 0x3c040001, 0x248469dc, +0xc002b3b, 0x3821, 0x8f420410, 0x24420001, +0xaf420410, 0x8f420410, 0x8fbf0018, 0x3e00008, +0x27bd0020, 0x27bdffd8, 0xafbf0020, 0xafb1001c, +0xafb00018, 0x8f4203a4, 0x24420001, 0xaf4203a4, +0x8f4203a4, 0x8f900220, 0x8f8200e0, 0xafa20010, +0x8f8200e4, 0xafa20014, 0x8f8600c4, 0x8f8700c8, +0x3c040001, 0x248469e8, 0xc002b3b, 0x2002821, +0x3c044000, 0x2041024, 0x504000b4, 0x3c040100, +0x8f4203bc, 0x24420001, 0xaf4203bc, 0x8f4203bc, +0x8f8700c4, 0x8f8300c8, 0x8f420148, 0x671823, +0x43102b, 0x10400003, 0x0, 0x8f420148, +0x621821, 0x10600005, 0x0, 0x8f42014c, +0x43102b, 0x1040000b, 0x0, 0x8f8200e0, +0x8f430124, 0xaf42011c, 0xaf430114, 0x8f820220, +0x3c0308ff, 0x3463fffb, 0x431024, 0x100000ce, +0x441025, 0x8f820220, 0x3c0308ff, 0x3463ffff, +0x431024, 0x34420004, 0xaf820220, 0x8f8200e0, +0x8f430124, 0xaf42011c, 0xaf430114, 0x8f8600c8, +0x8f840120, 0x8f830124, 0x10000005, 0x2821, +0x14620002, 0x24620020, 0x27624800, 0x401821, +0x1064000c, 0x30a200ff, 0x8c620018, 0x30420003, +0x1040fff7, 0x27624fe0, 0x8f4203d0, 0x24050001, +0x24420001, 0xaf4203d0, 0x8f4203d0, 0x8c660008, +0x30a200ff, 0x14400058, 0x0, 0x934205c4, +0x14400055, 0x0, 0x8f8700c4, 0x8f8800e0, +0x8f8400e4, 0x2402fff8, 0x1024024, 0x1041023, +0x218c3, 0x4620001, 0x24630200, 0x10600005, +0x24020001, 0x10620009, 0x0, 0x1000001f, +0x0, 0x8f4203c0, 0xe03021, 0x24420001, +0xaf4203c0, 0x10000040, 0x8f4203c0, 0x8f4203c4, +0x24420001, 0xaf4203c4, 0x8c860000, 0x8f420148, +0x8f4303c4, 0xe61823, 0x43102b, 0x10400004, +0x2c62233f, 0x8f420148, 0x621821, 0x2c62233f, +0x14400031, 0x0, 0x8f42020c, 0x24420001, +0xaf42020c, 0x8f42020c, 0xe03021, 0x24820008, +0xaf8200e4, 0x10000028, 0xaf8200e8, 0x8f4203c8, +0x24420001, 0xaf4203c8, 0x8f4203c8, 0x8c850000, +0x8f420148, 0xa71823, 0x43102b, 0x10400003, +0x0, 0x8f420148, 0x621821, 0x8f42014c, +0x43102b, 0x5440000a, 0xa03021, 0x8f42020c, +0x24420001, 0xaf42020c, 0x8f42020c, 0x24820008, +0xaf8200e4, 0x8f8400e4, 0x1488ffec, 0xaf8400e8, +0x1488000d, 0x27623000, 0x14820002, 0x2482fff8, +0x27623ff8, 0x94430006, 0x3c02001f, 0x3442ffff, +0xc33021, 0x46102b, 0x10400003, 0x0, +0x8f420148, 0xc23023, 0xaf8600c8, 0x8f8300c4, +0x8f420148, 0xc31823, 0x43102b, 0x10400003, +0x0, 0x8f420148, 0x621821, 0x10600005, +0x0, 0x8f42014c, 0x43102b, 0x50400008, +0x3c02fdff, 0x8f820220, 0x3c0308ff, 0x3463fffb, +0x431024, 0x3c034000, 0x1000003f, 0x431025, +0x8f4303cc, 0x3442ffff, 0x282a024, 0x24630001, +0xaf4303cc, 0x10000039, 0x8f4203cc, 0x2041024, +0x1040000e, 0x3c110200, 0x8f4203a8, 0x24420001, +0xaf4203a8, 0x8f4203a8, 0x8f820220, 0x3c0308ff, +0x3463ffff, 0x431024, 0x441025, 0xc003daf, +0xaf820220, 0x10000029, 0x0, 0x2111024, +0x50400008, 0x3c110400, 0x8f4203ac, 0x24420001, +0xaf4203ac, 0xc003daf, 0x8f4203ac, 0x10000019, +0x0, 0x2111024, 0x1040001c, 0x0, +0x8f830224, 0x24021402, 0x14620009, 0x3c050008, +0x3c040001, 0x248469f4, 0xafa00010, 0xafa00014, +0x8f860224, 0x34a50500, 0xc002b3b, 0x3821, +0x8f4203b0, 0x24420001, 0xaf4203b0, 0x8f4203b0, +0x8f820220, 0x2002021, 0x34420002, 0xc004e9c, +0xaf820220, 0x8f820220, 0x3c0308ff, 0x3463ffff, +0x431024, 0x511025, 0xaf820220, 0x8fbf0020, +0x8fb1001c, 0x8fb00018, 0x3e00008, 0x27bd0028, +0x3e00008, 0x0, 0x3c020001, 0x8c426da8, +0x27bdffb0, 0xafbf0048, 0xafbe0044, 0xafb50040, +0xafb3003c, 0xafb20038, 0xafb10034, 0x1040000f, +0xafb00030, 0x3c040001, 0x24846a00, 0x3c050008, +0xafa00010, 0xafa00014, 0x8f860220, 0x34a50600, +0x24020001, 0x3c010001, 0xac206da8, 0x3c010001, +0xac226d9c, 0xc002b3b, 0x3821, 0x3c037fff, +0x8c020268, 0x3463ffff, 0x3c04fdff, 0x431024, +0xac020268, 0x8f420004, 0x3484ffff, 0x30420002, +0x10400092, 0x284a024, 0x3c040600, 0x34842000, +0x8f420004, 0x2821, 0x2403fffd, 0x431024, +0xaf420004, 0xafa40020, 0x8f5e0018, 0x27aa0020, +0x240200ff, 0x13c20002, 0xafaa002c, 0x27c50001, +0x8c020228, 0xa09021, 0x1642000e, 0x1e38c0, +0x8f42033c, 0x24420001, 0xaf42033c, 0x8f42033c, +0x8c020228, 0x3c040001, 0x24846998, 0x3c050009, +0xafa00014, 0xafa20010, 0x8fa60020, 0x1000006d, +0x34a50500, 0xf71021, 0x8fa30020, 0x8fa40024, +0xac4304c0, 0xac4404c4, 0x8f830054, 0x8f820054, +0x247003e8, 0x2021023, 0x2c4203e9, 0x1040001b, +0x9821, 0xe08821, 0x263504c0, 0x8f440178, +0x8f45017c, 0x2201821, 0x240a0004, 0xafaa0010, +0xafb20014, 0x8f48000c, 0x1021, 0x2f53021, +0xafa80018, 0x8f48010c, 0x24070008, 0xa32821, +0xa3482b, 0x822021, 0x100f809, 0x892021, +0x54400006, 0x24130001, 0x8f820054, 0x2021023, +0x2c4203e9, 0x1440ffe9, 0x0, 0x326200ff, +0x54400017, 0xaf520018, 0x8f420378, 0x24420001, +0xaf420378, 0x8f420378, 0x8f820120, 0x8faa002c, +0xafa20010, 0x8f820124, 0x3c040001, 0x248469a4, +0x3c050009, 0xafa20014, 0x8d460000, 0x10000035, +0x34a50600, 0x8f420308, 0x24130001, 0x24420001, +0xaf420308, 0x8f420308, 0x1000001e, 0x326200ff, +0x8f830054, 0x8f820054, 0x247003e8, 0x2021023, +0x2c4203e9, 0x10400016, 0x9821, 0x3c150020, +0x24110010, 0x8f42000c, 0x8f440160, 0x8f450164, +0x8f860120, 0xafb10010, 0xafb20014, 0x551025, +0xafa20018, 0x8f42010c, 0x24070008, 0x40f809, +0x24c6001c, 0x1440ffe3, 0x0, 0x8f820054, +0x2021023, 0x2c4203e9, 0x1440ffee, 0x0, +0x326200ff, 0x14400011, 0x0, 0x8f420378, +0x24420001, 0xaf420378, 0x8f420378, 0x8f820120, +0x8faa002c, 0xafa20010, 0x8f820124, 0x3c040001, +0x248469ac, 0x3c050009, 0xafa20014, 0x8d460000, +0x34a50700, 0xc002b3b, 0x3c03821, 0x8f4202ec, +0x24420001, 0xaf4202ec, 0x8f4202ec, 0x8fbf0048, +0x8fbe0044, 0x8fb50040, 0x8fb3003c, 0x8fb20038, +0x8fb10034, 0x8fb00030, 0x3e00008, 0x27bd0050, +0x3c020001, 0x8c426da8, 0x27bdffe0, 0x1440000d, +0xafbf0018, 0x3c040001, 0x24846a0c, 0x3c050008, +0xafa00010, 0xafa00014, 0x8f860220, 0x34a50700, +0x24020001, 0x3c010001, 0xac226da8, 0xc002b3b, +0x3821, 0x3c020004, 0x2c21024, 0x10400007, +0x0, 0x8f820220, 0x3c0308ff, 0x3463ffff, +0x431024, 0x34420008, 0xaf820220, 0x3c050001, +0x8ca56d98, 0x24020001, 0x14a20007, 0x2021, +0xc00529b, 0x24050001, 0xac02026c, 0x8c03026c, +0x10000006, 0x3c020007, 0xc00529b, 0x2021, +0xac020268, 0x8c030268, 0x3c020007, 0x621824, +0x3c020002, 0x5062000d, 0x3c0205f5, 0x43102b, +0x14400006, 0x3c020004, 0x3c020001, 0x10620009, +0x3c020098, 0x1000000b, 0x0, 0x14620009, +0x3c023b9a, 0x10000004, 0x3442ca00, 0x10000002, +0x3442e100, 0x34429680, 0xaf4201fc, 0x8f4201fc, +0xaee20064, 0x8fbf0018, 0x3e00008, 0x27bd0020, +0x0, 0x0, 0x0, 0x86102b, +0x50400001, 0x872023, 0xc41023, 0x24843, +0x125102b, 0x1040001b, 0x91040, 0x824021, +0x88102b, 0x10400007, 0x1821, 0x94820000, +0x24840002, 0x621821, 0x88102b, 0x1440fffb, +0x0, 0x602021, 0xc73023, 0xa91023, +0x21040, 0xc22821, 0xc5102b, 0x10400007, +0x1821, 0x94c20000, 0x24c60002, 0x621821, +0xc5102b, 0x1440fffb, 0x0, 0x1000000d, +0x832021, 0x51040, 0x822821, 0x85102b, +0x10400007, 0x1821, 0x94820000, 0x24840002, +0x621821, 0x85102b, 0x1440fffb, 0x0, +0x602021, 0x41c02, 0x3082ffff, 0x622021, +0x41c02, 0x3082ffff, 0x622021, 0x3e00008, +0x3082ffff, 0x3e00008, 0x0, 0x802821, +0x30a20001, 0x1040002b, 0x3c03001f, 0x3463ffff, +0x24a20004, 0x62102b, 0x54400007, 0x65102b, +0x90a20001, 0x90a40003, 0x90a30000, 0x90a50002, +0x1000002a, 0x441021, 0x10400003, 0x0, +0x8f420148, 0xa22823, 0x90a40000, 0x24a50001, +0x65102b, 0x10400003, 0x0, 0x8f420148, +0xa22823, 0x90a20000, 0x24a50001, 0x21200, +0x822021, 0x65102b, 0x10400003, 0x0, +0x8f420148, 0xa22823, 0x90a20000, 0x24a50001, +0x822021, 0x65102b, 0x10400003, 0x0, +0x8f420148, 0xa22823, 0x90a20000, 0x1000002d, +0x21200, 0x3463ffff, 0x24a20004, 0x62102b, +0x5440000a, 0x65102b, 0x90a20000, 0x90a40002, +0x90a30001, 0x90a50003, 0x441021, 0x21200, +0x651821, 0x10000020, 0x432021, 0x10400003, +0x0, 0x8f420148, 0xa22823, 0x90a20000, +0x24a50001, 0x22200, 0x65102b, 0x10400003, +0x0, 0x8f420148, 0xa22823, 0x90a20000, +0x24a50001, 0x822021, 0x65102b, 0x10400003, +0x0, 0x8f420148, 0xa22823, 0x90a20000, +0x24a50001, 0x21200, 0x822021, 0x65102b, +0x10400003, 0x0, 0x8f420148, 0xa22823, +0x90a20000, 0x822021, 0x41c02, 0x3082ffff, +0x622021, 0x41c02, 0x3082ffff, 0x622021, +0x3e00008, 0x3082ffff, 0x0, 0x8f820220, +0x34420002, 0xaf820220, 0x3c020002, 0x8c428ff8, +0x30424000, 0x10400054, 0x24040001, 0x8f820200, +0x24067fff, 0x8f830200, 0x30450002, 0x2402fffd, +0x621824, 0xaf830200, 0xaf840204, 0x8f830054, +0x8f820054, 0x10000002, 0x24630001, 0x8f820054, +0x621023, 0x2c420002, 0x1440fffc, 0x0, +0x8f820224, 0x1444004d, 0x42040, 0xc4102b, +0x1040fff1, 0x0, 0x8f820200, 0x451025, +0xaf820200, 0x8f820220, 0x34428000, 0xaf820220, +0x8f830054, 0x8f820054, 0x10000002, 0x24630001, +0x8f820054, 0x621023, 0x2c420002, 0x1440fffc, +0x0, 0x8f820220, 0x3c030004, 0x431024, +0x1440000f, 0x0, 0x8f820220, 0x3c03ffff, +0x34637fff, 0x431024, 0xaf820220, 0x8f830054, +0x8f820054, 0x10000002, 0x24630001, 0x8f820054, +0x621023, 0x2c420002, 0x1440fffc, 0x0, +0x8f820220, 0x3c030004, 0x431024, 0x1440000d, +0x0, 0x8f820220, 0x34428000, 0xaf820220, +0x8f830054, 0x8f820054, 0x10000002, 0x24630001, +0x8f820054, 0x621023, 0x2c420002, 0x1440fffc, +0x0, 0x8f820220, 0x3c030004, 0x431024, +0x1040001b, 0x1021, 0x8f830220, 0x24020001, +0x10000015, 0x3c04f700, 0x8f820220, 0x3c04f700, +0x441025, 0xaf820220, 0x8f820220, 0x2403fffd, +0x431024, 0xaf820220, 0x8f820220, 0x3c030300, +0x431024, 0x14400003, 0x0, 0x10000008, +0x1021, 0x8f820220, 0x34420002, 0xaf820220, +0x8f830220, 0x24020001, 0x641825, 0xaf830220, +0x3e00008, 0x0, 0x2021, 0x3c050100, +0x24020001, 0xaf80021c, 0xaf820200, 0xaf820220, +0x27625000, 0xaf8200c0, 0x27625000, 0xaf8200c4, +0x27625000, 0xaf8200c8, 0x27625000, 0xaf8200d0, +0x27625000, 0xaf8200d4, 0x27625000, 0xaf8200d8, +0x27623000, 0xaf8200e0, 0x27623000, 0xaf8200e4, +0x27623000, 0xaf8200e8, 0x27622800, 0xaf8200f0, +0x27622800, 0xaf8200f4, 0x27622800, 0xaf8200f8, +0x418c0, 0x24840001, 0x3631021, 0xac453004, +0x3631021, 0xac403000, 0x28820200, 0x1440fff9, +0x418c0, 0x2021, 0x418c0, 0x24840001, +0x3631021, 0xac402804, 0x3631021, 0xac402800, +0x28820100, 0x1440fff9, 0x418c0, 0xaf80023c, +0x24030080, 0x24040100, 0xac600000, 0x24630004, +0x64102b, 0x5440fffd, 0xac600000, 0x8f830040, +0x3c02f000, 0x621824, 0x3c025000, 0x1062000c, +0x43102b, 0x14400006, 0x3c026000, 0x3c024000, +0x10620008, 0x24020800, 0x10000008, 0x0, +0x10620004, 0x24020800, 0x10000004, 0x0, +0x24020700, 0x3c010001, 0xac226dac, 0x3e00008, +0x0, 0x3c020001, 0x8c426dbc, 0x27bdffd0, +0xafbf002c, 0xafb20028, 0xafb10024, 0xafb00020, +0x3c010001, 0x10400005, 0xac206d94, 0xc004d9e, +0x0, 0x3c010001, 0xac206dbc, 0x8f830054, +0x8f820054, 0x10000002, 0x24630064, 0x8f820054, +0x621023, 0x2c420065, 0x1440fffc, 0x0, +0xc004db9, 0x0, 0x24040001, 0x2821, +0x27a60018, 0x34028000, 0xc0045be, 0xa7a20018, +0x8f830054, 0x8f820054, 0x10000002, 0x24630064, +0x8f820054, 0x621023, 0x2c420065, 0x1440fffc, +0x24040001, 0x24050001, 0xc00457c, 0x27a60018, +0x8f830054, 0x8f820054, 0x10000002, 0x24630064, +0x8f820054, 0x621023, 0x2c420065, 0x1440fffc, +0x24040001, 0x24050001, 0xc00457c, 0x27a60018, +0x8f830054, 0x8f820054, 0x10000002, 0x24630064, +0x8f820054, 0x621023, 0x2c420065, 0x1440fffc, +0x24040001, 0x3c060001, 0x24c66f24, 0xc00457c, +0x24050002, 0x8f830054, 0x8f820054, 0x10000002, +0x24630064, 0x8f820054, 0x621023, 0x2c420065, +0x1440fffc, 0x24040001, 0x24050003, 0x3c100001, +0x26106f26, 0xc00457c, 0x2003021, 0x97a60018, +0x3c070001, 0x94e76f24, 0x3c040001, 0x24846ae0, +0xafa00014, 0x96020000, 0x3c05000d, 0x34a50100, +0xc002b3b, 0xafa20010, 0x97a20018, 0x1040004d, +0x24036040, 0x96020000, 0x3042fff0, 0x1443000c, +0x24020020, 0x3c030001, 0x94636f24, 0x1462000b, +0x24027830, 0x24020003, 0x3c010001, 0xac226d94, +0x24020005, 0x3c010001, 0x1000003f, 0xac226f34, +0x3c030001, 0x94636f24, 0x24027830, 0x1462000c, +0x24030010, 0x3c020001, 0x94426f26, 0x3042fff0, +0x14430007, 0x24020003, 0x3c010001, 0xac226d94, +0x24020006, 0x3c010001, 0x1000002f, 0xac226f34, +0x3c020001, 0x8c426d94, 0x3c030001, 0x94636f24, +0x34420001, 0x3c010001, 0xac226d94, 0x24020015, +0x1462000b, 0x0, 0x3c020001, 0x94426f26, +0x3042fff0, 0x3843f420, 0x2c630001, 0x3842f430, +0x2c420001, 0x621825, 0x1460001b, 0x24020003, +0x3c030001, 0x94636f24, 0x24027810, 0x14620016, +0x24020002, 0x3c020001, 0x94426f26, 0x3042fff0, +0x14400011, 0x24020002, 0x1000000f, 0x24020004, +0x3c020001, 0x8c426d94, 0x34420008, 0x3c010001, +0xac226d94, 0x1000005e, 0x24020004, 0x3c020001, +0x8c426d94, 0x34420004, 0x3c010001, 0x100000af, +0xac226d94, 0x24020001, 0x3c010001, 0xac226f40, +0x3c020001, 0x8c426d94, 0x30420002, 0x144000b2, +0x3c09fff0, 0x24020e00, 0xaf820238, 0x8f840054, +0x8f820054, 0x24030008, 0x3c010001, 0xac236d98, +0x10000002, 0x248401f4, 0x8f820054, 0x821023, +0x2c4201f5, 0x1440fffc, 0x3c0200c8, 0x344201fb, +0xaf820238, 0x8f830054, 0x8f820054, 0x10000002, +0x246301f4, 0x8f820054, 0x621023, 0x2c4201f5, +0x1440fffc, 0x8021, 0x24120001, 0x24110009, +0xc004482, 0x0, 0x3c010001, 0xac326db4, +0xc004547, 0x0, 0x3c020001, 0x8c426db4, +0x1451fffb, 0x3c0200c8, 0x344201f6, 0xaf820238, +0x8f830054, 0x8f820054, 0x10000002, 0x2463000a, +0x8f820054, 0x621023, 0x2c42000b, 0x1440fffc, +0x0, 0x8f820220, 0x24040001, 0x34420002, +0xaf820220, 0x8f830200, 0x24057fff, 0x2402fffd, +0x621824, 0xaf830200, 0xaf840204, 0x8f830054, +0x8f820054, 0x10000002, 0x24630001, 0x8f820054, +0x621023, 0x2c420002, 0x1440fffc, 0x0, +0x8f820224, 0x14440005, 0x34028000, 0x42040, +0xa4102b, 0x1040fff0, 0x34028000, 0x1082ffa0, +0x26100001, 0x2e020014, 0x1440ffcd, 0x24020004, +0x3c010001, 0xac226d98, 0x8021, 0x24120009, +0x3c11ffff, 0x36313f7f, 0xc004482, 0x0, +0x24020001, 0x3c010001, 0xac226db4, 0xc004547, +0x0, 0x3c020001, 0x8c426db4, 0x1452fffb, +0x0, 0x8f820044, 0x511024, 0x34425080, +0xaf820044, 0x8f830054, 0x8f820054, 0x10000002, +0x2463000a, 0x8f820054, 0x621023, 0x2c42000b, +0x1440fffc, 0x0, 0x8f820044, 0x511024, +0x3442f080, 0xaf820044, 0x8f830054, 0x8f820054, +0x10000002, 0x2463000a, 0x8f820054, 0x621023, +0x2c42000b, 0x1440fffc, 0x0, 0x8f820220, +0x3c03f700, 0x431025, 0xaf820220, 0x8f830054, +0x8f820054, 0x10000002, 0x24630064, 0x8f820054, +0x621023, 0x2c420065, 0x1440fffc, 0x0, +0x8f820220, 0x24040001, 0x34420002, 0xaf820220, +0x8f830200, 0x24057fff, 0x2402fffd, 0x621824, +0xaf830200, 0xaf840204, 0x8f830054, 0x8f820054, +0x10000002, 0x24630001, 0x8f820054, 0x621023, +0x2c420002, 0x1440fffc, 0x0, 0x8f820224, +0x14440005, 0x34028000, 0x42040, 0xa4102b, +0x1040fff0, 0x34028000, 0x1082ff50, 0x26100001, +0x2e020064, 0x1440ffb0, 0x0, 0x3c020001, +0x8c426d94, 0x30420004, 0x14400007, 0x3c09fff0, +0x8f820044, 0x3c03ffff, 0x34633f7f, 0x431024, +0xaf820044, 0x3c09fff0, 0x3529bdc0, 0x3c060001, +0x8cc66d94, 0x3c040001, 0x24846ae0, 0x24020001, +0x3c010001, 0xac226d9c, 0x8f820054, 0x3c070001, +0x8ce76f40, 0x3c030001, 0x94636f24, 0x3c080001, +0x95086f26, 0x3c05000d, 0x34a50100, 0x3c010001, +0xac206d98, 0x491021, 0x3c010001, 0xac226f30, +0xafa30010, 0xc002b3b, 0xafa80014, 0x8fbf002c, +0x8fb20028, 0x8fb10024, 0x8fb00020, 0x3e00008, +0x27bd0030, 0x27bdffe8, 0x3c050001, 0x8ca56d98, +0x24060004, 0x24020001, 0x14a20014, 0xafbf0010, +0x3c020002, 0x8c428ffc, 0x30428000, 0x10400005, +0x3c04000f, 0x3c030001, 0x8c636f40, 0x10000005, +0x34844240, 0x3c040004, 0x3c030001, 0x8c636f40, +0x348493e0, 0x24020005, 0x14620016, 0x0, +0x3c04003d, 0x10000013, 0x34840900, 0x3c020002, +0x8c428ff8, 0x30428000, 0x10400005, 0x3c04001e, +0x3c030001, 0x8c636f40, 0x10000005, 0x34848480, +0x3c04000f, 0x3c030001, 0x8c636f40, 0x34844240, +0x24020005, 0x14620003, 0x0, 0x3c04007a, +0x34841200, 0x3c020001, 0x8c426f30, 0x8f830054, +0x441021, 0x431023, 0x44102b, 0x1440004c, +0x0, 0x3c020001, 0x8c426da0, 0x14400048, +0x0, 0x3c010001, 0x10c00025, 0xac206db0, +0x3c090001, 0x8d296d94, 0x24070001, 0x3c044000, +0x3c080002, 0x25088ffc, 0x250afffc, 0x52842, +0x14a00002, 0x24c6ffff, 0x24050008, 0xa91024, +0x10400010, 0x0, 0x14a70008, 0x0, +0x8d020000, 0x441024, 0x1040000a, 0x0, +0x3c010001, 0x10000007, 0xac256db0, 0x8d420000, +0x441024, 0x10400003, 0x0, 0x3c010001, +0xac276db0, 0x3c020001, 0x8c426db0, 0x6182b, +0x2c420001, 0x431024, 0x5440ffe5, 0x52842, +0x8f820054, 0x3c030001, 0x8c636db0, 0x3c010001, +0xac226f30, 0x1060003b, 0x24020005, 0x3c030001, +0x8c636f40, 0x3c010001, 0xac256d98, 0x14620012, +0x24020001, 0x3c020002, 0x8c428ff8, 0x3c032000, +0x34635000, 0x431024, 0x14400006, 0x24020001, +0x3c010001, 0xac206f1c, 0x3c010001, 0xac226d98, +0x24020001, 0x3c010001, 0xac226e24, 0x3c010001, +0xac226da4, 0x24020001, 0x3c010001, 0xac226d9c, +0x3c020001, 0x8c426db0, 0x1040001e, 0x0, +0x3c020001, 0x8c426d9c, 0x10400008, 0x24020001, +0x3c010001, 0xac206d9c, 0xaee204b8, 0x3c010001, +0xac206e1c, 0x3c010001, 0xac226dd4, 0x8ee304b8, +0x24020008, 0x10620005, 0x24020001, 0xc004239, +0x0, 0x1000000b, 0x0, 0x3c030001, +0x8c636d98, 0x10620007, 0x2402000e, 0x3c030002, +0x8c638f90, 0x10620003, 0x0, 0xc004e9c, +0x8f840220, 0x8fbf0010, 0x3e00008, 0x27bd0018, +0x27bdffe0, 0x3c03fdff, 0x3c040001, 0x8c846d98, +0x3c020001, 0x8c426dc0, 0x3463ffff, 0x283a024, +0x14820006, 0xafbf0018, 0x8ee304b8, 0x3c020001, +0x8c426dc4, 0x10620006, 0x0, 0x8ee204b8, +0x3c010001, 0xac246dc0, 0x3c010001, 0xac226dc4, +0x3c030001, 0x8c636d98, 0x24020002, 0x1062019c, +0x2c620003, 0x10400005, 0x24020001, 0x1062000a, +0x0, 0x10000226, 0x0, 0x24020004, +0x106200b6, 0x24020008, 0x1062010a, 0x24020001, +0x1000021f, 0x0, 0x8ee204b8, 0x2443ffff, +0x2c620008, 0x1040021c, 0x31080, 0x3c010001, +0x220821, 0x8c226af8, 0x400008, 0x0, +0x3c030001, 0x8c636f40, 0x24020005, 0x14620010, +0x0, 0x3c020001, 0x8c426da4, 0x10400008, +0x24020003, 0xc004482, 0x0, 0x24020002, +0xaee204b8, 0x3c010001, 0x10000002, 0xac206da4, +0xaee204b8, 0x3c010001, 0x10000203, 0xac206d30, +0xc004482, 0x0, 0x3c020001, 0x8c426da4, +0x3c010001, 0xac206d30, 0x1440017a, 0x24020002, +0x1000019d, 0x24020007, 0x3c030001, 0x8c636f40, +0x24020005, 0x14620003, 0x24020001, 0x3c010001, +0xac226dd0, 0xc0045ff, 0x0, 0x3c030001, +0x8c636dd0, 0x10000174, 0x24020011, 0x3c050001, +0x8ca56d98, 0x3c060002, 0x8cc68ffc, 0xc005104, +0x2021, 0x24020005, 0x3c010001, 0xac206da4, +0x100001e1, 0xaee204b8, 0x3c040001, 0x24846aec, +0x3c05000f, 0x34a50100, 0x3021, 0x3821, +0xafa00010, 0xc002b3b, 0xafa00014, 0x100001d6, +0x0, 0x8f820220, 0x3c030004, 0x431024, +0x14400175, 0x24020007, 0x8f830054, 0x3c020001, +0x8c426f28, 0x2463d8f0, 0x431023, 0x2c422710, +0x14400003, 0x24020001, 0x3c010001, 0xac226d9c, +0x3c020002, 0x8c428ffc, 0x30425000, 0x104001c2, +0x0, 0x8f820220, 0x30428000, 0x1040017d, +0x0, 0x10000175, 0x0, 0x3c050001, +0x8ca56d98, 0xc00529b, 0x2021, 0xc00551b, +0x2021, 0x3c030002, 0x8c638ff4, 0x46101b0, +0x24020001, 0x3c020008, 0x621024, 0x10400006, +0x0, 0x8f820214, 0x3c03ffff, 0x431024, +0x10000005, 0x3442251f, 0x8f820214, 0x3c03ffff, +0x431024, 0x3442241f, 0xaf820214, 0x8f820220, +0x3c030200, 0x34420002, 0xaf820220, 0x24020008, +0xaee204b8, 0x8f820220, 0x283a025, 0x3c030004, +0x431024, 0x14400016, 0x0, 0x3c020002, +0x8c428ffc, 0x30425000, 0x1040000d, 0x0, +0x8f820220, 0x30428000, 0x10400006, 0x0, +0x8f820220, 0x3c03ffff, 0x34637fff, 0x10000003, +0x431024, 0x8f820220, 0x34428000, 0xaf820220, +0x8f820220, 0x3c03f700, 0x431025, 0xaf820220, +0x3c030001, 0x8c636f40, 0x24020005, 0x1462000a, +0x0, 0x3c020001, 0x94426f26, 0x24429fbc, +0x2c420004, 0x10400004, 0x24040018, 0x24050002, +0xc004ddb, 0x24060020, 0xc003e6d, 0x0, +0x3c010001, 0x10000170, 0xac206e20, 0x8ee204b8, +0x2443ffff, 0x2c620008, 0x1040016b, 0x31080, +0x3c010001, 0x220821, 0x8c226b18, 0x400008, +0x0, 0xc004547, 0x0, 0x3c030001, +0x8c636db4, 0x100000e8, 0x24020009, 0x3c020002, +0x8c428ff8, 0x30424000, 0x10400004, 0x0, +0x8f820044, 0x10000006, 0x3442f080, 0x8f820044, +0x3c03ffff, 0x34633f7f, 0x431024, 0x3442a080, +0xaf820044, 0x8f830054, 0x100000ea, 0x24020004, +0x8f830054, 0x3c020001, 0x8c426f28, 0x2463d8f0, +0x431023, 0x2c422710, 0x14400147, 0x24020005, +0x100000d8, 0x0, 0x8f820220, 0x3c03f700, +0x431025, 0xaf820220, 0xaf800204, 0x3c010002, +0x100000d6, 0xac208fe0, 0x8f830054, 0x3c020001, +0x8c426f28, 0x2463fff6, 0x431023, 0x2c42000a, +0x14400135, 0x24020007, 0x100000d7, 0x0, +0xc003f50, 0x0, 0x1040012d, 0x24020001, +0x8f820214, 0x3c03ffff, 0x3c040001, 0x8c846f1c, +0x431024, 0x3442251f, 0xaf820214, 0x24020008, +0x10800005, 0xaee204b8, 0x3c020001, 0x8c426e44, +0x10400064, 0x24020001, 0x8f820220, 0x3c030008, +0x431024, 0x1040006a, 0x3c020200, 0x10000078, +0x0, 0x8ee204b8, 0x2443ffff, 0x2c620007, +0x10400115, 0x31080, 0x3c010001, 0x220821, +0x8c226b38, 0x400008, 0x0, 0xc003daf, +0x0, 0x3c010001, 0xac206d9c, 0xaf800204, +0x3c010002, 0xc004482, 0xac208fe0, 0x24020001, +0x3c010001, 0xac226db4, 0x24020002, 0x10000102, +0xaee204b8, 0xc004547, 0x0, 0x3c030001, +0x8c636db4, 0x10000084, 0x24020009, 0x3c020002, +0x8c428ff8, 0x30424000, 0x10400003, 0x3c0200c8, +0x10000002, 0x344201f6, 0x344201fe, 0xaf820238, +0x8f830054, 0x1000008b, 0x24020004, 0x8f830054, +0x3c020001, 0x8c426f28, 0x2463d8f0, 0x431023, +0x2c422710, 0x144000e8, 0x24020005, 0x10000079, +0x0, 0x8f820220, 0x3c03f700, 0x431025, +0xaf820220, 0xaf800204, 0x3c010002, 0x10000077, +0xac208fe0, 0x8f830054, 0x3c020001, 0x8c426f28, +0x2463fff6, 0x431023, 0x2c42000a, 0x144000d6, +0x24020007, 0x10000078, 0x0, 0xc003f50, +0x0, 0x104000ce, 0x24020001, 0x8f820214, +0x3c03ffff, 0x3c040001, 0x8c846f1c, 0x431024, +0x3442251f, 0xaf820214, 0x24020008, 0x1080000f, +0xaee204b8, 0x3c020001, 0x8c426e44, 0x1440000b, +0x0, 0x8f820220, 0x34420002, 0xaf820220, +0x24020001, 0x3c010002, 0xac228f90, 0xc004e9c, +0x8f840220, 0x10000016, 0x0, 0x8f820220, +0x3c030008, 0x431024, 0x14400011, 0x3c020200, +0x282a025, 0x2402000e, 0x3c010002, 0xac228f90, +0xc00551b, 0x2021, 0x8f820220, 0x34420002, +0xc003e6d, 0xaf820220, 0x3c050001, 0x8ca56d98, +0xc00529b, 0x2021, 0x100000a3, 0x0, +0x3c020001, 0x8c426e44, 0x1040009f, 0x0, +0x3c020001, 0x8c426e40, 0x2442ffff, 0x3c010001, +0xac226e40, 0x14400098, 0x24020002, 0x3c010001, +0xac206e44, 0x3c010001, 0x10000093, 0xac226e40, +0x8ee204b8, 0x2443ffff, 0x2c620007, 0x1040008e, +0x31080, 0x3c010001, 0x220821, 0x8c226b58, +0x400008, 0x0, 0x3c020001, 0x8c426da4, +0x10400018, 0x24020005, 0xc004482, 0x0, +0x24020002, 0xaee204b8, 0x3c010001, 0x1000007e, +0xac206da4, 0xc004963, 0x0, 0x3c030001, +0x8c636dd4, 0x24020006, 0x14620077, 0x24020003, +0x10000075, 0xaee204b8, 0x3c050001, 0x8ca56d98, +0x3c060002, 0x8cc68ff8, 0xc005104, 0x2021, +0x24020005, 0x1000006c, 0xaee204b8, 0x8f820220, +0x3c03f700, 0x431025, 0xaf820220, 0x8f830054, +0x24020006, 0xaee204b8, 0x3c010001, 0x10000062, +0xac236f28, 0x8f820220, 0x3c030004, 0x431024, +0x10400003, 0x24020007, 0x1000005b, 0xaee204b8, +0x8f830054, 0x3c020001, 0x8c426f28, 0x2463d8f0, +0x431023, 0x2c422710, 0x14400003, 0x24020001, +0x3c010001, 0xac226d9c, 0x3c020002, 0x8c428ff8, +0x30425000, 0x1040004c, 0x0, 0x8f820220, +0x30428000, 0x10400007, 0x0, 0x8f820220, +0x3c03ffff, 0x34637fff, 0x431024, 0x10000042, +0xaf820220, 0x8f820220, 0x34428000, 0x1000003e, +0xaf820220, 0x3c050001, 0x8ca56d98, 0xc00529b, +0x2021, 0xc00551b, 0x2021, 0x3c020002, +0x8c428ff0, 0x4410032, 0x24020001, 0x8f820214, +0x3c03ffff, 0x431024, 0x3442251f, 0xaf820214, +0x24020008, 0xaee204b8, 0x8f820220, 0x34420002, +0xaf820220, 0x8f820220, 0x3c030004, 0x431024, +0x14400016, 0x0, 0x3c020002, 0x8c428ff8, +0x30425000, 0x1040000d, 0x0, 0x8f820220, +0x30428000, 0x10400006, 0x0, 0x8f820220, +0x3c03ffff, 0x34637fff, 0x10000003, 0x431024, +0x8f820220, 0x34428000, 0xaf820220, 0x8f820220, +0x3c03f700, 0x431025, 0xaf820220, 0x3c020001, +0x94426f26, 0x24429fbc, 0x2c420004, 0x10400004, +0x24040018, 0x24050002, 0xc004ddb, 0x24060020, +0xc003e6d, 0x0, 0x10000003, 0x0, +0x3c010001, 0xac226d9c, 0x8fbf0018, 0x3e00008, +0x27bd0020, 0x8f820200, 0x8f820220, 0x8f820220, +0x34420004, 0xaf820220, 0x8f820200, 0x3c050001, +0x8ca56d98, 0x34420004, 0xaf820200, 0x24020002, +0x10a2004b, 0x2ca20003, 0x10400005, 0x24020001, +0x10a2000a, 0x0, 0x100000b1, 0x0, +0x24020004, 0x10a20072, 0x24020008, 0x10a20085, +0x3c02f0ff, 0x100000aa, 0x0, 0x8f830050, +0x3c02f0ff, 0x3442ffff, 0x3c040001, 0x8c846f40, +0x621824, 0x3c020700, 0x621825, 0x24020e00, +0x2484fffb, 0x2c840002, 0xaf830050, 0xaf850200, +0xaf850220, 0x14800006, 0xaf820238, 0x8f820044, +0x3c03ffff, 0x34633f7f, 0x431024, 0xaf820044, +0x3c030001, 0x8c636f40, 0x24020005, 0x14620004, +0x0, 0x8f820044, 0x34425000, 0xaf820044, +0x3c020001, 0x8c426d88, 0x3c030001, 0x8c636f40, +0x34420022, 0x2463fffc, 0x2c630002, 0x1460000c, +0xaf820200, 0x3c020001, 0x8c426dac, 0x3c030001, +0x8c636d90, 0x3c040001, 0x8c846d8c, 0x34428000, +0x621825, 0x641825, 0x1000000a, 0x34620002, +0x3c020001, 0x8c426d90, 0x3c030001, 0x8c636dac, +0x3c040001, 0x8c846d8c, 0x431025, 0x441025, +0x34420002, 0xaf820220, 0x1000002f, 0x24020001, +0x24020e01, 0xaf820238, 0x8f830050, 0x3c02f0ff, +0x3442ffff, 0x3c040001, 0x8c846f1c, 0x621824, +0x3c020d00, 0x621825, 0x24020001, 0xaf830050, +0xaf820200, 0xaf820220, 0x10800005, 0x3c033f00, +0x3c020001, 0x8c426d80, 0x10000004, 0x34630070, +0x3c020001, 0x8c426d80, 0x34630072, 0x431025, +0xaf820200, 0x3c030001, 0x8c636d84, 0x3c02f700, +0x621825, 0x3c020001, 0x8c426d90, 0x3c040001, +0x8c846dac, 0x3c050001, 0x8ca56f40, 0x431025, +0x441025, 0xaf820220, 0x24020005, 0x14a20006, +0x24020001, 0x8f820044, 0x2403afff, 0x431024, +0xaf820044, 0x24020001, 0x1000003d, 0xaf820238, +0x8f830050, 0x3c02f0ff, 0x3442ffff, 0x3c040001, +0x8c846f1c, 0x621824, 0x3c020a00, 0x621825, +0x24020001, 0xaf830050, 0xaf820200, 0x1080001e, +0xaf820220, 0x3c020001, 0x8c426e44, 0x1440001a, +0x3c033f00, 0x3c020001, 0x8c426d80, 0x1000001a, +0x346300e0, 0x8f830050, 0x3c040001, 0x8c846f1c, +0x3442ffff, 0x621824, 0x1080000f, 0xaf830050, +0x3c020001, 0x8c426e44, 0x1440000b, 0x3c043f00, +0x3c030001, 0x8c636d80, 0x348400e0, 0x24020001, +0xaf820200, 0xaf820220, 0x641825, 0xaf830200, +0x10000008, 0x3c05f700, 0x3c020001, 0x8c426d80, +0x3c033f00, 0x346300e2, 0x431025, 0xaf820200, +0x3c05f700, 0x34a58000, 0x3c030001, 0x8c636d84, +0x3c020001, 0x8c426d90, 0x3c040001, 0x8c846dac, +0x651825, 0x431025, 0x441025, 0xaf820220, +0x3e00008, 0x0, 0x3c030001, 0x8c636db4, +0x3c020001, 0x8c426db8, 0x10620003, 0x24020002, +0x3c010001, 0xac236db8, 0x1062001d, 0x2c620003, +0x10400025, 0x24020001, 0x14620023, 0x24020004, +0x3c030001, 0x8c636d98, 0x10620006, 0x24020008, +0x1462000c, 0x3c0200c8, 0x344201fb, 0x10000009, +0xaf820238, 0x24020e01, 0xaf820238, 0x8f820044, +0x3c03ffff, 0x34633f7f, 0x431024, 0x34420080, +0xaf820044, 0x8f830054, 0x24020002, 0x3c010001, +0xac226db4, 0x3c010001, 0x1000000b, 0xac236f2c, +0x8f830054, 0x3c020001, 0x8c426f2c, 0x2463d8f0, +0x431023, 0x2c422710, 0x14400003, 0x24020009, +0x3c010001, 0xac226db4, 0x3e00008, 0x0, +0x0, 0x0, 0x0, 0x27bdffd8, +0xafb20018, 0x809021, 0xafb3001c, 0xa09821, +0xafb10014, 0xc08821, 0xafb00010, 0x8021, +0xafbf0020, 0xa6200000, 0xc004d78, 0x24040001, +0x26100001, 0x2e020020, 0x1440fffb, 0x0, +0xc004d78, 0x2021, 0xc004d78, 0x24040001, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0x24100010, 0x2501024, 0x10400002, 0x2021, +0x24040001, 0xc004d78, 0x108042, 0x1600fffa, +0x2501024, 0x24100010, 0x2701024, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fffa, 0x2701024, 0xc004db9, 0x34108000, +0xc004db9, 0x0, 0xc004d58, 0x0, +0x50400005, 0x108042, 0x96220000, 0x501025, +0xa6220000, 0x108042, 0x1600fff7, 0x0, +0xc004db9, 0x0, 0x8fbf0020, 0x8fb3001c, +0x8fb20018, 0x8fb10014, 0x8fb00010, 0x3e00008, +0x27bd0028, 0x27bdffd8, 0xafb10014, 0x808821, +0xafb20018, 0xa09021, 0xafb3001c, 0xc09821, +0xafb00010, 0x8021, 0xafbf0020, 0xc004d78, +0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, +0x0, 0xc004d78, 0x2021, 0xc004d78, +0x24040001, 0xc004d78, 0x2021, 0xc004d78, +0x24040001, 0x24100010, 0x2301024, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fffa, 0x2301024, 0x24100010, 0x2501024, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x2501024, 0xc004d78, +0x24040001, 0xc004d78, 0x2021, 0x34108000, +0x96620000, 0x501024, 0x10400002, 0x2021, +0x24040001, 0xc004d78, 0x108042, 0x1600fff8, +0x0, 0xc004db9, 0x0, 0x8fbf0020, +0x8fb3001c, 0x8fb20018, 0x8fb10014, 0x8fb00010, +0x3e00008, 0x27bd0028, 0x3c040001, 0x8c846dd0, +0x3c020001, 0x8c426e18, 0x27bdffd8, 0xafbf0020, +0xafb1001c, 0x10820003, 0xafb00018, 0x3c010001, +0xac246e18, 0x3c030001, 0x8c636f40, 0x24020005, +0x14620005, 0x2483ffff, 0xc004963, 0x0, +0x1000034c, 0x0, 0x2c620013, 0x10400349, +0x31080, 0x3c010001, 0x220821, 0x8c226b80, +0x400008, 0x0, 0xc004db9, 0x8021, +0x34028000, 0xa7a20010, 0x27b10010, 0xc004d78, +0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, +0x0, 0xc004d78, 0x2021, 0xc004d78, +0x24040001, 0xc004d78, 0x2021, 0xc004d78, +0x24040001, 0x24100010, 0x32020001, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fffa, 0x32020001, 0x24100010, 0xc004d78, +0x2021, 0x108042, 0x1600fffc, 0x0, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0x34108000, 0x96220000, 0x501024, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fff8, 0x0, 0xc004db9, 0x0, +0x1000030e, 0x24020002, 0x27b10010, 0xa7a00010, +0x8021, 0xc004d78, 0x24040001, 0x26100001, +0x2e020020, 0x1440fffb, 0x0, 0xc004d78, +0x2021, 0xc004d78, 0x24040001, 0xc004d78, +0x24040001, 0xc004d78, 0x2021, 0x24100010, +0x32020001, 0x10400002, 0x2021, 0x24040001, +0xc004d78, 0x108042, 0x1600fffa, 0x32020001, +0x24100010, 0xc004d78, 0x2021, 0x108042, +0x1600fffc, 0x0, 0xc004db9, 0x34108000, +0xc004db9, 0x0, 0xc004d58, 0x0, +0x50400005, 0x108042, 0x96220000, 0x501025, +0xa6220000, 0x108042, 0x1600fff7, 0x0, +0xc004db9, 0x0, 0x97a20010, 0x30428000, +0x144002dc, 0x24020003, 0x100002d8, 0x0, +0x24021200, 0xa7a20010, 0x27b10010, 0x8021, +0xc004d78, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0xc004d78, 0x2021, 0x108042, 0x1600fffc, +0x0, 0xc004d78, 0x24040001, 0xc004d78, +0x2021, 0x34108000, 0x96220000, 0x501024, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fff8, 0x0, 0xc004db9, +0x0, 0x8f830054, 0x10000296, 0x24020004, +0x8f830054, 0x3c020001, 0x8c426f3c, 0x2463ff9c, +0x431023, 0x2c420064, 0x1440029e, 0x24020002, +0x3c030001, 0x8c636f40, 0x10620297, 0x2c620003, +0x14400296, 0x24020011, 0x24020003, 0x10620005, +0x24020004, 0x10620291, 0x2402000f, 0x1000028f, +0x24020011, 0x1000028d, 0x24020005, 0x24020014, +0xa7a20010, 0x27b10010, 0x8021, 0xc004d78, +0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, +0x0, 0xc004d78, 0x2021, 0xc004d78, +0x24040001, 0xc004d78, 0x2021, 0xc004d78, +0x24040001, 0x24100010, 0x32020001, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fffa, 0x32020001, 0x24100010, 0x32020012, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x32020012, 0xc004d78, +0x24040001, 0xc004d78, 0x2021, 0x34108000, +0x96220000, 0x501024, 0x10400002, 0x2021, +0x24040001, 0xc004d78, 0x108042, 0x1600fff8, +0x0, 0xc004db9, 0x0, 0x8f830054, +0x10000248, 0x24020006, 0x8f830054, 0x3c020001, +0x8c426f3c, 0x2463ff9c, 0x431023, 0x2c420064, +0x14400250, 0x24020007, 0x1000024c, 0x0, +0x24020006, 0xa7a20010, 0x27b10010, 0x8021, +0xc004d78, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0x32020013, 0x10400002, 0x2021, 0x24040001, +0xc004d78, 0x108042, 0x1600fffa, 0x32020013, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0x34108000, 0x96220000, 0x501024, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fff8, 0x0, 0xc004db9, 0x0, +0x8f830054, 0x10000207, 0x24020008, 0x8f830054, +0x3c020001, 0x8c426f3c, 0x2463ff9c, 0x431023, +0x2c420064, 0x1440020f, 0x24020009, 0x1000020b, +0x0, 0x27b10010, 0xa7a00010, 0x8021, +0xc004d78, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0xc004d78, 0x24040001, +0xc004d78, 0x2021, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0x32020018, 0x10400002, 0x2021, 0x24040001, +0xc004d78, 0x108042, 0x1600fffa, 0x32020018, +0xc004db9, 0x34108000, 0xc004db9, 0x0, +0xc004d58, 0x0, 0x50400005, 0x108042, +0x96220000, 0x501025, 0xa6220000, 0x108042, +0x1600fff7, 0x0, 0xc004db9, 0x8021, +0x97a20010, 0x27b10010, 0x34420001, 0xa7a20010, +0xc004d78, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0x32020018, 0x10400002, 0x2021, 0x24040001, +0xc004d78, 0x108042, 0x1600fffa, 0x32020018, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0x34108000, 0x96220000, 0x501024, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fff8, 0x0, 0xc004db9, 0x0, +0x8f830054, 0x10000193, 0x2402000a, 0x8f830054, +0x3c020001, 0x8c426f3c, 0x2463ff9c, 0x431023, +0x2c420064, 0x1440019b, 0x2402000b, 0x10000197, +0x0, 0x27b10010, 0xa7a00010, 0x8021, +0xc004d78, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0xc004d78, 0x24040001, +0xc004d78, 0x2021, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0x32020017, 0x10400002, 0x2021, 0x24040001, +0xc004d78, 0x108042, 0x1600fffa, 0x32020017, +0xc004db9, 0x34108000, 0xc004db9, 0x0, +0xc004d58, 0x0, 0x50400005, 0x108042, +0x96220000, 0x501025, 0xa6220000, 0x108042, +0x1600fff7, 0x0, 0xc004db9, 0x8021, +0x97a20010, 0x27b10010, 0x34420700, 0xa7a20010, +0xc004d78, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0x32020017, 0x10400002, 0x2021, 0x24040001, +0xc004d78, 0x108042, 0x1600fffa, 0x32020017, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0x34108000, 0x96220000, 0x501024, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fff8, 0x0, 0xc004db9, 0x0, +0x8f830054, 0x1000011f, 0x2402000c, 0x8f830054, +0x3c020001, 0x8c426f3c, 0x2463ff9c, 0x431023, +0x2c420064, 0x14400127, 0x24020012, 0x10000123, +0x0, 0x27b10010, 0xa7a00010, 0x8021, +0xc004d78, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0xc004d78, 0x24040001, +0xc004d78, 0x2021, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0x32020014, 0x10400002, 0x2021, 0x24040001, +0xc004d78, 0x108042, 0x1600fffa, 0x32020014, +0xc004db9, 0x34108000, 0xc004db9, 0x0, +0xc004d58, 0x0, 0x50400005, 0x108042, +0x96220000, 0x501025, 0xa6220000, 0x108042, +0x1600fff7, 0x0, 0xc004db9, 0x8021, +0x97a20010, 0x27b10010, 0x34420010, 0xa7a20010, +0xc004d78, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0x32020014, 0x10400002, 0x2021, 0x24040001, +0xc004d78, 0x108042, 0x1600fffa, 0x32020014, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0x34108000, 0x96220000, 0x501024, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fff8, 0x0, 0xc004db9, 0x0, +0x8f830054, 0x100000ab, 0x24020013, 0x8f830054, +0x3c020001, 0x8c426f3c, 0x2463ff9c, 0x431023, +0x2c420064, 0x144000b3, 0x2402000d, 0x100000af, +0x0, 0x27b10010, 0xa7a00010, 0x8021, +0xc004d78, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0xc004d78, 0x24040001, +0xc004d78, 0x2021, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0x32020018, 0x10400002, 0x2021, 0x24040001, +0xc004d78, 0x108042, 0x1600fffa, 0x32020018, +0xc004db9, 0x34108000, 0xc004db9, 0x0, +0xc004d58, 0x0, 0x50400005, 0x108042, +0x96220000, 0x501025, 0xa6220000, 0x108042, +0x1600fff7, 0x0, 0xc004db9, 0x8021, +0x97a20010, 0x27b10010, 0x3042fffe, 0xa7a20010, +0xc004d78, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0x32020018, 0x10400002, 0x2021, 0x24040001, +0xc004d78, 0x108042, 0x1600fffa, 0x32020018, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0x34108000, 0x96220000, 0x501024, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fff8, 0x0, 0xc004db9, 0x0, +0x8f830054, 0x10000037, 0x2402000e, 0x24020840, +0xa7a20010, 0x27b10010, 0x8021, 0xc004d78, +0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, +0x0, 0xc004d78, 0x2021, 0xc004d78, +0x24040001, 0xc004d78, 0x2021, 0xc004d78, +0x24040001, 0x24100010, 0x32020001, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fffa, 0x32020001, 0x24100010, 0x32020013, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x32020013, 0xc004d78, +0x24040001, 0xc004d78, 0x2021, 0x34108000, +0x96220000, 0x501024, 0x10400002, 0x2021, +0x24040001, 0xc004d78, 0x108042, 0x1600fff8, +0x0, 0xc004db9, 0x0, 0x8f830054, +0x24020010, 0x3c010001, 0xac226dd0, 0x3c010001, +0x1000000c, 0xac236f3c, 0x8f830054, 0x3c020001, +0x8c426f3c, 0x2463ff9c, 0x431023, 0x2c420064, +0x14400004, 0x0, 0x24020011, 0x3c010001, +0xac226dd0, 0x8fbf0020, 0x8fb1001c, 0x8fb00018, +0x3e00008, 0x27bd0028, 0x3c030001, 0x8c636d98, +0x27bdffc8, 0x24020002, 0xafbf0034, 0xafb20030, +0xafb1002c, 0x14620004, 0xafb00028, 0x3c120002, +0x10000003, 0x8e528ff8, 0x3c120002, 0x8e528ffc, +0x3c030001, 0x8c636dd4, 0x3c020001, 0x8c426e1c, +0x50620004, 0x2463ffff, 0x3c010001, 0xac236e1c, +0x2463ffff, 0x2c620006, 0x10400377, 0x31080, +0x3c010001, 0x220821, 0x8c226bd8, 0x400008, +0x0, 0x2021, 0x2821, 0xc004ddb, +0x34068000, 0x24040010, 0x24050002, 0x24060002, +0x24020002, 0xc004ddb, 0xa7a20018, 0x24020002, +0x3c010001, 0x10000364, 0xac226dd4, 0x27b10018, +0xa7a00018, 0x8021, 0xc004d78, 0x24040001, +0x26100001, 0x2e020020, 0x1440fffb, 0x0, +0xc004d78, 0x2021, 0xc004d78, 0x24040001, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0x24100010, 0x32020001, 0x10400002, 0x2021, +0x24040001, 0xc004d78, 0x108042, 0x1600fffa, +0x32020001, 0x24100010, 0xc004d78, 0x2021, +0x108042, 0x1600fffc, 0x0, 0xc004db9, +0x34108000, 0xc004db9, 0x0, 0xc004d58, +0x0, 0x50400005, 0x108042, 0x96220000, +0x501025, 0xa6220000, 0x108042, 0x1600fff7, +0x0, 0xc004db9, 0x0, 0x97a20018, +0x30428000, 0x14400004, 0x24020003, 0x3c010001, +0xac226dd4, 0x24020003, 0x3c010001, 0x1000032a, +0xac226dd4, 0x24040010, 0x24050002, 0x24060002, +0x24020002, 0xc004ddb, 0xa7a20018, 0x3c030001, +0x8c636e20, 0x24020001, 0x146201e1, 0x8021, +0x27b10018, 0xa7a00018, 0xc004d78, 0x24040001, +0x26100001, 0x2e020020, 0x1440fffb, 0x0, +0xc004d78, 0x2021, 0xc004d78, 0x24040001, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0x24100010, 0x32020001, 0x10400002, 0x2021, +0x24040001, 0xc004d78, 0x108042, 0x1600fffa, +0x32020001, 0x24100010, 0x32020018, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fffa, 0x32020018, 0xc004db9, 0x34108000, +0xc004db9, 0x0, 0xc004d58, 0x0, +0x50400005, 0x108042, 0x96220000, 0x501025, +0xa6220000, 0x108042, 0x1600fff7, 0x0, +0xc004db9, 0x8021, 0x27b10018, 0xa7a00018, +0xc004d78, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0xc004d78, 0x24040001, +0xc004d78, 0x2021, 0x24100010, 0x32020001, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x32020001, 0x24100010, +0x32020018, 0x10400002, 0x2021, 0x24040001, +0xc004d78, 0x108042, 0x1600fffa, 0x32020018, +0xc004db9, 0x34108000, 0xc004db9, 0x0, +0xc004d58, 0x0, 0x50400005, 0x108042, +0x96220000, 0x501025, 0xa6220000, 0x108042, +0x1600fff7, 0x0, 0xc004db9, 0x8021, +0x24040018, 0x2821, 0xc004ddb, 0x24060404, +0xa7a0001a, 0xc004d78, 0x24040001, 0x26100001, +0x2e020020, 0x1440fffb, 0x0, 0xc004d78, +0x2021, 0xc004d78, 0x24040001, 0xc004d78, +0x24040001, 0xc004d78, 0x2021, 0x24100010, +0x32020001, 0x10400002, 0x2021, 0x24040001, +0xc004d78, 0x108042, 0x1600fffa, 0x32020001, +0x24100010, 0x32020018, 0x10400002, 0x2021, +0x24040001, 0xc004d78, 0x108042, 0x1600fffa, +0x32020018, 0xc004db9, 0x34108000, 0xc004db9, +0x0, 0xc004d58, 0x0, 0x50400005, +0x108042, 0x97a2001a, 0x501025, 0xa7a2001a, +0x108042, 0x1600fff7, 0x0, 0xc004db9, +0x8021, 0xa7a0001a, 0xc004d78, 0x24040001, +0x26100001, 0x2e020020, 0x1440fffb, 0x0, +0xc004d78, 0x2021, 0xc004d78, 0x24040001, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0x24100010, 0x32020001, 0x10400002, 0x2021, +0x24040001, 0xc004d78, 0x108042, 0x1600fffa, +0x32020001, 0x24100010, 0x32020018, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fffa, 0x32020018, 0xc004db9, 0x34108000, +0xc004db9, 0x0, 0xc004d58, 0x0, +0x50400005, 0x108042, 0x97a2001a, 0x501025, +0xa7a2001a, 0x108042, 0x1600fff7, 0x0, +0xc004db9, 0x8021, 0xa7a0001c, 0xc004d78, +0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, +0x0, 0xc004d78, 0x2021, 0xc004d78, +0x24040001, 0xc004d78, 0x24040001, 0xc004d78, +0x2021, 0x24100010, 0xc004d78, 0x2021, +0x108042, 0x1600fffc, 0x0, 0x24100010, +0x3202001e, 0x10400002, 0x2021, 0x24040001, +0xc004d78, 0x108042, 0x1600fffa, 0x3202001e, +0xc004db9, 0x34108000, 0xc004db9, 0x0, +0xc004d58, 0x0, 0x50400005, 0x108042, +0x97a2001c, 0x501025, 0xa7a2001c, 0x108042, +0x1600fff7, 0x0, 0xc004db9, 0x8021, +0xa7a0001c, 0xc004d78, 0x24040001, 0x26100001, +0x2e020020, 0x1440fffb, 0x0, 0xc004d78, +0x2021, 0xc004d78, 0x24040001, 0xc004d78, +0x24040001, 0xc004d78, 0x2021, 0x24100010, +0xc004d78, 0x2021, 0x108042, 0x1600fffc, +0x0, 0x24100010, 0x3202001e, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fffa, 0x3202001e, 0xc004db9, 0x34108000, +0xc004db9, 0x0, 0xc004d58, 0x0, +0x50400005, 0x108042, 0x97a2001c, 0x501025, +0xa7a2001c, 0x108042, 0x1600fff7, 0x0, +0xc004db9, 0x8021, 0x24020002, 0xa7a2001e, +0xc004d78, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0x24100010, 0xc004d78, +0x2021, 0x108042, 0x1600fffc, 0x0, +0x24100010, 0x3202001e, 0x10400002, 0x2021, +0x24040001, 0xc004d78, 0x108042, 0x1600fffa, +0x3202001e, 0xc004d78, 0x24040001, 0xc004d78, +0x2021, 0x34108000, 0x97a2001e, 0x501024, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fff8, 0x0, 0xc004db9, +0x8021, 0xa7a00020, 0xc004d78, 0x24040001, +0x26100001, 0x2e020020, 0x1440fffb, 0x0, +0xc004d78, 0x2021, 0xc004d78, 0x24040001, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0x24100010, 0xc004d78, 0x2021, 0x108042, +0x1600fffc, 0x0, 0x24100010, 0x3202001e, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x3202001e, 0xc004db9, +0x34108000, 0xc004db9, 0x0, 0xc004d58, +0x0, 0x50400005, 0x108042, 0x97a20020, +0x501025, 0xa7a20020, 0x108042, 0x1600fff7, +0x0, 0xc004db9, 0x8021, 0xa7a00020, +0xc004d78, 0x24040001, 0x26100001, 0x2e020020, +0x1440fffb, 0x0, 0xc004d78, 0x2021, +0xc004d78, 0x24040001, 0xc004d78, 0x24040001, +0xc004d78, 0x2021, 0x24100010, 0xc004d78, +0x2021, 0x108042, 0x1600fffc, 0x0, +0x24100010, 0x3202001e, 0x10400002, 0x2021, +0x24040001, 0xc004d78, 0x108042, 0x1600fffa, +0x3202001e, 0xc004db9, 0x34108000, 0xc004db9, +0x0, 0xc004d58, 0x0, 0x50400005, +0x108042, 0x97a20020, 0x501025, 0xa7a20020, +0x108042, 0x1600fff7, 0x0, 0xc004db9, +0x8021, 0xa7a00022, 0xc004d78, 0x24040001, +0x26100001, 0x2e020020, 0x1440fffb, 0x0, +0xc004d78, 0x2021, 0xc004d78, 0x24040001, +0xc004d78, 0x2021, 0xc004d78, 0x24040001, +0x24100010, 0xc004d78, 0x2021, 0x108042, +0x1600fffc, 0x0, 0x24100010, 0xc004d78, +0x2021, 0x108042, 0x1600fffc, 0x0, +0xc004d78, 0x24040001, 0xc004d78, 0x2021, +0x34108000, 0x97a20022, 0x501024, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fff8, 0x0, 0xc004db9, 0x0, +0x24040018, 0x24050002, 0xc004ddb, 0x24060004, +0x3c100001, 0x8e106e24, 0x24020001, 0x1602011d, +0x0, 0x3c020001, 0x94426f26, 0x3c010001, +0xac206e24, 0x24429fbc, 0x2c420004, 0x1040000c, +0x24040009, 0x24050001, 0xc004ddb, 0x24060400, +0x24040018, 0x24050001, 0xc004ddb, 0x24060020, +0x24040018, 0x24050001, 0xc004ddb, 0x24062000, +0x3c024000, 0x2421024, 0x10400123, 0x3c022000, +0x2421024, 0x10400004, 0x0, 0x3c010001, +0x10000003, 0xac306f1c, 0x3c010001, 0xac206f1c, +0x3c030001, 0x8c636f34, 0x24020005, 0x146200f9, +0x0, 0x3c020001, 0x8c426f1c, 0x10400067, +0x3c020004, 0x2421024, 0x10400011, 0xa7a00018, +0x3c020008, 0x2421024, 0x10400002, 0x24020200, +0xa7a20018, 0x3c020010, 0x2421024, 0x10400004, +0x0, 0x97a20018, 0x34420100, 0xa7a20018, +0x97a60018, 0x24040009, 0x10000004, 0x2821, +0x24040009, 0x2821, 0x3021, 0xc004ddb, +0x0, 0x24020001, 0xa7a2001a, 0x3c020008, +0x2421024, 0x1040000c, 0x3c020002, 0x2421024, +0x10400002, 0x24020101, 0xa7a2001a, 0x3c020001, +0x2421024, 0x10400005, 0x3c020010, 0x97a2001a, +0x34420040, 0xa7a2001a, 0x3c020010, 0x2421024, +0x1040000e, 0x3c020002, 0x2421024, 0x10400005, +0x3c020001, 0x97a2001a, 0x34420080, 0xa7a2001a, +0x3c020001, 0x2421024, 0x10400005, 0x3c0300a0, +0x97a2001a, 0x34420020, 0xa7a2001a, 0x3c0300a0, +0x2431024, 0x54430004, 0x3c020020, 0x97a2001a, +0x1000000c, 0x34420400, 0x2421024, 0x50400004, +0x3c020080, 0x97a2001a, 0x10000006, 0x34420800, +0x2421024, 0x10400004, 0x0, 0x97a2001a, +0x34420c00, 0xa7a2001a, 0x97a6001a, 0x24040004, +0xc004ddb, 0x2821, 0x3c020004, 0x2421024, +0x10400004, 0xa7a0001c, 0x32425000, 0x14400004, +0x0, 0x32424000, 0x10400005, 0x2021, +0xc004cf9, 0x2402021, 0x10000096, 0x0, +0x97a6001c, 0x2821, 0x34c61200, 0xc004ddb, +0xa7a6001c, 0x1000008f, 0x0, 0x2421024, +0x10400004, 0xa7a00018, 0x32425000, 0x14400004, +0x0, 0x32424000, 0x10400005, 0x3c020010, +0xc004cf9, 0x2402021, 0x10000019, 0xa7a0001a, +0x2421024, 0x10400004, 0x0, 0x97a20018, +0x10000004, 0xa7a20018, 0x97a20018, 0x34420100, +0xa7a20018, 0x3c020001, 0x2421024, 0x10400004, +0x0, 0x97a20018, 0x10000004, 0xa7a20018, +0x97a20018, 0x34422000, 0xa7a20018, 0x97a60018, +0x2021, 0xc004ddb, 0x2821, 0xa7a0001a, +0x8021, 0xc004d78, 0x24040001, 0x26100001, +0x2e020020, 0x1440fffb, 0x0, 0xc004d78, +0x2021, 0xc004d78, 0x24040001, 0xc004d78, +0x24040001, 0xc004d78, 0x2021, 0x24100010, +0x32020001, 0x10400002, 0x2021, 0x24040001, +0xc004d78, 0x108042, 0x1600fffa, 0x32020001, +0x24100010, 0xc004d78, 0x2021, 0x108042, +0x1600fffc, 0x0, 0xc004db9, 0x34108000, +0xc004db9, 0x0, 0xc004d58, 0x0, +0x50400005, 0x108042, 0x97a2001a, 0x501025, +0xa7a2001a, 0x108042, 0x1600fff7, 0x0, +0xc004db9, 0x8021, 0xa7a0001a, 0xc004d78, +0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, +0x0, 0xc004d78, 0x2021, 0xc004d78, +0x24040001, 0xc004d78, 0x24040001, 0xc004d78, +0x2021, 0x24100010, 0x32020001, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fffa, 0x32020001, 0x24100010, 0xc004d78, +0x2021, 0x108042, 0x1600fffc, 0x0, +0xc004db9, 0x34108000, 0xc004db9, 0x0, +0xc004d58, 0x0, 0x50400005, 0x108042, +0x97a2001a, 0x501025, 0xa7a2001a, 0x108042, +0x1600fff7, 0x0, 0xc004db9, 0x0, +0x3c040001, 0x24846bcc, 0x97a60018, 0x97a7001a, +0x3c020001, 0x8c426d98, 0x3c030001, 0x8c636f1c, +0x3c05000d, 0x34a50205, 0xafa20010, 0xc002b3b, +0xafa30014, 0x8f830054, 0x24020004, 0x3c010001, +0xac226dd4, 0x3c010001, 0x10000017, 0xac236f38, +0x8f830054, 0x3c020001, 0x8c426f38, 0x2463ff9c, +0x431023, 0x2c420064, 0x1440000f, 0x0, +0x8f820220, 0x24030005, 0x3c010001, 0xac236dd4, +0x3c03f700, 0x431025, 0x10000007, 0xaf820220, +0x24020006, 0x3c010001, 0xac226dd4, 0x24020011, +0x3c010001, 0xac226dd0, 0x8fbf0034, 0x8fb20030, +0x8fb1002c, 0x8fb00028, 0x3e00008, 0x27bd0038, +0x27bdffd8, 0xafb00018, 0x808021, 0xafb1001c, +0x8821, 0x32024000, 0x10400013, 0xafbf0020, +0x3c020010, 0x2021024, 0x2c420001, 0x21023, +0x30434100, 0x3c020001, 0x2021024, 0x14400006, +0x34714000, 0x3c020002, 0x2021024, 0x14400002, +0x34716000, 0x34714040, 0x2021, 0x2821, +0x10000036, 0x2203021, 0x32021000, 0x10400035, +0x2021, 0x2821, 0xc004ddb, 0x24060040, +0x24040018, 0x2821, 0xc004ddb, 0x24060c00, +0x24040017, 0x2821, 0xc004ddb, 0x24060400, +0x24040016, 0x2821, 0xc004ddb, 0x24060006, +0x24040017, 0x2821, 0xc004ddb, 0x24062500, +0x24040016, 0x2821, 0xc004ddb, 0x24060006, +0x24040017, 0x2821, 0xc004ddb, 0x24064600, +0x24040016, 0x2821, 0xc004ddb, 0x24060006, +0x24040017, 0x2821, 0xc004ddb, 0x24066700, +0x24040016, 0x2821, 0xc004ddb, 0x24060006, +0x2404001f, 0x2821, 0xc004ddb, 0x24060010, +0x24040009, 0x2821, 0xc004ddb, 0x24061500, +0x24040009, 0x2821, 0x24061d00, 0xc004ddb, +0x0, 0x3c040001, 0x24846bf0, 0x3c05000e, +0x34a50100, 0x2003021, 0x2203821, 0xafa00010, +0xc002b3b, 0xafa00014, 0x8fbf0020, 0x8fb1001c, +0x8fb00018, 0x3e00008, 0x27bd0028, 0x8f850044, +0x8f820044, 0x3c030001, 0x431025, 0x3c030008, +0xaf820044, 0x8f840054, 0x8f820054, 0xa32824, +0x10000002, 0x24840001, 0x8f820054, 0x821023, +0x2c420002, 0x1440fffc, 0x0, 0x8f820044, +0x3c03fffe, 0x3463ffff, 0x431024, 0xaf820044, +0x8f830054, 0x8f820054, 0x10000002, 0x24630001, +0x8f820054, 0x621023, 0x2c420002, 0x1440fffc, +0x0, 0x3e00008, 0xa01021, 0x8f830044, +0x3c02fff0, 0x3442ffff, 0x42480, 0x621824, +0x3c020002, 0x822025, 0x641825, 0xaf830044, +0x8f820044, 0x3c03fffe, 0x3463ffff, 0x431024, +0xaf820044, 0x8f830054, 0x8f820054, 0x10000002, +0x24630001, 0x8f820054, 0x621023, 0x2c420002, +0x1440fffc, 0x0, 0x8f820044, 0x3c030001, +0x431025, 0xaf820044, 0x8f830054, 0x8f820054, +0x10000002, 0x24630001, 0x8f820054, 0x621023, +0x2c420002, 0x1440fffc, 0x0, 0x3e00008, +0x0, 0x8f820044, 0x2403ff7f, 0x431024, +0xaf820044, 0x8f830054, 0x8f820054, 0x10000002, +0x24630001, 0x8f820054, 0x621023, 0x2c420002, +0x1440fffc, 0x0, 0x8f820044, 0x34420080, +0xaf820044, 0x8f830054, 0x8f820054, 0x10000002, +0x24630001, 0x8f820054, 0x621023, 0x2c420002, +0x1440fffc, 0x0, 0x3e00008, 0x0, +0x8f820044, 0x3c03fff0, 0x3463ffff, 0x431024, +0xaf820044, 0x8f820044, 0x3c030001, 0x431025, +0xaf820044, 0x8f830054, 0x8f820054, 0x10000002, +0x24630001, 0x8f820054, 0x621023, 0x2c420002, +0x1440fffc, 0x0, 0x8f820044, 0x3c03fffe, +0x3463ffff, 0x431024, 0xaf820044, 0x8f830054, +0x8f820054, 0x10000002, 0x24630001, 0x8f820054, +0x621023, 0x2c420002, 0x1440fffc, 0x0, +0x3e00008, 0x0, 0x27bdffc8, 0xafb30024, +0x809821, 0xafbe002c, 0xa0f021, 0xafb20020, +0xc09021, 0x33c2ffff, 0xafbf0030, 0xafb50028, +0xafb1001c, 0xafb00018, 0x14400034, 0xa7b20010, +0x3271ffff, 0x27b20010, 0x8021, 0xc004d78, +0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, +0x0, 0xc004d78, 0x2021, 0xc004d78, +0x24040001, 0xc004d78, 0x2021, 0xc004d78, +0x24040001, 0x24100010, 0x32020001, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fffa, 0x32020001, 0x24100010, 0x2301024, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x2301024, 0xc004d78, +0x24040001, 0xc004d78, 0x2021, 0x34108000, +0x96420000, 0x501024, 0x10400002, 0x2021, +0x24040001, 0xc004d78, 0x108042, 0x12000075, +0x0, 0x1000fff6, 0x0, 0x3275ffff, +0x27b10010, 0xa7a00010, 0x8021, 0xc004d78, +0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, +0x0, 0xc004d78, 0x2021, 0xc004d78, +0x24040001, 0xc004d78, 0x24040001, 0xc004d78, +0x2021, 0x24100010, 0x32020001, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fffa, 0x32020001, 0x24100010, 0x2b01024, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x2b01024, 0xc004db9, +0x34108000, 0xc004db9, 0x0, 0xc004d58, +0x0, 0x50400005, 0x108042, 0x96220000, +0x501025, 0xa6220000, 0x108042, 0x1600fff7, +0x0, 0xc004db9, 0x0, 0x33c5ffff, +0x24020001, 0x54a20004, 0x24020002, 0x97a20010, +0x10000006, 0x521025, 0x14a20006, 0x3271ffff, +0x97a20010, 0x121827, 0x431024, 0xa7a20010, +0x3271ffff, 0x27b20010, 0x8021, 0xc004d78, +0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, +0x0, 0xc004d78, 0x2021, 0xc004d78, +0x24040001, 0xc004d78, 0x2021, 0xc004d78, +0x24040001, 0x24100010, 0x32020001, 0x10400002, +0x2021, 0x24040001, 0xc004d78, 0x108042, +0x1600fffa, 0x32020001, 0x24100010, 0x2301024, +0x10400002, 0x2021, 0x24040001, 0xc004d78, +0x108042, 0x1600fffa, 0x2301024, 0xc004d78, +0x24040001, 0xc004d78, 0x2021, 0x34108000, +0x96420000, 0x501024, 0x10400002, 0x2021, +0x24040001, 0xc004d78, 0x108042, 0x1600fff8, +0x0, 0xc004db9, 0x0, 0x8fbf0030, +0x8fbe002c, 0x8fb50028, 0x8fb30024, 0x8fb20020, +0x8fb1001c, 0x8fb00018, 0x3e00008, 0x27bd0038, +0x0, 0x0, 0x0, 0x27bdffe8, +0xafbf0010, 0x8ee304b8, 0x24020008, 0x146201e0, +0x0, 0x3c020001, 0x8c426f1c, 0x14400005, +0x0, 0xc003daf, 0x8f840224, 0x100001d8, +0x0, 0x8f820220, 0x3c030008, 0x431024, +0x10400026, 0x24020001, 0x8f840224, 0x8f820220, +0x3c030400, 0x431024, 0x10400006, 0x0, +0x3c010002, 0xac208fa0, 0x3c010002, 0x1000000b, +0xac208fc0, 0x3c030002, 0x24638fa0, 0x8c620000, +0x24420001, 0xac620000, 0x2c420002, 0x14400003, +0x24020001, 0x3c010002, 0xac228fc0, 0x3c020002, +0x8c428fc0, 0x10400006, 0x30820040, 0x10400004, +0x24020001, 0x3c010002, 0x10000003, 0xac228fc4, +0x3c010002, 0xac208fc4, 0x3c010002, 0xac248f9c, +0x3c010002, 0x1000000b, 0xac208fd0, 0x3c010002, +0xac228fd0, 0x3c010002, 0xac208fc0, 0x3c010002, +0xac208fa0, 0x3c010002, 0xac208fc4, 0x3c010002, +0xac208f9c, 0x3c030002, 0x8c638f90, 0x3c020002, +0x8c428f94, 0x50620004, 0x2463ffff, 0x3c010002, +0xac238f94, 0x2463ffff, 0x2c62000e, 0x10400194, +0x31080, 0x3c010001, 0x220821, 0x8c226c00, +0x400008, 0x0, 0x24020002, 0x3c010002, +0xac208fc0, 0x3c010002, 0xac208fa0, 0x3c010002, +0xac208f9c, 0x3c010002, 0xac208fc4, 0x3c010002, +0xac208fb8, 0x3c010002, 0xac208fb0, 0xaf800224, +0x3c010002, 0xac228f90, 0x3c020002, 0x8c428fd0, +0x1440004f, 0x3c02fdff, 0x3442ffff, 0xc003daf, +0x282a024, 0xaf800204, 0x8f820200, 0x2403fffd, +0x431024, 0xaf820200, 0x3c010002, 0xac208fe0, +0x8f830054, 0x3c020002, 0x8c428fb8, 0x24040001, +0x3c010002, 0xac248fcc, 0x24420001, 0x3c010002, +0xac228fb8, 0x2c420004, 0x3c010002, 0xac238fb4, +0x14400006, 0x24020003, 0x3c010001, 0xac246d9c, +0x3c010002, 0x1000015e, 0xac208fb8, 0x3c010002, +0x1000015b, 0xac228f90, 0x8f830054, 0x3c020002, +0x8c428fb4, 0x2463d8f0, 0x431023, 0x2c422710, +0x14400003, 0x24020004, 0x3c010002, 0xac228f90, +0x3c020002, 0x8c428fd0, 0x14400021, 0x3c02fdff, +0x3442ffff, 0x1000014a, 0x282a024, 0x3c040001, +0x8c846f20, 0x3c010002, 0xc005084, 0xac208fa8, +0x3c020002, 0x8c428fdc, 0xaf820204, 0x3c020002, +0x8c428fd0, 0x14400012, 0x3c03fdff, 0x8f820204, +0x3463ffff, 0x30420030, 0x1440012f, 0x283a024, +0x3c030002, 0x8c638fdc, 0x24020005, 0x3c010002, +0xac228f90, 0x3c010002, 0x10000131, 0xac238fe0, +0x3c020002, 0x8c428fd0, 0x10400010, 0x3c02fdff, +0x3c020001, 0x8c426e3c, 0x24420001, 0x3c010001, +0xac226e3c, 0x2c420002, 0x14400125, 0x24020001, +0x3c010001, 0xac226e44, 0x3c010001, 0xac206e3c, +0x3c010001, 0x1000011e, 0xac226d9c, 0x3c030002, +0x8c638fc0, 0x3442ffff, 0x10600119, 0x282a024, +0x3c020002, 0x8c428f9c, 0x10400115, 0x0, +0x3c010002, 0xac228fc8, 0x24020003, 0x3c010002, +0xac228fa0, 0x100000b8, 0x24020006, 0x3c010002, +0xac208fa8, 0x8f820204, 0x34420040, 0xaf820204, +0x3c020002, 0x8c428fe0, 0x24030007, 0x3c010002, +0xac238f90, 0x34420040, 0x3c010002, 0xac228fe0, +0x3c020002, 0x8c428fc0, 0x10400005, 0x0, +0x3c020002, 0x8c428f9c, 0x104000f0, 0x24020002, +0x3c050002, 0x24a58fa0, 0x8ca20000, 0x2c424e21, +0x104000ea, 0x24020002, 0x3c020002, 0x8c428fc4, +0x104000ef, 0x2404ffbf, 0x3c020002, 0x8c428f9c, +0x3c030002, 0x8c638fc8, 0x441024, 0x641824, +0x10430004, 0x24020001, 0x3c010002, 0x100000e4, +0xac228f90, 0x24020003, 0xaca20000, 0x24020008, +0x3c010002, 0xac228f90, 0x3c020002, 0x8c428fcc, +0x1040000c, 0x24020001, 0x3c040002, 0xc005091, +0x8c848f9c, 0x3c020002, 0x8c428fe8, 0x14400005, +0x24020001, 0x3c020002, 0x8c428fe4, 0x10400006, +0x24020001, 0x3c010001, 0xac226d9c, 0x3c010002, +0x100000cb, 0xac208fb8, 0x3c020002, 0x8c428fb0, +0x3c030002, 0x8c638f9c, 0x2c420001, 0x210c0, +0x30630008, 0x3c010002, 0xac228fb0, 0x3c010002, +0xac238fac, 0x8f830054, 0x24020009, 0x3c010002, +0xac228f90, 0x3c010002, 0x100000b9, 0xac238fb4, +0x8f830054, 0x3c020002, 0x8c428fb4, 0x2463d8f0, +0x431023, 0x2c422710, 0x1440009f, 0x0, +0x3c020002, 0x8c428fc0, 0x10400005, 0x0, +0x3c020002, 0x8c428f9c, 0x104000a0, 0x24020002, +0x3c030002, 0x24638fa0, 0x8c620000, 0x2c424e21, +0x1040009a, 0x24020002, 0x3c020002, 0x8c428fcc, +0x1040000e, 0x0, 0x3c020002, 0x8c428f9c, +0x3c010002, 0xac208fcc, 0x30420080, 0x1040002f, +0x2402000c, 0x8f820204, 0x30420080, 0x1440000c, +0x24020003, 0x10000029, 0x2402000c, 0x3c020002, +0x8c428f9c, 0x30420080, 0x14400005, 0x24020003, +0x8f820204, 0x30420080, 0x1040001f, 0x24020003, +0xac620000, 0x2402000a, 0x3c010002, 0xac228f90, +0x3c040002, 0x24848fd8, 0x8c820000, 0x3c030002, +0x8c638fb0, 0x431025, 0xaf820204, 0x8c830000, +0x3c040002, 0x8c848fb0, 0x2402000b, 0x3c010002, +0xac228f90, 0x641825, 0x3c010002, 0xac238fe0, +0x3c050002, 0x24a58fa0, 0x8ca20000, 0x2c424e21, +0x10400066, 0x24020002, 0x3c020002, 0x8c428fd0, +0x10400005, 0x0, 0x2402000c, 0x3c010002, +0x10000067, 0xac228f90, 0x3c020002, 0x8c428fc0, +0x10400063, 0x0, 0x3c040002, 0x8c848f9c, +0x10800055, 0x30820008, 0x3c030002, 0x8c638fac, +0x1062005b, 0x24020003, 0x3c010002, 0xac248fc8, +0xaca20000, 0x24020006, 0x3c010002, 0x10000054, +0xac228f90, 0x8f820200, 0x34420002, 0xaf820200, +0x8f830054, 0x2402000d, 0x3c010002, 0xac228f90, +0x3c010002, 0xac238fb4, 0x8f830054, 0x3c020002, +0x8c428fb4, 0x2463d8f0, 0x431023, 0x2c422710, +0x14400031, 0x0, 0x3c020002, 0x8c428fd0, +0x10400020, 0x2402000e, 0x3c030002, 0x8c638fe4, +0x3c010002, 0x14600015, 0xac228f90, 0xc003e6d, +0x0, 0x3c050001, 0x8ca56d98, 0xc00529b, +0x2021, 0x3c030001, 0x8c636d98, 0x24020004, +0x14620005, 0x2403fffb, 0x3c020001, 0x8c426d94, +0x10000003, 0x2403fff7, 0x3c020001, 0x8c426d94, +0x431024, 0x3c010001, 0xac226d94, 0x8f830224, +0x3c020200, 0x3c010002, 0xac238fec, 0x10000020, +0x282a025, 0x3c020002, 0x8c428fc0, 0x10400005, +0x0, 0x3c020002, 0x8c428f9c, 0x1040000f, +0x24020002, 0x3c020002, 0x8c428fa0, 0x2c424e21, +0x1040000a, 0x24020002, 0x3c020002, 0x8c428fc0, +0x1040000f, 0x0, 0x3c020002, 0x8c428f9c, +0x1440000b, 0x0, 0x24020002, 0x3c010002, +0x10000007, 0xac228f90, 0x3c020002, 0x8c428fc0, +0x10400003, 0x0, 0xc003daf, 0x0, +0x8f820220, 0x3c03f700, 0x431025, 0xaf820220, +0x8fbf0010, 0x3e00008, 0x27bd0018, 0x3c030002, +0x24638fe8, 0x8c620000, 0x10400005, 0x34422000, +0x3c010002, 0xac228fdc, 0x10000003, 0xac600000, +0x3c010002, 0xac248fdc, 0x3e00008, 0x0, +0x27bdffe0, 0x30820030, 0xafbf0018, 0x3c010002, +0xac228fe4, 0x14400067, 0x3c02ffff, 0x34421f0e, +0x821024, 0x14400061, 0x24020030, 0x30822000, +0x1040005d, 0x30838000, 0x31a02, 0x30820001, +0x21200, 0x3c040001, 0x8c846f20, 0x621825, +0x331c2, 0x3c030001, 0x24636e48, 0x30828000, +0x21202, 0x30840001, 0x42200, 0x441025, +0x239c2, 0x61080, 0x431021, 0x471021, +0x90430000, 0x24020001, 0x10620025, 0x0, +0x10600007, 0x24020002, 0x10620013, 0x24020003, +0x1062002c, 0x3c05000f, 0x10000037, 0x0, +0x8f820200, 0x2403feff, 0x431024, 0xaf820200, +0x8f820220, 0x3c03fffe, 0x3463ffff, 0x431024, +0xaf820220, 0x3c010002, 0xac209004, 0x3c010002, +0x10000034, 0xac20900c, 0x8f820200, 0x34420100, +0xaf820200, 0x8f820220, 0x3c03fffe, 0x3463ffff, +0x431024, 0xaf820220, 0x24020100, 0x3c010002, +0xac229004, 0x3c010002, 0x10000026, 0xac20900c, +0x8f820200, 0x2403feff, 0x431024, 0xaf820200, +0x8f820220, 0x3c030001, 0x431025, 0xaf820220, +0x3c010002, 0xac209004, 0x3c010002, 0x10000019, +0xac23900c, 0x8f820200, 0x34420100, 0xaf820200, +0x8f820220, 0x3c030001, 0x431025, 0xaf820220, +0x24020100, 0x3c010002, 0xac229004, 0x3c010002, +0x1000000c, 0xac23900c, 0x34a5ffff, 0x3c040001, +0x24846c38, 0xafa30010, 0xc002b3b, 0xafa00014, +0x10000004, 0x0, 0x24020030, 0x3c010002, +0xac228fe8, 0x8fbf0018, 0x3e00008, 0x27bd0020, +0x0, 0x0, 0x0, 0x27bdffc8, +0xafb20028, 0x809021, 0xafb3002c, 0xa09821, +0xafb00020, 0xc08021, 0x3c040001, 0x24846c50, +0x3c050009, 0x3c020001, 0x8c426d98, 0x34a59001, +0x2403021, 0x2603821, 0xafbf0030, 0xafb10024, +0xa7a0001a, 0xafb00014, 0xc002b3b, 0xafa20010, +0x24020002, 0x12620083, 0x2e620003, 0x10400005, +0x24020001, 0x1262000a, 0x0, 0x10000173, +0x0, 0x24020004, 0x126200f8, 0x24020008, +0x126200f7, 0x3c02ffec, 0x1000016c, 0x0, +0x3c020001, 0x8c426d94, 0x30420002, 0x14400004, +0x128940, 0x3c02fffb, 0x3442ffff, 0x2028024, +0x3c010002, 0x310821, 0xac308ffc, 0x3c024000, +0x2021024, 0x1040004e, 0x1023c2, 0x30840030, +0x101382, 0x3042001c, 0x3c030001, 0x24636dd8, +0x431021, 0x823821, 0x3c020020, 0x2021024, +0x10400006, 0x24020100, 0x3c010002, 0x310821, +0xac229000, 0x10000005, 0x3c020080, 0x3c010002, +0x310821, 0xac209000, 0x3c020080, 0x2021024, +0x10400006, 0x121940, 0x3c020001, 0x3c010002, +0x230821, 0x10000005, 0xac229008, 0x121140, +0x3c010002, 0x220821, 0xac209008, 0x94e40000, +0x3c030001, 0x8c636f40, 0x24020005, 0x10620010, +0xa7a40018, 0x32024000, 0x10400002, 0x34824000, +0xa7a20018, 0x24040001, 0x94e20002, 0x24050004, +0x24e60002, 0x34420001, 0xc0045be, 0xa4e20002, +0x24040001, 0x2821, 0xc0045be, 0x27a60018, +0x3c020001, 0x8c426d98, 0x24110001, 0x3c010001, +0xac316da4, 0x14530004, 0x32028000, 0xc003daf, +0x0, 0x32028000, 0x1040011c, 0x0, +0xc003daf, 0x0, 0x3c030001, 0x8c636f40, +0x24020005, 0x10620115, 0x24020002, 0x3c010001, +0xac316d9c, 0x3c010001, 0x10000110, 0xac226d98, +0x24040001, 0x24050004, 0x27b0001a, 0xc0045be, +0x2003021, 0x24040001, 0x2821, 0xc0045be, +0x2003021, 0x3c020002, 0x511021, 0x8c428ff4, +0x3c040001, 0x8c846d98, 0x3c03bfff, 0x3463ffff, +0x3c010001, 0xac336da4, 0x431024, 0x3c010002, +0x310821, 0x109300f7, 0xac228ff4, 0x100000f7, +0x0, 0x3c022000, 0x2021024, 0x10400005, +0x24020001, 0x3c010001, 0xac226f1c, 0x10000004, +0x128940, 0x3c010001, 0xac206f1c, 0x128940, +0x3c010002, 0x310821, 0xac308ff8, 0x3c024000, +0x2021024, 0x14400014, 0x0, 0x3c020001, +0x8c426f1c, 0x10400006, 0x24040004, 0x24050001, +0xc004ddb, 0x24062000, 0x24020001, 0xaee204b8, +0x3c020002, 0x511021, 0x8c428ff0, 0x3c03bfff, +0x3463ffff, 0x431024, 0x3c010002, 0x310821, +0x100000d0, 0xac228ff0, 0x3c020001, 0x8c426f1c, +0x10400028, 0x3c0300a0, 0x2031024, 0x5443000d, +0x3c020020, 0x3c020001, 0x8c426f20, 0x24030100, +0x3c010002, 0x310821, 0xac239004, 0x3c030001, +0x3c010002, 0x310821, 0xac23900c, 0x10000015, +0x34420400, 0x2021024, 0x10400008, 0x24030100, +0x3c020001, 0x8c426f20, 0x3c010002, 0x310821, +0xac239004, 0x1000000b, 0x34420800, 0x3c020080, +0x2021024, 0x1040002e, 0x3c030001, 0x3c020001, +0x8c426f20, 0x3c010002, 0x310821, 0xac23900c, +0x34420c00, 0x3c010001, 0xac226f20, 0x10000025, +0x24040001, 0x3c020020, 0x2021024, 0x10400006, +0x24020100, 0x3c010002, 0x310821, 0xac229004, +0x10000005, 0x3c020080, 0x3c010002, 0x310821, +0xac209004, 0x3c020080, 0x2021024, 0x10400007, +0x121940, 0x3c020001, 0x3c010002, 0x230821, +0xac22900c, 0x10000006, 0x24040001, 0x121140, +0x3c010002, 0x220821, 0xac20900c, 0x24040001, +0x2821, 0x27b0001e, 0xc00457c, 0x2003021, +0x24040001, 0x2821, 0xc00457c, 0x2003021, +0x24040001, 0x24050001, 0x27b0001c, 0xc00457c, +0x2003021, 0x24040001, 0x24050001, 0xc00457c, +0x2003021, 0x10000077, 0x0, 0x3c02ffec, +0x3442ffff, 0x2028024, 0x3c020008, 0x2028025, +0x121140, 0x3c010002, 0x220821, 0xac308ff8, +0x3c022000, 0x2021024, 0x10400009, 0x0, +0x3c020001, 0x8c426e44, 0x14400005, 0x24020001, +0x3c010001, 0xac226f1c, 0x10000004, 0x3c024000, +0x3c010001, 0xac206f1c, 0x3c024000, 0x2021024, +0x1440001d, 0x24020e01, 0x3c030001, 0x8c636f1c, +0xaf820238, 0x3c010001, 0xac206db0, 0x10600005, +0x24022020, 0x3c010001, 0xac226f20, 0x24020001, +0xaee204b8, 0x3c04bfff, 0x121940, 0x3c020002, +0x431021, 0x8c428ff0, 0x3c050001, 0x8ca56d98, +0x3484ffff, 0x441024, 0x3c010002, 0x230821, +0xac228ff0, 0x24020001, 0x10a20044, 0x0, +0x10000040, 0x0, 0x3c020001, 0x8c426f1c, +0x1040001c, 0x24022000, 0x3c010001, 0xac226f20, +0x3c0300a0, 0x2031024, 0x14430005, 0x121140, +0x3402a000, 0x3c010001, 0x1000002d, 0xac226f20, +0x3c030002, 0x621821, 0x8c638ff8, 0x3c020020, +0x621024, 0x10400004, 0x24022001, 0x3c010001, +0x10000023, 0xac226f20, 0x3c020080, 0x621024, +0x1040001f, 0x3402a001, 0x3c010001, 0x1000001c, +0xac226f20, 0x3c020020, 0x2021024, 0x10400007, +0x121940, 0x24020100, 0x3c010002, 0x230821, +0xac229004, 0x10000006, 0x3c020080, 0x121140, +0x3c010002, 0x220821, 0xac209004, 0x3c020080, +0x2021024, 0x10400006, 0x121940, 0x3c020001, +0x3c010002, 0x230821, 0x10000005, 0xac22900c, +0x121140, 0x3c010002, 0x220821, 0xac20900c, +0x3c030001, 0x8c636d98, 0x24020001, 0x10620003, +0x0, 0xc003daf, 0x0, 0x8fbf0030, +0x8fb3002c, 0x8fb20028, 0x8fb10024, 0x8fb00020, +0x3e00008, 0x27bd0038, 0x27bdffb0, 0xafb3003c, +0x9821, 0xafb50040, 0xa821, 0xafb10034, +0x8821, 0x24020002, 0xafbf0048, 0xafbe0044, +0xafb20038, 0xafb00030, 0xafa4002c, 0xa7a0001a, +0xa7a00018, 0xa7a00020, 0xa7a0001e, 0xa7a00022, +0x10a20130, 0xa7a0001c, 0x2ca20003, 0x10400005, +0x24020001, 0x10a2000a, 0x3c024000, 0x1000025d, +0x2201021, 0x24020004, 0x10a2020a, 0x24020008, +0x10a20208, 0x2201021, 0x10000256, 0x0, +0x8fa8002c, 0x88140, 0x3c030002, 0x701821, +0x8c638ffc, 0x621024, 0x14400009, 0x24040001, +0x3c027fff, 0x3442ffff, 0x628824, 0x3c010002, +0x300821, 0xac318ff4, 0x10000246, 0x2201021, +0x24050001, 0xc00457c, 0x27a60018, 0x24040001, +0x24050001, 0xc00457c, 0x27a60018, 0x97a20018, +0x30420004, 0x104000d9, 0x3c114000, 0x3c020001, +0x8c426f40, 0x2443ffff, 0x2c620006, 0x104000d9, +0x31080, 0x3c010001, 0x220821, 0x8c226c68, +0x400008, 0x0, 0x24040001, 0x24050011, +0x27b0001a, 0xc00457c, 0x2003021, 0x24040001, +0x24050011, 0xc00457c, 0x2003021, 0x97a3001a, +0x30624000, 0x10400002, 0x3c150010, 0x3c150008, +0x30628000, 0x104000aa, 0x3c130001, 0x100000a8, +0x3c130002, 0x24040001, 0x24050014, 0x27b0001a, +0xc00457c, 0x2003021, 0x24040001, 0x24050014, +0xc00457c, 0x2003021, 0x97a3001a, 0x30621000, +0x10400002, 0x3c150010, 0x3c150008, 0x30620800, +0x10400097, 0x3c130001, 0x10000095, 0x3c130002, +0x24040001, 0x24050019, 0x27b0001c, 0xc00457c, +0x2003021, 0x24040001, 0x24050019, 0xc00457c, +0x2003021, 0x97a2001c, 0x30430700, 0x24020400, +0x10620027, 0x28620401, 0x1040000e, 0x24020200, +0x1062001f, 0x28620201, 0x10400005, 0x24020100, +0x5062001e, 0x3c130001, 0x1000001e, 0x24040001, +0x24020300, 0x50620019, 0x3c130002, 0x10000019, +0x24040001, 0x24020600, 0x1062000d, 0x28620601, +0x10400005, 0x24020500, 0x5062000b, 0x3c130002, +0x10000010, 0x24040001, 0x24020700, 0x1462000d, +0x24040001, 0x3c130004, 0x1000000a, 0x3c150008, +0x10000006, 0x3c130004, 0x10000005, 0x3c150008, +0x3c130001, 0x10000002, 0x3c150008, 0x3c150010, +0x24040001, 0x24050018, 0x27b0001e, 0xc00457c, +0x2003021, 0x24040001, 0x24050018, 0xc00457c, +0x2003021, 0x8fa8002c, 0x97a7001e, 0x81140, +0x3c060002, 0xc23021, 0x8cc68ff4, 0x97a20022, +0x3c100001, 0x26106c5c, 0x2002021, 0xafa20010, +0x97a2001c, 0x3c05000c, 0x34a50303, 0xc002b3b, +0xafa20014, 0x3c020004, 0x16620010, 0x3c020001, +0x8f840054, 0x24030001, 0x24020002, 0x3c010001, +0xac236d9c, 0x3c010001, 0xac226d98, 0x3c010001, +0xac236da4, 0x3c010001, 0xac236e24, 0x3c010001, +0xac246f30, 0x1000004f, 0x2b38825, 0x16620039, +0x3c028000, 0x3c020001, 0x8c426e20, 0x1440001e, +0x24040018, 0x2021, 0x2821, 0xc004ddb, +0x34068000, 0x8f830054, 0x8f820054, 0x2b38825, +0x10000002, 0x24630032, 0x8f820054, 0x621023, +0x2c420033, 0x1440fffc, 0x0, 0x8f830054, +0x24020001, 0x3c010001, 0xac226e20, 0x3c010001, +0xac226d9c, 0x3c010001, 0xac226d98, 0x3c010001, +0xac226da4, 0x3c010001, 0xac226e24, 0x3c010001, +0x1000002c, 0xac236f30, 0x2821, 0xc004ddb, +0x24060404, 0x2021, 0x2405001e, 0x27a60018, +0x24020002, 0xc0045be, 0xa7a20018, 0x2021, +0x2821, 0x27a60018, 0xc0045be, 0xa7a00018, +0x24040018, 0x24050002, 0xc004ddb, 0x24060004, +0x3c028000, 0x2221025, 0x2b31825, 0x10000015, +0x438825, 0x2221025, 0x2751825, 0x438825, +0x2002021, 0x97a6001c, 0x3c070001, 0x8ce76d98, +0x3c05000c, 0x34a50326, 0xafb30010, 0xc002b3b, +0xafb10014, 0x10000007, 0x0, 0x3c110002, +0x2308821, 0x8e318ffc, 0x3c027fff, 0x3442ffff, +0x2228824, 0x3c020001, 0x8c426da8, 0x1040001e, +0x0, 0x3c020001, 0x8c426f1c, 0x10400002, +0x3c022000, 0x2228825, 0x8fa8002c, 0x81140, +0x3c010002, 0x220821, 0x8c229000, 0x10400003, +0x3c020020, 0x10000005, 0x2228825, 0x3c02ffdf, +0x3442ffff, 0x2228824, 0x8fa8002c, 0x81140, +0x3c010002, 0x220821, 0x8c229008, 0x10400003, +0x3c020080, 0x10000004, 0x2228825, 0x3c02ff7f, +0x3442ffff, 0x2228824, 0x8fa8002c, 0x81140, +0x3c010002, 0x220821, 0xac318ff4, 0x10000135, +0x2201021, 0x8fa8002c, 0x8f140, 0x3c030002, +0x7e1821, 0x8c638ff8, 0x3c024000, 0x621024, +0x14400009, 0x24040001, 0x3c027fff, 0x3442ffff, +0x628824, 0x3c010002, 0x3e0821, 0xac318ff0, +0x10000124, 0x2201021, 0x2821, 0xc00457c, +0x27a60018, 0x24040001, 0x2821, 0xc00457c, +0x27a60018, 0x24040001, 0x24050001, 0x27b20020, +0xc00457c, 0x2403021, 0x24040001, 0x24050001, +0xc00457c, 0x2403021, 0x24040001, 0x24050004, +0x27b1001e, 0xc00457c, 0x2203021, 0x24040001, +0x24050004, 0xc00457c, 0x2203021, 0x24040001, +0x24050005, 0x27b00022, 0xc00457c, 0x2003021, +0x24040001, 0x24050005, 0xc00457c, 0x2003021, +0x24040001, 0x24050010, 0xc00457c, 0x27a60018, +0x24040001, 0x24050010, 0xc00457c, 0x27a60018, +0x24040001, 0x2405000a, 0xc00457c, 0x2403021, +0x24040001, 0x2405000a, 0xc00457c, 0x2403021, +0x24040001, 0x24050018, 0xc00457c, 0x2203021, +0x24040001, 0x24050018, 0xc00457c, 0x2203021, +0x24040001, 0x24050001, 0xc00457c, 0x27a60018, +0x24040001, 0x24050001, 0xc00457c, 0x27a60018, +0x97a20018, 0x30420004, 0x10400066, 0x3c114000, +0x3c030001, 0x8c636f34, 0x24020005, 0x14620067, +0x24040001, 0x24050019, 0x27b0001c, 0xc00457c, +0x2003021, 0x24040001, 0x24050019, 0xc00457c, +0x2003021, 0x97a2001c, 0x30430700, 0x24020400, +0x10620027, 0x28620401, 0x1040000e, 0x24020200, +0x1062001f, 0x28620201, 0x10400005, 0x24020100, +0x5062001e, 0x3c130001, 0x1000001e, 0x3c020004, +0x24020300, 0x50620019, 0x3c130002, 0x10000019, +0x3c020004, 0x24020600, 0x1062000d, 0x28620601, +0x10400005, 0x24020500, 0x5062000b, 0x3c130002, +0x10000010, 0x3c020004, 0x24020700, 0x1462000d, +0x3c020004, 0x3c130004, 0x1000000a, 0x3c150008, +0x10000006, 0x3c130004, 0x10000005, 0x3c150008, +0x3c130001, 0x10000002, 0x3c150008, 0x3c150010, +0x3c020004, 0x12620017, 0x3c028000, 0x8f820054, +0x24100001, 0x3c010001, 0xac306d9c, 0x3c010001, +0xac306d98, 0x3c010001, 0xac306da4, 0x3c010001, +0xac306e24, 0x3c010001, 0xac226f30, 0x3c020001, +0x16620022, 0x2758825, 0x2021, 0x2821, +0xc004ddb, 0x34068000, 0x3c010001, 0x1000001b, +0xac306e20, 0x2221025, 0x2b31825, 0x438825, +0x97a6001c, 0x3c020001, 0x8c426f1c, 0x3c070001, +0x8ce76d98, 0x3c040001, 0x24846c5c, 0xafa20010, +0x97a2001e, 0x3c05000c, 0x34a50323, 0x3c010001, +0xac206e20, 0xc002b3b, 0xafa20014, 0x10000007, +0x0, 0x3c110002, 0x23e8821, 0x8e318ff0, +0x3c027fff, 0x3442ffff, 0x2228824, 0x3c020001, +0x8c426da8, 0x10400069, 0x0, 0x3c020001, +0x8c426f1c, 0x10400002, 0x3c022000, 0x2228825, +0x8fa8002c, 0x81140, 0x3c010002, 0x220821, +0x8c229004, 0x10400003, 0x3c020020, 0x10000005, +0x2228825, 0x3c02ffdf, 0x3442ffff, 0x2228824, +0x8fa8002c, 0x81140, 0x3c010002, 0x220821, +0x8c22900c, 0x10400003, 0x3c020080, 0x1000004f, +0x2228825, 0x3c02ff7f, 0x3442ffff, 0x1000004b, +0x2228824, 0x8fa8002c, 0x82940, 0x3c030002, +0x651821, 0x8c638ff8, 0x3c024000, 0x621024, +0x14400008, 0x3c027fff, 0x3442ffff, 0x628824, +0x3c010002, 0x250821, 0xac318ff0, 0x10000041, +0x2201021, 0x3c020001, 0x8c426da8, 0x10400034, +0x3c11c00c, 0x3c020001, 0x8c426e44, 0x3c04c00c, +0x34842000, 0x3c030001, 0x8c636f1c, 0x2102b, +0x21023, 0x441024, 0x10600003, 0x518825, +0x3c022000, 0x2228825, 0x3c020002, 0x451021, +0x8c429004, 0x10400003, 0x3c020020, 0x10000004, +0x2228825, 0x3c02ffdf, 0x3442ffff, 0x2228824, +0x8fa8002c, 0x81140, 0x3c010002, 0x220821, +0x8c22900c, 0x10400003, 0x3c020080, 0x10000004, +0x2228825, 0x3c02ff7f, 0x3442ffff, 0x2228824, +0x3c020001, 0x8c426e30, 0x10400002, 0x3c020800, +0x2228825, 0x3c020001, 0x8c426e34, 0x10400002, +0x3c020400, 0x2228825, 0x3c020001, 0x8c426e38, +0x10400006, 0x3c020100, 0x10000004, 0x2228825, +0x3c027fff, 0x3442ffff, 0x628824, 0x8fa8002c, +0x81140, 0x3c010002, 0x220821, 0xac318ff0, +0x2201021, 0x8fbf0048, 0x8fbe0044, 0x8fb50040, +0x8fb3003c, 0x8fb20038, 0x8fb10034, 0x8fb00030, +0x3e00008, 0x27bd0050, 0x27bdffd0, 0xafb20028, +0x809021, 0xafbf002c, 0xafb10024, 0xafb00020, +0x8f840200, 0x3c100001, 0x8e106d98, 0x8f860220, +0x24020002, 0x1202005c, 0x2e020003, 0x10400005, +0x24020001, 0x1202000a, 0x121940, 0x1000010c, +0x0, 0x24020004, 0x120200bf, 0x24020008, +0x120200be, 0x128940, 0x10000105, 0x0, +0x3c050002, 0xa32821, 0x8ca58ffc, 0x3c100002, +0x2038021, 0x8e108ff4, 0x3c024000, 0xa21024, +0x10400038, 0x3c020008, 0x2021024, 0x10400020, +0x34840002, 0x3c020002, 0x431021, 0x8c429000, +0x10400005, 0x34840020, 0x34840100, 0x3c020020, +0x10000006, 0x2028025, 0x2402feff, 0x822024, +0x3c02ffdf, 0x3442ffff, 0x2028024, 0x121140, +0x3c010002, 0x220821, 0x8c229008, 0x10400005, +0x3c020001, 0xc23025, 0x3c020080, 0x10000016, +0x2028025, 0x3c02fffe, 0x3442ffff, 0xc23024, +0x3c02ff7f, 0x3442ffff, 0x1000000f, 0x2028024, +0x2402fedf, 0x822024, 0x3c02fffe, 0x3442ffff, +0xc23024, 0x3c02ff5f, 0x3442ffff, 0x2028024, +0x3c010002, 0x230821, 0xac209000, 0x3c010002, +0x230821, 0xac209008, 0xaf840200, 0xaf860220, +0x8f820220, 0x34420002, 0xaf820220, 0x1000000a, +0x121140, 0x3c02bfff, 0x3442ffff, 0x8f830200, +0x2028024, 0x2402fffd, 0x621824, 0xc003daf, +0xaf830200, 0x121140, 0x3c010002, 0x220821, +0x100000b7, 0xac308ff4, 0x3c020001, 0x8c426f1c, +0x10400069, 0x24050004, 0x24040001, 0xc00457c, +0x27a60018, 0x24040001, 0x24050005, 0xc00457c, +0x27a6001a, 0x97a30018, 0x97a2001a, 0x3c040001, +0x24846e48, 0x30630c00, 0x31a82, 0x30420c00, +0x21282, 0xa7a2001a, 0x21080, 0x441021, +0x431021, 0xa7a30018, 0x90480000, 0x24020001, +0x3103ffff, 0x10620029, 0x28620002, 0x10400005, +0x0, 0x10600009, 0x0, 0x1000003d, +0x0, 0x10700013, 0x24020003, 0x1062002c, +0x0, 0x10000037, 0x0, 0x8f820200, +0x2403feff, 0x431024, 0xaf820200, 0x8f820220, +0x3c03fffe, 0x3463ffff, 0x431024, 0xaf820220, +0x3c010002, 0xac209004, 0x3c010002, 0x10000032, +0xac20900c, 0x8f820200, 0x34420100, 0xaf820200, +0x8f820220, 0x3c03fffe, 0x3463ffff, 0x431024, +0xaf820220, 0x24020100, 0x3c010002, 0xac229004, +0x3c010002, 0x10000024, 0xac20900c, 0x8f820200, +0x2403feff, 0x431024, 0xaf820200, 0x8f820220, +0x3c030001, 0x431025, 0xaf820220, 0x3c010002, +0xac209004, 0x3c010002, 0x10000017, 0xac23900c, +0x8f820200, 0x34420100, 0xaf820200, 0x8f820220, +0x3c030001, 0x431025, 0xaf820220, 0x24020100, +0x3c010002, 0xac229004, 0x3c010002, 0x1000000a, +0xac23900c, 0x3c040001, 0x24846c80, 0x97a6001a, +0x97a70018, 0x3c050001, 0x34a5ffff, 0xafa80010, +0xc002b3b, 0xafa00014, 0x8f820200, 0x34420002, +0x1000004b, 0xaf820200, 0x128940, 0x3c050002, +0xb12821, 0x8ca58ff8, 0x3c100002, 0x2118021, +0x8e108ff0, 0x3c024000, 0xa21024, 0x14400010, +0x0, 0x3c020001, 0x8c426f1c, 0x14400005, +0x3c02bfff, 0x8f820200, 0x34420002, 0xaf820200, +0x3c02bfff, 0x3442ffff, 0xc003daf, 0x2028024, +0x3c010002, 0x310821, 0x10000031, 0xac308ff0, +0x3c020001, 0x8c426f1c, 0x10400005, 0x3c020020, +0x3c020001, 0x8c426e44, 0x10400025, 0x3c020020, +0xa21024, 0x10400007, 0x34840020, 0x24020100, +0x3c010002, 0x310821, 0xac229004, 0x10000006, +0x34840100, 0x3c010002, 0x310821, 0xac209004, +0x2402feff, 0x822024, 0x3c020080, 0xa21024, +0x10400007, 0x121940, 0x3c020001, 0x3c010002, +0x230821, 0xac22900c, 0x10000008, 0xc23025, +0x121140, 0x3c010002, 0x220821, 0xac20900c, +0x3c02fffe, 0x3442ffff, 0xc23024, 0xaf840200, +0xaf860220, 0x8f820220, 0x34420002, 0xaf820220, +0x121140, 0x3c010002, 0x220821, 0xac308ff0, +0x8fbf002c, 0x8fb20028, 0x8fb10024, 0x8fb00020, +0x3e00008, 0x27bd0030, 0x0, 0x1821, +0x308400ff, 0x2405ffdf, 0x2406ffbf, 0x641007, +0x30420001, 0x10400004, 0x0, 0x8f820044, +0x10000003, 0x34420040, 0x8f820044, 0x461024, +0xaf820044, 0x8f820044, 0x34420020, 0xaf820044, +0x8f820044, 0x451024, 0xaf820044, 0x24630001, +0x28620008, 0x5440ffee, 0x641007, 0x3e00008, +0x0, 0x2c820008, 0x1040001b, 0x0, +0x2405ffdf, 0x2406ffbf, 0x41880, 0x3c020001, +0x24426e60, 0x621821, 0x24640004, 0x90620000, +0x10400004, 0x0, 0x8f820044, 0x10000003, +0x34420040, 0x8f820044, 0x461024, 0xaf820044, +0x8f820044, 0x34420020, 0xaf820044, 0x8f820044, +0x451024, 0xaf820044, 0x24630001, 0x64102b, +0x1440ffee, 0x0, 0x3e00008, 0x0, +0x0, 0x0, 0x0, 0x8f8400c4, +0x8f8600e0, 0x8f8700e4, 0x2402fff8, 0xc22824, +0x10e5001a, 0x27623ff8, 0x14e20002, 0x24e80008, +0x27683000, 0x55050004, 0x8d0a0000, 0x30c20004, +0x14400012, 0x805021, 0x8ce90000, 0x8f42013c, +0x1494823, 0x49182b, 0x94eb0006, 0x10600002, +0x25630050, 0x494821, 0x123182b, 0x50400003, +0x8f4201fc, 0x3e00008, 0xe01021, 0xaf8800e8, +0x24420001, 0xaf4201fc, 0xaf8800e4, 0x3e00008, +0x1021, 0x3e00008, 0x0, 0x8f8300e4, +0x27623ff8, 0x10620004, 0x24620008, 0xaf8200e8, +0x3e00008, 0xaf8200e4, 0x27623000, 0xaf8200e8, +0x3e00008, 0xaf8200e4, 0x3e00008, 0x0, +0x0, 0x0, 0x0, 0x8f880120, +0x27624fe0, 0x8f830128, 0x15020002, 0x25090020, +0x27694800, 0x11230012, 0x8fa20010, 0xad040000, +0xad050004, 0xad060008, 0xa507000e, 0x8fa30014, +0xad020018, 0x8fa20018, 0xad03001c, 0x25030016, +0xad020010, 0xad030014, 0xaf890120, 0x8f4300fc, +0x24020001, 0x2463ffff, 0x3e00008, 0xaf4300fc, +0x8f430324, 0x1021, 0x24630001, 0x3e00008, +0xaf430324, 0x3e00008, 0x0, 0x8f880100, +0x276247e0, 0x8f830108, 0x15020002, 0x25090020, +0x27694000, 0x1123000f, 0x8fa20010, 0xad040000, +0xad050004, 0xad060008, 0xa507000e, 0x8fa30014, +0xad020018, 0x8fa20018, 0xad03001c, 0x25030016, +0xad020010, 0xad030014, 0xaf890100, 0x3e00008, +0x24020001, 0x8f430328, 0x1021, 0x24630001, +0x3e00008, 0xaf430328, 0x3e00008, 0x0, +0x0, 0x0, 0x0, 0x0 }; +static int tigon2FwRodata[/*(MAX_RODATA_LEN/4) + 1*/] = { +0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, +0x6e2f6677, 0x6d61696e, 0x2e632c76, 0x20312e31, +0x2e322e34, 0x35203139, 0x39392f30, 0x312f3234, +0x2030303a, 0x31303a35, 0x35207368, 0x75616e67, +0x20457870, 0x20240000, 0x65767452, 0x6e674600, +0x51657674, 0x46000000, 0x51657674, 0x505f4600, +0x4d657674, 0x526e6746, 0x0, 0x4d516576, +0x74460000, 0x4d516576, 0x505f4600, 0x5173436f, +0x6e495f46, 0x0, 0x5173436f, 0x6e734600, +0x51725072, 0x6f644600, 0x6261644d, 0x656d537a, +0x0, 0x68775665, 0x72000000, 0x62616448, +0x77566572, 0x0, 0x2a2a4441, 0x574e5f41, +0x0, 0x74785278, 0x4266537a, 0x0, +0x62664174, 0x6e4d726b, 0x0, 0x7265645a, +0x6f6e6531, 0x0, 0x70636943, 0x6f6e6600, +0x67656e43, 0x6f6e6600, 0x2a646d61, 0x5244666c, +0x0, 0x2a50414e, 0x49432a00, 0x2e2e2f2e, +0x2e2f2e2e, 0x2f2e2e2f, 0x2e2e2f73, 0x72632f6e, +0x69632f66, 0x77322f63, 0x6f6d6d6f, 0x6e2f6677, +0x6d61696e, 0x2e630000, 0x72636246, 0x6c616773, +0x0, 0x62616452, 0x78526362, 0x0, +0x676c6f62, 0x466c6773, 0x0, 0x2b5f6469, +0x73705f6c, 0x6f6f7000, 0x2b65765f, 0x68616e64, +0x6c657200, 0x63616e74, 0x31446d61, 0x0, +0x2b715f64, 0x6d615f74, 0x6f5f6e69, 0x635f636b, +0x73756d00, 0x2b685f73, 0x656e645f, 0x64617461, +0x5f726561, 0x64795f63, 0x6b73756d, 0x0, +0x2b685f64, 0x6d615f72, 0x645f6173, 0x73697374, +0x5f636b73, 0x756d0000, 0x74436b73, 0x6d4f6e00, +0x2b715f64, 0x6d615f74, 0x6f5f6e69, 0x63000000, +0x2b685f73, 0x656e645f, 0x64617461, 0x5f726561, +0x64790000, 0x2b685f64, 0x6d615f72, 0x645f6173, +0x73697374, 0x0, 0x74436b73, 0x6d4f6666, +0x0, 0x2b685f73, 0x656e645f, 0x62645f72, +0x65616479, 0x0, 0x68737453, 0x52696e67, +0x0, 0x62616453, 0x52696e67, 0x0, +0x6e696353, 0x52696e67, 0x0, 0x77446d61, +0x416c6c41, 0x0, 0x2b715f64, 0x6d615f74, +0x6f5f686f, 0x73745f63, 0x6b73756d, 0x0, +0x2b685f6d, 0x61635f72, 0x785f636f, 0x6d705f63, +0x6b73756d, 0x0, 0x2b685f64, 0x6d615f77, +0x725f6173, 0x73697374, 0x5f636b73, 0x756d0000, +0x72436b73, 0x6d4f6e00, 0x2b715f64, 0x6d615f74, +0x6f5f686f, 0x73740000, 0x2b685f6d, 0x61635f72, +0x785f636f, 0x6d700000, 0x2b685f64, 0x6d615f77, +0x725f6173, 0x73697374, 0x0, 0x72436b73, +0x6d4f6666, 0x0, 0x2b685f72, 0x6563765f, +0x62645f72, 0x65616479, 0x0, 0x2b685f72, +0x6563765f, 0x6a756d62, 0x6f5f6264, 0x5f726561, +0x64790000, 0x2b685f72, 0x6563765f, 0x6d696e69, +0x5f62645f, 0x72656164, 0x79000000, 0x2b6d685f, +0x636f6d6d, 0x616e6400, 0x2b685f74, 0x696d6572, +0x0, 0x2b685f64, 0x6f5f7570, 0x64617465, +0x5f74785f, 0x636f6e73, 0x0, 0x2b685f64, +0x6f5f7570, 0x64617465, 0x5f72785f, 0x70726f64, +0x0, 0x2b636b73, 0x756d3136, 0x0, +0x2b706565, 0x6b5f6d61, 0x635f7278, 0x5f776100, +0x2b706565, 0x6b5f6d61, 0x635f7278, 0x0, +0x2b646571, 0x5f6d6163, 0x5f727800, 0x2b685f6d, +0x61635f72, 0x785f6174, 0x746e0000, 0x62616452, +0x6574537a, 0x0, 0x72784264, 0x4266537a, +0x0, 0x2b6e756c, 0x6c5f6861, 0x6e646c65, +0x72000000, 0x66774f70, 0x4661696c, 0x0, +0x2b685f75, 0x70646174, 0x655f6c65, 0x64340000, +0x2b685f75, 0x70646174, 0x655f6c65, 0x64360000, +0x2b685f75, 0x70646174, 0x655f6c65, 0x64320000, +0x696e7453, 0x74617465, 0x0, 0x2a2a696e, +0x69744370, 0x0, 0x23736372, 0x65616d00, +0x69537461, 0x636b4572, 0x0, 0x70726f62, +0x654d656d, 0x0, 0x2a2a4441, 0x574e5f42, +0x0, 0x2b73775f, 0x646d615f, 0x61737369, +0x73745f70, 0x6c75735f, 0x74696d65, 0x72000000, +0x2b267072, 0x656c6f61, 0x645f7772, 0x5f646573, +0x63720000, 0x2b267072, 0x656c6f61, 0x645f7264, +0x5f646573, 0x63720000, 0x2b685f68, 0x665f7469, +0x6d657200, 0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, +0x6e2f7469, 0x6d65722e, 0x632c7620, 0x312e312e, +0x322e3335, 0x20313939, 0x392f3031, 0x2f323720, +0x31393a30, 0x393a3530, 0x20686179, 0x65732045, +0x78702024, 0x0, 0x65767452, 0x6e674600, +0x51657674, 0x46000000, 0x51657674, 0x505f4600, +0x4d657674, 0x526e6746, 0x0, 0x4d516576, +0x74460000, 0x4d516576, 0x505f4600, 0x5173436f, +0x6e495f46, 0x0, 0x5173436f, 0x6e734600, +0x51725072, 0x6f644600, 0x542d446d, 0x61526432, +0x0, 0x542d446d, 0x61526431, 0x0, +0x542d446d, 0x61526442, 0x0, 0x542d446d, +0x61577232, 0x0, 0x542d446d, 0x61577231, +0x0, 0x542d446d, 0x61577242, 0x0, +0x0, 0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, +0x6e2f636f, 0x6d6d616e, 0x642e632c, 0x7620312e, +0x312e322e, 0x32382031, 0x3939392f, 0x30312f32, +0x30203139, 0x3a34393a, 0x34392073, 0x6875616e, +0x67204578, 0x70202400, 0x65767452, 0x6e674600, +0x51657674, 0x46000000, 0x51657674, 0x505f4600, +0x4d657674, 0x526e6746, 0x0, 0x4d516576, +0x74460000, 0x4d516576, 0x505f4600, 0x5173436f, +0x6e495f46, 0x0, 0x5173436f, 0x6e734600, +0x51725072, 0x6f644600, 0x3f48636d, 0x644d6278, +0x0, 0x3f636d64, 0x48737453, 0x0, +0x3f636d64, 0x4d634d64, 0x0, 0x3f636d64, +0x50726f6d, 0x0, 0x3f636d64, 0x4c696e6b, +0x0, 0x3f636d64, 0x45727200, 0x86ac, +0x8e5c, 0x8e5c, 0x8de4, 0x8b78, +0x8e30, 0x8e5c, 0x8790, 0x8800, +0x8990, 0x8a68, 0x8a34, 0x8e5c, +0x8870, 0x8b24, 0x8e5c, 0x8b34, +0x87b4, 0x8824, 0x0, 0x0, +0x0, 0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, +0x6e2f6d63, 0x6173742e, 0x632c7620, 0x312e312e, +0x322e3820, 0x31393938, 0x2f31322f, 0x30382030, +0x323a3336, 0x3a333620, 0x73687561, 0x6e672045, +0x78702024, 0x0, 0x65767452, 0x6e674600, +0x51657674, 0x46000000, 0x51657674, 0x505f4600, +0x4d657674, 0x526e6746, 0x0, 0x4d516576, +0x74460000, 0x4d516576, 0x505f4600, 0x5173436f, +0x6e495f46, 0x0, 0x5173436f, 0x6e734600, +0x51725072, 0x6f644600, 0x6164644d, 0x63447570, +0x0, 0x6164644d, 0x6346756c, 0x0, +0x64656c4d, 0x634e6f45, 0x0, 0x0, +0x0, 0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, +0x6e2f646d, 0x612e632c, 0x7620312e, 0x312e322e, +0x32342031, 0x3939382f, 0x31322f32, 0x31203030, +0x3a33333a, 0x30392073, 0x6875616e, 0x67204578, +0x70202400, 0x65767452, 0x6e674600, 0x51657674, +0x46000000, 0x51657674, 0x505f4600, 0x4d657674, +0x526e6746, 0x0, 0x4d516576, 0x74460000, +0x4d516576, 0x505f4600, 0x5173436f, 0x6e495f46, +0x0, 0x5173436f, 0x6e734600, 0x51725072, +0x6f644600, 0x7377446d, 0x614f6666, 0x0, +0x31446d61, 0x4f6e0000, 0x7377446d, 0x614f6e00, +0x2372446d, 0x6141544e, 0x0, 0x72446d61, +0x41544e30, 0x0, 0x72446d61, 0x41544e31, +0x0, 0x72446d61, 0x34476200, 0x2a50414e, +0x49432a00, 0x2e2e2f2e, 0x2e2f2e2e, 0x2f2e2e2f, +0x2e2e2f73, 0x72632f6e, 0x69632f66, 0x77322f63, +0x6f6d6d6f, 0x6e2f646d, 0x612e6300, 0x2377446d, +0x6141544e, 0x0, 0x77446d61, 0x41544e30, +0x0, 0x77446d61, 0x41544e31, 0x0, +0x77446d61, 0x34476200, 0x0, 0x0, +0x0, 0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, +0x6e2f7472, 0x6163652e, 0x632c7620, 0x312e312e, +0x322e3520, 0x31393938, 0x2f30392f, 0x33302031, +0x383a3530, 0x3a323820, 0x73687561, 0x6e672045, +0x78702024, 0x0, 0x0, 0x0, +0x0, 0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, +0x6e2f6461, 0x74612e63, 0x2c762031, 0x2e312e32, +0x2e313220, 0x31393939, 0x2f30312f, 0x32302031, +0x393a3439, 0x3a353120, 0x73687561, 0x6e672045, +0x78702024, 0x0, 0x46575f56, 0x45525349, +0x4f4e3a20, 0x23312046, 0x72692041, 0x70722037, +0x2031373a, 0x35373a35, 0x32205044, 0x54203230, +0x30300000, 0x46575f43, 0x4f4d5049, 0x4c455f54, +0x494d453a, 0x2031373a, 0x35373a35, 0x32000000, +0x46575f43, 0x4f4d5049, 0x4c455f42, 0x593a2064, +0x65767263, 0x73000000, 0x46575f43, 0x4f4d5049, +0x4c455f48, 0x4f53543a, 0x20636f6d, 0x70757465, +0x0, 0x46575f43, 0x4f4d5049, 0x4c455f44, +0x4f4d4149, 0x4e3a2065, 0x6e672e61, 0x6374656f, +0x6e2e636f, 0x6d000000, 0x46575f43, 0x4f4d5049, +0x4c45523a, 0x20676363, 0x20766572, 0x73696f6e, +0x20322e37, 0x2e320000, 0x0, 0x12041100, +0x0, 0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, +0x6e2f6d65, 0x6d2e632c, 0x7620312e, 0x312e322e, +0x35203139, 0x39382f30, 0x392f3330, 0x2031383a, +0x35303a30, 0x38207368, 0x75616e67, 0x20457870, +0x20240000, 0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, +0x6e2f7365, 0x6e642e63, 0x2c762031, 0x2e312e32, +0x2e343420, 0x31393938, 0x2f31322f, 0x32312030, +0x303a3333, 0x3a313820, 0x73687561, 0x6e672045, +0x78702024, 0x0, 0x65767452, 0x6e674600, +0x51657674, 0x46000000, 0x51657674, 0x505f4600, +0x4d657674, 0x526e6746, 0x0, 0x4d516576, +0x74460000, 0x4d516576, 0x505f4600, 0x5173436f, +0x6e495f46, 0x0, 0x5173436f, 0x6e734600, +0x51725072, 0x6f644600, 0x69736e74, 0x54637055, +0x0, 0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, +0x6e2f7265, 0x63762e63, 0x2c762031, 0x2e312e32, +0x2e353320, 0x31393939, 0x2f30312f, 0x31362030, +0x323a3535, 0x3a343320, 0x73687561, 0x6e672045, +0x78702024, 0x0, 0x65767452, 0x6e674600, +0x51657674, 0x46000000, 0x51657674, 0x505f4600, +0x4d657674, 0x526e6746, 0x0, 0x4d516576, +0x74460000, 0x4d516576, 0x505f4600, 0x5173436f, +0x6e495f46, 0x0, 0x5173436f, 0x6e734600, +0x51725072, 0x6f644600, 0x724d6163, 0x43686b30, +0x0, 0x72784672, 0x6d324c67, 0x0, +0x72784e6f, 0x53744264, 0x0, 0x72784e6f, +0x4d694264, 0x0, 0x72784e6f, 0x4a6d4264, +0x0, 0x7278436b, 0x446d6146, 0x0, +0x72785144, 0x6d457846, 0x0, 0x72785144, +0x6d614600, 0x72785144, 0x4c426446, 0x0, +0x72785144, 0x6d426446, 0x0, 0x72784372, +0x63506164, 0x0, 0x72536d51, 0x446d6146, +0x0, 0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, +0x6e2f6d61, 0x632e632c, 0x7620312e, 0x312e322e, +0x32322031, 0x3939382f, 0x31322f30, 0x38203032, +0x3a33363a, 0x33302073, 0x6875616e, 0x67204578, +0x70202400, 0x65767452, 0x6e674600, 0x51657674, +0x46000000, 0x51657674, 0x505f4600, 0x4d657674, +0x526e6746, 0x0, 0x4d516576, 0x74460000, +0x4d516576, 0x505f4600, 0x5173436f, 0x6e495f46, +0x0, 0x5173436f, 0x6e734600, 0x51725072, +0x6f644600, 0x6d616354, 0x68726573, 0x0, +0x23744d61, 0x6341544e, 0x0, 0x23724d61, +0x6341544e, 0x0, 0x72656d41, 0x73737274, +0x0, 0x6c696e6b, 0x444f574e, 0x0, +0x6c696e6b, 0x55500000, 0x0, 0x0, +0x0, 0x24486561, 0x6465723a, 0x202f7072, +0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, +0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, +0x6e2f636b, 0x73756d2e, 0x632c7620, 0x312e312e, +0x322e3920, 0x31393939, 0x2f30312f, 0x31342030, +0x303a3033, 0x3a343820, 0x73687561, 0x6e672045, +0x78702024, 0x0, 0x65767452, 0x6e674600, +0x51657674, 0x46000000, 0x51657674, 0x505f4600, +0x4d657674, 0x526e6746, 0x0, 0x4d516576, +0x74460000, 0x4d516576, 0x505f4600, 0x5173436f, +0x6e495f46, 0x0, 0x5173436f, 0x6e734600, +0x51725072, 0x6f644600, 0x0, 0x0, +0x0, 0x50726f62, 0x65506879, 0x0, +0x6c6e6b41, 0x53535254, 0x0, 0x109a4, +0x10a1c, 0x10a50, 0x10a7c, 0x11050, +0x10aa8, 0x10b10, 0x111fc, 0x10dc0, +0x10c68, 0x10c80, 0x10cc4, 0x10cec, +0x10d0c, 0x10d34, 0x111fc, 0x10dc0, +0x10df8, 0x10e10, 0x10e40, 0x10e68, +0x10e88, 0x10eb0, 0x0, 0x10fdc, +0x11008, 0x1102c, 0x111fc, 0x11050, +0x11078, 0x11108, 0x0, 0x0, +0x0, 0x1186c, 0x1193c, 0x11a14, +0x11ae4, 0x11b40, 0x11c1c, 0x11c44, +0x11d20, 0x11d48, 0x11ef0, 0x11f18, +0x120c0, 0x122b8, 0x1254c, 0x12460, +0x1254c, 0x12578, 0x120e8, 0x12290, +0x7273745f, 0x676d6969, 0x0, 0x12608, +0x12640, 0x12728, 0x13374, 0x133b4, +0x133cc, 0x7365746c, 0x6f6f7000, 0x0, +0x0, 0x13bbc, 0x13bfc, 0x13c8c, +0x13cd0, 0x13d34, 0x13dc0, 0x13df4, +0x13e7c, 0x13f14, 0x13fe4, 0x14024, +0x140a8, 0x140cc, 0x141dc, 0x646f4261, +0x73655067, 0x0, 0x0, 0x0, +0x0, 0x73746d61, 0x634c4e4b, 0x0, +0x6765746d, 0x636c6e6b, 0x0, 0x14ed8, +0x14ed8, 0x14b8c, 0x14bd8, 0x14c24, +0x14ed8, 0x7365746d, 0x61636163, 0x74000000, +0x0, 0x0 }; +static int tigon2FwData[/*(MAX_DATA_LEN/4) + 1*/] = { +0x1, +0x1, 0x1, 0xc001fc, 0x3ffc, +0xc00000, 0x416c7465, 0x6f6e2041, 0x63654e49, +0x43205600, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x416c7465, +0x6f6e2041, 0x63654e49, 0x43205600, 0x42424242, +0x0, 0x0, 0x0, 0x1ffffc, +0x1fff7c, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x60cf00, +0x60, 0xcf000000, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x3, 0x0, +0x1, 0x0, 0x0, 0x0, +0x1, 0x0, 0x1, 0x0, +0x0, 0x0, 0x0, 0x1, +0x1, 0x0, 0x0, 0x0, +0x0, 0x0, 0x1000000, 0x21000000, +0x12000140, 0x0, 0x0, 0x20000000, +0x120000a0, 0x0, 0x12000060, 0x12000180, +0x120001e0, 0x0, 0x0, 0x0, +0x1, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x2, +0x0, 0x0, 0x30001, 0x1, +0x30201, 0x0, 0x0, 0x1010101, +0x1010100, 0x10100, 0x1010001, 0x10001, +0x1000101, 0x101, 0x0, 0x0 }; diff -Nru /sys/src/fs/pc/etherif.c /sys/src/fs/pc/etherif.c --- /sys/src/fs/pc/etherif.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/pc/etherif.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,338 @@ +#include "all.h" +#include "io.h" +#include "mem.h" + +#include "../ip/ip.h" +#include "etherif.h" + +#define dprint(...) /* print(__VA_ARGS__) */ + +extern int etherga620reset(Ether*); +extern int ether21140reset(Ether*); +extern int etherelnk3reset(Ether*); +extern int etheri82557reset(Ether*); +extern int igbepnp(Ether *); +extern int i82563reset(Ether *); +extern int dp83815reset(Ether*); +extern int dp83820pnp(Ether*); +extern int rtl8139pnp(Ether*); +extern int rtl8169pnp(Ether*); + +static struct +{ + char* type; + int (*reset)(Ether*); +} etherctlr[] = +{ + { "21140", ether21140reset, }, + { "2114x", ether21140reset, }, + { "3C509", etherelnk3reset, }, + { "83815", dp83815reset, }, + { "dp83820", dp83820pnp, }, + { "elnk3", etherelnk3reset, }, + { "ga620", etherga620reset, }, + { "i82557", etheri82557reset, }, + { "igbe", igbepnp, }, + { "i82563", i82563reset, }, + { "rtl8139", rtl8139pnp, }, + { "rtl8169", rtl8169pnp, }, + { 0, }, +}; + +static Ether etherif[MaxEther]; + +void +etheriq(Ether* ether, Msgbuf* mb) +{ + ilock(ðer->rqlock); + if(ether->rqhead) + ether->rqtail->next = mb; + else + ether->rqhead = mb; + ether->rqtail = mb; + mb->next = 0; + iunlock(ðer->rqlock); + + wakeup(ðer->rqr); +} + +static int +isinput(void* arg) +{ + return ((Ether*)arg)->rqhead != 0; +} + +#include "compat.h" + +static void +etheri(void) +{ + Ether *ether; + Ifc *ifc; + Msgbuf *mb; + Enpkt *p; + + ether = getarg(); + ifc = ðer->ifc; + print("ether%di: %E %I\n", ether->ctlrno, ether->ifc.ea, ether->ifc.ipa); + (*ether->attach)(ether); + + for(;;) { + while(!isinput(ether)) + sleep(ðer->rqr, isinput, ether); + + ilock(ðer->rqlock); + if(ether->rqhead == 0) { + iunlock(ðer->rqlock); + continue; + } + mb = ether->rqhead; + ether->rqhead = mb->next; + iunlock(ðer->rqlock); + + p = (Enpkt*)mb->data; + switch(nhgets(p->type)){ + case Arptype: + arpreceive(p, mb->count, ifc); + break; + case Iptype: + ipreceive(p, mb->count, ifc); + ifc->rxpkt++; + ifc->work[0].count++; + ifc->work[1].count++; + ifc->work[2].count++; + ifc->rate[0].count += mb->count; + ifc->rate[1].count += mb->count; + ifc->rate[2].count += mb->count; + break; + } + mbfree(mb); + } +} + +static void +ethero(void) +{ + Ether *ether; + Ifc *ifc; + Msgbuf *mb; + int len; + + ether = getarg(); + ifc = ðer->ifc; + print("ether%do: %E %I\n", ether->ctlrno, ifc->ea, ifc->ipa); + + for(;;) { + for(;;) { + mb = recv(ifc->reply, 0); + if(mb != nil) + break; + } + + if(mb->data == 0) { + print("ether%do: pkt nil cat=%d free=%d\n", + ether->ctlrno, mb->category, mb->flags&FREE); + if(!(mb->flags & FREE)) + mbfree(mb); + continue; + } + + len = mb->count; + if(len > ETHERMAXTU) { + print("ether%do: pkt too big - %d\n", ether->ctlrno, len); + mbfree(mb); + continue; + } + if(len < ETHERMINTU) { + memset(mb->data+len, 0, ETHERMINTU-len); + mb->count = len = ETHERMINTU; + } + memmove(((Enpkt*)(mb->data))->s, ifc->ea, sizeof(ifc->ea)); + + ilock(ðer->tqlock); + if(ether->tqhead) + ether->tqtail->next = mb; + else + ether->tqhead = mb; + ether->tqtail = mb; + mb->next = 0; + iunlock(ðer->tqlock); + + (*ether->transmit)(ether); + + ifc->work[0].count++; + ifc->work[1].count++; + ifc->work[2].count++; + ifc->rate[0].count += len; + ifc->rate[1].count += len; + ifc->rate[2].count += len; + ifc->txpkt++; + } +} + +Msgbuf* +etheroq(Ether* ether) +{ + Msgbuf *mb; + + mb = nil; + + ilock(ðer->tqlock); + if(ether->tqhead){ + mb = ether->tqhead; + ether->tqhead = mb->next; + } + iunlock(ðer->tqlock); + + return mb; +} + +static void +cmd_state(int, char*[]) +{ + Ether *ether; + Ifc *ifc; + + for(ether = ðerif[0]; ether < ðerif[MaxEther]; ether++){ + if(ether->mbps == 0) + continue; + + ifc = ðer->ifc; + if(!isvalidip(ifc->ipa)) + continue; + + print("ether stats %d\n", ether->ctlrno); + print(" work =%7W%7W%7W pkts\n", ifc->work+0, ifc->work+1, ifc->work+2); + print(" rate =%7W%7W%7W tBps\n", ifc->rate+0, ifc->rate+1, ifc->rate+2); + print(" err = %3ld rc %3ld sum\n", ifc->rcverr, ifc->sumerr); + } +} + +void +etherstart(void) +{ + Ether *ether; + Ifc *ifc; + int anystarted; + char buf[100], *p; + + anystarted = 0; + for(ether = ðerif[0]; ether < ðerif[MaxEther]; ether++){ + if(ether->mbps == 0) + continue; + + ifc = ðer->ifc; + lock(ifc); + getipa(ifc, ether->ctlrno); + if(!isvalidip(ifc->ipa)){ + unlock(ifc); + ether->mbps = 0; + continue; + } + if(ifc->reply == 0){ + dofilter(ifc->work+0, C0a, C0b, 1); + dofilter(ifc->work+1, C1a, C1b, 1); + dofilter(ifc->work+2, C2a, C2b, 1); + dofilter(ifc->rate+0, C0a, C0b, 1000); + dofilter(ifc->rate+1, C1a, C1b, 1000); + dofilter(ifc->rate+2, C2a, C2b, 1000); + ifc->reply = newqueue(Nqueue); + } + unlock(ifc); + + sprint(ether->oname, "ether%do", ether->ctlrno); + userinit(ethero, ether, ether->oname); + sprint(ether->iname, "ether%di", ether->ctlrno); + userinit(etheri, ether, ether->iname); + + ifc->next = enets; + enets = ifc; + + anystarted++; + } + + if(anystarted){ + cmd_install("state", "-- ether stats", cmd_state); + arpstart(); + if((p = getconf("route")) && strlen(p) < sizeof(buf)-7){ + sprint(buf, "route %s", p); + cmd_exec(buf); + } + } +} + +static int +parseether(uchar *to, char *from) +{ + char nip[4]; + char *p; + int i; + + p = from; + while(*p == ' ') + ++p; + 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; +} + +void +etherinit(void) +{ + Ether *ether; + int i, n, ctlrno; + + for(ctlrno = 0; ctlrno < MaxEther; ctlrno++){ + ether = ðerif[ctlrno]; + memset(ether, 0, sizeof(Ether)); + if(!isaconfig("ether", ctlrno, ether)) + continue; + + for(n = 0; etherctlr[n].type; n++){ + if(cistrcmp(etherctlr[n].type, ether->type)) + continue; + dprint("FOUND ether %s\n", etherctlr[n].type); + ether->ctlrno = ctlrno; + ether->tbdf = BUSUNKNOWN; + for(i = 0; i < ether->nopt; i++){ + if(strncmp(ether->opt[i], "ea=", 3)) + continue; + if(parseether(ether->ea, ðer->opt[i][3]) == -1) + memset(ether->ea, 0, Easize); + } + dprint(" reset ... "); + if((*etherctlr[n].reset)(ether)){ + dprint("fail\n"); + break; + } + dprint("okay\n"); + if(ether->irq == 2) + ether->irq = 9; + setvec(Int0vec + ether->irq, ether->interrupt, ether); + memmove(ether->ifc.ea, ether->ea, sizeof(ether->ea)); + + print("ether%d: %s: %dMbps port 0x%lux irq %ld", + ctlrno, ether->type, ether->mbps, ether->port, ether->irq); + if(ether->mem) + print(" addr 0x%lux", ether->mem & ~KZERO); + if(ether->size) + print(" size 0x%lux", ether->size); + print(": "); + for(i = 0; i < sizeof(ether->ea); i++) + print("%2.2ux", ether->ea[i]); + print("\n"); + + break; + } + } +} diff -Nru /sys/src/fs/pc/etherif.h /sys/src/fs/pc/etherif.h --- /sys/src/fs/pc/etherif.h Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/pc/etherif.h Tue Nov 1 00:00:00 2011 @@ -0,0 +1,36 @@ +typedef struct Ether Ether; +struct Ether { + ISAConf; /* hardware info */ + + int ctlrno; + char iname[NAMELEN]; + char oname[NAMELEN]; + int tbdf; /* type+busno+devno+funcno */ + int mbps; /* Mbps */ + uchar ea[Easize]; + + void (*attach)(Ether*); /* filled in by reset routine */ + void (*transmit)(Ether*); + void (*interrupt)(Ureg*, void*); + void *ctlr; + + Ifc ifc; + + Lock rqlock; + Msgbuf* rqhead; + Msgbuf* rqtail; + Rendez rqr; + + Lock tqlock; + Msgbuf* tqhead; + Msgbuf* tqtail; + Rendez tqr; +}; + +#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)) + +extern void etheriq(Ether*, Msgbuf*); +extern Msgbuf* etheroq(Ether*); diff -Nru /sys/src/fs/pc/etherigbe.c /sys/src/fs/pc/etherigbe.c --- /sys/src/fs/pc/etherigbe.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/pc/etherigbe.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,2064 @@ +/* + * Intel 8254[340]NN Gigabit Ethernet Controller + * as found on the Intel PRO/1000 series of adapters: + * 82543GC Intel PRO/1000 T + * 82544EI Intel PRO/1000 XT + * 82540EM Intel PRO/1000 MT + * 82541[GP]I + * 82547GI + * 82546GB + * 82546EB + * To Do: + * finish autonegotiation code; + * integrate fiber stuff back in (this ONLY handles + * the CAT5 cards at the moment); + * add checksum-offload; + * add tuning control via ctl file; + * this driver is little-endian specific. + */ + +#ifdef FS +#include "all.h" +#include "io.h" +#include "mem.h" +#include "../ip/ip.h" + +#else + +#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" +#endif /* FS */ + +#include "etherif.h" +#include "ethermii.h" +#include "compat.h" + +enum { + i82542 = (0x1000<<16)|0x8086, + i82543gc = (0x1004<<16)|0x8086, + i82544ei = (0x1008<<16)|0x8086, + i82547ei = (0x1019<<16)|0x8086, + i82540em = (0x100E<<16)|0x8086, + i82540eplp = (0x101E<<16)|0x8086, + i82545gmc = (0x1026<<16)|0x8086, + i82547gi = (0x1075<<16)|0x8086, + i82541gi = (0x1076<<16)|0x8086, + i82541gi2 = (0x1077<<16)|0x8086, + i82546gb = (0x1079<<16)|0x8086, + i82541pi = (0x107c<<16)|0x8086, + i82546eb = (0x1010<<16)|0x8086, +}; + +/* from pci.c */ +enum +{ /* command register (pcidev->pcr) */ + IOen = (1<<0), + MEMen = (1<<1), + MASen = (1<<2), + MemWrInv = (1<<4), + PErrEn = (1<<6), + SErrEn = (1<<8), +}; +enum { + Ctrl = 0x00000000, /* Device Control */ + Ctrldup = 0x00000004, /* Device Control Duplicate */ + Status = 0x00000008, /* Device Status */ + Eecd = 0x00000010, /* EEPROM/Flash Control/Data */ + Ctrlext = 0x00000018, /* Extended Device Control */ + Mdic = 0x00000020, /* MDI Control */ + Fcal = 0x00000028, /* Flow Control Address Low */ + Fcah = 0x0000002C, /* Flow Control Address High */ + Fct = 0x00000030, /* Flow Control Type */ + Icr = 0x000000C0, /* Interrupt Cause Read */ + Ics = 0x000000C8, /* Interrupt Cause Set */ + Ims = 0x000000D0, /* Interrupt Mask Set/Read */ + Imc = 0x000000D8, /* Interrupt mask Clear */ + Rctl = 0x00000100, /* Receive Control */ + Fcttv = 0x00000170, /* Flow Control Transmit Timer Value */ + Txcw = 0x00000178, /* Transmit Configuration Word */ + Rxcw = 0x00000180, /* Receive Configuration Word */ + Tctl = 0x00000400, /* Transmit Control */ + Tipg = 0x00000410, /* Transmit IPG */ + Tbt = 0x00000448, /* Transmit Burst Timer */ + Ait = 0x00000458, /* Adaptive IFS Throttle */ + Fcrtl = 0x00002160, /* Flow Control RX Threshold Low */ + Fcrth = 0x00002168, /* Flow Control Rx Threshold High */ + Rdfh = 0x00002410, /* Receive data fifo head */ + Rdft = 0x00002418, /* Receive data fifo tail */ + Rdfhs = 0x00002420, /* Receive data fifo head saved */ + Rdfts = 0x00002428, /* Receive data fifo tail saved */ + Rdfpc = 0x00002430, /* Receive data fifo packet count */ + Rdbal = 0x00002800, /* Rd Base Address Low */ + Rdbah = 0x00002804, /* Rd Base Address High */ + Rdlen = 0x00002808, /* Receive Descriptor Length */ + Rdh = 0x00002810, /* Receive Descriptor Head */ + Rdt = 0x00002818, /* Receive Descriptor Tail */ + Rdtr = 0x00002820, /* Receive Descriptor Timer Ring */ + Rxdctl = 0x00002828, /* Receive Descriptor Control */ + Radv = 0x0000282C, /* Receive Interrupt Absolute Delay Timer */ + Txdmac = 0x00003000, /* Transfer DMA Control */ + Ett = 0x00003008, /* Early Transmit Control */ + Tdfh = 0x00003410, /* Transmit data fifo head */ + Tdft = 0x00003418, /* Transmit data fifo tail */ + Tdfhs = 0x00003420, /* Transmit data Fifo Head saved */ + Tdfts = 0x00003428, /* Transmit data fifo tail saved */ + Tdfpc = 0x00003430, /* Trasnmit data Fifo packet count */ + Tdbal = 0x00003800, /* Td Base Address Low */ + Tdbah = 0x00003804, /* Td Base Address High */ + Tdlen = 0x00003808, /* Transmit Descriptor Length */ + Tdh = 0x00003810, /* Transmit Descriptor Head */ + Tdt = 0x00003818, /* Transmit Descriptor Tail */ + Tidv = 0x00003820, /* Transmit Interrupt Delay Value */ + Txdctl = 0x00003828, /* Transmit Descriptor Control */ + Tadv = 0x0000382C, /* Transmit Interrupt Absolute Delay Timer */ + + Statistics = 0x00004000, /* Start of Statistics Area */ + Gorcl = 0x88/4, /* Good Octets Received Count */ + Gotcl = 0x90/4, /* Good Octets Transmitted Count */ + Torl = 0xC0/4, /* Total Octets Received */ + Totl = 0xC8/4, /* Total Octets Transmitted */ + Nstatistics = 64, + + Rxcsum = 0x00005000, /* Receive Checksum Control */ + Mta = 0x00005200, /* Multicast Table Array */ + Ral = 0x00005400, /* Receive Address Low */ + Rah = 0x00005404, /* Receive Address High */ + Manc = 0x00005820, /* Management Control */ +}; + +enum { /* Ctrl */ + Bem = 0x00000002, /* Big Endian Mode */ + Prior = 0x00000004, /* Priority on the PCI bus */ + Lrst = 0x00000008, /* Link Reset */ + Asde = 0x00000020, /* Auto-Speed Detection Enable */ + Slu = 0x00000040, /* Set Link Up */ + Ilos = 0x00000080, /* Invert Loss of Signal (LOS) */ + SspeedMASK = 0x00000300, /* Speed Selection */ + SspeedSHIFT = 8, + Sspeed10 = 0x00000000, /* 10Mb/s */ + Sspeed100 = 0x00000100, /* 100Mb/s */ + Sspeed1000 = 0x00000200, /* 1000Mb/s */ + Frcspd = 0x00000800, /* Force Speed */ + Frcdplx = 0x00001000, /* Force Duplex */ + SwdpinsloMASK = 0x003C0000, /* Software Defined Pins - lo nibble */ + SwdpinsloSHIFT = 18, + SwdpioloMASK = 0x03C00000, /* Software Defined Pins - I or O */ + SwdpioloSHIFT = 22, + Devrst = 0x04000000, /* Device Reset */ + Rfce = 0x08000000, /* Receive Flow Control Enable */ + Tfce = 0x10000000, /* Transmit Flow Control Enable */ + Vme = 0x40000000, /* VLAN Mode Enable */ +}; + +enum { /* Status */ + Lu = 0x00000002, /* Link Up */ + Tckok = 0x00000004, /* Transmit clock is running */ + Rbcok = 0x00000008, /* Receive clock is running */ + Txoff = 0x00000010, /* Transmission Paused */ + Tbimode = 0x00000020, /* TBI Mode Indication */ + LspeedMASK = 0x000000C0, /* Link Speed Setting */ + LspeedSHIFT = 6, + Lspeed10 = 0x00000000, /* 10Mb/s */ + Lspeed100 = 0x00000040, /* 100Mb/s */ + Lspeed1000 = 0x00000080, /* 1000Mb/s */ + Mtxckok = 0x00000400, /* MTX clock is running */ + Pci66 = 0x00000800, /* PCI Bus speed indication */ + Bus64 = 0x00001000, /* PCI Bus width indication */ + Pcixmode = 0x00002000, /* PCI-X mode */ + PcixspeedMASK = 0x0000C000, /* PCI-X bus speed */ + PcixspeedSHIFT = 14, + Pcix66 = 0x00000000, /* 50-66MHz */ + Pcix100 = 0x00004000, /* 66-100MHz */ + Pcix133 = 0x00008000, /* 100-133MHz */ +}; + +enum { /* Ctrl and Status */ + Fd = 0x00000001, /* Full-Duplex */ + AsdvMASK = 0x00000300, + AsdvSHIFT = 8, + Asdv10 = 0x00000000, /* 10Mb/s */ + Asdv100 = 0x00000100, /* 100Mb/s */ + Asdv1000 = 0x00000200, /* 1000Mb/s */ +}; + +enum { /* Eecd */ + Sk = 0x00000001, /* Clock input to the EEPROM */ + Cs = 0x00000002, /* Chip Select */ + Di = 0x00000004, /* Data Input to the EEPROM */ + Do = 0x00000008, /* Data Output from the EEPROM */ + Areq = 0x00000040, /* EEPROM Access Request */ + Agnt = 0x00000080, /* EEPROM Access Grant */ + Eepresent = 0x00000100, /* EEPROM Present */ + Eesz256 = 0x00000200, /* EEPROM is 256 words not 64 */ + Eeszaddr = 0x00000400, /* EEPROM size for 8254[17] */ + Spi = 0x00002000, /* EEPROM is SPI not Microwire */ +}; + +enum { /* Ctrlext */ + Gpien = 0x0000000F, /* General Purpose Interrupt Enables */ + SwdpinshiMASK = 0x000000F0, /* Software Defined Pins - hi nibble */ + SwdpinshiSHIFT = 4, + SwdpiohiMASK = 0x00000F00, /* Software Defined Pins - I or O */ + SwdpiohiSHIFT = 8, + Asdchk = 0x00001000, /* ASD Check */ + Eerst = 0x00002000, /* EEPROM Reset */ + Ips = 0x00004000, /* Invert Power State */ + Spdbyps = 0x00008000, /* Speed Select Bypass */ +}; + +enum { /* EEPROM content offsets */ + Ea = 0x00, /* Ethernet Address */ + Cf = 0x03, /* Compatibility Field */ + Pba = 0x08, /* Printed Board Assembly number */ + /* in fs kernel, Icw1 is defined in io.h; changed it here */ +#define Icw1 Igbe_icw1 + Icw1 = 0x0A, /* Initialization Control Word 1 */ + Sid = 0x0B, /* Subsystem ID */ + Svid = 0x0C, /* Subsystem Vendor ID */ + Did = 0x0D, /* Device ID */ + Vid = 0x0E, /* Vendor ID */ + Icw2 = 0x0F, /* Initialization Control Word 2 */ +}; + +enum { /* Mdic */ + MDIdMASK = 0x0000FFFF, /* Data */ + MDIdSHIFT = 0, + MDIrMASK = 0x001F0000, /* PHY Register Address */ + MDIrSHIFT = 16, + MDIpMASK = 0x03E00000, /* PHY Address */ + MDIpSHIFT = 21, + MDIwop = 0x04000000, /* Write Operation */ + MDIrop = 0x08000000, /* Read Operation */ + MDIready = 0x10000000, /* End of Transaction */ + MDIie = 0x20000000, /* Interrupt Enable */ + MDIe = 0x40000000, /* Error */ +}; + +enum { /* Icr, Ics, Ims, Imc */ + Txdw = 0x00000001, /* Transmit Descriptor Written Back */ + Txqe = 0x00000002, /* Transmit Queue Empty */ + Lsc = 0x00000004, /* Link Status Change */ + Rxseq = 0x00000008, /* Receive Sequence Error */ + Rxdmt0 = 0x00000010, /* Rd Minimum Threshold Reached */ + Rxo = 0x00000040, /* Receiver Overrun */ + Rxt0 = 0x00000080, /* Receiver Timer Interrupt */ + Mdac = 0x00000200, /* MDIO Access Completed */ + Rxcfg = 0x00000400, /* Receiving /C/ ordered sets */ + Gpi0 = 0x00000800, /* General Purpose Interrupts */ + Gpi1 = 0x00001000, + Gpi2 = 0x00002000, + Gpi3 = 0x00004000, +}; + +/* + * The Mdic register isn't implemented on the 82543GC, + * the software defined pins are used instead. + * These definitions work for the Intel PRO/1000 T Server Adapter. + * The direction pin bits are read from the EEPROM. + */ +enum { + Mdd = ((1<<2)<nic+((r)/4))) +#define csr32w(c, r, v) (*((c)->nic+((r)/4)) = (v)) + +static Ctlr* igbectlrhead; +static Ctlr* igbectlrtail; + +static Lock igberblock; /* free receive Blocks */ +static Block* igberbpool; + +static char* statistics[Nstatistics] = { + "CRC Error", + "Alignment Error", + "Symbol Error", + "RX Error", + "Missed Packets", + "Single Collision", + "Excessive Collisions", + "Multiple Collision", + "Late Collisions", + nil, + "Collision", + "Transmit Underrun", + "Defer", + "Transmit - No CRS", + "Sequence Error", + "Carrier Extension Error", + "Receive Error Length", + nil, + "XON Received", + "XON Transmitted", + "XOFF Received", + "XOFF Transmitted", + "FC Received Unsupported", + "Packets Received (64 Bytes)", + "Packets Received (65-127 Bytes)", + "Packets Received (128-255 Bytes)", + "Packets Received (256-511 Bytes)", + "Packets Received (512-1023 Bytes)", + "Packets Received (1024-1522 Bytes)", + "Good Packets Received", + "Broadcast Packets Received", + "Multicast Packets Received", + "Good Packets Transmitted", + nil, + "Good Octets Received", + nil, + "Good Octets Transmitted", + nil, + nil, + nil, + "Receive No Buffers", + "Receive Undersize", + "Receive Fragment", + "Receive Oversize", + "Receive Jabber", + nil, + nil, + nil, + "Total Octets Received", + nil, + "Total Octets Transmitted", + nil, + "Total Packets Received", + "Total Packets Transmitted", + "Packets Transmitted (64 Bytes)", + "Packets Transmitted (65-127 Bytes)", + "Packets Transmitted (128-255 Bytes)", + "Packets Transmitted (256-511 Bytes)", + "Packets Transmitted (512-1023 Bytes)", + "Packets Transmitted (1024-1522 Bytes)", + "Multicast Packets Transmitted", + "Broadcast Packets Transmitted", + "TCP Segmentation Context Transmitted", + "TCP Segmentation Context Fail", +}; + +#ifndef FS +static long +igbeifstat(Ether* edev, void* a, long n, ulong offset) +{ + Ctlr *ctlr; + char *p, *s; + int i, l, r; + uvlong tuvl, ruvl; + + ctlr = edev->ctlr; + qlock(&ctlr->slock); + p = malloc(2*READSTR); + if (p == nil) + panic("igbeifstat: no mem"); + l = 0; + for(i = 0; i < Nstatistics; i++){ + r = csr32r(ctlr, Statistics+i*4); + if((s = statistics[i]) == nil) + continue; + switch(i){ + case Gorcl: + case Gotcl: + case Torl: + case Totl: + ruvl = r; + ruvl += ((uvlong)csr32r(ctlr, Statistics+(i+1)*4))<<32; + tuvl = ruvl; + tuvl += ctlr->statistics[i]; + tuvl += ((uvlong)ctlr->statistics[i+1])<<32; + if(tuvl == 0) + continue; + ctlr->statistics[i] = tuvl; + ctlr->statistics[i+1] = tuvl>>32; + l += snprint(p+l, 2*READSTR-l, "%s: %llud %llud\n", + s, tuvl, ruvl); + i++; + break; + + default: + ctlr->statistics[i] += r; + if(ctlr->statistics[i] == 0) + continue; + l += snprint(p+l, 2*READSTR-l, "%s: %ud %ud\n", + s, ctlr->statistics[i], r); + break; + } + } + + l += snprint(p+l, 2*READSTR-l, "lintr: %ud %ud\n", + ctlr->lintr, ctlr->lsleep); + l += snprint(p+l, 2*READSTR-l, "rintr: %ud %ud\n", + ctlr->rintr, ctlr->rsleep); + l += snprint(p+l, 2*READSTR-l, "tintr: %ud %ud\n", + ctlr->tintr, ctlr->txdw); + l += snprint(p+l, 2*READSTR-l, "ixcs: %ud %ud %ud\n", + ctlr->ixsm, ctlr->ipcs, ctlr->tcpcs); + l += snprint(p+l, 2*READSTR-l, "rdtr: %ud\n", ctlr->rdtr); + l += snprint(p+l, 2*READSTR-l, "Ctrlext: %08x\n", csr32r(ctlr, Ctrlext)); + + l += snprint(p+l, 2*READSTR-l, "eeprom:"); + for(i = 0; i < 0x40; i++){ + if(i && ((i & 0x07) == 0)) + l += snprint(p+l, 2*READSTR-l, "\n "); + l += snprint(p+l, 2*READSTR-l, " %4.4uX", ctlr->eeprom[i]); + } + l += snprint(p+l, 2*READSTR-l, "\n"); + + if(ctlr->mii != nil && ctlr->mii->curphy != nil){ + l += snprint(p+l, 2*READSTR, "phy: "); + for(i = 0; i < NMiiPhyr; i++){ + if(i && ((i & 0x07) == 0)) + l += snprint(p+l, 2*READSTR-l, "\n "); + r = miimir(ctlr->mii, i); + l += snprint(p+l, 2*READSTR-l, " %4.4uX", r); + } + snprint(p+l, 2*READSTR-l, "\n"); + } + n = readstr(offset, a, n, p); + free(p); + qunlock(&ctlr->slock); + + return n; +} + +enum { + CMrdtr, +}; + +static Cmdtab igbectlmsg[] = { + CMrdtr, "rdtr", 2, +}; + +static long +igbectl(Ether* edev, void* buf, long n) +{ + int v; + char *p; + Ctlr *ctlr; + Cmdbuf *cb; + Cmdtab *ct; + + if((ctlr = edev->ctlr) == nil) + error(Enonexist); + + cb = parsecmd(buf, n); + if(waserror()){ + free(cb); + nexterror(); + } + + ct = lookupcmd(cb, igbectlmsg, nelem(igbectlmsg)); + switch(ct->index){ + case CMrdtr: + v = strtol(cb->f[1], &p, 0); + if(v < 0 || p == cb->f[1] || v > 0xFFFF) + error(Ebadarg); + ctlr->rdtr = v;; + csr32w(ctlr, Rdtr, Fpd|v); + break; + } + free(cb); + poperror(); + + return n; +} +#endif /* FS */ + +static void +igbepromiscuous(void* arg, int on) +{ + int rctl; + Ctlr *ctlr; + Ether *edev; + + edev = arg; + ctlr = edev->ctlr; + + rctl = csr32r(ctlr, Rctl); + rctl &= ~MoMASK; + rctl |= Mo47b36; + if(on) + rctl |= Upe|Mpe; + else + rctl &= ~(Upe|Mpe); + csr32w(ctlr, Rctl, rctl); +} + +static void +igbemulticast(void* arg, uchar* addr, int on) +{ + int bit, x; + Ctlr *ctlr; + Ether *edev; + + edev = arg; + ctlr = edev->ctlr; + + x = addr[5]>>1; + bit = ((addr[5] & 1)<<4)|(addr[4]>>4); + if(on) + ctlr->mta[x] |= 1<mta[x] &= ~(1<mta[x]); +} + +static Block* +igberballoc(void) +{ + Block *bp; + + ilock(&igberblock); + if((bp = igberbpool) != nil){ + igberbpool = bp->next; + bp->next = nil; + } + iunlock(&igberblock); + + return bp; +} + +static void +igberbfree(Block* bp) +{ + BLKRESET(bp); + ilock(&igberblock); + bp->next = igberbpool; + igberbpool = bp; + iunlock(&igberblock); +} + +static void +igbeim(Ctlr* ctlr, int im) +{ + ilock(&ctlr->imlock); + ctlr->im |= im; + csr32w(ctlr, Ims, ctlr->im); + iunlock(&ctlr->imlock); +} + +static int +igbelim(void* ctlr) +{ + return ((Ctlr*)ctlr)->lim != 0; +} + +static void +igbelproc(PROCARG(void *arg)) +{ + Ctlr *ctlr; + Ether *edev; + MiiPhy *phy; + int ctrl, r; + + edev = GETARG(arg); + ctlr = edev->ctlr; + for(;;){ + if(ctlr->mii == nil || ctlr->mii->curphy == nil) + continue; + + /* + * To do: + * logic to manage status change, + * this is incomplete but should work + * one time to set up the hardware. + * + * MiiPhy.speed, etc. should be in Mii. + */ + if(miistatus(ctlr->mii) < 0) + //continue; + goto enable; + + phy = ctlr->mii->curphy; + ctrl = csr32r(ctlr, Ctrl); + + switch(ctlr->id){ + case i82543gc: + case i82544ei: + default: + if(!(ctrl & Asde)){ + ctrl &= ~(SspeedMASK|Ilos|Fd); + ctrl |= Frcdplx|Frcspd; + if(phy->speed == 1000) + ctrl |= Sspeed1000; + else if(phy->speed == 100) + ctrl |= Sspeed100; + if(phy->fd) + ctrl |= Fd; + } + break; + + case i82540em: + case i82540eplp: + case i82547gi: + case i82541gi: + case i82541gi2: + case i82541pi: + break; + } + + /* + * Collision Distance. + */ + r = csr32r(ctlr, Tctl); + r &= ~ColdMASK; + if(phy->fd) + r |= 64<rfc) + ctrl |= Rfce; + if(phy->tfc) + ctrl |= Tfce; + csr32w(ctlr, Ctrl, ctrl); + +enable: + ctlr->lim = 0; + igbeim(ctlr, Lsc); + + ctlr->lsleep++; + sleep(&ctlr->lrendez, igbelim, ctlr); + } +} + +static void +igbetxinit(Ctlr* ctlr) +{ + int i, r; + Block *bp; + + csr32w(ctlr, Tctl, (0x0F<id){ + default: + r = 6; + break; + case i82543gc: + case i82544ei: + case i82547ei: + case i82540em: + case i82540eplp: + case i82541gi: + case i82541gi2: + case i82541pi: + case i82545gmc: + case i82546gb: + case i82546eb: + case i82547gi: + r = 8; + break; + } + csr32w(ctlr, Tipg, (6<<20)|(8<<10)|r); + csr32w(ctlr, Ait, 0); + csr32w(ctlr, Txdmac, 0); + + csr32w(ctlr, Tdbal, PCIWADDR(ctlr->tdba)); + csr32w(ctlr, Tdbah, 0); + csr32w(ctlr, Tdlen, ctlr->ntd*sizeof(Td)); + ctlr->tdh = PREV(0, ctlr->ntd); + csr32w(ctlr, Tdh, 0); + ctlr->tdt = 0; + csr32w(ctlr, Tdt, 0); + + for(i = 0; i < ctlr->ntd; i++){ + if((bp = ctlr->tb[i]) != nil){ + ctlr->tb[i] = nil; + freeb(bp); + } + memset(&ctlr->tdba[i], 0, sizeof(Td)); + } + ctlr->tdfree = ctlr->ntd; + + csr32w(ctlr, Tidv, 128); + r = (4<id){ + default: + break; + case i82540em: + case i82540eplp: + case i82547gi: + case i82545gmc: + case i82546gb: + case i82546eb: + case i82541gi: + case i82541gi2: + case i82541pi: + r = csr32r(ctlr, Txdctl); + r &= ~WthreshMASK; + r |= Gran|(4<ctlr; + + ilock(&ctlr->tlock); + + /* + * Free any completed packets + */ + tdh = ctlr->tdh; + while(NEXT(tdh, ctlr->ntd) != csr32r(ctlr, Tdh)){ + if((bp = ctlr->tb[tdh]) != nil){ + ctlr->tb[tdh] = nil; + freeb(bp); + } + memset(&ctlr->tdba[tdh], 0, sizeof(Td)); + tdh = NEXT(tdh, ctlr->ntd); + } + ctlr->tdh = tdh; + + /* + * Try to fill the ring back up. + */ + tdt = ctlr->tdt; + while(NEXT(tdt, ctlr->ntd) != tdh){ + if((bp = etheroq(edev)) == nil) + break; + td = &ctlr->tdba[tdt]; + td->addr[0] = PCIWADDR(bp->rp); + td->control = ((BLEN(bp) & LenMASK)<control |= Dext|Ifcs|Teop|DtypeDD; + ctlr->tb[tdt] = bp; + tdt = NEXT(tdt, ctlr->ntd); + if(NEXT(tdt, ctlr->ntd) == tdh){ + td->control |= Rs; + ctlr->txdw++; + ctlr->tdt = tdt; + csr32w(ctlr, Tdt, tdt); + igbeim(ctlr, Txdw); + break; + } + ctlr->tdt = tdt; + csr32w(ctlr, Tdt, tdt); + } + + iunlock(&ctlr->tlock); +} + +static void +igbereplenish(Ctlr* ctlr) +{ + Rd *rd; + int rdt; + Block *bp; + + rdt = ctlr->rdt; + while(NEXT(rdt, ctlr->nrd) != ctlr->rdh){ + rd = &ctlr->rdba[rdt]; + if(ctlr->rb[rdt] == nil){ + bp = igberballoc(); + if(bp == nil) { + iprint("igbereplenish: no available buffers\n"); + break; + } + ctlr->rb[rdt] = bp; + rd->addr[0] = PCIWADDR(bp->rp); + rd->addr[1] = 0; + } + coherence(); + rd->status = 0; + rdt = NEXT(rdt, ctlr->nrd); + ctlr->rdfree++; + } + ctlr->rdt = rdt; + csr32w(ctlr, Rdt, rdt); +} + +static void +igberxinit(Ctlr* ctlr) +{ + int i; + Block *bp; + + csr32w(ctlr, Rctl, Dpf|Bsize2048|Bam|RdtmsHALF); + + csr32w(ctlr, Rdbal, PCIWADDR(ctlr->rdba)); + csr32w(ctlr, Rdbah, 0); + csr32w(ctlr, Rdlen, ctlr->nrd*sizeof(Rd)); + ctlr->rdh = 0; + csr32w(ctlr, Rdh, 0); + ctlr->rdt = 0; + csr32w(ctlr, Rdt, 0); + ctlr->rdtr = 0; + csr32w(ctlr, Rdtr, Fpd|0); + + for(i = 0; i < ctlr->nrd; i++){ + if((bp = ctlr->rb[i]) != nil){ + ctlr->rb[i] = nil; + freeb(bp); + } + } + igbereplenish(ctlr); + + switch(ctlr->id){ + case i82540em: + case i82540eplp: + case i82541gi: + case i82541gi2: + case i82541pi: + case i82545gmc: + case i82546gb: + case i82546eb: + case i82547gi: + csr32w(ctlr, Radv, 64); + break; + } + csr32w(ctlr, Rxdctl, (8<rim != 0; +} + +static void +igberproc(PROCARG(void *arg)) +{ + Rd *rd; + Block *bp; + Ctlr *ctlr; + int r, rdh; + Ether *edev; + + edev = GETARG(arg); + ctlr = edev->ctlr; + + igberxinit(ctlr); + r = csr32r(ctlr, Rctl); + r |= Ren; + csr32w(ctlr, Rctl, r); + + for(;;){ + ctlr->rim = 0; + igbeim(ctlr, Rxt0|Rxo|Rxdmt0|Rxseq); + ctlr->rsleep++; + sleep(&ctlr->rrendez, igberim, ctlr); + + rdh = ctlr->rdh; + for(;;){ + rd = &ctlr->rdba[rdh]; + + if(!(rd->status & Rdd)) + break; + + /* + * Accept eop packets with no errors. + * With no errors and the Ixsm bit set, + * the descriptor status Tpcs and Ipcs bits give + * an indication of whether the checksums were + * calculated and valid. + */ + if((rd->status & Reop) && rd->errors == 0){ + bp = ctlr->rb[rdh]; + ctlr->rb[rdh] = nil; + INCRPTR(bp, rd->length); + bp->next = nil; + if(!(rd->status & Ixsm)){ + ctlr->ixsm++; + if(rd->status & Ipcs){ + /* + * IP checksum calculated + * (and valid as errors == 0). + */ + ctlr->ipcs++; +#ifndef FS + bp->flag |= Bipck; +#endif + } + if(rd->status & Tcpcs){ + /* + * TCP/UDP checksum calculated + * (and valid as errors == 0). + */ + ctlr->tcpcs++; +#ifndef FS + bp->flag |= Btcpck|Budpck; +#endif + } +#ifndef FS + bp->checksum = rd->checksum; + bp->flag |= Bpktck; +#endif + } + ETHERIQ(edev, bp, 1); + } + else if(ctlr->rb[rdh] != nil){ + freeb(ctlr->rb[rdh]); + ctlr->rb[rdh] = nil; + } + + memset(rd, 0, sizeof(Rd)); + coherence(); + ctlr->rdfree--; + rdh = NEXT(rdh, ctlr->nrd); + } + ctlr->rdh = rdh; + + if(ctlr->rdfree < ctlr->nrd/2 || (ctlr->rim & Rxdmt0)) + igbereplenish(ctlr); + } +} + +static void +igbeattach(Ether* edev) +{ + Block *bp; + Ctlr *ctlr; + char name[KNAMELEN]; + + ctlr = edev->ctlr; + qlock(&ctlr->alock); + if(ctlr->alloc != nil){ + qunlock(&ctlr->alock); + return; + } + + ctlr->nrd = ROUND(Nrd, 8); + ctlr->ntd = ROUND(Ntd, 8); + ctlr->alloc = malloc(ctlr->nrd*sizeof(Rd)+ctlr->ntd*sizeof(Td) + 127); + if(ctlr->alloc == nil){ + qunlock(&ctlr->alock); + return; + } + ctlr->rdba = (Rd*)ROUNDUP((uintptr)ctlr->alloc, 128); + ctlr->tdba = (Td*)(ctlr->rdba+ctlr->nrd); + + ctlr->rb = malloc(ctlr->nrd*sizeof(Block*)); + ctlr->tb = malloc(ctlr->ntd*sizeof(Block*)); + if (ctlr->tb == nil) + panic("igbeattach: no mem"); + + if(waserror()){ + while(ctlr->nrb > 0){ + bp = igberballoc(); + if (bp == nil) + panic("igbeattach: no mem for rcv bufs"); + bp->free = nil; + freeb(bp); + ctlr->nrb--; + } + free(ctlr->tb); + ctlr->tb = nil; + free(ctlr->rb); + ctlr->rb = nil; + free(ctlr->alloc); + ctlr->alloc = nil; + qunlock(&ctlr->alock); + nexterror(); + } + + for(ctlr->nrb = 0; ctlr->nrb < Nrb; ctlr->nrb++){ + if((bp = allocb(Rbsz)) == nil) + break; +#ifdef FS + bp->flags |= Mbrcvbuf; +#endif + bp->free = igberbfree; + freeb(bp); + } + + snprint(name, KNAMELEN, "#l%dlproc", edev->ctlrno); + kproc(name, igbelproc, edev); + + snprint(name, KNAMELEN, "#l%drproc", edev->ctlrno); + kproc(name, igberproc, edev); + + igbetxinit(ctlr); + + qunlock(&ctlr->alock); + poperror(); +} + +static void +igbeinterrupt(Ureg*, void* arg) +{ + Ctlr *ctlr; + Ether *edev; + int icr, im, txdw; + + edev = arg; + ctlr = edev->ctlr; + + ilock(&ctlr->imlock); + csr32w(ctlr, Imc, ~0); + im = ctlr->im; + txdw = 0; + + while((icr = csr32r(ctlr, Icr) & ctlr->im) != 0){ + if(icr & Lsc){ + im &= ~Lsc; + ctlr->lim = icr & Lsc; + wakeup(&ctlr->lrendez); + ctlr->lintr++; + } + if(icr & (Rxt0|Rxo|Rxdmt0|Rxseq)){ + im &= ~(Rxt0|Rxo|Rxdmt0|Rxseq); + ctlr->rim = icr & (Rxt0|Rxo|Rxdmt0|Rxseq); + wakeup(&ctlr->rrendez); + ctlr->rintr++; + } + if(icr & Txdw){ + im &= ~Txdw; + txdw++; + ctlr->tintr++; + } + } + + ctlr->im = im; + csr32w(ctlr, Ims, im); + iunlock(&ctlr->imlock); + + if(txdw) + igbetransmit(edev); +} + +static int +i82543mdior(Ctlr* ctlr, int n) +{ + int ctrl, data, i, r; + + /* + * Read n bits from the Management Data I/O Interface. + */ + ctrl = csr32r(ctlr, Ctrl); + r = (ctrl & ~Mddo)|Mdco; + data = 0; + for(i = n-1; i >= 0; i--){ + if(csr32r(ctlr, Ctrl) & Mdd) + data |= (1<= 0; i--){ + if(bits & (1<ctlr; + + /* + * MII Management Interface Read. + * + * Preamble; + * ST+OP+PHYAD+REGAD; + * TA + 16 data bits. + */ + i82543mdiow(ctlr, 0xFFFFFFFF, 32); + i82543mdiow(ctlr, 0x1800|(pa<<5)|ra, 14); + data = i82543mdior(ctlr, 18); + + if(data & 0x10000) + return -1; + + return data & 0xFFFF; +} + +static int +i82543miimiw(Mii* mii, int pa, int ra, int data) +{ + Ctlr *ctlr; + + ctlr = mii->ctlr; + + /* + * MII Management Interface Write. + * + * Preamble; + * ST+OP+PHYAD+REGAD+TA + 16 data bits; + * Z. + */ + i82543mdiow(ctlr, 0xFFFFFFFF, 32); + data &= 0xFFFF; + data |= (0x05<<(5+5+2+16))|(pa<<(5+2+16))|(ra<<(2+16))|(0x02<<16); + i82543mdiow(ctlr, data, 32); + + return 0; +} + +static int +igbemiimir(Mii* mii, int pa, int ra) +{ + Ctlr *ctlr; + int mdic, timo; + + ctlr = mii->ctlr; + + csr32w(ctlr, Mdic, MDIrop|(pa<ctlr; + + data &= MDIdMASK; + csr32w(ctlr, Mdic, MDIwop|(pa<mii = malloc(sizeof(Mii))) == nil) + return -1; + ctlr->mii->ctlr = ctlr; + + ctrl = csr32r(ctlr, Ctrl); + ctrl |= Slu; + + switch(ctlr->id){ + case i82543gc: + ctrl |= Frcdplx|Frcspd; + csr32w(ctlr, Ctrl, ctrl); + + /* + * The reset pin direction (Mdro) should already + * be set from the EEPROM load. + * If it's not set this configuration is unexpected + * so bail. + */ + r = csr32r(ctlr, Ctrlext); + if(!(r & Mdro)) + return -1; + csr32w(ctlr, Ctrlext, r); + delay(20); + r = csr32r(ctlr, Ctrlext); + r &= ~Mdr; + csr32w(ctlr, Ctrlext, r); + delay(20); + r = csr32r(ctlr, Ctrlext); + r |= Mdr; + csr32w(ctlr, Ctrlext, r); + delay(20); + + ctlr->mii->mir = i82543miimir; + ctlr->mii->miw = i82543miimiw; + break; + case i82544ei: + case i82547ei: + case i82540em: + case i82540eplp: + case i82547gi: + case i82541gi: + case i82541gi2: + case i82541pi: + case i82545gmc: + case i82546gb: + case i82546eb: + ctrl &= ~(Frcdplx|Frcspd); + csr32w(ctlr, Ctrl, ctrl); + ctlr->mii->mir = igbemiimir; + ctlr->mii->miw = igbemiimiw; + break; + default: + free(ctlr->mii); + ctlr->mii = nil; + return -1; + } + + if(mii(ctlr->mii, ~0) == 0 || (phy = ctlr->mii->curphy) == nil){ + free(ctlr->mii); + ctlr->mii = nil; + return -1; + } + USED(phy); + // print("oui %X phyno %d\n", phy->oui, phy->phyno); + + /* + * 8254X-specific PHY registers not in 802.3: + * 0x10 PHY specific control + * 0x14 extended PHY specific control + * Set appropriate values then reset the PHY to have + * changes noted. + */ + switch(ctlr->id){ + case i82547gi: + case i82541gi: + case i82541gi2: + case i82541pi: + case i82545gmc: + case i82546gb: + case i82546eb: + break; + default: + r = miimir(ctlr->mii, 16); + r |= 0x0800; /* assert CRS on Tx */ + r |= 0x0060; /* auto-crossover all speeds */ + r |= 0x0002; /* polarity reversal enabled */ + miimiw(ctlr->mii, 16, r); + + r = miimir(ctlr->mii, 20); + r |= 0x0070; /* +25MHz clock */ + r &= ~0x0F00; + r |= 0x0100; /* 1x downshift */ + miimiw(ctlr->mii, 20, r); + + miireset(ctlr->mii); + p = 0; + if(ctlr->txcw & TxcwPs) + p |= AnaP; + if(ctlr->txcw & TxcwAs) + p |= AnaAP; + miiane(ctlr->mii, ~0, p, ~0); + break; + } + return 0; +} + +static int +at93c46io(Ctlr* ctlr, char* op, int data) +{ + char *lp, *p; + int i, loop, eecd, r; + + eecd = csr32r(ctlr, Eecd); + + r = 0; + loop = -1; + lp = nil; + for(p = op; *p != '\0'; p++){ + switch(*p){ + default: + return -1; + case ' ': + continue; + case ':': /* start of loop */ + loop = strtol(p+1, &lp, 0)-1; + lp--; + if(p == lp) + loop = 7; + p = lp; + continue; + case ';': /* end of loop */ + if(lp == nil) + return -1; + loop--; + if(loop >= 0) + p = lp; + else + lp = nil; + continue; + case 'C': /* assert clock */ + eecd |= Sk; + break; + case 'c': /* deassert clock */ + eecd &= ~Sk; + break; + case 'D': /* next bit in 'data' byte */ + if(loop < 0) + return -1; + if(data & (1<= 0) + r |= (i<= 0) + return -1; + return r; +} + +static int +at93c46r(Ctlr* ctlr) +{ + ushort sum; + char rop[20]; + int addr, areq, bits, data, eecd, i; + + eecd = csr32r(ctlr, Eecd); + if(eecd & Spi){ + print("igbe: SPI EEPROM access not implemented\n"); + return 0; + } + if(eecd & (Eeszaddr|Eesz256)) + bits = 8; + else + bits = 6; + + sum = 0; + + switch(ctlr->id){ + default: + areq = 0; + break; + case i82541gi: + case i82547gi: + case i82540em: + case i82540eplp: + case i82541pi: + case i82541gi2: + case i82545gmc: + case i82546gb: + case i82546eb: + areq = 1; + csr32w(ctlr, Eecd, eecd|Areq); + for(i = 0; i < 1000; i++){ + if((eecd = csr32r(ctlr, Eecd)) & Agnt) + break; + microdelay(5); + } + if(!(eecd & Agnt)){ + print("igbe: not granted EEPROM access\n"); + goto release; + } + break; + } + snprint(rop, sizeof(rop), "S :%dDCc;", bits+3); + + for(addr = 0; addr < 0x40; addr++){ + /* + * Read a word at address 'addr' from the Atmel AT93C46 + * 3-Wire Serial EEPROM or compatible. The EEPROM access is + * controlled by 4 bits in Eecd. See the AT93C46 datasheet + * for protocol details. + */ + if(at93c46io(ctlr, rop, (0x06<eeprom[addr] = data; + sum += data; + } + +release: + if(areq) + csr32w(ctlr, Eecd, eecd & ~Areq); + return sum; +} + +static int +igbedetach(Ctlr* ctlr) +{ + int r, timeo; + + if (ctlr == nil) + return -1; + + /* + * Perform a device reset to get the chip back to the + * power-on state, followed by an EEPROM reset to read + * the defaults for some internal registers. + * + * The alpha needs the rest of this routine to run at splhi + * or else the card interrupts and resets the processor. Don't know + * why. Since the alpha isn't important any more, let's ignore that. + */ + csr32w(ctlr, Imc, ~0); + csr32w(ctlr, Rctl, 0); + csr32w(ctlr, Tctl, 0); + + delay(10); /* was 10 then 100 */ + + csr32w(ctlr, Ctrl, Devrst); + delay(1); /* was 100 */ + for(timeo = 0; timeo < 1000; timeo++){ + if(!(csr32r(ctlr, Ctrl) & Devrst)) + break; + delay(1); + } + if(csr32r(ctlr, Ctrl) & Devrst) + return -1; + r = csr32r(ctlr, Ctrlext); + csr32w(ctlr, Ctrlext, r|Eerst); + delay(1); + for(timeo = 0; timeo < 1000; timeo++){ + if(!(csr32r(ctlr, Ctrlext) & Eerst)) + break; + delay(1); + } + if(csr32r(ctlr, Ctrlext) & Eerst) + return -1; + + switch(ctlr->id){ + default: + break; + case i82540em: + case i82540eplp: + case i82541gi: + case i82541pi: + case i82547gi: + case i82541gi2: + case i82545gmc: + case i82546gb: + case i82546eb: + r = csr32r(ctlr, Manc); + r &= ~Arpen; + csr32w(ctlr, Manc, r); + break; + } + + csr32w(ctlr, Imc, ~0); + delay(1); /* was 100 */ + for(timeo = 0; timeo < 1000; timeo++){ + if(!csr32r(ctlr, Icr)) + break; + delay(1); + } + if(csr32r(ctlr, Icr)) + return -1; + + return 0; +} + +static void +igbeshutdown(Ether* ether) +{ + igbedetach(ether->ctlr); +} + +int +etherigbereset(Ctlr* ctlr) +{ + int ctrl, i, pause, r, swdpio, txcw; + + if(igbedetach(ctlr)) + return -1; + + /* + * Read the EEPROM, validate the checksum + * then get the device back to a power-on state. + */ + if((r = at93c46r(ctlr)) != 0xBABA){ + print("igbe: bad EEPROM checksum - 0x%4.4uX\n", r); + return -1; + } + + /* + * Snarf and set up the receive addresses. + * There are 16 addresses. The first should be the MAC address. + * The others are cleared and not marked valid (MS bit of Rah). + */ + if ((ctlr->id == i82546gb || ctlr->id == i82546eb) && BUSFNO(ctlr->pcidev->tbdf) == 1) + ctlr->eeprom[Ea+2] += 0x100; // second interface + for(i = Ea; i < Eaddrlen/2; i++){ +if(i == Ea && ctlr->id == i82541gi && ctlr->eeprom[i] == 0xFFFF) + ctlr->eeprom[i] = 0xD000; + ctlr->ra[2*i] = ctlr->eeprom[i]; + ctlr->ra[2*i+1] = ctlr->eeprom[i]>>8; + } + r = (ctlr->ra[3]<<24)|(ctlr->ra[2]<<16)|(ctlr->ra[1]<<8)|ctlr->ra[0]; + csr32w(ctlr, Ral, r); + r = 0x80000000|(ctlr->ra[5]<<8)|ctlr->ra[4]; + csr32w(ctlr, Rah, r); + for(i = 1; i < 16; i++){ + csr32w(ctlr, Ral+i*8, 0); + csr32w(ctlr, Rah+i*8, 0); + } + + /* + * Clear the Multicast Table Array. + * It's a 4096 bit vector accessed as 128 32-bit registers. + */ + memset(ctlr->mta, 0, sizeof(ctlr->mta)); + for(i = 0; i < 128; i++) + csr32w(ctlr, Mta+i*4, 0); + + /* + * Just in case the Eerst didn't load the defaults + * (doesn't appear to fully on the 8243GC), do it manually. + */ + if (ctlr->id == i82543gc) { // 82543 + txcw = csr32r(ctlr, Txcw); + txcw &= ~(TxcwAne|TxcwPauseMASK|TxcwFd); + ctrl = csr32r(ctlr, Ctrl); + ctrl &= ~(SwdpioloMASK|Frcspd|Ilos|Lrst|Fd); + + if(ctlr->eeprom[Icw1] & 0x0400){ + ctrl |= Fd; + txcw |= TxcwFd; + } + if(ctlr->eeprom[Icw1] & 0x0200) + ctrl |= Lrst; + if(ctlr->eeprom[Icw1] & 0x0010) + ctrl |= Ilos; + if(ctlr->eeprom[Icw1] & 0x0800) + ctrl |= Frcspd; + swdpio = (ctlr->eeprom[Icw1] & 0x01E0)>>5; + ctrl |= swdpio<eeprom[Icw2] & 0x00F0)>>4; + if(ctlr->eeprom[Icw1] & 0x1000) + ctrl |= Ips; + ctrl |= swdpio<eeprom[Icw2] & 0x0800) + txcw |= TxcwAne; + pause = (ctlr->eeprom[Icw2] & 0x3000)>>12; + txcw |= pause<fcrtl = 0x00002000; + ctlr->fcrth = 0x00004000; + txcw |= TxcwAs|TxcwPs; + break; + case 0: + ctlr->fcrtl = 0x00002000; + ctlr->fcrth = 0x00004000; + break; + case 2: + ctlr->fcrtl = 0; + ctlr->fcrth = 0; + txcw |= TxcwAs; + break; + } + ctlr->txcw = txcw; + csr32w(ctlr, Txcw, txcw); + } + + + /* + * Flow control - values from the datasheet. + */ + csr32w(ctlr, Fcal, 0x00C28001); + csr32w(ctlr, Fcah, 0x00000100); + csr32w(ctlr, Fct, 0x00008808); + csr32w(ctlr, Fcttv, 0x00000100); + + csr32w(ctlr, Fcrtl, ctlr->fcrtl); + csr32w(ctlr, Fcrth, ctlr->fcrth); + + if(!(csr32r(ctlr, Status) & Tbimode) && igbemii(ctlr) < 0) + return -1; + + return 0; +} + +static void +igbepci(void) +{ + int cls; + Pcidev *p; + Ctlr *ctlr; + void *mem; + + p = nil; + while(p = pcimatch(p, 0, 0)){ + /* ccru is a short in the FS kernel, thus the cast to uchar */ + if(p->ccrb != 0x02 || +#ifdef FS + (uchar) +#endif + p->ccru != 0) + continue; + + switch((p->did<<16)|p->vid){ + default: + continue; + case i82543gc: + case i82544ei: + case i82547ei: + case i82540em: + case i82540eplp: + case i82541gi: + case i82547gi: + case i82541gi2: + case i82541pi: + case i82545gmc: + case i82546gb: + case i82546eb: + break; + } + + /* cast for FS */ + mem = (void *)vmap(p->mem[0].bar & ~0x0F, p->mem[0].size); + if(mem == nil){ + print("igbe: can't map %8.8luX\n", p->mem[0].bar); + continue; + } + + /* + * from etherga620.c: + * If PCI Write-and-Invalidate is enabled set the max write DMA + * value to the host cache-line size (32 on Pentium or later). + */ + if(p->pcr & MemWrInv){ + cls = pcicfgr8(p, PciCLS) * 4; + if(cls != CACHELINESZ) + pcicfgw8(p, PciCLS, CACHELINESZ/4); + } + + cls = pcicfgr8(p, PciCLS); + switch(cls){ + default: + print("igbe: unexpected CLS - %d bytes\n", + cls*sizeof(long)); + break; + case 0x00: + case 0xFF: + /* alphapc 164lx returns 0 */ + print("igbe: unusable PciCLS: %d, using %d longs\n", + cls, CACHELINESZ/sizeof(long)); + cls = CACHELINESZ/sizeof(long); + pcicfgw8(p, PciCLS, cls); + break; + case 0x08: + case 0x10: + break; + } + ctlr = malloc(sizeof(Ctlr)); + if (ctlr == nil) + panic("ibgepci: no mem"); + ctlr->port = p->mem[0].bar & ~0x0F; + ctlr->pcidev = p; + ctlr->id = (p->did<<16)|p->vid; + ctlr->cls = cls*4; + ctlr->nic = mem; + + if(etherigbereset(ctlr)){ + free(ctlr); + continue; + } + pcisetbme(p); + + if(igbectlrhead != nil) + igbectlrtail->next = ctlr; + else + igbectlrhead = ctlr; + igbectlrtail = ctlr; + } +} + +int +igbepnp(Ether* edev) +{ + Ctlr *ctlr; + + if(igbectlrhead == nil) + igbepci(); + + /* + * Any adapter matches if no edev->port is supplied, + * otherwise the ports must match. + */ + for(ctlr = igbectlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + if(edev->port == 0 || edev->port == ctlr->port){ + ctlr->active = 1; + break; + } + } + if(ctlr == nil) + return -1; + + edev->ctlr = ctlr; + edev->port = ctlr->port; + edev->irq = ctlr->pcidev->intl; + edev->tbdf = ctlr->pcidev->tbdf; + edev->mbps = 1000; + memmove(edev->ea, ctlr->ra, Eaddrlen); + + /* + * Linkage to the generic ethernet driver. + */ + edev->attach = igbeattach; + edev->transmit = igbetransmit; + edev->interrupt = igbeinterrupt; +#ifndef FS + edev->ifstat = igbeifstat; + edev->ctl = igbectl; + + edev->arg = edev; + edev->promiscuous = igbepromiscuous; + edev->shutdown = igbeshutdown; + edev->multicast = igbemulticast; +#endif + return 0; +} + +#ifndef FS +void +etherigbebothlink(void) +{ + addethercard("i82543", igbepnp); + addethercard("igbe", igbepnp); +} +#endif diff -Nru /sys/src/fs/pc/etherm10g.c /sys/src/fs/pc/etherm10g.c --- /sys/src/fs/pc/etherm10g.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/pc/etherm10g.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,1465 @@ +/* + * myricom 10 Gb ethernet (file server driver) + * © 2007 erik quanstrom, coraid + */ +#include "all.h" +#include "io.h" +#include "../ip/ip.h" +#include "etherif.h" +#include "portfns.h" +#include "mem.h" + +#undef MB +#define K * 1024 +#define MB * 1024 K + +#define dprint(...) if(debug) print(__VA_ARGS__) +#define pcicapdbg(...) +#define malign(n) ialloc(n, 4 K) +#define PCIWADDR(x) PADDR(x)+0 + +extern ulong upamalloc(ulong, int, int); + +#include "etherm10g2k.i" +#include "etherm10g4k.i" + +static int debug = 0; +static char Etimeout[] = "timeout"; +static char Enomem[] = "no memory"; +static char Enonexist[] = "controller lost"; +static char Ebadarg[] = "bad argument"; + +enum { + Epromsz = 256, + Maxslots= 1024, + Align = 4096, + Maxmtu = 9000, + Noconf = 0xffffffff, + + Fwoffset= 1 MB, + Cmdoff = 0xf80000, /* command port offset */ + Fwsubmt = 0xfc0000, /* firmware submission command port offset */ + Rdmaoff = 0xfc01c0, /* rdma command port offset */ +}; + +enum { + CZero, + Creset, + Cversion, + + CSintrqdma, /* issue these before Cetherup */ + CSbigsz, /* in bytes bigsize = 2^n */ + CSsmallsz, + + CGsendoff, + CGsmallrxoff, + CGbigrxoff, + CGirqackoff, + CGirqdeassoff, + CGsendrgsz, + CGrxrgsz, + + CSintrqsz, /* 2^n */ + Cetherup, /* above parameters + mtu/mac addr must be set first. */ + Cetherdn, + + CSmtu, /* below may be issued live */ + CGcoaloff, /* in µs */ + CSstatsrate, /* in µs */ + CSstatsdma, + + Cpromisc, + Cnopromisc, + CSmac, + + Cenablefc, + Cdisablefc, + + Cdmatest, /* address in d[0-1], d[2]=length */ + + Cenableallmc, + Cdisableallmc, + + CSjoinmc, + CSleavemc, + Cleaveallmc, + + CSstatsdma2, /* adds (unused) multicast stats */ +}; + +typedef union { + uint i[2]; + uchar c[8]; +} Cmd; + +typedef struct { + u16int cksum; + u16int len; +} Slot; + +enum { + SFsmall = 1, + SFfirst = 2, + SFalign = 4, + SFnotso = 16, +}; + +typedef struct { + u32int high; + u32int low; + u16int hdroff; + u16int len; + uchar pad; + uchar nrdma; + uchar chkoff; + uchar flags; +} Send; + +typedef struct { + QLock; + Send *lanai; /* tx ring (cksum+len in lanai memory) */ + Send *host; /* tx ring (data in our memory) */ + Msgbuf **bring; +// uchar *wcfifo; /* what the heck is a w/c fifo? */ + int size; /* of buffers in the z8's memory */ + u32int segsz; + uint n; /* rxslots */ + uint m; /* mask; rxslots must be a power of two */ + uint i; /* number of segments (not frames) queued */ + uint cnt; /* number of segments sent by the card */ + + ulong npkt; + vlong nbytes; +} Tx; + +typedef struct { + Lock; + Msgbuf *head; + uint size; /* buffer size of each block */ + uint n; /* n free buffers */ + uint cnt; +} Bpool; + +Bpool smpool = { .size = 128, }; +Bpool bgpool = { .size = Maxmtu, }; + +typedef struct { + Bpool *pool; /* free buffers */ + u32int *lanai; /* rx ring; we have no permanent host shadow */ + Msgbuf **host; /* called "info" in myricom driver */ +// uchar *wcfifo; /* cmd submission fifo */ + uint m; + uint n; /* rxslots */ + uint i; + uint cnt; /* number of buffers allocated (lifetime) */ + uint allocfail; +} Rx; + +/* dma mapped. internet network byte order. */ +typedef struct { + uchar txcnt[4]; + uchar linkstat[4]; + uchar dlink[4]; + uchar derror[4]; + uchar drunt[4]; + uchar doverrun[4]; + uchar dnosm[4]; + uchar dnobg[4]; + uchar nrdma[4]; + uchar txstopped; + uchar down; + uchar updated; + uchar valid; +} Stats; + +enum { + Detached, + Attached, + Runed, +}; + +typedef struct { + Slot *entry; + uintptr busaddr; + uint m; + uint n; + uint i; +} Done; + +typedef struct Ctlr Ctlr; +typedef struct Ctlr { + QLock; + int state; + int kprocs; + uintptr port; + Pcidev* pcidev; + Ctlr* next; + int active; + int id; /* do we need this? */ + + uchar ra[Easize]; + + int ramsz; + uchar *ram; + + u32int *irqack; + u32int *irqdeass; + u32int *coal; + + char eprom[Epromsz]; + ulong serial; /* unit serial number */ + + QLock cmdl; + Cmd *cmd; /* address of command return */ + uintptr cprt; /* bus address of command */ + + uintptr boot; /* boot address */ + + Done done; + Tx tx; + Rx sm; + Rx bg; + Stats *stats; + uintptr statsprt; + + Rendez rxrendez; + Rendez txrendez; + + int msi; + u32int linkstat; + u32int nrdma; +} Ctlr; + +enum { + PciCapPMG = 0x01, /* power management */ + PciCapAGP = 0x02, + PciCapVPD = 0x03, /* vital product data */ + PciCapSID = 0x04, /* slot id */ + PciCapMSI = 0x05, + PciCapCHS = 0x06, /* compact pci hot swap */ + PciCapPCIX = 0x07, + PciCapHTC = 0x08, /* hypertransport irq conf */ + PciCapVND = 0x09, /* vendor specific information */ + PciCapHSW = 0x0C, /* hot swap */ + PciCapPCIe = 0x10, + PciCapMSIX = 0x11, +}; + +enum { + PcieAERC = 1, + PcieVC, + PcieSNC, + PciePBC, +}; + +enum { + AercCCR = 0x18, /* control register */ +}; + +enum { + PcieCTL = 8, + PcieLCR = 12, + PcieMRD = 0x7000, /* maximum read size */ +}; + +int +pcicap(Pcidev *p, int cap) +{ + int i, c, off; + + pcicapdbg("pcicap: %x:%d\n", p->vid, p->did); + off = 0x34; /* 0x14 for cardbus */ + for(i = 48; i--;){ + pcicapdbg("\t" "loop %x\n", off); + off = pcicfgr8(p, off); + pcicapdbg("\t" "pcicfgr8 %x\n", off); + if(off < 0x40) + break; + off &= ~3; + c = pcicfgr8(p, off); + pcicapdbg("\t" "pcicfgr8 %x\n", c); + if(c == 0xff) + break; + if(c == cap) + return off; + off++; + } + return 0; +} + +/* + * this function doesn't work because pcicgr32 doesn't have access + * to the pcie extended configuration space. + */ +int +pciecap(Pcidev *p, int cap) +{ + uint off, i; + + off = 0x100; + while(((i = pcicfgr32(p, off)) & 0xffff) != cap){ + off = i >> 20; + print("pciecap offset = %ud\n", off); + if(off < 0x100 || off >= 4 K - 1) + return 0; + } + print("pciecap found = %ud\n", off); + return off; +} + +static int +setpcie(Pcidev *p) +{ + int off; + + /* set 4k writes */ + off = pcicap(p, PciCapPCIe); + if(off < 64) + return -1; + off += PcieCTL; + pcicfgw16(p, off, (pcicfgr16(p, off) & ~PcieMRD) | 5<<12); + return 0; +} + +static int +whichfw(Pcidev *p) +{ + char *s; + int i, off, lanes, ecrc; + u32int cap; + + /* check the number of configured lanes. */ + off = pcicap(p, PciCapPCIe); + if(off < 64) + return -1; + off += PcieLCR; + cap = pcicfgr16(p, off); + lanes = (cap>>4)&0x3f; + + /* check AERC register. we need it on. */ + off = pciecap(p, PcieAERC); + print("%d offset\n", off); + cap = 0; + if(off != 0){ + off += AercCCR; + cap = pcicfgr32(p, off); + print("%ud cap\n", cap); + } + ecrc = (cap>>4)&0xf; + /* if we don't like the aerc, kick it here. */ + + print("m10g %d lanes; ecrc=%d; ", lanes, ecrc); + if(s = getconf("myriforce")){ + i = strtoul(s, 0, 0); + if(i != 4 K || i != 2 K) + i = 2 K; + print("fw=%d [forced]\n", i); + return i; + } + if(lanes <= 4){ + print("fw = 4096 [lanes]\n"); + return 4 K; + } + if(ecrc & 10){ + print("fw = 4096 [ecrc set]\n"); + return 4K; + } + print("fw = 4096 [default]\n"); + return 4 K; +} + +static int +parseeprom(Ctlr *c) +{ + int i, j, k, l, bits; + char *s; + + dprint("m10g eprom:\n"); + s = c->eprom; + bits = 3; + for(i = 0; s[i] && i < Epromsz; i++){ + l = strlen(s+i); + dprint("\t%s\n", s+i); + if(strncmp(s+i, "MAC=", 4) == 0 && l == 4+12+5){ + bits ^= 1; + j = i + 4; + for(k = 0; k < 6; k++) + c->ra[k] = strtoul(s+j+3*k, 0, 16); + } else if(strncmp(s+i, "SN=", 3) == 0){ + bits ^= 2; + c->serial = strtoul(s+i+3, 0, 0); + } + i += l; + } + if(bits) + return -1; + return 0; +} + +u16int +pbit16(u16int i) +{ + u16int j; + uchar *p; + + p = (uchar*)&j; + p[1] = i; + p[0] = i>>8; + return j; +} + +u16int +gbit16(uchar i[2]) +{ + u16int j; + + j = i[1]; + j |= i[0]<<8; + return j; +} + +u32int +pbit32(u32int i) +{ + u32int j; + uchar *p; + + p = (uchar*)&j; + p[3] = i; + p[2] = i>>8; + p[1] = i>>16; + p[0] = i>>24; + return j; +} + +static u32int +gbit32(uchar i[4]) +{ + u32int j; + + j = i[3]; + j |= i[2]<<8; + j |= i[1]<<16; + j |= i[0]<<24; + return j; +} + +static void +prepcmd(uint *cmd, int i) +{ + while(i-- > 0) + cmd[i] = pbit32(cmd[i]); +} + +/* + * the command looks like this (int 32bit integers) + * cmd type + * addr (low) + * addr (high) + * pad (used for dma testing) + * response (high) + * response (low) + * 40 byte = 5 int pad. + */ + +static Rendez cmdr; + +static int +return0(void *) +{ + return 0; +} + +u32int +cmd(Ctlr *c, int type, u32int data) +{ + u32int buf[16], i; + Cmd *cmd; + + qlock(&c->cmdl); + cmd = c->cmd; + cmd->i[1] = Noconf; + memset(buf, 0, sizeof buf); + buf[0] = type; + buf[1] = data; + buf[5] = c->cprt; + prepcmd(buf, 6); + coherence(); + memmove(c->ram+Cmdoff, buf, sizeof buf); + + for(i = 0; i < 15; i++){ + if(cmd->i[1] != Noconf){ + i = gbit32(cmd->c); + qunlock(&c->cmdl); + if(cmd->i[1] != 0) + dprint("[%ux]", i); + return i; + } + delay(1); + } + qunlock(&c->cmdl); + panic("m10g: cmd timeout [%ux %ux] cmd=%d\n", + cmd->i[0], cmd->i[1], type); + return ~0; /* silence! */ +} + +u32int +maccmd(Ctlr *c, int type, uchar *m) +{ + u32int buf[16], i; + Cmd *cmd; + + qlock(&c->cmdl); + cmd = c->cmd; + cmd->i[1] = Noconf; + memset(buf, 0, sizeof buf); + buf[0] = type; + buf[1] = m[0]<<24 | m[1]<<16 | m[2]<<8 | m[3]; + buf[2] = m[4]<< 8 | m[5]; + buf[5] = c->cprt; + prepcmd(buf, 6); + coherence(); + memmove(c->ram+Cmdoff, buf, sizeof buf); + + for(i = 0; i < 15; i++){ + if(cmd->i[1] != Noconf){ + i = gbit32(cmd->c); + qunlock(&c->cmdl); + if(cmd->i[1] != 0) + dprint("[%ux]", i); + return i; + } + delay(1); + } + qunlock(&c->cmdl); + print("m10g: maccmd timeout [%ux %ux] cmd=%d\n", + cmd->i[0], cmd->i[1], type); + panic(Etimeout); + return ~0; /* silence! */ +} + +/* remove this garbage after testing */ +enum { + DMAread = 0x10000, + DMAwrite= 0x1, +}; + +u32int +dmatestcmd(Ctlr *c, int type, u32int addr, int len) +{ + u32int buf[16], i; + + memset(buf, 0, sizeof buf); + memset(c->cmd, Noconf, sizeof *c->cmd); + buf[0] = Cdmatest; + buf[1] = addr; + buf[3] = len*type; + buf[5] = c->cprt; + prepcmd(buf, 6); + coherence(); + memmove(c->ram+Cmdoff, buf, sizeof buf); + + for(i = 0; i < 15; i++){ + if(c->cmd->i[1] != Noconf){ + i = gbit32(c->cmd->c); + if(i == 0) + return 0; + return i; + } + delay(5); + } + panic(Etimeout); + return ~0; /* silence! */ +} + +u32int +rdmacmd(Ctlr *c, int on) +{ + u32int buf[16], i; + + memset(buf, 0, sizeof buf); + c->cmd->i[0] = 0; + coherence(); + buf[1] = c->cprt; + buf[2] = Noconf; + buf[4] = c->cprt; + buf[5] = on; + prepcmd(buf, 6); + memmove(c->ram+Rdmaoff, buf, sizeof buf); + + for(i = 0; i < 20; i++){ + if(c->cmd->i[0] == Noconf) + return gbit32(c->cmd->c); + delay(1); + } + panic(Etimeout); + return ~0; /* silence! */ +} + +static int +loadfw(Ctlr *c, int *align) +{ + uint *f, *s, sz; + int i; + + if((*align = whichfw(c->pcidev)) == 4 K){ + f = (u32int*)fw4k; + sz = sizeof fw4k; + } else { + f = (u32int*)fw2k; + sz = sizeof fw2k; + } + + s = (u32int*)(c->ram + Fwoffset); + for(i = 0; i < sz / 4; i++) + s[i] = f[i]; + return sz & ~3; +} + +static int +bootfw(Ctlr *c) +{ + int i, sz, align; + uint buf[16]; + Cmd* cmd; + + if((sz = loadfw(c, &align)) == 0) + return 0; + dprint("bootfw %d bytes ... ", sz); + cmd = c->cmd; + + memset(buf, 0, sizeof buf); + c->cmd->i[0] = 0; + coherence(); + buf[0] = 0; /* upper 32 bits of dma target address */ + buf[1] = c->cprt; /* lower */ + buf[2] = Noconf; /* writeback */ + buf[3] = Fwoffset+8, + buf[4] = sz-8; + buf[5] = 8; + buf[6] = 0; + prepcmd(buf, 7); + coherence(); + memmove(c->ram + Fwsubmt, buf, sizeof buf); + + for(i = 0; i < 20; i++){ + if(cmd->i[0] == Noconf) + break; + delay(1); + } + dprint("[%ux %ux]", gbit32(cmd->c), gbit32(cmd->c+4)); + if(i == 20){ + print("m10g: cannot load fw\n"); + return -1; + } + dprint("\n"); + c->tx.segsz = align; + return 0; +} + +int +kickthebaby(Pcidev *p, Ctlr *c) +{ + /* don't kick the baby! */ + u32int code; + + pcicfgw8(p, 0x10+c->boot, 0x3); + pcicfgw32(p, 0x18+c->boot, 0xfffffff0); + code = pcicfgr32(p, 0x14+c->boot); + + dprint("reboot status = %ux\n", code); + if(code != 0xfffffff0) + return -1; + return 0; +} + +typedef struct { + uchar len[4]; + uchar type[4]; + char version[128]; + uchar globals[4]; + uchar ramsz[4]; + uchar specs[4]; + uchar specssz[4]; +} Fwhdr; + +enum { + Tmx = 0x4d582020, + Tpcie = 0x70636965, + Teth = 0x45544820, + Tmcp0 = 0x4d435030, +}; + +char* +fwtype(u32int type) +{ + switch(type){ + case Tmx: + return "mx"; + case Tpcie: + return "PCIe"; + case Teth: + return "eth"; + case Tmcp0: + return "mcp0"; + } + return "*GOK*"; +} + +int +chkfw(Ctlr *c) +{ + uintptr off; + Fwhdr *h; + u32int type; + + off = gbit32(c->ram+0x3c); + dprint("firmware %ulx\n", off); + if((off&3) || off + sizeof *h > c->ramsz){ + print("!m10g: bad firmware %ulx\n", off); + return -1; + } + h = (Fwhdr*)(c->ram + off); + type = gbit32(h->type); + dprint("\t" "type %s\n", fwtype(type)); + dprint("\t" "vers %s\n", h->version); + dprint("\t" "ramsz %ux\n", gbit32(h->ramsz)); + if(type != Teth){ + print("!m10g: bad card type %s\n", fwtype(type)); + return -1; + } + + return bootfw(c) || rdmacmd(c, 0); +} + +static int +reset(Ether *, Ctlr *c) +{ + u32int i, sz; + + chkfw(c); + cmd(c, Creset, 0); + + cmd(c, CSintrqsz, c->done.n * sizeof *c->done.entry); + cmd(c, CSintrqdma, c->done.busaddr); + c->irqack = (u32int*)(c->ram + cmd(c, CGirqackoff, 0)); + /* required only if we're not doing msi? */ + c->irqdeass = (u32int*)(c->ram + cmd(c, CGirqdeassoff, 0)); + /* this is the driver default, why fiddle with this? */ + c->coal = (u32int*)(c->ram + cmd(c, CGcoaloff, 0)); + *c->coal = pbit32(25); + + dprint("dma stats:\n"); + rdmacmd(c, 1); + sz = c->tx.segsz; + i = dmatestcmd(c, DMAread, c->done.busaddr, sz); + print("\t" "read: %ud MB/s\n", ((i>>16)*sz*2)/(i&0xffff)); + i = dmatestcmd(c, DMAwrite, c->done.busaddr, sz); + print("\t" "write: %ud MB/s\n", ((i>>16)*sz*2)/(i&0xffff)); + i = dmatestcmd(c, DMAwrite|DMAread, c->done.busaddr, sz); + print("\t" "r/w: %ud MB/s\n", ((i>>16)*sz*2*2)/(i&0xffff)); + memset(c->done.entry, 0, c->done.n * sizeof *c->done.entry); + + maccmd(c, CSmac, c->ra); +// cmd(c, Cnopromisc, 0); + cmd(c, Cenablefc, 0); + cmd(c, CSmtu, Maxmtu); + dprint("CSmtu %d...\n", Maxmtu); + + return 0; +} + +static int +setmem(Pcidev *p, Ctlr *c) +{ + u32int i, raddr; + Done *d; + void *mem; + + c->tx.segsz = 2048; + c->ramsz = 2 MB - (2*48 K + 32 K) - 0x100; + if(c->ramsz > p->mem[0].size) + return -1; + + raddr = p->mem[0].bar & ~0x0F; + mem = (void*)upamalloc(raddr, p->mem[0].size, 0); + if(mem == nil){ + print("m10g: can't map %8.8lux\n", p->mem[0].bar); + return -1; + } + dprint("%ux <- vmap(mem[0].size = %ux)\n", raddr, p->mem[0].size); + c->port = raddr; + c->ram = mem; + c->cmd = malign(sizeof *c->cmd); + c->cprt = PCIWADDR(c->cmd); + + d = &c->done; + d->n = Maxslots; + d->m = d->n - 1; + i = d->n * sizeof *d->entry; + d->entry = malign(i); + memset(d->entry, 0, i); + d->busaddr = PCIWADDR(d->entry); + + c->stats = malign(sizeof *c->stats); + memset(c->stats, 0, sizeof *c->stats); + c->statsprt = PCIWADDR(c->stats); + + memmove(c->eprom, c->ram + c->ramsz - Epromsz, Epromsz-2); + return setpcie(p) || parseeprom(c); +} + +static Rx* +whichrx(Ctlr *c, int sz) +{ + if(sz <= smpool.size) + return &c->sm; + return &c->bg; +} + +static Msgbuf* +m10balloc(Rx* rx) +{ + Msgbuf *m; + + ilock(rx->pool); + if((m = rx->pool->head) != nil){ + rx->pool->head = m->next; + m->next = nil; + rx->pool->n--; + } + iunlock(rx->pool); + return m; +} + +static void +smbfree(Msgbuf *m) +{ + Bpool *p; + + m->data = (uchar*)PGROUND((uintptr)m->xdata); + m->count = 0; + p = &smpool; + ilock(p); + m->next = p->head; + p->head = m; + p->n++; + p->cnt++; + iunlock(p); +} + +static void +bgbfree(Msgbuf *m) +{ + Bpool *p; + + m->data = (uchar*)PGROUND((uintptr)m->xdata); + m->count = 0; + p = &bgpool; + ilock(p); + m->next = p->head; + p->head = m; + p->n++; + p->cnt++; + iunlock(p); +} + +static void +replenish(Rx *rx) +{ + u32int buf[16], i, idx, e; + Bpool *p; + Msgbuf *m; + + p = rx->pool; + if(p->n < 8) + return; + memset(buf, 0, sizeof buf); + e = (rx->i - rx->cnt) & ~7; + e += rx->n; + while(p->n >= 8 && e){ + idx = rx->cnt & rx->m; + for(i = 0; i < 8; i++){ + m = m10balloc(rx); + buf[i*2+1] = pbit32(PCIWADDR(m->data)); + rx->host[idx+i] = m; + } + memmove(rx->lanai + 2*idx, buf, sizeof buf); + coherence(); + rx->cnt += 8; + e -= 8; + } + if(e && p->n > 7+1) + print("should panic? pool->n = %d\n", p->n); +} + +/* + * future: + * if (c->mtrr >= 0) { + * c->tx.wcfifo = c->ram+0x200000; + * c->sm.wcfifo = c->ram+0x300000; + * c->bg.wcfifo = c->ram+0x340000; + * } + */ + +static int +nextpow(int j) +{ + int i; + + for(i = 0; j > (1<tx.lanai; + c->tx.lanai = (Send*)(c->ram + cmd(c, CGsendoff, 0)); + c->tx.host = emalign(entries * sizeof *c->tx.host); + c->tx.bring = emalign(entries * sizeof *c->tx.bring); + c->tx.n = entries; + c->tx.m = entries-1; + + entries = cmd(c, CGrxrgsz, 0)/8; + c->sm.pool = &smpool; + cmd(c, CSsmallsz, c->sm.pool->size); + c->sm.lanai = (u32int*)(c->ram + cmd(c, CGsmallrxoff, 0)); + c->sm.n = entries; + c->sm.m = entries-1; + c->sm.host = emalign(entries * sizeof *c->sm.host); + + c->bg.pool = &bgpool; + c->bg.pool->size = nextpow(2 + Maxmtu); /* 2-byte alignment pad */ + cmd(c, CSbigsz, c->bg.pool->size); + c->bg.lanai = (u32int*)(c->ram + cmd(c, CGbigrxoff, 0)); + c->bg.n = entries; + c->bg.m = entries-1; + c->bg.host = emalign(entries * sizeof *c->bg.host); + + sz = c->sm.pool->size + BY2PG; + for(i = 0; i < c->sm.n; i++){ + m = mballoc(sz, 0, Mbeth10gbesm); + m->free = smbfree; + mbfree(m); + } + /* allocate our own buffers. leak the ones we're given. */ +// sz = c->bg.pool->size + BY2PG; + for(i = 0; i < c->bg.n; i++){ +// m = mballoc(sz, 0, Mbeth10gbebg); + m = mballoc( 1, 0, Mbeth10gbebg); + m->xdata = emalign(c->bg.pool->size); + m->free = bgbfree; + mbfree(m); + } + + cmd(c, CSstatsdma, c->statsprt); + c->linkstat = ~0; + c->nrdma = 15; + + cmd(c, Cetherup, 0); +} + +static Msgbuf* +nextbuf(Ctlr *c) +{ + uint i; + u16int l; + Done *d; + Msgbuf *m; + Rx *rx; + Slot *s; + + d = &c->done; + s = d->entry; + i = d->i & d->m; + l = s[i].len; + if(l == 0) + return nil; + *(u32int*)(s+i) = 0; + d->i++; + l = gbit16((uchar*)&l); + rx = whichrx(c, l); + if(rx->i >= rx->cnt){ + print("m10g: overrun\n"); + return nil; + } + i = rx->i & rx->m; + m = rx->host[i]; + rx->host[i] = 0; + if(m == 0){ + print("m10g: error rx to no block. memory is hosed.\n"); + return nil; + } + rx->i++; + m->data += 2; + m->count = l; + return m; +} + +static int +rxcansleep(void *v) +{ + Ctlr *c; + Slot *s; + Done *d; + + c = v; + d = &c->done; + s = c->done.entry; + if(s[d->i & d->m].len != 0) + return -1; + c->irqack[0] = pbit32(3); + return 0; +} + +static void +m10rx(void) +{ + Ether *e; + Ctlr *c; + Msgbuf *m; + + e = u->arg; + c = e->ctlr; + for(;;){ + replenish(&c->sm); + replenish(&c->bg); + sleep(&c->rxrendez, rxcansleep, c); + while(m = nextbuf(c)) + etheriq(e, m); + } +} + +void +txcleanup(Tx *tx, u32int n) +{ + Msgbuf *mb; + uint j, l, m; + + if(tx->npkt == n) + return; + l = 0; + m = tx->m; + /* if tx->cnt == tx->i, yet tx->npkt == n-1 we just */ + /* caught ourselves and myricom card updating. */ + for(;; tx->cnt++){ + j = tx->cnt & tx->m; + if(mb = tx->bring[j]){ + tx->bring[j] = 0; + tx->nbytes += mb->count; + mbfree(mb); + if(++tx->npkt == n) + return; + } + if(tx->cnt == tx->i) + return; + if(l++ == m){ + print("tx ovrun: %ud %uld\n", n, tx->npkt); + return; + } + } +} + +static int +txcansleep(void *v) +{ + Ctlr *c; + + c = v; + if(c->tx.cnt != c->tx.i && c->tx.npkt != gbit32(c->stats->txcnt)) + return -1; + return 0; +} + +void +txproc(void) +{ + Ether *e; + Ctlr *c; + Tx *tx; + + e = u->arg; + c = e->ctlr; + tx = &c->tx; + for(;;){ + sleep(&c->txrendez, txcansleep, c); + txcleanup(tx, gbit32(c->stats->txcnt)); + } +} + +void +submittx(Tx *tx, int n) +{ + Send *l, *h; + int i0, i, m; + + m = tx->m; + i0 = tx->i & m; + l = tx->lanai; + h = tx->host; + for(i = n-1; i >= 0; i--) + memmove(l+(i + i0 & m), h+(i + i0 & m), sizeof *h); + tx->i += n; +// coherence(); +} + +int +nsegments(Msgbuf *m, int segsz) +{ + uintptr bus, end, slen, len; + int i; + + bus = PCIWADDR(m->data); + i = 0; + for(len = m->count; len; len -= slen){ + end = bus + segsz & ~(segsz-1); + slen = end-bus; + if(slen > len) + slen = len; + bus += slen; + i++; + } + return i; +} + +static void +m10gtransmit(Ether *e) +{ + u16int slen; + u32int i, cnt, rdma, nseg, count, end, bus, len, segsz; + uchar flags; + Ctlr *c; + Msgbuf *m; + Send *s, *s0, *s0m8; + Tx *tx; + + c = e->ctlr; + tx = &c->tx; + segsz = tx->segsz; + + qlock(tx); + count = 0; + s = tx->host + (tx->i & tx->m); + cnt = tx->cnt; + s0 = tx->host + (cnt & tx->m); + s0m8 = tx->host + (cnt - 8 & tx->m); + i = tx->i; + for(; s >= s0 || s < s0m8; i += nseg){ + if((m = etheroq(e)) == nil) + break; + flags = SFfirst|SFnotso; + if((len = m->count) < 1520) + flags |= SFsmall; + rdma = nseg = nsegments(m, segsz); + bus = PCIWADDR(m->data); + for(; len; len -= slen){ + end = bus + segsz & ~(segsz-1); + slen = end-bus; + if(slen > len) + slen = len; + s->low = pbit32(bus); + s->len = pbit16(slen); + s->nrdma = rdma; + s->flags = flags; + + bus += slen; + if(++s == tx->host + tx->n) + s = tx->host; + count++; + flags &= ~SFfirst; + rdma = 1; + } + tx->bring[i + nseg - 1 & tx->m] = m; + submittx(tx, count); + count = 0; + cnt = tx->cnt; + s0 = tx->host + (cnt & tx->m); + s0m8 = tx->host + (cnt - 8 & tx->m); + } + qunlock(tx); +} + +static void +checkstats(Ether *, Ctlr *c, Stats *s) +{ + u32int i; + + if(s->updated == 0) + return; + + i = gbit32(s->linkstat); + if(c->linkstat != i) + if(c->linkstat = i) + dprint("m10g: link up\n"); + else + dprint("m10g: link down\n"); + i = gbit32(s->nrdma); + if(i != c->nrdma){ + dprint("m10g: rdma timeout %d\n", i); + c->nrdma = i; + } +} + +static void +waitintx(Ctlr *c) +{ + int i; + + for(i = 0; i < 1024*1024; i++){ + if(c->stats->valid == 0) + break; + coherence(); + } +} + +static void +m10ginterrupt(Ureg *, void *v) +{ + Ether *e; + Ctlr *c; + + e = v; + c = e->ctlr; + + if(c->state != Runed || c->stats->valid == 0) /* not ready for us? */ + return; + + if(c->stats->valid & 1) + wakeup(&c->rxrendez); + if(gbit32(c->stats->txcnt) != c->tx.npkt) + wakeup(&c->txrendez); + if(c->msi == 0) + *c->irqdeass = 0; + else + c->stats->valid = 0; + waitintx(c); + checkstats(e, c, c->stats); + c->irqack[1] = pbit32(3); +} + +static void +m10gattach(Ether *e) +{ + Ctlr *c; + char name[12]; + + dprint("m10gattach\n"); + + qlock(e->ctlr); + c = e->ctlr; + if(c->state != Detached){ + qunlock(c); + return; + } + reset(e, c); + c->state = Attached; + open0(e, c); + if(c->kprocs == 0){ + c->kprocs++; + snprint(name, sizeof name, "#l%drxproc", e->ctlrno); + userinit(m10rx, e, name); + snprint(name, sizeof name, "#l%dtxproc", e->ctlrno); + userinit(txproc, e, name); + } + c->state = Runed; + qunlock(c); +} + +static int +lstcount(Msgbuf *m) +{ + int i; + + i = 0; + for(; m; m = m->next) + i++; + return i; +} + +static char ifstatbuf[2 K]; + +static void +cifstat(Ctlr *c, int, char **) +{ + Stats s; + + /* no point in locking this because this is done via dma. */ + memmove(&s, c->stats, sizeof s); + snprint(ifstatbuf, sizeof ifstatbuf, + "txcnt = %ud\n" "linkstat = %ud\n" "dlink = %ud\n" + "derror = %ud\n" "drunt = %ud\n" "doverrun = %ud\n" + "dnosm = %ud\n" "dnobg = %ud\n" "nrdma = %ud\n" + "txstopped = %ud\n" "down = %ud\n" "updated = %ud\n" + "valid = %ud\n\n" + "tx pkt = %uld\n" "tx bytes = %lld\n" + "tx cnt = %ud\n" "tx n = %ud\n" "tx i = %ud\n" + "sm cnt = %ud\n" "sm i = %ud\n" "sm n = %ud\n" "sm lst = %ud\n" + "bg cnt = %ud\n" "bg i = %ud\n" "bg n = %ud\n" "bg lst = %ud\n" + "segsz = %ud\n" "coal = %d\n", + gbit32(s.txcnt), gbit32(s.linkstat), gbit32(s.dlink), + gbit32(s.derror), gbit32(s.drunt), gbit32(s.doverrun), + gbit32(s.dnosm), gbit32(s.dnobg), gbit32(s.nrdma), + s.txstopped, s.down, s.updated, s.valid, + c->tx.npkt, c->tx.nbytes, + c->tx.cnt, c->tx.n, c->tx.i, + c->sm.cnt, c->sm.i, c->sm.pool->n, lstcount(c->sm.pool->head), + c->bg.cnt, c->bg.i, c->bg.pool->n, lstcount(c->bg.pool->head), + c->tx.segsz, gbit32((uchar*)c->coal)); + print("%s", ifstatbuf); +} + +static void +cdebug(Ctlr *, int, char**) +{ + debug ^= 1; + print("debug %d\n", debug); +} + +static void +ccoal(Ctlr *c, int n, char **v) +{ + int i; + + if(n == 2){ + i = strtoul(*v, 0, 0); + *c->coal = pbit32(i); + coherence(); + } + print("%d\n", gbit32((uchar*)c->coal)); +} + +static void +chelp(Ctlr*, int, char **) +{ + print("coal ctlr n -- get/set interrupt colesing delay\n"); + print("debug -- toggle debug (all ctlrs)\n"); + print("ifstat ctlr -- print statistics\n"); +} + +static Ctlr ctlrs[3]; +static int nctlr; + +typedef struct { + void (*f)(Ctlr *, int, char**); + char* name; + int minarg; + int maxarg; +} Cmdtab; + +static void +docmd(Cmdtab *t, int n, int c, char **v) +{ + int i; + + for(i = 0; i < n; i++) + if(strcmp(*v, t[n].name) == 0) + break; + c--; + v++; + t += i; + if(i == n) + print("unknown subcommand\n"); + else if(c < t->minarg) + print("too few args, need %d\n", t->minarg); + else if(c > t->maxarg) + print("too many args, max %d\n", t->maxarg); + else { + i = 0; + if(t->minarg > 0){ + i = strtoul(*v++, 0, 0); + c--; + } + if(i < 0 || i == nctlr) + print("bad controller %d\n", i); + else + t->f(ctlrs+i, c, v); + } +} + +static Cmdtab ctab[] = { + cdebug, "debug", 0, 0, + ccoal, "coal", 1, 2, + cifstat,"ifstat", 1, 1, + chelp, "help", 0, 0, +}; + +static void +m10gctl(int c, char **v) +{ + docmd(ctab, nelem(ctab), c, v); +} + +static void +m10gpci(void) +{ + Pcidev *p; + Ctlr *c; + + for(p = 0; p = pcimatch(p, 0x14c1, 0x0008); ){ + c = ctlrs + nctlr; + memset(c, 0, sizeof *c); + c->pcidev = p; + c->id = p->did<<16 | p->vid; + c->boot = pcicap(p, PciCapVND); +// kickthebaby(p, c); + pcisetbme(p); + if(setmem(p, c) == -1){ + print("m10g failed\n"); + continue; + } + if(++nctlr == nelem(ctlrs)) + break; + } +} + +int +m10gpnp(Ether *e) +{ + Ctlr *c; + static int once, cmd; + + if(once++ == 0) + m10gpci(); + for(c = ctlrs; c < ctlrs + nctlr; c++) + if(c->active) + continue; + else if(e->port == 0 || e->port == c->port) + break; + if(c == ctlrs + nctlr) + return -1; + c->active = 1; + e->ctlr = c; + e->port = c->port; + e->irq = c->pcidev->intl; + e->tbdf = c->pcidev->tbdf; + e->mbps = 10000; + memmove(e->ea, c->ra, Easize); + + e->attach = m10gattach; + e->transmit = m10gtransmit; + e->interrupt = m10ginterrupt; + if(cmd++) + cmd_install("myrictl", "tweak myri parameters", m10gctl); + + return 0; +} diff -Nru /sys/src/fs/pc/etherm10g2k.i /sys/src/fs/pc/etherm10g2k.i --- /sys/src/fs/pc/etherm10g2k.i Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/pc/etherm10g2k.i Tue Nov 1 00:00:00 2011 @@ -0,0 +1 @@ +#include "../../9/pc/etherm10g2k.i" diff -Nru /sys/src/fs/pc/etherm10g4k.i /sys/src/fs/pc/etherm10g4k.i --- /sys/src/fs/pc/etherm10g4k.i Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/pc/etherm10g4k.i Tue Nov 1 00:00:00 2011 @@ -0,0 +1 @@ +#include "../../9/pc/etherm10g4k.i" diff -Nru /sys/src/fs/pc/ethermii.c /sys/src/fs/pc/ethermii.c --- /sys/src/fs/pc/ethermii.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/pc/ethermii.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,243 @@ +#ifdef FS +#include "all.h" +#include "io.h" +#include "mem.h" +#include "../ip/ip.h" +#else + +#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" +#endif /* FS */ + +#include "etherif.h" +#include "ethermii.h" + +#include "compat.h" + +int +mii(Mii* mii, int mask) +{ + MiiPhy *miiphy; + int bit, phyno, r, rmask; + + /* + * Probe through mii for PHYs in mask; + * return the mask of those found in the current probe. + * If the PHY has not already been probed, update + * the Mii information. + */ + rmask = 0; + for(phyno = 0; phyno < NMiiPhy; phyno++){ + bit = 1<mask & bit){ + rmask |= bit; + continue; + } + if(mii->mir(mii, phyno, Bmsr) == -1) + continue; + if((miiphy = malloc(sizeof(MiiPhy))) == nil) + continue; + + miiphy->mii = mii; + r = mii->mir(mii, phyno, Phyidr1); + miiphy->oui = (r & 0x3FFF)<<6; + r = mii->mir(mii, phyno, Phyidr2); + miiphy->oui |= r>>10; + miiphy->phyno = phyno; + + miiphy->anar = ~0; + miiphy->fc = ~0; + miiphy->mscr = ~0; + + mii->phy[phyno] = miiphy; + if(mii->curphy == nil) + mii->curphy = miiphy; + mii->mask |= bit; + mii->nphy++; + + rmask |= bit; + } + return rmask; +} + +int +miimir(Mii* mii, int r) +{ + if(mii == nil || mii->ctlr == nil || mii->curphy == nil) + return -1; + return mii->mir(mii, mii->curphy->phyno, r); +} + +int +miimiw(Mii* mii, int r, int data) +{ + if(mii == nil || mii->ctlr == nil || mii->curphy == nil) + return -1; + return mii->miw(mii, mii->curphy->phyno, r, data); +} + +int +miireset(Mii* mii) +{ + int bmcr; + + if(mii == nil || mii->ctlr == nil || mii->curphy == nil) + return -1; + bmcr = mii->mir(mii, mii->curphy->phyno, Bmcr); + bmcr |= BmcrR; + mii->miw(mii, mii->curphy->phyno, Bmcr, bmcr); + microdelay(1); + + return 0; +} + +int +miiane(Mii* mii, int a, int p, int e) +{ + int anar, bmsr, mscr, r, phyno; + + if(mii == nil || mii->ctlr == nil || mii->curphy == nil) + return -1; + phyno = mii->curphy->phyno; + + bmsr = mii->mir(mii, phyno, Bmsr); + if(!(bmsr & BmsrAna)) + return -1; + + if(a != ~0) + anar = (AnaTXFD|AnaTXHD|Ana10FD|Ana10HD) & a; + else if(mii->curphy->anar != ~0) + anar = mii->curphy->anar; + else{ + anar = mii->mir(mii, phyno, Anar); + anar &= ~(AnaAP|AnaP|AnaT4|AnaTXFD|AnaTXHD|Ana10FD|Ana10HD); + if(bmsr & Bmsr10THD) + anar |= Ana10HD; + if(bmsr & Bmsr10TFD) + anar |= Ana10FD; + if(bmsr & Bmsr100TXHD) + anar |= AnaTXHD; + if(bmsr & Bmsr100TXFD) + anar |= AnaTXFD; + } + mii->curphy->anar = anar; + + if(p != ~0) + anar |= (AnaAP|AnaP) & p; + else if(mii->curphy->fc != ~0) + anar |= mii->curphy->fc; + mii->curphy->fc = (AnaAP|AnaP) & anar; + + if(bmsr & BmsrEs){ + mscr = mii->mir(mii, phyno, Mscr); + mscr &= ~(Mscr1000TFD|Mscr1000THD); + if(e != ~0) + mscr |= (Mscr1000TFD|Mscr1000THD) & e; + else if(mii->curphy->mscr != ~0) + mscr = mii->curphy->mscr; + else{ + r = mii->mir(mii, phyno, Esr); + if(r & Esr1000THD) + mscr |= Mscr1000THD; + if(r & Esr1000TFD) + mscr |= Mscr1000TFD; + } + mii->curphy->mscr = mscr; + mii->miw(mii, phyno, Mscr, mscr); + } + mii->miw(mii, phyno, Anar, anar); + + r = mii->mir(mii, phyno, Bmcr); + if(!(r & BmcrR)){ + r |= BmcrAne|BmcrRan; + mii->miw(mii, phyno, Bmcr, r); + } + + return 0; +} + +int +miistatus(Mii* mii) +{ + MiiPhy *phy; + int anlpar, bmsr, p, r, phyno; + + if(mii == nil || mii->ctlr == nil || mii->curphy == nil) + return -1; + phy = mii->curphy; + phyno = phy->phyno; + + /* + * Check Auto-Negotiation is complete and link is up. + * (Read status twice as the Ls bit is sticky). + */ + bmsr = mii->mir(mii, phyno, Bmsr); + if(!(bmsr & (BmsrAnc|BmsrAna))) +{ +//print("miistatus 1\n"); + return -1; +} + + bmsr = mii->mir(mii, phyno, Bmsr); + if(!(bmsr & BmsrLs)){ +//print("miistatus 2\n"); + phy->link = 0; + return -1; + } + + phy->speed = phy->fd = phy->rfc = phy->tfc = 0; + if(phy->mscr){ + r = mii->mir(mii, phyno, Mssr); + if((phy->mscr & Mscr1000TFD) && (r & Mssr1000TFD)){ + phy->speed = 1000; + phy->fd = 1; + } + else if((phy->mscr & Mscr1000THD) && (r & Mssr1000THD)) + phy->speed = 1000; + } + + anlpar = mii->mir(mii, phyno, Anlpar); + if(phy->speed == 0){ + r = phy->anar & anlpar; + if(r & AnaTXFD){ + phy->speed = 100; + phy->fd = 1; + } + else if(r & AnaTXHD) + phy->speed = 100; + else if(r & Ana10FD){ + phy->speed = 10; + phy->fd = 1; + } + else if(r & Ana10HD) + phy->speed = 10; + } + if(phy->speed == 0) +{ +//print("miistatus 3\n"); + return -1; +} + + if(phy->fd){ + p = phy->fc; + r = anlpar & (AnaAP|AnaP); + if(p == AnaAP && r == (AnaAP|AnaP)) + phy->tfc = 1; + else if(p == (AnaAP|AnaP) && r == AnaAP) + phy->rfc = 1; + else if((p & AnaP) && (r & AnaP)) + phy->rfc = phy->tfc = 1; + } + + phy->link = 1; + + return 0; +} diff -Nru /sys/src/fs/pc/ethermii.h /sys/src/fs/pc/ethermii.h --- /sys/src/fs/pc/ethermii.h Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/pc/ethermii.h Tue Nov 1 00:00:00 2011 @@ -0,0 +1,116 @@ +typedef struct Mii Mii; +typedef struct MiiPhy MiiPhy; + +enum { /* registers */ + Bmcr = 0x00, /* Basic Mode Control */ + Bmsr = 0x01, /* Basic Mode Status */ + Phyidr1 = 0x02, /* PHY Identifier #1 */ + Phyidr2 = 0x03, /* PHY Identifier #2 */ + Anar = 0x04, /* Auto-Negotiation Advertisement */ + Anlpar = 0x05, /* AN Link Partner Ability */ + Aner = 0x06, /* AN Expansion */ + Annptr = 0x07, /* AN Next Page TX */ + Annprr = 0x08, /* AN Next Page RX */ + Mscr = 0x09, /* MASTER-SLAVE Control */ + Mssr = 0x0A, /* MASTER-SLAVE Status */ + Esr = 0x0F, /* Extended Status */ + + NMiiPhyr = 32, + NMiiPhy = 32, +}; + +enum { /* Bmcr */ + BmcrSs1 = 0x0040, /* Speed Select[1] */ + BmcrCte = 0x0080, /* Collision Test Enable */ + BmcrDm = 0x0100, /* Duplex Mode */ + BmcrRan = 0x0200, /* Restart Auto-Negotiation */ + BmcrI = 0x0400, /* Isolate */ + BmcrPd = 0x0800, /* Power Down */ + BmcrAne = 0x1000, /* Auto-Negotiation Enable */ + BmcrSs0 = 0x2000, /* Speed Select[0] */ + BmcrLe = 0x4000, /* Loopback Enable */ + BmcrR = 0x8000, /* Reset */ +}; + +enum { /* Bmsr */ + BmsrEc = 0x0001, /* Extended Capability */ + BmsrJd = 0x0002, /* Jabber Detect */ + BmsrLs = 0x0004, /* Link Status */ + BmsrAna = 0x0008, /* Auto-Negotiation Ability */ + BmsrRf = 0x0010, /* Remote Fault */ + BmsrAnc = 0x0020, /* Auto-Negotiation Complete */ + BmsrPs = 0x0040, /* Preamble Suppression Capable */ + BmsrEs = 0x0100, /* Extended Status */ + Bmsr100T2HD = 0x0200, /* 100BASE-T2 HD Capable */ + Bmsr100T2FD = 0x0400, /* 100BASE-T2 FD Capable */ + Bmsr10THD = 0x0800, /* 100BASE-T HD Capable */ + Bmsr10TFD = 0x1000, /* 10BASE-T FD Capable */ + Bmsr100TXHD = 0x2000, /* 100BASE-TX HD Capable */ + Bmsr100TXFD = 0x4000, /* 100BASE-TX FD Capable */ + Bmsr100T4 = 0x8000, /* 100BASE-T4 Capable */ +}; + +enum { /* Anar/Anlpar */ + Ana10HD = 0x0020, /* Advertise 10BASE-T */ + Ana10FD = 0x0040, /* Advertise 10BASE-T FD */ + AnaTXHD = 0x0080, /* Advertise 100BASE-TX */ + AnaTXFD = 0x0100, /* Advertise 100BASE-TX FD */ + AnaT4 = 0x0200, /* Advertise 100BASE-T4 */ + AnaP = 0x0400, /* Pause */ + AnaAP = 0x0800, /* Asymmetrical Pause */ + AnaRf = 0x2000, /* Remote Fault */ + AnaAck = 0x4000, /* Acknowledge */ + AnaNp = 0x8000, /* Next Page Indication */ +}; + +enum { /* Mscr */ + Mscr1000THD = 0x0100, /* Advertise 1000BASE-T HD */ + Mscr1000TFD = 0x0200, /* Advertise 1000BASE-T FD */ +}; + +enum { /* Mssr */ + Mssr1000THD = 0x0400, /* Link Partner 1000BASE-T HD able */ + Mssr1000TFD = 0x0800, /* Link Partner 1000BASE-T FD able */ +}; + +enum { /* Esr */ + Esr1000THD = 0x1000, /* 1000BASE-T HD Capable */ + Esr1000TFD = 0x2000, /* 1000BASE-T FD Capable */ + Esr1000XHD = 0x4000, /* 1000BASE-X HD Capable */ + Esr1000XFD = 0x8000, /* 1000BASE-X FD Capable */ +}; + +typedef struct Mii { + Lock; + int nphy; + int mask; + MiiPhy* phy[NMiiPhy]; + MiiPhy* curphy; + + void* ctlr; + int (*mir)(Mii*, int, int); + int (*miw)(Mii*, int, int, int); +} Mii; + +typedef struct MiiPhy { + Mii* mii; + int oui; + int phyno; + + int anar; + int fc; + int mscr; + + int link; + int speed; + int fd; + int rfc; + int tfc; +}; + +extern int mii(Mii*, int); +extern int miiane(Mii*, int, int, int); +extern int miimir(Mii*, int); +extern int miimiw(Mii*, int, int); +extern int miireset(Mii*); +extern int miistatus(Mii*); diff -Nru /sys/src/fs/pc/floppy.c /sys/src/fs/pc/floppy.c --- /sys/src/fs/pc/floppy.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/pc/floppy.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,771 @@ +/* + * Papa's got a brand new bag on the side. + */ +#include "all.h" +#include "io.h" +#include "mem.h" + +#define DPRINT if(0)print + +typedef struct Floppy Floppy; +typedef struct Ctlr Ctlr; +typedef struct Type Type; + +enum +{ + Pdor= 0x3f2, /* motor port */ + Fintena= 0x8, /* enable floppy interrupt */ + Fena= 0x4, /* 0 == reset controller */ + + Pmsr= 0x3f4, /* controller main status port */ + Fready= 0x80, /* ready to be touched */ + Ffrom= 0x40, /* data from controller */ + Fbusy= 0x10, /* operation not over */ + + Pdata= 0x3f5, /* controller data port */ + Frecal= 0x7, /* recalibrate cmd */ + Fseek= 0xf, /* seek cmd */ + Fsense= 0x8, /* sense cmd */ + Fread= 0x66, /* read cmd */ + Fwrite= 0x45, /* write cmd */ + Fmulti= 0x80, /* or'd with Fread or Fwrite for multi-head */ + + /* digital input register */ + Pdir= 0x3F7, /* disk changed port (read only) */ + Pdsr= 0x3F7, /* data rate select port (write only) */ + Fchange= 0x80, /* disk has changed */ + + DMAmode0= 0xb, + DMAmode1= 0xc, + DMAaddr= 0x4, + DMAtop= 0x81, + DMAinit= 0xa, + DMAcount= 0x5, + + Maxfloppy= 4, /* floppies/controller */ + + /* sector size encodings */ + S128= 0, + S256= 1, + S512= 2, + S1024= 3, + + /* status 0 byte */ + Floppymask= 3<<0, + Seekend= 1<<5, + Codemask= (3<<6)|(3<<3), + Cmdexec= 1<<6, + + /* status 1 byte */ + Overrun= 0x10, +}; + +#define MOTORBIT(i) (1<<((i)+4)) + +/* + * types of drive (from PC equipment byte) + */ +enum +{ + T360kb= 1, + T1200kb= 2, + T720kb= 3, + T1440kb= 4, +}; + +/* + * floppy types (all MFM encoding) + */ +struct Type +{ + char *name; + int dt; /* compatible drive type */ + int bytes; /* bytes/sector */ + int sectors; /* sectors/track */ + int heads; /* number of heads */ + int steps; /* steps per cylinder */ + int tracks; /* tracks/disk */ + int gpl; /* intersector gap length for read/write */ + int fgpl; /* intersector gap length for format */ + int rate; /* rate code */ + + /* + * these depend on previous entries and are set filled in + * by floppyinit + */ + int bcode; /* coded version of bytes for the controller */ + long cap; /* drive capacity in bytes */ + long tsize; /* track size in bytes */ +}; +Type floppytype[] = +{ + { "3½HD", T1440kb, 512, 18, 2, 1, 80, 0x1B, 0x54, 0, }, + { "3½DD", T1440kb, 512, 9, 2, 1, 80, 0x1B, 0x54, 2, }, + { "3½DD", T720kb, 512, 9, 2, 1, 80, 0x1B, 0x54, 2, }, + { "5¼HD", T1200kb, 512, 15, 2, 1, 80, 0x2A, 0x50, 0, }, + { "5¼DD", T1200kb, 512, 9, 2, 2, 40, 0x2A, 0x50, 1, }, + { "5¼DD", T360kb, 512, 9, 2, 1, 40, 0x2A, 0x50, 2, }, +}; +#define NTYPES (sizeof(floppytype)/sizeof(Type)) + +/* + * bytes per sector encoding for the controller. + * - index for b2c is is (bytes per sector/128). + * - index for c2b is code from b2c + */ +static int b2c[] = +{ +[1] 0, +[2] 1, +[4] 2, +[8] 3, +}; +static int c2b[] = +{ + 128, + 256, + 512, + 1024, +}; + +/* + * a floppy drive + */ +struct Floppy +{ + Type *t; + int dt; + int dev; + + Timet lasttouched; /* time last touched */ + int cyl; /* current cylinder */ + int confused; /* needs to be recalibrated (or worse) */ + long offset; /* current offset */ + + int tcyl; /* target cylinder */ + int thead; /* target head */ + int tsec; /* target sector */ + long len; + int maxtries; +}; + +/* + * NEC PD765A controller for 4 floppys + */ +struct Ctlr +{ + QLock; + Rendez; + + Floppy d[Maxfloppy]; /* the floppy drives */ + int rw; /* true if a read or write in progress */ + int seek; /* one bit for each seek in progress */ + uchar stat[8]; /* status of an operation */ + int intr; + int confused; + int motor; + Floppy *selected; + int rate; + + int cdev; + uchar *ccache; /* cyclinder cache */ + int ccyl; + int chead; +}; + +Ctlr fl; + +static int floppysend(int); +static int floppyrcv(void); +static int floppyrdstat(int); +static void floppypos(Floppy*, long); +static void floppywait(char*); +static int floppysense(Floppy*); +static int floppyrecal(Floppy*); +static void floppyon(Floppy*); +static long floppyxfer(Floppy*, int, void*, long); +static void floppyrevive(void); + +static void +timedsleep(int ms) +{ + ulong end; + + end = MACHP(0)->ticks + 1 + MS2TK(ms); + while(MACHP(0)->ticks < end) + ; +} + +/* + * set floppy drive to its default type + */ +static void +setdef(Floppy *dp) +{ + Type *t; + + for(t = floppytype; t < &floppytype[NTYPES]; t++) + if(dp->dt == t->dt){ + dp->t = t; + break; + } +} + +static void +floppyintr(Ureg *ur, void *arg) +{ + USED(ur, arg); + fl.intr = 1; +} + +static void +floppystop(Floppy *dp) +{ + fl.motor &= ~MOTORBIT(dp->dev); + outb(Pdor, fl.motor | Fintena | Fena | dp->dev); +} + +void +floppyhalt(void) +{ + Floppy *dp; + + for(dp = fl.d; dp < &fl.d[Maxfloppy]; dp++) + if((fl.motor&MOTORBIT(dp->dev)) && canqlock(&fl)){ + floppystop(dp); + qunlock(&fl); + } +} + +static void +floppyalarm(Alarm* a, void *arg) +{ + USED(arg); + cancel(a); + wakeup(&fl); +} + +void +floppyproc(void) +{ + Floppy *dp; + + for(;;){ + for(dp = fl.d; dp < &fl.d[Maxfloppy]; dp++){ + if((fl.motor&MOTORBIT(dp->dev)) + && TK2SEC(MACHP(0)->ticks - dp->lasttouched) > 5 + && canqlock(&fl)){ + if(TK2SEC(MACHP(0)->ticks - dp->lasttouched) > 5) + floppystop(dp); + qunlock(&fl); + } + } + alarm(5*1000, floppyalarm, 0); + sleep(&fl, no, 0); + } + +} + +int +floppyinit(void) +{ + Floppy *dp; + uchar equip; + int nfloppy = 0; + Type *t; + + setvec(Floppyvec, floppyintr, &fl); + + delay(10); + outb(Pdor, 0); + delay(1); + outb(Pdor, Fintena | Fena); + delay(10); + + /* + * init dependent parameters + */ + for(t = floppytype; t < &floppytype[NTYPES]; t++){ + t->cap = t->bytes * t->heads * t->sectors * t->tracks; + t->bcode = b2c[t->bytes/128]; + t->tsize = t->bytes * t->sectors; + } + + /* + * init drive parameters + */ + for(dp = fl.d; dp < &fl.d[Maxfloppy]; dp++){ + dp->cyl = -1; + dp->dev = dp - fl.d; + dp->maxtries = 1; + } + + /* + * read nvram for types of floppies 0 & 1 + */ + equip = nvramread(0x10); + if(Maxfloppy > 0){ + fl.d[0].dt = (equip >> 4) & 0xf; + setdef(&fl.d[0]); + nfloppy++; + } + if(Maxfloppy > 1){ + fl.d[1].dt = equip & 0xf; + setdef(&fl.d[1]); + nfloppy++; + } + + fl.rate = -1; + fl.motor = 0; + fl.confused = 1; + fl.ccyl = -1; + fl.chead = -1; + fl.cdev = -1; + fl.ccache = (uchar*)ialloc(18*2*512, 64*1024); + if(DMAOK(fl.ccache, 18*2*512) == 0) + panic("floppy: no memory < 16Mb\n"); + + return nfloppy; +} + +static void +floppyon(Floppy *dp) +{ + int alreadyon; + int tries; + + if(fl.confused) + floppyrevive(); + + dp->lasttouched = MACHP(0)->ticks; + alreadyon = fl.motor & MOTORBIT(dp->dev); + fl.motor |= MOTORBIT(dp->dev); + outb(Pdor, fl.motor | Fintena | Fena | dp->dev); + + /* get motor going */ + if(!alreadyon) + timedsleep(750); + + /* set transfer rate */ + if(fl.rate != dp->t->rate){ + fl.rate = dp->t->rate; + outb(Pdsr, fl.rate); + } + + /* get drive to a known cylinder */ + if(dp->confused) + for(tries = 0; tries < 4; tries++) + if(floppyrecal(dp) >= 0) + break; + + dp->lasttouched = MACHP(0)->ticks; + fl.selected = dp; +} + +static void +floppyrevive(void) +{ + Floppy *dp; + + /* + * reset the controller if it's confused + */ + if(fl.confused){ + /* reset controller and turn all motors off */ + fl.intr = 0; + splhi(); + outb(Pdor, 0); + delay(1); + outb(Pdor, Fintena|Fena); + spllo(); + for(dp = fl.d; dp < &fl.d[Maxfloppy]; dp++) + dp->confused = 1; + fl.motor = 0; + floppywait("revive"); + fl.confused = 0; + outb(Pdsr, 0); + } +} + +static int +floppysend(int data) +{ + int tries; + + for(tries = 0; tries < 1000; tries++){ + if((inb(Pmsr)&(Ffrom|Fready)) == Fready){ + outb(Pdata, data); + return 0; + } + microdelay(8); + } + return -1; +} + +static int +floppyrcv(void) +{ + int tries; + uchar c; + + for(tries = 0; tries < 1000; tries++){ + if((inb(Pmsr)&(Ffrom|Fready)) == (Ffrom|Fready)) + return inb(Pdata)&0xff; + microdelay(8); + } + DPRINT("floppyrcv returns -1 status = %ux\n", c); + return -1; +} + +static int +floppyrdstat(int n) +{ + int i; + int c; + + for(i = 0; i < n; i++){ + c = floppyrcv(); + if(c < 0) + return -1; + fl.stat[i] = c; + } + return 0; +} + +static void +floppypos(Floppy *dp, long off) +{ + int lsec; + int cyl; + + lsec = off/dp->t->bytes; + dp->tcyl = lsec/(dp->t->sectors*dp->t->heads); + dp->tsec = (lsec % dp->t->sectors) + 1; + dp->thead = (lsec/dp->t->sectors) % dp->t->heads; + + /* + * can't read across cylinder boundaries. + * if so, decrement the bytes to be read. + */ + lsec = (off+dp->len)/dp->t->bytes; + cyl = lsec/(dp->t->sectors*dp->t->heads); + if(cyl != dp->tcyl){ + dp->len -= (lsec % dp->t->sectors)*dp->t->bytes; + dp->len -= ((lsec/dp->t->sectors) % dp->t->heads)*dp->t->bytes + *dp->t->sectors; + } + + dp->lasttouched = MACHP(0)->ticks; + fl.intr = 0; +} + +static void +floppywait(char *cmd) +{ + ulong end; + + end = MACHP(0)->ticks + 1 + MS2TK(750); + while(MACHP(0)->ticks < end && fl.intr == 0) + ; + if(m->ticks > end) + DPRINT("floppy timed out, cmd=%s\n", cmd); + fl.intr = 0; +} + +static int +floppysense(Floppy *dp) +{ + /* + * ask for floppy status + */ + if(floppysend(Fsense) < 0){ + fl.confused = 1; + return -1; + } + if(floppyrdstat(2) < 0){ + fl.confused = 1; + dp->confused = 1; + return -1; + } + + /* + * make sure it's the right drive + */ + if((fl.stat[0] & Floppymask) != dp->dev){ + DPRINT("sense failed, %ux %ux\n", fl.stat[0], fl.stat[1]); + dp->confused = 1; + return -1; + } + return 0; +} + +static int +floppyrecal(Floppy *dp) +{ + fl.intr = 0; + if(floppysend(Frecal) < 0 + || floppysend(dp - fl.d) < 0){ + DPRINT("recalibrate rejected\n"); + fl.confused = 0; + return -1; + } + floppywait("recal"); + + /* + * get return values + */ + if(floppysense(dp) < 0) + return -1; + + /* + * see what cylinder we got to + */ + dp->tcyl = 0; + dp->cyl = fl.stat[1]/dp->t->steps; + if(dp->cyl != dp->tcyl){ + DPRINT("recalibrate went to wrong cylinder %d\n", dp->cyl); + dp->confused = 1; + return -1; + } + + dp->confused = 0; + return 0; +} + +Devsize +floppyseek(int dev, Devsize off) +{ + Floppy *dp; + + dp = &fl.d[dev]; + floppyon(dp); + floppypos(dp, off); + if(dp->cyl == dp->tcyl){ + dp->offset = off; + return off; + } + + /* + * tell floppy to seek + */ + if(floppysend(Fseek) < 0 + || floppysend((dp->thead<<2) | dp->dev) < 0 + || floppysend(dp->tcyl * dp->t->steps) < 0){ + DPRINT("seek cmd failed\n"); + fl.confused = 1; + return -1; + } + + /* + * wait for interrupt + */ + floppywait("seek"); + + /* + * get floppy status + */ + if(floppysense(dp) < 0) + return -1; + + /* + * see if it worked + */ + if((fl.stat[0] & (Codemask|Seekend)) != Seekend){ + DPRINT("seek failed\n"); + dp->confused = 1; + return -1; + } + + /* + * see what cylinder we got to + */ + dp->cyl = fl.stat[1]/dp->t->steps; + if(dp->cyl != dp->tcyl){ + DPRINT("seek went to wrong cylinder %d instead of %d\n", + dp->cyl, dp->tcyl); + dp->confused = 1; + return -1; + } + + dp->offset = off; + DPRINT("seek to %ld succeeded\n", dp->offset); + return dp->offset; +} + +static long +floppyxfer(Floppy *dp, int cmd, void *a, long n) +{ + ulong addr; + long offset; + + addr = (ulong)a; + + /* + * dma can't cross 64 k boundaries + */ + if((addr & 0xffff0000) != ((addr+n) & 0xffff0000)) + n -= (addr+n)&0xffff; + + dp->len = n; + if(floppyseek(dp->dev, dp->offset) < 0){ + DPRINT("xfer seek failed\n"); + return -1; + } + + DPRINT("floppy %d tcyl %d, thead %d, tsec %d, addr %lux, n %ld\n", + dp->dev, dp->tcyl, dp->thead, dp->tsec, addr, n);/**/ + + /* + * set up the dma + */ + outb(DMAmode1, cmd==Fread ? 0x46 : 0x4a); + outb(DMAmode0, cmd==Fread ? 0x46 : 0x4a); + outb(DMAaddr, addr); + outb(DMAaddr, addr>>8); + outb(DMAtop, addr>>16); + outb(DMAcount, n-1); + outb(DMAcount, (n-1)>>8); + outb(DMAinit, 2); + + /* + * tell floppy to go + */ + cmd = cmd | (dp->t->heads > 1 ? Fmulti : 0); + if(floppysend(cmd) < 0 + || floppysend((dp->thead<<2) | dp->dev) < 0 + || floppysend(dp->tcyl * dp->t->steps) < 0 + || floppysend(dp->thead) < 0 + || floppysend(dp->tsec) < 0 + || floppysend(dp->t->bcode) < 0 + || floppysend(dp->t->sectors) < 0 + || floppysend(dp->t->gpl) < 0 + || floppysend(0xFF) < 0){ + DPRINT("xfer cmd failed\n"); + fl.confused = 1; + return -1; + } + + floppywait("xfer"); + + /* + * get status + */ + if(floppyrdstat(7) < 0){ + DPRINT("xfer status failed\n"); + fl.confused = 1; + return -1; + } + + if((fl.stat[0] & Codemask)!=0 || fl.stat[1] || fl.stat[2]){ + DPRINT("xfer failed %ux %ux %ux\n", fl.stat[0], + fl.stat[1], fl.stat[2]); + if((fl.stat[0]&Codemask)==Cmdexec && fl.stat[1]==Overrun){ + DPRINT("DMA overrun: retry\n"); + return 0; + } + dp->confused = 1; + return -1; + } + + offset = (fl.stat[3]/dp->t->steps) * dp->t->heads + fl.stat[4]; + offset = offset*dp->t->sectors + fl.stat[5] - 1; + offset = offset * c2b[fl.stat[6]]; + if(offset != dp->offset+n){ + DPRINT("new offset %ld instead of %ld\n", offset, dp->offset+dp->len); + dp->confused = 1; + return -1;/**/ + } + dp->offset += dp->len; + return dp->len; +} + +Off +floppyread(int dev, void *a, long n) +{ + Floppy *dp; + int tries; + Off rv, i, nn, offset, sec; + uchar *aa; + + dp = &fl.d[dev]; + + dp->len = n; + qlock(&fl); + floppypos(dp, dp->offset); + offset = dp->offset; + sec = dp->tsec + (Off)dp->t->sectors*(Off)dp->thead; + n = dp->len; + if(fl.ccyl==dp->tcyl && fl.cdev==dev) + goto out; + + fl.ccyl = -1; + fl.cdev = dev; + aa = fl.ccache; + nn = (Off)dp->t->bytes * (Off)dp->t->sectors * (Off)dp->t->heads; + dp->offset = dp->tcyl * nn; + for(rv = 0; rv < nn; rv += i){ + i = 0; + for(tries = 0; tries < dp->maxtries; tries++){ + i = floppyxfer(dp, Fread, aa+rv, nn-rv); + if(i > 0) + break; + } + if(tries == dp->maxtries) + break; + } + if(rv != nn){ + dp->confused = 1; + return -1; // ?!!? no qunlock(&fl)? no "goto out"? + } + fl.ccyl = dp->tcyl; +out: + memmove(a, fl.ccache + dp->t->bytes*(sec-1), n); + dp->offset = offset + n; + dp->maxtries = 3; + + qunlock(&fl); + return n; +} + +Off +floppywrite(int dev, void *a, long n) +{ + Floppy *dp; + int tries; + Off rv, i, offset; + uchar *aa; + + dp = &fl.d[dev]; + + qlock(&fl); + fl.ccyl = -1; + fl.cdev = dev; + + dp->len = n; + offset = dp->offset; + + aa = a; + if(DMAOK(aa, n) == 0){ + aa = fl.ccache; + memmove(aa, a, n); + } + for(rv = 0; rv < n; rv += i){ + i = 0; + for(tries = 0; tries < dp->maxtries; tries++){ + floppypos(dp, offset+rv); + i = floppyxfer(dp, Fwrite, aa+rv, n-rv); + if(i > 0) + break; + } + if(tries == dp->maxtries) + break; + } + if(rv != n) + dp->confused = 1; + + dp->offset = offset + rv; + dp->maxtries = 20; + + qunlock(&fl); + return rv; +} diff -Nru /sys/src/fs/pc/kbd.c /sys/src/fs/pc/kbd.c --- /sys/src/fs/pc/kbd.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/pc/kbd.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,333 @@ +#include "all.h" +#include "mem.h" +#include "io.h" +#include "ureg.h" + +enum { + Data= 0x60, /* data port */ + + Status= 0x64, /* status port */ + Inready= 0x01, /* input character ready */ + Outbusy= 0x02, /* output busy */ + Sysflag= 0x04, /* system flag */ + Cmddata= 0x08, /* cmd==0, data==1 */ + Inhibit= 0x10, /* keyboard/mouse inhibited */ + Minready= 0x20, /* mouse character ready */ + Rtimeout= 0x40, /* general timeout */ + Parity= 0x80, + + Cmd= 0x64, /* command port (write only) */ + + CTdata= 0x0, /* chips & Technologies ps2 data port */ + CTstatus= 0x1, /* chips & Technologies ps2 status port */ + Enable= 1<<7, + Clear= 1<<6, + Error= 1<<5, + Intenable= 1<<4, + Reset= 1<<3, + Tready= 1<<2, + Rready= 1<<1, + Idle= 1<<0, + + Spec= 0x80, + + PF= Spec|0x20, /* num pad function key */ + View= Spec|0x00, /* view (shift window up) */ + KF= Spec|0x40, /* function key */ + Shift= Spec|0x60, + Break= Spec|0x61, + Ctrl= Spec|0x62, + Latin= Spec|0x63, + Caps= Spec|0x64, + Num= Spec|0x65, + Middle= Spec|0x66, + No= 0x00, /* peter */ + + Home= KF|13, + Up= KF|14, + Pgup= KF|15, + Print= KF|16, + Left= View, + Right= View, + End= '\r', + Down= View, + Pgdown= View, + Ins= KF|20, + Del= 0x7F, + + Rbutton=4, + Mbutton=2, + Lbutton=1, +}; + +uchar kbtab[] = +{ +[0x00] No, 0x1b, '1', '2', '3', '4', '5', '6', +[0x08] '7', '8', '9', '0', '-', '=', '\b', '\t', +[0x10] 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', +[0x18] 'o', 'p', '[', ']', '\n', Ctrl, 'a', 's', +[0x20] 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', +[0x28] '\'', '`', Shift, '\\', 'z', 'x', 'c', 'v', +[0x30] 'b', 'n', 'm', ',', '.', '/', Shift, '*', +[0x38] Latin, ' ', Ctrl, KF|1, KF|2, KF|3, KF|4, KF|5, +[0x40] KF|6, KF|7, KF|8, KF|9, KF|10, Num, KF|12, '7', +[0x48] '8', '9', '-', '4', '5', '6', '+', '1', +[0x50] '2', '3', '0', '.', Del, No, No, KF|11, +[0x58] KF|12, No, No, No, No, No, No, No, +}; + +uchar kbtabshift[] = +{ +[0x00] No, 0x1b, '!', '@', '#', '$', '%', '^', +[0x08] '&', '*', '(', ')', '_', '+', '\b', '\t', +[0x10] 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', +[0x18] 'O', 'P', '{', '}', '\n', Ctrl, 'A', 'S', +[0x20] 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', +[0x28] '"', '~', Shift, '|', 'Z', 'X', 'C', 'V', +[0x30] 'B', 'N', 'M', '<', '>', '?', Shift, '*', +[0x38] Latin, ' ', Ctrl, KF|1, KF|2, KF|3, KF|4, KF|5, +[0x40] KF|6, KF|7, KF|8, KF|9, KF|10, Num, KF|12, '7', +[0x48] '8', '9', '-', '4', '5', '6', '+', '1', +[0x50] '2', '3', '0', '.', No, No, No, KF|11, +[0x58] KF|12, No, No, No, No, No, No, No, +}; + +uchar kbtabesc1[] = +{ +[0x00] No, No, No, No, No, No, No, No, +[0x08] No, No, No, No, No, No, No, No, +[0x10] No, No, No, No, No, No, No, No, +[0x18] No, No, No, No, '\n', Ctrl, No, No, +[0x20] No, No, No, No, No, No, No, No, +[0x28] No, No, Shift, No, No, No, No, No, +[0x30] No, No, No, No, No, '/', No, Print, +[0x38] Latin, No, No, No, No, No, No, No, +[0x40] No, No, No, No, No, No, Break, Home, +[0x48] Up, Pgup, No, Left, No, Right, No, End, +[0x50] Down, Pgdown, Ins, Del, No, No, No, No, +[0x58] No, No, No, No, No, No, No, No, +}; + +static uchar ccc; +static int shift; + +enum +{ + /* controller command byte */ + Cscs1= (1<<6), /* scan code set 1 */ + Cmousedis= (1<<5), /* mouse disable */ + Ckbddis= (1<<4), /* kbd disable */ + Csf= (1<<2), /* system flag */ + Cmouseint= (1<<1), /* mouse interrupt enable */ + Ckbdint= (1<<0), /* kbd interrupt enable */ +}; + +/* + * wait for output no longer busy + */ +static int +outready(void) +{ + int tries; + + for(tries = 0; (inb(Status) & Outbusy); tries++){ + if(tries > 500) + return -1; + delay(2); + } + return 0; +} + +/* + * wait for input + */ +static int +inready(void) +{ + int tries; + + for(tries = 0; !(inb(Status) & Inready); tries++){ + if(tries > 500) + return -1; + delay(2); + } + return 0; +} + +/* + * ask 8042 to enable the use of address bit 20 + */ +void +i8042a20(void) +{ + outready(); + outb(Cmd, 0xD1); + outready(); + outb(Data, 0xDF); + outready(); +} + +/* + * ask 8042 to reset the machine + */ +void +i8042reset(void) +{ + ushort *s = (ushort*)(KZERO|0x472); + + *s = 0x1234; /* BIOS warm-boot flag */ + + outready(); + outb(Cmd, 0xFE); /* pulse reset line (means resend on AT&T machines) */ + outready(); +} + +/* + * keyboard processing + */ +int +kbdintr0(void) +{ + int s, c; + static int esc1, esc2; + static int caps; + static int ctl; + static int num; + int keyup; + + /* + * get status + */ + s = inb(Status); + if(!(s&Inready)) + return -1; + + /* + * get the character + */ + c = inb(Data); + + /* + * e0's is the first of a 2 character sequence + */ + if(c == 0xe0){ + esc1 = 1; + return -1; + } else if(c == 0xe1){ + esc2 = 2; + return -1; + } + + keyup = c&0x80; + c &= 0x7f; + if(c > sizeof kbtab){ + print("unknown key %ux\n", c|keyup); + return -1; + } + + if(esc1){ + c = kbtabesc1[c]; + esc1 = 0; + } else if(esc2){ + esc2--; + return -1; + } else if(shift) + c = kbtabshift[c]; + else + c = kbtab[c]; + + if(caps && c<='z' && c>='a') + c += 'A' - 'a'; + + /* + * keyup only important for shifts + */ + if(keyup){ + switch(c){ + case Shift: + shift = 0; + break; + case Ctrl: + ctl = 0; + break; + } + return -1; + } + + /* + * normal character + */ + if(!(c & Spec)){ + if(ctl) + c &= 0x1f; + return c; + } else { + switch(c){ + case Caps: + caps ^= 1; + return -1; + case Num: + num ^= 1; + return -1; + case Shift: + shift = 1; + return -1; + case Ctrl: + ctl = 1; + return -1; + } + } + return c; +} + +static void +kbdintr(Ureg *ur, void *v) +{ + int c; + + USED(ur, v); + if((c = kbdintr0()) >= 0) + kbdchar(c); +} + +int +kbdgetc(void) +{ + int c; + + if((c = kbdintr0()) < 0) + return 0; + return c; +} + +void +kbdinit(void) +{ + int c; + + setvec(Kbdvec, kbdintr, 0); + + /* wait for a quiescent controller */ + while((c = inb(Status)) & (Outbusy | Inready)) + if(c & Inready) + inb(Data); + + /* get current controller command byte */ + outb(Cmd, 0x20); + if(inready() < 0){ + print("kbdinit: can't read ccc\n"); + ccc = 0; + } else + ccc = inb(Data); + + /* enable kbd xfers and interrupts */ + ccc &= ~Ckbddis; + ccc |= Csf | Ckbdint | Cscs1; + if(outready() < 0) + print("kbd init failed\n"); + outb(Cmd, 0x60); + if(outready() < 0) + print("kbd init failed\n"); + outb(Data, ccc); + outready(); +} diff -Nru /sys/src/fs/pc/l.s /sys/src/fs/pc/l.s --- /sys/src/fs/pc/l.s Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/pc/l.s Tue Nov 1 00:00:00 2011 @@ -0,0 +1,708 @@ +#include "mem.h" + +#define OP16 BYTE $0x66 +#define NOP XCHGL AX,AX +#define CPUID BYTE $0x0F; BYTE $0xA2 /* CPUID, argument in AX */ +#define WRMSR BYTE $0x0F; BYTE $0x30 /* WRMSR, argument in AX/DX (lo/hi) */ +#define RDMSR BYTE $0x0F; BYTE $0x32 /* RDMSR, result in AX/DX (lo/hi) */ +#define RDTSC BYTE $0x0F; BYTE $0x31 + +TEXT origin(SB),$0 + + CLI + + /* + * Clear BSS + */ + LEAL edata-KZERO(SB),SI + MOVL SI,DI + ADDL $4,DI + MOVL $0,AX + MOVL AX,(SI) + LEAL end-KZERO(SB),CX + SUBL DI,CX + SHRL $2,CX + CLD; REP; MOVSL + + /* + * make a bottom level page table page that maps the first + * 16 meg of physical memory + */ + LEAL tpt-KZERO(SB),AX /* get phys addr of temporary page table */ + ADDL $(BY2PG-1),AX /* must be page alligned */ + ANDL $(~(BY2PG-1)),AX /* ... */ + MOVL $(1024),CX /* pte's per page */ + MOVL $((((1024)-1)<>1)&0x7FFFFFFF)>>(2*PGSHIFT-1-4))+0)(AX) + + /* + * point processor to top level page & turn on paging & make + * supervisor obey the R/W bit in the page map + */ + MOVL AX,CR3 + MOVL CR0,AX + ORL $0X80010000,AX + ANDL $~(0x40000000|0x20000000|0x8|0x2),AX /* CD=0, NW=0, TS=0, MP=0 */ + MOVL AX,CR0 + + /* + * use a jump to an absolute location to get the PC into + * KZERO. + */ + LEAL tokzero(SB),AX + JMP* AX + +TEXT tokzero(SB),$0 + + /* + * stack and mach + */ + MOVL $mach0(SB),SP + MOVL SP,m(SB) + MOVL $0,0(SP) + ADDL $(MACHSIZE-4),SP /* start stack under machine struct */ + MOVL $0, u(SB) + + /* + * clear flags + */ + MOVL $0,AX + PUSHL AX + POPFL + + CALL main(SB) + +loop: + JMP loop + +GLOBL mach0+0(SB), $MACHSIZE +GLOBL u(SB), $4 +GLOBL m(SB), $4 +GLOBL tpt(SB), $(BY2PG*3) + +/* + * gdt to get us to 32-bit/segmented/unpaged mode + */ +TEXT tgdt(SB),$0 + + /* null descriptor */ + LONG $0 + LONG $0 + + /* data segment descriptor for 4 gigabytes (PL 0) */ + LONG $(0xFFFF) + LONG $(SEGG|SEGB|(0xF<<16)|SEGP|SEGPL(0)|SEGDATA|SEGW) + + /* exec segment descriptor for 4 gigabytes (PL 0) */ + LONG $(0xFFFF) + LONG $(SEGG|SEGD|(0xF<<16)|SEGP|SEGPL(0)|SEGEXEC|SEGR) + +/* + * pointer to initial gdt + */ +TEXT tgdtptr(SB),$0 + + WORD $(3*8) + LONG $tgdt-KZERO(SB) + +/* + * input a byte + */ +TEXT inb(SB),$0 + + MOVL p+0(FP),DX + XORL AX,AX + INB + RET + +/* + * input a string of bytes from a port + */ +TEXT insb(SB),$0 + + MOVL p+0(FP),DX + MOVL a+4(FP),DI + MOVL c+8(FP),CX + CLD; REP; INSB + RET + +/* + * output a byte + */ +TEXT outb(SB),$0 + + MOVL p+0(FP),DX + MOVL b+4(FP),AX + OUTB + RET + +/* + * output a string of bytes to a port + */ +TEXT outsb(SB),$0 + + MOVL p+0(FP),DX + MOVL a+4(FP),SI + MOVL c+8(FP),CX + CLD; REP; OUTSB + RET + +/* + * input a short from a port + */ +TEXT ins(SB), $0 + + MOVL p+0(FP), DX + XORL AX, AX + OP16; INL + RET + +/* + * input a string of shorts from a port + */ +TEXT inss(SB),$0 + + MOVL p+0(FP),DX + MOVL a+4(FP),DI + MOVL c+8(FP),CX + CLD; REP; OP16; INSL + RET + +/* + * input a long from a port + */ +TEXT inl(SB), $0 + + MOVL p+0(FP), DX + XORL AX, AX + INL + RET + +/* + * input a string of longs from a port + */ +TEXT insl(SB),$0 + + MOVL p+0(FP),DX + MOVL a+4(FP),DI + MOVL c+8(FP),CX + CLD; REP; INSL + RET + +/* + * output a short to a port + */ +TEXT outs(SB), $0 + MOVL p+0(FP), DX + MOVL s+4(FP), AX + OP16; OUTL + RET + +/* + * output a string of shorts to a port + */ +TEXT outss(SB),$0 + + MOVL p+0(FP),DX + MOVL a+4(FP),SI + MOVL c+8(FP),CX + CLD; REP; OP16; OUTSL + RET + +/* + * output a long to a port + */ +TEXT outl(SB), $0 + MOVL p+0(FP), DX + MOVL s+4(FP), AX + OUTL + RET + +/* + * output a string of longs to a port + */ +TEXT outsl(SB),$0 + + MOVL p+0(FP),DX + MOVL a+4(FP),SI + MOVL c+8(FP),CX + CLD; REP; OUTSL + RET + +/* + * test and set + */ +TEXT tas(SB),$0 + MOVL $0xdeadead,AX + MOVL l+0(FP),BX + XCHGL AX,(BX) + RET + +TEXT wbflush(SB), $0 + XORL AX, AX + CPUID + RET + +/* + * routines to load/read various system registers + */ +GLOBL idtptr(SB),$6 +TEXT putidt(SB),$0 /* interrupt descriptor table */ + MOVL t+0(FP),AX + MOVL AX,idtptr+2(SB) + MOVL l+4(FP),AX + MOVW AX,idtptr(SB) + MOVL idtptr(SB),IDTR + RET + +GLOBL gdtptr(SB),$6 +TEXT putgdt(SB),$0 /* global descriptor table */ + MOVL t+0(FP),AX + MOVL AX,gdtptr+2(SB) + MOVL l+4(FP),AX + MOVW AX,gdtptr(SB) + MOVL gdtptr(SB),GDTR + RET + +TEXT putcr3(SB),$0 /* top level page table pointer */ + MOVL t+0(FP),AX + MOVL AX,CR3 + RET + +TEXT getcr4(SB), $0 /* CR4 - extensions */ + MOVL CR4, AX + RET + +TEXT putcr4(SB), $0 + MOVL cr4+0(FP), AX + MOVL AX, CR4 + RET + +TEXT puttr(SB),$0 /* task register */ + MOVL t+0(FP),AX + MOVW AX,TASK + RET + +TEXT getcr0(SB),$0 /* coprocessor bits */ + MOVL CR0,AX + RET + +TEXT getcr2(SB),$0 /* fault address */ + MOVL CR2,AX + RET + +#define FPOFF\ + WAIT;\ + MOVL CR0,AX;\ + ORL $0x24,AX /* EM=1, NE=1 */;\ + MOVL AX,CR0 + +#define FPON\ + MOVL CR0,AX;\ + ANDL $~0x4,AX /* EM=0 */;\ + MOVL AX,CR0 + +TEXT fpoff(SB),$0 /* turn off floating point */ + FPOFF + RET + +TEXT fpinit(SB),$0 /* turn on & init the floating point */ + FPON + FINIT + WAIT + PUSHW $0x033E + FLDCW 0(SP) /* ignore underflow/precision, signal others */ + POPW AX + WAIT + RET + +TEXT fpsave(SB),$0 /* save floating point state and turn off */ + MOVL p+0(FP),AX + WAIT + FSAVE 0(AX) + FPOFF + RET + +TEXT fprestore(SB),$0 /* turn on floating point and restore regs */ + FPON + MOVL p+0(FP),AX + FRSTOR 0(AX) + WAIT + RET + +TEXT fpstatus(SB),$0 /* get floating point status */ + FSTSW AX + RET + +TEXT fpenv(SB),$0 /* save floating point environment without waiting */ + MOVL p+0(FP),AX + FSTENV 0(AX) + RET + +/* + * special traps + */ +TEXT intr0(SB),$0 + PUSHL $0 + PUSHL $0 + JMP intrcommon +TEXT intr1(SB),$0 + PUSHL $0 + PUSHL $1 + JMP intrcommon +TEXT intr2(SB),$0 + PUSHL $0 + PUSHL $2 + JMP intrcommon +TEXT intr3(SB),$0 + PUSHL $0 + PUSHL $3 + JMP intrcommon +TEXT intr4(SB),$0 + PUSHL $0 + PUSHL $4 + JMP intrcommon +TEXT intr5(SB),$0 + PUSHL $0 + PUSHL $5 + JMP intrcommon +TEXT intr6(SB),$0 + PUSHL $0 + PUSHL $6 + JMP intrcommon +TEXT intr7(SB),$0 + PUSHL $0 + PUSHL $7 + JMP intrcommon +TEXT intr8(SB),$0 + PUSHL $8 + JMP intrscommon +TEXT intr9(SB),$0 + PUSHL $0 + PUSHL $9 + JMP intrcommon +TEXT intr10(SB),$0 + PUSHL $10 + JMP intrscommon +TEXT intr11(SB),$0 + PUSHL $11 + JMP intrscommon +TEXT intr12(SB),$0 + PUSHL $12 + JMP intrscommon +TEXT intr13(SB),$0 + PUSHL $13 + JMP intrscommon +TEXT intr14(SB),$0 + PUSHL $14 + JMP intrscommon +TEXT intr15(SB),$0 + PUSHL $0 + PUSHL $15 + JMP intrcommon +TEXT intr16(SB),$0 + PUSHL $0 + PUSHL $16 + JMP intrcommon +TEXT intr24(SB),$0 + PUSHL $0 + PUSHL $24 + JMP intrcommon +TEXT intr25(SB),$0 + PUSHL $0 + PUSHL $25 + JMP intrcommon +TEXT intr26(SB),$0 + PUSHL $0 + PUSHL $26 + JMP intrcommon +TEXT intr27(SB),$0 + PUSHL $0 + PUSHL $27 + JMP intrcommon +TEXT intr28(SB),$0 + PUSHL $0 + PUSHL $28 + JMP intrcommon +TEXT intr29(SB),$0 + PUSHL $0 + PUSHL $29 + JMP intrcommon +TEXT intr30(SB),$0 + PUSHL $0 + PUSHL $30 + JMP intrcommon +TEXT intr31(SB),$0 + PUSHL $0 + PUSHL $31 + JMP intrcommon +TEXT intr32(SB),$0 + PUSHL $0 + PUSHL $32 + JMP intrcommon +TEXT intr33(SB),$0 + PUSHL $0 + PUSHL $33 + JMP intrcommon +TEXT intr34(SB),$0 + PUSHL $0 + PUSHL $34 + JMP intrcommon +TEXT intr35(SB),$0 + PUSHL $0 + PUSHL $35 + JMP intrcommon +TEXT intr36(SB),$0 + PUSHL $0 + PUSHL $36 + JMP intrcommon +TEXT intr37(SB),$0 + PUSHL $0 + PUSHL $37 + JMP intrcommon +TEXT intr38(SB),$0 + PUSHL $0 + PUSHL $38 + JMP intrcommon +TEXT intr39(SB),$0 + PUSHL $0 + PUSHL $39 + JMP intrcommon +TEXT intr64(SB),$0 + PUSHL $0 + PUSHL $64 + JMP intrcommon +TEXT intrbad(SB),$0 + PUSHL $0 + PUSHL $0x1ff + JMP intrcommon + +intrcommon: + PUSHL DS + PUSHL ES + PUSHL FS + PUSHL GS + PUSHAL + MOVL $(KDSEL),AX + MOVW AX,DS + MOVW AX,ES + LEAL 0(SP),AX + PUSHL AX + CALL trap(SB) + POPL AX + POPAL + NOP + POPL GS + POPL FS + POPL ES + POPL DS + NOP + ADDL $8,SP /* error code and trap type */ + IRETL + +intrscommon: + PUSHL DS + PUSHL ES + PUSHL FS + PUSHL GS + PUSHAL + MOVL $(KDSEL),AX + MOVW AX,DS + MOVW AX,ES + LEAL 0(SP),AX + PUSHL AX + CALL trap(SB) + POPL AX + POPAL + NOP + POPL GS + POPL FS + POPL ES + POPL DS + NOP + ADDL $8,SP /* error code and trap type */ + IRETL + +/* + * interrupt level is interrupts on or off + */ +TEXT spllo(SB),$0 + PUSHFL + POPL AX + STI + RET + +TEXT splhi(SB),$0 + PUSHFL + POPL AX + CLI + RET + +TEXT splx(SB),$0 + MOVL s+0(FP),AX + PUSHL AX + POPFL + RET + +TEXT getstatus(SB),$0 + PUSHFL + POPL AX + RET + +TEXT _cycles(SB), $0 /* time stamp counter; cycles since power up */ + RDTSC + MOVL vlong+0(FP), CX /* &vlong */ + MOVL AX, 0(CX) /* lo */ + MOVL DX, 4(CX) /* hi */ + RET + +TEXT rdmsr(SB), $0 /* model-specific register */ + MOVL index+0(FP), CX + RDMSR + MOVL vlong+4(FP), CX /* &vlong */ + MOVL AX, (CX) /* lo */ + MOVL DX, 4(CX) /* hi */ + RET + +TEXT wrmsr(SB), $0 + MOVL index+0(FP), CX + MOVL lo+4(FP), AX + MOVL hi+8(FP), DX + WRMSR + RET + +/* + * Try to determine the CPU type which requires fiddling with EFLAGS. + * If the Id bit can be toggled then the CPUID instruciton can be used + * to determine CPU identity and features. First have to check if it's + * a 386 (Ac bit can't be set). If it's not a 386 and the Id bit can't be + * toggled then it's an older 486 of some kind. + * + * cpuid(id[], &ax, &dx); + */ +TEXT cpuid(SB), $0 + MOVL $0x240000, AX + PUSHL AX + POPFL /* set Id|Ac */ + + PUSHFL + POPL BX /* retrieve value */ + + MOVL $0, AX + PUSHL AX + POPFL /* clear Id|Ac, EFLAGS initialised */ + + PUSHFL + POPL AX /* retrieve value */ + XORL BX, AX + TESTL $0x040000, AX /* Ac */ + JZ _cpu386 /* can't set this bit on 386 */ + TESTL $0x200000, AX /* Id */ + JZ _cpu486 /* can't toggle this bit on some 486 */ + + MOVL $0, AX + CPUID + MOVL id+0(FP), BP + MOVL BX, 0(BP) /* "Genu" "Auth" "Cyri" */ + MOVL DX, 4(BP) /* "ineI" "enti" "xIns" */ + MOVL CX, 8(BP) /* "ntel" "cAMD" "tead" */ + + MOVL $1, AX + CPUID + JMP _cpuid + +_cpu486: + MOVL $0x400, AX + MOVL $0, DX + JMP _cpuid + +_cpu386: + MOVL $0x300, AX + MOVL $0, DX + +_cpuid: + MOVL ax+4(FP), BP + MOVL AX, 0(BP) + MOVL dx+8(FP), BP + MOVL DX, 0(BP) + RET + +/* + * basic timing loop to determine CPU frequency + */ +TEXT aamloop(SB),$0 + + MOVL c+0(FP),CX +aaml1: + AAM + LOOP aaml1 + RET + +/* + * do nothing whatsoever till interrupt happens + */ +TEXT idle(SB),$0 + HLT + RET + +/* + * label consists of a stack pointer and a PC + */ +TEXT gotolabel(SB),$0 + MOVL l+0(FP),AX + MOVL 4(AX),SP /* restore sp */ + MOVL 0(AX),AX /* put return pc on the stack */ + MOVL AX,0(SP) + MOVL $1,AX /* return 1 */ + RET + +TEXT setlabel(SB),$0 + MOVL l+0(FP),AX + MOVL SP,4(AX) /* store sp */ + MOVL 0(SP),BX /* store return pc */ + MOVL BX,0(AX) + MOVL $0,AX /* return 0 */ + RET + + +TEXT famd+0(SB), $4 + PUSHFL + CLI /* spl hi */ + + FMOVL b+4(FP), F0 + FADDF a+0(FP), F0 + FMOVL c+8(FP), F0 + FMULDP F0, F1 + FMOVL d+12(FP), F0 + FDIVDP F0, F1 + + FMOVFP F0, .safe-4(SP) + MOVL .safe-4(SP), AX + + POPFL /* splx */ + RET + +TEXT fdf+0(SB), $4 + PUSHFL + CLI /* spl hi */ + + FMOVL b+4(FP), F0 + FDIVRF a+0(FP), F0 + FMOVLP F0, .safe-4(SP) + MOVL .safe-4(SP), AX + + POPFL /* splx */ + RET diff -Nru /sys/src/fs/pc/lock.c /sys/src/fs/pc/lock.c --- /sys/src/fs/pc/lock.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/pc/lock.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,264 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "mem.h" + +void +lock(Lock *l) +{ + int i; + ulong caller; + + caller = getcallerpc(&l); + /* + * Try the fast grab first + */ +loop: + if(tas(l) == 0) { + l->pc = caller; + return; + } + for(i = 0; i < 1000000; i++) { + if(tas(l) == 0) { + l->pc = caller; + return; + } + /* If we are spl low resched */ + if(getstatus() & IFLAG) + sched(); + } + l->sbsem = 0; + + print("lock loop 0x%lux called by 0x%lux held by pc 0x%lux\n", (ulong)l, + caller, l->pc); + goto loop; +} + +void +ilock(Lock *l) +{ + l->sr = splhi(); + lock(l); +} + +void +iunlock(Lock *l) +{ + ulong sr; + + sr = l->sr; + l->sbsem = 0; + l->pc = 0; + splx(sr); +} + +int +canlock(Lock *l) +{ + if (tas(l) == 0) + return 1; + return 0; +} + +void +unlock(Lock *l) +{ + l->pc = 0; + l->sbsem = 0; +} + +void +qlock(QLock *q) +{ + User *p; + int i; + + lock(q); + if(!q->locked){ + q->locked = 1; + unlock(q); + goto out; + } + if(1 && u) { + for(i=0; ihas.q[i] == q) { + print("circular qlock by %d at 0x%lux (other 0x%lux, 0x%lux)\n", + u->pid, getcallerpc(&q), u->has.pc[i], q->pc); + dumpstack(u); + break; + } + } + p = q->tail; + if(p == 0) + q->head = u; + else + p->qnext = u; + q->tail = u; + u->qnext = 0; + u->state = Queueing; + u->has.want = q; + unlock(q); + sched(); + u->has.want = 0; + +out: + if(1 && u) { + for(i=0; ihas.q[i] == 0) { + u->has.q[i] = q; + u->has.pc[i] = getcallerpc(&q); + return; + } + print("NHAS(%d) too small\n", NHAS); + } +} + +int +canqlock(QLock *q) +{ + int i; + + lock(q); + if(q->locked){ + unlock(q); + return 0; + } + q->locked = 1; + unlock(q); + + if(1 && u) { + for(i=0; ihas.q[i] == 0) { + u->has.q[i] = q; + u->has.pc[i] = getcallerpc(&q); + return 1; + } + print("NHAS(%d) too small\n", NHAS); + } + return 1; +} + +void +qunlock(QLock *q) +{ + User *p; + int i; + + lock(q); + p = q->head; + if(p) { + q->head = p->qnext; + if(q->head == 0) + q->tail = 0; + unlock(q); + ready(p); + } else { + q->locked = 0; + unlock(q); + } + + if(1 && u) { + for(i=0; ihas.q[i] == q) { + u->has.q[i] = 0; + return; + } + panic("qunlock: not there %lux, called from %lux\n", + (ulong)q, getcallerpc(&q)); + } +} + +/* + * readers/writers lock + * allows 1 writer or many readers + */ +void +rlock(RWlock *l) +{ + QLock *q; + + qlock(&l->wr); /* wait here for writers and exclusion */ + + q = &l->rd; /* first reader in, qlock(&l->rd) */ + lock(q); + q->locked = 1; + l->nread++; + unlock(q); + + qunlock(&l->wr); + + if(1 && u) { + int i; + int found; + + found = 0; + for(i=0; ihas.q[i] == q){ + print("circular rlock by %d at 0x%lux (other 0x%lux)\n", + u->pid, getcallerpc(&l), u->has.pc[i]); + dumpstack(u); + } + if(!found && u->has.q[i] == 0) { + u->has.q[i] = q; + u->has.pc[i] = getcallerpc(&l); + found = 1; + } + } + if(!found) + print("NHAS(%d) too small\n", NHAS); + } +} + +void +runlock(RWlock *l) +{ + QLock *q; + User *p; + int n; + + q = &l->rd; + lock(q); + n = l->nread - 1; + l->nread = n; + if(n == 0) { /* last reader out, qunlock(&l->rd) */ + p = q->head; + if(p) { + q->head = p->qnext; + if(q->head == 0) + q->tail = 0; + unlock(q); + ready(p); + goto accounting; + } + q->locked = 0; + } + unlock(q); + +accounting: + if(1 && u) { + int i; + for(i=0; ihas.q[i] == q) { + u->has.q[i] = 0; + return; + } + panic("runlock: not there %lux, called from %lux\n", + (ulong)q, getcallerpc(&l)); + } +} + +void +wlock(RWlock *l) +{ + qlock(&l->wr); /* wait here for writers and exclusion */ + qlock(&l->rd); /* wait here for last reader */ +} + +void +wunlock(RWlock *l) +{ + qunlock(&l->rd); + qunlock(&l->wr); +} diff -Nru /sys/src/fs/pc/malloc.c /sys/src/fs/pc/malloc.c --- /sys/src/fs/pc/malloc.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/pc/malloc.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,154 @@ +#include "all.h" +#include "mem.h" +#include "io.h" + +long niob; +long nhiob; +Hiob *hiob; + +/* + * Called to allocate permanent data structures + * Alignment is in number of bytes. It pertains both to the start and + * end of the allocated memory. + */ +void* +ialloc(ulong n, int align) +{ + Mbank *mbp; + ulong p; + int m; + + ilock(&mconf); + for(mbp = &mconf.bank[0]; mbp < &mconf.bank[mconf.nbank]; mbp++){ + p = mbp->base; + + if(align <= 0) + align = 4; + if(m = n % align) + n += align - m; + if(m = p % align) + p += align - m; + + if(p+n > mbp->limit) + continue; + + mbp->base = p+n; + iunlock(&mconf); + + memset((void*)(p|KZERO), 0, n); + return (void*)(p|KZERO); + } + + iunlock(&mconf); + for(mbp = mconf.bank; mbp < &mconf.bank[mconf.nbank]; mbp++) + print("bank[%ld]: base 0x%8.8lux, limit 0x%8.8lux\n", + mbp - mconf.bank, mbp->base, mbp->limit); + panic("ialloc(0x%lux, 0x%lx): out of memory\n", n, align); + return 0; +} + +void +prbanks(void) +{ + Mbank *mbp; + + for(mbp = mconf.bank; mbp < &mconf.bank[mconf.nbank]; mbp++) + print("bank[%ld]: base 0x%8.8lux, limit 0x%8.8lux\n", + mbp - mconf.bank, mbp->base, mbp->limit); +} + +static void +cmd_memory(int, char *[]) +{ + prbanks(); +} + +/* + * allocate rest of mem + * for io buffers. + */ +#define HWIDTH 8 /* buffers per hash */ +void +iobufinit(void) +{ + long m; + int i; + Iobuf *p, *q; + Hiob *hp; + Mbank *mbp; + + wlock(&mainlock); /* init */ + wunlock(&mainlock); + + prbanks(); + m = 0; + for(mbp = mconf.bank; mbp < &mconf.bank[mconf.nbank]; mbp++) { + m += mbp->limit - mbp->base; + } + m -= conf.sparemem; + + niob = m / (sizeof(Iobuf) + RBUFSIZE + sizeof(Hiob)/HWIDTH); + nhiob = niob / HWIDTH; + while(!prime(nhiob)) + nhiob++; + print(" %ld buffers; %ld hashes\n", niob, nhiob); + hiob = ialloc(nhiob * sizeof(Hiob), 0); + hp = hiob; + for(i=0; iname = "buf"; + qlock(p); + qunlock(p); + if(hp == hiob) + hp = hiob + nhiob; + hp--; + q = hp->link; + if(q) { + p->fore = q; + p->back = q->back; + q->back = p; + p->back->fore = p; + } else { + hp->link = p; + p->fore = p; + p->back = p; + } + p->dev = devnone; + p->addr = -1; + p->xiobuf = ialloc(RBUFSIZE, RBUFSIZE); + p->iobuf = (char*)-1; + p++; + } + + /* + * Make sure that no more of bank[0] can be used: + * 'check' will do an ialloc(0, 1) to find the base of + * sparemem. + */ + mconf.bank[0].base = mconf.bank[0].limit+1; + + i = 0; + for(mbp = mconf.bank; mbp < &mconf.bank[mconf.nbank]; mbp++) + i += mbp->limit - mbp->base; + print(" mem left = %,d, out of %,ld\n", i, conf.mem); + /* paranoia: add this command as late as is easy */ + cmd_install("memory", "-- print ranges of memory banks", cmd_memory); +} + +void* +iobufmap(Iobuf *p) +{ + return p->iobuf = p->xiobuf; +} + +void +iobufunmap(Iobuf *p) +{ + p->iobuf = (char*)-1; +} diff -Nru /sys/src/fs/pc/mkfile /sys/src/fs/pc/mkfile --- /sys/src/fs/pc/mkfile Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/pc/mkfile Tue Nov 1 00:00:00 2011 @@ -0,0 +1,13 @@ +PCCFILES=`{builtin cd ../pc;echo *.c | sed 's/ /|/g; s/\.c//g'} +PCSFILES=`{builtin cd ../pc;echo *.s | sed 's/ /|/g; s/\.s//g'} +^($PCCFILES)\.$O:R: '../pc/\1.c' + $CC $CFLAGS ../pc/$stem1.c + +^($PCSFILES)\.$O:R: '../pc/\1.s' + $AS ../pc/$stem1.s + +$ETHER: ../pc/etherif.h + +dosfs.$O nvr.$O: ../pc/dosfs.h +compat.$O ether8139.$O ether83815.$O etherdp83820.$O etherigbe.$O etherif.$O\ + ethermii.$O sdata.$O sdscsi.$O: ../pc/compat.h diff -Nru /sys/src/fs/pc/mmu.c /sys/src/fs/pc/mmu.c --- /sys/src/fs/pc/mmu.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/pc/mmu.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,373 @@ +#include "all.h" +#include "mem.h" +#include "io.h" +#include "ureg.h" + +/* + * task state segment. Plan 9 ignores all the task switching goo and just + * uses the tss for esp0 and ss0 on gate's into the kernel, interrupts, + * and exceptions. The rest is completely ignored. + * + * This means that we only need one tss in the whole system. + */ +typedef struct Tss Tss; +struct Tss +{ + ulong backlink; /* unused */ + ulong sp0; /* pl0 stack pointer */ + ulong ss0; /* pl0 stack selector */ + ulong sp1; /* pl1 stack pointer */ + ulong ss1; /* pl1 stack selector */ + ulong sp2; /* pl2 stack pointer */ + ulong ss2; /* pl2 stack selector */ + ulong cr3; /* page table descriptor */ + ulong eip; /* instruction pointer */ + ulong eflags; /* processor flags */ + ulong eax; /* general (hah?) registers */ + ulong ecx; + ulong edx; + ulong ebx; + ulong esp; + ulong ebp; + ulong esi; + ulong edi; + ulong es; /* segment selectors */ + ulong cs; + ulong ss; + ulong ds; + ulong fs; + ulong gs; + ulong ldt; /* local descriptor table */ + ulong iomap; /* io map base */ +}; +Tss tss; + +/* + * segment descriptor initializers + */ +#define DATASEGM(p) { 0xFFFF, SEGG|SEGB|(0xF<<16)|SEGP|SEGPL(p)|SEGDATA|SEGW } +#define EXECSEGM(p) { 0xFFFF, SEGG|SEGD|(0xF<<16)|SEGP|SEGPL(p)|SEGEXEC|SEGR } +#define CALLGATE(s,o,p) { ((o)&0xFFFF)|((s)<<16), (o)&0xFFFF0000|SEGP|SEGPL(p)|SEGCG } +#define D16SEGM(p) { 0xFFFF, (0x0<<16)|SEGP|SEGPL(p)|SEGDATA|SEGW } +#define E16SEGM(p) { 0xFFFF, (0x0<<16)|SEGP|SEGPL(p)|SEGEXEC|SEGR } +#define TSSSEGM(b,p) { ((b)<<16)|sizeof(Tss),\ + ((b)&0xFF000000)|(((b)>>16)&0xFF)|SEGTSS|SEGPL(p)|SEGP } + +/* + * global descriptor table describing all segments + */ +Segdesc gdt[] = +{ +[NULLSEG] { 0, 0}, /* null descriptor */ +[KDSEG] DATASEGM(0), /* kernel data/stack */ +[KESEG] EXECSEGM(0), /* kernel code */ +[UDSEG] DATASEGM(3), /* user data/stack */ +[UESEG] EXECSEGM(3), /* user code */ +[TSSSEG] TSSSEGM(0,0), /* tss segment */ +}; + +static struct { + ulong va; + ulong pa; +} ktoppg; /* prototype top level page table + * containing kernel mappings */ +static ulong *kpt; /* 2nd level page tables for kernel mem */ + +#define ROUNDUP(s,v) (((s)+(v-1))&~(v-1)) +/* + * offset of virtual address into + * top level page table + */ +#define TOPOFF(v) (((ulong)(v))>>(2*PGSHIFT-2)) + +/* + * offset of virtual address into + * bottom level page table + */ +#define BTMOFF(v) ((((ulong)(v))>>(PGSHIFT))&(WD2PG-1)) + +/* + * Change current page table and the stack to use for exceptions + * (traps & interrupts). The exception stack comes from the tss. + * Since we use only one tss, (we hope) there's no need for a + * puttr(). + */ +static void +taskswitch(ulong pagetbl, ulong stack) +{ + tss.ss0 = KDSEL; + tss.sp0 = stack; +tss.ss1 = KDSEL; +tss.sp1 = stack; +tss.ss2 = KDSEL; +tss.sp2 = stack; + tss.cr3 = pagetbl; + putcr3(pagetbl); +} + +/* + * Create a prototype page map that maps all of memory into + * kernel (KZERO) space. This is the default map. It is used + * whenever the processor is not running a process or whenever running + * a process which does not yet have its own map. + */ +void +mmuinit(void) +{ + int i, nkpt, npage, nbytes; + ulong x; + ulong y; + ulong *top; + + /* + * set up the global descriptor table. we make the tss entry here + * since it requires arithmetic on an address and hence cannot + * be a compile or link time constant. + */ + x = (ulong)&tss; + gdt[TSSSEG].d0 = (x<<16)|sizeof(Tss); + gdt[TSSSEG].d1 = (x&0xFF000000)|((x>>16)&0xFF)|SEGTSS|SEGPL(0)|SEGP; + putgdt(gdt, sizeof gdt); + + /* + * set up system page tables. + * map all of physical memory to start at KZERO. + * leave a map entry for a user area. + */ + + /* + * allocate top level table + */ + top = ialloc(BY2PG, BY2PG); + + ktoppg.va = (ulong)top; + ktoppg.pa = ktoppg.va & ~KZERO; + + /* map all memory to KZERO */ + npage = mconf.topofmem/BY2PG; + nbytes = PGROUND(npage*BY2WD); /* words of page map */ + nkpt = nbytes/BY2PG; /* pages of page map */ + + kpt = ialloc(nbytes, BY2PG); + + for(i = 0; i < npage; i++) + kpt[i] = (0+i*BY2PG) | PTEVALID | PTEKERNEL | PTEWRITE; + x = TOPOFF(KZERO); + y = ((ulong)kpt)&~KZERO; + for(i = 0; i < nkpt; i++) + top[x+i] = (y+i*BY2PG) | PTEVALID | PTEKERNEL | PTEWRITE; + + /* + * set up the task segment + */ + memset(&tss, 0, sizeof(tss)); + taskswitch(ktoppg.pa, BY2PG + (ulong)m); + puttr(TSSSEL);/**/ +} + +/* + * used to map a page into 16 meg - BY2PG for confinit(). tpt is the temporary + * page table set up by l.s. + */ +long* +mapaddr(ulong addr) +{ + ulong base; + ulong off; + static ulong *pte, top; + extern ulong tpt[]; + + if(pte == 0){ + top = (((ulong)tpt)+(BY2PG-1))&~(BY2PG-1); + pte = (ulong*)top; + top &= ~KZERO; + top += BY2PG; + pte += (4*1024*1024-BY2PG)>>PGSHIFT; + } + + base = off = addr; + base &= ~(KZERO|(BY2PG-1)); + off &= BY2PG-1; + + *pte = base|PTEVALID|PTEKERNEL|PTEWRITE; /**/ + putcr3((ulong)top); + + return (long*)(KZERO | 4*1024*1024-BY2PG | off); +} + + +#define PDX(va) ((((ulong)(va))>>22) & 0x03FF) +#define PTX(va) ((((ulong)(va))>>12) & 0x03FF) +#define PPN(x) ((x)&~(BY2PG-1)) +#define KADDR(a) ((void*)((ulong)(a)|KZERO)) + +ulong* +mmuwalk(ulong* pdb, ulong va, int level, int create) +{ + ulong pa, *table; + + /* + * Walk the page-table pointed to by pdb and return a pointer + * to the entry for virtual address va at the requested level. + * If the entry is invalid and create isn't requested then bail + * out early. Otherwise, for the 2nd level walk, allocate a new + * page-table page and register it in the 1st level. + */ + table = &pdb[PDX(va)]; + if(!(*table & PTEVALID) && create == 0) + return 0; + + switch(level){ + + default: + return 0; + + case 1: + return table; + + case 2: + if(*table & PTESIZE) + panic("mmuwalk2: va 0x%ux entry 0x%ux\n", va, *table); + if(!(*table & PTEVALID)){ + pa = PADDR(ialloc(BY2PG, BY2PG)); + *table = pa|PTEWRITE|PTEVALID; + } + table = KADDR(PPN(*table)); + + return &table[PTX(va)]; + } +} + +static Lock mmukmaplock; + +ulong +mmukmap(ulong pa, ulong va, int size) +{ + ulong pae, *table, *pdb, pgsz, *pte, x; + int pse, sync; + extern int cpuidax, cpuiddx; + + pdb = (ulong*)ktoppg.va; + if((cpuiddx & 0x08) && (getcr4() & 0x10)) + pse = 1; + else + pse = 0; + sync = 0; + + pa = PPN(pa); + if(va == 0) + va = (ulong)KADDR(pa); + else + va = PPN(va); + + pae = pa + size; + lock(&mmukmaplock); + while(pa < pae){ + table = &pdb[PDX(va)]; + /* + * Possibly already mapped. + */ + if(*table & PTEVALID){ + if(*table & PTESIZE){ + /* + * Big page. Does it fit within? + * If it does, adjust pgsz so the correct end can be + * returned and get out. + * If not, adjust pgsz up to the next 4MB boundary + * and continue. + */ + x = PPN(*table); + if(x != pa) + panic("mmukmap1: pa 0x%ux entry 0x%ux\n", + pa, *table); + x += 4*MB; + if(pae <= x){ + pa = pae; + break; + } + pgsz = x - pa; + pa += pgsz; + va += pgsz; + + continue; + } + else{ + /* + * Little page. Walk to the entry. + * If the entry is valid, set pgsz and continue. + * If not, make it so, set pgsz, sync and continue. + */ + pte = mmuwalk(pdb, va, 2, 0); + if(pte && *pte & PTEVALID){ + x = PPN(*pte); + if(x != pa) + panic("mmukmap2: pa 0x%ux entry 0x%ux\n", + pa, *pte); + pgsz = BY2PG; + pa += pgsz; + va += pgsz; + sync++; + + continue; + } + } + } + + /* + * Not mapped. Check if it can be mapped using a big page - + * starts on a 4MB boundary, size >= 4MB and processor can do it. + * If not a big page, walk the walk, talk the talk. + * Sync is set. + */ + if(pse && (pa % (4*MB)) == 0 && (pae >= pa+4*MB)){ + *table = pa|PTESIZE|PTEWRITE|PTEUNCACHED|PTEVALID; + pgsz = 4*MB; + } + else{ + pte = mmuwalk(pdb, va, 2, 1); + *pte = pa|PTEWRITE|PTEUNCACHED|PTEVALID; + pgsz = BY2PG; + } + pa += pgsz; + va += pgsz; + sync++; + } + unlock(&mmukmaplock); + + /* + * If something was added + * then need to sync up. + */ + if(sync) + putcr3(ktoppg.pa); + + return pa; +} + +ulong +upamalloc(ulong addr, int size, int align) +{ + ulong ae; + + /* + * Another horrible hack because + * I CAN'T BE BOTHERED WITH THIS FILESERVER BEING + * COMPLETELY INCOMPATIBLE ANYMORE. + */ + if((addr < mconf.topofmem) || align) + panic("upamalloc: (0x%lux < 0x%lux) || %d\n", + addr, mconf.topofmem, align); + + ae = mmukmap(addr, 0, size); + + /* + * Should check here that it was all delivered + * and put it back and barf if not. + */ + USED(ae); + + /* + * Be very careful this returns a PHYSICAL address. + */ + return addr; +} diff -Nru /sys/src/fs/pc/nvr.c /sys/src/fs/pc/nvr.c --- /sys/src/fs/pc/nvr.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/pc/nvr.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,54 @@ +#include "all.h" +#include "mem.h" +#include "io.h" +#include "ureg.h" + +#include "dosfs.h" + +static Dosfile file; +static int opened; +char nvrfile[128] = "plan9.nvr"; + +static void +nvopen(void) +{ + int s; + Dosfile *fp; + + if(opened) + return; + opened = 1; + s = spllo(); + fp = dosopen(&dos, nvrfile, &file); + splx(s); + if(fp == 0) + panic("can't open %s\n", nvrfile); +} + +int +nvread(int offset, void *a, int n) +{ + int r, s; + + nvopen(); + + s = spllo(); + file.offset = offset; + r = dosread(&file, a, n); + splx(s); + return r; +} + +int +nvwrite(int offset, void *a, int n) +{ + int r, s; + + nvopen(); + + s = spllo(); + file.offset = offset; + r = doswrite(&file, a, n); + splx(s); + return r; +} diff -Nru /sys/src/fs/pc/pc.c /sys/src/fs/pc/pc.c --- /sys/src/fs/pc/pc.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/pc/pc.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,466 @@ +#include "all.h" +#include "mem.h" +#include "io.h" +#include "ureg.h" + +/* + * Where configuration info is left for the loaded programme. + * This will turn into a structure as more is done by the boot loader + * (e.g. why parse the .ini file twice?). + * There are 1024 bytes available at CONFADDR. + */ +#define BOOTLINE ((char*)CONFADDR) +#define BOOTLINELEN 64 +#define BOOTARGS ((char*)(CONFADDR+BOOTLINELEN)) +#define BOOTARGSLEN (1024-BOOTLINELEN) +#define MAXCONF 32 + +#define KADDR(a) ((void*)((ulong)(a)|KZERO)) + +char bootdisk[NAMELEN]; +char *confname[MAXCONF]; +char *confval[MAXCONF]; +int nconf; + +static int isoldbcom; + +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; +} + +static void +options(void) +{ + uchar *bda; + long i, n; + char *cp, *line[MAXCONF], *p, *q; + + if(strncmp(BOOTARGS, "ZORT 0\r\n", 8)){ + isoldbcom = 1; + + memmove(BOOTARGS, KADDR(1024), BOOTARGSLEN); + memmove(BOOTLINE, KADDR(0x100), BOOTLINELEN); + + bda = KADDR(0x400); + bda[0x13] = 639; + bda[0x14] = 639>>8; + } + + /* + * parse configuration args from dos file plan9.ini + */ + cp = BOOTARGS; /* where b.com leaves its config */ + cp[BOOTARGSLEN-1] = 0; + + /* + * Strip out '\r', change '\t' -> ' '. + */ + p = cp; + for(q = cp; *q; q++){ + if(*q == '\r') + continue; + if(*q == '\t') + *q = ' '; + *p++ = *q; + } + *p = 0; + + n = getcfields(cp, line, MAXCONF, "\n"); + for(i = 0; i < n; i++){ + if(*line[i] == '#') + continue; + cp = strchr(line[i], '='); + if(cp == 0) + continue; + *cp++ = 0; + if(cp - line[i] >= NAMELEN+1) + *(line[i]+NAMELEN-1) = 0; + confname[nconf] = line[i]; + confval[nconf] = cp; + nconf++; + } +} + + +/* + * Vecinit is the first hook we have into configuring the machine, + * so we do it all here. A pox on special fileserver code. + * We do more in meminit below. + */ +void +vecinit(void) +{ + options(); +} + +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; +} + +char* +getconf(char *name) +{ + int i; + + for(i = 0; i < nconf; i++) + if(cistrcmp(confname[i], name) == 0) + return confval[i]; + return 0; +} + +/* memory map; this kernel will see MAXMEG megabytes of RAM at most. */ +#ifndef MAXMEG +#define MAXMEG 1791 /* 1.75GB-1MB, to avoid overshooting into PCI space */ +#endif + +char mmap[MAXMEG+2]; +Mconf mconf; + +static void +mconfinit(void) +{ + long x, i, j; + ulong ktop; + Mbank *mbp; + uchar *bda; + + /* + * size memory above 1 meg. Kernel sits at 1 meg. We + * only recognize MB size chunks. + */ + memset(mmap, ' ', sizeof(mmap)); + x = 0x12345678; + for(i = 1; i <= MAXMEG; i++){ + /* + * write the first & last word in a megabyte of memory + */ + *mapaddr(KZERO|(i*MB)) = x; + *mapaddr(KZERO|((i+1)*MB-BY2WD)) = x; + + /* + * write the first and last word in all previous megs to + * handle address wrap around + */ + for(j = 1; j < i; j++){ + *mapaddr(KZERO|(j*MB)) = ~x; + *mapaddr(KZERO|((j+1)*MB-BY2WD)) = ~x; + } + + /* + * check for correct value + */ + if(*mapaddr(KZERO|(i*MB)) == x && *mapaddr(KZERO|((i+1)*MB-BY2WD)) == x) + mmap[i] = 'x'; + x += 0x3141526; + } + + /* + * bank[0] goes from the end of the bootstrap structures to ~640k. + * want to use this up first for ialloc because sparemem + * will want a large contiguous chunk. + */ + mbp = mconf.bank; + mbp->base = PADDR(CPU0MACH+BY2PG); + bda = (uchar*)(KZERO|0x400); + mbp->limit = ((bda[0x14]<<8)|bda[0x13])*1024; + mbp++; + + /* + * bank[1] usually goes from the end of kernel bss to the end of memory + */ + ktop = PGROUND((ulong)end); + ktop = PADDR(ktop); + mbp->base = ktop; + for(i = 1; mmap[i] == 'x'; i++) + ; + mbp->limit = i*MB; + mconf.topofmem = mbp->limit; + mbp++; + + /* + * Look for any other chunks of memory. + */ + for(; i <= MAXMEG; i++){ + if(mmap[i] == 'x'){ + mbp->base = i*MB; + for(j = i+1; mmap[j] == 'x'; j++) + ; + mbp->limit = j*MB; + mconf.topofmem = j*MB; + mbp++; + + if((mbp - mconf.bank) >= MAXBANK) + break; + } + } + + mconf.nbank = mbp - mconf.bank; +} + +ulong +meminit(void) +{ + conf.nmach = 1; + mconfinit(); + mmuinit(); + trapinit(); + + /* + * This is not really right, but the port code assumes + * a linear memory array and this is as close as we can + * get to satisfying that. + * Dancing around the 'port' code is all just an ugly hack + * anyway. + */ + return mconf.topofmem; +} + +void +userinit(void (*f)(void), void *arg, char *text) +{ + User *p; + + p = newproc(); + + /* + * Kernel Stack. + * The -4 is because the path sched()->gotolabel()->init0()->f() + * uses a stack location without creating any local space. + */ + p->sched.pc = (ulong)init0; + p->sched.sp = (ulong)p->stack + sizeof(p->stack) - 4; + p->start = f; + p->text = text; + p->arg = arg; + dofilter(p->time+0, C0a, C0b, 1); + dofilter(p->time+1, C1a, C1b, 1); + dofilter(p->time+2, C2a, C2b, 1); + ready(p); +} + +static int useuart; +static void (*intrputs)(char*, int); + +static int +pcgetc(void) +{ + int c; + + if(c = kbdgetc()) + return c; + if(useuart) + return uartgetc(); + return 0; +} + +static void +pcputc(int c) +{ + if(predawn) + cgaputc(c); + if(useuart) + uartputc(c); +} + +static void +pcputs(char* s, int n) +{ + if(!predawn) + cgaputs(s, n); + if(intrputs) + (*intrputs)(s, n); +} + +void +consinit(void (*puts)(char*, int)) +{ + char *p; + int baud, port; + + kbdinit(); + + consgetc = pcgetc; + consputc = pcputc; + consputs = pcputs; + intrputs = puts; + + if((p = getconf("console")) == 0 || cistrcmp(p, "cga") == 0) + return; + + port = strtoul(p, 0, 0); + baud = 0; + if(p = getconf("baud")) + baud = strtoul(p, 0, 0); + if(baud == 0) + baud = 9600; + + uartspecial(port, kbdchar, conschar, baud); + useuart = 1; +} + +void +consreset(void) +{ +} + +void +firmware(void) +{ + char *p; + + /* + * Always called splhi(). + */ + if((p = getconf("reset")) && cistrcmp(p, "manual") == 0){ + predawn = 1; + print("\nHit Reset\n"); + for(;;); + } + pcireset(); + i8042reset(); +} + +int +isaconfig(char *class, int ctlrno, ISAConf *isa) +{ + char cc[NAMELEN], *p, *q, *r; + int n; + + sprint(cc, "%s%d", class, ctlrno); + for(n = 0; n < nconf; n++){ + if(cistrncmp(confname[n], cc, NAMELEN)) + continue; + isa->nopt = 0; + p = confval[n]; + while(*p){ + while(*p == ' ' || *p == '\t') + p++; + if(*p == '\0') + break; + if(cistrncmp(p, "type=", 5) == 0){ + p += 5; + for(q = isa->type; q < &isa->type[NAMELEN-1]; q++){ + if(*p == '\0' || *p == ' ' || *p == '\t') + break; + *q = *p++; + } + *q = '\0'; + } + 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); + else if(isa->nopt < NISAOPT){ + r = isa->opt[isa->nopt]; + while(*p && *p != ' ' && *p != '\t'){ + *r++ = *p++; + if(r-isa->opt[isa->nopt] >= ISAOPTLEN-1) + break; + } + *r = '\0'; + isa->nopt++; + } + while(*p && *p != ' ' && *p != '\t') + p++; + } + return 1; + } + return 0; +} + +void +lockinit(void) +{ +} + +void +launchinit(void) +{ +} + +void +lights(int, int) +{ +} + +/* in assembly language +Float +famd(Float a, int b, int c, int d) +{ + return ((a+b) * c) / d; +} + +ulong +fdf(Float a, int b) +{ + return a / b; +} +*/ diff -Nru /sys/src/fs/pc/pci.c /sys/src/fs/pc/pci.c --- /sys/src/fs/pc/pci.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/pc/pci.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,640 @@ +/* + * PCI support code. + * To do: + * initialise bridge mappings if the PCI BIOS didn't. + */ +#include "all.h" +#include "io.h" + +enum { /* configuration mechanism #1 */ + PciADDR = 0xCF8, /* CONFIG_ADDRESS */ + PciDATA = 0xCFC, /* CONFIG_DATA */ + + /* configuration mechanism #2 */ + PciCSE = 0xCF8, /* configuration space enable */ + PciFORWARD = 0xCFA, /* which bus */ + + MaxFNO = 7, + MaxUBN = 255, +}; + +enum +{ /* command register */ + IOen = (1<<0), + MEMen = (1<<1), + MASen = (1<<2), + MemWrInv = (1<<4), + PErrEn = (1<<6), + SErrEn = (1<<8), +}; + +static Lock pcicfglock; +static Lock pcicfginitlock; +static int pcicfgmode = -1; +static int pcimaxdno; +static Pcidev* pciroot; +static Pcidev* pcilist; +static Pcidev* pcitail; + +static int pcicfgrw32(int, int, int, int); +static int pcicfgrw8(int, int, int, int); + +ulong +pcibarsize(Pcidev *p, int rno) +{ + ulong v, size; + + v = pcicfgrw32(p->tbdf, rno, 0, 1); + pcicfgrw32(p->tbdf, rno, 0xFFFFFFF0, 0); + size = pcicfgrw32(p->tbdf, rno, 0, 1); + if(v & 1) + size |= 0xFFFF0000; + pcicfgrw32(p->tbdf, rno, v, 0); + + return -(size & ~0x0F); +} + +static void +cmd_pcihinv(int argc, char *argv[]) +{ + int i, flags = 0; + + for (i = 1; i < argc; i++) + if (strcmp(argv[i], "-v") == 0) + flags |= 1; + else { + print("unknown pcihinv option %s; options are: -v\n", argv[i]); + return; + } + pcihinv(nil, flags); /* print the whole device tree */ +} + +static int +pciscan(int bno, Pcidev** list) +{ + Pcidev *p, *head, *tail; + int dno, fno, i, hdt, l, maxfno, maxubn, rno, sbn, tbdf, ubn; + static int first = 1; + + maxubn = bno; + head = nil; + tail = nil; + for(dno = 0; dno <= pcimaxdno; dno++){ + maxfno = 0; + for(fno = 0; fno <= maxfno; fno++){ + /* + * For this possible device, form the bus+device+function + * triplet needed to address it and try to read the vendor + * and device ID. If successful, allocate a device struct + * and start to fill it in with some useful information from + * the device's configuration space. + */ + tbdf = MKBUS(BusPCI, bno, dno, fno); + l = pcicfgrw32(tbdf, PciVID, 0, 1); + if(l == 0xFFFFFFFF || l == 0) + continue; + p = ialloc(sizeof(*p), 0); + p->tbdf = tbdf; + p->vid = l; + p->did = l>>16; + + if(pcilist != nil) + pcitail->list = p; + else + pcilist = p; + pcitail = p; + + p->rid = pcicfgr8(p, PciRID); + p->ccrp = pcicfgr8(p, PciCCRp); + p->ccrb = pcicfgr8(p, PciCCRb); + p->pcr = pcicfgr32(p, PciPCR); + /* ccru is uchar in cpu kernel */ + /* p->ccru = pcicfgr8(p, PciCCRu); */ + p->ccru = pcicfgr16(p, PciCCRu); + + p->intl = pcicfgr8(p, PciINTL); + + /* + * If the device is a multi-function device adjust the + * loop count so all possible functions are checked. + */ + hdt = pcicfgr8(p, PciHDT); + if(hdt & 0x80) + maxfno = MaxFNO; + + /* + * If appropriate, read the base address registers + * and work out the sizes. + */ + switch(p->ccru>>8){ + + case 0x01: /* mass storage controller */ + case 0x02: /* network controller */ + case 0x03: /* display controller */ + case 0x04: /* multimedia device */ + case 0x07: /* simple communication controllers */ + case 0x08: /* base system peripherals */ + case 0x09: /* input devices */ + case 0x0A: /* docking stations */ + case 0x0B: /* processors */ + case 0x0C: /* serial bus controllers */ + if((hdt & 0x7F) != 0) + break; + rno = PciBAR0 - 4; + for(i = 0; i < nelem(p->mem); i++){ + rno += 4; + p->mem[i].bar = pcicfgr32(p, rno); + p->mem[i].size = pcibarsize(p, rno); + } + break; + + case 0x00: + case 0x05: /* memory controller */ + case 0x06: /* bridge device */ + default: + break; + } + + if(head != nil) + tail->link = p; + else + head = p; + tail = p; + } + } + + *list = head; + for(p = head; p != nil; p = p->link){ + /* + * Find PCI-PCI bridges and recursively descend the tree. + */ + if(p->ccru != ((0x06<<8)|0x04)) + continue; + + /* + * If the secondary or subordinate bus number is not initialized + * try to do what the PCI BIOS should have done and fill in the + * numbers as the tree is descended. On the way down the subordinate + * bus number is set to the maximum as it's not known how many + * buses are behind this one; the final value is set on the way + * back up. + */ + sbn = pcicfgr8(p, PciSBN); + ubn = pcicfgr8(p, PciUBN); + if(sbn == 0 || ubn == 0){ + sbn = maxubn+1; + /* + * Make sure memory, I/O and master enables are off, + * set the primary, secondary and subordinate bus numbers + * and clear the secondary status before attempting to + * scan the secondary bus. + * + * Initialisation of the bridge should be done here. + */ + pcicfgw32(p, PciPCR, 0xFFFF0000); + l = (MaxUBN<<16)|(sbn<<8)|bno; + pcicfgw32(p, PciPBN, l); + pcicfgw16(p, PciSPSR, 0xFFFF); + maxubn = pciscan(sbn, &p->bridge); + l = (maxubn<<16)|(sbn<<8)|bno; + pcicfgw32(p, PciPBN, l); + } + else{ + maxubn = ubn; + pciscan(sbn, &p->bridge); + } + } + if (first) { + first = 0; + cmd_install("pcihinv", "-- print PCI bus device inventory", + cmd_pcihinv); + } + return maxubn; +} + +static void +pcicfginit(void) +{ + char *p; + int bno; + Pcidev **list; + + lock(&pcicfginitlock); + if(pcicfgmode == -1){ + /* + * Try to determine which PCI configuration mode is implemented. + * Mode2 uses a byte at 0xCF8 and another at 0xCFA; Mode1 uses + * a DWORD at 0xCF8 and another at 0xCFC and will pass through + * any non-DWORD accesses as normal I/O cycles. There shouldn't be + * a device behind these addresses so if Mode2 accesses fail try + * for Mode1 (which is preferred, Mode2 is deprecated). + */ + outb(PciCSE, 0); + if(inb(PciCSE) == 0){ + pcicfgmode = 2; + pcimaxdno = 15; + } + else{ + outl(PciADDR, 0); + if(inl(PciADDR) == 0){ + pcicfgmode = 1; + pcimaxdno = 31; + } + } + + if(pcicfgmode > 0){ + if(p = getconf("*pcimaxdno")) + pcimaxdno = strtoul(p, 0, 0); + + list = &pciroot; + for(bno = 0; bno < 256; bno++){ + bno = pciscan(bno, list); + while(*list) + list = &(*list)->link; + } + + } + } + unlock(&pcicfginitlock); +} + +static int +pcicfgrw8(int tbdf, int rno, int data, int read) +{ + int o, type, x; + + if(pcicfgmode == -1) + pcicfginit(); + + if(BUSBNO(tbdf)) + type = 0x01; + else + type = 0x00; + x = -1; + if(BUSDNO(tbdf) > pcimaxdno) + return x; + + lock(&pcicfglock); + switch(pcicfgmode){ + + case 1: + o = rno & 0x03; + rno &= ~0x03; + outl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type); + if(read) + x = inb(PciDATA+o); + else + outb(PciDATA+o, data); + outl(PciADDR, 0); + break; + + case 2: + outb(PciCSE, 0x80|(BUSFNO(tbdf)<<1)); + outb(PciFORWARD, BUSBNO(tbdf)); + if(read) + x = inb((0xC000|(BUSDNO(tbdf)<<8)) + rno); + else + outb((0xC000|(BUSDNO(tbdf)<<8)) + rno, data); + outb(PciCSE, 0); + break; + } + unlock(&pcicfglock); + + return x; +} + +int +pcicfgr8(Pcidev* pcidev, int rno) +{ + return pcicfgrw8(pcidev->tbdf, rno, 0, 1); +} + +void +pcicfgw8(Pcidev* pcidev, int rno, int data) +{ + pcicfgrw8(pcidev->tbdf, rno, data, 0); +} + +static int +pcicfgrw16(int tbdf, int rno, int data, int read) +{ + int o, type, x; + + if(pcicfgmode == -1) + pcicfginit(); + + if(BUSBNO(tbdf)) + type = 0x01; + else + type = 0x00; + x = -1; + if(BUSDNO(tbdf) > pcimaxdno) + return x; + + lock(&pcicfglock); + switch(pcicfgmode){ + + case 1: + o = rno & 0x02; + rno &= ~0x03; + outl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type); + if(read) + x = ins(PciDATA+o); + else + outs(PciDATA+o, data); + outl(PciADDR, 0); + break; + + case 2: + outb(PciCSE, 0x80|(BUSFNO(tbdf)<<1)); + outb(PciFORWARD, BUSBNO(tbdf)); + if(read) + x = ins((0xC000|(BUSDNO(tbdf)<<8)) + rno); + else + outs((0xC000|(BUSDNO(tbdf)<<8)) + rno, data); + outb(PciCSE, 0); + break; + } + unlock(&pcicfglock); + + return x; +} + +int +pcicfgr16(Pcidev* pcidev, int rno) +{ + return pcicfgrw16(pcidev->tbdf, rno, 0, 1); +} + +void +pcicfgw16(Pcidev* pcidev, int rno, int data) +{ + pcicfgrw16(pcidev->tbdf, rno, data, 0); +} + +static int +pcicfgrw32(int tbdf, int rno, int data, int read) +{ + int type, x; + + if(pcicfgmode == -1) + pcicfginit(); + + if(BUSBNO(tbdf)) + type = 0x01; + else + type = 0x00; + x = -1; + if(BUSDNO(tbdf) > pcimaxdno) + return x; + + lock(&pcicfglock); + switch(pcicfgmode){ + + case 1: + rno &= ~0x03; + outl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type); + if(read) + x = inl(PciDATA); + else + outl(PciDATA, data); + outl(PciADDR, 0); + break; + + case 2: + outb(PciCSE, 0x80|(BUSFNO(tbdf)<<1)); + outb(PciFORWARD, BUSBNO(tbdf)); + if(read) + x = inl((0xC000|(BUSDNO(tbdf)<<8)) + rno); + else + outl((0xC000|(BUSDNO(tbdf)<<8)) + rno, data); + outb(PciCSE, 0); + break; + } + unlock(&pcicfglock); + + return x; +} + +int +pcicfgr32(Pcidev* pcidev, int rno) +{ + return pcicfgrw32(pcidev->tbdf, rno, 0, 1); +} + +void +pcicfgw32(Pcidev* pcidev, int rno, int data) +{ + pcicfgrw32(pcidev->tbdf, rno, data, 0); +} + +void +pciclrmwi(Pcidev* p) +{ + p->pcr &= ~MemWrInv; + pcicfgw16(p, PciPCR, p->pcr); +} + + +Pcidev* +pcimatch(Pcidev* prev, int vid, int did) +{ + if(pcicfgmode == -1) + pcicfginit(); + + if(prev == nil) + prev = pcilist; + else + prev = prev->list; + + while(prev != nil){ + if((vid == 0 || prev->vid == vid) + && (did == 0 || prev->did == did)) + break; + prev = prev->list; + } + return prev; +} + +Pcidev* +pcimatchtbdf(int tbdf) +{ + Pcidev *pcidev; + + if(pcicfgmode == -1) + pcicfginit(); + + for(pcidev = pcilist; pcidev != nil; pcidev = pcidev->list){ + if(pcidev->tbdf == tbdf) + break; + } + return pcidev; +} + +static char * +ccru2name(int ccru) +{ + switch (ccru>>8) { + case 0x01: /* mass storage controller */ + return "disks"; + case 0x02: /* network controller */ + return "net"; /* probably ether */ + case 0x03: /* display controller */ + return "video"; + case 0x04: /* multimedia device */ + return "audio"; + case 0x07: /* simple communication controllers */ + return "serial"; + case 0x08: /* base system peripherals */ + return "basic"; + case 0x09: /* input devices */ + return "input"; + case 0x0A: /* docking stations */ + return "dock"; + case 0x0B: /* processors */ + return "cpu"; + case 0x0C: /* serial bus controllers */ + return "usb"; + case 0x00: + case 0x05: /* memory controller */ + return "memctl"; + case 0x06: /* bridge device */ + return "bridge"; + default: + return "*GOK*"; + } +} + +static char * +vid2name(int vid) +{ + switch (vid) { + case 0x1000: + return "ncr"; + case 0x1002: + return "ati"; + case 0x100b: + return "natsemi"; + case 0x1011: + return "dec"; + case 0x1013: + return "cirrus"; + case 0x1022: + return "amd"; + case 0x1023: + return "cyber?"; + case 0x102b: + return "matrox"; + case 0x102c: + return "hiq"; + case 0x1039: + return "sis"; + case 0x104b: + return "mylex"; + case 0x105a: + return "promise"; + case 0x105d: + return "number9"; + case 0x10a9: + return "sgi"; + case 0x10b7: + return "3com"; + case 0x10c8: + return "neomagic"; /* or magicgraph */ + case 0x10de: + return "nvidia"; + case 0x11ab: + return "marvell"; + case 0x11ad: + return "(pnic?)"; + case 0x121a: + return "voodoo"; + case 0x12ae: + return "alteon"; + case 0x1385: + return "netgear"; + case 0x15ad: + return "vmware"; + case 0x16ec: + return "usrobot"; + case 0x5333: /* "S" "3". har, har. */ + return "s3"; + case 0x8086: + return "intel"; + default: + return "*GOK*"; + } +} + +void +pcihinv(Pcidev* p, ulong flags) +{ + int i; + Pcidev *t; + + if(p == nil) { + p = pciroot; + print("bus dev type "); + if (flags) + print("%7s", ""); + print("vid "); + if (flags) + print("%8s", ""); + print("did intl memory\n"); + } + for(t = p; t != nil; t = t->link) { + print("%d %2d/%d %.4ux", BUSBNO(t->tbdf), BUSDNO(t->tbdf), + BUSFNO(t->tbdf), t->ccru); + if (flags) + print(" %-6s", ccru2name(t->ccru)); + print(" %.4ux", t->vid); + if (flags) + print(" %-7s", vid2name(t->vid)); + print(" %.4ux %2d ", t->did, t->intl); + + for(i = 0; i < nelem(p->mem); i++) { + if(t->mem[i].size == 0) + continue; + print("%d:%.8lux %d ", i, + t->mem[i].bar, t->mem[i].size); + } + print("\n"); + } + while(p != nil) { + if(p->bridge != nil) + pcihinv(p->bridge, flags); + p = p->link; + } +} + +void +pcireset(void) +{ + Pcidev *p; + int pcr; + + if(pcicfgmode == -1) + pcicfginit(); + + for(p = pcilist; p != nil; p = p->list){ + pcr = pcicfgr16(p, PciPSR); + pcicfgw16(p, PciPSR, pcr & ~0x04); + } +} + +void +pcisetbme(Pcidev* p) +{ + int pcr; + + pcr = pcicfgr16(p, PciPCR); + pcr |= 0x04; + pcicfgw16(p, PciPCR, pcr); +} + +void +pciclrbme(Pcidev* p) +{ + p->pcr &= ~MASen; + pcicfgw16(p, PciPCR, p->pcr); +} diff -Nru /sys/src/fs/pc/script.i /sys/src/fs/pc/script.i --- /sys/src/fs/pc/script.i Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/pc/script.i Tue Nov 1 00:00:00 2011 @@ -0,0 +1,772 @@ +unsigned long na_script[] = { + /* extern scsi_id_buf */ + /* extern msg_out_buf */ + /* extern cmd_buf */ + /* extern data_buf */ + /* extern status_buf */ + /* extern msgin_buf */ + /* extern dsa_0 */ + /* extern dsa_1 */ + /* extern dsa_head */ + /* extern ssid_mask */ + /* SIR_MSG_IO_COMPLETE = 0 */ + /* error_not_cmd_complete = 1 */ + /* error_disconnected = 2 */ + /* error_reselected = 3 */ + /* error_unexpected_phase = 4 */ + /* error_weird_message = 5 */ + /* SIR_ERROR_NOT_MSG_IN_AFTER_RESELECT = 6 */ + /* error_not_identify_after_reselect = 7 */ + /* error_too_much_data = 8 */ + /* error_too_little_data = 9 */ + /* SIR_MSG_REJECT = 10 */ + /* SIR_MSG_SDTR = 11 */ + /* SIR_EV_RESPONSE_OK = 12 */ + /* error_sigp_set = 13 */ + /* SIR_EV_PHASE_SWITCH_AFTER_ID = 14 */ + /* SIR_MSG_WDTR = 15 */ + /* SIR_MSG_IGNORE_WIDE_RESIDUE = 16 */ + /* SIR_NOTIFY_DISC = 100 */ + /* SIR_NOTIFY_RESELECT = 101 */ + /* SIR_NOTIFY_MSG_IN = 102 */ + /* SIR_NOTIFY_STATUS = 103 */ + /* SIR_NOTIFY_DUMP = 104 */ + /* SIR_NOTIFY_DUMP2 = 105 */ + /* SIR_NOTIFY_SIGP = 106 */ + /* SIR_NOTIFY_ISSUE = 107 */ + /* SIR_NOTIFY_WAIT_RESELECT = 108 */ + /* SIR_NOTIFY_ISSUE_CHECK = 109 */ + /* SIR_NOTIFY_DUMP_NEXT_CODE = 110 */ + /* SIR_NOTIFY_COMMAND = 111 */ + /* SIR_NOTIFY_DATA_IN = 112 */ + /* SIR_NOTIFY_DATA_OUT = 113 */ + /* SIR_NOTIFY_BLOCK_DATA_IN = 114 */ + /* SIR_NOTIFY_WSR = 115 */ + /* SIR_NOTIFY_LOAD_SYNC = 116 */ + /* SIR_NOTIFY_RESELECTED_ON_SELECT = 117 */ + /* STATE_FREE = 0 */ + /* STATE_ALLOCATED = 1 */ + /* STATE_ISSUE = 2 */ + /* STATE_DISCONNECTED = 3 */ + /* STATE_DONE = 4 */ + /* RESULT_OK = 0 */ + /* MSG_IDENTIFY = 0x80 */ + /* MSG_DISCONNECT = 0x04 */ + /* MSG_SAVE_DATA_POINTER = 0x02 */ + /* MSG_RESTORE_POINTERS = 0x03 */ + /* MSG_IGNORE_WIDE_RESIDUE = 0x23 */ + /* X_MSG = 0x01 */ + /* X_MSG_SDTR = 0x01 */ + /* X_MSG_WDTR = 0x03 */ + /* MSG_REJECT = 0x07 */ + /* BSIZE = 512 */ +/* 0000 */ 0x80880000L, /* jump wait_for_reselection */ +/* 0004 */ 0x00000514L, +/* 0008 */ 0x88880000L, /* call load_sync */ +/* 000c */ 0x0000074cL, +/* 0010 */ 0x60000200L, /* clear target */ +/* 0014 */ 0x00000000L, +/* 0018 */ 0x47000000L, /* select atn from scsi_id_buf, reselected_on_select */ +/* 001c */ 0x000004ecL, +/* 0020 */ 0x878b0000L, /* jump start1, when msg_in */ +/* 0024 */ 0x00000000L, +/* 0028 */ 0x1e000000L, /* move from msg_out_buf, when msg_out */ +/* 002c */ 0x00000001L, +/* 0030 */ 0x868b0000L, /* jump start1, when msg_out */ +/* 0034 */ 0x00fffff0L, +/* 0038 */ 0x82830000L, /* jump to_decisions, when not cmd */ +/* 003c */ 0x000005f0L, +/* 0040 */ 0x60000008L, /* clear atn */ +/* 0044 */ 0x00000000L, +/* 0048 */ 0x1a000000L, /* move from cmd_buf, when cmd */ +/* 004c */ 0x00000002L, +/* 0050 */ 0x81830000L, /* jump to_decisions, when not data_in */ +/* 0054 */ 0x000005d8L, +/* 0058 */ 0xc0000004L, /* move memory 4, state, scratcha */ +/* 005c */ 0x00000678L, +/* 0060 */ 0x00000034L, +/* 0064 */ 0xc0000004L, /* move memory 4, dmaaddr, scratchb */ +/* 0068 */ 0x0000067cL, +/* 006c */ 0x0000005cL, +/* 0070 */ 0x72360000L, /* move scratcha2 to sfbr */ +/* 0074 */ 0x00000000L, +/* 0078 */ 0x808c0000L, /* jump data_in_normal, if 0 */ +/* 007c */ 0x00000078L, +/* 0080 */ 0x29000200L, /* move BSIZE, ptr dmaaddr, when data_in */ +/* 0084 */ 0x0000067cL, +/* 0088 */ 0x7e5d0200L, /* move scratchb1 + BSIZE / 256 to scratchb1 */ +/* 008c */ 0x00000000L, +/* 0090 */ 0x7f5e0000L, /* move scratchb2 + 0 to scratchb2 with carry */ +/* 0094 */ 0x00000000L, +/* 0098 */ 0x7f5f0000L, /* move scratchb3 + 0 to scratchb3 with carry */ +/* 009c */ 0x00000000L, +/* 00a0 */ 0x7e36ff00L, /* move scratcha2 + 255 to scratcha2 */ +/* 00a4 */ 0x00000000L, +/* 00a8 */ 0xc0000004L, /* move memory 4, scratchb, dmaaddr */ +/* 00ac */ 0x0000005cL, +/* 00b0 */ 0x0000067cL, +/* 00b4 */ 0x818b0000L, /* jump data_in_block_loop, when data_in */ +/* 00b8 */ 0x00ffffb4L, +/* 00bc */ 0xc0000004L, /* move memory 4, scratcha, state */ +/* 00c0 */ 0x00000034L, +/* 00c4 */ 0x00000678L, +/* 00c8 */ 0x88880000L, /* call save_state */ +/* 00cc */ 0x000005e0L, +/* 00d0 */ 0x80880000L, /* jump to_decisions */ +/* 00d4 */ 0x00000558L, +/* 00d8 */ 0xc0000004L, /* move memory 4, scratchb, dmaaddr */ +/* 00dc */ 0x0000005cL, +/* 00e0 */ 0x0000067cL, +/* 00e4 */ 0xc0000004L, /* move memory 4, scratcha, state */ +/* 00e8 */ 0x00000034L, +/* 00ec */ 0x00000678L, +/* 00f0 */ 0x80880000L, /* jump to_decisions */ +/* 00f4 */ 0x00000538L, +/* 00f8 */ 0x72370000L, /* move scratcha3 to sfbr */ +/* 00fc */ 0x00000000L, +/* 0100 */ 0x98040000L, /* int error_too_much_data, if not 0 */ +/* 0104 */ 0x00000008L, +/* 0108 */ 0x19000000L, /* move from data_buf, when data_in */ +/* 010c */ 0x00000003L, +/* 0110 */ 0x78370100L, /* move 1 to scratcha3 */ +/* 0114 */ 0x00000000L, +/* 0118 */ 0xc0000004L, /* move memory 4, scratcha, state */ +/* 011c */ 0x00000034L, +/* 0120 */ 0x00000678L, +/* 0124 */ 0x88880000L, /* call save_state */ +/* 0128 */ 0x00000584L, +/* 012c */ 0x80880000L, /* jump post_data_to_decisions */ +/* 0130 */ 0x0000052cL, +/* 0134 */ 0xc0000004L, /* move memory 4, state, scratcha */ +/* 0138 */ 0x00000678L, +/* 013c */ 0x00000034L, +/* 0140 */ 0xc0000004L, /* move memory 4, dmaaddr, scratchb */ +/* 0144 */ 0x0000067cL, +/* 0148 */ 0x0000005cL, +/* 014c */ 0x72360000L, /* move scratcha2 to sfbr */ +/* 0150 */ 0x00000000L, +/* 0154 */ 0x808c0000L, /* jump data_out_normal, if 0 */ +/* 0158 */ 0x0000005cL, +/* 015c */ 0xc0000004L, /* move memory 4, dmaaddr, scratchb */ +/* 0160 */ 0x0000067cL, +/* 0164 */ 0x0000005cL, +/* 0168 */ 0x28000200L, /* move BSIZE, ptr dmaaddr, when data_out */ +/* 016c */ 0x0000067cL, +/* 0170 */ 0x7e5d0200L, /* move scratchb1 + BSIZE / 256 to scratchb1 */ +/* 0174 */ 0x00000000L, +/* 0178 */ 0x7f5e0000L, /* move scratchb2 + 0 to scratchb2 with carry */ +/* 017c */ 0x00000000L, +/* 0180 */ 0x7f5f0000L, /* move scratchb3 + 0 to scratchb3 with carry */ +/* 0184 */ 0x00000000L, +/* 0188 */ 0x7e36ff00L, /* move scratcha2 + 255 to scratcha2 */ +/* 018c */ 0x00000000L, +/* 0190 */ 0xc0000004L, /* move memory 4, scratchb, dmaaddr */ +/* 0194 */ 0x0000005cL, +/* 0198 */ 0x0000067cL, +/* 019c */ 0x808b0000L, /* jump data_out_block_loop, when data_out */ +/* 01a0 */ 0x00ffffa8L, +/* 01a4 */ 0xc0000004L, /* move memory 4, scratcha, state */ +/* 01a8 */ 0x00000034L, +/* 01ac */ 0x00000678L, +/* 01b0 */ 0x80880000L, /* jump to_decisions */ +/* 01b4 */ 0x00000478L, +/* 01b8 */ 0x72370000L, /* move scratcha3 to sfbr */ +/* 01bc */ 0x00000000L, +/* 01c0 */ 0x98040000L, /* int error_too_little_data, if not 0 */ +/* 01c4 */ 0x00000009L, +/* 01c8 */ 0x18000000L, /* move from data_buf, when data_out */ +/* 01cc */ 0x00000003L, +/* 01d0 */ 0x78370100L, /* move 1 to scratcha3 */ +/* 01d4 */ 0x00000000L, +/* 01d8 */ 0xc0000004L, /* move memory 4, scratcha, state */ +/* 01dc */ 0x00000034L, +/* 01e0 */ 0x00000678L, +/* 01e4 */ 0x88880000L, /* call save_state */ +/* 01e8 */ 0x000004c4L, +/* 01ec */ 0x80880000L, /* jump post_data_to_decisions */ +/* 01f0 */ 0x0000046cL, +/* 01f4 */ 0x1b000000L, /* move from status_buf, when status */ +/* 01f8 */ 0x00000004L, +/* 01fc */ 0x9f030000L, /* int error_unexpected_phase, when not msg_in */ +/* 0200 */ 0x00000004L, +/* 0204 */ 0x0f000001L, /* move 1, scratcha, when msg_in */ +/* 0208 */ 0x00000034L, +/* 020c */ 0x808c0007L, /* jump rejected, if MSG_REJECT */ +/* 0210 */ 0x00000088L, +/* 0214 */ 0x808c0004L, /* jump disconnected, if MSG_DISCONNECT */ +/* 0218 */ 0x00000298L, +/* 021c */ 0x808c0002L, /* jump msg_in_skip, if MSG_SAVE_DATA_POINTER */ +/* 0220 */ 0x00000090L, +/* 0224 */ 0x808c0003L, /* jump msg_in_skip, if MSG_RESTORE_POINTERS */ +/* 0228 */ 0x00000088L, +/* 022c */ 0x808c0023L, /* jump ignore_wide, if MSG_IGNORE_WIDE_RESIDUE */ +/* 0230 */ 0x000001f0L, +/* 0234 */ 0x808c0001L, /* jump extended, if X_MSG */ +/* 0238 */ 0x00000088L, +/* 023c */ 0x98040000L, /* int error_not_cmd_complete, if not 0 */ +/* 0240 */ 0x00000001L, +/* 0244 */ 0x7c027e00L, /* move scntl2&0x7e to scntl2 */ +/* 0248 */ 0x00000000L, +/* 024c */ 0x60000040L, /* clear ack */ +/* 0250 */ 0x00000000L, +/* 0254 */ 0x48000000L, /* wait disconnect */ +/* 0258 */ 0x00000000L, +/* 025c */ 0xc0000004L, /* move memory 4, state, scratcha */ +/* 0260 */ 0x00000678L, +/* 0264 */ 0x00000034L, +/* 0268 */ 0x78340400L, /* move STATE_DONE to scratcha0 */ +/* 026c */ 0x00000000L, +/* 0270 */ 0x78350000L, /* move RESULT_OK to scratcha1 */ +/* 0274 */ 0x00000000L, +/* 0278 */ 0xc0000004L, /* move memory 4, scratcha, state */ +/* 027c */ 0x00000034L, +/* 0280 */ 0x00000678L, +/* 0284 */ 0x88880000L, /* call save_state */ +/* 0288 */ 0x00000424L, +/* 028c */ 0x98180000L, /* intfly 0 */ +/* 0290 */ 0x00000000L, +/* 0294 */ 0x80880000L, /* jump issue_check */ +/* 0298 */ 0x0000043cL, +/* 029c */ 0x98080000L, /* int SIR_MSG_REJECT */ +/* 02a0 */ 0x0000000aL, +/* 02a4 */ 0x60000040L, /* clear ack */ +/* 02a8 */ 0x00000000L, +/* 02ac */ 0x80880000L, /* jump to_decisions */ +/* 02b0 */ 0x0000037cL, +/* 02b4 */ 0x60000040L, /* clear ack */ +/* 02b8 */ 0x00000000L, +/* 02bc */ 0x80880000L, /* jump to_decisions */ +/* 02c0 */ 0x0000036cL, +/* 02c4 */ 0x60000040L, /* clear ack */ +/* 02c8 */ 0x00000000L, +/* 02cc */ 0x9f030000L, /* int error_unexpected_phase, when not msg_in */ +/* 02d0 */ 0x00000004L, +/* 02d4 */ 0x0f000001L, /* move 1, scratcha1, when msg_in */ +/* 02d8 */ 0x00000035L, +/* 02dc */ 0x808c0003L, /* jump ext_3, if 3 */ +/* 02e0 */ 0x00000030L, +/* 02e4 */ 0x808c0002L, /* jump ext_2, if 2 */ +/* 02e8 */ 0x00000098L, +/* 02ec */ 0x98040001L, /* int error_weird_message, if not 1 */ +/* 02f0 */ 0x00000005L, +/* 02f4 */ 0x60000040L, /* clear ack */ +/* 02f8 */ 0x00000000L, +/* 02fc */ 0x9f030000L, /* int error_unexpected_phase, when not msg_in */ +/* 0300 */ 0x00000004L, +/* 0304 */ 0x0f000001L, /* move 1, scratcha1, when msg_in */ +/* 0308 */ 0x00000035L, +/* 030c */ 0x80880000L, /* jump ext_done */ +/* 0310 */ 0x000000c8L, +/* 0314 */ 0x60000040L, /* ext_3: clear ack */ +/* 0318 */ 0x00000000L, +/* 031c */ 0x9f030000L, /* int error_unexpected_phase, when not msg_in */ +/* 0320 */ 0x00000004L, +/* 0324 */ 0x0f000001L, /* move 1, scratcha1, when msg_in */ +/* 0328 */ 0x00000035L, +/* 032c */ 0x60000040L, /* clear ack */ +/* 0330 */ 0x00000000L, +/* 0334 */ 0x9f030000L, /* int error_unexpected_phase, when not msg_in */ +/* 0338 */ 0x00000004L, +/* 033c */ 0x0f000001L, /* move 1, scratcha2, when msg_in */ +/* 0340 */ 0x00000036L, +/* 0344 */ 0x60000040L, /* clear ack */ +/* 0348 */ 0x00000000L, +/* 034c */ 0x9f030000L, /* int error_unexpected_phase, when not msg_in */ +/* 0350 */ 0x00000004L, +/* 0354 */ 0x0f000001L, /* move 1, scratcha3, when msg_in */ +/* 0358 */ 0x00000037L, +/* 035c */ 0x72350000L, /* move scratcha1 to sfbr */ +/* 0360 */ 0x00000000L, +/* 0364 */ 0x80840001L, /* jump ext_done, if not X_MSG_SDTR */ +/* 0368 */ 0x00000070L, +/* 036c */ 0x98080000L, /* sdtr: int SIR_MSG_SDTR */ +/* 0370 */ 0x0000000bL, +/* 0374 */ 0x60000040L, /* clear ack */ +/* 0378 */ 0x00000000L, +/* 037c */ 0x80880000L, /* jump to_decisions */ +/* 0380 */ 0x000002acL, +/* 0384 */ 0x60000040L, /* ext_2: clear ack */ +/* 0388 */ 0x00000000L, +/* 038c */ 0x9f030000L, /* int error_unexpected_phase, when not msg_in */ +/* 0390 */ 0x00000004L, +/* 0394 */ 0x0f000001L, /* move 1, scratcha1, when msg_in */ +/* 0398 */ 0x00000035L, +/* 039c */ 0x60000040L, /* clear ack */ +/* 03a0 */ 0x00000000L, +/* 03a4 */ 0x9f030000L, /* int error_unexpected_phase, when not msg_in */ +/* 03a8 */ 0x00000004L, +/* 03ac */ 0x0f000001L, /* move 1, scratcha2, when msg_in */ +/* 03b0 */ 0x00000036L, +/* 03b4 */ 0x72350000L, /* move scratcha1 to sfbr */ +/* 03b8 */ 0x00000000L, +/* 03bc */ 0x80840003L, /* jump ext_done, if not X_MSG_WDTR */ +/* 03c0 */ 0x00000018L, +/* 03c4 */ 0x98080000L, /* wdtr: int SIR_MSG_WDTR */ +/* 03c8 */ 0x0000000fL, +/* 03cc */ 0x60000040L, /* clear ack */ +/* 03d0 */ 0x00000000L, +/* 03d4 */ 0x80880000L, /* jump to_decisions */ +/* 03d8 */ 0x00000254L, +/* 03dc */ 0x58000008L, /* set atn */ +/* 03e0 */ 0x00000000L, +/* 03e4 */ 0x60000040L, /* clear ack */ +/* 03e8 */ 0x00000000L, +/* 03ec */ 0x78340700L, /* move MSG_REJECT to scratcha */ +/* 03f0 */ 0x00000000L, +/* 03f4 */ 0x9e030000L, /* int error_unexpected_phase, when not msg_out */ +/* 03f8 */ 0x00000004L, +/* 03fc */ 0x60000008L, /* clear atn */ +/* 0400 */ 0x00000000L, +/* 0404 */ 0x0e000001L, /* move 1, scratcha, when msg_out */ +/* 0408 */ 0x00000034L, +/* 040c */ 0x60000040L, /* clear ack */ +/* 0410 */ 0x00000000L, +/* 0414 */ 0x868b0000L, /* jump reject, when msg_out */ +/* 0418 */ 0x00ffffc0L, +/* 041c */ 0x80880000L, /* jump to_decisions */ +/* 0420 */ 0x0000020cL, +/* 0424 */ 0x60000040L, /* clear ack */ +/* 0428 */ 0x00000000L, +/* 042c */ 0x9f030000L, /* int error_unexpected_phase, when not msg_in */ +/* 0430 */ 0x00000004L, +/* 0434 */ 0x0f000001L, /* move 1, scratcha1, when msg_in */ +/* 0438 */ 0x00000035L, +/* 043c */ 0x98080000L, /* int SIR_MSG_IGNORE_WIDE_RESIDUE */ +/* 0440 */ 0x00000010L, +/* 0444 */ 0x60000040L, /* clear ack */ +/* 0448 */ 0x00000000L, +/* 044c */ 0x80880000L, /* jump to_decisions */ +/* 0450 */ 0x000001dcL, +/* 0454 */ 0x58000008L, /* set atn */ +/* 0458 */ 0x00000000L, +/* 045c */ 0x60000040L, /* clear ack */ +/* 0460 */ 0x00000000L, +/* 0464 */ 0x9e030000L, /* int error_unexpected_phase, when not msg_out */ +/* 0468 */ 0x00000004L, +/* 046c */ 0x1e000000L, /* move from msg_out_buf, when msg_out */ +/* 0470 */ 0x00000001L, +/* 0474 */ 0x868b0000L, /* jump response_repeat, when msg_out */ +/* 0478 */ 0x00fffff0L, +/* 047c */ 0x878b0000L, /* jump response_msg_in, when msg_in */ +/* 0480 */ 0x00000010L, +/* 0484 */ 0x98080000L, /* int SIR_EV_RESPONSE_OK */ +/* 0488 */ 0x0000000cL, +/* 048c */ 0x80880000L, /* jump to_decisions */ +/* 0490 */ 0x0000019cL, +/* 0494 */ 0x0f000001L, /* move 1, scratcha, when msg_in */ +/* 0498 */ 0x00000034L, +/* 049c */ 0x808c0007L, /* jump rejected, if MSG_REJECT */ +/* 04a0 */ 0x00fffdf8L, +/* 04a4 */ 0x98080000L, /* int SIR_EV_RESPONSE_OK */ +/* 04a8 */ 0x0000000cL, +/* 04ac */ 0x80880000L, /* jump msg_in_not_reject */ +/* 04b0 */ 0x00fffd60L, +/* 04b4 */ 0x7c027e00L, /* move scntl2&0x7e to scntl2 */ +/* 04b8 */ 0x00000000L, +/* 04bc */ 0x60000040L, /* clear ack */ +/* 04c0 */ 0x00000000L, +/* 04c4 */ 0x48000000L, /* wait disconnect */ +/* 04c8 */ 0x00000000L, +/* 04cc */ 0xc0000004L, /* move memory 4, state, scratcha */ +/* 04d0 */ 0x00000678L, +/* 04d4 */ 0x00000034L, +/* 04d8 */ 0x78340300L, /* move STATE_DISCONNECTED to scratcha0 */ +/* 04dc */ 0x00000000L, +/* 04e0 */ 0xc0000004L, /* move memory 4, scratcha, state */ +/* 04e4 */ 0x00000034L, +/* 04e8 */ 0x00000678L, +/* 04ec */ 0x88880000L, /* call save_state */ +/* 04f0 */ 0x000001bcL, +/* 04f4 */ 0x74020100L, /* move scntl2&0x01 to sfbr */ +/* 04f8 */ 0x00000000L, +/* 04fc */ 0x98040000L, /* int SIR_NOTIFY_WSR, if not 0 */ +/* 0500 */ 0x00000073L, +/* 0504 */ 0x80880000L, /* jump issue_check */ +/* 0508 */ 0x000001ccL, +/* 050c */ 0x98080000L, /* int SIR_NOTIFY_RESELECTED_ON_SELECT */ +/* 0510 */ 0x00000075L, +/* 0514 */ 0x80880000L, /* jump reselected */ +/* 0518 */ 0x00000008L, +/* 051c */ 0x54000000L, /* wait reselect sigp_set */ +/* 0520 */ 0x000001acL, +/* 0524 */ 0x60000200L, /* clear target */ +/* 0528 */ 0x00000000L, +/* 052c */ 0x9f030000L, /* int SIR_ERROR_NOT_MSG_IN_AFTER_RESELECT, when not msg_in */ +/* 0530 */ 0x00000006L, +/* 0534 */ 0x0f000001L, /* move 1, scratchb, when msg_in */ +/* 0538 */ 0x0000005cL, +/* 053c */ 0x98041f80L, /* int error_not_identify_after_reselect, if not MSG_IDENTIFY and mask 0x1f */ +/* 0540 */ 0x00000007L, +/* 0544 */ 0xc0000004L, /* move memory 4, dsa_head, dsa */ +/* 0548 */ 0x00000008L, +/* 054c */ 0x00000010L, +/* 0550 */ 0x72100000L, /* move dsa0 to sfbr */ +/* 0554 */ 0x00000000L, +/* 0558 */ 0x80840000L, /* jump find_dsa_1, if not 0 */ +/* 055c */ 0x00000030L, +/* 0560 */ 0x72110000L, /* move dsa1 to sfbr */ +/* 0564 */ 0x00000000L, +/* 0568 */ 0x80840000L, /* jump find_dsa_1, if not 0 */ +/* 056c */ 0x00000020L, +/* 0570 */ 0x72120000L, /* move dsa2 to sfbr */ +/* 0574 */ 0x00000000L, +/* 0578 */ 0x80840000L, /* jump find_dsa_1, if not 0 */ +/* 057c */ 0x00000010L, +/* 0580 */ 0x72130000L, /* move dsa3 to sfbr */ +/* 0584 */ 0x00000000L, +/* 0588 */ 0x980c0000L, /* int error_reselected, if 0 */ +/* 058c */ 0x00000003L, +/* 0590 */ 0x88880000L, /* call load_state */ +/* 0594 */ 0x000000f8L, +/* 0598 */ 0xc0000004L, /* move memory 4, state, scratcha */ +/* 059c */ 0x00000678L, +/* 05a0 */ 0x00000034L, +/* 05a4 */ 0x72340000L, /* move scratcha0 to sfbr */ +/* 05a8 */ 0x00000000L, +/* 05ac */ 0x80840003L, /* jump find_dsa_next, if not STATE_DISCONNECTED */ +/* 05b0 */ 0x00000038L, +/* 05b4 */ 0x740a0900L, /* move ssid & ssid_mask to sfbr */ +/* 05b8 */ 0x00000000L, +/* 05bc */ 0xc0000001L, /* move memory 1, targ, find_dsa_smc1 */ +/* 05c0 */ 0x00000680L, +/* 05c4 */ 0x000005c8L, +/* 05c8 */ 0x808400ffL, /* jump find_dsa_next, if not 255 */ +/* 05cc */ 0x0000001cL, +/* 05d0 */ 0xc0000001L, /* move memory 1, lun, find_dsa_smc2 */ +/* 05d4 */ 0x00000684L, +/* 05d8 */ 0x000005e4L, +/* 05dc */ 0x725c0000L, /* move scratchb0 to sfbr */ +/* 05e0 */ 0x00000000L, +/* 05e4 */ 0x808cf8ffL, /* jump reload_sync, if 255 and mask ~7 */ +/* 05e8 */ 0x00000034L, +/* 05ec */ 0xc0000004L, /* move memory 4, next, dsa */ +/* 05f0 */ 0x0000068cL, +/* 05f4 */ 0x00000010L, +/* 05f8 */ 0x80880000L, /* jump find_dsa_loop */ +/* 05fc */ 0x00ffff50L, +/* 0600 */ 0x60000008L, /* clear atn */ +/* 0604 */ 0x00000000L, +/* 0608 */ 0x878b0000L, /* jump msg_in_phase, when msg_in */ +/* 060c */ 0x00fffbf4L, +/* 0610 */ 0x98080000L, /* int SIR_MSG_REJECT */ +/* 0614 */ 0x0000000aL, +/* 0618 */ 0x80880000L, /* jump to_decisions */ +/* 061c */ 0x00000010L, +/* 0620 */ 0x88880000L, /* call load_sync */ +/* 0624 */ 0x00000134L, +/* 0628 */ 0x60000040L, /* clear ack */ +/* 062c */ 0x00000000L, +/* 0630 */ 0x818b0000L, /* jump data_in_phase, when data_in */ +/* 0634 */ 0x00fffa20L, +/* 0638 */ 0x828a0000L, /* jump cmd_phase, if cmd */ +/* 063c */ 0x00fffa00L, +/* 0640 */ 0x808a0000L, /* jump data_out_phase, if data_out */ +/* 0644 */ 0x00fffaecL, +/* 0648 */ 0x838a0000L, /* jump status_phase, if status */ +/* 064c */ 0x00fffba4L, +/* 0650 */ 0x878a0000L, /* jump msg_in_phase, if msg_in */ +/* 0654 */ 0x00fffbacL, +/* 0658 */ 0x98080000L, /* int error_unexpected_phase */ +/* 065c */ 0x00000004L, +/* 0660 */ 0x838b0000L, /* jump status_phase, when status */ +/* 0664 */ 0x00fffb8cL, +/* 0668 */ 0x878a0000L, /* jump msg_in_phase, if msg_in */ +/* 066c */ 0x00fffb94L, +/* 0670 */ 0x98080000L, /* int error_unexpected_phase */ +/* 0674 */ 0x00000004L, +/* 0678 */ 0x00000000L, /* state: defw 0 */ +/* 067c */ 0x00000000L, /* dmaaddr: defw 0 */ +/* 0680 */ 0x00000000L, /* targ: defw 0 */ +/* 0684 */ 0x00000000L, /* lun: defw 0 */ +/* 0688 */ 0x00000000L, /* sync: defw 0 */ +/* 068c */ 0x00000000L, /* next: defw 0 */ + /* dsa_load_len = dsa_load_end - dsa_copy */ + /* dsa_save_len = dsa_save_end - dsa_copy */ +/* 0690 */ 0xc0000004L, /* move memory 4, dsa, load_state_smc0 + 4 */ +/* 0694 */ 0x00000010L, +/* 0698 */ 0x000006a0L, +/* 069c */ 0xc0000018L, /* move memory dsa_load_len, 0, dsa_copy */ +/* 06a0 */ 0x00000000L, +/* 06a4 */ 0x00000678L, +/* 06a8 */ 0x90080000L, /* return */ +/* 06ac */ 0x00000000L, +/* 06b0 */ 0xc0000004L, /* move memory 4, dsa, save_state_smc0 + 8 */ +/* 06b4 */ 0x00000010L, +/* 06b8 */ 0x000006c4L, +/* 06bc */ 0xc0000008L, /* move memory dsa_save_len, dsa_copy, 0 */ +/* 06c0 */ 0x00000678L, +/* 06c4 */ 0x00000000L, +/* 06c8 */ 0x90080000L, /* return */ +/* 06cc */ 0x00000000L, +/* 06d0 */ 0x721a0000L, /* move ctest2 to sfbr */ +/* 06d4 */ 0x00000000L, +/* 06d8 */ 0xc0000004L, /* move memory 4, dsa_head, dsa */ +/* 06dc */ 0x00000008L, +/* 06e0 */ 0x00000010L, +/* 06e4 */ 0x72100000L, /* move dsa0 to sfbr */ +/* 06e8 */ 0x00000000L, +/* 06ec */ 0x80840000L, /* jump issue_check_1, if not 0 */ +/* 06f0 */ 0x00000030L, +/* 06f4 */ 0x72110000L, /* move dsa1 to sfbr */ +/* 06f8 */ 0x00000000L, +/* 06fc */ 0x80840000L, /* jump issue_check_1, if not 0 */ +/* 0700 */ 0x00000020L, +/* 0704 */ 0x72120000L, /* move dsa2 to sfbr */ +/* 0708 */ 0x00000000L, +/* 070c */ 0x80840000L, /* jump issue_check_1, if not 0 */ +/* 0710 */ 0x00000010L, +/* 0714 */ 0x72130000L, /* move dsa3 to sfbr */ +/* 0718 */ 0x00000000L, +/* 071c */ 0x808c0000L, /* jump wait_for_reselection, if 0 */ +/* 0720 */ 0x00fffdf8L, +/* 0724 */ 0x88880000L, /* call load_state */ +/* 0728 */ 0x00ffff64L, +/* 072c */ 0xc0000004L, /* move memory 4, state, scratcha */ +/* 0730 */ 0x00000678L, +/* 0734 */ 0x00000034L, +/* 0738 */ 0x72340000L, /* move scratcha0 to sfbr */ +/* 073c */ 0x00000000L, +/* 0740 */ 0x808c0002L, /* jump start, if STATE_ISSUE */ +/* 0744 */ 0x00fff8c0L, +/* 0748 */ 0xc0000004L, /* move memory 4, next, dsa */ +/* 074c */ 0x0000068cL, +/* 0750 */ 0x00000010L, +/* 0754 */ 0x80880000L, /* jump issue_check_loop */ +/* 0758 */ 0x00ffff88L, +/* 075c */ 0xc0000004L, /* move memory 4, sync, scratcha */ +/* 0760 */ 0x00000688L, +/* 0764 */ 0x00000034L, +/* 0768 */ 0x72340000L, /* move scratcha0 to sfbr */ +/* 076c */ 0x00000000L, +/* 0770 */ 0x6a030000L, /* move sfbr to scntl3 */ +/* 0774 */ 0x00000000L, +/* 0778 */ 0x72350000L, /* move scratcha1 to sfbr */ +/* 077c */ 0x00000000L, +/* 0780 */ 0x6a050000L, /* move sfbr to sxfer */ +/* 0784 */ 0x00000000L, +/* 0788 */ 0x90080000L, /* return */ +/* 078c */ 0x00000000L, +}; + +#define NA_SCRIPT_SIZE 484 + +struct na_patch na_patches[] = { + { 0x0006, 5 }, /* 00000018 */ + { 0x000b, 4 }, /* 0000002c */ + { 0x0013, 4 }, /* 0000004c */ + { 0x0017, 1 }, /* 0000005c */ + { 0x0018, 2 }, /* 00000060 */ + { 0x001a, 1 }, /* 00000068 */ + { 0x001b, 2 }, /* 0000006c */ + { 0x0021, 1 }, /* 00000084 */ + { 0x002b, 2 }, /* 000000ac */ + { 0x002c, 1 }, /* 000000b0 */ + { 0x0030, 2 }, /* 000000c0 */ + { 0x0031, 1 }, /* 000000c4 */ + { 0x0037, 2 }, /* 000000dc */ + { 0x0038, 1 }, /* 000000e0 */ + { 0x003a, 2 }, /* 000000e8 */ + { 0x003b, 1 }, /* 000000ec */ + { 0x0043, 4 }, /* 0000010c */ + { 0x0047, 2 }, /* 0000011c */ + { 0x0048, 1 }, /* 00000120 */ + { 0x004e, 1 }, /* 00000138 */ + { 0x004f, 2 }, /* 0000013c */ + { 0x0051, 1 }, /* 00000144 */ + { 0x0052, 2 }, /* 00000148 */ + { 0x0058, 1 }, /* 00000160 */ + { 0x0059, 2 }, /* 00000164 */ + { 0x005b, 1 }, /* 0000016c */ + { 0x0065, 2 }, /* 00000194 */ + { 0x0066, 1 }, /* 00000198 */ + { 0x006a, 2 }, /* 000001a8 */ + { 0x006b, 1 }, /* 000001ac */ + { 0x0073, 4 }, /* 000001cc */ + { 0x0077, 2 }, /* 000001dc */ + { 0x0078, 1 }, /* 000001e0 */ + { 0x007e, 4 }, /* 000001f8 */ + { 0x0082, 2 }, /* 00000208 */ + { 0x0098, 1 }, /* 00000260 */ + { 0x0099, 2 }, /* 00000264 */ + { 0x009f, 2 }, /* 0000027c */ + { 0x00a0, 1 }, /* 00000280 */ + { 0x00b6, 2 }, /* 000002d8 */ + { 0x00c2, 2 }, /* 00000308 */ + { 0x00ca, 2 }, /* 00000328 */ + { 0x00d0, 2 }, /* 00000340 */ + { 0x00d6, 2 }, /* 00000358 */ + { 0x00e6, 2 }, /* 00000398 */ + { 0x00ec, 2 }, /* 000003b0 */ + { 0x0102, 2 }, /* 00000408 */ + { 0x010e, 2 }, /* 00000438 */ + { 0x011c, 4 }, /* 00000470 */ + { 0x0126, 2 }, /* 00000498 */ + { 0x0134, 1 }, /* 000004d0 */ + { 0x0135, 2 }, /* 000004d4 */ + { 0x0139, 2 }, /* 000004e4 */ + { 0x013a, 1 }, /* 000004e8 */ + { 0x014e, 2 }, /* 00000538 */ + { 0x0152, 4 }, /* 00000548 */ + { 0x0153, 2 }, /* 0000054c */ + { 0x0167, 1 }, /* 0000059c */ + { 0x0168, 2 }, /* 000005a0 */ + { 0x016d, 3 }, /* 000005b4 */ + { 0x0170, 1 }, /* 000005c0 */ + { 0x0171, 1 }, /* 000005c4 */ + { 0x0175, 1 }, /* 000005d4 */ + { 0x0176, 1 }, /* 000005d8 */ + { 0x017c, 1 }, /* 000005f0 */ + { 0x017d, 2 }, /* 000005f4 */ + { 0x01a5, 2 }, /* 00000694 */ + { 0x01a6, 1 }, /* 00000698 */ + { 0x01a9, 1 }, /* 000006a4 */ + { 0x01ad, 2 }, /* 000006b4 */ + { 0x01ae, 1 }, /* 000006b8 */ + { 0x01b0, 1 }, /* 000006c0 */ + { 0x01b7, 4 }, /* 000006dc */ + { 0x01b8, 2 }, /* 000006e0 */ + { 0x01cc, 1 }, /* 00000730 */ + { 0x01cd, 2 }, /* 00000734 */ + { 0x01d3, 1 }, /* 0000074c */ + { 0x01d4, 2 }, /* 00000750 */ + { 0x01d8, 1 }, /* 00000760 */ + { 0x01d9, 2 }, /* 00000764 */ +}; +#define NA_PATCHES 80 + +enum na_external { + X_scsi_id_buf, + X_msg_out_buf, + X_cmd_buf, + X_data_buf, + X_status_buf, + X_msgin_buf, + X_dsa_0, + X_dsa_1, + X_dsa_head, + X_ssid_mask, +}; + +enum { + E_issue_check_next = 1864, + E_issue_check_1 = 1828, + E_issue_check_loop = 1764, + E_save_state_smc0 = 1724, + E_load_state_smc0 = 1692, + E_dsa_load_end = 1680, + E_sync = 1672, + E_dsa_save_end = 1664, + E_dsa_copy = 1656, + E_id_out_mismatch_recover = 1536, + E_next = 1676, + E_reload_sync = 1568, + E_find_dsa_smc2 = 1508, + E_lun = 1668, + E_find_dsa_smc1 = 1480, + E_targ = 1664, + E_find_dsa_next = 1516, + E_load_state = 1680, + E_find_dsa_1 = 1424, + E_find_dsa_loop = 1360, + E_find_dsa = 1348, + E_sigp_set = 1744, + E_reselected = 1316, + E_wsr_check = 1268, + E_response_msg_in = 1172, + E_response_repeat = 1132, + E_response = 1108, + E_reject = 988, + E_wdtr = 964, + E_sdtr = 876, + E_ext_done = 988, + E_ext_1 = 756, + E_ext_2 = 900, + E_ext_3 = 788, + E_issue_check = 1752, + E_extended = 708, + E_ignore_wide = 1060, + E_msg_in_skip = 692, + E_disconnected = 1204, + E_msg_in_not_reject = 532, + E_rejected = 668, + E_msg_in_phase = 516, + E_status_phase = 500, + E_data_out_mismatch = 464, + E_data_out_block_mismatch = 368, + E_data_out_normal = 440, + E_data_out_block_loop = 332, + E_data_out_phase = 308, + E_post_data_to_decisions = 1632, + E_data_in_mismatch = 272, + E_data_block_mismatch_recover = 216, + E_save_state = 1712, + E_data_in_block_mismatch = 136, + E_data_in_normal = 248, + E_data_in_block_loop = 112, + E_dmaaddr = 1660, + E_state = 1656, + E_data_in_phase = 88, + E_cmd_out_mismatch = 80, + E_cmd_phase = 64, + E_to_decisions = 1584, + E_id_out_mismatch = 48, + E_start1 = 40, + E_reselected_on_select = 1292, + E_load_sync = 1884, + E_start = 8, + E_wait_for_reselection = 1308, + E_idle = 0, +}; +#define A_dsa_save_len 8 +#define A_dsa_load_len 24 +#define A_BSIZE 512 +#define A_MSG_REJECT 7 +#define A_X_MSG_WDTR 3 +#define A_X_MSG_SDTR 1 +#define A_X_MSG 1 +#define A_MSG_IGNORE_WIDE_RESIDUE 35 +#define A_MSG_RESTORE_POINTERS 3 +#define A_MSG_SAVE_DATA_POINTER 2 +#define A_MSG_DISCONNECT 4 +#define A_MSG_IDENTIFY 128 +#define A_RESULT_OK 0 +#define A_STATE_DONE 4 +#define A_STATE_DISCONNECTED 3 +#define A_STATE_ISSUE 2 +#define A_STATE_ALLOCATED 1 +#define A_STATE_FREE 0 +#define A_SIR_NOTIFY_RESELECTED_ON_SELECT 117 +#define A_SIR_NOTIFY_LOAD_SYNC 116 +#define A_SIR_NOTIFY_WSR 115 +#define A_SIR_NOTIFY_BLOCK_DATA_IN 114 +#define A_SIR_NOTIFY_DATA_OUT 113 +#define A_SIR_NOTIFY_DATA_IN 112 +#define A_SIR_NOTIFY_COMMAND 111 +#define A_SIR_NOTIFY_DUMP_NEXT_CODE 110 +#define A_SIR_NOTIFY_ISSUE_CHECK 109 +#define A_SIR_NOTIFY_WAIT_RESELECT 108 +#define A_SIR_NOTIFY_ISSUE 107 +#define A_SIR_NOTIFY_SIGP 106 +#define A_SIR_NOTIFY_DUMP2 105 +#define A_SIR_NOTIFY_DUMP 104 +#define A_SIR_NOTIFY_STATUS 103 +#define A_SIR_NOTIFY_MSG_IN 102 +#define A_SIR_NOTIFY_RESELECT 101 +#define A_SIR_NOTIFY_DISC 100 +#define A_SIR_MSG_IGNORE_WIDE_RESIDUE 16 +#define A_SIR_MSG_WDTR 15 +#define A_SIR_EV_PHASE_SWITCH_AFTER_ID 14 +#define A_error_sigp_set 13 +#define A_SIR_EV_RESPONSE_OK 12 +#define A_SIR_MSG_SDTR 11 +#define A_SIR_MSG_REJECT 10 +#define A_error_too_little_data 9 +#define A_error_too_much_data 8 +#define A_error_not_identify_after_reselect 7 +#define A_SIR_ERROR_NOT_MSG_IN_AFTER_RESELECT 6 +#define A_error_weird_message 5 +#define A_error_unexpected_phase 4 +#define A_error_reselected 3 +#define A_error_disconnected 2 +#define A_error_not_cmd_complete 1 +#define A_SIR_MSG_IO_COMPLETE 0 diff -Nru /sys/src/fs/pc/scsi.c /sys/src/fs/pc/scsi.c --- /sys/src/fs/pc/scsi.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/pc/scsi.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,416 @@ +#include "all.h" +#include "io.h" +#include "mem.h" + +extern Scsiio buslogic(int, ISAConf*); +extern Scsiio ncr53c8xxreset(int, ISAConf*); + +static struct { + char* type; + Scsiio (*reset)(int, ISAConf*); +} scsictlr[] = { + { "aha1542", buslogic, }, + { "buslogic", buslogic, }, + { "mylex", buslogic, }, /* the name 9load knows */ + { "ncr53c8xx", ncr53c8xxreset, }, + { 0, }, +}; + +enum { + Ninquiry = 255, + Nsense = 255, + + CMDtest = 0x00, + CMDreqsense = 0x03, + CMDread6 = 0x08, + CMDwrite6 = 0x0A, + CMDinquiry = 0x12, + CMDstart = 0x1B, + CMDread10 = 0x28, + CMDwrite10 = 0x2A, +}; + +typedef struct { + ISAConf; + Scsiio io; + + Target target[NTarget]; +} Ctlr; +static Ctlr scsi[MaxScsi]; + +static void +cmd_stat(int, char*[]) +{ + Ctlr *ctlr; + int ctlrno, targetno; + Target *tp; + + for(ctlrno = 0; ctlrno < MaxScsi; ctlrno++){ + ctlr = &scsi[ctlrno]; + if(ctlr->io == 0) + continue; + for(targetno = 0; targetno < NTarget; targetno++){ + tp = &ctlr->target[targetno]; + if(tp->fflag == 0) + continue; + print("\t%d.%d work =%7W%7W%7W xfrs\n", + ctlrno, targetno, + tp->work+0, tp->work+1, tp->work+2); + print("\t rate =%7W%7W%7W tBps\n", + tp->rate+0, tp->rate+1, tp->rate+2); + } + } +} + +void +scsiinit(void) +{ + Ctlr *ctlr; + int ctlrno, n, targetno; + Target *tp; + + for(ctlrno = 0; ctlrno < MaxScsi; ctlrno++){ + ctlr = &scsi[ctlrno]; + memset(ctlr, 0, sizeof(Ctlr)); + if(!isaconfig("scsi", ctlrno, ctlr)) + continue; + + for(n = 0; scsictlr[n].type; n++) { + if(strcmp(scsictlr[n].type, ctlr->type)) + continue; + if((ctlr->io = (*scsictlr[n].reset)(ctlrno, ctlr)) == 0) + break; + + print("scsi#%d: %s: port 0x%lux irq %lud", + ctlrno, ctlr->type, ctlr->port, + ctlr->irq); + if(ctlr->mem) + print(" addr 0x%lux", ctlr->mem & ~KZERO); + if(ctlr->size) + print(" size 0x%lux", ctlr->size); + print("\n"); + + for(targetno = 0; targetno < NTarget; targetno++){ + tp = &ctlr->target[targetno]; + + qlock(tp); + qunlock(tp); + sprint(tp->id, "scsi#%d.%d", ctlrno, targetno); + tp->name = tp->id; + + tp->ctlrno = ctlrno; + tp->targetno = targetno; + tp->inquiry = ialloc(Ninquiry, 0); + tp->sense = ialloc(Nsense, 0); + } + break; + } + if(ctlr->io == 0) + print("scsi#%d: %s: port %lux Failed to INIT controller\n", + ctlrno, ctlr->type, ctlr->port); + } + cmd_install("statd", "-- scsi stats", cmd_stat); +} + +static uchar lastcmd[16]; +static int lastcmdsz; + +static int +scsiexec(Target* tp, int rw, uchar* cmd, int cbytes, void* data, int* dbytes) +{ + int s; + + /* + * Call the device-specific I/O routine. + */ + switch(s = scsi[tp->ctlrno].io(tp, rw, cmd, cbytes, data, dbytes)){ + + case STcheck: + memmove(lastcmd, cmd, cbytes); + lastcmdsz = cbytes; + /*FALLTHROUGH*/ + + default: + /* + * It's more complicated than this. There are conditions which + * are 'ok' but for which the returned status code is not 'STok'. + * Also, not all conditions require a reqsense, there may be a + * need to do a reqsense here when necessary and making it + * available to the caller somehow. + * + * Later. + */ + break; + } + + return s; +} + +static int +scsitest(Target* tp, char lun) +{ + uchar cmd[6]; + + memset(cmd, 0, sizeof(cmd)); + cmd[0] = CMDtest; + cmd[1] = lun<<5; + return scsiexec(tp, SCSIread, cmd, sizeof(cmd), 0, 0); +} + +static int +scsistart(Target* tp, char lun, int start) +{ + uchar cmd[6]; + + memset(cmd, 0, sizeof cmd); + cmd[0] = CMDstart; + cmd[1] = lun<<5; + if(start) + cmd[4] = 1; + return scsiexec(tp, SCSIread, cmd, sizeof(cmd), 0, 0); +} + +static int +scsiinquiry(Target* tp, char lun, int* nbytes) +{ + uchar cmd[6]; + + memset(cmd, 0, sizeof cmd); + cmd[0] = CMDinquiry; + cmd[1] = lun<<5; + *nbytes = Ninquiry; + cmd[4] = *nbytes; + return scsiexec(tp, SCSIread, cmd, sizeof(cmd), tp->inquiry, nbytes); +} + +static char *key[] = +{ + "no sense", + "recovered error", + "not ready", + "medium error", + "hardware error", + "illegal request", + "unit attention", + "data protect", + "blank check", + "vendor specific", + "copy aborted", + "aborted command", + "equal", + "volume overflow", + "miscompare", + "reserved" +}; + +static int +scsireqsense(Target* tp, char lun, int* nbytes, int quiet) +{ + char *s; + int n, status, try; + uchar cmd[6], *sense; + + sense = tp->sense; + for(try = 0; try < 20; try++) { + memset(cmd, 0, sizeof(cmd)); + cmd[0] = CMDreqsense; + cmd[1] = lun<<5; + cmd[4] = Ninquiry; + memset(sense, 0, Ninquiry); + + *nbytes = Ninquiry; + status = scsiexec(tp, SCSIread, cmd, sizeof(cmd), sense, nbytes); + if(status != STok) + return status; + *nbytes = sense[0x07]+8; + + switch(sense[2] & 0x0F){ + + case 6: /* unit attention */ + /* + * 0x28 - not ready to ready transition, + * medium may have changed. + * 0x29 - power on, RESET or BUS DEVICE RESET occurred. + */ + if(sense[12] != 0x28 && sense[12] != 0x29) + goto buggery; + /*FALLTHROUGH*/ + case 0: /* no sense */ + case 1: /* recovered error */ + return STok; + + case 8: /* blank data */ + return STblank; + + case 2: /* not ready */ + if(sense[12] == 0x3A) /* medium not present */ + goto buggery; + /*FALLTHROUGH*/ + + default: + /* + * If unit is becoming ready, rather than not ready, + * then wait a little then poke it again; should this + * be here or in the caller? + */ + if((sense[12] == 0x04 && sense[13] == 0x01)){ + waitsec(500); + scsitest(tp, lun); + break; + } + goto buggery; + } + } + +buggery: + if(quiet == 0){ + s = key[sense[2]&0x0F]; + print("%s: reqsense: '%s' code #%2.2ux #%2.2ux\n", + tp->id, s, sense[12], sense[13]); + print("%s: byte 2: #%2.2ux, bytes 15-17: #%2.2ux #%2.2ux #%2.2ux\n", + tp->id, sense[2], sense[15], sense[16], sense[17]); + print("lastcmd (%d): ", lastcmdsz); + for(n = 0; n < lastcmdsz; n++) + print(" #%2.2ux", lastcmd[n]); + print("\n"); + } + + return STcheck; +} + +static Target* +scsitarget(Device* d) +{ + int ctlrno, targetno; + + ctlrno = d->wren.ctrl; + if(ctlrno < 0 || ctlrno >= MaxScsi || scsi[ctlrno].io == 0) + return 0; + targetno = d->wren.targ; + if(targetno < 0 || targetno >= NTarget) + return 0; + return &scsi[ctlrno].target[targetno]; +} + +static void +scsiprobe(Device* d) +{ + Target *tp; + int nbytes, s; + uchar *sense; + int acount; + + if((tp = scsitarget(d)) == 0) + panic("scsiprobe: device = %Z\n", d); + + acount = 0; +again: + s = scsitest(tp, d->wren.lun); + if(s < STok){ + print("%s: test, status %d\n", tp->id, s); + return; + } + + /* + * Determine if the drive exists and is not ready or + * is simply not responding. + * If the status is OK but the drive came back with a 'power on' or + * 'reset' status, try the test again to make sure the drive is really + * ready. + * If the drive is not ready and requires intervention, try to spin it + * up. + */ + s = scsireqsense(tp, d->wren.lun, &nbytes, acount); + sense = tp->sense; + switch(s){ + case STok: + if((sense[2] & 0x0F) == 0x06 && (sense[12] == 0x28 || sense[12] == 0x29)){ + if(acount == 0){ + acount = 1; + goto again; + } + } + break; + case STcheck: + if((sense[2] & 0x0F) == 0x02){ + if(sense[12] == 0x3A) + break; + if(sense[12] == 0x04 && sense[13] == 0x02){ + print("%s: starting...\n", tp->id); + if(scsistart(tp, d->wren.lun, 1) == STok) + break; + s = scsireqsense(tp, d->wren.lun, &nbytes, 0); + } + } + /*FALLTHROUGH*/ + default: + print("%s: unavailable, status %d\n", tp->id, s); + return; + } + + /* + * Inquire to find out what the device is. + * Hardware drivers may need some of the info. + */ + s = scsiinquiry(tp, d->wren.lun, &nbytes); + if(s != STok) { + print("%s: inquiry failed, status %d\n", tp->id, s); + return; + } + print("%s: %s\n", tp->id, (char*)tp->inquiry+8); + tp->ok = 1; +} + +int +scsiio(Device* d, int rw, uchar* cmd, int cbytes, void* data, int dbytes) +{ + Target *tp; + int e, nbytes, s; + + if((tp = scsitarget(d)) == 0) + panic("scsiio: device = %Z\n", d); + + qlock(tp); + if(tp->ok == 0) + scsiprobe(d); + if(tp->fflag == 0) { + /* last args were 1000, now 1 */ + dofilter(tp->work+0, C0a, C0b, 1); + dofilter(tp->work+1, C1a, C1b, 1); + dofilter(tp->work+2, C2a, C2b, 1); + /* */ + dofilter(tp->rate+0, C0a, C0b, 1); + dofilter(tp->rate+1, C1a, C1b, 1); + dofilter(tp->rate+2, C2a, C2b, 1); + tp->fflag = 1; + } + tp->work[0].count++; + tp->work[1].count++; + tp->work[2].count++; + tp->rate[0].count += dbytes; + tp->rate[1].count += dbytes; + tp->rate[2].count += dbytes; + qunlock(tp); + + s = STinit; + for(e = 0; e < 10; e++){ + for(;;){ + nbytes = dbytes; + s = scsiexec(tp, rw, cmd, cbytes, data, &nbytes); + if(s == STok) + break; + s = scsireqsense(tp, d->wren.lun, &nbytes, 0); + if(s == STblank && rw == SCSIread) { + memset(data, 0, dbytes); + return STok; + } + if(s != STok) + break; + } + if(s == STok) + break; + } + if(e) + print("%s: retry %d cmd #%x\n", tp->id, e, cmd[0]); + return s; +} diff -Nru /sys/src/fs/pc/scsibuslogic.c /sys/src/fs/pc/scsibuslogic.c --- /sys/src/fs/pc/scsibuslogic.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/pc/scsibuslogic.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,1267 @@ +/* + * Buslogic BT-* SCSI Host Adapter in both 24-bit and 32-bit mode. + * 24-bit mode works for Adaptec 154xx series too. + * + * To do: + * tidy the PCI probe and do EISA; + * allocate more Ccb's as needed, up to NMbox-1; + * add nmbox and nccb to Ctlr struct for the above; + * 64-bit LUN/explicit wide support necessary? + * + */ +#include "all.h" +#include "io.h" +#include "mem.h" + +enum { /* registers */ + Rcontrol = 0x00, /* WO: control register */ + Rstatus = 0x00, /* RO: status register */ + Rcpr = 0x01, /* WO: command/parameter register */ + Rdatain = 0x01, /* RO: data-in register */ + Rinterrupt = 0x02, /* RO: interrupt register */ +}; + +enum { /* Rcontrol */ + Rsbus = 0x10, /* SCSI Bus Reset */ + Rint = 0x20, /* Interrupt Reset */ + Rsoft = 0x40, /* Soft Reset */ + Rhard = 0x80, /* Hard Reset */ +}; + +enum { /* Rstatus */ + Cmdinv = 0x01, /* Command Invalid */ + Dirrdy = 0x04, /* Data In Register Ready */ + Cprbsy = 0x08, /* Command/Parameter-Out Register Busy */ + Hardy = 0x10, /* Host Adapter Ready */ + Inreq = 0x20, /* Initialisation Required */ + Dfail = 0x40, /* Diagnostic Failure */ + Dact = 0x80, /* Diagnostic Active */ +}; + +enum { /* Rcpr */ + Cinitialise = 0x01, /* Initialise Mailbox */ + Cstart = 0x02, /* Start Mailbox Command */ + Cinquiry = 0x04, /* Adapter Anquiry */ + Ceombri = 0x05, /* Enable OMBR Interrupt */ + Cinquire = 0x0B, /* Inquire Configuration */ + Cextbios = 0x28, /* AHA-1542: Return extended BIOS information */ + Cmbienable = 0x29, /* AHA-1542: Mailbox interface enable */ + Ciem = 0x81, /* Initialise Extended Mailbox */ + Ciesi = 0x8D, /* Inquire Extended Setup Information */ + Cerrm = 0x8F, /* Enable strict round-robin mode */ + Cwide = 0x96, /* Wide CCB */ +}; + +enum { /* Rinterrupt */ + Imbl = 0x01, /* Incoming Mailbox Loaded */ + Mbor = 0x02, /* Mailbox Out Ready */ + Cmdc = 0x04, /* Command Complete */ + Rsts = 0x08, /* SCSI Reset State */ + Intv = 0x80, /* Interrupt Valid */ +}; + +typedef struct { + uchar code; /* action/completion code */ + uchar ccb[3]; /* CCB pointer (MSB, ..., LSB) */ +} Mbox24; + +typedef struct { + uchar ccb[4]; /* CCB pointer (LSB, ..., MSB) */ + uchar btstat; /* BT-7[45]7[SD] status */ + uchar sdstat; /* SCSI device status */ + uchar pad; + uchar code; /* action/completion code */ +} Mbox32; + +typedef union { + Mbox24; + Mbox32; +} Mbox; + +enum { /* mailbox commands */ + Mbfree = 0x00, /* Mailbox not in use */ + + Mbostart = 0x01, /* Start a mailbox command */ + Mboabort = 0x02, /* Abort a mailbox command */ + + Mbiok = 0x01, /* CCB completed without error */ + Mbiabort = 0x02, /* CCB aborted at request of host */ + Mbinx = 0x03, /* Aborted CCB not found */ + Mbierror = 0x04, /* CCB completed with error */ +}; + +typedef struct Ccb24 Ccb24; +typedef struct Ccb32 Ccb32; +typedef union Ccb Ccb; + +typedef struct Ccb24 { + uchar opcode; /* Operation code */ + uchar datadir; /* Data direction control */ + uchar cdblen; /* Length of CDB */ + uchar senselen; /* Length of sense area */ + uchar datalen[3]; /* Data length (MSB, ..., LSB) */ + uchar dataptr[3]; /* Data pointer (MSB, ..., LSB) */ + uchar linkptr[3]; /* Link pointer (MSB, ..., LSB) */ + uchar linkid; /* command linking identifier */ + uchar btstat; /* BT-* adapter status */ + uchar sdstat; /* SCSI device status */ + uchar reserved[2]; /* */ + uchar cs[12+0xFF]; /* Command descriptor block + Sense bytes */ + + Rendez; + int done; /* command completed */ + + Ccb* ccb; /* link on free list */ +} Ccb24; + +typedef struct Ccb32 { + uchar opcode; /* Operation code */ + uchar datadir; /* Data direction control */ + uchar cdblen; /* Length of CDB */ + uchar senselen; /* Length of sense area */ + uchar datalen[4]; /* Data length (LSB, ..., MSB) */ + uchar dataptr[4]; /* Data pointer (LSB, ..., MSB) */ + uchar reserved[2]; + uchar btstat; /* BT-* adapter status */ + uchar sdstat; /* SCSI device status */ + uchar targetid; /* Target ID */ + uchar luntag; /* LUN & tag */ + uchar cdb[12]; /* Command descriptor block */ + uchar ccbctl; /* CCB control */ + uchar linkid; /* command linking identifier */ + uchar linkptr[4]; /* Link pointer (LSB, ..., MSB) */ + uchar senseptr[4]; /* Sense pointer (LSB, ..., MSB) */ + uchar sense[0xFF]; /* Sense bytes */ + + Rendez; + int done; /* command completed */ + + Ccb* ccb; /* link on free list */ +} Ccb32; + +typedef union Ccb { + Ccb24; + Ccb32; +} Ccb; + +enum { /* opcode */ + OInitiator = 0x00, /* initiator CCB */ + Ordl = 0x03, /* initiator CCB with residual data length returned */ +}; + +enum { /* datadir */ + CCBdatain = 0x08, /* inbound data transfer, length is checked */ + CCBdataout = 0x10, /* outbound data transfer, length is checked */ +}; + +enum { /* btstat */ + Eok = 0x00, /* CCB completed normally with no errors */ +}; + +enum { /* luntag */ + TagEnable = 0x20, /* Tag enable */ + SQTag = 0x00, /* Simple Queue Tag */ + HQTag = 0x40, /* Head of Queue Tag */ + OQTag = 0x80, /* Ordered Queue Tag */ +}; + +enum { /* CCB control */ + NoDisc = 0x08, /* No disconnect */ + NoUnd = 0x10, /* No underrrun error report */ + NoData = 0x20, /* No data transfer */ + NoStat = 0x40, /* No CCB status if zero */ + NoIntr = 0x80, /* No Interrupts */ +}; + +typedef struct Bbuf Bbuf; +struct Bbuf { + Bbuf* next; + uchar* data; /* original data */ + uchar* buf; /* bounce buffer */ +}; + +typedef struct { + ulong port; /* I/O port */ + int id; /* adapter SCSI id */ + int ctlrno; + int bus; /* 24 or 32 -bit */ + int wide; + int tbdf; + + Lock ccblock; + QLock ccbq; + Rendez ccbr; + + Lock mboxlock; + Mbox* mb; /* mailbox out + mailbox in */ + int mbox; /* current mailbox out index into mb */ + int mbix; /* current mailbox in index into mb */ + + Lock cachelock; + Ccb* ccb; /* list of free Ccb's */ + Ccb* cache[NTarget]; /* last completed Ccb */ + + Lock bblock; + Bbuf* bbuf; /* list of free 24-bit bounce buffers */ + QLock bbq; + Rendez bbr; +} Ctlr; + +/* + * The number of mailboxes should be a multiple of 8 (4 for Mbox32) + * to ensure the boundary between the out and in mailboxes doesn't + * straddle a cache-line boundary. + * The number of Ccb's should be less than the number of mailboxes to + * ensure no queueing is necessary on mailbox allocation. + * NBbuf should minimally be the actual number of targets plus some + * slop for queuing. Since 24-bit controllers are never wide and it's + * unlikely anyone would fully populate the controller, 8 is probably + * enough. + */ +enum { + NMbox = 8*8, /* number of Mbox's */ + NCcb = NMbox-1, /* number of Ccb's */ + NBbuf = 8, /* number of 24-bit bounce buffers */ +}; + +#define PADDR24(a, n) ((PADDR(a)+(n)) <= (1<<24)) + +static Ctlr *ctlrxx[MaxScsi]; +static int ctrls; + +static void +cmd_scsi(int, char**) +{ + int i, j; + Ctlr *ctlr; + Mbox24 *mb24; + Mbox32 *mb32; + + for(i=0; imbox); + print(" mbix %2.2d\n", ctlr->mbix); + if(ctlr->bus == 24){ + for(j=0; jmb[j]; + print(" mbox %2.2d: code #%x\tpaddr #%ux\n", + j, mb24->code, + (mb24->ccb[0]<<16)|(mb24->ccb[1]<<8)|mb24->ccb[2]); + prflush(); + } + } + else { + for(j=0; jmb[j]; + print(" mbox %2.2d: code #%x\tpaddr #%ux\tbt#%ux\tsd#%ux\n", + j, mb32->code, + (mb32->ccb[3]<<24) + |(mb32->ccb[2]<<16) + |(mb32->ccb[1]<<8) + |mb32->ccb[0], + mb32->btstat, mb32->sdstat); + prflush(); + } + } + } +} + +static void +ccbfree(Ctlr* ctlr, Ccb* ccb) +{ + lock(&ctlr->ccblock); + if(ctlr->bus == 24) + ((Ccb24*)ccb)->ccb = ctlr->ccb; + else + ((Ccb32*)ccb)->ccb = ctlr->ccb; + if(ctlr->ccb == nil) + wakeup(&ctlr->ccbr); + ctlr->ccb = ccb; + unlock(&ctlr->ccblock); +} + +static int +ccbavailable(void* a) +{ + return ((Ctlr*)a)->ccb != nil; +} + +static Ccb* +ccballoc(Ctlr* ctlr) +{ + Ccb *ccb; + + for(;;){ + lock(&ctlr->ccblock); + if(ccb = ctlr->ccb){ + if(ctlr->bus == 24) + ctlr->ccb = ((Ccb24*)ccb)->ccb; + else + ctlr->ccb = ((Ccb32*)ccb)->ccb; + unlock(&ctlr->ccblock); + break; + } + + unlock(&ctlr->ccblock); + qlock(&ctlr->ccbq); + sleep(&ctlr->ccbr, ccbavailable, ctlr); + qunlock(&ctlr->ccbq); + } + + return ccb; +} + +static void +bbfree(Ctlr* ctlr, Bbuf *bb) +{ + lock(&ctlr->bblock); + bb->next = ctlr->bbuf; + if(ctlr->bbuf == nil) + wakeup(&ctlr->bbr); + ctlr->bbuf = bb; + unlock(&ctlr->bblock); +} + +static int +bbavailable(void* a) +{ + return ((Ctlr*)a)->bbuf != nil; +} + +static Bbuf* +bballoc(Ctlr* ctlr) +{ + Bbuf *bb; + + for(;;){ + lock(&ctlr->bblock); + if(bb = ctlr->bbuf){ + ctlr->bbuf = bb->next; + unlock(&ctlr->bblock); + break; + } + + unlock(&ctlr->bblock); + qlock(&ctlr->bbq); + sleep(&ctlr->bbr, bbavailable, ctlr); + qunlock(&ctlr->bbq); + } + + return bb; +} + +static int +done24(void* arg) +{ + return ((Ccb24*)arg)->done; +} + +static int +scsiio24(Target* t, int rw, uchar* cmd, int cbytes, void* data, int* dbytes) +{ + Ctlr *ctlr; + Ccb24 *ccb; + Mbox24 *mb; + Bbuf *bb; + ulong p; + int d, id, n, btstat, sdstat; + uchar *sense; + uchar lun; + + if((ctlr = ctlrxx[t->ctlrno]) == nil || ctlr->port == 0) + return STharderr; + id = t->targetno; + if(ctlr->id == id) + return STownid; + lun = (cmd[1]>>5) & 0x07; + + /* + * Ctlr->cache holds the last completed Ccb for this target if it + * returned 'check condition'. + * If this command is a request-sense and there is valid sense data + * from the last completed Ccb, return it immediately. + */ + lock(&ctlr->cachelock); + if(ccb = ctlr->cache[id]){ + ctlr->cache[id] = nil; + if(cmd[0] == 0x03 && ccb->sdstat == STcheck && lun == ((ccb->cs[1]>>5) & 0x07)){ + unlock(&ctlr->cachelock); + if(dbytes){ + sense = &ccb->cs[ccb->cdblen]; + n = 8+sense[7]; + if(n > *dbytes) + n = *dbytes; + memmove(data, sense, n); + *dbytes = n; + } + ccbfree(ctlr, (Ccb*)ccb); + return STok; + } + } + unlock(&ctlr->cachelock); + if(ccb == nil) + ccb = ccballoc(ctlr); + + /* + * Check if the transfer is to memory above the 24-bit limit the + * controller can address. If it is, try to allocate a temporary + * buffer as a staging area. + */ + if(dbytes) + n = *dbytes; + else + n = 0; + if(n && !PADDR24(data, n)){ + bb = bballoc(ctlr); + bb->data = data; + if(rw == SCSIwrite) + memmove(bb->buf, data, n); + data = bb->buf; + } + else + bb = nil; + + /* + * Fill in the ccb. + */ + ccb->opcode = Ordl; + + ccb->datadir = (id<<5)|lun; + if(n == 0) + ccb->datadir |= CCBdataout|CCBdatain; + else if(rw == SCSIread) + ccb->datadir |= CCBdatain; + else + ccb->datadir |= CCBdataout; + + ccb->cdblen = cbytes; + ccb->senselen = 0xFF; + + ccb->datalen[0] = n>>16; + ccb->datalen[1] = n>>8; + ccb->datalen[2] = n; + p = PADDR(data); + ccb->dataptr[0] = p>>16; + ccb->dataptr[1] = p>>8; + ccb->dataptr[2] = p; + + ccb->linkptr[0] = ccb->linkptr[1] = ccb->linkptr[2] = 0; + ccb->linkid = 0; + ccb->btstat = ccb->sdstat = 0; + ccb->reserved[0] = ccb->reserved[1] = 0; + + memmove(ccb->cs, cmd, cbytes); + + /* + * There's one more mbox than there there is + * ccb so there is always one free. + */ + lock(&ctlr->mboxlock); + mb = ctlr->mb; + mb += ctlr->mbox; + p = PADDR(ccb); + mb->ccb[0] = p>>16; + mb->ccb[1] = p>>8; + mb->ccb[2] = p; + mb->code = Mbostart; + ctlr->mbox++; + if(ctlr->mbox >= NMbox) + ctlr->mbox = 0; + + /* + * This command does not require Hardy + * and doesn't generate a Cmdc interrupt. + */ + ccb->done = 0; + outb(ctlr->port+Rcpr, Cstart); + unlock(&ctlr->mboxlock); + + /* + * Wait for the request to complete and return the status. + * Since the buffer is not reference counted cannot return + * until the DMA is done writing into the buffer so the caller + * cannot free the buffer prematurely. + */ + sleep(ccb, done24, ccb); + + /* + * Save the status and patch up the number of + * bytes actually transferred. + * There's a firmware bug on some 956C controllers + * which causes the return count from a successful + * READ CAPACITY not be updated, so fix it here. + */ + sdstat = ccb->sdstat; + btstat = ccb->btstat; + + d = ccb->datalen[0]<<16; + d |= ccb->datalen[1]<<8; + d |= ccb->datalen[2]; + if(ccb->cs[0] == 0x25 && sdstat == STok) + d = 0; + n -= d; + if(dbytes) + *dbytes = n; + + /* + * Tidy things up if a staging area was used for the data, + */ + if(bb != nil){ + if(sdstat == STok && btstat == 0 && rw == SCSIread) + memmove(bb->data, data, n); + bbfree(ctlr, bb); + } + + /* + * If there was a check-condition, save the + * ccb for a possible request-sense command. + */ + if(sdstat == STcheck){ + lock(&ctlr->cachelock); + if(ctlr->cache[id]) + ccbfree(ctlr, ctlr->cache[id]); + ctlr->cache[id] = (Ccb*)ccb; + unlock(&ctlr->cachelock); + return STcheck; + } + ccbfree(ctlr, (Ccb*)ccb); + + if(btstat){ + if(btstat == 0x11) + return STtimeout; + return STharderr; + } + return sdstat; +} + +static void +interrupt24(Ureg*, void* arg) +{ + Ctlr *ctlr; + ulong port; + uchar rinterrupt, rstatus; + Mbox24 *mb, *mbox; + Ccb24 *ccb; + + ctlr = arg; + + /* + * Save and clear the interrupt(s). The only + * interrupts expected are Cmdc, which is ignored, + * and Imbl which means something completed. + */ + port = ctlr->port; + rinterrupt = inb(port+Rinterrupt); + rstatus = inb(port+Rstatus); + if((rinterrupt & ~(Cmdc|Imbl)) != Intv) + print("scsi#%d: interrupt 0x%2.2ux\n", ctlr->ctlrno, rinterrupt); + if((rinterrupt & Cmdc) && (rstatus & Cmdinv)) + print("scsi#%d: command invalid\n", ctlr->ctlrno); + + /* + * Look for something in the mail. + * If there is, save the status, free the mailbox + * and wakeup whoever. + */ + mb = ctlr->mb; + for(mbox = &mb[ctlr->mbix]; mbox->code; mbox = &mb[ctlr->mbix]){ + ccb = (Ccb*)(KZERO + |(mbox->ccb[0]<<16) + |(mbox->ccb[1]<<8) + |mbox->ccb[2]); + mbox->code = 0; + ccb->done = 1; + wakeup(ccb); + + ctlr->mbix++; + if(ctlr->mbix >= NMbox+NMbox) + ctlr->mbix = NMbox; + } + outb(port+Rcontrol, Rint); +} + +static int +done32(void* arg) +{ + return ((Ccb32*)arg)->done; +} + +static int +scsiio32(Target* t, int rw, uchar* cmd, int cbytes, void* data, int* dbytes) +{ + Ctlr *ctlr; + Ccb32 *ccb; + Mbox32 *mb; + ulong p; + int d, id, n, btstat, sdstat; + uchar lun; + + if((ctlr = ctlrxx[t->ctlrno]) == nil || ctlr->port == 0) + return STharderr; + id = t->targetno; + if(ctlr->id == id) + return STownid; + lun = (cmd[1]>>5) & 0x07; + + /* + * Ctlr->cache holds the last completed Ccb for this target if it + * returned 'check condition'. + * If this command is a request-sense and there is valid sense data + * from the last completed Ccb, return it immediately. + */ + lock(&ctlr->cachelock); + if(ccb = ctlr->cache[id]){ + ctlr->cache[id] = nil; + if(cmd[0] == 0x03 && ccb->sdstat == STcheck && lun == (ccb->luntag & 0x07)){ + unlock(&ctlr->cachelock); + if(dbytes){ + n = 8+ccb->sense[7]; + if(n > *dbytes) + n = *dbytes; + memmove(data, ccb->sense, n); + *dbytes = n; + } + ccbfree(ctlr, (Ccb*)ccb); + return STok; + } + } + unlock(&ctlr->cachelock); + if(ccb == nil) + ccb = ccballoc(ctlr); + + /* + * Fill in the ccb. + */ + ccb->opcode = Ordl; + + if(dbytes) + n = *dbytes; + else + n = 0; + if(n == 0) + ccb->datadir = CCBdataout|CCBdatain; + else if(rw == SCSIread) + ccb->datadir = CCBdatain; + else + ccb->datadir = CCBdataout; + + ccb->cdblen = cbytes; + + ccb->datalen[0] = n; + ccb->datalen[1] = n>>8; + ccb->datalen[2] = n>>16; + ccb->datalen[3] = n>>24; + p = PADDR(data); + ccb->dataptr[0] = p; + ccb->dataptr[1] = p>>8; + ccb->dataptr[2] = p>>16; + ccb->dataptr[3] = p>>24; + + ccb->targetid = id; + ccb->luntag = lun; + if(t->ok && (t->inquiry[7] & 0x02)){ + if(ctlr->wide) + ccb->datadir |= SQTag|TagEnable; + else + ccb->luntag |= SQTag|TagEnable; + } + memmove(ccb->cdb, cmd, cbytes); + ccb->btstat = ccb->sdstat = 0; + ccb->ccbctl = 0; + + /* + * There's one more mbox than there there is + * ccb so there is always one free. + */ + lock(&ctlr->mboxlock); + mb = ctlr->mb; + mb += ctlr->mbox; + p = PADDR(ccb); + mb->ccb[0] = p; + mb->ccb[1] = p>>8; + mb->ccb[2] = p>>16; + mb->ccb[3] = p>>24; + mb->code = Mbostart; + ctlr->mbox++; + if(ctlr->mbox >= NMbox) + ctlr->mbox = 0; + + /* + * This command does not require Hardy + * and doesn't generate a Cmdc interrupt. + */ + ccb->done = 0; + outb(ctlr->port+Rcpr, Cstart); + unlock(&ctlr->mboxlock); + + /* + * Wait for the request to complete and return the status. + * Since the buffer is not reference counted cannot return + * until the DMA is done writing into the buffer so the caller + * cannot free the buffer prematurely. + */ + sleep(ccb, done32, ccb); + + /* + * Save the status and patch up the number of + * bytes actually transferred. + * There's a firmware bug on some 956C controllers + * which causes the return count from a successful + * READ CAPACITY not be updated, so fix it here. + */ + sdstat = ccb->sdstat; + btstat = ccb->btstat; + + d = ccb->datalen[0]; + d |= (ccb->datalen[1]<<8); + d |= (ccb->datalen[2]<<16); + d |= (ccb->datalen[3]<<24); + if(ccb->cdb[0] == 0x25 && sdstat == STok) + d = 0; + n -= d; + if(dbytes) + *dbytes = n; + + + /* + * If there was a check-condition, save the + * ccb for a possible request-sense command. + */ + if(sdstat == STcheck){ + lock(&ctlr->cachelock); + if(ctlr->cache[id]) + ccbfree(ctlr, ctlr->cache[id]); + ctlr->cache[id] = (Ccb*)ccb; + unlock(&ctlr->cachelock); + return STcheck; + } + ccbfree(ctlr, (Ccb*)ccb); + + if(btstat){ + if(btstat == 0x11) + return STtimeout; + return STharderr; + } + return sdstat; +} + +static void +interrupt32(Ureg*, void* arg) +{ + Ctlr *ctlr; + ulong port; + uchar rinterrupt, rstatus; + Mbox32 *mb, *mbox; + Ccb32 *ccb; + + ctlr = arg; + + /* + * Save and clear the interrupt(s). The only + * interrupts expected are Cmdc, which is ignored, + * and Imbl which means something completed. + */ + port = ctlr->port; + rinterrupt = inb(port+Rinterrupt); + rstatus = inb(port+Rstatus); + if((rinterrupt & ~(Cmdc|Imbl)) != Intv) + print("scsi#%d: interrupt 0x%2.2ux\n", ctlr->ctlrno, rinterrupt); + if((rinterrupt & Cmdc) && (rstatus & Cmdinv)) + print("scsi#%d: command invalid\n", ctlr->ctlrno); + + /* + * Look for something in the mail. + * If there is, save the status, free the mailbox + * and wakeup whoever. + */ + mb = ctlr->mb; + for(mbox = &mb[ctlr->mbix]; mbox->code; mbox = &mb[ctlr->mbix]){ + ccb = (Ccb*)(KZERO + |(mbox->ccb[3]<<24) + |(mbox->ccb[2]<<16) + |(mbox->ccb[1]<<8) + |mbox->ccb[0]); + mbox->code = 0; + ccb->done = 1; + wakeup(ccb); + + ctlr->mbix++; + if(ctlr->mbix >= NMbox+NMbox) + ctlr->mbix = NMbox; + } + outb(port+Rcontrol, Rint); +} + +static Lock cmdlock[MaxScsi]; + +/* + * Issue a command to a controller. The command and its length is + * contained in cmd and cmdlen. If any data is to be + * returned, datalen should be non-zero, and the returned data + * will be placed in data. + * If Cmdc is set, bail out, the invalid command will be handled + * when the interrupt is processed. + */ +static int +issueio(int port, uchar* cmd, int cmdlen, uchar* data, int datalen) +{ + int len; + + if(cmd[0] != Cstart && cmd[0] != Ceombri){ + while(!(inb(port+Rstatus) & Hardy)) + ; + } + outb(port+Rcpr, cmd[0]); + + len = 1; + while(len < cmdlen){ + if(!(inb(port+Rstatus) & Cprbsy)){ + outb(port+Rcpr, cmd[len]); + len++; + } + if(inb(port+Rinterrupt) & Cmdc) + return 0; + } + + len = 0; + if(datalen){ + while(len < datalen){ + if(inb(port+Rstatus) & Dirrdy){ + data[len] = inb(port+Rdatain); + len++; + } + if(inb(port+Rinterrupt) & Cmdc) + break; + } + } + + return len; +} + +/* + * Issue a command to a controller, wait for it to complete then + * reset the interrupt. + * Should only be called at initialisation. + */ +static int +issue(int ctlrno, int port, uchar* cmd, int cmdlen, uchar* data, int datalen) +{ + int len; + uchar rinterrupt, rstatus; + + ilock(&cmdlock[ctlrno]); + len = issueio(port, cmd, cmdlen, data, datalen); + + while(!((rinterrupt = inb(port+Rinterrupt)) & Cmdc)) + ; + + rstatus = inb(port+Rstatus); + outb(port+Rcontrol, Rint); + if((rinterrupt & Cmdc) && (rstatus & Cmdinv)){ + iunlock(&cmdlock[ctlrno]); + return -1; + } + iunlock(&cmdlock[ctlrno]); + + return len; +} + +Scsiio +buslogic24(Ctlr* ctlr, ISAConf* isa) +{ + ulong p; + Ccb24 *ccb, *ccbp; + Bbuf *bb; + uchar cmd[6], *v; + int i, len; + + len = (sizeof(Mbox24)*NMbox*2)+(sizeof(Ccb24)*NCcb)+(sizeof(Bbuf)*NBbuf); + v = ialloc(len, 0); + + if(!PADDR24(ctlr, sizeof(Ctlr)) || !PADDR24(v, len)){ + print("scsi#%d: %s: 24-bit allocation failed\n", + ctlr->ctlrno, isa->type); + return 0; + } + + ctlr->mb = (Mbox*)v; + v += sizeof(Mbox24)*NMbox*2; + + ccb = (Ccb24*)v; + for(ccbp = ccb; ccbp < &ccb[NCcb]; ccbp++){ + ccbp->ccb = ctlr->ccb; + ctlr->ccb = (Ccb*)ccbp; + } + v += sizeof(Ccb24)*NCcb; + + for(i = 0; i < NBbuf; i++){ + bb = (Bbuf*)v; + bb->buf = ialloc(RBUFSIZE, 32); + if(bb->buf == nil || !PADDR24(bb->buf, RBUFSIZE)){ + print("scsi#%d: %s: 24-bit bb allocation failed (%d)\n", + ctlr->ctlrno, isa->type, i); + break; + } + bb->next = ctlr->bbuf; + ctlr->bbuf = bb; + v += sizeof(Bbuf); + } + + /* + * Initialise the software controller and + * set the board scanning the mailboxes. + */ + ctlr->mbix = NMbox; + + cmd[0] = Cinitialise; + cmd[1] = NMbox; + p = PADDR(ctlr->mb); + cmd[2] = p>>16; + cmd[3] = p>>8; + cmd[4] = p; + if(issue(ctlr->ctlrno, ctlr->port, cmd, 5, 0, 0) >= 0){ + ctlrxx[ctlr->ctlrno] = ctlr; + setvec(Int0vec+isa->irq, interrupt24, ctlr); + return scsiio24; + } + + print("scsi#%d: %s: mbox24 init failed\n", ctlr->ctlrno, isa->type); + return 0; +} + +Scsiio +buslogic32(Ctlr* ctlr, ISAConf* isa) +{ + ulong p; + Ccb32 *ccb, *ccbp; + uchar cmd[6], *v; + + v = ialloc((sizeof(Mbox32)*NMbox*2)+(sizeof(Ccb32)*NCcb), 0); + + ctlr->mb = (Mbox*)v; + v += sizeof(Mbox32)*NMbox*2; + + ccb = (Ccb32*)v; + for(ccbp = ccb; ccbp < &ccb[NCcb]; ccbp++){ + /* + * Fill in some stuff that doesn't change. + */ + ccbp->senselen = sizeof(ccbp->sense); + p = PADDR(ccbp->sense); + ccbp->senseptr[0] = p; + ccbp->senseptr[1] = p>>8; + ccbp->senseptr[2] = p>>16; + ccbp->senseptr[3] = p>>24; + + ccbp->ccb = ctlr->ccb; + ctlr->ccb = (Ccb*)ccbp; + } + + /* + * Wide mode setup. + */ + if(ctlr->wide){ + cmd[0] = Cwide; + cmd[1] = 1; + if(issue(ctlr->ctlrno, ctlr->port, cmd, 2, 0, 0) < 0) + print("scsi#%d: %s: wide init failed\n", + ctlr->ctlrno, isa->type); + } + + /* + * Initialise the software controller and + * set the board scanning the mailboxes. + */ + ctlr->mbix = NMbox; + + cmd[0] = Ciem; + cmd[1] = NMbox; + p = PADDR(ctlr->mb); + cmd[2] = p; + cmd[3] = p>>8; + cmd[4] = p>>16; + cmd[5] = p>>24; + if(issue(ctlr->ctlrno, ctlr->port, cmd, 6, 0, 0) >= 0){ + ctlrxx[ctlr->ctlrno] = ctlr; + /* + intrenable(VectorPIC+isa->irq, interrupt32, ctlr, ctlr->tbdf); + */ + setvec(Int0vec+isa->irq, interrupt32, ctlr); + return scsiio32; + } + + print("scsi#%d: %s: mbox32 init failed\n", ctlr->ctlrno, isa->type); + return 0; +} + +typedef struct Adapter { + int port; + Pcidev* pcidev; +} Adapter; +static Msgbuf* adapter; + +static void +buslogicpci(void) +{ + Msgbuf *mb; + Adapter *ap; + Pcidev *p; + + p = nil; + while(p = pcimatch(p, 0x104B, 0)){ + mb = mballoc(sizeof(Adapter), 0, Mxxx); + ap = (Adapter*)mb->data; + ap->port = p->mem[0].bar & ~0x01; + ap->pcidev = p; + + mb->next = adapter; + adapter = mb; + } +} + +Scsiio +buslogic(int ctlrno, ISAConf* isa) +{ + Scsiio io; + Ctlr *ctlr; + Adapter *ap; + Pcidev *pcidev; + Msgbuf *mb, **mbb; + uchar cmd[6], data[256]; + int bus, cmdlen, datalen, port, timeo, wide; + static int scandone; + + if(scandone == 0){ + buslogicpci(); + scandone = 1; + } + + /* + * Any adapter matches if no isa->port is supplied, + * otherwise the ports must match. + */ + port = 0; + pcidev = nil; + mbb = &adapter; + for(mb = *mbb; mb != nil; mb = mb->next){ + ap = (Adapter*)mb->data; + if(isa->port == 0 || isa->port == ap->port){ + port = ap->port; + pcidev = ap->pcidev; + *mbb = mb->next; + mbfree(mb); + break; + } + mbb = &mb->next; + } + if(port == 0){ + if(isa->port == 0) + return nil; + port = isa->port; + } + isa->port = port; + + /* + * Attempt to hard-reset the board and reset + * the SCSI bus. If the board state doesn't settle to + * idle with mailbox initialisation required, either + * it isn't a compatible board or it's broken. + * If the controller has SCAM set this can take a while. + */ + outb(port+Rcontrol, Rhard|Rsbus); + for(timeo = 0; timeo < 100; timeo++){ + if(inb(port+Rstatus) == (Inreq|Hardy)) + break; + delay(100); + } + if(inb(port+Rstatus) != (Inreq|Hardy)){ + print("scsi#%d: %s: port 0x%ux failed to hard-reset 0x%ux\n", + ctlrno, isa->type, port, inb(port+Rstatus)); + return 0; + } + + /* + * Try to determine if this is a Buslogic 32-bit controller + * by attempting to obtain the extended inquiry information; + * this command is not implemented on Adaptec 154xx + * controllers. If successful, the first byte of the returned + * data is the host adapter bus type, 'E' for 32-bit EISA, + * PCI and VLB buses. + */ + cmd[0] = Ciesi; + cmd[1] = 14; + cmdlen = 2; + datalen = 256; + bus = 24; + wide = 0; + datalen = issue(ctlrno, port, cmd, cmdlen, data, datalen); + if(datalen >= 0){ + if(data[0] == 'E') + bus = 32; + wide = data[0x0D] & 0x01; + } + else{ + /* + * Inconceivable though it may seem, a hard controller reset is + * necessary here to clear out the command queue. Every board seems to + * lock-up in a different way if you give an invalid command and then + * try to clear out the command/parameter and/or data-in register. + * Soft reset doesn't do the job either. Fortunately no serious + * initialisation has been done yet so there's nothing to tidy up. + */ + outb(port+Rcontrol, Rhard); + for(timeo = 0; timeo < 100; timeo++){ + if(inb(port+Rstatus) == (Inreq|Hardy)) + break; + delay(100); + } + if(inb(port+Rstatus) != (Inreq|Hardy)){ + print("scsi#%d: %s: port 0x%ux Ciesi 0x%ux\n", + ctlrno, isa->type, port, inb(port+Rstatus)); + return 0; + } + } + + /* + * If the BIOS is enabled on the 1542C/CF and BIOS options for support of drives + * > 1Gb, dynamic scanning of the SCSI bus or more than 2 drives under DOS 5.0 + * are enabled, the BIOS disables accepting Cmbinit to protect against running + * with drivers which don't support those options. In order to unlock the + * interface it is necessary to read a lock-code using Cextbios and write it back + * using Cmbienable; the lock-code is non-zero. + */ + cmd[0] = Cinquiry; + cmdlen = 1; + datalen = 4; + if(issue(ctlrno, port, cmd, cmdlen, data, datalen) < 0){ + print("scsi#%d: %s: Cinquiry\n", ctlrno, isa->type); + return 0; + } + if(data[0] >= 0x43){ + cmd[0] = Cextbios; + cmdlen = 1; + datalen = 2; + if(issue(ctlrno, port, cmd, cmdlen, data, datalen) < 0){ + print("scsi#%d: %s: Cextbios\n", ctlrno, isa->type); + return 0; + } + + /* + * Lock-code returned in data[1]. If it's non-zero write it back + * along with bit 0 of byte 0 cleared to enable mailbox initialisation. + */ + if(data[1]){ + cmd[0] = Cmbienable; + cmd[1] = 0; + cmd[2] = data[1]; + cmdlen = 3; + if(issue(ctlrno, port, cmd, cmdlen, 0, 0) < 0){ + print("scsi#%d: %s: Cmbienable\n", ctlrno, isa->type); + return 0; + } + } + } + + /* + * Get the DMA, IRQ and adapter SCSI ID from the board. + * This is necessary as the DMA won't be set up if the it's + * not a PCI adapter and the BIOS is disabled. + */ + cmd[0] = Cinquire; + cmdlen = 1; + datalen = 3; + if(issue(ctlrno, port, cmd, cmdlen, data, datalen) < 0){ + print("scsi#%d: %s: can't inquire configuration\n", ctlrno, isa->type); + return 0; + } + + if(pcidev && pcidev->intl) + isa->irq = pcidev->intl; + else{ + switch(data[0]){ /* DMA Arbitration Priority */ + case 0x80: /* Channel 7 */ + outb(0xD6, 0xC3); + outb(0xD4, 0x03); + isa->dma = 7; + break; + case 0x40: /* Channel 6 */ + outb(0xD6, 0xC2); + outb(0xD4, 0x02); + isa->dma = 6; + break; + case 0x20: /* Channel 5 */ + outb(0xD6, 0xC1); + outb(0xD4, 0x01); + isa->dma = 5; + break; + case 0x01: /* Channel 0 */ + outb(0x0B, 0xC0); + outb(0x0A, 0x00); + isa->dma = 0; + break; + default: + /* + * No DMA channel will show for 32-bit EISA/VLB + * cards which don't have ISA DMA compatibility set. + * Carry on regardless. + */ + isa->dma = -1; + break; + } + + switch(data[1]){ /* Interrupt Channel */ + case 0x40: + isa->irq = 15; + break; + case 0x20: + isa->irq = 14; + break; + case 0x08: + isa->irq = 12; + break; + case 0x04: + isa->irq = 11; + break; + case 0x02: + isa->irq = 10; + break; + case 0x01: + isa->irq = 9; + break; + default: + print("scsi#%d: %s: invalid irq #%2.2ux\n", + ctlrno, isa->type, data[1]); + return 0; + } + } + + /* + * Allocate and start to initialise the software controller. + */ + ctlr = ialloc(sizeof(Ctlr), 0); + + ctlr->port = isa->port; + ctlr->id = data[2] & 0x07; + ctlr->ctlrno = ctlrno; + ctlr->bus = bus; + ctlr->wide = wide; + if(pcidev) + ctlr->tbdf = pcidev->tbdf; + else + ctlr->tbdf = BUSUNKNOWN; + + if(bus == 24) + io = buslogic24(ctlr, isa); + else + io = buslogic32(ctlr, isa); + if(io){ + if(ctrls == 0) + cmd_install("scsi", "-- scsi stats", cmd_scsi); + ctrls |= 1< 7 + * + * 01/12/00 Removed previous comments. Fixed a small problem in + * mismatch recovery for targets with synchronous offsets of >=16 + * connected to >=875s. Thanks, Jean. + * + * Known problems + * + * Read/write mismatch recovery may fail on 53c1010s. Really need to get a manual. +*/ + +#define MAXTARGET 16 /* can be 8 or 16 */ + +/* Define one or the other ... */ +//#define CPU +#ifndef FS +#define FS +#endif + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#ifndef FS +#include "../port/error.h" +#endif + +#include "ureg.h" +#ifdef NAINCLUDE +#include "na/na.h" +#include "na/nasupport.h" +#endif /* NAINCLUDE */ + +/**********************************/ +/* Portable configuration macros */ +/**********************************/ + +//#define BOOTDEBUG +//#define SINGLE_TARGET +//#define ASYNC_ONLY +//#define QUICK_TIMEOUT +//#define INTERNAL_SCLK +//#define ALWAYS_DO_WDTR +#define WMR_DEBUG + +/**********************************/ +/* CPU specific macros */ +/**********************************/ + +#ifdef CPU +#ifdef BOOTDEBUG + +#define KPRINT oprint +#define IPRINT iprint +#define DEBUG(n) 0 +#define IFLUSH() iflush() + +#else + +#define KPRINT kprint +#define IPRINT kprint +#define DEBUG(n) scsidebugs[n] +#define IFLUSH() + +#endif +#endif + +/*******************************/ +/* Fileserver specific defines */ +/*******************************/ +#ifdef FS +#define DEVICENAME "ncr53c8xx" +#define PRINTPREFIX DEVICENAME ": " +#define xalloc(n) ialloc(n, 1) +#define KADDR(a) ((void*)((ulong)(a)|KZERO)) + +#define KPRINT if(0)print +#define IPRINT if(0)print +#define DEBUG(n) 0 +#define IFLUSH() +#endif + +/*******************************/ +/* General */ +/*******************************/ + +#ifndef DMASEG +#define DMASEG(x) PADDR(x) +#define legetl(x) (*(ulong*)(x)) +#define lesetl(x,v) (*(ulong*)(x) = (v)) +#define swabl(a,b,c) +#define IRQBASE Int0vec +#else +#define IRQBASE (PCIvec + 5) +#endif +#define DMASEG_TO_KADDR(x) KADDR(PADDR(x)) +#define KPTR(x) ((x) == 0 ? 0 : DMASEG_TO_KADDR(x)) + +#define MEGA 1000000L +#ifdef INTERNAL_SCLK +#define SCLK (33 * MEGA) +#else +#define SCLK (40 * MEGA) +#endif +#define ULTRA_NOCLOCKDOUBLE_SCLK (80 * MEGA) + +#define MAXSYNCSCSIRATE (5 * MEGA) +#define MAXFASTSYNCSCSIRATE (10 * MEGA) +#define MAXULTRASYNCSCSIRATE (20 * MEGA) +#define MAXULTRA2SYNCSCSIRATE (40 * MEGA) +#define MAXASYNCCORERATE (25 * MEGA) +#define MAXSYNCCORERATE (25 * MEGA) +#define MAXFASTSYNCCORERATE (50 * MEGA) +#define MAXULTRASYNCCORERATE (80 * MEGA) +#define MAXULTRA2SYNCCORERATE (160 * MEGA) + + +#define X_MSG 1 +#define X_MSG_SDTR 1 +#define X_MSG_WDTR 3 + +#ifndef NAINCLUDE +struct na_patch { + unsigned lwoff; + unsigned char type; +}; +#endif /* NAINCLUDE */ + +extern int scsidebugs[]; + +typedef struct Ncr { + uchar scntl0; /* 00 */ + uchar scntl1; + uchar scntl2; + uchar scntl3; + + uchar scid; /* 04 */ + uchar sxfer; + uchar sdid; + uchar gpreg; + + uchar sfbr; /* 08 */ + uchar socl; + uchar ssid; + uchar sbcl; + + uchar dstat; /* 0c */ + uchar sstat0; + uchar sstat1; + uchar sstat2; + + uchar dsa[4]; /* 10 */ + + uchar istat; /* 14 */ + uchar istatpad[3]; + + uchar ctest0; /* 18 */ + uchar ctest1; + uchar ctest2; + uchar ctest3; + + uchar temp[4]; /* 1c */ + + uchar dfifo; /* 20 */ + uchar ctest4; + uchar ctest5; + uchar ctest6; + + uchar dbc[3]; /* 24 */ + uchar dcmd; /* 27 */ + + uchar dnad[4]; /* 28 */ + uchar dsp[4]; /* 2c */ + uchar dsps[4]; /* 30 */ + + uchar scratcha[4]; /* 34 */ + + uchar dmode; /* 38 */ + uchar dien; + uchar dwt; + uchar dcntl; + + uchar adder[4]; /* 3c */ + + uchar sien0; /* 40 */ + uchar sien1; + uchar sist0; + uchar sist1; + + uchar slpar; /* 44 */ + uchar slparpad0; + uchar macntl; + uchar gpcntl; + + uchar stime0; /* 48 */ + uchar stime1; + uchar respid; + uchar respidpad0; + + uchar stest0; /* 4c */ + uchar stest1; + uchar stest2; + uchar stest3; + + uchar sidl; /* 50 */ + uchar sidlpad[3]; + + uchar sodl; /* 54 */ + uchar sodlpad[3]; + + uchar sbdl; /* 58 */ + uchar sbdlpad[3]; + + uchar scratchb[4]; /* 5c */ +} Ncr; + +typedef struct Movedata { + uchar dbc[4]; + uchar pa[4]; +} Movedata; + +typedef enum NegoState { + NeitherDone, WideInit, WideResponse, WideDone, + SyncInit, SyncResponse, BothDone +} NegoState; + +typedef enum State { + Allocated, Queued, Active, Done +} State; + +typedef struct Dsa { + union { + uchar state[4]; + struct { + uchar stateb; + uchar result; + uchar dmablks; + uchar flag; /* setbyte(state,3,...) */ + }; + }; + + union { + ulong dmancr; /* For block transfer: NCR order (little-endian) */ + uchar dmaaddr[4]; + }; + + uchar target; /* Target */ + uchar pad0[3]; + + uchar lun; /* Logical Unit Number */ + uchar pad1[3]; + + uchar scntl3; + uchar sxfer; + uchar pad2[2]; + + uchar next[4]; /* chaining for SCRIPT (NCR byte order) */ + struct Dsa *freechain; /* chaining for freelist */ + Rendez; + uchar scsi_id_buf[4]; + Movedata msg_out_buf; + Movedata cmd_buf; + Movedata data_buf; + Movedata status_buf; + uchar msg_out[10]; /* enough to include SDTR */ + uchar status; + ushort p9status; + uchar parityerror; +} Dsa; + +typedef enum Feature { + BigFifo = 1, /* 536 byte fifo */ + BurstOpCodeFetch = 2, /* burst fetch opcodes */ + Prefetch = 4, /* prefetch 8 longwords */ + LocalRAM = 8, /* 4K longwords of local RAM */ + Differential = 16, /* Differential support */ + Wide = 32, /* Wide capable */ + Ultra = 64, /* Ultra capable */ + ClockDouble = 128, /* Has clock doubler */ + ClockQuad = 256, /* Has clock quadrupler (same as Ultra2) */ + Ultra2 = 256, +} Feature; + +typedef enum Burst { + Burst2 = 0, + Burst4 = 1, + Burst8 = 2, + Burst16 = 3, + Burst32 = 4, + Burst64 = 5, + Burst128 = 6 +} Burst; + +typedef struct Variant { + ushort did; + uchar maxrid; /* maximum allowed revision ID */ + char *name; + Burst burst; /* codings for max burst */ + uchar maxsyncoff; /* max synchronous offset - must be power of 2 */ + uchar registers; /* number of 32 bit registers */ + unsigned feature; +} Variant; + + +static unsigned char cf2[] = { 6, 2, 3, 4, 6, 8, 12, 16 }; +#define NULTRA2SCF (sizeof(cf2)/sizeof(cf2[0])) +#define NULTRASCF (NULTRA2SCF - 2) +#define NSCF (NULTRASCF - 1) + +typedef struct Controller { + Lock; + struct { + uchar scntl3; + uchar stest2; + } bios; + int ctlrno; + uchar synctab[NULTRA2SCF - 1][8];/* table of legal tpfs */ + NegoState s[MAXTARGET]; + uchar scntl3[MAXTARGET]; + uchar sxfer[MAXTARGET]; + uchar cap[MAXTARGET]; /* capabilities byte from Identify */ + ushort capvalid; /* bit per target for validity of cap[] */ + ushort wide; /* bit per target set if wide negotiated */ + ulong sclk; /* clock speed of controller */ + uchar clockmult; /* set by synctabinit */ + uchar ccf; /* CCF bits */ + uchar tpf; /* best tpf value for this controller */ + uchar feature; /* requested features */ + int running; /* is the script processor running? */ + int ssm; /* single step mode */ + Ncr *n; /* pointer to registers */ + Variant *v; /* pointer to variant type */ + ulong *script; /* where the real script is */ + ulong scriptpa; /* where the real script is */ + + struct { + Lock; + uchar head[4]; /* head of free list (NCR byte order) */ + Dsa *tail; + Dsa *freechain; + } dsalist; + + QLock q[MAXTARGET]; /* queues for each target */ +} Controller; + +#define SYNCOFFMASK(c) (((c)->v->maxsyncoff * 2) - 1) +#define SSIDMASK(c) (((c)->v->feature & Wide) ? 15 : 7) + +static Controller *ctlrxx[MaxScsi]; + +/* ISTAT */ +enum { Abrt = 0x80, Srst = 0x40, Sigp = 0x20, Sem = 0x10, Con = 0x08, Intf = 0x04, Sip = 0x02, Dip = 0x01 }; + +/* DSTAT */ +enum { Dfe = 0x80, Mdpe = 0x40, Bf = 0x20, Abrted = 0x10, Ssi = 0x08, Sir = 0x04, Iid = 0x01 }; + +/* SSTAT */ +enum { DataOut, DataIn, Cmd, Status, ReservedOut, ReservedIn, MessageOut, MessageIn }; + +#define STATUS_COMPLETE 0x6000 +#define STATUS_FAIL 0x8000 +#define STATUS_SELECTION_TIMEOUT 0x0200 + +static void setmovedata(Movedata*, ulong, ulong); +static void advancedata(Movedata*, long); +static int bios_set_differential(Controller *c); + +static char *phase[] = { + "data out", "data in", "command", "status", + "reserved out", "reserved in", "message out", "message in" +}; + +#ifdef BOOTDEBUG +#define DEBUGSIZE 10240 +char debugbuf[DEBUGSIZE]; +char *debuglast; + +void +iprint(char *format, ...) +{ + if (debuglast == 0) + debuglast = debugbuf; + debuglast = doprint(debuglast, debugbuf + (DEBUGSIZE - 1), format, (&format + 1)); +} + +void +iflush() +{ + int s; + char *endp; + s = splhi(); + if (debuglast == 0) + debuglast = debugbuf; + if (debuglast == debugbuf) { + splx(s); + return; + } + endp = debuglast; + splx(s); + screenputs(debugbuf, endp - debugbuf); + s = splhi(); + memmove(debugbuf, endp, debuglast - endp); + debuglast -= endp - debugbuf; + splx(s); +} + +void +oprint(char *format, ...) +{ + int s; + + iflush(); + s = splhi(); + if (debuglast == 0) + debuglast = debugbuf; + debuglast = doprint(debuglast, debugbuf + (DEBUGSIZE - 1), format, (&format + 1)); + splx(s); + iflush(); +} +#endif + +#include "script.i" + +static Dsa * +dsaalloc(Controller *c, int target, int lun) +{ + Dsa *d; + + ilock(&c->dsalist); + if ((d = c->dsalist.freechain) == 0) { + d = xalloc(sizeof(*d)); + if (DEBUG(1)) + KPRINT(PRINTPREFIX "%d/%d: allocated new dsa %lux\n", target, lun, (ulong)d); + lesetl(d->next, 0); + lesetl(d->state, A_STATE_ALLOCATED); + if (legetl(c->dsalist.head) == 0) + lesetl(c->dsalist.head, DMASEG(d)); /* ATOMIC?!? */ + else + lesetl(c->dsalist.tail->next, DMASEG(d)); /* ATOMIC?!? */ + c->dsalist.tail = d; + } + else { + if (DEBUG(1)) + KPRINT(PRINTPREFIX "%d/%d: reused dsa %lux\n", target, lun, (ulong)d); + c->dsalist.freechain = d->freechain; + lesetl(d->state, A_STATE_ALLOCATED); + } + iunlock(&c->dsalist); + d->target = target; + d->lun = lun; + return d; +} + +static void +dsafree(Controller *c, Dsa *d) +{ + ilock(&c->dsalist); + d->freechain = c->dsalist.freechain; + c->dsalist.freechain = d; + lesetl(d->state, A_STATE_FREE); + iunlock(&c->dsalist); +} + +static Dsa * +dsafind(Controller *c, uchar target, uchar lun, uchar state) +{ + Dsa *d; + for (d = KPTR(legetl(c->dsalist.head)); d; d = KPTR(legetl(d->next))) { + if (d->target != 0xff && d->target != target) + continue; + if (lun != 0xff && d->lun != lun) + continue; + if (state != 0xff && d->stateb != state) + continue; + break; + } + return d; +} + +static void +dumpncrregs(Controller *c, int intr) +{ + int i; + Ncr *n = c->n; + int depth = c->v->registers / 4; + + KPRINT("sa = %.8lux\n", c->scriptpa); + for (i = 0; i < depth; i++) { + int j; + for (j = 0; j < 4; j++) { + int k = j * depth + i; + uchar *p; + + /* display little-endian to make 32-bit values readable */ + p = (uchar*)n+k*4; + if (intr) + IPRINT(" %.2x%.2x%.2x%.2x %.2x %.2x", p[3], p[2], p[1], p[0], k * 4, (k * 4) + 0x80); + else + KPRINT(" %.2x%.2x%.2x%.2x %.2x %.2x", p[3], p[2], p[1], p[0], k * 4, (k * 4) + 0x80); + USED(p); + } + if (intr) + IPRINT("\n"); + else + KPRINT("\n"); + } +} + +static int +chooserate(Controller *c, int tpf, int *scfp, int *xferpp) +{ + /* find lowest entry >= tpf */ + int besttpf = 1000; + int bestscfi = 0; + int bestxferp = 0; + int scf, xferp; + int maxscf; + + if (c->v->feature & Ultra2) + maxscf = NULTRA2SCF; + else if (c->v->feature & Ultra) + maxscf = NULTRASCF; + else + maxscf = NSCF; + + /* + * search large clock factors first since this should + * result in more reliable transfers + */ + for (scf = maxscf; scf >= 1; scf--) { + for (xferp = 0; xferp < 8; xferp++) { + unsigned char v = c->synctab[scf - 1][xferp]; + if (v == 0) + continue; + if (v >= tpf && v < besttpf) { + besttpf = v; + bestscfi = scf; + bestxferp = xferp; + } + } + } + if (besttpf == 1000) + return 0; + if (scfp) + *scfp = bestscfi; + if (xferpp) + *xferpp = bestxferp; + return besttpf; +} + +static void +synctabinit(Controller *c) +{ + int scf; + unsigned long scsilimit; + int xferp; + unsigned long cr, sr; + int tpf; + int fast; + int maxscf; + + if (c->v->feature & Ultra2) + maxscf = NULTRA2SCF; + else if (c->v->feature & Ultra) + maxscf = NULTRASCF; + else + maxscf = NSCF; + + /* + * for chips with no clock doubler, but Ultra capable (e.g. 860, or interestingly the + * first spin of the 875), assume 80MHz + * otherwise use the internal (33 Mhz) or external (40MHz) default + */ + + if ((c->v->feature & Ultra) != 0 && (c->v->feature & (ClockDouble | ClockQuad)) == 0) + c->sclk = ULTRA_NOCLOCKDOUBLE_SCLK; + else + c->sclk = SCLK; + + /* + * otherwise, if the chip is Ultra capable, but has a slow(ish) clock, + * invoke the doubler + */ + + if (SCLK <= 40000000) { + if (c->v->feature & ClockDouble) { + c->sclk *= 2; + c->clockmult = 1; + } + else if (c->v->feature & ClockQuad) { + c->sclk *= 4; + c->clockmult = 1; + } + else + c->clockmult = 0; + } + else + c->clockmult = 0; + + /* derive CCF from sclk */ + /* woebetide anyone with SCLK < 16.7 or > 80MHz */ + if (c->sclk <= 25 * MEGA) + c->ccf = 1; + else if (c->sclk <= 3750000) + c->ccf = 2; + else if (c->sclk <= 50 * MEGA) + c->ccf = 3; + else if (c->sclk <= 75 * MEGA) + c->ccf = 4; + else if ((c->v->feature & ClockDouble) && c->sclk <= 80 * MEGA) + c->ccf = 5; + else if ((c->v->feature & ClockQuad) && c->sclk <= 120 * MEGA) + c->ccf = 6; + else if ((c->v->feature & ClockQuad) && c->sclk <= 160 * MEGA) + c->ccf = 7; + + for (scf = 1; scf < maxscf; scf++) { + /* check for legal core rate */ + /* round up so we run slower for safety */ + cr = (c->sclk * 2 + cf2[scf] - 1) / cf2[scf]; + if (cr <= MAXSYNCCORERATE) { + scsilimit = MAXSYNCSCSIRATE; + fast = 0; + } + else if (cr <= MAXFASTSYNCCORERATE) { + scsilimit = MAXFASTSYNCSCSIRATE; + fast = 1; + } + else if ((c->v->feature & Ultra) && cr <= MAXULTRASYNCCORERATE) { + scsilimit = MAXULTRASYNCSCSIRATE; + fast = 2; + } + else if ((c->v->feature & Ultra2) && cr <= MAXULTRA2SYNCCORERATE) { + scsilimit = MAXULTRA2SYNCSCSIRATE; + fast = 3; + } + else + continue; + for (xferp = 11; xferp >= 4; xferp--) { + int ok; + int tp; + /* calculate scsi rate - round up again */ + /* start from sclk for accuracy */ + int totaldivide = xferp * cf2[scf]; + sr = (c->sclk * 2 + totaldivide - 1) / totaldivide; + if (sr > scsilimit) + break; + /* + * now work out transfer period + * round down now so that period is pessimistic + */ + tp = (MEGA * 1000) / sr; + /* + * bounds check it + */ + if (tp < 25 || tp > 255 * 4) + continue; + /* + * spot stupid special case for Ultra or Ultra2 + * while working out factor + */ + if (tp == 25) + tpf = 10; + else if (tp == 50) + tpf = 12; + else if (tp < 52) + continue; + else + tpf = tp / 4; + /* + * now check tpf looks sensible + * given core rate + */ + switch (fast) { + case 0: + /* scf must be ccf for SCSI 1 */ + ok = tpf >= 50 && scf == c->ccf; + break; + case 1: + ok = tpf >= 25 && tpf < 50; + break; + case 2: + /* + * must use xferp of 4, or 5 at a pinch + * for an Ultra transfer + */ + ok = xferp <= 5 && tpf >= 12 && tpf < 25; + break; + case 3: + ok = xferp == 4 && (tpf == 10 || tpf == 11); + break; + default: + ok = 0; + } + if (!ok) + continue; + c->synctab[scf - 1][xferp - 4] = tpf; + } + } + +#ifndef NO_ULTRA2 + if (c->v->feature & Ultra2) + tpf = 10; + else +#endif + if (c->v->feature & Ultra) + tpf = 12; + else + tpf = 25; + for (; tpf < 256; tpf++) { + if (chooserate(c, tpf, &scf, &xferp) == tpf) { + unsigned tp = tpf == 10 ? 25 : (tpf == 12 ? 50 : tpf * 4); + unsigned long khz = (MEGA + tp - 1) / (tp); + KPRINT(PRINTPREFIX "tpf=%d scf=%d.%.1d xferp=%d mhz=%ld.%.3ld\n", + tpf, cf2[scf] / 2, (cf2[scf] & 1) ? 5 : 0, + xferp + 4, khz / 1000, khz % 1000); + USED(khz); + if (c->tpf == 0) + c->tpf = tpf; /* note lowest value for controller */ + } + } +} + +static void +synctodsa(Dsa *dsa, Controller *c) +{ +/* + KPRINT("synctodsa(dsa=%lux, target=%d, scntl3=%.2lx sxfer=%.2x)\n", + dsa, dsa->target, c->scntl3[dsa->target], c->sxfer[dsa->target]); +*/ + dsa->scntl3 = c->scntl3[dsa->target]; + dsa->sxfer = c->sxfer[dsa->target]; +} + +static void +setsync(Dsa *dsa, Controller *c, int target, uchar ultra, uchar scf, uchar xferp, uchar reqack) +{ + c->scntl3[target] = + (c->scntl3[target] & 0x08) | (((scf << 4) | c->ccf | (ultra << 7)) & ~0x08); + c->sxfer[target] = (xferp << 5) | reqack; + c->s[target] = BothDone; + if (dsa) { + synctodsa(dsa, c); + c->n->scntl3 = c->scntl3[target]; + c->n->sxfer = c->sxfer[target]; + } +} + +static void +setasync(Dsa *dsa, Controller *c, int target) +{ + setsync(dsa, c, target, 0, c->ccf, 0, 0); +} + +static void +setwide(Dsa *dsa, Controller *c, int target, uchar wide) +{ + c->scntl3[target] = wide ? (1 << 3) : 0; + setasync(dsa, c, target); + c->s[target] = WideDone; +} + +static int +buildsdtrmsg(uchar *buf, uchar tpf, uchar offset) +{ + *buf++ = X_MSG; + *buf++ = 3; + *buf++ = X_MSG_SDTR; + *buf++ = tpf; + *buf = offset; + return 5; +} + +static int +buildwdtrmsg(uchar *buf, uchar expo) +{ + *buf++ = X_MSG; + *buf++ = 2; + *buf++ = X_MSG_WDTR; + *buf = expo; + return 4; +} + +static void +start(Controller *c, long entry) +{ + ulong p; + + if (c->running) + panic(PRINTPREFIX "start called while running"); + c->running = 1; + p = c->scriptpa + entry; + lesetl(c->n->dsp, p); + if (c->ssm) + c->n->dcntl |= 0x4; /* start DMA in SSI mode */ +} + +static void +ncrcontinue(Controller *c) +{ + if (c->running) + panic(PRINTPREFIX "ncrcontinue called while running"); + /* set the start DMA bit to continue execution */ + c->running = 1; + c->n->dcntl |= 0x4; +} + +static void +softreset(Controller *c) +{ + Ncr *n = c->n; + + n->istat = Srst; /* software reset */ + n->istat = 0; + /* general initialisation */ + n->scid = (1 << 6) | 7; /* respond to reselect, ID 7 */ + n->respid = 1 << 7; /* response ID = 7 */ + +#ifdef INTERNAL_SCLK + n->stest1 = 0x80; /* disable external scsi clock */ +#else + n->stest1 = 0x00; +#endif + + n->stime0 = 0xdd; /* about 0.5 second timeout on each device */ + n->scntl0 |= 0x8; /* Enable parity checking */ + + /* continued setup */ + n->sien0 = 0x8f; + n->sien1 = 0x04; + n->dien = 0x7d; + n->stest3 = 0x80; /* TolerANT enable */ + c->running = 0; + + if (c->v->feature & BigFifo) + n->ctest5 = (1 << 5); + n->dmode = c->v->burst << 6; /* set burst length bits */ + if (c->v->burst & 4) + n->ctest5 |= (1 << 2); /* including overflow into ctest5 bit 2 */ + if (c->v->feature & Prefetch) + n->dcntl |= (1 << 5); /* prefetch enable */ + else if (c->v->feature & BurstOpCodeFetch) + n->dmode |= (1 << 1); /* burst opcode fetch */ + if (c->v->feature & Differential) { + /* chip capable */ + if ((c->feature & Differential) || bios_set_differential(c)) { + /* user enabled, or some evidence bios set differential */ + if (n->sstat2 & (1 << 2)) + print(PRINTPREFIX "can't go differential; wrong cable\n"); + else { + n->stest2 = (1 << 5); + print(PRINTPREFIX "differential mode set\n"); + } + } + } + if (c->clockmult) { + n->stest1 |= (1 << 3); /* power up doubler */ + delay(2); + n->stest3 |= (1 << 5); /* stop clock */ + n->stest1 |= (1 << 2); /* enable doubler */ + n->stest3 &= ~(1 << 5); /* start clock */ + /* pray */ + } +} + +static void +msgsm(Dsa *dsa, Controller *c, int msg, int *cont, int *wakeme) +{ + uchar histpf, hisreqack; + int tpf; + int scf, xferp; + int len; + + Ncr *n = c->n; + + switch (c->s[dsa->target]) { + case SyncInit: + switch (msg) { + case A_SIR_MSG_SDTR: + /* reply to my SDTR */ + histpf = n->scratcha[2]; + hisreqack = n->scratcha[3]; + KPRINT(PRINTPREFIX "%d: SDTN response %d %d\n", + dsa->target, histpf, hisreqack); + + if (hisreqack == 0) + setasync(dsa, c, dsa->target); + else { + /* hisreqack should be <= c->v->maxsyncoff */ + tpf = chooserate(c, histpf, &scf, &xferp); + KPRINT(PRINTPREFIX "%d: SDTN: using %d %d\n", + dsa->target, tpf, hisreqack); + setsync(dsa, c, dsa->target, tpf < 25, scf, xferp, hisreqack); + } + *cont = -2; + return; + case A_SIR_EV_PHASE_SWITCH_AFTER_ID: + /* target ignored ATN for message after IDENTIFY - not SCSI-II */ + KPRINT(PRINTPREFIX "%d: illegal phase switch after ID message - SCSI-1 device?\n", dsa->target); + KPRINT(PRINTPREFIX "%d: SDTN: async\n", dsa->target); + setasync(dsa, c, dsa->target); + *cont = E_to_decisions; + return; + case A_SIR_MSG_REJECT: + /* rejection of my SDTR */ + KPRINT(PRINTPREFIX "%d: SDTN: rejected SDTR\n", dsa->target); + //async: + KPRINT(PRINTPREFIX "%d: SDTN: async\n", dsa->target); + setasync(dsa, c, dsa->target); + *cont = -2; + return; + } + break; + case WideInit: + switch (msg) { + case A_SIR_MSG_WDTR: + /* reply to my WDTR */ + KPRINT(PRINTPREFIX "%d: WDTN: response %d\n", + dsa->target, n->scratcha[2]); + setwide(dsa, c, dsa->target, n->scratcha[2]); + *cont = -2; + return; + case A_SIR_EV_PHASE_SWITCH_AFTER_ID: + /* target ignored ATN for message after IDENTIFY - not SCSI-II */ + KPRINT(PRINTPREFIX "%d: illegal phase switch after ID message - SCSI-1 device?\n", dsa->target); + setwide(dsa, c, dsa->target, 0); + *cont = E_to_decisions; + return; + case A_SIR_MSG_REJECT: + /* rejection of my SDTR */ + KPRINT(PRINTPREFIX "%d: WDTN: rejected WDTR\n", dsa->target); + setwide(dsa, c, dsa->target, 0); + *cont = -2; + return; + } + break; + + case NeitherDone: + case WideDone: + case BothDone: + switch (msg) { + case A_SIR_MSG_WDTR: { + uchar hiswide, mywide; + hiswide = n->scratcha[2]; + mywide = (c->v->feature & Wide) != 0; + KPRINT(PRINTPREFIX "%d: WDTN: target init %d\n", + dsa->target, hiswide); + if (hiswide < mywide) + mywide = hiswide; + KPRINT(PRINTPREFIX "%d: WDTN: responding %d\n", + dsa->target, mywide); + setwide(dsa, c, dsa->target, mywide); + len = buildwdtrmsg(dsa->msg_out, mywide); + setmovedata(&dsa->msg_out_buf, DMASEG(dsa->msg_out), len); + *cont = E_response; + c->s[dsa->target] = WideResponse; + return; + } + case A_SIR_MSG_SDTR: +#ifdef ASYNC_ONLY + *cont = E_reject; + return; +#else + /* target decides to renegotiate */ + histpf = n->scratcha[2]; + hisreqack = n->scratcha[3]; + KPRINT(PRINTPREFIX "%d: SDTN: target init %d %d\n", + dsa->target, histpf, hisreqack); + if (hisreqack == 0) { + /* he wants asynchronous */ + setasync(dsa, c, dsa->target); + tpf = 0; + } + else { + /* he wants synchronous */ + tpf = chooserate(c, histpf, &scf, &xferp); + if (hisreqack > c->v->maxsyncoff) + hisreqack = c->v->maxsyncoff; + KPRINT(PRINTPREFIX "%d: using %d %d\n", + dsa->target, tpf, hisreqack); + setsync(dsa, c, dsa->target, tpf < 25, scf, xferp, hisreqack); + } + /* build my SDTR message */ + len = buildsdtrmsg(dsa->msg_out, tpf, hisreqack); + setmovedata(&dsa->msg_out_buf, DMASEG(dsa->msg_out), len); + *cont = E_response; + c->s[dsa->target] = SyncResponse; + return; +#endif + } + break; + case WideResponse: + switch (msg) { + case A_SIR_EV_RESPONSE_OK: + c->s[dsa->target] = WideDone; + KPRINT(PRINTPREFIX "%d: WDTN: response accepted\n", dsa->target); + *cont = -2; + return; + case A_SIR_MSG_REJECT: + setwide(dsa, c, dsa->target, 0); + KPRINT(PRINTPREFIX "%d: WDTN: response REJECTed\n", dsa->target); + *cont = -2; + return; + } + break; + case SyncResponse: + switch (msg) { + case A_SIR_EV_RESPONSE_OK: + c->s[dsa->target] = BothDone; + KPRINT(PRINTPREFIX "%d: SDTN: response accepted (%s)\n", + dsa->target, phase[n->sstat1 & 7]); + *cont = -2; + return; /* chf */ + case A_SIR_MSG_REJECT: + setasync(dsa, c, dsa->target); + KPRINT(PRINTPREFIX "%d: SDTN: response REJECTed\n", dsa->target); + *cont = -2; + return; + } + break; + } + KPRINT(PRINTPREFIX "%d: msgsm: state %d msg %d\n", + dsa->target, c->s[dsa->target], msg); + *wakeme = 1; + return; +} + +static void +calcblockdma(Dsa *d, ulong base, ulong count) +{ + ulong blocks; + if (DEBUG(3)) + blocks = 0; + else { + blocks = count / A_BSIZE; + if (blocks > 255) + blocks = 255; + } + d->dmablks = blocks; + d->dmaaddr[0] = base; + d->dmaaddr[1] = base >> 8; + d->dmaaddr[2] = base >> 16; + d->dmaaddr[3] = base >> 24; + setmovedata(&d->data_buf, base + blocks * A_BSIZE, count - blocks * A_BSIZE); + if (legetl(d->data_buf.dbc) == 0) + d->flag = 1; +} + +static ulong +read_mismatch_recover(Controller *c, Ncr *n, Dsa *dsa) +{ + ulong dbc; + uchar dfifo = n->dfifo; + int inchip; + + dbc = (n->dbc[2]<<16)|(n->dbc[1]<<8)|n->dbc[0]; + if (n->ctest5 & (1 << 5)) + inchip = ((dfifo | ((n->ctest5 & 3) << 8)) - (dbc & 0x3ff)) & 0x3ff; + else + inchip = ((dfifo & 0x7f) - (dbc & 0x7f)) & 0x7f; + if (inchip) { + IPRINT(PRINTPREFIX "%d/%d: read_mismatch_recover: DMA FIFO = %d\n", + dsa->target, dsa->lun, inchip); + } + if (n->sxfer & SYNCOFFMASK(c)) { + /* SCSI FIFO */ + uchar fifo = n->sstat1 >> 4; + if (c->v->maxsyncoff > 8) + fifo |= (n->sstat2 & (1 << 4)); + if (fifo) { + inchip += fifo; + IPRINT(PRINTPREFIX "%d/%d: read_mismatch_recover: SCSI FIFO = %d\n", + dsa->target, dsa->lun, fifo); + } + } + else { + if (n->sstat0 & (1 << 7)) { + inchip++; + IPRINT(PRINTPREFIX "%d/%d: read_mismatch_recover: SIDL full\n", + dsa->target, dsa->lun); + } + if (n->sstat2 & (1 << 7)) { + inchip++; + IPRINT(PRINTPREFIX "%d/%d: read_mismatch_recover: SIDL msb full\n", + dsa->target, dsa->lun); + } + } + USED(inchip); + return dbc; +} + +static ulong +write_mismatch_recover(Controller *c, Ncr *n, Dsa *dsa) +{ + ulong dbc; + uchar dfifo = n->dfifo; + int inchip; + + dbc = (n->dbc[2]<<16)|(n->dbc[1]<<8)|n->dbc[0]; + USED(dsa); + if (n->ctest5 & (1 << 5)) + inchip = ((dfifo | ((n->ctest5 & 3) << 8)) - (dbc & 0x3ff)) & 0x3ff; + else + inchip = ((dfifo & 0x7f) - (dbc & 0x7f)) & 0x7f; +#ifdef WMR_DEBUG + if (inchip) { + IPRINT(PRINTPREFIX "%d/%d: write_mismatch_recover: DMA FIFO = %d\n", + dsa->target, dsa->lun, inchip); + } +#endif + if (n->sstat0 & (1 << 5)) { + inchip++; +#ifdef WMR_DEBUG + IPRINT(PRINTPREFIX "%d/%d: write_mismatch_recover: SODL full\n", dsa->target, dsa->lun); +#endif + } + if (n->sstat2 & (1 << 5)) { + inchip++; +#ifdef WMR_DEBUG + IPRINT(PRINTPREFIX "%d/%d: write_mismatch_recover: SODL msb full\n", dsa->target, dsa->lun); +#endif + } + if (n->sxfer & SYNCOFFMASK(c)) { + /* synchronous SODR */ + if (n->sstat0 & (1 << 6)) { + inchip++; +#ifdef WMR_DEBUG + IPRINT(PRINTPREFIX "%d/%d: write_mismatch_recover: SODR full\n", + dsa->target, dsa->lun); +#endif + } + if (n->sstat2 & (1 << 6)) { + inchip++; +#ifdef WMR_DEBUG + IPRINT(PRINTPREFIX "%d/%d: write_mismatch_recover: SODR msb full\n", + dsa->target, dsa->lun); +#endif + } + } + /* clear the dma fifo */ + n->ctest3 |= (1 << 2); + /* wait till done */ + while ((n->dstat & Dfe) == 0) + ; + return dbc + inchip; +} + +static void +interrupt(Ureg *ur, void *a) +{ + int wakeme = 0, cont = -1; + uchar istat, dstat; + ushort sist; + Controller *c = a; + Ncr *n = c->n; + Dsa *dsa; + + USED(ur); + if (DEBUG(1)) + IPRINT(PRINTPREFIX "int\n"); + ilock(c); + istat = n->istat; + if (istat & Intf) { + Dsa *d; + int wokesomething = 0; + if (DEBUG(1)) + IPRINT(PRINTPREFIX "Intfly\n"); + n->istat = Intf; + /* search for structures in A_STATE_DONE */ + for (d = KPTR(legetl(c->dsalist.head)); d; d = KPTR(legetl(d->next))) { + if (d->stateb == A_STATE_DONE) { + d->p9status = STATUS_COMPLETE | d->status; + if (DEBUG(1)) + IPRINT(PRINTPREFIX "waking up dsa %lux\n", (ulong)d); + wakeup(d); + wokesomething = 1; + } + } + if (!wokesomething) + IPRINT(PRINTPREFIX "nothing to wake up\n"); + } + + if ((istat & (Sip | Dip)) == 0) { + if (DEBUG(1)) + IPRINT(PRINTPREFIX "int end %x\n", istat); + iunlock(c); + return; + } + + sist = (n->sist1<<8)|n->sist0; /* BUG? can two-byte read be inconsistent? */ + dstat = n->dstat; + + dsa = (Dsa *)DMASEG_TO_KADDR(legetl(n->dsa)); + c->running = 0; + if (dsa == nil || dsa == (Dsa *)-1) + panic("53c8xx: dsa == %ld in interrupt; bad controller", (long)dsa); + if (istat & Sip) { + if (DEBUG(1)) + IPRINT("sist = %.4x\n", sist); + if (sist & 0x80) { + ulong addr; + ulong sa; + ulong dbc; + ulong tbc; + int dmablks; + ulong dmaaddr; + + addr = legetl(n->dsp); + sa = addr - c->scriptpa; + if (DEBUG(1) || DEBUG(2)) + IPRINT(PRINTPREFIX "%d/%d: Phase Mismatch sa=%.8lux\n", + dsa->target, dsa->lun, sa); + /* + * now recover + */ + if (sa == E_data_in_mismatch) { + dbc = read_mismatch_recover(c, n, dsa); + tbc = legetl(dsa->data_buf.dbc) - dbc; + advancedata(&dsa->data_buf, tbc); + if (DEBUG(1) || DEBUG(2)) + IPRINT(PRINTPREFIX "%d/%d: transferred = %ld residue = %ld\n", + dsa->target, dsa->lun, tbc, legetl(dsa->data_buf.dbc)); + cont = E_to_decisions; + } + else if (sa == E_data_in_block_mismatch) { + dbc = read_mismatch_recover(c, n, dsa); + tbc = A_BSIZE - dbc; + /* recover current state from registers */ + dmablks = n->scratcha[2]; + dmaaddr = legetl(n->scratchb); + /* we have got to dmaaddr + tbc */ + /* we have dmablks * A_BSIZE - tbc + residue left to do */ + /* so remaining transfer is */ + IPRINT("in_block_mismatch: dmaaddr = 0x%lux tbc=%lud dmablks=%d\n", + dmaaddr, tbc, dmablks); + calcblockdma(dsa, dmaaddr + tbc, + dmablks * A_BSIZE - tbc + legetl(dsa->data_buf.dbc)); + /* copy changes into scratch registers */ + IPRINT("recalc: dmablks %d dmaaddr 0x%lx pa 0x%lx dbc %ld\n", + dsa->dmablks, legetl(dsa->dmaaddr), + legetl(dsa->data_buf.pa), legetl(dsa->data_buf.dbc)); + n->scratcha[2] = dsa->dmablks; + lesetl(n->scratchb, dsa->dmancr); + cont = E_data_block_mismatch_recover; + } + else if (sa == E_data_out_mismatch) { + dbc = write_mismatch_recover(c, n, dsa); + tbc = legetl(dsa->data_buf.dbc) - dbc; + advancedata(&dsa->data_buf, tbc); + if (DEBUG(1) || DEBUG(2)) + IPRINT(PRINTPREFIX "%d/%d: transferred = %ld residue = %ld\n", + dsa->target, dsa->lun, tbc, legetl(dsa->data_buf.dbc)); + cont = E_to_decisions; + } + else if (sa == E_data_out_block_mismatch) { + dbc = write_mismatch_recover(c, n, dsa); + tbc = legetl(dsa->data_buf.dbc) - dbc; + /* recover current state from registers */ + dmablks = n->scratcha[2]; + dmaaddr = legetl(n->scratchb); + /* we have got to dmaaddr + tbc */ + /* we have dmablks blocks - tbc + residue left to do */ + /* so remaining transfer is */ + IPRINT("out_block_mismatch: dmaaddr = %lux tbc=%lud dmablks=%d\n", + dmaaddr, tbc, dmablks); + calcblockdma(dsa, dmaaddr + tbc, + dmablks * A_BSIZE - tbc + legetl(dsa->data_buf.dbc)); + /* copy changes into scratch registers */ + n->scratcha[2] = dsa->dmablks; + lesetl(n->scratchb, dsa->dmancr); + cont = E_data_block_mismatch_recover; + } + else if (sa == E_id_out_mismatch) { + /* + * target switched phases while attention held during + * message out. The possibilities are: + * 1. It didn't like the last message. This is indicated + * by the new phase being message_in. Use script to recover + * + * 2. It's not SCSI-II compliant. The new phase will be other + * than message_in. We should also indicate that the device + * is asynchronous, if it's the SDTR that got ignored + * + * For now, if the phase switch is not to message_in, and + * and it happens after IDENTIFY and before SDTR, we + * notify the negotiation state machine. + */ + ulong lim = legetl(dsa->msg_out_buf.dbc); + uchar p = n->sstat1 & 7; + dbc = write_mismatch_recover(c, n, dsa); + tbc = lim - dbc; + IPRINT(PRINTPREFIX "%d/%d: msg_out_mismatch: %lud/%lud sent, phase %s\n", + dsa->target, dsa->lun, tbc, lim, phase[p]); + if (p != MessageIn && tbc == 1) { + msgsm(dsa, c, A_SIR_EV_PHASE_SWITCH_AFTER_ID, &cont, &wakeme); + } + else + cont = E_id_out_mismatch_recover; + } + else if (sa == E_cmd_out_mismatch) { + /* + * probably the command count is longer than the device wants ... + */ + ulong lim = legetl(dsa->cmd_buf.dbc); + uchar p = n->sstat1 & 7; + dbc = write_mismatch_recover(c, n, dsa); + tbc = lim - dbc; + IPRINT(PRINTPREFIX "%d/%d: cmd_out_mismatch: %lud/%lud sent, phase %s\n", + dsa->target, dsa->lun, tbc, lim, phase[p]); + USED(p, tbc); + cont = E_to_decisions; + } + else { + IPRINT(PRINTPREFIX "%d/%d: ma sa=%.8lux wanted=%s got=%s\n", + dsa->target, dsa->lun, sa, + phase[n->dcmd & 7], + phase[n->sstat1 & 7]); + dumpncrregs(c, 1); + dsa->p9status = STATUS_FAIL; /* chf */ + wakeme = 1; + } + } + /*else*/ if (sist & 0x400) { + if (DEBUG(0)) + IPRINT(PRINTPREFIX "%d/%d Sto\n", dsa->target, dsa->lun); + dsa->p9status = STATUS_SELECTION_TIMEOUT; + dsa->stateb = A_STATE_DONE; + softreset(c); + cont = E_issue_check; + wakeme = 1; + } + if (sist & 0x1) { + IPRINT(PRINTPREFIX "%d/%d: parity error\n", dsa->target, dsa->lun); + dsa->parityerror = 1; + } + if (sist & 0x4) { + IPRINT(PRINTPREFIX "%d/%d: unexpected disconnect\n", + dsa->target, dsa->lun); + dumpncrregs(c, 1); + //wakeme = 1; + dsa->p9status = STATUS_FAIL; + } + } + if (istat & Dip) { + if (DEBUG(1)) + IPRINT("dstat = %.2x\n", dstat); + /*else*/ if (dstat & Ssi) { + ulong *p = DMASEG_TO_KADDR(legetl(n->dsp)); + ulong w = (uchar *)p - (uchar *)c->script; + IPRINT("[%lux]", w); + USED(w); + cont = -2; /* restart */ + } + if (dstat & Sir) { + switch (legetl(n->dsps)) { + case A_SIR_MSG_IO_COMPLETE: + dsa->p9status = STATUS_COMPLETE | dsa->status; + wakeme = 1; + break; + case A_SIR_MSG_SDTR: + case A_SIR_MSG_WDTR: + case A_SIR_MSG_REJECT: + case A_SIR_EV_RESPONSE_OK: + msgsm(dsa, c, legetl(n->dsps), &cont, &wakeme); + break; + case A_SIR_MSG_IGNORE_WIDE_RESIDUE: + /* back up one in the data transfer */ + IPRINT(PRINTPREFIX "%d/%d: ignore wide residue %d, WSR = %d\n", + dsa->target, dsa->lun, n->scratcha[1], n->scntl2 & 1); + if (dsa->dmablks == 0 && dsa->flag) + IPRINT(PRINTPREFIX "%d/%d: transfer over; residue ignored\n", + dsa->target, dsa->lun); + else + calcblockdma(dsa, legetl(dsa->dmaaddr) - 1, + dsa->dmablks * A_BSIZE + legetl(dsa->data_buf.dbc) + 1); + cont = -2; + break; + case A_SIR_ERROR_NOT_MSG_IN_AFTER_RESELECT: + IPRINT(PRINTPREFIX "%d: not msg_in after reselect (%s)", + n->ssid & SSIDMASK(c), phase[n->sstat1 & 7]); + dsa = dsafind(c, n->ssid & SSIDMASK(c), -1, A_STATE_DISCONNECTED); + dumpncrregs(c, 1); + wakeme = 1; + break; + case A_SIR_NOTIFY_MSG_IN: + IPRINT(PRINTPREFIX "%d/%d: msg_in %d\n", + dsa->target, dsa->lun, n->sfbr); + cont = -2; + break; + case A_SIR_NOTIFY_DISC: + IPRINT(PRINTPREFIX "%d/%d: disconnect:", dsa->target, dsa->lun); + goto dsadump; + case A_SIR_NOTIFY_STATUS: + IPRINT(PRINTPREFIX "%d/%d: status\n", dsa->target, dsa->lun); + cont = -2; + break; + case A_SIR_NOTIFY_COMMAND: + IPRINT(PRINTPREFIX "%d/%d: commands\n", dsa->target, dsa->lun); + cont = -2; + break; + case A_SIR_NOTIFY_DATA_IN: + IPRINT(PRINTPREFIX "%d/%d: data in a %lx b %lx\n", + dsa->target, dsa->lun, legetl(n->scratcha), legetl(n->scratchb)); + cont = -2; + break; + case A_SIR_NOTIFY_BLOCK_DATA_IN: + IPRINT(PRINTPREFIX "%d/%d: block data in: a2 %x b %lx\n", + dsa->target, dsa->lun, n->scratcha[2], legetl(n->scratchb)); + cont = -2; + break; + case A_SIR_NOTIFY_DATA_OUT: + IPRINT(PRINTPREFIX "%d/%d: data out\n", dsa->target, dsa->lun); + cont = -2; + break; + case A_SIR_NOTIFY_DUMP: + IPRINT(PRINTPREFIX "%d/%d: dump\n", dsa->target, dsa->lun); + dumpncrregs(c, 1); + cont = -2; + break; + case A_SIR_NOTIFY_DUMP2: + IPRINT(PRINTPREFIX "%d/%d: dump2:", dsa->target, dsa->lun); + IPRINT(" sa %lux", legetl(n->dsp) - c->scriptpa); + IPRINT(" dsa %lux", legetl(n->dsa)); + IPRINT(" sfbr %ux", n->sfbr); + IPRINT(" a %lux", legetl(n->scratcha)); + IPRINT(" b %lux", legetl(n->scratchb)); + IPRINT(" ssid %ux", n->ssid); + IPRINT("\n"); + cont = -2; + break; + case A_SIR_NOTIFY_WAIT_RESELECT: + IPRINT(PRINTPREFIX "wait reselect\n"); + cont = -2; + break; + case A_SIR_NOTIFY_RESELECT: + IPRINT(PRINTPREFIX "reselect: ssid %.2x sfbr %.2x at %ld\n", + n->ssid, n->sfbr, TK2MS(m->ticks)); + cont = -2; + break; + case A_SIR_NOTIFY_ISSUE: + IPRINT(PRINTPREFIX "%d/%d: issue:", dsa->target, dsa->lun); + dsadump: + IPRINT(" tgt=%d", dsa->target); + IPRINT(" time=%ld", TK2MS(m->ticks)); + IPRINT("\n"); + cont = -2; + break; + case A_SIR_NOTIFY_ISSUE_CHECK: + IPRINT(PRINTPREFIX "issue check\n"); + cont = -2; + break; + case A_SIR_NOTIFY_SIGP: + IPRINT(PRINTPREFIX "responded to SIGP\n"); + cont = -2; + break; + case A_SIR_NOTIFY_DUMP_NEXT_CODE: { + ulong *dsp = DMASEG_TO_KADDR(legetl(n->dsp)); + int x; + IPRINT(PRINTPREFIX "code at %lux", dsp - c->script); + for (x = 0; x < 6; x++) + IPRINT(" %.8lux", dsp[x]); + IPRINT("\n"); + USED(dsp); + cont = -2; + break; + } + case A_SIR_NOTIFY_WSR: + IPRINT(PRINTPREFIX "%d/%d: WSR set\n", dsa->target, dsa->lun); + cont = -2; + break; + case A_SIR_NOTIFY_LOAD_SYNC: + IPRINT(PRINTPREFIX "%d/%d: scntl=%.2x sxfer=%.2x\n", + dsa->target, dsa->lun, n->scntl3, n->sxfer); + cont = -2; + break; + case A_SIR_NOTIFY_RESELECTED_ON_SELECT: + IPRINT(PRINTPREFIX "%d/%d: reselected during select\n", + dsa->target, dsa->lun); + cont = -2; + break; + default: + IPRINT(PRINTPREFIX "%d/%d: script error %ld\n", + dsa->target, dsa->lun, legetl(n->dsps)); + dumpncrregs(c, 1); + wakeme = 1; + } + } + /*else*/ if (dstat & Iid) { + ulong addr = legetl(n->dsp); + ulong dbc = (n->dbc[2]<<16)|(n->dbc[1]<<8)|n->dbc[0]; + IPRINT(PRINTPREFIX "%d/%d: Iid pa=%.8lux sa=%.8lux dbc=%lux\n", + dsa->target, dsa->lun, + addr, addr - c->scriptpa, dbc); + addr = (ulong)DMASEG_TO_KADDR(addr); + IPRINT("%.8lux %.8lux %.8lux\n", + *(ulong *)(addr - 12), *(ulong *)(addr - 8), *(ulong *)(addr - 4)); + USED(addr, dbc); + if (dsa) + dsa->p9status = STATUS_FAIL; + wakeme = 1; + } + /*else*/ if (dstat & Bf) { + if (dsa == nil) + print(PRINTPREFIX "Bus Fault with dsa==0\n"); + else { + print(PRINTPREFIX "%d/%d: Bus Fault\n", dsa->target, dsa->lun); + dsa->p9status = STATUS_FAIL; + } + dumpncrregs(c, 1); + wakeme = 1; + } + } + if (cont == -2) + ncrcontinue(c); + else if (cont >= 0) + start(c, cont); + if (wakeme && dsa){ + if(dsa->p9status == 0xffff) + dsa->p9status = STATUS_FAIL; + wakeup(dsa); + } + iunlock(c); + if (DEBUG(1)) { + IPRINT(PRINTPREFIX "int end 1\n"); + } +} + +static int +done(void *arg) +{ + return ((Dsa *)arg)->p9status != 0xffff; +} + +static int +xfunc(Controller *c, enum na_external x, unsigned long *v) +{ + switch (x) { + default: + print("xfunc: can't find external %d\n", x); + return 0; + case X_scsi_id_buf: + *v = offsetof(Dsa, scsi_id_buf[0]); + break; + case X_msg_out_buf: + *v = offsetof(Dsa, msg_out_buf); + break; + case X_cmd_buf: + *v = offsetof(Dsa, cmd_buf); + break; + case X_data_buf: + *v = offsetof(Dsa, data_buf); + break; + case X_status_buf: + *v = offsetof(Dsa, status_buf); + break; + case X_dsa_head: + *v = DMASEG(&c->dsalist.head[0]); + break; + case X_ssid_mask: + *v = SSIDMASK(c); + break; + } + return 1; +} + +static void +setmovedata(Movedata *d, ulong pa, ulong bc) +{ + d->pa[0] = pa; + d->pa[1] = pa>>8; + d->pa[2] = pa>>16; + d->pa[3] = pa>>24; + d->dbc[0] = bc; + d->dbc[1] = bc>>8; + d->dbc[2] = bc>>16; + d->dbc[3] = bc>>24; +} + +static void +advancedata(Movedata *d, long v) +{ + lesetl(d->pa, legetl(d->pa) + v); + lesetl(d->dbc, legetl(d->dbc) - v); +} + +static void +dumpwritedata(uchar *data, int datalen) +{ + int i; + uchar *bp; + if (!DEBUG(0)){ + USED(data, datalen); + return; + } + + if (datalen) { + KPRINT(PRINTPREFIX "write:"); + for (i = 0, bp = data; i < 50 && i < datalen; i++, bp++) + KPRINT("%.2ux", *bp); + if (i < datalen) { + KPRINT("..."); + } + KPRINT("\n"); + } +} + +static void +dumpreaddata(uchar *data, int datalen) +{ + int i; + uchar *bp; + if (!DEBUG(0)){ + USED(data, datalen); + return; + } + + if (datalen) { + KPRINT(PRINTPREFIX "read:"); + for (i = 0, bp = data; i < 50 && i < datalen; i++, bp++) + KPRINT("%.2ux", *bp); + if (i < datalen) { + KPRINT("..."); + } + KPRINT("\n"); + } +} + +static void +busreset(Controller *c) +{ + int x, ntarget; + + /* bus reset */ + c->n->scntl1 |= (1 << 3); + delay(500); + c->n->scntl1 &= ~(1 << 3); + if(!(c->v->feature & Wide)) + ntarget = 8; + else + ntarget = MAXTARGET; + for (x = 0; x < ntarget; x++) { + setwide(0, c, x, 0); +#ifndef ASYNC_ONLY + c->s[x] = NeitherDone; +#endif + } + c->capvalid = 0; +} + +static void +reset(Controller *c) +{ + /* should wakeup all pending tasks */ + softreset(c); + busreset(c); +} + +static int +io(Controller *c, uchar target, uchar lun, int rw, uchar *cmd, int cmdlen, uchar *data, int datalen, + int *transferred) +{ + uchar *bp; + int bc; + Dsa *d; + Ncr *n = c->n; + uchar target_expo, my_expo; + ushort status; + + d = dsaalloc(c, target, lun); + + qlock(&c->q[target]); /* obtain access to target */ + + /* load the transfer control stuff */ + d->scsi_id_buf[0] = 0; + d->scsi_id_buf[1] = c->sxfer[target]; + d->scsi_id_buf[2] = target; + d->scsi_id_buf[3] = c->scntl3[target]; + synctodsa(d, c); + + bc = 0; + + d->msg_out[bc] = 0x80 | lun; + +#ifndef NO_DISCONNECT + d->msg_out[bc] |= (1 << 6); +#endif + bc++; + + /* work out what to do about negotiation */ + switch (c->s[target]) { + default: + KPRINT(PRINTPREFIX "%d: strange nego state %d\n", target, c->s[target]); + c->s[target] = NeitherDone; + /* fall through */ + case NeitherDone: + if ((c->capvalid & (1 << target)) == 0) + break; + target_expo = (c->cap[target] >> 5) & 3; + my_expo = (c->v->feature & Wide) != 0; + if (target_expo < my_expo) + my_expo = target_expo; +#ifdef ALWAYS_DO_WDTR + bc += buildwdtrmsg(d->msg_out + bc, my_expo); + KPRINT(PRINTPREFIX "%d: WDTN: initiating expo %d\n", target, my_expo); + c->s[target] = WideInit; + break; +#else + if (my_expo) { + bc += buildwdtrmsg(d->msg_out + bc, (c->v->feature & Wide) ? 1 : 0); + KPRINT(PRINTPREFIX "%d: WDTN: initiating expo %d\n", target, my_expo); + c->s[target] = WideInit; + break; + } + KPRINT(PRINTPREFIX "%d: WDTN: narrow\n", target); + /* fall through */ +#endif + case WideDone: + if (c->cap[target] & (1 << 4)) { + KPRINT(PRINTPREFIX "%d: SDTN: initiating %d %d\n", target, c->tpf, c->v->maxsyncoff); + bc += buildsdtrmsg(d->msg_out + bc, c->tpf, c->v->maxsyncoff); + c->s[target] = SyncInit; + break; + } + KPRINT(PRINTPREFIX "%d: SDTN: async only\n", target); + c->s[target] = BothDone; + break; + + case BothDone: + break; + } + + setmovedata(&d->msg_out_buf, DMASEG(d->msg_out), bc); + setmovedata(&d->cmd_buf, DMASEG(cmd), cmdlen); + calcblockdma(d, DMASEG(data), datalen); + + if (DEBUG(0)) { + KPRINT(PRINTPREFIX "%d/%d: exec: ", target, lun); + for (bp = cmd; bp < &cmd[cmdlen]; bp++) + KPRINT("%.2ux", *bp); + KPRINT("\n"); + if (rw) + KPRINT(PRINTPREFIX "%d/%d: exec: limit=(%d)%ld\n", + target, lun, d->dmablks, legetl(d->data_buf.dbc)); + else + dumpwritedata(data, datalen); + } + + setmovedata(&d->status_buf, DMASEG(&d->status), 1); + + d->p9status = 0xffff; + d->parityerror = 0; + + d->stateb = A_STATE_ISSUE; /* start operation */ + + ilock(c); + if (c->ssm) + c->n->dcntl |= 0x10; /* SSI */ + if (c->running) { + n->istat |= Sigp; + } + else { + start(c, E_issue_check); + } + iunlock(c); + +#ifdef CPU + while(waserror()) + ; +#ifdef QUICK_TIMEOUT + tsleep(d, done, d, 30 * 1000); +#else + tsleep(d, done, d, 60 * 60 * 1000); +#endif + poperror(); + + if (!done(d)) { + KPRINT(PRINTPREFIX "%d/%d: exec: Timed out\n", target, lun); + dumpncrregs(c, 0); + dsafree(c, d); + reset(c); + qunlock(&c->q[target]); + error(Eio); + } +#endif + +#ifdef FS + sleep(d, done, d); +#endif + + if (d->p9status == STATUS_SELECTION_TIMEOUT) + c->s[target] = NeitherDone; + if (d->parityerror) { + d->p9status = STATUS_FAIL; + } + /* + * adjust datalen + */ + if (d->dmablks > 0) + datalen -= d->dmablks * A_BSIZE; + else if (d->flag == 0) + datalen -= legetl(d->data_buf.dbc); + if (transferred) + *transferred = datalen; + if (rw) + dumpreaddata(data, datalen); + if (DEBUG(0)) + KPRINT(PRINTPREFIX "%d/%d: exec: p9status=%d status %d rlen %d\n", + target, lun, d->p9status, status, datalen); + /* + * spot the identify + */ + if ((c->capvalid & (1 << target)) == 0 && + d->p9status == STATUS_COMPLETE && cmd[0] == 0x12 && datalen >= 8) { + c->capvalid |= 1 << target; + c->cap[target] = data[7]; + KPRINT(PRINTPREFIX "%d: capabilities %.2x\n", target, data[7]); + } + status = d->p9status; + dsafree(c, d); + qunlock(&c->q[target]); + return status; +} + +#ifdef CPU +static int +exec(Scsi *p, int rw) +{ + int transferred; + Controller *c = &controller; /* MULTIPLE - map from scsi bus to controller instance */ + + p->status = io(c, p->target, p->lun, rw, + p->cmd.base, p->cmd.lim - p->cmd.base, + p->data.base, p->data.lim - p->data.base, &transferred); + p->data.ptr = p->data.base + transferred; + return p->status; +} +#endif + +#ifdef Plan9fs +static int +exec(Device d, int rw, uchar *cmd, int clen, void *data, int dlen) +{ + Controller *c = &controller; /* MULTIPLE - map from scsi bus to controller instance */ + + return io(c, d.unit, (cmd[1] >> 5) & 7, rw, cmd, clen, data, dlen, 0); +} +#endif /* Plan9fs + +#ifdef FS +static int +exec(Target* t, int rw, uchar* cmd, int cbytes, void* data, int* dbytes) +{ + int n, s; + Controller *ctlr; + + if((ctlr = ctlrxx[t->ctlrno]) == nil || ctlr->n == nil) + return STharderr; + if(t->targetno == 0x07) + return STownid; + if(dbytes) + n = *dbytes; + else + n = 0; + s = io(ctlr, t->targetno, (cmd[1] >> 5) & 7, rw, cmd, cbytes, data, n, dbytes); + switch(s){ + case STATUS_COMPLETE: + return STok; + case STATUS_FAIL: + return STharderr; + case STATUS_SELECTION_TIMEOUT: + return STtimeout; + case 0x6002: + return STcheck; + default: + print("scsi#%d: exec status 0x%ux\n", ctlr->ctlrno, s); + return STharderr; + } +} +#endif + +static void +cribbios(Controller *c) +{ + c->bios.scntl3 = c->n->scntl3; + c->bios.stest2 = c->n->stest2; + KPRINT(PRINTPREFIX "bios scntl3(%.2x) stest2(%.2x)\n", c->bios.scntl3, c->bios.stest2); +} + +static int +bios_set_differential(Controller *c) +{ + /* Concept lifted from FreeBSD - thanks Gerard */ + /* basically, if clock conversion factors are set, then there is + * evidence the bios had a go at the chip, and if so, it would + * have set the differential enable bit in stest2 + */ + return (c->bios.scntl3 & 7) != 0 && (c->bios.stest2 & 0x20) != 0; +} + +#define NCR_VID 0x1000 +#define NCR_810_DID 0x0001 +#define NCR_820_DID 0x0002 /* don't know enough about this one to support it */ +#define NCR_825_DID 0x0003 +#define NCR_815_DID 0x0004 +#define SYM_810AP_DID 0x0005 +#define SYM_860_DID 0x0006 +#define SYM_896_DID 0x000b +#define SYM_895_DID 0x000c +#define SYM_885_DID 0x000d /* ditto */ +#define SYM_875_DID 0x000f /* ditto */ +#define SYM_1010_DID 0x0020 +#define SYM_875J_DID 0x008f + +static Variant variant[] = { +{ NCR_810_DID, 0x0f, "NCR53C810", Burst16, 8, 24, 0 }, +{ NCR_810_DID, 0x1f, "SYM53C810ALV", Burst16, 8, 24, Prefetch }, +{ NCR_810_DID, 0xff, "SYM53C810A", Burst16, 8, 24, Prefetch }, +{ SYM_810AP_DID, 0xff, "SYM53C810AP", Burst16, 8, 24, Prefetch }, +{ NCR_815_DID, 0xff, "NCR53C815", Burst16, 8, 24, BurstOpCodeFetch }, +{ NCR_825_DID, 0x0f, "NCR53C825", Burst16, 8, 24, Wide|BurstOpCodeFetch|Differential }, +{ NCR_825_DID, 0xff, "SYM53C825A", Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Differential|Wide }, +{ SYM_860_DID, 0x0f, "SYM53C860", Burst16, 8, 24, Prefetch|Ultra }, +{ SYM_860_DID, 0xff, "SYM53C860LV", Burst16, 8, 24, Prefetch|Ultra }, +{ SYM_875_DID, 0x01, "SYM53C875r1", Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Differential|Wide|Ultra }, +{ SYM_875_DID, 0xff, "SYM53C875", Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Differential|Wide|Ultra|ClockDouble }, +{ SYM_875J_DID, 0xff, "SYM53C875j", Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Differential|Wide|Ultra|ClockDouble }, +{ SYM_885_DID, 0xff, "SYM53C885", Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Wide|Ultra|ClockDouble }, +{ SYM_895_DID, 0xff, "SYM53C895", Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Wide|Ultra|Ultra2 }, +{ SYM_896_DID, 0xff, "SYM53C896", Burst128, 16, 64, Prefetch|LocalRAM|BigFifo|Wide|Ultra|Ultra2 }, +{ SYM_1010_DID, 0xff, "SYM53C1010", Burst128, 16, 64, Prefetch|LocalRAM|BigFifo|Wide|Ultra|Ultra2 }, +}; + +#define MAXVAR (sizeof(variant)/sizeof(variant[0])) + +#ifndef NAINCLUDE +static int +na_fixup(Controller *c, ulong pa_reg, + struct na_patch *patch, int patches, + int (*externval)(Controller*, int, ulong*)) +{ + int p; + int v; + ulong *script, pa_script; + unsigned long lw, lv; + + script = c->script; + pa_script = c->scriptpa; + for (p = 0; p < patches; p++) { + switch (patch[p].type) { + case 1: + /* script relative */ + script[patch[p].lwoff] += pa_script; + break; + case 2: + /* register i/o relative */ + script[patch[p].lwoff] += pa_reg; + break; + case 3: + /* data external */ + lw = script[patch[p].lwoff]; + v = (lw >> 8) & 0xff; + if (!(*externval)(c, v, &lv)) + return 0; + v = lv & 0xff; + script[patch[p].lwoff] = (lw & 0xffff00ffL) | (v << 8); + break; + case 4: + /* 32 bit external */ + lw = script[patch[p].lwoff]; + if (!(*externval)(c, lw, &lv)) + return 0; + script[patch[p].lwoff] = lv; + break; + case 5: + /* 24 bit external */ + lw = script[patch[p].lwoff]; + if (!(*externval)(c, lw & 0xffffff, &lv)) + return 0; + script[patch[p].lwoff] = (lw & 0xff000000L) | (lv & 0xffffffL); + break; + } + } + return 1; +} +#endif /* NAINCLUDE */ + +typedef struct Adapter { + Pcidev* pcidev; + int port; + int irq; + int rid; +} Adapter; +static Msgbuf* adapter; + +static void +scanpci(void) +{ + Pcidev *p; + Msgbuf *mb, *last; + Adapter *ap; + + p = nil; + last = nil; + while(p = pcimatch(p, NCR_VID, 0)){ + mb = mballoc(sizeof(Adapter), 0, Mxxx); + ap = (Adapter*)mb->data; + ap->pcidev = p; + ap->port = p->mem[0].bar & ~0x01; + if(adapter == nil) + adapter = mb; + else + last->next = mb; + last = mb; + } +} + +static int +init(Controller* ctlr, ISAConf* isa, int differential) +{ + int is64, var, rid; + ulong regpa, scriptpa; + Pcidev *pcidev; + Msgbuf *mb, **mbb; + Adapter *ap; + extern ulong upamalloc(ulong, int, int); + + /* + * Any adapter matches if no isa->port is supplied, + * otherwise the ports must match. + */ + pcidev = nil; + mbb = &adapter; + for(mb = *mbb; mb; mb = mb->next){ + ap = (Adapter*)mb->data; + if(isa->port == 0 || isa->port == ap->port){ + pcidev = ap->pcidev; + *mbb = mb->next; + mbfree(mb); + break; + } + mbb = &mb->next; + } + if(pcidev == nil) + return 0; + + rid = pcicfgr8(pcidev, PciRID); + for (var = 0; var < MAXVAR; var++) { + if (pcidev->did == variant[var].did && rid <= variant[var].maxrid) + break; + } + if (var >= MAXVAR) + return 0; + print("scsi#%d: %s rev. 0x%.2x intr=%d command=%.4x\n", + ctlr->ctlrno, variant[var].name, rid, + pcidev->intl, pcicfgr16(pcidev, PciPCR)); + + is64 = pcidev->mem[1].bar & 0x04; + if(is64 && pcidev->mem[2].bar){ + print("scsi#%d: registers in 64-bit space\n", + ctlr->ctlrno); + return 0; + } + regpa = upamalloc(pcidev->mem[1].bar & ~0x0F, pcidev->mem[1].size, 0); + if (regpa == 0) { + print("scsi#%d: failed to map registers\n", ctlr->ctlrno); + return 0; + } + ctlr->n = KADDR(regpa); + + ctlr->v = &variant[var]; + + scriptpa = 0; + if ((ctlr->v->feature & LocalRAM) && sizeof(na_script) <= 4096) { + if(is64){ + if((pcidev->mem[3].bar & 0x04) && pcidev->mem[4].bar){ + print("scsi#%d: RAM in 64-bit space\n", + ctlr->ctlrno); + scriptpa = 0; + } + else + scriptpa = upamalloc(pcidev->mem[3].bar & ~0x0F, + pcidev->mem[3].size, 0); + } + else + scriptpa = upamalloc(pcidev->mem[2].bar & ~0x0F, + pcidev->mem[2].size, 0); + if (scriptpa == 0) + print("scsi#%d: failed to map onboard RAM\n", ctlr->ctlrno); + else { + ctlr->script = KADDR(scriptpa); + ctlr->scriptpa = scriptpa; + memmove(ctlr->script, na_script, sizeof(na_script)); + print("scsi#%d: local SCRIPT ram enabled\n", ctlr->ctlrno); + } + } + + if (scriptpa == 0) { + /* either the map failed, or this chip does not have local RAM + * it will need a copy of the microcode + */ + /* + * should allocate memory and copy na_script into it for + * multiple controllers here + * single controller version uses the microcode in place + */ + ctlr->script = na_script; + ctlr->scriptpa = DMASEG(na_script); + } + + /* fixup script */ + if (!na_fixup(ctlr, regpa, na_patches, NA_PATCHES, xfunc)) { + print("script fixup failed\n"); + return 0; + } + + if (differential) + ctlr->feature |= Differential; + + swabl(ctlr->script, ctlr->script, sizeof(na_script)); + + ctlr->dsalist.freechain = 0; + lesetl(ctlr->dsalist.head, 0); + + isa->port = (ulong)KADDR(regpa); + isa->irq = pcidev->intl; + + synctabinit(ctlr); + cribbios(ctlr); + /* + intrenable(isa->irq, interrupt, ctlr, pcidev->tbdf); + */ + setvec(IRQBASE + isa->irq, interrupt, ctlr); + reset(ctlr); + + return 1; +} + +#ifdef CPU +int (* +ncr53c8xxreset(void))(Scsi*, int) +{ + ISAConf isaconf; + int ic; + int differential = 0; + int o; + Controller *c; + + memset(&isaconf, 0, sizeof(isaconf)); + strcpy(isaconf.type, DEVICENAME); + ic = isaconfig("scsi", 0, &isaconf); + + /* search any ISA opts */ + if (ic) { + for (o = 0; o < isaconf.nopt; o++) { + if (strcmp(isaconf.opt[o], "diff") == 0) + differential = 1; + } + } + + c = &controller; /* MULTIPLE - map from scsi bus to controller instance */ + if (init(c, differential)) + return exec; + return 0; +} +#endif + +#ifdef Plan9fs +int (* +ncr53c8xxreset(int /*ctlrno*/, ISAConf */*isa*/))(Device, int, uchar*, int, void*, int) +{ + Controller *c = &controller; /* MULTIPLE - map from scsi bus to controller instance */ + if (init(c, 0)) + return exec; + return 0; +} +#endif /* Plan9fs */ + +#ifdef FS +Scsiio +ncr53c8xxreset(int ctlrno, ISAConf* isa) +{ + int differential, o; + Controller *ctlr; + static int scandone; + + if(scandone == 0){ + scanpci(); + scandone = 1; + } + + differential = 0; + for (o = 0; o < isa->nopt; o++) { + if (strcmp(isa->opt[o], "diff") == 0) + differential = 1; + } + + if((ctlr = xalloc(sizeof(Controller))) == 0){ + print("scsi#%d: %s: controller allocation failed\n", + ctlrno, isa->type); + return 0; + } + ctlrxx[ctlrno] = ctlr; + ctlr->ctlrno = ctlrno; + + if (init(ctlr, isa, differential)) + return exec; + + return 0; +} +#endif /* FS */ diff -Nru /sys/src/fs/pc/sdata.c /sys/src/fs/pc/sdata.c --- /sys/src/fs/pc/sdata.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/pc/sdata.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,2835 @@ +/* + * (S)ATA(PI)/(E)IDE disk driver for file server. + * derived from /sys/src/boot/pc/sdata.c and /sys/src/9/pc/sdata.c + * + * we can't write message into a ctl file on the file server, so + * enable dma and rwm as advertised by the drive & controller. + * if that doesn't work, fix the hardware or turn it off in the source + * (set conf.idedma = 0). + * + * entry points: +../fs64/9fsfs64.c:38: { "hd", ataread, ataseek, atawrite, setatapart, }, +../fs64/9fsfs64.c:58: nhd = atainit(); +../port/sub.c:1065: return ideread(d, b, c); +../port/sub.c:1129: return idewrite(d, b, c); +../port/sub.c:1182: return idesize(d); +../port/sub.c:1362: ideinit(d); + */ +#include "all.h" +#include "io.h" +#include "mem.h" + +#include "sd.h" +#include "compat.h" +#undef error + +#define HOWMANY(x, y) (((x)+((y)-1))/(y)) +#define ROUNDUP(x, y) (HOWMANY((x), (y))*(y)) + +enum { + IDEBUG = 0, + + /* old stuff carried forward */ + NCtlr= 8, + NCtlrdrv= 2, /* fixed by hardware for pata */ + NDrive= NCtlr*NCtlrdrv, + Maxxfer= 16*1024, /* maximum transfer size/cmd */ + + Read = 0, + Write, + + /* I/O ports */ + Ctlr0cmd = 0x1f0, + Ctlr0ctl = 0x3f4, + Ctlr1cmd = 0x170, + Ctlr1ctl = 0x374, + Ctl2cmd = Ctlr0ctl - Ctlr0cmd, +}; + +#define IDPRINT if(IDEBUG)print + +extern SDifc sdataifc; + +enum { + DbgCONFIG = 0x0001, /* detected drive config info */ + DbgIDENTIFY = 0x0002, /* detected drive identify info */ + DbgSTATE = 0x0004, /* dump state on panic */ + DbgPROBE = 0x0008, /* trace device probing */ + DbgDEBUG = 0x0080, /* the current problem... */ + DbgINL = 0x0100, /* That Inil20+ message we hate */ + Dbg48BIT = 0x0200, /* 48-bit LBA */ + DbgBsy = 0x0400, /* interrupt but Bsy (shared IRQ) */ +}; + +/* adjust to taste */ +#define DEBUG (DbgDEBUG|DbgCONFIG) + +enum { /* I/O ports */ + Data = 0, + Error = 1, /* (read) */ + Features = 1, /* (write) */ + Count = 2, /* sector count<7-0>, sector count<15-8> */ + Ir = 2, /* interrupt reason (PACKET) */ + Sector = 3, /* sector number */ + Lbalo = 3, /* LBA<7-0>, LBA<31-24> */ + Cyllo = 4, /* cylinder low */ + Bytelo = 4, /* byte count low (PACKET) */ + Lbamid = 4, /* LBA<15-8>, LBA<39-32> */ + Cylhi = 5, /* cylinder high */ + Bytehi = 5, /* byte count hi (PACKET) */ + Lbahi = 5, /* LBA<23-16>, LBA<47-40> */ + Dh = 6, /* Device/Head, LBA<32-14> */ + Status = 7, /* (read) */ + Cmd = 7, /* (write) */ + + As = 2, /* Alternate Status (read) */ + Dc = 2, /* Device Control (write) */ +}; + +enum { /* Error */ + Med = 0x01, /* Media error */ + Ili = 0x01, /* command set specific (PACKET) */ + Nm = 0x02, /* No Media */ + Eom = 0x02, /* command set specific (PACKET) */ + Abrt = 0x04, /* Aborted command */ + Mcr = 0x08, /* Media Change Request */ + Idnf = 0x10, /* no user-accessible address */ + Mc = 0x20, /* Media Change */ + Unc = 0x40, /* Uncorrectable data error */ + Wp = 0x40, /* Write Protect */ + Icrc = 0x80, /* Interface CRC error */ +}; + +enum { /* Features */ + Dma = 0x01, /* data transfer via DMA (PACKET) */ + Ovl = 0x02, /* command overlapped (PACKET) */ +}; + +enum { /* Interrupt Reason */ + Cd = 0x01, /* Cmd/Data */ + Io = 0x02, /* I/O direction */ + Rel = 0x04, /* Bus Release */ +}; + +enum { /* Device/Head */ + Dev0 = 0xA0, /* Master */ + Dev1 = 0xB0, /* Slave */ + Lba = 0x40, /* LBA mode */ +}; + +enum { /* internal flags */ + Lba48 = 0x1, /* LBA48 mode */ + Lba48always = 0x2, /* ... */ +}; + +enum { /* Status, Alternate Status */ + Err = 0x01, /* Error */ + Chk = 0x01, /* Check error (PACKET) */ + Drq = 0x08, /* Data Request */ + Dsc = 0x10, /* Device Seek Complete */ + Serv = 0x10, /* Service */ + Df = 0x20, /* Device Fault */ + Dmrd = 0x20, /* DMA ready (PACKET) */ + Drdy = 0x40, /* Device Ready */ + Bsy = 0x80, /* Busy */ +}; + +enum { /* Cmd */ + Cnop = 0x00, /* NOP */ + Cdr = 0x08, /* Device Reset */ + Crs = 0x20, /* Read Sectors */ + Crs48 = 0x24, /* Read Sectors Ext */ + Crd48 = 0x25, /* Read w/ DMA Ext */ + Crdq48 = 0x26, /* Read w/ DMA Queued Ext */ + Crsm48 = 0x29, /* Read Multiple Ext */ + Cws = 0x30, /* Write Sectors */ + Cws48 = 0x34, /* Write Sectors Ext */ + Cwd48 = 0x35, /* Write w/ DMA Ext */ + Cwdq48 = 0x36, /* Write w/ DMA Queued Ext */ + Cwsm48 = 0x39, /* Write Multiple Ext */ + Cedd = 0x90, /* Execute Device Diagnostics */ + Cpkt = 0xA0, /* Packet */ + Cidpkt = 0xA1, /* Identify Packet Device */ + Crsm = 0xC4, /* Read Multiple */ + Cwsm = 0xC5, /* Write Multiple */ + Csm = 0xC6, /* Set Multiple */ + Crdq = 0xC7, /* Read DMA queued */ + Crd = 0xC8, /* Read DMA */ + Cwd = 0xCA, /* Write DMA */ + Cwdq = 0xCC, /* Write DMA queued */ + Cstandby = 0xE2, /* Standby */ + Cid = 0xEC, /* Identify Device */ + Csf = 0xEF, /* Set Features */ +}; + +enum { /* Device Control */ + Nien = 0x02, /* (not) Interrupt Enable */ + Srst = 0x04, /* Software Reset */ + Hob = 0x80, /* High Order Bit [sic] */ +}; + +enum { /* PCI Configuration Registers */ + Bmiba = 0x20, /* Bus Master Interface Base Address */ + Idetim = 0x40, /* IE Timing */ + Sidetim = 0x44, /* Slave IE Timing */ + Udmactl = 0x48, /* Ultra DMA/33 Control */ + Udmatim = 0x4A, /* Ultra DMA/33 Timing */ +}; + +enum { /* Bus Master IDE I/O Ports */ + Bmicx = 0, /* Cmd */ + Bmisx = 2, /* Status */ + Bmidtpx = 4, /* Descriptor Table Pointer */ +}; + +enum { /* Bmicx */ + Ssbm = 0x01, /* Start/Stop Bus Master */ + Rwcon = 0x08, /* Read/Write Control */ +}; + +enum { /* Bmisx */ + Bmidea = 0x01, /* Bus Master IDE Active */ + Idedmae = 0x02, /* IDE DMA Error (R/WC) */ + Ideints = 0x04, /* IDE Interrupt Status (R/WC) */ + Dma0cap = 0x20, /* Drive 0 DMA Capable */ + Dma1cap = 0x40, /* Drive 0 DMA Capable */ +}; +enum { /* Physical Region Descriptor */ + PrdEOT = 0x80000000, /* Bus Master IDE Active */ +}; + +enum { /* offsets into the identify info. */ + Iconfig = 0, /* general configuration */ + Ilcyl = 1, /* logical cylinders */ + Ilhead = 3, /* logical heads */ + Ilsec = 6, /* logical sectors per logical track */ + Iserial = 10, /* serial number */ + Ifirmware = 23, /* firmware revision */ + Imodel = 27, /* model number */ + Imaxrwm = 47, /* max. read/write multiple sectors */ + Icapabilities = 49, /* capabilities */ + Istandby = 50, /* device specific standby timer */ + Ipiomode = 51, /* PIO data transfer mode number */ + Ivalid = 53, + Iccyl = 54, /* cylinders if (valid&0x01) */ + Ichead = 55, /* heads if (valid&0x01) */ + Icsec = 56, /* sectors if (valid&0x01) */ + Iccap = 57, /* capacity if (valid&0x01) */ + Irwm = 59, /* read/write multiple */ + Ilba = 60, /* LBA size */ + Imwdma = 63, /* multiword DMA mode */ + Iapiomode = 64, /* advanced PIO modes supported */ + Iminmwdma = 65, /* min. multiword DMA cycle time */ + Irecmwdma = 66, /* rec. multiword DMA cycle time */ + Iminpio = 67, /* min. PIO cycle w/o flow control */ + Iminiordy = 68, /* min. PIO cycle with IORDY */ + Ipcktbr = 71, /* time from PACKET to bus release */ + Iserbsy = 72, /* time from SERVICE to !Bsy */ + Iqdepth = 75, /* max. queue depth */ + Imajor = 80, /* major version number */ + Iminor = 81, /* minor version number */ + Icsfs = 82, /* command set/feature supported */ + Icsfe = 85, /* command set/feature enabled */ + Iudma = 88, /* ultra DMA mode */ + Ierase = 89, /* time for security erase */ + Ieerase = 90, /* time for enhanced security erase */ + Ipower = 91, /* current advanced power management */ + Ilba48 = 100, /* 48-bit LBA size (64 bits in 100-103) */ + Irmsn = 127, /* removable status notification */ + Isecstat = 128, /* security status */ + Icfapwr = 160, /* CFA power mode */ + Imediaserial = 176, /* current media serial number */ + Icksum = 255, /* checksum */ +}; + +enum { /* bit masks for config identify info */ + Mpktsz = 0x0003, /* packet command size */ + Mincomplete = 0x0004, /* incomplete information */ + Mdrq = 0x0060, /* DRQ type */ + Mrmdev = 0x0080, /* device is removable */ + Mtype = 0x1F00, /* device type */ + Mproto = 0x8000, /* command protocol */ +}; + +enum { /* bit masks for capabilities identify info */ + Mdma = 0x0100, /* DMA supported */ + Mlba = 0x0200, /* LBA supported */ + Mnoiordy = 0x0400, /* IORDY may be disabled */ + Miordy = 0x0800, /* IORDY supported */ + Msoftrst = 0x1000, /* needs soft reset when Bsy */ + Mstdby = 0x2000, /* standby supported */ + Mqueueing = 0x4000, /* queueing overlap supported */ + Midma = 0x8000, /* interleaved DMA supported */ +}; + +enum { /* bit masks for supported/enabled features */ + Msmart = 0x0001, + Msecurity = 0x0002, + Mrmmedia = 0x0004, + Mpwrmgmt = 0x0008, + Mpkt = 0x0010, + Mwcache = 0x0020, + Mlookahead = 0x0040, + Mrelirq = 0x0080, + Msvcirq = 0x0100, + Mreset = 0x0200, + Mprotected = 0x0400, + Mwbuf = 0x1000, + Mrbuf = 0x2000, + Mnop = 0x4000, + Mmicrocode = 0x0001, + Mqueued = 0x0002, + Mcfa = 0x0004, + Mapm = 0x0008, + Mnotify = 0x0010, + Mstandby = 0x0020, + Mspinup = 0x0040, + Mmaxsec = 0x0100, + Mautoacoustic = 0x0200, + Maddr48 = 0x0400, + Mdevconfov = 0x0800, + Mflush = 0x1000, + Mflush48 = 0x2000, + Msmarterror = 0x0001, + Msmartselftest = 0x0002, + Mmserial = 0x0004, + Mmpassthru = 0x0008, + Mlogging = 0x0020, +}; + +typedef struct Ctlr Ctlr; +typedef struct Drive Drive; + +typedef struct Prd { + ulong pa; /* Physical Base Address */ + int count; +} Prd; + +enum { + BMspan = 64*1024, /* must be power of 2 <= 64*1024 */ + + Nprd = SDmaxio/BMspan+2, +}; + +typedef struct Ctlr { + int cmdport; + int ctlport; + int irq; + int tbdf; + int bmiba; /* bus master interface base address */ + int maxio; /* sector count transfer maximum */ + int span; /* don't span this boundary with dma */ + + Pcidev* pcidev; + void (*ienable)(Ctlr*); + void (*idisable)(Ctlr*); + SDev* sdev; + + Drive* drive[NCtlrdrv]; + Target target[NTarget]; /* contains filters for stats */ + + Prd* prdt; /* physical region descriptor table */ + void* prdtbase; + + QLock; /* current command */ + Drive* curdrive; + int command; /* last command issued (debugging) */ + Rendez; + int done; + + Lock; /* register access */ + + /* old stuff carried forward */ + QLock idelock; /* make seek & i/o atomic in ide* routines */ +} Ctlr; + +typedef struct Drive { + Ctlr* ctlr; + + int dev; + ushort info[256]; + int c; /* cylinder */ + int h; /* head */ + int s; /* sector */ + Devsize sectors; /* total sectors */ + int secsize; /* sector size */ + + int dma; /* DMA R/W possible */ + int dmactl; + int rwm; /* read/write multiple possible */ + int rwmctl; + + int pkt; /* PACKET device, length of pktcmd */ + uchar pktcmd[16]; + int pktdma; /* this PACKET command using dma */ + + uchar sense[18]; + uchar inquiry[48]; + + QLock; /* drive access */ + int command; /* current command */ + int write; + uchar* data; + int dlen; + uchar* limit; + int count; /* sectors */ + int block; /* R/W bytes per block */ + int status; + int error; + int flags; /* internal flags */ + + /* for ata* routines */ + int online; + Devsize offset; + int driveno; /* ctlr*NCtlrdrv + unit */ + + char lba; /* true if drive has logical block addressing */ + char multi; /* non-0 if drive does multiple block xfers */ + + /* + * old stuff carried forward. it's in Drive not Ctlr to maximise + * possible concurrency. + */ + uchar buf[RBUFSIZE]; +} Drive; + +/* file-server-specific data */ + +static Ctlr *atactlr[NCtlr]; +static SDev *sdevs[NCtlr]; + +static Drive *atadrive[NDrive]; +// static SDunit *sdunits[NDrive]; + +SDunit* sdgetunit(SDev* sdev, int subno); + +static Drive *atadriveprobe(int driveno); + + +void +presleep(Rendez *r, int (*fn)(void*), void *v) +{ + int x; + + if (u != nil) { + sleep(r, fn, v); + return; + } + /* else we're in predawn with no u */ + x = spllo(); + while (!fn(v)) + continue; + splx(x); +} + +void +pretsleep(Rendez *r, int (*fn)(void*), void *v, int msec) +{ + int x; + ulong start; + + if (u != nil) { + tsleep(r, fn, v, msec); + return; + } + /* else we're in predawn with no u */ + x = spllo(); + for (start = m->ticks; TK2MS(m->ticks - start) < msec && + !fn(v); ) + continue; + splx(x); +} + +#define sleep presleep +#define tsleep pretsleep + +static void +pc87415ienable(Ctlr* ctlr) +{ + Pcidev *p; + int x; + + p = ctlr->pcidev; + if(p == nil) + return; + + x = pcicfgr32(p, 0x40); + if(ctlr->cmdport == p->mem[0].bar) + x &= ~0x00000100; + else + x &= ~0x00000200; + pcicfgw32(p, 0x40, x); +} + +static void +atadumpstate(Drive* drive, uchar* cmd, Devsize lba, int count) +{ + Prd *prd; + Pcidev *p; + Ctlr *ctlr; + int i, bmiba; + + if(!(DEBUG & DbgSTATE)){ + USED(drive, cmd, lba, count); + return; + } + + ctlr = drive->ctlr; + print("command %2.2uX\n", ctlr->command); + print("data %8.8p limit %8.8p dlen %d status %uX error %uX\n", + drive->data, drive->limit, drive->dlen, + drive->status, drive->error); + if(cmd != nil){ + print("lba %d -> %lld, count %d -> %d (%d)\n", + (cmd[2]<<24)|(cmd[3]<<16)|(cmd[4]<<8)|cmd[5], + (Wideoff)lba, + (cmd[7]<<8)|cmd[8], count, drive->count); + } + if(!(inb(ctlr->ctlport+As) & Bsy)){ + for(i = 1; i < 7; i++) + print(" 0x%2.2uX", inb(ctlr->cmdport+i)); + print(" 0x%2.2uX\n", inb(ctlr->ctlport+As)); + } + if(drive->command == Cwd || drive->command == Crd){ + bmiba = ctlr->bmiba; + prd = ctlr->prdt; + print("bmicx %2.2uX bmisx %2.2uX prdt %8.8p\n", + inb(bmiba+Bmicx), inb(bmiba+Bmisx), prd); + for(;;){ + print("pa 0x%8.8luX count %8.8uX\n", + prd->pa, prd->count); + if(prd->count & PrdEOT) + break; + prd++; + } + } + if(ctlr->pcidev && ctlr->pcidev->vid == 0x8086){ + p = ctlr->pcidev; + print("0x40: %4.4uX 0x42: %4.4uX", + pcicfgr16(p, 0x40), pcicfgr16(p, 0x42)); + print("0x48: %2.2uX\n", pcicfgr8(p, 0x48)); + print("0x4A: %4.4uX\n", pcicfgr16(p, 0x4A)); + } +} + +static int +atadebug(int cmdport, int ctlport, char* fmt, ...) +{ + int i, n; + va_list arg; + char buf[PRINTSIZE]; + + if(!(DEBUG & DbgPROBE)){ + USED(cmdport, ctlport, fmt); + return 0; + } + + va_start(arg, fmt); + n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + + if(cmdport){ + if(buf[n-1] == '\n') + n--; + n += snprint(buf+n, PRINTSIZE-n, " ataregs 0x%uX:", + cmdport); + for(i = Features; i < Cmd; i++) + n += snprint(buf+n, PRINTSIZE-n, " 0x%2.2uX", + inb(cmdport+i)); + if(ctlport) + n += snprint(buf+n, PRINTSIZE-n, " 0x%2.2uX", + inb(ctlport+As)); + n += snprint(buf+n, PRINTSIZE-n, "\n"); + } + putstrn(buf, n); + + return n; +} + +static int +ataready(int cmdport, int ctlport, int dev, int reset, int ready, int micro) +{ + int as; + + atadebug(cmdport, ctlport, "ataready: dev %uX reset %uX ready %uX", + dev, reset, ready); + + for(;;){ + /* + * Wait for the controller to become not busy and + * possibly for a status bit to become true (usually + * Drdy). Must change to the appropriate device + * register set if necessary before testing for ready. + * Always run through the loop at least once so it + * can be used as a test for !Bsy. + */ + as = inb(ctlport+As); + if(as & reset){ + /* nothing to do */ + } + else if(dev){ + outb(cmdport+Dh, dev); + dev = 0; + } + else if(ready == 0 || (as & ready)){ + atadebug(0, 0, "ataready: %d 0x%2.2uX\n", micro, as); + return as; + } + + if(micro-- <= 0){ + atadebug(0, 0, "ataready: %d 0x%2.2uX\n", micro, as); + break; + } + microdelay(1); + } + atadebug(cmdport, ctlport, "ataready: timeout"); + + return -1; +} + +/* +static int +atacsf(Drive* drive, vlong csf, int supported) +{ + ushort *info; + int cmdset, i, x; + + if(supported) + info = &drive->info[Icsfs]; + else + info = &drive->info[Icsfe]; + + for(i = 0; i < 3; i++){ + x = (csf>>(16*i)) & 0xFFFF; + if(x == 0) + continue; + cmdset = info[i]; + if(cmdset == 0 || cmdset == 0xFFFF) + return 0; + return cmdset & x; + } + + return 0; +} +*/ + +static int +atadone(void* arg) +{ + return ((Ctlr*)arg)->done; +} + +static int +atarwmmode(Drive* drive, int cmdport, int ctlport, int dev) +{ + int as, maxrwm, rwm; + + maxrwm = (drive->info[Imaxrwm] & 0xFF); + if(maxrwm == 0) + return 0; + + /* + * Sometimes drives come up with the current count set + * to 0; if so, set a suitable value, otherwise believe + * the value in Irwm if the 0x100 bit is set. + */ + if(drive->info[Irwm] & 0x100) + rwm = (drive->info[Irwm] & 0xFF); + else + rwm = 0; + if(rwm == 0) + rwm = maxrwm; + if(rwm > 16) + rwm = 16; + if(ataready(cmdport, ctlport, dev, Bsy|Drq, Drdy, 102*1000) < 0) + return 0; + outb(cmdport+Count, rwm); + outb(cmdport+Cmd, Csm); + microdelay(1); + as = ataready(cmdport, ctlport, 0, Bsy, Drdy|Df|Err, 1000); + inb(cmdport+Status); + if(as < 0 || (as & (Df|Err))) + return 0; + + drive->rwm = rwm; + if (conf.idedma) + drive->rwmctl = drive->rwm; /* FS special */ + else + drive->rwm = 0; + return rwm; +} + +static int +atadmamode(Drive* drive) +{ + int dma; + + /* + * Check if any DMA mode enabled. + * Assumes the BIOS has picked and enabled the best. + * This is completely passive at the moment, no attempt is + * made to ensure the hardware is correctly set up. + */ + dma = drive->info[Imwdma] & 0x0707; + drive->dma = (dma>>8) & dma; + if(drive->dma == 0 && (drive->info[Ivalid] & 0x04)){ + dma = drive->info[Iudma] & 0x7F7F; + drive->dma = (dma>>8) & dma; + if(drive->dma) + drive->dma |= 'U'<<16; + } + if (conf.idedma) + drive->dmactl = drive->dma; /* FS special */ + else + drive->dma = 0; + return dma; +} + +static int +ataidentify(int cmdport, int ctlport, int dev, int pkt, void* info) +{ + int as, command, drdy; + + if(pkt){ + command = Cidpkt; + drdy = 0; + } + else{ + command = Cid; + drdy = Drdy; + } + as = ataready(cmdport, ctlport, dev, Bsy|Drq, drdy, 103*1000); + if(as < 0) + return as; + outb(cmdport+Cmd, command); + microdelay(1); + + as = ataready(cmdport, ctlport, 0, Bsy, Drq|Err, 400*1000); + if(as < 0) + return -1; + if(as & Err) + return as; + + memset(info, 0, 512); + inss(cmdport+Data, info, 256); + inb(cmdport+Status); + + if(DEBUG & DbgIDENTIFY){ + int i; + ushort *sp; + + sp = (ushort*)info; + for(i = 0; i < 256; i++){ + if(i && (i%16) == 0) + print("\n"); + print(" %4.4uX", *sp); + sp++; + } + print("\n"); + } + + return 0; +} + +/* + * DEBUGGING only. + * write, read and verify block 1 (never used in an fs otherwise) + * to see if dma and rwm actually work. + * if not, turn them off, though the kernel could be corrupt by then. + */ +static void +ataverify(Drive *dp) +{ + int n, nb, dev = dp->driveno; + uchar *buf = dp->buf; + + if (dp->ctlr == nil) + panic("ataverify: nil ctlr for drive"); + atadriveprobe(dev); + print("ataverify h%d...", dev); + for (n = 0; n < RBUFSIZE; n++) + buf[n] = n; + if (ataseek(dev, RBUFSIZE) < 0) + panic("ataverify: seek 1"); + nb = atawrite(dev, buf, RBUFSIZE); + if (nb != RBUFSIZE) + print("short write of %d bytes to block 1\n", nb); + else { + for (n = 0; n < RBUFSIZE; n++) + buf[n] = ~n; + if (ataseek(dev, RBUFSIZE) < 0) + panic("ataverify: seek 1"); + nb = ataread(dev, buf, RBUFSIZE); + if (nb != RBUFSIZE) + print("short read of %d bytes to block 1\n", nb); + else { + for (n = 0; n < RBUFSIZE; n++) + if ((uchar)buf[n] != (uchar)n) + break; + if (n == RBUFSIZE) { + print("OK\n"); + return; /* verified OK */ + } + print("byte comparison failed\n"); + } + } + print("ataverify: disabling dma and rwm\n"); + dp->dmactl = dp->rwmctl = 0; +} + +static Drive* +atagetdrive(int cmdport, int ctlport, int dev) +{ + Drive *drive; + int as, i, pkt, driveno; + uchar buf[512], *p; + ushort iconfig, *sp; + + driveno = (cmdport == Ctlr0cmd? 0: + cmdport == Ctlr1cmd? NCtlrdrv: 2*NCtlrdrv); + if (dev == Dev1) + driveno++; + + atadebug(0, 0, "identify: port 0x%uX dev 0x%2.2uX\n", cmdport, dev); + pkt = 1; +retry: + as = ataidentify(cmdport, ctlport, dev, pkt, buf); + if(as < 0) + return nil; + if(as & Err){ + if(pkt == 0) + return nil; + pkt = 0; + goto retry; + } + + if((drive = malloc(sizeof(Drive))) == nil) + return nil; + drive->dev = dev; + drive->driveno = -1; /* unset */ + memmove(drive->info, buf, sizeof(drive->info)); + drive->sense[0] = 0x70; + drive->sense[7] = sizeof(drive->sense)-7; + + drive->inquiry[2] = 2; + drive->inquiry[3] = 2; + drive->inquiry[4] = sizeof(drive->inquiry)-4; + p = &drive->inquiry[8]; + sp = &drive->info[Imodel]; + for(i = 0; i < 20; i++){ + *p++ = *sp>>8; + *p++ = *sp++; + } + + drive->secsize = 512; + + /* + * Beware the CompactFlash Association feature set. + * Now, why this value in Iconfig just walks all over the bit + * definitions used in the other parts of the ATA/ATAPI standards + * is a mystery and a sign of true stupidity on someone's part. + * Anyway, the standard says if this value is 0x848A then it's + * CompactFlash and it's NOT a packet device. + */ + iconfig = drive->info[Iconfig]; + if(iconfig != 0x848A && (iconfig & 0xC000) == 0x8000){ + print("atagetdrive: port 0x%uX dev 0x%2.2uX: packet device\n", + cmdport, dev); + if(iconfig & 0x01) + drive->pkt = 16; + else + drive->pkt = 12; + } + else{ + if (iconfig == 0x848A) + print("atagetdrive: port 0x%uX dev 0x%2.2uX: non-packet CF device\n", + cmdport, dev); + if(drive->info[Ivalid] & 0x0001){ + drive->c = drive->info[Iccyl]; + drive->h = drive->info[Ichead]; + drive->s = drive->info[Icsec]; + }else{ + drive->c = drive->info[Ilcyl]; + drive->h = drive->info[Ilhead]; + drive->s = drive->info[Ilsec]; + } + if(drive->info[Icapabilities] & Mlba){ + if(drive->info[Icsfs+1] & Maddr48){ + drive->sectors = drive->info[Ilba48] + | (drive->info[Ilba48+1]<<16) + | ((Devsize)drive->info[Ilba48+2]<<32); + drive->flags |= Lba48; + }else + drive->sectors = (drive->info[Ilba+1]<<16) + |drive->info[Ilba]; + drive->dev |= Lba; + drive->lba = 1; + }else + drive->sectors = drive->c * drive->h * drive->s; + atarwmmode(drive, cmdport, ctlport, dev); + } + atadmamode(drive); + + if(DEBUG & DbgCONFIG){ + print("ata h%d: dev %2.2uX port %uX config %4.4uX capabilities %4.4uX", + driveno, dev, cmdport, iconfig, + drive->info[Icapabilities]); + print(" mwdma %4.4uX", drive->info[Imwdma]); + if(drive->info[Ivalid] & 0x04) + print(" udma %4.4uX", drive->info[Iudma]); + print(" dma %8.8uX rwm %ud\n", drive->dma, drive->rwm); + if(drive->flags&Lba48) + print("\tLLBA sectors %lld\n", (Wideoff)drive->sectors); + } + return drive; +} + +static void +atasrst(int ctlport) +{ + /* + * Srst is a big stick and may cause problems if further + * commands are tried before the drives become ready again. + * Also, there will be problems here if overlapped commands + * are ever supported. + */ + microdelay(5); + outb(ctlport+Dc, Srst); + microdelay(5); + outb(ctlport+Dc, 0); + microdelay(2*1000); +} + +static int drivenum = 0; /* hope that we probe in order */ + +static void +updprobe(int cmdport) +{ + if(cmdport == Ctlr0cmd) + drivenum = NCtlrdrv; + else if (cmdport == Ctlr1cmd) + drivenum = 2*NCtlrdrv; +} + +static SDev* +ataprobe(int cmdport, int ctlport, int irq) +{ + Ctlr* ctlr; + SDev *sdev; + Drive *drive; + int i, dev, error, rhi, rlo; + + if(cmdport == Ctlr0cmd) + drivenum = 0; + else if (cmdport == Ctlr1cmd) + drivenum = NCtlrdrv; + + if(ioalloc(cmdport, 8, 0, "atacmd") < 0) { + print("ataprobe: Cannot allocate %X\n", cmdport); + updprobe(cmdport); + return nil; + } + if(ioalloc(ctlport+As, 1, 0, "atactl") < 0){ + print("ataprobe: Cannot allocate %X\n", ctlport + As); + iofree(cmdport); + updprobe(cmdport); + return nil; + } + + /* + * Try to detect a floating bus. + * Bsy should be cleared. If not, see if the cylinder registers + * are read/write capable. + * If the master fails, try the slave to catch slave-only + * configurations. + * There's no need to restore the tested registers as they will + * be reset on any detected drives by the Cedd command. + * All this indicates is that there is at least one drive on the + * controller; when the non-existent drive is selected in a + * single-drive configuration the registers of the existing drive + * are often seen, only command execution fails. + */ + dev = Dev0; + if(inb(ctlport+As) & Bsy){ + outb(cmdport+Dh, dev); + microdelay(1); +trydev1: + atadebug(cmdport, ctlport, "ataprobe bsy"); + outb(cmdport+Cyllo, 0xAA); + outb(cmdport+Cylhi, 0x55); + outb(cmdport+Sector, 0xFF); + rlo = inb(cmdport+Cyllo); + rhi = inb(cmdport+Cylhi); + if(rlo != 0xAA && (rlo == 0xFF || rhi != 0x55)){ + if(dev == Dev1){ +release: + iofree(cmdport); + iofree(ctlport+As); + updprobe(cmdport); + return nil; + } + dev = Dev1; + if(ataready(cmdport, ctlport, dev, Bsy, 0, 20*1000) < 0) + goto trydev1; + } + } + + /* + * Disable interrupts on any detected controllers. + */ + outb(ctlport+Dc, Nien); +tryedd1: + if(ataready(cmdport, ctlport, dev, Bsy|Drq, 0, 105*1000) < 0){ + /* + * There's something there, but it didn't come up clean, + * so try hitting it with a big stick. The timing here is + * wrong but this is a last-ditch effort and it sometimes + * gets some marginal hardware back online. + */ + atasrst(ctlport); + if(ataready(cmdport, ctlport, dev, Bsy|Drq, 0, 106*1000) < 0) + goto release; + } + + /* + * Can only get here if controller is not busy. + * If there are drives Bsy will be set within 400nS, + * must wait 2mS before testing Status. + * Wait for the command to complete (6 seconds max). + */ + outb(cmdport+Cmd, Cedd); + delay(2); + if(ataready(cmdport, ctlport, dev, Bsy|Drq, 0, 6*1000*1000) < 0) + goto release; + + /* + * If bit 0 of the error register is set then the selected drive + * exists. This is enough to detect single-drive configurations. + * However, if the master exists there is no way short of executing + * a command to determine if a slave is present. + * It appears possible to get here testing Dev0 although it doesn't + * exist and the EDD won't take, so try again with Dev1. + */ + error = inb(cmdport+Error); + atadebug(cmdport, ctlport, "ataprobe: dev %uX", dev); + if((error & ~0x80) != 0x01){ + if(dev == Dev1) + goto release; + dev = Dev1; + goto tryedd1; + } + + /* + * At least one drive is known to exist, try to + * identify it. If that fails, don't bother checking + * any further. + * If the one drive found is Dev0 and the EDD command + * didn't indicate Dev1 doesn't exist, check for it. + */ + if((drive = atagetdrive(cmdport, ctlport, dev)) == nil) + goto release; + if((ctlr = malloc(sizeof(Ctlr))) == nil){ + free(drive); + goto release; + } + memset(ctlr, 0, sizeof(Ctlr)); + if((sdev = malloc(sizeof(SDev))) == nil){ + free(ctlr); + free(drive); + goto release; + } + memset(sdev, 0, sizeof(SDev)); + drive->ctlr = ctlr; + + atactlr[drivenum/NCtlrdrv] = ctlr; + atadrive[drivenum++] = drive; + sdevs[drivenum/NCtlrdrv] = sdev; + + if(dev == Dev0){ + ctlr->drive[0] = drive; + if(!(error & 0x80)){ + /* + * Always leave Dh pointing to a valid drive, + * otherwise a subsequent call to ataready on + * this controller may try to test a bogus Status. + * Ataprobe is the only place possibly invalid + * drives should be selected. + */ + drive = atagetdrive(cmdport, ctlport, Dev1); + if(drive != nil){ + drive->ctlr = ctlr; + ctlr->drive[1] = drive; + } + else{ + outb(cmdport+Dh, Dev0); + microdelay(1); + } + atadrive[drivenum] = drive; + } + } + else + ctlr->drive[1] = drive; + drivenum++; + + print("ata%d: cmd 0x%ux ctl 0x%ux irq %d\n", + (drivenum-1)/NCtlrdrv, cmdport, ctlport, irq); + ctlr->cmdport = cmdport; + ctlr->ctlport = ctlport; + ctlr->irq = irq; + ctlr->tbdf = BUSUNKNOWN; + ctlr->command = Cedd; /* debugging */ + + sdev->ifc = &sdataifc; + sdev->ctlr = ctlr; + sdev->nunit = NCtlrdrv; + ctlr->sdev = sdev; + + if (0) + for (i = drivenum - 2; i < drivenum; i++) + if (atadrive[i]) + ataverify(atadrive[i]); + updprobe(cmdport); + return sdev; +} + +static void +ataclear(SDev *sdev) +{ + Ctlr* ctlr; + + ctlr = sdev->ctlr; + iofree(ctlr->cmdport); + iofree(ctlr->ctlport + As); + + if (ctlr->drive[0]) + free(ctlr->drive[0]); + if (ctlr->drive[1]) + free(ctlr->drive[1]); + /* TODO: clear entries in atadrive[] too */ + if (sdev->name) + free(sdev->name); + free(ctlr); + free(sdev); +} + +static char * +atastat(SDev *sdev, char *p, char *e) +{ + Ctlr *ctlr = sdev->ctlr; + + return seprint(p, e, "%s ata port %X ctl %X irq %d\n", + sdev->name, ctlr->cmdport, ctlr->ctlport, ctlr->irq); +} + +#ifndef FS +static SDev* +ataprobew(DevConf *cf) +{ + if (cf->nports != 2) + error(Ebadarg); + + return ataprobe(cf->ports[0].port, cf->ports[1].port, cf->intnum); +} +#endif + +static int +atasetsense(Drive* drive, int status, int key, int asc, int ascq) +{ + drive->sense[2] = key; + drive->sense[12] = asc; + drive->sense[13] = ascq; + + return status; +} + +static int +atastandby(Drive* drive, int period) +{ + Ctlr* ctlr; + int cmdport, done; + + ctlr = drive->ctlr; + drive->command = Cstandby; + qlock(ctlr); + + cmdport = ctlr->cmdport; + ilock(ctlr); + outb(cmdport+Count, period); + outb(cmdport+Dh, drive->dev); + ctlr->done = 0; + ctlr->curdrive = drive; + ctlr->command = Cstandby; /* debugging */ + outb(cmdport+Cmd, Cstandby); + iunlock(ctlr); + + while(waserror()) + ; + tsleep(ctlr, atadone, ctlr, 30*1000); + poperror(); + + done = ctlr->done; + qunlock(ctlr); + + if(!done || (drive->status & Err)) + return atasetsense(drive, SDcheck, 4, 8, drive->error); + return SDok; +} + +static int +atamodesense(Drive* drive, uchar* cmd) +{ + int len; + + /* + * Fake a vendor-specific request with page code 0, + * return the drive info. + */ + if((cmd[2] & 0x3F) != 0 && (cmd[2] & 0x3F) != 0x3F) + return atasetsense(drive, SDcheck, 0x05, 0x24, 0); + len = (cmd[7]<<8)|cmd[8]; + if(len == 0) + return SDok; + if(len < 8+sizeof(drive->info)) + return atasetsense(drive, SDcheck, 0x05, 0x1A, 0); + if(drive->data == nil || drive->dlen < len) + return atasetsense(drive, SDcheck, 0x05, 0x20, 1); + memset(drive->data, 0, 8); + drive->data[0] = sizeof(drive->info)>>8; + drive->data[1] = sizeof(drive->info); + memmove(drive->data+8, drive->info, sizeof(drive->info)); + drive->data += 8+sizeof(drive->info); + + return SDok; +} + +static void +atanop(Drive* drive, int subcommand) +{ + Ctlr* ctlr; + int as, cmdport, ctlport, timeo; + + /* + * Attempt to abort a command by using NOP. + * In response, the drive is supposed to set Abrt + * in the Error register, set (Drdy|Err) in Status + * and clear Bsy when done. However, some drives + * (e.g. ATAPI Zip) just go Bsy then clear Status + * when done, hence the timeout loop only on Bsy + * and the forced setting of drive->error. + */ + ctlr = drive->ctlr; + cmdport = ctlr->cmdport; + outb(cmdport+Features, subcommand); + outb(cmdport+Dh, drive->dev); + ctlr->command = Cnop; /* debugging */ + outb(cmdport+Cmd, Cnop); + + microdelay(1); + ctlport = ctlr->ctlport; + for(timeo = 0; timeo < 1000; timeo++){ + as = inb(ctlport+As); + if(!(as & Bsy)) + break; + microdelay(1); + } + drive->error |= Abrt; +} + +static void +ataabort(Drive* drive, int dolock) +{ + /* + * If NOP is available (packet commands) use it otherwise + * must try a software reset. + */ + if(dolock) + ilock(drive->ctlr); + if(drive->info[Icsfs] & Mnop) + atanop(drive, 0); + else{ + atasrst(drive->ctlr->ctlport); + drive->error |= Abrt; + } + if(dolock) + iunlock(drive->ctlr); +} + +static int +atadmasetup(Drive* drive, int len) +{ + Prd *prd; + ulong pa; + Ctlr *ctlr; + int bmiba, bmisx, count, i, span; + + ctlr = drive->ctlr; + pa = PCIWADDR(drive->data); + if(pa & 0x03) + return -1; + + /* + * Sometimes drives identify themselves as being DMA capable + * although they are not on a busmastering controller. + */ + prd = ctlr->prdt; + if(prd == nil){ + drive->dmactl = 0; + print("h%d: disabling dma: not on a busmastering controller\n", + drive->driveno); + return -1; + } + + for(i = 0; len && i < Nprd; i++){ + prd->pa = pa; + span = ROUNDUP(pa, ctlr->span); + if(span == pa) + span += ctlr->span; + count = span - pa; + if(count >= len){ + prd->count = PrdEOT|len; + break; + } + prd->count = count; + len -= count; + pa += count; + prd++; + } + if(i == Nprd) + (prd-1)->count |= PrdEOT; + + bmiba = ctlr->bmiba; + outl(bmiba+Bmidtpx, PCIWADDR(ctlr->prdt)); + if(drive->write) + outb(ctlr->bmiba+Bmicx, 0); + else + outb(ctlr->bmiba+Bmicx, Rwcon); + bmisx = inb(bmiba+Bmisx); + outb(bmiba+Bmisx, bmisx|Ideints|Idedmae); + + return 0; +} + +static void +atadmastart(Ctlr* ctlr, int write) +{ + if(write) + outb(ctlr->bmiba+Bmicx, Ssbm); + else + outb(ctlr->bmiba+Bmicx, Rwcon|Ssbm); +} + +static int +atadmastop(Ctlr* ctlr) +{ + int bmiba; + + bmiba = ctlr->bmiba; + outb(bmiba+Bmicx, inb(bmiba+Bmicx) & ~Ssbm); + + return inb(bmiba+Bmisx); +} + +static void +atadmainterrupt(Drive* drive, int count) +{ + Ctlr* ctlr; + int bmiba, bmisx; + + ctlr = drive->ctlr; + bmiba = ctlr->bmiba; + bmisx = inb(bmiba+Bmisx); + switch(bmisx & (Ideints|Idedmae|Bmidea)){ + case Bmidea: + /* + * Data transfer still in progress, nothing to do + * (this should never happen). + */ + return; + + case Ideints: + case Ideints|Bmidea: + /* + * Normal termination, tidy up. + */ + drive->data += count; + break; + + default: + /* + * What's left are error conditions (memory transfer + * problem) and the device is not done but the PRD is + * exhausted. For both cases must somehow tell the + * drive to abort. + */ + ataabort(drive, 0); + break; + } + atadmastop(ctlr); + ctlr->done = 1; +} + +static void +atapktinterrupt(Drive* drive) +{ + Ctlr* ctlr; + int cmdport, len; + + ctlr = drive->ctlr; + cmdport = ctlr->cmdport; + switch(inb(cmdport+Ir) & (/*Rel|*/Io|Cd)){ + case Cd: + outss(cmdport+Data, drive->pktcmd, drive->pkt/2); + break; + + case 0: + len = (inb(cmdport+Bytehi)<<8)|inb(cmdport+Bytelo); + if(drive->data+len > drive->limit){ + atanop(drive, 0); + break; + } + outss(cmdport+Data, drive->data, len/2); + drive->data += len; + break; + + case Io: + len = (inb(cmdport+Bytehi)<<8)|inb(cmdport+Bytelo); + if(drive->data+len > drive->limit){ + atanop(drive, 0); + break; + } + inss(cmdport+Data, drive->data, len/2); + drive->data += len; + break; + + case Io|Cd: + if(drive->pktdma) + atadmainterrupt(drive, drive->dlen); + else + ctlr->done = 1; + break; + } +} + +static int +atapktio(Drive* drive, uchar* cmd, int clen) +{ + Ctlr *ctlr; + int as, cmdport, ctlport, len, r, timeo; + + if(cmd[0] == 0x5A && (cmd[2] & 0x3F) == 0) + return atamodesense(drive, cmd); + + r = SDok; + + drive->command = Cpkt; + memmove(drive->pktcmd, cmd, clen); + memset(drive->pktcmd+clen, 0, drive->pkt-clen); + drive->limit = drive->data+drive->dlen; + + ctlr = drive->ctlr; + cmdport = ctlr->cmdport; + ctlport = ctlr->ctlport; + + qlock(ctlr); + + if(ataready(cmdport, ctlport, drive->dev, Bsy|Drq, 0, 107*1000) < 0){ + qunlock(ctlr); + return -1; + } + + ilock(ctlr); + if(drive->dlen && drive->dmactl && !atadmasetup(drive, drive->dlen)) + drive->pktdma = Dma; + else + drive->pktdma = 0; + + outb(cmdport+Features, drive->pktdma); + outb(cmdport+Count, 0); + outb(cmdport+Sector, 0); + len = 16*drive->secsize; + outb(cmdport+Bytelo, len); + outb(cmdport+Bytehi, len>>8); + outb(cmdport+Dh, drive->dev); + ctlr->done = 0; + ctlr->curdrive = drive; + ctlr->command = Cpkt; /* debugging */ + if(drive->pktdma) + atadmastart(ctlr, drive->write); + outb(cmdport+Cmd, Cpkt); + + if((drive->info[Iconfig] & Mdrq) != 0x0020){ + microdelay(1); + as = ataready(cmdport, ctlport, 0, Bsy, Drq|Chk, 4*1000); + if(as < 0) + r = SDtimeout; + else if(as & Chk) + r = SDcheck; + else + atapktinterrupt(drive); + } + iunlock(ctlr); + + while(waserror()) + ; + if(!drive->pktdma) + sleep(ctlr, atadone, ctlr); + else for(timeo = 0; !ctlr->done; timeo++){ + tsleep(ctlr, atadone, ctlr, 1000); + if(ctlr->done) + break; + ilock(ctlr); + atadmainterrupt(drive, 0); + if(!drive->error && timeo > 10){ + ataabort(drive, 0); + atadmastop(ctlr); + drive->dmactl = 0; + drive->error |= Abrt; + } + if(drive->error){ + drive->status |= Chk; + ctlr->curdrive = nil; + } + iunlock(ctlr); + } + poperror(); + + qunlock(ctlr); + + if(drive->status & Chk) + r = SDcheck; + + return r; +} + +static uchar cmd48[256] = { + [Crs] Crs48, + [Crd] Crd48, + [Crdq] Crdq48, + [Crsm] Crsm48, + [Cws] Cws48, + [Cwd] Cwd48, + [Cwdq] Cwdq48, + [Cwsm] Cwsm48, +}; + +static int +atageniostart(Drive* drive, Devsize lba) +{ + Ctlr *ctlr; + uchar cmd; + int as, c, cmdport, ctlport, h, len, s, use48; + + use48 = 0; + if((drive->flags&Lba48always) || (lba>>28) || drive->count > 256){ + if(!(drive->flags & Lba48)) + return -1; + use48 = 1; + c = h = s = 0; + }else if(drive->dev & Lba){ + c = (lba>>8) & 0xFFFF; + h = (lba>>24) & 0x0F; + s = lba & 0xFF; + }else{ + c = lba/(drive->s*drive->h); + h = ((lba/drive->s) % drive->h); + s = (lba % drive->s) + 1; + } + + ctlr = drive->ctlr; + cmdport = ctlr->cmdport; + ctlport = ctlr->ctlport; + if(ataready(cmdport, ctlport, drive->dev, Bsy|Drq, 0, 101*1000) < 0) + return -1; + + ilock(ctlr); + if(drive->dmactl && !atadmasetup(drive, drive->count*drive->secsize)){ + if(drive->write) + drive->command = Cwd; + else + drive->command = Crd; + } + else if(drive->rwmctl){ + drive->block = drive->rwm*drive->secsize; + if(drive->write) + drive->command = Cwsm; + else + drive->command = Crsm; + } + else{ + drive->block = drive->secsize; + if(drive->write) + drive->command = Cws; + else + drive->command = Crs; + } + drive->limit = drive->data + drive->count*drive->secsize; + cmd = drive->command; + if(use48){ + outb(cmdport+Count, (drive->count>>8) & 0xFF); + outb(cmdport+Count, drive->count & 0XFF); + outb(cmdport+Lbalo, (lba>>24) & 0xFF); + outb(cmdport+Lbalo, lba & 0xFF); + outb(cmdport+Lbamid, (lba>>32) & 0xFF); + outb(cmdport+Lbamid, (lba>>8) & 0xFF); + outb(cmdport+Lbahi, (lba>>40) & 0xFF); + outb(cmdport+Lbahi, (lba>>16) & 0xFF); + outb(cmdport+Dh, drive->dev|Lba); + cmd = cmd48[cmd]; + + if(DEBUG & Dbg48BIT) + print("using 48-bit commands\n"); + }else{ + outb(cmdport+Count, drive->count); + outb(cmdport+Sector, s); + outb(cmdport+Cyllo, c); + outb(cmdport+Cylhi, c>>8); + outb(cmdport+Dh, drive->dev|h); + } + ctlr->done = 0; + ctlr->curdrive = drive; + ctlr->command = drive->command; /* debugging */ + outb(cmdport+Cmd, cmd); + + switch(drive->command){ + case Cws: + case Cwsm: + microdelay(1); + as = ataready(cmdport, ctlport, 0, Bsy, Drq|Err, 1000); + if(as < 0 || (as & Err)){ + iunlock(ctlr); + return -1; + } + len = drive->block; + if(drive->data+len > drive->limit) + len = drive->limit-drive->data; + outss(cmdport+Data, drive->data, len/2); + break; + + case Crd: + case Cwd: + atadmastart(ctlr, drive->write); + break; + } + iunlock(ctlr); + + return 0; +} + +static int +atagenioretry(Drive* drive) +{ + if(drive->dmactl){ + drive->dmactl = 0; + print("atagenioretry: disabling dma\n"); + } + else if(drive->rwmctl) + drive->rwmctl = 0; + else + return atasetsense(drive, SDcheck, 4, 8, drive->error); + + return SDretry; +} + +static int +atagenio(Drive* drive, uchar* cmd, int) +{ + uchar *p; + Ctlr *ctlr; + Devsize lba, len; + int count, maxio; + + /* + * Map SCSI commands into ATA commands for discs. + * Fail any command with a LUN except INQUIRY which + * will return 'logical unit not supported'. + */ + if((cmd[1]>>5) && cmd[0] != 0x12) + return atasetsense(drive, SDcheck, 0x05, 0x25, 0); + + switch(cmd[0]){ + default: + return atasetsense(drive, SDcheck, 0x05, 0x20, 0); + + case 0x00: /* test unit ready */ + return SDok; + + case 0x03: /* request sense */ + if(cmd[4] < sizeof(drive->sense)) + len = cmd[4]; + else + len = sizeof(drive->sense); + if(drive->data && drive->dlen >= len){ + memmove(drive->data, drive->sense, len); + drive->data += len; + } + return SDok; + + case 0x12: /* inquiry */ + if(cmd[4] < sizeof(drive->inquiry)) + len = cmd[4]; + else + len = sizeof(drive->inquiry); + if(drive->data && drive->dlen >= len){ + memmove(drive->data, drive->inquiry, len); + drive->data += len; + } + return SDok; + + case 0x1B: /* start/stop unit */ + /* + * NOP for now, can use the power management feature + * set later. + */ + return SDok; + + case 0x25: /* read capacity */ + if((cmd[1] & 0x01) || cmd[2] || cmd[3]) + return atasetsense(drive, SDcheck, 0x05, 0x24, 0); + if(drive->data == nil || drive->dlen < 8) + return atasetsense(drive, SDcheck, 0x05, 0x20, 1); + /* + * Read capacity returns the LBA of the last sector. + */ + len = drive->sectors-1; + p = drive->data; + *p++ = len>>24; + *p++ = len>>16; + *p++ = len>>8; + *p++ = len; + len = drive->secsize; + *p++ = len>>24; + *p++ = len>>16; + *p++ = len>>8; + *p = len; + drive->data += 8; + return SDok; + + case 0x9E: /* long read capacity */ + if((cmd[1] & 0x01) || cmd[2] || cmd[3]) + return atasetsense(drive, SDcheck, 0x05, 0x24, 0); + if(drive->data == nil || drive->dlen < 8) + return atasetsense(drive, SDcheck, 0x05, 0x20, 1); + /* + * Read capacity returns the LBA of the last sector. + */ + len = drive->sectors-1; + p = drive->data; + *p++ = len>>56; + *p++ = len>>48; + *p++ = len>>40; + *p++ = len>>32; + *p++ = len>>24; + *p++ = len>>16; + *p++ = len>>8; + *p++ = len; + len = drive->secsize; + *p++ = len>>24; + *p++ = len>>16; + *p++ = len>>8; + *p = len; + drive->data += 8; + return SDok; + + case 0x28: /* read */ + case 0x2A: /* write */ + break; + + case 0x5A: + return atamodesense(drive, cmd); + } + + ctlr = drive->ctlr; + lba = (cmd[2]<<24)|(cmd[3]<<16)|(cmd[4]<<8)|cmd[5]; + count = (cmd[7]<<8)|cmd[8]; + if(drive->data == nil) + return SDok; + if(drive->dlen < count*drive->secsize) + count = drive->dlen/drive->secsize; + qlock(ctlr); + if(ctlr->maxio) + maxio = ctlr->maxio; + else if(drive->flags & Lba48) + maxio = 65536; + else + maxio = 256; + while(count){ + if(count > maxio) + drive->count = maxio; + else + drive->count = count; + if(atageniostart(drive, lba)){ + ilock(ctlr); + atanop(drive, 0); + iunlock(ctlr); + qunlock(ctlr); + return atagenioretry(drive); + } + + while(waserror()) + ; + tsleep(ctlr, atadone, ctlr, 60*1000); + poperror(); + if(!ctlr->done){ + /* + * What should the above timeout be? In + * standby and sleep modes it could take as + * long as 30 seconds for a drive to respond. + * Very hard to get out of this cleanly. + */ + atadumpstate(drive, cmd, lba, count); + ataabort(drive, 1); + qunlock(ctlr); + return atagenioretry(drive); + } + + if(drive->status & Err){ + qunlock(ctlr); + return atasetsense(drive, SDcheck, 4, 8, drive->error); + } + count -= drive->count; + lba += drive->count; + } + qunlock(ctlr); + + return SDok; +} + +static int +atario(SDreq* r) +{ + Ctlr *ctlr; + Drive *drive; + SDunit *unit; + uchar cmd10[10], *cmdp, *p; + int clen, reqstatus, status; + + unit = r->unit; + if((ctlr = unit->dev->ctlr) == nil || ctlr->drive[unit->subno] == nil){ + r->status = SDtimeout; + return SDtimeout; + } + drive = ctlr->drive[unit->subno]; + + /* + * Most SCSI commands can be passed unchanged except for + * the padding on the end. The few which require munging + * are not used internally. Mode select/sense(6) could be + * converted to the 10-byte form but it's not worth the + * effort. Read/write(6) are easy. + */ + switch(r->cmd[0]){ + case 0x08: /* read */ + case 0x0A: /* write */ + cmdp = cmd10; + memset(cmdp, 0, sizeof(cmd10)); + cmdp[0] = r->cmd[0]|0x20; + cmdp[1] = r->cmd[1] & 0xE0; + cmdp[5] = r->cmd[3]; + cmdp[4] = r->cmd[2]; + cmdp[3] = r->cmd[1] & 0x0F; + cmdp[8] = r->cmd[4]; + clen = sizeof(cmd10); + break; + + default: + cmdp = r->cmd; + clen = r->clen; + break; + } + + qlock(drive); +retry: + drive->write = r->write; + drive->data = r->data; + drive->dlen = r->dlen; + + drive->status = 0; + drive->error = 0; + if(drive->pkt) + status = atapktio(drive, cmdp, clen); + else + status = atagenio(drive, cmdp, clen); + if(status == SDretry){ + if(DEBUG & DbgDEBUG) + print("%s: retry: dma %8.8uX rwm %4.4uX\n", + unit->name, drive->dmactl, drive->rwmctl); + goto retry; + } + if(status == SDok){ + atasetsense(drive, SDok, 0, 0, 0); + if(drive->data){ + p = r->data; + r->rlen = drive->data - p; + } + else + r->rlen = 0; + } + else if(status == SDcheck && !(r->flags & SDnosense)){ + drive->write = 0; + memset(cmd10, 0, sizeof(cmd10)); + cmd10[0] = 0x03; + cmd10[1] = r->lun<<5; + cmd10[4] = sizeof(r->sense)-1; + drive->data = r->sense; + drive->dlen = sizeof(r->sense)-1; + drive->status = 0; + drive->error = 0; + if(drive->pkt) + reqstatus = atapktio(drive, cmd10, 6); + else + reqstatus = atagenio(drive, cmd10, 6); + if(reqstatus == SDok){ + r->flags |= SDvalidsense; + atasetsense(drive, SDok, 0, 0, 0); + } + } + qunlock(drive); + r->status = status; + if(status != SDok) + return status; + + /* + * Fix up any results. + * Many ATAPI CD-ROMs ignore the LUN field completely and + * return valid INQUIRY data. Patch the response to indicate + * 'logical unit not supported' if the LUN is non-zero. + */ + switch(cmdp[0]){ + case 0x12: /* inquiry */ + if((p = r->data) == nil) + break; + if((cmdp[1]>>5) && (!drive->pkt || (p[0] & 0x1F) == 0x05)) + p[0] = 0x7F; + /*FALLTHROUGH*/ + default: + break; + } + + return SDok; +} + +static void +atainterrupt(Ureg*, void* arg) +{ + Ctlr *ctlr; + Drive *drive; + int cmdport, len, status; + + ctlr = arg; + + ilock(ctlr); + if(inb(ctlr->ctlport+As) & Bsy){ + iunlock(ctlr); + if(DEBUG & DbgBsy) + print("IBsy+"); + return; + } + cmdport = ctlr->cmdport; + status = inb(cmdport+Status); + if((drive = ctlr->curdrive) == nil){ + iunlock(ctlr); + if((DEBUG & DbgINL) && ctlr->command != Cedd) + print("Inil%2.2uX+", ctlr->command); + return; + } + + if(status & Err) + drive->error = inb(cmdport+Error); + else switch(drive->command){ + default: + drive->error = Abrt; + break; + + case Crs: + case Crsm: + if(!(status & Drq)){ + drive->error = Abrt; + break; + } + len = drive->block; + if(drive->data+len > drive->limit) + len = drive->limit-drive->data; + inss(cmdport+Data, drive->data, len/2); + drive->data += len; + if(drive->data >= drive->limit) + ctlr->done = 1; + break; + + case Cws: + case Cwsm: + len = drive->block; + if(drive->data+len > drive->limit) + len = drive->limit-drive->data; + drive->data += len; + if(drive->data >= drive->limit){ + ctlr->done = 1; + break; + } + if(!(status & Drq)){ + drive->error = Abrt; + break; + } + len = drive->block; + if(drive->data+len > drive->limit) + len = drive->limit-drive->data; + outss(cmdport+Data, drive->data, len/2); + break; + + case Cpkt: + atapktinterrupt(drive); + break; + + case Crd: + case Cwd: + atadmainterrupt(drive, drive->count*drive->secsize); + break; + + case Cstandby: + ctlr->done = 1; + break; + } + iunlock(ctlr); + + if(drive->error){ + status |= Err; + ctlr->done = 1; + } + + if(ctlr->done){ + ctlr->curdrive = nil; + drive->status = status; + wakeup(ctlr); + } +} + +static SDev* +atapnp(void) +{ + Ctlr *ctlr; + Pcidev *p; + SDev *legacy[2], *sdev, *head, *tail; + int channel, ispc87415, maxio, pi, r, span; + static int done; + + if (done) + return nil; + done = 1; + + legacy[0] = legacy[1] = head = tail = nil; + if(sdev = ataprobe(Ctlr0cmd, Ctlr0ctl, IrqATA0)){ + head = tail = sdev; + legacy[0] = sdev; + } + if(sdev = ataprobe(Ctlr1cmd, Ctlr1ctl, IrqATA1)){ + if(head != nil) + tail->next = sdev; + else + head = sdev; + tail = sdev; + legacy[1] = sdev; + } + + p = nil; + while(p = pcimatch(p, 0, 0)){ + /* + * Look for devices with the correct class and sub-class + * code and known device and vendor ID; add native-mode + * channels to the list to be probed, save info for the + * compatibility mode channels. + * Note that the legacy devices should not be considered + * PCI devices by the interrupt controller. + * For both native and legacy, save info for busmastering + * if capable. + * Promise Ultra ATA/66 (PDC20262) appears to + * 1) give a sub-class of 'other mass storage controller' + * instead of 'IDE controller', regardless of whether it's + * the only controller or not; + * 2) put 0 in the programming interface byte (probably + * as a consequence of 1) above). + * Sub-class code 0x04 is 'RAID controller', e.g. VIA VT8237. + */ + if(p->ccrb != 0x01) + continue; + + /* + * file server special: ccru is a short in the FS kernel, + * thus the cast to uchar. + */ + switch ((uchar)p->ccru) { + case 1: + case 4: + case 0x80: + break; + default: + continue; + } + + pi = p->ccrp; + ispc87415 = 0; + maxio = 0; + span = BMspan; + + switch((p->did<<16)|p->vid){ + default: + continue; + + case (0x0002<<16)|0x100B: /* NS PC87415 */ + /* + * Disable interrupts on both channels until + * after they are probed for drives. + * This must be called before interrupts are + * enabled because the IRQ may be shared. + */ + ispc87415 = 1; + pcicfgw32(p, 0x40, 0x00000300); + break; + case (0x1000<<16)|0x1042: /* PC-Tech RZ1000 */ + /* + * Turn off prefetch. Overkill, but cheap. + */ + r = pcicfgr32(p, 0x40); + r &= ~0x2000; + pcicfgw32(p, 0x40, r); + break; + case (0x4D38<<16)|0x105A: /* Promise PDC20262 */ + case (0x4D30<<16)|0x105A: /* Promise PDC202xx */ + case (0x4D68<<16)|0x105A: /* Promise PDC20268 */ + case (0x4D69<<16)|0x105A: /* Promise Ultra/133 TX2 */ + case (0x3373<<16)|0x105A: /* Promise 20378 RAID */ + case (0x3149<<16)|0x1106: /* VIA VT8237 SATA/RAID */ + case (0x3112<<16)|0x1095: /* SiI 3112 SATA/RAID */ + maxio = 15; + span = 8*1024; + /*FALLTHROUGH*/ + case (0x3114<<16)|0x1095: /* SiI 3114 SATA/RAID */ + pi = 0x85; + break; + case (0x0004<<16)|0x1103: /* HighPoint HPT366 */ + pi = 0x85; + /* + * Turn off fast interrupt prediction. + */ + if((r = pcicfgr8(p, 0x51)) & 0x80) + pcicfgw8(p, 0x51, r & ~0x80); + if((r = pcicfgr8(p, 0x55)) & 0x80) + pcicfgw8(p, 0x55, r & ~0x80); + break; + case (0x0640<<16)|0x1095: /* CMD 640B */ + /* + * Bugfix code here... + */ + break; + case (0x7441<<16)|0x1022: /* AMD 768 */ + /* + * Set: + * 0x41 prefetch, postwrite; + * 0x43 FIFO configuration 1/2 and 1/2; + * 0x44 status register read retry; + * 0x46 DMA read and end of sector flush. + */ + r = pcicfgr8(p, 0x41); + pcicfgw8(p, 0x41, r|0xF0); + r = pcicfgr8(p, 0x43); + pcicfgw8(p, 0x43, (r & 0x90)|0x2A); + r = pcicfgr8(p, 0x44); + pcicfgw8(p, 0x44, r|0x08); + r = pcicfgr8(p, 0x46); + pcicfgw8(p, 0x46, (r & 0x0C)|0xF0); + case (0x7469<<16)|0x1022: /* AMD 3111 */ + /* + * This can probably be lumped in with the 768 above. + */ + /*FALLTHROUGH*/ + case (0x01BC<<16)|0x10DE: /* nVidia nForce1 */ + case (0x0065<<16)|0x10DE: /* nVidia nForce2 */ + case (0x0085<<16)|0x10DE: /* nVidia nForce2 MCP */ + case (0x00D5<<16)|0x10DE: /* nVidia nForce3 */ + case (0x00E5<<16)|0x10DE: /* nVidia nForce3 Pro */ + case (0x0035<<16)|0x10DE: /* nVidia nForce3 MCP */ + case (0x0053<<16)|0x10DE: /* nVidia nForce4 */ + /* + * Ditto, although it may have a different base + * address for the registers (0x50?). + */ + /*FALLTHROUGH*/ + case (0x4376<<16)|0x1002: /* ATI Radeon Xpress 200M */ + break; + case (0x0211<<16)|0x1166: /* ServerWorks IB6566 */ + { + Pcidev *sb; + + sb = pcimatch(nil, 0x1166, 0x0200); + if(sb == nil) + break; + r = pcicfgr32(sb, 0x64); + r &= ~0x2000; + pcicfgw32(sb, 0x64, r); + } + span = 32*1024; + break; + case (0x5513<<16)|0x1039: /* SiS 962 */ + case (0x0646<<16)|0x1095: /* CMD 646 */ + case (0x0571<<16)|0x1106: /* VIA 82C686 */ + case (0x1230<<16)|0x8086: /* 82371FB (PIIX) */ + case (0x7010<<16)|0x8086: /* 82371SB (PIIX3) */ + case (0x7111<<16)|0x8086: /* 82371[AE]B (PIIX4[E]) */ + case (0x2411<<16)|0x8086: /* 82801AA (ICH) */ + case (0x2421<<16)|0x8086: /* 82801AB (ICH0) */ + case (0x244A<<16)|0x8086: /* 82801BA (ICH2, Mobile) */ + case (0x244B<<16)|0x8086: /* 82801BA (ICH2, High-End) */ + case (0x248A<<16)|0x8086: /* 82801CA (ICH3, Mobile) */ + case (0x248B<<16)|0x8086: /* 82801CA (ICH3, High-End) */ + case (0x24CA<<16)|0x8086: /* 82801DBM (ICH4, Mobile) */ + case (0x24CB<<16)|0x8086: /* 82801DB (ICH4, High-End) */ + case (0x24DB<<16)|0x8086: /* 82801EB (ICH5) */ + case (0x266F<<16)|0x8086: /* 82801FB (ICH6) */ + break; + } + + for(channel = 0; channel < 2; channel++){ + if(pi & (1<<(2*channel))){ + sdev = ataprobe(p->mem[0+2*channel].bar & ~0x01, + p->mem[1+2*channel].bar & ~0x01, + p->intl); + if(sdev == nil) + continue; + + ctlr = sdev->ctlr; + if(ispc87415) { + ctlr->ienable = pc87415ienable; + print("pc87415disable: not yet implemented\n"); + } + + if(head != nil) + tail->next = sdev; + else + head = sdev; + tail = sdev; + ctlr->tbdf = p->tbdf; + } + else if((sdev = legacy[channel]) == nil) + continue; + else + ctlr = sdev->ctlr; + + ctlr->pcidev = p; + ctlr->maxio = maxio; + ctlr->span = span; + if(!(pi & 0x80)) + continue; + ctlr->bmiba = (p->mem[4].bar & ~0x01) + channel*8; + } + } + return head; +} + +static SDev* +atalegacy(int port, int irq) +{ + return ataprobe(port, port+Ctl2cmd, irq); +} + +static SDev* +ataid(SDev* sdev) +{ + int i; + Ctlr *ctlr; + char name[32]; + + /* + * Legacy controllers are always 'C' and 'D' and if + * they exist and have drives will be first in the list. + * If there are no active legacy controllers, native + * controllers start at 'C'. + */ + if(sdev == nil) + return nil; + ctlr = sdev->ctlr; + if(ctlr->cmdport == Ctlr0cmd || ctlr->cmdport == Ctlr1cmd) + i = 2; + else + i = 0; + while(sdev){ + if(sdev->ifc == &sdataifc){ + ctlr = sdev->ctlr; + if(ctlr->cmdport == Ctlr0cmd) + sdev->idno = 'C'; + else if(ctlr->cmdport == Ctlr1cmd) + sdev->idno = 'D'; + else{ + sdev->idno = 'C'+i; + i++; + } + snprint(name, sizeof(name), "sd%c", sdev->idno); + kstrdup(&sdev->name, name); + } + sdev = sdev->next; + } + + return nil; +} + +static int +ataenable(SDev* sdev) +{ + Ctlr *ctlr; + char name[32]; + + ctlr = sdev->ctlr; + + if(ctlr->bmiba){ +#define ALIGN (4 * 1024) + if(ctlr->pcidev != nil) + pcisetbme(ctlr->pcidev); + // ctlr->prdt = xspanalloc(Nprd*sizeof(Prd), 4, 4*1024); + ctlr->prdtbase = xalloc(Nprd * sizeof(Prd) + ALIGN); + ctlr->prdt = (Prd *)(((ulong)ctlr->prdtbase + ALIGN) & ~(ALIGN - 1)); + } + snprint(name, sizeof(name), "%s (%s)", sdev->name, sdev->ifc->name); + intrenable(ctlr->irq, atainterrupt, ctlr, ctlr->tbdf, name); + outb(ctlr->ctlport+Dc, 0); + if(ctlr->ienable) + ctlr->ienable(ctlr); + + return 1; +} + +static int +atadisable(SDev *sdev) +{ + Ctlr *ctlr; + char name[32]; + + ctlr = sdev->ctlr; + outb(ctlr->ctlport+Dc, Nien); /* disable interrupts */ + if (ctlr->idisable) + ctlr->idisable(ctlr); + snprint(name, sizeof(name), "%s (%s)", sdev->name, sdev->ifc->name); + intrdisable(ctlr->irq, atainterrupt, ctlr, ctlr->tbdf, name); + if (ctlr->bmiba) { + if (ctlr->pcidev) + pciclrbme(ctlr->pcidev); + xfree(ctlr->prdtbase); + } + return 0; +} + +#ifndef FS +static int +atarctl(SDunit* unit, char* p, int l) +{ + int n; + Ctlr *ctlr; + Drive *drive; + + if((ctlr = unit->dev->ctlr) == nil || ctlr->drive[unit->subno] == nil) + return 0; + drive = ctlr->drive[unit->subno]; + + qlock(drive); + n = snprint(p, l, "config %4.4uX capabilities %4.4uX", + drive->info[Iconfig], drive->info[Icapabilities]); + if(drive->dma) + n += snprint(p+n, l-n, " dma %8.8uX dmactl %8.8uX", + drive->dma, drive->dmactl); + if(drive->rwm) + n += snprint(p+n, l-n, " rwm %ud rwmctl %ud", + drive->rwm, drive->rwmctl); + if(drive->flags&Lba48) + n += snprint(p+n, l-n, " lba48always %s", + (drive->flags&Lba48always) ? "on" : "off"); + n += snprint(p+n, l-n, "\n"); + if(drive->sectors){ + n += snprint(p+n, l-n, "geometry %lld %d", + (Wideoff)drive->sectors, drive->secsize); + if(drive->pkt == 0) + n += snprint(p+n, l-n, " %d %d %d", + drive->c, drive->h, drive->s); + n += snprint(p+n, l-n, "\n"); + } + qunlock(drive); + + return n; +} + +static int +atawctl(SDunit* unit, Cmdbuf* cb) +{ + int period; + Ctlr *ctlr; + Drive *drive; + + if((ctlr = unit->dev->ctlr) == nil || ctlr->drive[unit->subno] == nil) + return 0; + drive = ctlr->drive[unit->subno]; + + qlock(drive); + if(waserror()){ + qunlock(drive); + nexterror(); + } + + /* + * Dma and rwm control is passive at the moment, + * i.e. it is assumed that the hardware is set up + * correctly already either by the BIOS or when + * the drive was initially identified. + */ + if(strcmp(cb->f[0], "dma") == 0){ + if(cb->nf != 2 || drive->dma == 0) + error(Ebadctl); + if(strcmp(cb->f[1], "on") == 0) + drive->dmactl = drive->dma; + else if(strcmp(cb->f[1], "off") == 0) + drive->dmactl = 0; + else + error(Ebadctl); + } + else if(strcmp(cb->f[0], "rwm") == 0){ + if(cb->nf != 2 || drive->rwm == 0) + error(Ebadctl); + if(strcmp(cb->f[1], "on") == 0) + drive->rwmctl = drive->rwm; + else if(strcmp(cb->f[1], "off") == 0) + drive->rwmctl = 0; + else + error(Ebadctl); + } + else if(strcmp(cb->f[0], "standby") == 0){ + switch(cb->nf){ + default: + error(Ebadctl); + case 2: + period = strtol(cb->f[1], 0, 0); + if(period && (period < 30 || period > 240*5)) + error(Ebadctl); + period /= 5; + break; + } + if(atastandby(drive, period) != SDok) + error(Ebadctl); + } + else if(strcmp(cb->f[0], "lba48always") == 0){ + if(cb->nf != 2 || !(drive->flags&Lba48)) + error(Ebadctl); + if(strcmp(cb->f[1], "on") == 0) + drive->flags |= Lba48always; + else if(strcmp(cb->f[1], "off") == 0) + drive->flags &= ~Lba48always; + else + error(Ebadctl); + } + else + error(Ebadctl); + qunlock(drive); + poperror(); + + return 0; +} +#endif + +SDifc sdataifc = { + "ata", /* name */ + + atapnp, /* pnp */ + atalegacy, /* legacy */ + ataid, /* id */ + ataenable, /* enable */ + atadisable, /* disable */ + + scsiverify, /* verify */ + scsionline, /* online */ + atario, /* rio */ + nil, //atarctl, /* rctl */ + nil, //atawctl, /* wctl */ + + scsibio, /* bio */ +#ifndef FS + nil, //ataprobew, /* probe */ + ataclear, /* clear */ + atastat, /* stat */ +#endif +}; + +/* + * file-server-specific routines + * + * ata* routines below this point are used to access nvram file, + * ide* routines implement the `h' device and call the ata* routines. + */ + +static Drive* +atapart(Drive *dp) +{ + return dp; +} + +static Drive* +atadriveprobe(int driveno) +{ + Drive *drive; + + drive = atadrive[driveno]; + if (drive == nil) + return nil; + drive->driveno = driveno; + if(drive->online == 0){ + if(drive->lba) + print("h%d: LBA %llud sectors\n", + drive->driveno, (Wideoff)drive->sectors); + else + print("h%d: CHS %d/%d/%d %llud sectors\n", + drive->driveno, drive->c, drive->h, drive->s, + (Wideoff)drive->sectors); + drive->online = 1; + } + return atapart(drive); +} + +static void +cmd_stat(int, char*[]) +{ + Ctlr *ctlr; + int ctlrno, targetno; + Target *tp; + + for(ctlrno = 0; ctlrno < nelem(atactlr); ctlrno++){ + ctlr = atactlr[ctlrno]; + if(ctlr == nil || ctlr->sdev == nil) + continue; + for(targetno = 0; targetno < NTarget; targetno++){ + tp = &ctlr->target[targetno]; + if(tp->fflag == 0) + continue; + print("\t%d.%d work =%7W%7W%7W xfrs\n", + ctlrno, targetno, + tp->work+0, tp->work+1, tp->work+2); + print("\t rate =%7W%7W%7W tBps\n", + tp->rate+0, tp->rate+1, tp->rate+2); + } + } +} + +/* find all the controllers, enable interrupts, set up SDevs & SDunits */ +int +atainit(void) +{ + unsigned i; + SDev *sdp; + SDev **sdpp; + static int first = 1; + + if (first) + first = 0; + else + return 0xFF; + + atapnp(); + + for (sdpp = sdevs; sdpp < sdevs + nelem(sdevs); sdpp++) { + sdp = *sdpp; + if (sdp == nil) + continue; + i = sdpp - sdevs; + sdp->ifc = &sdataifc; + sdp->nunit = NCtlrdrv; + sdp->index = i; + sdp->idno = 'C' + i; + sdp->ctlr = atactlr[i]; + if (sdp->ctlr != nil) + ataenable(sdp); + } + cmd_install("stati", "-- ide/ata stats", cmd_stat); + return 0xFF; +} + +Devsize +ataseek(int driveno, Devsize offset) +{ + Drive *drive = atadrive[driveno]; + + if (drive == nil || !drive->online) + return -1; + drive->offset = offset; + return offset; +} + +/* zero indicates failure; only otherinit() cares */ +int +setatapart(int driveno, char *) +{ + /* atadriveprobe() sets drive->online */ + if(atadriveprobe(driveno) == nil) + return 0; + return 1; +} + +static void +keepstats(Drive *dp, int dbytes) +{ + Target *tp = &dp->ctlr->target[dp->driveno%NCtlrdrv]; + + qlock(tp); +// if(tp->ok == 0) +// scsiprobe(d); + if(tp->fflag == 0) { + dofilter(tp->work+0, C0a, C0b, 1); /* was , 1000); */ + dofilter(tp->work+1, C1a, C1b, 1); /* was , 1000); */ + dofilter(tp->work+2, C2a, C2b, 1); /* was , 1000); */ + dofilter(tp->rate+0, C0a, C0b, 1); + dofilter(tp->rate+1, C1a, C1b, 1); + dofilter(tp->rate+2, C2a, C2b, 1); + tp->fflag = 1; + } + tp->work[0].count++; + tp->work[1].count++; + tp->work[2].count++; + tp->rate[0].count += dbytes; + tp->rate[1].count += dbytes; + tp->rate[2].count += dbytes; + qunlock(tp); +} + +static long +ataxfer(Drive *dp, int inout, Devsize start, long bytes) +{ + unsigned driveno = dp->driveno; + ulong secsize = dp->secsize, sects; + SDunit *unit = sdgetunit(sdevs[driveno/NCtlrdrv], driveno%NCtlrdrv); + + if (unit == nil) { + print("mvsataxfer: nil unit\n"); + return -1; + } + if (dp->driveno == -1) + panic("ataxfer: dp->driveno unset"); + /* + * unit->dev will be nil if the controller is missing (e.g., h0 on a + * machine with only sdD, not sdC), so make this a non-fatal error. + */ + if (unit->dev == nil) { + print("ataxfer: missing controller for h%d\n", driveno); + return -1; + } + if (unit->dev != sdevs[driveno/NCtlrdrv]) + panic("ataxfer: sdunits[%d].dev=%#p is wrong controller (want %#p)", + driveno, unit->dev, sdevs[driveno/NCtlrdrv]); + if (unit->subno != driveno%NCtlrdrv) + panic("ataxfer: sdunits[%d].subno is %d, not %d", + driveno, unit->subno, driveno%NCtlrdrv); + keepstats(dp, bytes); + if (unit->sectors == 0) { + unit->sectors = dp->sectors; + unit->secsize = secsize; + } + sects = (bytes + secsize - 1) / secsize; /* round up */ + if (start%secsize != 0) + print("ataxfer: start offset not on sector boundary\n"); + return scsibio(unit, 0, inout, dp->buf, sects, start/secsize); +} + +/* + * ataread & atawrite do the real work; ideread & idewrite just call them. + * ataread & atawrite are called by the nvram routines. + * ideread & idewrite are called for normal file server I/O. + */ + +Off +ataread(int driveno, void *a, long n) +{ + int skip; + Off rv, i; + uchar *aa = a; +// Ctlr *cp; + Drive *dp; + + dp = atadrive[driveno]; + if(dp == nil || !dp->online) + return 0; + +// cp = dp->ctlr; + if (dp->secsize == 0) + panic("ataread: sector size of zero"); + skip = dp->offset % dp->secsize; + for(rv = 0; rv < n; rv += i){ + i = ataxfer(dp, Read, dp->offset+rv-skip, n-rv+skip); + if(i == 0) + break; + if(i < 0) + return -1; + i -= skip; + if(i > n - rv) + i = n - rv; + memmove(aa+rv, dp->buf + skip, i); + skip = 0; + } + dp->offset += rv; + return rv; +} + +Off +atawrite(int driveno, void *a, long n) +{ + Off rv, i, partial; + uchar *aa = a; +// Ctlr *cp; + Drive *dp; + + dp = atadrive[driveno]; + if(dp == nil || !dp->online) + return 0; + +// cp = dp->ctlr; + + /* + * if not starting on a sector boundary, + * read in the first sector before writing it out. + */ + if (dp->secsize == 0) + panic("atawrite: sector size of zero"); + partial = dp->offset % dp->secsize; + if(partial){ + if (ataxfer(dp, Read, dp->offset-partial, dp->secsize) < 0) + return -1; + if(partial+n > dp->secsize) + rv = dp->secsize - partial; + else + rv = n; + memmove(dp->buf+partial, aa, rv); + if(ataxfer(dp, Write, dp->offset-partial, dp->secsize) < 0) + return -1; + } else + rv = 0; + + /* + * write out the full sectors (common case) + */ + partial = (n - rv) % dp->secsize; + n -= partial; + for(; rv < n; rv += i){ + i = n - rv; + if(i > Maxxfer) + i = Maxxfer; + memmove(dp->buf, aa+rv, i); + i = ataxfer(dp, Write, dp->offset+rv, i); + if(i == 0) + break; + if(i < 0) + return -1; + } + + /* + * if not ending on a sector boundary, + * read in the last sector before writing it out. + */ + if(partial){ + if(ataxfer(dp, Read, dp->offset+rv, dp->secsize) < 0) + return -1; + memmove(dp->buf, aa+rv, partial); + if(ataxfer(dp, Write, dp->offset+rv, dp->secsize) < 0) + return -1; + rv += partial; + } + dp->offset += rv; + return rv; +} + +/* + * normal I/O interface + */ + +/* result is size of d in blocks of RBUFSIZE bytes */ +Devsize +idesize(Device *d) +{ + Drive *dp = d->private; + + if (dp == nil) + return 0; + /* + * dividing first is sloppy but reduces the range of intermediate + * values, avoiding possible overflow. + */ + return (dp->sectors / RBUFSIZE) * dp->secsize; +} + +void +ideinit(Device *d) +{ + int driveno; + Drive *dp; + + atainit(); + if (d->private) + return; + /* call setatapart() first in case we didn't boot off this drive */ + driveno = d->wren.ctrl*NCtlrdrv + d->wren.targ; + setatapart(driveno, "disk"); + dp = atadriveprobe(driveno); + if (dp) { + print("ideinit(ctrl %d targ %d) driveno %d\n", + d->wren.ctrl, d->wren.targ, dp->driveno); + if (dp->driveno != driveno) + panic("ideinit: dp->dev != driveno"); + d->private = dp; + /* print the sizes now, not later */ + print( + " idesize(driveno %d): %llud %d-byte sectors -> %llud blocks\n", + dp->driveno, (Wideoff)dp->sectors, dp->secsize, + (Wideoff)idesize(d)); + } +} + +int +ideread(Device *d, Devsize b, void *c) +{ + int x, driveno; + Drive *dp; + Ctlr *cp; + + if (d == nil || d->private == nil) + return 1; + dp = d->private; + cp = dp->ctlr; + if (cp == nil) + panic("ideread: no controller for drive"); + + qlock(&cp->idelock); + cp->idelock.name = "ideio"; + driveno = dp->driveno; + if (driveno == -1) + panic("ideread: dp->driveno unset"); + IDPRINT("ideread(dev %p, %lld, %p, %d): %p\n", d, (Wideoff)b, c, + driveno, dp); + ataseek(driveno, b * RBUFSIZE); + x = ataread(driveno, c, RBUFSIZE) != RBUFSIZE; + qunlock(&cp->idelock); + return x; +} + +int +idewrite(Device *d, Devsize b, void *c) +{ + int x, driveno; + Drive *dp; + Ctlr *cp; + + if (d == nil || d->private == nil) + return 1; + dp = d->private; + cp = dp->ctlr; + if (cp == nil) + panic("idewrite: no controller for drive"); + + qlock(&cp->idelock); + cp->idelock.name = "ideio"; + driveno = dp->driveno; + if (driveno == -1) + panic("idewrite: dp->driveno unset"); + IDPRINT("idewrite(%p, %lld, %p): driveno %d\n", d, (Wideoff)b, c, + driveno); + ataseek(driveno, b * RBUFSIZE); + x = atawrite(driveno, c, RBUFSIZE) != RBUFSIZE; + qunlock(&cp->idelock); + return x; +} diff -Nru /sys/src/fs/pc/sdmv50xx.c /sys/src/fs/pc/sdmv50xx.c --- /sys/src/fs/pc/sdmv50xx.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/pc/sdmv50xx.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,2095 @@ +/* + * Marvell 88SX[56]0[48][01] fileserver Serial ATA (SATA) driver + * + * See MV-S101357-00 Rev B Marvell PCI/PCI-X to 8-Port/4-Port + * SATA Host Controller, ATA-5 ANSI NCITS 340-2000. + * + * This is a heavily-modified version (by Coraid) of a heavily-modified + * version (from The Labs) of a driver written by Coraid, Inc. + * The original copyright notice appears at the end of this file. + */ + +#include "all.h" +#include "io.h" +#include "mem.h" + +#include "sd.h" +#include "compat.h" + +enum { + /* old stuff carried forward */ + NCtlr = 8, + NCtlrdrv = 8, + NDrive = NCtlr*NCtlrdrv, + Maxxfer = 16*1024, /* maximum transfer size/cmd */ + + Read = 0, + Write, + + Drvmagic = 0xcafebabeUL, + Ctlrmagic = 0xfeedfaceUL, +}; + +#define dprint(...) /* print(__VA_ARGS__) */ +#define idprint(...) +#define ioprint(...) + +enum { + SrbRing = 32, + + /* Addresses of ATA register */ + ARcmd = 027, + ARdev = 026, + ARerr = 021, + ARfea = 021, + ARlba2 = 025, + ARlba1 = 024, + ARlba0 = 023, + ARseccnt = 022, + ARstat = 027, + + ATAerr = (1<<0), + ATAdrq = (1<<3), + ATAdf = (1<<5), + ATAdrdy = (1<<6), + ATAbusy = (1<<7), + ATAabort = (1<<2), + ATAobs = (1<<1 | 1<<2 | 1<<4), + ATAeIEN = (1<<1), + ATAsrst = (1<<2), + ATAhob = (1<<7), + ATAbad = (ATAbusy|ATAdf|ATAdrq|ATAerr), + + SFdone = (1<<0), + SFerror = (1<<1), + + SRBident = 0, + SRBread, + SRBwrite, + SRBsmart, + + SRBnodata = 0, + SRBdatain, + SRBdataout, + + RQread = 1, /* data coming IN from device */ + + PRDeot = (1<<15), + + /* EDMA interrupt error cause register */ + + ePrtDataErr = (1<<0), + ePrtPRDErr = (1<<1), + eDevErr = (1<<2), + eDevDis = (1<<3), + eDevCon = (1<<4), + eOverrun = (1<<5), + eUnderrun = (1<<6), + eSelfDis = (1<<8), + ePrtCRQBErr = (1<<9), + ePrtCRPBErr = (1<<10), + ePrtIntErr = (1<<11), + eIORdyErr = (1<<12), + + /* flags for sata 2 version */ + eSelfDis2 = (1<<7), + SerrInt = (1<<5), + + /* EDMA Command Register */ + + eEnEDMA = (1<<0), + eDsEDMA = (1<<1), + eAtaRst = (1<<2), + + /* Interrupt mask for errors we care about */ + IEM = (eDevDis | eDevCon | eSelfDis), + IEM2 = (eDevDis | eDevCon | eSelfDis2), + + /* drive states */ + Dnull = 0, + Dnew, + Dready, + Derror, + Dmissing, + Dlast, + + /* drive flags */ + Dext = (1<<0), /* use ext commands */ + Dpio = (1<<1), /* doing pio */ + Dwanted = (1<<2), /* someone wants an srb entry */ + Dedma = (1<<3), /* device in edma mode */ + Dpiowant = (1<<4), /* some wants to use the pio mode */ + + /* phyerrata magic crap */ + Mpreamp = 0x7e0, + Dpreamp = 0x720, + + REV60X1B2 = 0x7, + REV60X1C0 = 0x9, + +}; + +static char* diskstates[Dlast] = { + "null", + "new", + "ready", + "error", + "missing", +}; + +extern SDifc sdmv50xxifc; + +typedef struct Arb Arb; +typedef struct Bridge Bridge; +typedef struct Chip Chip; +typedef struct Ctlr Ctlr; +typedef struct Drive Drive; +typedef struct Edma Edma; +typedef struct Prd Prd; +typedef struct Rx Rx; +typedef struct Srb Srb; +typedef struct Tx Tx; + +struct Chip /* pointers to per-Chip mmio */ +{ + Arb *arb; + Edma *edma; /* array of 4 */ +}; + +enum{ + DMautoneg, + DMsatai, + DMsataii, +}; + +struct Drive /* a single disk */ +{ + Lock; + + Ctlr *ctlr; + SDunit *unit; + char name[10]; + ulong magic; + + Bridge *bridge; + Edma *edma; + Chip *chip; + int chipx; + + int state; + int flag; + uvlong sectors; + ulong pm2; /* phymode 2 init state */ + ulong intick; /* check for hung westerdigital drives. */ + int wait; + int mode; /* DMautoneg, satai or sataii. */ + + char serial[20+1]; + char firmware[8+1]; + char model[40+1]; + + ushort info[256]; + + Srb *srb[SrbRing-1]; + int nsrb; + Prd *prd; + Tx *tx; + Rx *rx; + + Srb *srbhead; + Srb *srbtail; + + /* added for file server */ + + /* for ata* routines */ + Devsize offset; + int driveno; /* ctlr*NCtlrdrv + unit */ + /* + * old stuff carried forward. it's in Drive not Ctlr to maximise + * possible concurrency. + */ + uchar buf[RBUFSIZE]; +}; + +struct Ctlr /* a single PCI card */ +{ + Lock; + + int irq; + int tbdf; + int rid; + ulong magic; + int enabled; + int type; + SDev *sdev; + Pcidev *pcidev; + + uchar *mmio; + ulong *lmmio; + Chip chip[2]; + int nchip; + Drive drive[NCtlrdrv]; + int ndrive; + Target target[NTarget]; /* contains filters for stats */ + + /* old stuff carried forward */ + QLock idelock; /* make seek & i/o atomic in ide* routines */ +}; + +struct Srb /* request buffer */ +{ + Lock; + Rendez; + Srb *next; + + Drive *drive; + uvlong blockno; + int count; + int req; + int flag; + uchar *data; + + uchar cmd; + uchar lba[6]; + uchar sectors; + int sta; + int err; +}; + +/* + * Memory-mapped I/O registers in many forms. + */ +struct Bridge +{ + ulong status; + ulong serror; + ulong sctrl; + ulong phyctrl; + ulong phymode3; + ulong phymode4; + uchar fill0[0x14]; + ulong phymode1; + ulong phymode2; + char fill1[8]; + ulong ctrl; + char fill2[0x34]; + ulong phymode; + char fill3[0x88]; +}; /* most be 0x100 hex in length */ + +struct Arb /* memory-mapped per-Chip registers */ +{ + ulong config; /* satahc configuration register (sata2 only) */ + ulong rqop; /* request queue out-pointer */ + ulong rqip; /* response queue in pointer */ + ulong ict; /* inerrupt caolescing threshold */ + ulong itt; /* interrupt timer threshold */ + ulong ic; /* interrupt cause */ + ulong btc; /* bridges test control */ + ulong bts; /* bridges test status */ + ulong bpc; /* bridges pin configuration */ + char fill1[0xdc]; + Bridge bridge[4]; +}; + +struct Edma /* memory-mapped per-Drive DMA-related registers */ +{ + ulong config; /* configuration register */ + ulong timer; + ulong iec; /* interrupt error cause */ + ulong iem; /* interrupt error mask */ + + ulong txbasehi; /* request queue base address high */ + ulong txi; /* request queue in pointer */ + ulong txo; /* request queue out pointer */ + + ulong rxbasehi; /* response queue base address high */ + ulong rxi; /* response queue in pointer */ + ulong rxo; /* response queue out pointer */ + + ulong ctl; /* command register */ + ulong testctl; /* test control */ + ulong status; + ulong iordyto; /* IORDY timeout */ + char fill[0x18]; + ulong sataconfig; /* sata 2 */ + char fill[0xac]; + ushort pio; /* data register */ + char pad0[2]; + uchar err; /* features and error */ + char pad1[3]; + uchar seccnt; /* sector count */ + char pad2[3]; + uchar lba0; + char pad3[3]; + uchar lba1; + char pad4[3]; + uchar lba2; + char pad5[3]; + uchar lba3; + char pad6[3]; + uchar cmdstat; /* cmd/status */ + char pad7[3]; + uchar altstat; /* alternate status */ + uchar fill2[0x1df]; + Bridge port; + char fill3[0x1c00]; /* pad to 0x2000 bytes */ +}; + +/* + * Memory structures shared with card. + */ +struct Prd /* physical region descriptor */ +{ + ulong pa; /* byte address of physical memory */ + ushort count; /* byte count (bit0 must be 0) */ + ushort flag; + ulong zero; /* high long of 64 bit address */ + ulong reserved; +}; + +struct Tx /* command request block */ +{ + ulong prdpa; /* physical region descriptor table structures */ + ulong zero; /* must be zero (high long of prd address) */ + ushort flag; /* control flags */ + ushort regs[11]; +}; + +struct Rx /* command response block */ +{ + ushort cid; /* cID of response */ + uchar cEdmaSts; /* EDMA status */ + uchar cDevSts; /* status from disk */ + ulong ts; /* time stamp */ +}; + +/* file-server-specific data */ + +static Ctlr *mvsatactlr[NCtlr]; +static SDev * sdevs[NCtlr]; + +static Drive *mvsatadrive[NDrive]; +static int nmvsatadrive; +static SDunit *sdunits[NDrive]; + +static Drive *mvsatadriveprobe(int driveno); +static void statsinit(void); + + +/* + * Little-endian parsing for drive data. + */ +static ushort +lhgets(void *p) +{ + uchar *a = p; + return ((ushort) a[1] << 8) | a[0]; +} + +static ulong +lhgetl(void *p) +{ + uchar *a = p; + return ((ulong) lhgets(a+2) << 16) | lhgets(a); +} + +static uvlong +lhgetv(void *p) +{ + uchar *a = p; + return ((uvlong) lhgetl(a+4) << 32) | lhgetl(a); +} + +static void +idmove(char *p, ushort *a, int n) +{ + char *op; + int i; + + op = p; + for(i=0; i>8; + *p++ = a[i]; + } + while(p>op && *--p == ' ') + *p = 0; +} + +/* + * Request buffers. + */ +static struct +{ + Lock; + Srb *freechain; + int nalloc; +} srblist; + +static Srb* +allocsrb(void) +{ + Srb *p; + + ilock(&srblist); + if((p = srblist.freechain) == nil){ + srblist.nalloc++; + iunlock(&srblist); + p = smalloc(sizeof *p); + }else{ + srblist.freechain = p->next; + iunlock(&srblist); + } + return p; +} + +static void +freesrb(Srb *p) +{ + ilock(&srblist); + p->next = srblist.freechain; + srblist.freechain = p; + iunlock(&srblist); +} + +static int +ret0(void *) +{ + return 0; +} + +/* + * Wait for a byte to be a particular value. + */ +static int +satawait(volatile uchar *p, uchar mask, uchar v, int ms) +{ + int i; + + for(i=0; ictlr->type == 1) + return; + microdelay(200); + n = d->bridge->phymode2; + while ((n & BadAutoCal) == BadAutoCal) { + dprint("%s: badautocal\n", d->unit->name); + n &= ~(1<<16); + n |= (1<<31); + d->bridge->phymode2 = n; + microdelay(200); + d->bridge->phymode2 &= ~((1<<16) | (1<<31)); + microdelay(200); + n = d->bridge->phymode2; + } + n &= ~(1<<31); + d->bridge->phymode2 = n; + microdelay(200); + + /* abra cadabra! (random magic) */ + m = d->bridge->phymode3; + m &= ~0x7f800000; + m |= 0x2a800000; + d->bridge->phymode3 = m; + + /* fix phy mode 4 */ + m = d->bridge->phymode3; + n = d->bridge->phymode4; + n &= ~(1<<1); + n |= 1; + switch(d->ctlr->rid){ + case REV60X1B2: + default: + d->bridge->phymode4 = n; + d->bridge->phymode3 = m; + break; + case REV60X1C0: + d->bridge->phymode4 = n; + break; + } + + /* revert values of pre-emphasis and signal amps to the saved ones */ + n = d->bridge->phymode2; + n &= ~Mpreamp; + n |= d->pm2; + n &= ~(1<<16); + d->bridge->phymode2 = n; +} + +static void +edmacleanout(Drive *d) +{ + int i; + Srb *srb; + + for(i=0; isrb); i++){ + if(srb = d->srb[i]){ + d->srb[i] = nil; + d->nsrb--; + srb->flag |= SFerror|SFdone; + wakeup(srb); + } + } + while(srb = d->srbhead){ + d->srbhead = srb->next; + srb->flag |= SFerror|SFdone; + wakeup(srb); + } +} + +static void +resetdisk(Drive *d) +{ + ulong n; + + d->sectors = 0; + d->unit->sectors = 0; + if (d->ctlr->type == 2) { + /* without bit 8 we can boot without disks, but */ + /* inserted disks will never appear. :-X */ + n = d->edma->sataconfig; + n &= 0xff; + n |= 0x9b1100; + d->edma->sataconfig = n; + n = d->edma->sataconfig; //flush + USED(n); + } + d->edma->ctl = eDsEDMA; + microdelay(1); + d->edma->ctl = eAtaRst; + microdelay(25); + d->edma->ctl = 0; + if (satawait((uchar *)&d->edma->ctl, eEnEDMA, 0, 3*1000) == 0) + print("%s: eEnEDMA never cleared on reset\n", d->unit->name); + edmacleanout(d); + phyerrata(d); + d->bridge->sctrl = 0x301 | (d->mode << 4); + d->state = Dmissing; +} + +static void +edmainit(Drive *d) +{ + int i; + + if(d->tx != nil) + return; + + d->tx = xspanalloc(32*sizeof(Tx), 1024, 0); + d->rx = xspanalloc(32*sizeof(Rx), 256, 0); + d->prd = xspanalloc(32*sizeof(Prd), 32, 0); + for(i = 0; i < 32; i++) + d->tx[i].prdpa = PADDR(&d->prd[i]); + coherence(); +} + +static int +configdrive(Ctlr *ctlr, Drive *d, SDunit *unit) +{ + dprint("%s: configdrive\n", unit->name); + if (d->driveno < 0) + panic("mv50xx: configdrive: unset driveno\n"); + d->unit = unit; + d->ctlr = ctlr; + d->chipx = unit->subno%4; + d->chip = &ctlr->chip[unit->subno/4]; + d->edma = &d->chip->edma[d->chipx]; + sdunits[d->driveno] = unit; + + edmainit(d); + d->mode = DMsatai; + if(d->ctlr->type == 1){ + d->edma->iem = IEM; + d->bridge = &d->chip->arb->bridge[d->chipx]; + }else{ + d->edma->iem = IEM2; + d->bridge = &d->chip->edma[d->chipx].port; + d->edma->iem = ~(1<<6); + d->pm2 = Dpreamp; + if(d->ctlr->lmmio[0x180d8/4] & 1) + d->pm2 = d->bridge->phymode2 & Mpreamp; + } + resetdisk(d); + unmask(ctlr->lmmio, d->driveno, 0); + delay(100); + if(d->bridge->status){ + dprint("%s: configdrive: found drive %lx\n", unit->name, d->bridge->status); + delay(1400); /* don't burn out the power supply. */ + } + return 0; +} + +static int +enabledrive(Drive *d) +{ + Edma *edma; + + dprint("%s: enabledrive..", d->unit->name); + + if((d->bridge->status & 0xf) != 3){ + dprint("%s: not present\n", d->unit->name); + d->state = Dmissing; + return -1; + } + edma = d->edma; + if(satawait(&edma->cmdstat, ATAbusy, 0, 5*1000) == 0){ + dprint("%s: busy timeout\n", d->unit->name); + d->state = Dmissing; + return -1; + } + edma->iec = 0; + d->chip->arb->ic &= ~(0x101 << d->chipx); + edma->config = 0x51f; + if (d->ctlr->type == 2) + edma->config |= 7<<11; + edma->txi = PADDR(d->tx); + edma->txo = (ulong)d->tx & 0x3e0; + edma->rxi = (ulong)d->rx & 0xf8; + edma->rxo = PADDR(d->rx); + edma->ctl |= 1; /* enable dma */ + + if(d->bridge->status = 0x113){ + dprint("%s: new\n", d->unit->name); + d->state = Dnew; + }else + print("%s: status not forced (should be okay)\n", d->unit->name); + return 0; +} + +static void +disabledrive(Drive *d) +{ + int i; + ulong *r; + + dprint("%s: disabledrive\n", d->unit->name); + + if(d->tx == nil) /* never enabled */ + return; + + d->edma->ctl = 0; + d->edma->iem = 0; + + r = (ulong*)(d->ctlr->mmio + 0x1d64); + i = d->chipx; + if(d->chipx < 4) + *r &= ~(3 << (i*2)); + else + *r |= ~(3 << (i*2+9)); +} + +static int +setudmamode(Drive *d, uchar mode) +{ + Edma *edma; + + dprint("%s: setudmamode %d\n", d->unit->name, mode); + + edma = d->edma; + if (edma == nil) { + print("setudamode(m%d): zero d->edma\m", d->driveno); + return 0; + } + if(satawait(&edma->cmdstat, ~ATAobs, ATAdrdy, 9*1000) == 0){ + print("%s: cmdstat 0x%.2ux ready timeout\n", d->unit->name, edma->cmdstat); + return 0; + } + edma->altstat = ATAeIEN; + edma->err = 3; + edma->seccnt = 0x40 | mode; + edma->cmdstat = 0xef; + microdelay(1); + if(satawait(&edma->cmdstat, ATAbusy, 0, 5*1000) == 0){ + print("%s: cmdstat 0x%.2ux busy timeout\n", d->unit->name, edma->cmdstat); + return 0; + } + return 1; +} + +static int +identifydrive(Drive *d) +{ + int i; + ushort *id; + Edma *edma; + SDunit *unit; + + dprint("%s: identifydrive\n", d->unit->name); + + if(setudmamode(d, 5) == 0) /* do all SATA support 5? */ + goto Error; + + id = d->info; + memset(d->info, 0, sizeof d->info); + edma = d->edma; + if(satawait(&edma->cmdstat, ~ATAobs, ATAdrdy, 5*1000) == 0) + goto Error; + + edma->altstat = ATAeIEN; /* no interrupts */ + edma->cmdstat = 0xec; + microdelay(1); + if(satawait(&edma->cmdstat, ATAbusy, 0, 5*1000) == 0) + goto Error; + for(i = 0; i < 256; i++) + id[i] = edma->pio; + if(edma->cmdstat & ATAbad) + goto Error; + i = lhgets(id+83) | lhgets(id+86); + if(i & (1<<10)){ + d->flag |= Dext; + d->sectors = lhgetv(id+100); + }else{ + d->flag &= ~Dext; + d->sectors = lhgetl(id+60); + } + idmove(d->serial, id+10, 20); + idmove(d->firmware, id+23, 8); + idmove(d->model, id+27, 40); + + unit = d->unit; + memset(unit->inquiry, 0, sizeof unit->inquiry); + unit->inquiry[2] = 2; + unit->inquiry[3] = 2; + unit->inquiry[4] = sizeof(unit->inquiry)-4; + idmove((char*)unit->inquiry+8, id+27, 40); + + if(enabledrive(d) == 0) { + d->state = Dready; + print("%s: LLBA %lld sectors\n", d->unit->name, d->sectors); + unit->sectors = d->sectors; + unit->secsize = 512; + } else + d->state = Derror; + if(d->state == Dready) + return 0; + return -1; +Error: + dprint("error..."); + d->state = Derror; + return -1; +} + +/* p. 163: + M recovered error + P protocol error + N PhyRdy change + W CommWake + B 8-to-10 encoding error + D disparity error + C crc error + H handshake error + S link sequence error + T transport state transition error + F unrecognized fis type + X device changed +*/ + +static char stab[] = { +[1] 'M', +[10] 'P', +[16] 'N', +[18] 'W', 'B', 'D', 'C', 'H', 'S', 'T', 'F', 'X' +}; +static ulong sbad = (7<<20)|(3<<23); + +static void +serrdecode(ulong r, char *s, char *e) +{ + int i; + + e -=3; + for(i = 0; i < nelem(stab) && s < e; i++){ + if((r&(1<edma; + if((edma->ctl&eEnEDMA) == 0){ + /* FEr SATA#4 40xx */ + x = d->edma->cmdstat; + USED(x); + } + cause = edma->iec; + if(cause == 0) + return; + dprint("%s: cause %08ulx [%s]\n", d->unit->name, cause, iecdecode(cause)); + if(cause & eDevCon) + d->state = Dnew; + switch(d->ctlr->type){ + case 1: + if(cause&eSelfDis) + d->state = Derror; + break; + case 2: + if(cause&eDevDis && d->state == Dready) + print("%s: pulled: st=%08ulx\n", d->unit->name, cause); + if(cause&Cerror) + d->state = Derror; + if(cause&SerrInt){ + serrdecode(d->bridge->serror, buf, buf+sizeof buf); + dprint("%s: serror %08ulx [%s]\n", d->unit->name, (ulong)d->bridge->serror, buf); + d->bridge->serror = d->bridge->serror; + } + } + edma->iec = ~cause; +} + +/* + * Requests + */ +static Srb* +srbrw(int req, Drive *d, uchar *data, uint sectors, uvlong lba) +{ + int i; + Srb *srb; + static uchar cmd[2][2] = { 0xC8, 0x25, 0xCA, 0x35 }; + + srb = allocsrb(); + srb->req = req; + srb->drive = d; + srb->blockno = lba; + srb->sectors = sectors; + srb->count = sectors*512; + srb->flag = 0; + srb->data = data; + + for(i=0; i<6; i++) + srb->lba[i] = lba >> (8*i); + srb->cmd = cmd[srb->req!=SRBread][(d->flag&Dext)!=0]; + return srb; +} + +static uintptr +advance(uintptr pa, int shift) +{ + int n, mask; + + mask = 0x1F<sectors); + *cmd++ = CMD(ARfea, 0); + if(ext){ + *cmd++ = CMD(ARlba0, srb->lba[3]); + *cmd++ = CMD(ARlba0, srb->lba[0]); + *cmd++ = CMD(ARlba1, srb->lba[4]); + *cmd++ = CMD(ARlba1, srb->lba[1]); + *cmd++ = CMD(ARlba2, srb->lba[5]); + *cmd++ = CMD(ARlba2, srb->lba[2]); + *cmd++ = CMD(ARdev, 0xe0); + }else{ + *cmd++ = CMD(ARlba0, srb->lba[0]); + *cmd++ = CMD(ARlba1, srb->lba[1]); + *cmd++ = CMD(ARlba2, srb->lba[2]); + *cmd++ = CMD(ARdev, srb->lba[3] | 0xe0); + } + *cmd = CMD(ARcmd, srb->cmd) | (1<<15); +} + +static void +startsrb(Drive *d, Srb *srb) +{ + int i; + Edma *edma; + Prd *prd; + Tx *tx; + + if(d->nsrb >= nelem(d->srb)){ + srb->next = nil; + if(d->srbhead) + d->srbtail->next = srb; + else + d->srbhead = srb; + d->srbtail = srb; + return; + } + + d->nsrb++; + for(i=0; isrb); i++) + if(d->srb[i] == nil) + break; + if(i == nelem(d->srb)) + panic("sdmv50xx: no free srbs"); + d->intick = MACHP(0)->ticks; + d->srb[i] = srb; + edma = d->edma; + tx = (Tx*)KADDR(edma->txi); + tx->flag = (i<<1) | (srb->req == SRBread); + prd = KADDR(tx->prdpa); + prd->pa = PADDR(srb->data); + prd->count = srb->count; + prd->flag = PRDeot; + mvsatarequest(tx->regs, srb, d->flag&Dext); + coherence(); + edma->txi = advance(edma->txi, 5); + d->intick = MACHP(0)->ticks; +} + +enum{ + Rpidx = 0x1f<<3, +}; + +static void +completesrb(Drive *d) +{ + Edma *edma; + Rx *rx; + Srb *srb; + + edma = d->edma; + if(edma == 0) + print("%s: completesrb: zero d->edma\n", d->unit->name); + if(edma == 0 || (edma->ctl & eEnEDMA) == 0) + return; + + while((edma->rxo&Rpidx) != (edma->rxi&Rpidx)){ + rx = (Rx*)KADDR(edma->rxo); + if(srb = d->srb[rx->cid]){ + d->srb[rx->cid] = nil; + d->nsrb--; + if(rx->cDevSts & ATAbad) + srb->flag |= SFerror; + if (rx->cEdmaSts) + iprint("cEdmaSts: %02ux\n", rx->cEdmaSts); + srb->sta = rx->cDevSts; + srb->flag |= SFdone; + wakeup(srb); + }else + iprint("srb missing\n"); + edma->rxo = advance(edma->rxo, 3); + if(srb = d->srbhead){ + d->srbhead = srb->next; + startsrb(d, srb); + } + } +} + +static int +srbdone(void *v) +{ + Srb *srb; + + srb = v; + return srb->flag & SFdone; +} + +/* + * Interrupts + */ +static void +mv50interrupt(Ureg*, void *a) +{ + int i; + ulong cause; + Ctlr *ctlr; + Drive *drive; + + ctlr = a; + ilock(ctlr); + cause = *(ulong*)(ctlr->mmio+0x1d60); +// dprint("sd%c: mv50interrupt: 0x%lux\n", ctlr->sdev->idno, cause); + for(i=0; indrive; i++) + if(cause & (3<<(i*2+i/4))){ + drive = &ctlr->drive[i]; + if (drive->magic != Drvmagic) { + print("m%d: interrupt for unconfigured drive\n", i); + continue; + } + ilock(drive); + updatedrive(drive); + while(ctlr->chip[i/4].arb->ic & (0x0101 << (i%4))){ + ctlr->chip[i/4].arb->ic = ~(0x101 << (i%4)); + completesrb(drive); + } + iunlock(drive); + } + iunlock(ctlr); +} + +enum{ + Nms = 256, + Midwait = 16*1024/Nms-1, + Mphywait = 512/Nms-1, +}; + +static void +westerndigitalhung(Drive *d) +{ + Edma *e; + + e = d->edma; + if(d->srb + && TK2MS(MACHP(0)->ticks-d->intick) > 5*1000 + && TK2SEC(MACHP(0)->ticks-d->intick) < ~0-10UL /* wrap protection. */ + && (e->rxo&Rpidx) == (e->rxi&Rpidx)){ + dprint("westerndigital drive hung; resetting\n"); + d->state = Derror; + } +} + +static void +checkdrive(Drive *d, int i) +{ + static ulong s, olds[NCtlr*NCtlrdrv]; + char *name; + + ilock(d); + name = d->unit->name; + s = d->bridge->status; + if(s != olds[i]){ + dprint("%s: status: %08lx -> %08lx: %s\n", name, olds[i], s, diskstates[d->state]); + olds[i] = s; + } + /* westerndigitalhung(d); */ + switch(d->state){ + case Dnew: + case Dmissing: + switch(s){ + case 0x000: + break; + default: + dprint("%s: unknown state %8lx\n", name, s); + case 0x100: + if(++d->wait&Mphywait) + break; + reset: d->mode ^= 1; + dprint("%s: reset; new mode %d\n", name, d->mode); + resetdisk(d); + break; + case 0x123: + case 0x113: + s = d->edma->cmdstat; + if(s == 0x7f || (s&~ATAobs) != ATAdrdy){ + if((++d->wait&Midwait) == 0) + goto reset; + }else if(identifydrive(d) == -1) + goto reset; + } + break; + case Dready: + if(s != 0) + break; + print("%s: pulled: st=%08ulx\n", name, s); +// case Dreset: + case Derror: + dprint("%s reset: mode %d\n", name, d->mode); + resetdisk(d); + break; + } + iunlock(d); +} + +static void +satakproc(void) +{ + int i; + static Rendez r; + + + memset(&r, 0, sizeof r); + for(;;){ + tsleep(&r, ret0, 0, Nms); + for(i = 0; i < nmvsatadrive; i++) + checkdrive(mvsatadrive[i], i); + } +} + +/* + * Device discovery + */ +static SDev* +mv50pnp(void) +{ + int i, nunit; + uchar *base; + ulong io, n, *mem; + Ctlr *ctlr; + Pcidev *p; + SDev *head, *tail, *sdev; + Drive *drive; + static int ctlrno, done; + + dprint("mv50pnp\n"); + if (done) + return nil; + done = 1; + + p = nil; + head = nil; + tail = nil; + while((p = pcimatch(p, 0x11ab, 0)) != nil){ + switch(p->did){ + case 0x5040: + case 0x5041: + case 0x5080: + case 0x5081: + case 0x6041: + case 0x6081: + break; + default: + print("mv50pnp: unknown did %ux ignored\n", (ushort)p->did); + continue; + } + if (ctlrno >= NCtlr) { + print("mv50pnp: too many controllers\n"); + break; + } + nunit = (p->did&0xf0) >> 4; + print("Marvell 88SX%ux: %d SATA-%s ports with%s flash\n", + (ushort)p->did, nunit, + ((p->did&0xf000)==0x6000? "II": "I"), + (p->did&1? "": "out")); + if((sdev = malloc(sizeof(SDev))) == nil) + continue; + if((ctlr = malloc(sizeof(Ctlr))) == nil){ + free(sdev); + continue; + } + memset(sdev, 0, sizeof *sdev); + memset(ctlr, 0, sizeof *ctlr); + + io = p->mem[0].bar & ~0x0F; + mem = (ulong*)vmap(io, p->mem[0].size); + if(mem == 0){ + print("sdmv50xx: address 0x%luX in use\n", io); + free(sdev); + free(ctlr); + continue; + } + ctlr->rid = p->rid; + + /* avert thine eyes! (what does this do?) */ + mem[0x104f0/4] = 0; + ctlr->type = (p->did >> 12) & 3; + if(ctlr->type == 1){ + n = mem[0xc00/4]; + n &= ~(3<<4); + mem[0xc00/4] = n; + } + + sdev->ifc = &sdmv50xxifc; + sdev->ctlr = ctlr; + sdev->nunit = nunit; + sdev->idno = 'E' + ctlrno; + sdevs[ctlrno] = sdev; + ctlr->sdev = sdev; + ctlr->irq = p->intl; + ctlr->tbdf = p->tbdf; + ctlr->pcidev = p; + ctlr->lmmio = mem; + ctlr->mmio = (uchar*)mem; + ctlr->nchip = (nunit+3)/4; + ctlr->ndrive = nunit; + ctlr->magic = Ctlrmagic; + ctlr->enabled = 0; + for(i = 0; i < ctlr->nchip; i++){ + base = ctlr->mmio+0x20000+0x10000*i; + ctlr->chip[i].arb = (Arb*)base; + ctlr->chip[i].edma = (Edma*)(base + 0x2000); + } + for (i = 0; i < nunit; i++) { + drive = &ctlr->drive[i]; + drive->driveno = -1; /* unset */ + drive->sectors = 0; + drive->ctlr = ctlr; + drive->driveno = ctlrno*NCtlrdrv + i; + mvsatactlr[ctlrno] = ctlr; + mvsatadrive[drive->driveno] = drive; + drive->magic = Drvmagic; + } + nmvsatadrive += i; + ctlrno++; + if(head) + tail->next = sdev; + else + head = sdev; + tail = sdev; + } + return head; +} + +/* + * Enable the controller. Each disk has its own interrupt mask, + * and those get enabled as the disks are brought online. + */ +static int +mv50enable(SDev *sdev) +{ + char name[32]; + Ctlr *ctlr; + + dprint("sd%c: enable\n", sdev->idno); + + ctlr = sdev->ctlr; + if (ctlr == nil) + panic("mv50enable: nil sdev->ctlr"); + if (ctlr->enabled) + return 1; + snprint(name, sizeof name, "%s (%s)", sdev->name, sdev->ifc->name); + dprint("sd%c: irq %d\n", sdev->idno, ctlr->irq); + if (ctlr->magic != Ctlrmagic) + panic("mv50enable: bad controller magic 0x%lux", ctlr->magic); + intrenable(ctlr->irq, mv50interrupt, ctlr, ctlr->tbdf, name); + ctlr->enabled = 1; + return 1; +} + +/* + * Disable the controller. + */ +static int +mv50disable(SDev *sdev) +{ + char name[32]; + int i; + Ctlr *ctlr; + Drive *drive; + + dprint("sd%c: disable\n", sdev->idno); + + ctlr = sdev->ctlr; + ilock(ctlr); + for(i=0; isdev->nunit; i++){ + drive = &ctlr->drive[i]; + ilock(drive); + disabledrive(drive); + iunlock(drive); + } + iunlock(ctlr); + snprint(name, sizeof name, "%s (%s)", sdev->name, sdev->ifc->name); + intrdisable(ctlr->irq, mv50interrupt, ctlr, ctlr->tbdf, name); + return 0; +} + +/* + * Clean up all disk structures. Already disabled. + * Could keep count of number of allocated controllers + * and free the srblist when it drops to zero. + */ +static void +mv50clear(SDev *sdev) +{ + int i; + Ctlr *ctlr; + Drive *d; + + dprint("sd%c: clear\n", sdev->idno); + + ctlr = sdev->ctlr; + for(i=0; indrive; i++){ + d = &ctlr->drive[i]; + free(d->tx); + free(d->rx); + free(d->prd); + } + free(ctlr); +} + +/* + * Check that there is a disk. + */ +static int +mv50verify(SDunit *unit) +{ + Ctlr *ctlr; + Drive *drive; + int i; + + dprint("%s: verify\n", unit->name); + ctlr = unit->dev->ctlr; + drive = &ctlr->drive[unit->subno]; + ilock(ctlr); + ilock(drive); + i = configdrive(ctlr, drive, unit); + iunlock(drive); + iunlock(ctlr); + + if(i == -1) + return 0; + return 1; +} + +/* + * Check whether the disk is online. + */ +static int +mv50online(SDunit *unit) +{ + Ctlr *ctlr; + Drive *d; + int r, s0; + static int kproc; + + if(kproc++ == 0) + userinit(satakproc, 0, "mvsata"); + + ctlr = unit->dev->ctlr; + d = &ctlr->drive[unit->subno]; + if (d->magic != Drvmagic) + print("mv50online: bad drive magic 0x%lux\n", d->magic); + r = 0; + ilock(d); + s0 = d->state; + if(d->state == Dnew){ + if(d->state == Derror) + resetdisk(d); + identifydrive(d); + if(d->state == Dready) + r++; + } + print("%s: online: %s -> %s\n", unit->name, diskstates[s0], diskstates[d->state]); + if(d->state == Dready) + r++; + iunlock(d); + return r; +} + +#ifdef GROVEL +/* + * Register dumps + */ +typedef struct Regs Regs; +struct Regs +{ + ulong offset; + char *name; +}; + +static Regs regsctlr[] = +{ + 0x0C28, "pci serr# mask", + 0x1D40, "pci err addr low", + 0x1D44, "pci err addr hi", + 0x1D48, "pci err attr", + 0x1D50, "pci err cmd", + 0x1D58, "pci intr cause", + 0x1D5C, "pci mask cause", + 0x1D60, "device micr", + 0x1D64, "device mimr", +}; + +static Regs regsarb[] = +{ + 0x0004, "arb rqop", + 0x0008, "arb rqip", + 0x000C, "arb ict", + 0x0010, "arb itt", + 0x0014, "arb ic", + 0x0018, "arb btc", + 0x001C, "arb bts", + 0x0020, "arb bpc", +}; + +static Regs regsbridge[] = +{ + 0x0000, "bridge status", + 0x0004, "bridge serror", + 0x0008, "bridge sctrl", + 0x000C, "bridge phyctrl", + 0x003C, "bridge ctrl", + 0x0074, "bridge phymode", +}; + +static Regs regsedma[] = +{ + 0x0000, "edma config", + 0x0004, "edma timer", + 0x0008, "edma iec", + 0x000C, "edma iem", + 0x0010, "edma txbasehi", + 0x0014, "edma txi", + 0x0018, "edma txo", + 0x001C, "edma rxbasehi", + 0x0020, "edma rxi", + 0x0024, "edma rxo", + 0x0028, "edma c", + 0x002C, "edma tc", + 0x0030, "edma status", + 0x0034, "edma iordyto", +/* 0x0100, "edma pio", + 0x0104, "edma err", + 0x0108, "edma sectors", + 0x010C, "edma lba0", + 0x0110, "edma lba1", + 0x0114, "edma lba2", + 0x0118, "edma lba3", + 0x011C, "edma cmdstat", + 0x0120, "edma altstat", +*/ +}; + +static char* +rdregs(char *p, char *e, void *base, Regs *r, int n, char *prefix) +{ + int i; + + for(i=0; ibridge->status; + iunlock(d); + if(s == 0) + return SDeio; + if (d->state == Dready) + return SDok; + if ((i+1)%60 == 0){ + ilock(d); + resetdisk(d); + iunlock(d); + } + memset(&r, 0, sizeof r); + tsleep(&r, ret0, 0, 1000); + } + print("%s: not responding after 2 minutes\n", d->unit->name); + return SDeio; +} + +static int +mv50rio(SDreq *r) +{ + int count, max, n, status, try, flag; + uchar *cmd, *data; + uvlong lba; + Ctlr *ctlr; + Drive *drive; + SDunit *unit; + Srb *srb; + Rendez rz; + + unit = r->unit; + ctlr = unit->dev->ctlr; + drive = &ctlr->drive[unit->subno]; + cmd = r->cmd; + + if((status = sdfakescsi(r, drive->info, sizeof drive->info)) != SDnostatus){ + /* XXX check for SDcheck here */ + r->status = status; + return status; + } + + switch(cmd[0]){ + case 0x28: /* read */ + case 0x2A: /* write */ + break; + default: + print("%s: bad cmd 0x%.2ux\n", drive->unit->name, cmd[0]); + r->status = SDcheck; + return SDcheck; + } + + lba = (cmd[2]<<24)|(cmd[3]<<16)|(cmd[4]<<8)|cmd[5]; + count = (cmd[7]<<8)|cmd[8]; + if(r->data == nil) + return SDok; + if(r->dlen < count*unit->secsize) + count = r->dlen/unit->secsize; + + try = 0; +retry: + if(waitready(drive) != SDok) + return SDeio; + /* + * Could arrange here to have an Srb always outstanding: + * + * lsrb = nil; + * while(count > 0 || lsrb != nil){ + * srb = nil; + * if(count > 0){ + * srb = issue next srb; + * } + * if(lsrb){ + * sleep on lsrb and handle it + * } + * } + * + * On the disks I tried, this didn't help. If anything, + * it's a little slower. -rsc + */ + data = r->data; + while(count > 0){ + /* + * Max is 128 sectors (64kB) because prd->count is 16 bits. + */ + max = 128; + n = count; + if(n > max) + n = max; +// if((drive->edma->ctl&eEnEDMA) == 0) +// goto check try++ line; + srb = srbrw(cmd[0]==0x28 ? SRBread : SRBwrite, drive, data, n, lba); + ilock(drive); + startsrb(drive, srb); + iunlock(drive); + + sleep(&srb->Rendez, srbdone, srb); + flag = srb->flag; + freesrb(srb); + if(flag == 0){ + if(++try == 10){ + print("%s: bad disk\n", drive->unit->name); + return SDeio; + } + dprint("%s: retry\n", drive->unit->name); + memset(&rz, 0, sizeof rz); + tsleep(&rz, ret0, 0, 1000); + goto retry; + } + if(srb->flag & SFerror){ + print("%s: i/o error\n", drive->unit->name); + return SDeio; + } + count -= n; + lba += n; + data += n*unit->secsize; + } + r->rlen = data - (uchar*)r->data; + return SDok; +} + +SDifc sdmv50xxifc = { + "m", /* name */ + + mv50pnp, /* pnp */ + nil, /* legacy */ + nil, /* id */ + mv50enable, /* enable */ + mv50disable, /* disable */ + + mv50verify, /* verify */ + mv50online, /* online */ + mv50rio, /* rio */ + nil, + nil, + scsibio, /* bio */ +}; + +/* + * file-server-specific routines + * + * mvide* routines implement the `m' device and call the mvsata* routines. + */ + +static Drive* +mvsatapart(Drive *d) +{ + return d; +} + +static Drive* +mvsatadriveprobe(int driveno) +{ + Drive *d; + + d = mvsatadrive[driveno]; + if (d == nil) + return nil; + if (d->magic != Drvmagic) + print("m%d: mvsatadriveprobe: bad magic 0x%lux\n", driveno, d->magic); + d->driveno = driveno; + return mvsatapart(d); +} + +/* find all the controllers, enable interrupts, set up SDevs & SDunits */ +int +mvsatainit(void) +{ + unsigned i; + SDev *sdp, **sdpp; + SDunit *sup, **supp; + static int first = 1; + + dprint("mvsatainit(first=%d)\n", first); + if (first) + first = 0; + else + return 0xFF; + + mv50pnp(); + + for (sdpp = sdevs; sdpp < sdevs + nelem(sdevs); sdpp++) { + sdp = *sdpp; + if (sdp == nil) + continue; + i = sdpp - sdevs; + sdp->ifc = &sdmv50xxifc; + sdp->nunit = NCtlrdrv; +// sdp->index = i; + sdp->idno = 'E' + i; + sdp->ctlr = mvsatactlr[i]; + if (sdp->ctlr != nil) + mv50enable(sdp); + } + for (supp = sdunits; supp < sdunits + nelem(sdunits); supp++) { + sup = *supp; + if (sup == nil) + continue; + i = supp - sdunits; + sup->dev = sdevs[i/NCtlrdrv]; /* controller */ + sup->subno = i%NCtlrdrv; /* drive within controller */ + snprint(sup->name, sizeof sup->name, "m%d", i); + } + statsinit(); + return 0xFF; +} + +Devsize +mvsataseek(int n, Devsize offset) +{ + Drive *d; + + if((d = mvsatadrive[n]) == nil) + return -1; + d->offset = offset; + return n; +} + +/* zero indicates failure; only otherinit() cares */ +int +setmv50part(int driveno, char *) +{ +// dprint("m%d: setmv50part(%s)\n", driveno, name); + if(mvsatadriveprobe(driveno) == nil) + return 0; + return 1; +} + +static void +keepstats(SDunit *unit, int dbytes) +{ + Ctlr *ctlr = unit->dev->ctlr; + Target *tp = &ctlr->target[unit->subno]; + + qlock(tp); + if(tp->fflag == 0) { + dofilter(tp->work+0, C0a, C0b, 1); /* was , 1000); */ + dofilter(tp->work+1, C1a, C1b, 1); /* was , 1000); */ + dofilter(tp->work+2, C2a, C2b, 1); /* was , 1000); */ + dofilter(tp->rate+0, C0a, C0b, 1); + dofilter(tp->rate+1, C1a, C1b, 1); + dofilter(tp->rate+2, C2a, C2b, 1); + tp->fflag = 1; + } + tp->work[0].count++; + tp->work[1].count++; + tp->work[2].count++; + tp->rate[0].count += dbytes; + tp->rate[1].count += dbytes; + tp->rate[2].count += dbytes; + qunlock(tp); +} + +static void +pedanticchecks(Drive *d) +{ + SDunit *u; + int n; + + n = d->driveno; + if(n == -1) + panic("mvsataxfer: d->driveno unset"); + if((u = sdunits[n]) == nil) + panic("mvsataxfer: nil unit"); + if(d->unit != u) + panic("mvsataxfer: units differ: d->unit %p != %p", d->unit, u); + if(u->dev != sdevs[n/NCtlrdrv]) + panic("mvsataxfer: SDunit[%d].dev on wrong controller", n); + if(u->subno != n%NCtlrdrv) + panic("mvsataxfer: SDunit[%d].subno %d != %d\n", n, u->subno, n%NCtlrdrv); +} + +static long +mvsataxfer(Drive *d, int inout, Devsize start, long bytes) +{ + ulong secsize, sects; + SDunit *unit; +// static int n; + +// if((++n&0x7fff) == 0) +// idprint("%s: mvsataxfer(%c, %lld, %ld)\n", d->unit->name, "rw"[inout], start, bytes); + secsize = d->unit->secsize; + unit = d->unit; +// pedanticchecks(d); + if (unit->sectors == 0) { + unit->sectors = d->sectors; + unit->secsize = secsize; + } + keepstats(unit, bytes); + sects = (bytes + secsize - 1) / secsize; /* round up */ + if (start%secsize != 0) + print("%s: start offset not on sector boundary\n", d->unit->name); + return scsibio(unit, 0, inout, d->buf, sects, start/secsize); +} + +/* + * mvsataread & mvsatawrite do the real work; + * mvideread & mvidewrite just call them. + * mvsataread & mvsatawrite are called by the nvram routines. + * mvideread & mvidewrite are called for normal file server I/O. + */ + +Off +mvsataread(int driveno, void *a, long n) +{ + int skip; + Off rv, i; + uchar *aa = a; + Drive *dp; + + dp = mvsatadrive[driveno]; + if(dp == nil) + return 0; + ioprint("%s: mvsataread drive=%p\n", dp->unit->name, dp); + + if (dp->unit->secsize == 0) + panic("mvsataread: %s: sector size of zero", dp->unit->name); + skip = dp->offset % dp->unit->secsize; + for(rv = 0; rv < n; rv += i){ + i = mvsataxfer(dp, Read, dp->offset+rv-skip, n-rv+skip); + if(i == 0) + break; + if(i < 0) + return -1; + i -= skip; + if(i > n - rv) + i = n - rv; + memmove(aa+rv, dp->buf + skip, i); + skip = 0; + } + dp->offset += rv; + return rv; +} + +Off +mvsatawrite(int driveno, void *a, long n) +{ + Off rv, i, partial; + uchar *aa = a; + Drive *dp; + + dp = mvsatadrive[driveno]; + if(dp == nil) + return 0; + ioprint("%s: mvsatawrite drive=%p\n", dp->unit->name, dp); + + /* + * if not starting on a sector boundary, + * read in the first sector before writing it out. + */ + if (dp->unit->secsize == 0) + panic("mvsatawrite: %s: sector size of zero", dp->unit->name); + partial = dp->offset % dp->unit->secsize; + if(partial){ + if(mvsataxfer(dp, Read, dp->offset-partial, dp->unit->secsize) < 0) + return -1; + if(partial+n > dp->unit->secsize) + rv = dp->unit->secsize - partial; + else + rv = n; + memmove(dp->buf+partial, aa, rv); + if(mvsataxfer(dp, Write, dp->offset-partial, dp->unit->secsize) < 0) + return -1; + } else + rv = 0; + + /* + * write out the full sectors (common case) + */ + partial = (n - rv) % dp->unit->secsize; + n -= partial; + for(; rv < n; rv += i){ + i = n - rv; + if(i > Maxxfer) + i = Maxxfer; + memmove(dp->buf, aa+rv, i); + i = mvsataxfer(dp, Write, dp->offset+rv, i); + if(i == 0) + break; + if(i < 0) + return -1; + } + + /* + * if not ending on a sector boundary, + * read in the last sector before writing it out. + */ + if(partial){ + if (mvsataxfer(dp, Read, dp->offset+rv, dp->unit->secsize) < 0) + return -1; + memmove(dp->buf, aa+rv, partial); + if (mvsataxfer(dp, Write, dp->offset+rv, dp->unit->secsize) < 0) + return -1; + rv += partial; + } + dp->offset += rv; + return rv; +} + +/* + * normal file server I/O interface + */ + +/* result is size of d in blocks of RBUFSIZE bytes */ +Devsize +mvidesize(Device *d) +{ + Drive *dp = d->private; + + if (dp == nil) + return 0; + /* + * dividing first is sloppy but reduces the range of intermediate + * values, avoiding possible overflow. + */ + return (dp->sectors / RBUFSIZE) * dp->unit->secsize; +} + +void +mvideinit(Device *d) +{ + int driveno; + Drive *dp; + + mvsatainit(); + if (d->private) + return; + /* call setmv50part() first in case we didn't boot off this drive */ + driveno = d->wren.ctrl*NCtlrdrv + d->wren.targ; + dprint("%Z: mvideinit\n", d); + setmv50part(driveno, "disk"); + if((dp = mvsatadriveprobe(driveno)) == 0) + return; + d->private = dp; + if (dp->unit == nil) + panic("mvideinit: %Z: nil dp->unit", d); + /* 0 size okay. */ + print("\t\t%llud sectors/%llud blocks\n", dp->sectors, mvidesize(d)); +} + +int +mvideread(Device *d, Devsize b, void *c) +{ + int x, driveno; + Drive *dp; + Ctlr *cp; + + if (d == nil || d->private == nil) { + print("mvideread: %Z: nil device/drive\n", d); + return 1; + } + dp = d->private; + cp = dp->ctlr; + if (cp == nil) + panic("mvideread: no controller for drive"); + + qlock(&cp->idelock); + cp->idelock.name = "mvideio"; + driveno = dp->driveno; + if (driveno == -1) + panic("mvideread: dp->driveno unset"); + idprint("mvideread(%d, %lld)\n", driveno, (Wideoff)b); + mvsataseek(driveno, b * RBUFSIZE); + x = mvsataread(driveno, c, RBUFSIZE) != RBUFSIZE; + qunlock(&cp->idelock); + return x; +} + +int +mvidewrite(Device *d, Devsize b, void *c) +{ + int x, driveno; + Drive *dp; + Ctlr *cp; + + if (d == nil || d->private == nil) { + print("mvideread: %Z: nil device/drive\n", d); + return 1; + } + dp = d->private; + cp = dp->ctlr; + if (cp == nil) + panic("mvidewrite: no controller for drive"); + + qlock(&cp->idelock); + cp->idelock.name = "mvideio"; + driveno = dp->driveno; + if (driveno == -1) + panic("mvidewrite: dp->driveno unset"); + idprint("mvidewrite(%d, %lld)\n", driveno, (Wideoff)b); + mvsataseek(driveno, b * RBUFSIZE); + x = mvsatawrite(driveno, c, RBUFSIZE) != RBUFSIZE; + qunlock(&cp->idelock); + return x; +} + +static void +cmd_stat(int, char*[]) +{ + Ctlr *ctlr; + int ctlrno, targetno; + Target *tp; + + for(ctlrno = 0; ctlrno < nelem(mvsatactlr); ctlrno++){ + ctlr = mvsatactlr[ctlrno]; + if(ctlr == nil || ctlr->sdev == nil) + continue; + for(targetno = 0; targetno < NTarget; targetno++){ + tp = &ctlr->target[targetno]; + if(tp->fflag == 0) + continue; + print("\t%d.%d work =%9W%9W%9W xfrs\n", + ctlrno, targetno, + tp->work+0, tp->work+1, tp->work+2); + print("\t rate =%9W%9W%9W tBps\n", + tp->rate+0, tp->rate+1, tp->rate+2); + } + } +} + +static void +statsinit(void) +{ + cmd_install("statm", "-- marvell sata stats", cmd_stat); +} + +/* Tab 4 Font +* Copyright 2005 +* Coraid, Inc. +* +* This software is provided `as-is,' without any express or implied +* warranty. In no event will the author be held liable for any damages +* arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must +* not claim that you wrote the original software. If you use this +* software in a product, an acknowledgment in the product documentation +* would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must +* not be misrepresented as being the original software. +* +* 3. This notice may not be removed or altered from any source +* distribution. +*/ diff -Nru /sys/src/fs/pc/sdscsi.c /sys/src/fs/pc/sdscsi.c --- /sys/src/fs/pc/sdscsi.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/pc/sdscsi.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,373 @@ +#include "all.h" +#include "io.h" +#include "mem.h" + +#include "sd.h" +#include "../ip/ip.h" /* for Ether */ +#include "etherif.h" /* for Ether */ +#include "compat.h" +#undef error + +static int +scsitest(SDreq* r) +{ + r->write = 0; + memset(r->cmd, 0, sizeof(r->cmd)); + r->cmd[1] = r->lun<<5; + r->clen = 6; + r->data = nil; + r->dlen = 0; + r->flags = 0; + + r->status = ~0; + +// cgascreenputs("A", 1); + return r->unit->dev->ifc->rio(r); +} + +int +scsiverify(SDunit* unit) +{ + static SDreq *r; + int i, status; + static uchar *inquiry; + + if((r = sdmalloc(r, sizeof(SDreq))) == nil) + return 0; + + if((inquiry = sdmalloc(inquiry, sizeof(unit->inquiry))) == nil) + return 0; + + r->unit = unit; + r->lun = 0; /* ??? */ + + memset(unit->inquiry, 0, sizeof(unit->inquiry)); + r->write = 0; + r->cmd[0] = 0x12; + r->cmd[1] = r->lun<<5; + r->cmd[4] = sizeof(unit->inquiry)-1; + r->clen = 6; + r->data = inquiry; + r->dlen = sizeof(unit->inquiry)-1; + r->flags = 0; + + r->status = ~0; +// cgascreenputs("B", 1); + if(unit->dev->ifc->rio(r) != SDok){ + return 0; + } + memmove(unit->inquiry, inquiry, r->dlen); + + SET(status); + for(i = 0; i < 3; i++){ + while((status = scsitest(r)) == SDbusy) + ; + if(status == SDok || status != SDcheck) + break; + if(!(r->flags & SDvalidsense)) + break; + if((r->sense[2] & 0x0F) != 0x02) + continue; + + /* + * Unit is 'not ready'. + * If it is in the process of becoming ready or needs + * an initialising command, set status so it will be spun-up + * below. + * If there's no medium, that's OK too, but don't + * try to spin it up. + */ + if(r->sense[12] == 0x04){ + if(r->sense[13] == 0x02 || r->sense[13] == 0x01){ + status = SDok; + break; + } + } + if(r->sense[12] == 0x3A) + break; + } + + if(status == SDok){ + /* + * Try to ensure a direct-access device is spinning. + * Ignore the result. + */ + if((unit->inquiry[0] & 0x1F) == 0){ + memset(r->cmd, 0, sizeof(r->cmd)); + r->write = 0; + r->cmd[0] = 0x1B; + r->cmd[1] = r->lun<<5; + r->cmd[4] = 1; + r->clen = 6; + r->data = nil; + r->dlen = 0; + r->flags = 0; + + r->status = ~0; + unit->dev->ifc->rio(r); + } + return 1; + } + return 0; +} + +int +return0(void*) +{ + return 0; +} + +static int +scsirio(SDreq* r) +{ + /* + * Perform an I/O request, returning + * -1 failure + * 0 ok + * 2 retry + * The contents of r may be altered so the + * caller should re-initialise if necesary. + */ + r->status = ~0; +// cgascreenputs("C", 1); + switch(r->unit->dev->ifc->rio(r)){ + default: + return -1; + case SDcheck: + if(!(r->flags & SDvalidsense)) + return -1; + switch(r->sense[2] & 0x0F){ + case 0x00: /* no sense */ + case 0x01: /* recovered error */ + return 2; + case 0x06: /* check condition */ + /* + * 0x28 - not ready to ready transition, + * medium may have changed. + * 0x29 - power on or some type of reset. + */ + if(r->sense[12] == 0x28 && r->sense[13] == 0) + return 2; + if(r->sense[12] == 0x29) + return 2; + return -1; + case 0x02: /* not ready */ + /* + * If no medium present, bail out. + * If unit is becoming ready, rather than not + * not ready, wait a little then poke it again. */ + if(r->sense[12] == 0x3A) + return -1; + if(r->sense[12] != 0x04 || r->sense[13] != 0x01) + return -1; + + tsleep(nil, return0, 0, 500); + scsitest(r); + return 2; + default: + return -1; + } + case SDok: + return 0; + } +} + +int +scsionline(SDunit* unit) +{ + int ok; + static SDreq *r; + static uchar *p; + + if((r = sdmalloc(r, sizeof(SDreq))) == nil) + return 0; + + if((p = sdmalloc(p, 8)) == nil) + return 0; + + ok = 0; + + r->unit = unit; + r->lun = 0; /* ??? */ + for(;;){ + /* + * Read-capacity is mandatory for DA, WORM, CD-ROM and + * MO. It may return 'not ready' if type DA is not + * spun up, type MO or type CD-ROM are not loaded or just + * plain slow getting their act together after a reset. + */ + r->write = 0; + memset(r->cmd, 0, sizeof(r->cmd)); + r->cmd[0] = 0x25; + r->cmd[1] = r->lun<<5; + r->clen = 10; + r->data = p; + r->dlen = 8; + r->flags = 0; + + r->status = ~0; +// cgascreenputs("F", 1); + switch(scsirio(r)){ + default: + break; + case 0: + unit->sectors = (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3]; + /* + * Read-capacity returns the LBA of the last sector, + * therefore the number of sectors must be incremented. + */ + unit->sectors++; + unit->secsize = (p[4]<<24)|(p[5]<<16)|(p[6]<<8)|p[7]; + ok = 1; + break; + case 2: + continue; + } + break; + } + + return ok; +} + +int +scsiexec(SDunit* unit, int write, uchar* cmd, int clen, void* data, int* dlen) +{ + static SDreq *r; + int status; + + if((r = sdmalloc(r, sizeof(SDreq))) == nil) + return SDmalloc; + + r->unit = unit; + r->lun = cmd[1]>>5; /* ??? */ + r->write = write; + memmove(r->cmd, cmd, clen); + r->clen = clen; + r->data = data; + if(dlen) + r->dlen = *dlen; + r->flags = 0; + + r->status = ~0; + + /* + * Call the device-specific I/O routine. + * There should be no calls to 'error()' below this + * which percolate back up. + */ +// cgascreenputs("D", 1); + switch(status = unit->dev->ifc->rio(r)){ + case SDok: + if(dlen) + *dlen = r->rlen; + /*FALLTHROUGH*/ + case SDcheck: + /*FALLTHROUGH*/ + default: + /* + * It's more complicated than this. There are conditions + * which are 'ok' but for which the returned status code + * is not 'SDok'. + * Also, not all conditions require a reqsense, might + * need to do a reqsense here and make it available to the + * caller somehow. + * + * Mañana. + */ + break; + } + + return status; +} + +long +scsibio(SDunit* unit, int lun, int write, void* data, long nb, Off bno) +{ + static SDreq *r; + long rlen; + + if((r = sdmalloc(r, sizeof(SDreq))) == nil) + return SDmalloc; + + r->unit = unit; + r->lun = lun; +again: + r->write = write; + if(write == 0) + r->cmd[0] = 0x28; + else + r->cmd[0] = 0x2A; + r->cmd[1] = (lun<<5); + r->cmd[2] = bno>>24; + r->cmd[3] = bno>>16; + r->cmd[4] = bno>>8; + r->cmd[5] = bno; + r->cmd[6] = 0; + r->cmd[7] = nb>>8; + r->cmd[8] = nb; + r->cmd[9] = 0; + r->clen = 10; + r->data = data; + r->dlen = nb*unit->secsize; + r->flags = 0; + + r->status = ~0; +// cgascreenputs("E", 1); + switch(scsirio(r)){ + default: + rlen = -1; + break; + case 0: + rlen = r->rlen; + break; + case 2: + rlen = -1; + if(!(r->flags & SDvalidsense)) + break; + switch(r->sense[2] & 0x0F){ + default: + break; + case 0x06: /* check condition */ + /* + * Check for a removeable media change. + * If so, mark it and zap the geometry info + * to force an online request. + */ + if(r->sense[12] != 0x28 || r->sense[13] != 0) + break; + if(unit->inquiry[1] & 0x80){ + unit->sectors = 0; + } + break; + case 0x02: /* not ready */ + /* + * If unit is becoming ready, + * rather than not not ready, try again. + */ + if(r->sense[12] == 0x04 && r->sense[13] == 0x01) + goto again; + break; + } + break; + } + + return rlen; +} + +SDev* +scsiid(SDev* sdev, SDifc* ifc) +{ + static char idno[16] = "0123456789abcdef"; + static char *p = idno; + + while(sdev){ + if(sdev->ifc == ifc){ + sdev->idno = *p++; + if(p >= &idno[sizeof(idno)]) + break; + } + sdev = sdev->next; + } + + return nil; +} diff -Nru /sys/src/fs/pc/toy.c /sys/src/fs/pc/toy.c --- /sys/src/fs/pc/toy.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/pc/toy.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,122 @@ +#include "all.h" +#include "io.h" +#include "mem.h" + +enum { + Paddr= 0x70, /* address port */ + Pdata= 0x71, /* data port */ + + Seconds= 0x00, + Minutes= 0x02, + Hours= 0x04, + Mday= 0x07, + Month= 0x08, + Year= 0x09, + Status= 0x0A, + + Nbcd= 6, +}; + +#define GETBCD(o) ((bcdclock[o]&0xf) + 10*(bcdclock[o]>>4)) +#define PUTBCD(n,o) bcdclock[o] = (n % 10) | (((n / 10) % 10)<<4) + +static Lock rtclock; + +void +setrtc(Timet secs) +{ + Rtc rtc; + uchar bcdclock[Nbcd]; + + sec2rtc(secs, &rtc); + + PUTBCD(rtc.sec, 0); + PUTBCD(rtc.min, 1); + PUTBCD(rtc.hour, 2); + PUTBCD(rtc.mday, 3); + PUTBCD(rtc.mon, 4); + PUTBCD(rtc.year, 5); + + ilock(&rtclock); + outb(Paddr, Seconds); outb(Pdata, bcdclock[0]); + outb(Paddr, Minutes); outb(Pdata, bcdclock[1]); + outb(Paddr, Hours); outb(Pdata, bcdclock[2]); + outb(Paddr, Mday); outb(Pdata, bcdclock[3]); + outb(Paddr, Month); outb(Pdata, bcdclock[4]); + outb(Paddr, Year); outb(Pdata, bcdclock[5]); + iunlock(&rtclock); +} + +static ulong +_rtctime(void) +{ + uchar bcdclock[Nbcd]; + Rtc rtc; + int i; + + /* don't do the read until the clock is no longer busy */ + for(i = 0; i < 10000; i++){ + outb(Paddr, Status); + if(inb(Pdata) & 0x80) + continue; + + /* read clock values */ + outb(Paddr, Seconds); bcdclock[0] = inb(Pdata); + outb(Paddr, Minutes); bcdclock[1] = inb(Pdata); + outb(Paddr, Hours); bcdclock[2] = inb(Pdata); + outb(Paddr, Mday); bcdclock[3] = inb(Pdata); + outb(Paddr, Month); bcdclock[4] = inb(Pdata); + outb(Paddr, Year); bcdclock[5] = inb(Pdata); + + outb(Paddr, Status); + if((inb(Pdata) & 0x80) == 0) + break; + } + + /* + * convert from BCD + */ + rtc.sec = GETBCD(0); + rtc.min = GETBCD(1); + rtc.hour = GETBCD(2); + rtc.mday = GETBCD(3); + rtc.mon = GETBCD(4); + rtc.year = GETBCD(5); + + /* + * the world starts jan 1 1970 + */ + if(rtc.year < 70) + rtc.year += 2000; + else + rtc.year += 1900; + return rtc2sec(&rtc); +} + +Timet +rtctime(void) +{ + int i; + Timet t, ot; + + ilock(&rtclock); + + /* loop till we get two reads in a row the same */ + t = _rtctime(); + for(i = 0; i < 100; i++){ + ot = t; + t = _rtctime(); + if(ot == t) + break; + } + iunlock(&rtclock); + + return t; +} + +uchar +nvramread(int offset) +{ + outb(Paddr, offset); + return inb(Pdata); +} diff -Nru /sys/src/fs/pc/trap.c /sys/src/fs/pc/trap.c --- /sys/src/fs/pc/trap.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/pc/trap.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,360 @@ +#include "all.h" +#include "mem.h" +#include "ureg.h" +#include "io.h" + +void intr0(void), intr1(void), intr2(void), intr3(void); +void intr4(void), intr5(void), intr6(void), intr7(void); +void intr8(void), intr9(void), intr10(void), intr11(void); +void intr12(void), intr13(void), intr14(void), intr15(void); +void intr16(void); +void intr24(void), intr25(void), intr26(void), intr27(void); +void intr28(void), intr29(void), intr30(void), intr31(void); +void intr32(void), intr33(void), intr34(void), intr35(void); +void intr36(void), intr37(void), intr38(void), intr39(void); +void intr64(void); +void intrbad(void); + +int int0mask = 0xff; /* interrupts enabled for first 8259 */ +int int1mask = 0xff; /* interrupts enabled for second 8259 */ + +int elcr; /* mask of level-triggered interrupts */ + +/* + * trap/interrupt gates + */ +Segdesc ilt[256]; +int badintr[16]; + +enum +{ + Maxhandler= 128, /* max number of interrupt handlers */ +}; + +typedef struct Handler Handler; +struct Handler +{ + void (*r)(Ureg*, void*); + void *arg; + Handler *next; +}; + +struct +{ + Lock; + Handler *ivec[256]; + Handler h[Maxhandler]; + int free; +} halloc; + +void +sethvec(int v, void (*r)(void), int type, int pri) +{ + ilt[v].d0 = ((ulong)r)&0xFFFF|(KESEL<<16); + ilt[v].d1 = ((ulong)r)&0xFFFF0000|SEGP|SEGPL(pri)|type; +} + +void +setvec(int v, void (*r)(Ureg*, void*), void *arg) +{ + Handler *h; + + lock(&halloc); + if(halloc.free >= Maxhandler) + panic("out of interrupt handlers"); + h = &halloc.h[halloc.free++]; + h->next = halloc.ivec[v]; + h->r = r; + h->arg = arg; + halloc.ivec[v] = h; + unlock(&halloc); + + /* + * enable corresponding interrupt in 8259 + */ + if((v&~0x7) == Int0vec){ + int0mask &= ~(1<<(v&7)); + outb(Int0aux, int0mask); + } else if((v&~0x7) == Int1vec){ + int1mask &= ~(1<<(v&7)); + outb(Int1aux, int1mask); + } +} + +/* + * set up the interrupt/trap gates + */ +void +trapinit(void) +{ + int elcr1, i; + + /* + * set all interrupts to panics + */ + for(i = 0; i < 256; i++) + sethvec(i, intrbad, SEGTG, 0); + + /* + * 80386 processor (and coprocessor) traps + */ + sethvec(0, intr0, SEGTG, 0); + sethvec(1, intr1, SEGTG, 0); + sethvec(2, intr2, SEGTG, 0); + sethvec(4, intr4, SEGTG, 0); + sethvec(5, intr5, SEGTG, 0); + sethvec(6, intr6, SEGTG, 0); + sethvec(7, intr7, SEGTG, 0); + sethvec(8, intr8, SEGTG, 0); + sethvec(9, intr9, SEGTG, 0); + sethvec(10, intr10, SEGTG, 0); + sethvec(11, intr11, SEGTG, 0); + sethvec(12, intr12, SEGTG, 0); + sethvec(13, intr13, SEGTG, 0); + sethvec(14, intr14, SEGIG, 0); /* page fault, interrupts off */ + sethvec(15, intr15, SEGTG, 0); + sethvec(16, intr16, SEGIG, 0); /* math coprocessor, interrupts off */ + + /* + * device interrupts + */ + sethvec(24, intr24, SEGIG, 0); + sethvec(25, intr25, SEGIG, 0); + sethvec(26, intr26, SEGIG, 0); + sethvec(27, intr27, SEGIG, 0); + sethvec(28, intr28, SEGIG, 0); + sethvec(29, intr29, SEGIG, 0); + sethvec(30, intr30, SEGIG, 0); + sethvec(31, intr31, SEGIG, 0); + sethvec(32, intr32, SEGIG, 0); + sethvec(33, intr33, SEGIG, 0); + sethvec(34, intr34, SEGIG, 0); + sethvec(35, intr35, SEGIG, 0); + sethvec(36, intr36, SEGIG, 0); + sethvec(37, intr37, SEGIG, 0); + sethvec(38, intr38, SEGIG, 0); + sethvec(39, intr39, SEGIG, 0); + + /* + * tell the hardware where the table is (and how long) + */ + putidt(ilt, sizeof(ilt)); + + /* + * Set up the first 8259 interrupt processor. + * Make 8259 interrupts start at CPU vector Int0vec. + * Set the 8259 as master with edge triggered + * input with fully nested interrupts. + */ + outb(Int0ctl, 0x11); /* ICW1 - edge triggered, master, + ICW4 will be sent */ + outb(Int0aux, Int0vec); /* ICW2 - interrupt vector offset */ + outb(Int0aux, 0x04); /* ICW3 - have slave on level 2 */ + outb(Int0aux, 0x01); /* ICW4 - 8086 mode, not buffered */ + + /* + * Set up the second 8259 interrupt processor. + * Make 8259 interrupts start at CPU vector Int0vec. + * Set the 8259 as master with edge triggered + * input with fully nested interrupts. + */ + outb(Int1ctl, 0x11); /* ICW1 - edge triggered, master, + ICW4 will be sent */ + outb(Int1aux, Int1vec); /* ICW2 - interrupt vector offset */ + outb(Int1aux, 0x02); /* ICW3 - I am a slave on level 2 */ + outb(Int1aux, 0x01); /* ICW4 - 8086 mode, not buffered */ + outb(Int1aux, 0xFF); + + /* + * pass #2 8259 interrupts to #1 + */ + int0mask &= ~0x04; + outb(Int0aux, int0mask); + + /* + * Set Ocw3 to return the ISR when ctl read. + */ + inb(Int0ctl); + outb(Int0ctl, Ocw3|0x03); + inb(Int1ctl); + outb(Int1ctl, Ocw3|0x03); + + /* + * Check for Edge/Level register. + * This check may not work for all chipsets. + */ + elcr1 = inb(Elcr1); + outb(Elcr1, 0); + if(inb(Elcr1) == 0){ + outb(Elcr1, 0x20); + if(inb(Elcr1) == 0x20) + elcr = (inb(Elcr2)<<8)|elcr1; + } + outb(Elcr1, elcr1); + + fpinit(); +} + +static int +i8259isr(int v) +{ + int isr; + + /* + * tell the 8259 that we're done with the + * highest level interrupt (interrupts are still + * off at this point) + */ + isr = 0; + if(v >= Int0vec && v < Int0vec+16){ + isr = inb(Int0ctl); + outb(Int0ctl, EOI); + if(v >= Int0vec+8){ + isr |= inb(Int1ctl)<<8; + outb(Int1ctl, EOI); + } + } + + return isr & (1<<(v-Int0vec)); +} + +char *excname[] = { + [0] "divide error", + [1] "debug exception", + [2] " nonmaskable interrupt", + [3] "breakpoint", + [4] "overflow", + [5] "bounds check", + [6] "invalid opcode", + [7] "coprocessor not available", + [8] "double fault", + [9] "9 (reserved)", + [10] "invalid TSS", + [11] "segment not present", + [12] "stack exception", + [13] "general protection violation", + [14] "page fault", + [15] "15 (reserved)", + [16] "coprocessor error", + [17] "alignment check", + [18] "machine check", +}; + +Ureg lasttrap, *lastur; +static int nspuriousintr; +static int lastintr; + +/* + * All traps + */ +void +trap(Ureg *ur) +{ + int v; + Handler *h; + + v = ur->trap; + if(h = halloc.ivec[v]){ + if((v >= Int0vec && v < Int0vec+16) && !(elcr & (1<<(v-Int0vec)))){ + i8259isr(v); + lastintr = v-Int0vec; + } + + MACHP(0)->intrp = 0; + /* there may be multiple handlers on one interrupt level */ + do { + if (h->r == 0) + panic("trap: nil h->r"); + (*h->r)(ur, h->arg); + h = h->next; + } while(h); + + if((v >= Int0vec && v < Int0vec+16) && (elcr & (1<<(v-Int0vec)))){ + i8259isr(v); + lastintr = v-Int0vec; + } + } + else if(v >= Int0vec && v < Int0vec+16){ + /* + * An unknown interrupt. + * Check for a default IRQ7. This can happen when + * the IRQ input goes away before the acknowledge. + * In this case, a 'default IRQ7' is generated, but + * the corresponding bit in the ISR isn't set. + * In fact, just ignore all such interrupts. + */ + if(nspuriousintr < 2) + print("spurious interrupt %d, lastintr %d\n", v-Int0vec, lastintr); + nspuriousintr++; + return; + } + else{ + dumpregs(ur); + if(v < (sizeof(excname)/sizeof(excname[0]))) + panic("%s", excname[v]); + panic("unknown trap/intr: %d\n", v); + } + + splhi(); + if(u && u->state == Running) + sched(); + + lasttrap = *ur; + lastur = ur; +} + +/* + * dump registers + */ +void +dumpregs2(Ureg *ur) +{ + print("FLAGS=%lux TRAP=%lux ECODE=%lux CS=%lux PC=%lux\n", ur->flags, ur->trap, + ur->ecode, ur->cs&0xff, ur->pc); + print(" AX %8.8lux BX %8.8lux CX %8.8lux DX %8.8lux\n", + ur->ax, ur->bx, ur->cx, ur->dx); + print(" SI %8.8lux DI %8.8lux BP %8.8lux\n", + ur->si, ur->di, ur->bp); + print(" DS %4.4lux ES %4.4lux FS %4.4lux GS %4.4lux\n", + ur->ds&0xffff, ur->es&0xffff, ur->fs&0xffff, ur->gs&0xffff); + print(" CR0 %8.8lux CR2 %8.8lux\n", getcr0(), getcr2()); +} + +void +dumpregs(Ureg *ur) +{ + dumpregs2(ur); + print(" ur %lux\n", (ulong)ur); + dumpregs2(&lasttrap); + print(" lastur %lux\n", (ulong)lastur); +} + +void +dumpstack(User *p) +{ + ulong i, l, v, hl; + extern ulong etext; + + if(p == 0) + return; + hl = 0; + print("stack trace of %d\n", p->pid); + i = 0; + for(l = (ulong)(p->stack+MAXSTACK)-4; l >= (ulong)(p->stack); l -= 4){ + v = *(ulong*)l; + if(v) + hl = l; + if(KTZERO < v && v < (ulong)&etext){ + print("0x%8.8lux ", v); + i++; + } + if(i == 8){ + i = 0; + print("\n"); + } + } + if(hl) + print("%ld stack used out of %d\n", + (ulong)(p->stack+MAXSTACK)-hl, MAXSTACK); + print("\n"); +} diff -Nru /sys/src/fs/port/9p1.c /sys/src/fs/port/9p1.c --- /sys/src/fs/port/9p1.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/port/9p1.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,1650 @@ +#include "all.h" + +#include "9p1.h" + +extern Nvrsafe nvr; + +typedef struct { + uchar chal[CHALLEN]; /* locally generated challenge */ + uchar rchal[CHALLEN]; /* remotely generated challenge */ + Lock idlock; + ulong idoffset; /* offset of id vector */ + ulong idvec; /* vector of acceptable id's */ +} Authinfo; + +static void +f_nop(Chan *cp, Fcall*, Fcall*) +{ + + if(CHAT(cp)) + print("c_nop %d\n", cp->chan); +} + +static void +f_flush(Chan *cp, Fcall*, Fcall*) +{ + + if(CHAT(cp)) + print("c_flush %d\n", cp->chan); + runlock(&cp->reflock); + wlock(&cp->reflock); + wunlock(&cp->reflock); + rlock(&cp->reflock); +} + +/* + * create a challenge for a fid space + */ +static void +mkchallenge(Authinfo *aip) +{ + int i; + + srand((ulong)aip + m->ticks); + for(i = 0; i < CHALLEN; i++) + aip->chal[i] = nrand(256); + + aip->idoffset = 0; + aip->idvec = 0; +} + +static void +f_session(Chan *cp, Fcall *in, Fcall *ou) +{ + Authinfo *aip; + + aip = (Authinfo*)cp->authinfo; + + if(CHAT(cp)) + print("c_session %d\n", cp->chan); + memmove(aip->rchal, in->chal, sizeof(aip->rchal)); + mkchallenge(aip); + memmove(ou->chal, aip->chal, sizeof(ou->chal)); + if(noauth || wstatallow) + memset(ou->authid, 0, sizeof(ou->authid)); + else + memmove(ou->authid, nvr.authid, sizeof(ou->authid)); + + sprint(ou->authdom, "%s.%s", service, nvr.authdom); + fileinit(cp); +} + +/* + * match a challenge from an attach + */ +static int +authorize(Chan *cp, Fcall *in, Fcall *ou) +{ + Ticket t; + Authenticator a; + int x; + ulong bit; + Authinfo *aip; + + if(noauth || wstatallow) /* set to allow entry during boot */ + return 1; + + if(strcmp(in->uname, "none") == 0) + return 1; + + if(in->type == Toattach) + return 0; + + /* decrypt and unpack ticket */ + convM2T9p1(in->ticket, &t, nvr.authkey); + if(t.num != AuthTs){ + print("9p1: bad AuthTs num\n"); + return 0; + } + + /* decrypt and unpack authenticator */ + convM2A9p1(in->auth, &a, t.key); + if(a.num != AuthAc){ + print("9p1: bad AuthAc num\n"); + return 0; + } + + /* challenges must match */ + aip = (Authinfo*)cp->authinfo; + if(memcmp(a.chal, aip->chal, sizeof(a.chal)) != 0){ + print("9p1: bad challenge\n"); + return 0; + } + + /* + * the id must be in a valid range. the range is specified by a + * lower bound (idoffset) and a bit vector (idvec) where a + * bit set to 1 means unusable + */ + lock(&aip->idlock); + x = a.id - aip->idoffset; + bit = 1< 31 || (bit&aip->idvec)){ + unlock(&aip->idlock); + print("9p1: id out of range: idoff %ld idvec %lux id %ld\n", + aip->idoffset, aip->idvec, a.id); + return 0; + } + aip->idvec |= bit; + + /* normalize the vector */ + while(aip->idvec&0xffff0001){ + aip->idvec >>= 1; + aip->idoffset++; + } + unlock(&aip->idlock); + + /* ticket name and attach name must match */ + if(memcmp(in->uname, t.cuid, sizeof(in->uname)) != 0){ + print("9p1: names don't match\n"); + return 0; + } + + /* copy translated name into input record */ + memmove(in->uname, t.suid, sizeof(in->uname)); + + /* craft a reply */ + a.num = AuthAs; + memmove(a.chal, aip->rchal, CHALLEN); + convA2M9p1(&a, ou->rauth, t.key); + + return 1; +} + +/* + * buggery to give false qid for + * the top 2 levels of the dump fs + */ +void +mkqid(Qid* qid, Dentry *d, int buggery) +{ + int c; + + if(buggery && d->qid.path == (QPDIR|QPROOT)){ + c = d->name[0]; + if(c >= '0' && c <= '9'){ + qid->path = 3; + qid->vers = d->qid.version; + qid->type = QTDIR; + + c = (c-'0')*10 + (d->name[1]-'0'); + if(c >= 1 && c <= 12) + qid->path = 4; + return; + } + } + + mkqid9p2(qid, &d->qid, d->mode); +} + +int +mkqidcmp(Qid* qid, Dentry *d) +{ + Qid tmp; + + mkqid(&tmp, d, 1); + if(qid->path == tmp.path && qid->type == tmp.type) + return 0; + return Eqid; +} + +static void +f_attach(Chan *cp, Fcall *in, Fcall *ou) +{ + Iobuf *p; + Dentry *d; + File *f; + int u; + Filsys *fs; + Off raddr; + + if(CHAT(cp)) { + print("c_attach %d\n", cp->chan); + print(" fid = %d\n", in->fid); + print(" uid = %s\n", in->uname); + print(" arg = %s\n", in->aname); + } + + ou->qid = QID9P1(0,0); + ou->fid = in->fid; + if(!in->aname[0]) /* default */ + strncpy(in->aname, "main", sizeof(in->aname)); + p = 0; + f = filep(cp, in->fid, 1); + if(!f) { + ou->err = Efid; + goto out; + } + + u = -1; + if(cp != cons.chan) { + if(noattach && strcmp(in->uname, "none")) { + ou->err = Enoattach; + goto out; + } + if(authorize(cp, in, ou) == 0 || strcmp(in->uname, "adm") == 0) { + ou->err = Eauth; + goto out; + } + u = strtouid(in->uname); + if(u < 0) { + ou->err = Ebadu; + goto out; + } + } + f->uid = u; + + fs = fsstr(in->aname); + if(fs == 0) { + ou->err = Ebadspc; + goto out; + } + raddr = getraddr(fs->dev); + p = getbuf(fs->dev, raddr, Bread); + d = getdir(p, 0); + if(!d || checktag(p, Tdir, QPROOT) || !(d->mode & DALLOC)) { + ou->err = Ealloc; + goto out; + } + if (iaccess(f, d, DEXEC) || + f->uid == 0 && fs->dev->type == Devro) { + /* + * 'none' not allowed on dump + */ + ou->err = Eaccess; + goto out; + } + accessdir(p, d, FREAD, f->uid); + mkqid(&f->qid, d, 1); + f->fs = fs; + f->addr = raddr; + f->slot = 0; + f->open = 0; + freewp(f->wpath); + f->wpath = 0; + + mkqid9p1(&ou->qid, &f->qid); + + strncpy(cp->whoname, in->uname, sizeof(cp->whoname)); + cp->whotime = time(); + if(cons.flags & attachflag) + print("9p1: attach %s %T to \"%s\" C%d\n", + cp->whoname, cp->whotime, fs->name, cp->chan); + +out: + if((cons.flags & attachflag) && ou->err) + print("9p1: attach %s %T SUCK EGGS --- %s\n", + in->uname, time(), errstr9p[ou->err]); + if(p) + putbuf(p); + if(f) { + qunlock(f); + if(ou->err) + freefp(f); + } +} + +static void +f_clone(Chan *cp, Fcall *in, Fcall *ou) +{ + File *f1, *f2; + Wpath *p; + int fid, fid1; + + if(CHAT(cp)) { + print("c_clone %d\n", cp->chan); + print(" old fid = %d\n", in->fid); + print(" new fid = %d\n", in->newfid); + } + + fid = in->fid; + fid1 = in->newfid; + + f1 = 0; + f2 = 0; + if(fid < fid1) { + f1 = filep(cp, fid, 0); + f2 = filep(cp, fid1, 1); + } else + if(fid1 < fid) { + f2 = filep(cp, fid1, 1); + f1 = filep(cp, fid, 0); + } + if(!f1 || !f2) { + ou->err = Efid; + goto out; + } + + + f2->fs = f1->fs; + f2->addr = f1->addr; + f2->open = f1->open & ~FREMOV; + f2->uid = f1->uid; + f2->slot = f1->slot; + f2->qid = f1->qid; + + freewp(f2->wpath); + lock(&wpathlock); + f2->wpath = f1->wpath; + for(p = f2->wpath; p; p = p->up) + p->refs++; + unlock(&wpathlock); + +out: + ou->fid = fid; + if(f1) + qunlock(f1); + if(f2) { + qunlock(f2); + if(ou->err) + freefp(f2); + } +} + +static void +f_walk(Chan *cp, Fcall *in, Fcall *ou) +{ + Iobuf *p, *p1; + Dentry *d, *d1; + File *f; + Wpath *w; + int slot; + Off addr, qpath; + + if(CHAT(cp)) { + print("c_walk %d\n", cp->chan); + print(" fid = %d\n", in->fid); + print(" name = %s\n", in->name); + } + + ou->fid = in->fid; + ou->qid = QID9P1(0,0); + p = 0; + f = filep(cp, in->fid, 0); + if(!f) { + ou->err = Efid; + goto out; + } + p = getbuf(f->fs->dev, f->addr, Bread); + d = getdir(p, f->slot); + if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) { + ou->err = Ealloc; + goto out; + } + if(!(d->mode & DDIR)) { + ou->err = Edir1; + goto out; + } + if(ou->err = mkqidcmp(&f->qid, d)) + goto out; + if(cp != cons.chan && iaccess(f, d, DEXEC)) { + ou->err = Eaccess; + goto out; + } + accessdir(p, d, FREAD, f->uid); + if(strcmp(in->name, ".") == 0) + goto setdot; + if(strcmp(in->name, "..") == 0) { + if(f->wpath == 0) + goto setdot; + putbuf(p); + p = 0; + addr = f->wpath->addr; + slot = f->wpath->slot; + p1 = getbuf(f->fs->dev, addr, Bread); + d1 = getdir(p1, slot); + if(!d1 || checktag(p1, Tdir, QPNONE) || !(d1->mode & DALLOC)) { + if(p1) + putbuf(p1); + ou->err = Ephase; + goto out; + } + lock(&wpathlock); + f->wpath->refs--; + f->wpath = f->wpath->up; + unlock(&wpathlock); + goto found; + } + for(addr=0;; addr++) { + if(p == 0) { + p = getbuf(f->fs->dev, f->addr, Bread); + d = getdir(p, f->slot); + if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) { + ou->err = Ealloc; + goto out; + } + } + qpath = d->qid.path; + p1 = dnodebuf1(p, d, addr, 0, f->uid); + p = 0; + if(!p1 || checktag(p1, Tdir, qpath) ) { + if(p1) + putbuf(p1); + ou->err = Eentry; + goto out; + } + for(slot=0; slotmode & DALLOC)) + continue; + if(strncmp(in->name, d1->name, sizeof(in->name)) != 0) + continue; + /* + * update walk path + */ + w = newwp(); + if(!w) { + ou->err = Ewalk; + putbuf(p1); + goto out; + } + w->addr = f->addr; + w->slot = f->slot; + w->up = f->wpath; + f->wpath = w; + slot += DIRPERBUF*addr; + goto found; + } + putbuf(p1); + } + +found: + f->addr = p1->addr; + mkqid(&f->qid, d1, 1); + putbuf(p1); + f->slot = slot; + +setdot: + mkqid9p1(&ou->qid, &f->qid); + f->open = 0; + +out: + if(p) + putbuf(p); + if(f) + qunlock(f); +} + +static void +f_open(Chan *cp, Fcall *in, Fcall *ou) +{ + Iobuf *p; + Dentry *d; + File *f; + Tlock *t; + Qid qid; + int ro, fmod, wok; + + if(CHAT(cp)) { + print("c_open %d\n", cp->chan); + print(" fid = %d\n", in->fid); + print(" mode = %o\n", in->mode); + } + + wok = 0; + if(cp == cons.chan || writeallow) + wok = 1; + + p = 0; + f = filep(cp, in->fid, 0); + if(!f) { + ou->err = Efid; + goto out; + } + + /* + * if remove on close, check access here + */ + ro = f->fs->dev->type == Devro; + if(in->mode & ORCLOSE) { + if(ro) { + ou->err = Eronly; + goto out; + } + /* + * check on parent directory of file to be deleted + */ + if(f->wpath == 0 || f->wpath->addr == f->addr) { + ou->err = Ephase; + goto out; + } + p = getbuf(f->fs->dev, f->wpath->addr, Bread); + d = getdir(p, f->wpath->slot); + if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) { + ou->err = Ephase; + goto out; + } + if(iaccess(f, d, DWRITE)) { + ou->err = Eaccess; + goto out; + } + putbuf(p); + } + p = getbuf(f->fs->dev, f->addr, Bread); + d = getdir(p, f->slot); + if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) { + ou->err = Ealloc; + goto out; + } + if(ou->err = mkqidcmp(&f->qid, d)) + goto out; + mkqid(&qid, d, 1); + switch(in->mode & 7) { + + case OREAD: + if(iaccess(f, d, DREAD) && !wok) + goto badaccess; + fmod = FREAD; + break; + + case OWRITE: + if((d->mode & DDIR) || + (iaccess(f, d, DWRITE) && !wok)) + goto badaccess; + if(ro) { + ou->err = Eronly; + goto out; + } + fmod = FWRITE; + break; + + case ORDWR: + if((d->mode & DDIR) || + (iaccess(f, d, DREAD) && !wok) || + (iaccess(f, d, DWRITE) && !wok)) + goto badaccess; + if(ro) { + ou->err = Eronly; + goto out; + } + fmod = FREAD+FWRITE; + break; + + case OEXEC: + if((d->mode & DDIR) || + (iaccess(f, d, DEXEC) && !wok)) + goto badaccess; + fmod = FREAD; + break; + + default: + ou->err = Emode; + goto out; + } + if(in->mode & OTRUNC) { + if((d->mode & DDIR) || + (iaccess(f, d, DWRITE) && !wok)) + goto badaccess; + if(ro) { + ou->err = Eronly; + goto out; + } + } + t = 0; + if(d->mode & DLOCK) { + t = tlocked(p, d); + if(t == nil) { + ou->err = Elocked; + goto out; + } + } + if(in->mode & ORCLOSE) + fmod |= FREMOV; + f->open = fmod; + if(in->mode & OTRUNC) + if(!(d->mode & DAPND)) { + dtrunc(p, d, f->uid); + qid.vers = d->qid.version; + } + f->tlock = t; + if(t) + t->file = f; + f->lastra = 1; + mkqid9p1(&ou->qid, &qid); + goto out; + +badaccess: + ou->err = Eaccess; + f->open = 0; + +out: + if(p) + putbuf(p); + if(f) + qunlock(f); + ou->fid = in->fid; +} + +static void +f_create(Chan *cp, Fcall *in, Fcall *ou) +{ + Iobuf *p, *p1; + Dentry *d, *d1; + File *f; + int slot, slot1, fmod, wok; + Off addr, addr1, path; + Qid qid; + Tlock *t; + Wpath *w; + + if(CHAT(cp)) { + print("c_create %d\n", cp->chan); + print(" fid = %d\n", in->fid); + print(" name = %s\n", in->name); + print(" perm = %lx+%lo\n", (in->perm>>28)&0xf, + in->perm&0777); + print(" mode = %o\n", in->mode); + } + + wok = 0; + if(cp == cons.chan || writeallow) + wok = 1; + + p = 0; + f = filep(cp, in->fid, 0); + if(!f) { + ou->err = Efid; + goto out; + } + if(f->fs->dev->type == Devro) { + ou->err = Eronly; + goto out; + } + + p = getbuf(f->fs->dev, f->addr, Bread); + d = getdir(p, f->slot); + if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) { + ou->err = Ealloc; + goto out; + } + if(ou->err = mkqidcmp(&f->qid, d)) + goto out; + if(!(d->mode & DDIR)) { + ou->err = Edir2; + goto out; + } + if(iaccess(f, d, DWRITE) && !wok) { + ou->err = Eaccess; + goto out; + } + accessdir(p, d, FREAD, f->uid); + if(!strncmp(in->name, ".", sizeof(in->name)) || + !strncmp(in->name, "..", sizeof(in->name))) { + ou->err = Edot; + goto out; + } + if(checkname(in->name)) { + ou->err = Ename; + goto out; + } + addr1 = 0; + slot1 = 0; /* set */ + for(addr=0;; addr++) { + p1 = dnodebuf(p, d, addr, 0, f->uid); + if(!p1) { + if(addr1) + break; + p1 = dnodebuf(p, d, addr, Tdir, f->uid); + } + if(p1 == 0) { + ou->err = Efull; + goto out; + } + if(checktag(p1, Tdir, d->qid.path)) { + putbuf(p1); + goto phase; + } + for(slot=0; slotmode & DALLOC)) { + if(!addr1) { + addr1 = p1->addr; + slot1 = slot + addr*DIRPERBUF; + } + continue; + } + if(!strncmp(in->name, d1->name, sizeof(in->name))) { + putbuf(p1); + ou->err = Eexist; + goto out; + } + } + putbuf(p1); + } + switch(in->mode & 7) { + case OEXEC: + case OREAD: /* seems only useful to make directories */ + fmod = FREAD; + break; + + case OWRITE: + fmod = FWRITE; + break; + + case ORDWR: + fmod = FREAD+FWRITE; + break; + + default: + ou->err = Emode; + goto out; + } + if(in->perm & PDIR) + if((in->mode & OTRUNC) || (in->perm & PAPND) || (fmod & FWRITE)) + goto badaccess; + /* + * do it + */ + path = qidpathgen(f->fs->dev); + p1 = getbuf(f->fs->dev, addr1, Bread|Bimm|Bmod); + d1 = getdir(p1, slot1); + if(!d1 || checktag(p1, Tdir, d->qid.path)) { + if(p1) + putbuf(p1); + goto phase; + } + if(d1->mode & DALLOC) { + putbuf(p1); + goto phase; + } + + strncpy(d1->name, in->name, sizeof(in->name)); + if(cp == cons.chan) { + d1->uid = cons.uid; + d1->gid = cons.gid; + } else { + d1->uid = f->uid; + d1->gid = d->gid; + in->perm &= d->mode | ~0666; + if(in->perm & PDIR) + in->perm &= d->mode | ~0777; + } + d1->qid.path = path; + d1->qid.version = 0; + d1->mode = DALLOC | (in->perm & 0777); + if(in->perm & PDIR) { + d1->mode |= DDIR; + d1->qid.path |= QPDIR; + } + if(in->perm & PAPND) + d1->mode |= DAPND; + t = 0; + if(in->perm & PLOCK) { + d1->mode |= DLOCK; + t = tlocked(p1, d1); + /* if nil, out of tlock structures */ + } + accessdir(p1, d1, FWRITE, f->uid); + mkqid(&qid, d1, 0); + putbuf(p1); + accessdir(p, d, FWRITE, f->uid); + + /* + * do a walk to new directory entry + */ + w = newwp(); + if(!w) { + ou->err = Ewalk; + goto out; + } + w->addr = f->addr; + w->slot = f->slot; + w->up = f->wpath; + f->wpath = w; + f->qid = qid; + f->tlock = t; + if(t) + t->file = f; + f->lastra = 1; + if(in->mode & ORCLOSE) + fmod |= FREMOV; + f->open = fmod; + f->addr = addr1; + f->slot = slot1; + mkqid9p1(&ou->qid, &qid); + goto out; + +badaccess: + ou->err = Eaccess; + goto out; + +phase: + ou->err = Ephase; + +out: + if(p) + putbuf(p); + if(f) + qunlock(f); + ou->fid = in->fid; +} + +static void +f_read(Chan *cp, Fcall *in, Fcall *ou) +{ + Iobuf *p, *p1; + File *f; + Dentry *d, *d1; + Tlock *t; + Off addr, offset; + Timet tim; + int nread, count, n, o, slot; + + if(CHAT(cp)) { + print("c_read %d\n", cp->chan); + print(" fid = %d\n", in->fid); + print(" offset = %lld\n", (Wideoff)in->offset); + print(" count = %ld\n", in->count); + } + + p = 0; + count = in->count; + offset = in->offset; + nread = 0; + f = filep(cp, in->fid, 0); + if(!f) { + ou->err = Efid; + goto out; + } + if(!(f->open & FREAD)) { + ou->err = Eopen; + goto out; + } + if(count < 0 || count > MAXDAT) { + ou->err = Ecount; + goto out; + } + if(offset < 0) { + ou->err = Eoffset; + goto out; + } + p = getbuf(f->fs->dev, f->addr, Bread); + d = getdir(p, f->slot); + if(!d || !(d->mode & DALLOC)) { + ou->err = Ealloc; + goto out; + } + if(ou->err = mkqidcmp(&f->qid, d)) + goto out; + if(t = f->tlock) { + tim = toytime(); + if(t->time < tim || t->file != f) { + ou->err = Ebroken; + goto out; + } + /* renew the lock */ + t->time = tim + TLOCK; + } + accessdir(p, d, FREAD, f->uid); + if(d->mode & DDIR) { + addr = 0; + goto dread; + } + + /* XXXX terrible hack to get at raw data XXXX */ + if(rawreadok && strncmp(d->name, "--raw--", 7) == 0) { + Device *dev; + Devsize boff, bsize; + + dev = p->dev; + putbuf(p); + p = 0; + + boff = number(d->name + 7, 0, 10) * 100000; + if(boff < 0) + boff = 0; + if(boff > devsize(dev)) + boff = devsize(dev); + bsize = devsize(dev) - boff; + + if(offset+count >= 100000*RBUFSIZE) + count = 100000*RBUFSIZE - offset; + + if((offset+count)/RBUFSIZE >= bsize) { + /* will not overflow */ + count = bsize*RBUFSIZE - offset; + } + + while(count > 0) { + addr = offset / RBUFSIZE; + addr += boff; + o = offset % RBUFSIZE; + n = RBUFSIZE - o; + if(n > count) + n = count; + + p1 = getbuf(dev, addr, Bread); + if(p1) { + memmove(ou->data+nread, p1->iobuf+o, n); + putbuf(p1); + } else + memset(ou->data+nread, 0, n); + count -= n; + nread += n; + offset += n; + } + goto out; + } + + if(offset+count > d->size) + count = d->size - offset; + while(count > 0) { + if(p == 0) { + p = getbuf(f->fs->dev, f->addr, Bread); + d = getdir(p, f->slot); + if(!d || !(d->mode & DALLOC)) { + ou->err = Ealloc; + goto out; + } + } + addr = offset / BUFSIZE; + f->lastra = dbufread(p, d, addr, f->lastra, f->uid); + o = offset % BUFSIZE; + n = BUFSIZE - o; + if(n > count) + n = count; + p1 = dnodebuf1(p, d, addr, 0, f->uid); + p = 0; + if(p1) { + if(checktag(p1, Tfile, QPNONE)) { + ou->err = Ephase; + putbuf(p1); + goto out; + } + memmove(ou->data+nread, p1->iobuf+o, n); + putbuf(p1); + } else + memset(ou->data+nread, 0, n); + count -= n; + nread += n; + offset += n; + } + goto out; + +dread: + for (;;) { + if(p == 0) { + p = getbuf(f->fs->dev, f->addr, Bread); + d = getdir(p, f->slot); + if(!d || !(d->mode & DALLOC)) { + ou->err = Ealloc; + goto out; + } + } + p1 = dnodebuf1(p, d, addr, 0, f->uid); + p = 0; + if(!p1) + goto out; + if(checktag(p1, Tdir, QPNONE)) { + ou->err = Ephase; + putbuf(p1); + goto out; + } + n = DIRREC; + for(slot=0; slotmode & DALLOC)) + continue; + if(offset >= n) { + offset -= n; + continue; + } + if(count < n) { + putbuf(p1); + goto out; + } + if(convD2M9p1(d1, ou->data+nread) != n) + print("9p1: dirread convD2M1990\n"); + nread += n; + count -= n; + } + putbuf(p1); + addr++; + } +out: + count = in->count - nread; + if(count > 0) + memset(ou->data+nread, 0, count); + if(p) + putbuf(p); + if(f) + qunlock(f); + ou->fid = in->fid; + ou->count = nread; + if(CHAT(cp)) + print(" nread = %d\n", nread); +} + +static void +f_write(Chan *cp, Fcall *in, Fcall *ou) +{ + Iobuf *p, *p1; + Dentry *d; + File *f; + Tlock *t; + Off offset, addr, qpath; + Timet tim; + int count, nwrite, o, n; + + if(CHAT(cp)) { + print("c_write %d\n", cp->chan); + print(" fid = %d\n", in->fid); + print(" offset = %lld\n", (Wideoff)in->offset); + print(" count = %ld\n", in->count); + } + + offset = in->offset; + count = in->count; + nwrite = 0; + p = 0; + f = filep(cp, in->fid, 0); + if(!f) { + ou->err = Efid; + goto out; + } + if(!(f->open & FWRITE)) { + ou->err = Eopen; + goto out; + } + if(f->fs->dev->type == Devro) { + ou->err = Eronly; + goto out; + } + if(count < 0 || count > MAXDAT) { + ou->err = Ecount; + goto out; + } + if(offset < 0) { + ou->err = Eoffset; + goto out; + } + p = getbuf(f->fs->dev, f->addr, Bread|Bmod); + d = getdir(p, f->slot); + if(!d || !(d->mode & DALLOC)) { + ou->err = Ealloc; + goto out; + } + if(ou->err = mkqidcmp(&f->qid, d)) + goto out; + if(t = f->tlock) { + tim = toytime(); + if(t->time < tim || t->file != f) { + ou->err = Ebroken; + goto out; + } + /* renew the lock */ + t->time = tim + TLOCK; + } + accessdir(p, d, FWRITE, f->uid); + if(d->mode & DAPND) + offset = d->size; + if(offset+count > d->size) + d->size = offset+count; + while(count > 0) { + if(p == 0) { + p = getbuf(f->fs->dev, f->addr, Bread|Bmod); + d = getdir(p, f->slot); + if(!d || !(d->mode & DALLOC)) { + ou->err = Ealloc; + goto out; + } + } + addr = offset / BUFSIZE; + o = offset % BUFSIZE; + n = BUFSIZE - o; + if(n > count) + n = count; + qpath = d->qid.path; + p1 = dnodebuf1(p, d, addr, Tfile, f->uid); + p = 0; + if(p1 == 0) { + ou->err = Efull; + goto out; + } + if(checktag(p1, Tfile, qpath)) { + putbuf(p1); + ou->err = Ephase; + goto out; + } + memmove(p1->iobuf+o, in->data+nwrite, n); + p1->flags |= Bmod; + putbuf(p1); + count -= n; + nwrite += n; + offset += n; + } + if(CHAT(cp)) + print(" nwrite = %d\n", nwrite); + +out: + if(p) + putbuf(p); + if(f) + qunlock(f); + ou->fid = in->fid; + ou->count = nwrite; +} + +int +doremove(File *f, int wok) +{ + Iobuf *p, *p1; + Dentry *d, *d1; + Off addr; + int slot, err; + + p = 0; + p1 = 0; + if(f->fs->dev->type == Devro) { + err = Eronly; + goto out; + } + /* + * check on parent directory of file to be deleted + */ + if(f->wpath == 0 || f->wpath->addr == f->addr) { + err = Ephase; + goto out; + } + p1 = getbuf(f->fs->dev, f->wpath->addr, Bread); + d1 = getdir(p1, f->wpath->slot); + if(!d1 || checktag(p1, Tdir, QPNONE) || !(d1->mode & DALLOC)) { + err = Ephase; + goto out; + } + if(iaccess(f, d1, DWRITE) && !wok) { + err = Eaccess; + goto out; + } + accessdir(p1, d1, FWRITE, f->uid); + putbuf(p1); + p1 = 0; + + /* + * check on file to be deleted + */ + p = getbuf(f->fs->dev, f->addr, Bread); + d = getdir(p, f->slot); + if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) { + err = Ealloc; + goto out; + } + if(err = mkqidcmp(&f->qid, d)) + goto out; + + /* + * if deleting a directory, make sure it is empty + */ + if((d->mode & DDIR)) + for(addr=0;; addr++) { + p1 = dnodebuf(p, d, addr, 0, f->uid); + if(!p1) + break; + if(checktag(p1, Tdir, d->qid.path)) { + err = Ephase; + goto out; + } + for(slot=0; slotmode & DALLOC)) + continue; + err = Eempty; + goto out; + } + putbuf(p1); + } + + /* + * do it + */ + dtrunc(p, d, f->uid); + memset(d, 0, sizeof(Dentry)); + settag(p, Tdir, QPNONE); + +out: + if(p1) + putbuf(p1); + if(p) + putbuf(p); + return err; +} + +static int +doclunk(File* f, int remove, int wok) +{ + Tlock *t; + int err; + + err = 0; + if(t = f->tlock) { + if(t->file == f) + t->time = 0; /* free the lock */ + f->tlock = 0; + } + if(remove) + err = doremove(f, wok); + f->open = 0; + freewp(f->wpath); + freefp(f); + + return err; +} + +static void +f_clunk(Chan *cp, Fcall *in, Fcall *ou) +{ + File *f; + + if(CHAT(cp)) { + print("c_clunk %d\n", cp->chan); + print(" fid = %d\n", in->fid); + } + + f = filep(cp, in->fid, 0); + if(!f) + ou->err = Efid; + else { + doclunk(f, f->open & FREMOV, 0); + qunlock(f); + } + ou->fid = in->fid; +} + +static void +f_remove(Chan *cp, Fcall *in, Fcall *ou) +{ + File *f; + + if(CHAT(cp)) { + print("c_remove %d\n", cp->chan); + print(" fid = %d\n", in->fid); + } + + f = filep(cp, in->fid, 0); + if(!f) + ou->err = Efid; + else { + ou->err = doclunk(f, 1, cp==cons.chan); + qunlock(f); + } + ou->fid = in->fid; +} + +static void +f_stat(Chan *cp, Fcall *in, Fcall *ou) +{ + Iobuf *p; + Dentry *d; + File *f; + + if(CHAT(cp)) { + print("c_stat %d\n", cp->chan); + print(" fid = %d\n", in->fid); + } + + p = 0; + memset(ou->stat, 0, sizeof(ou->stat)); + f = filep(cp, in->fid, 0); + if(!f) { + ou->err = Efid; + goto out; + } + p = getbuf(f->fs->dev, f->addr, Bread); + d = getdir(p, f->slot); + if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) { + ou->err = Ealloc; + goto out; + } + if(ou->err = mkqidcmp(&f->qid, d)) + goto out; + if(d->qid.path == QPROOT) /* stat of root gives time */ + d->atime = time(); + if(convD2M9p1(d, ou->stat) != DIRREC) + print("9p1: stat convD2M\n"); + +out: + if(p) + putbuf(p); + if(f) + qunlock(f); + ou->fid = in->fid; +} + +static void +f_wstat(Chan *cp, Fcall *in, Fcall *ou) +{ + Iobuf *p, *p1; + Dentry *d, *d1, xd; + File *f; + int slot; + Off addr; + + if(CHAT(cp)) { + print("c_wstat %d\n", cp->chan); + print(" fid = %d\n", in->fid); + } + + p = 0; + p1 = 0; + d1 = 0; + f = filep(cp, in->fid, 0); + if(!f) { + ou->err = Efid; + goto out; + } + if(f->fs->dev->type == Devro) { + ou->err = Eronly; + goto out; + } + + /* + * first get parent + */ + if(f->wpath) { + p1 = getbuf(f->fs->dev, f->wpath->addr, Bread); + d1 = getdir(p1, f->wpath->slot); + if(!d1 || checktag(p1, Tdir, QPNONE) || !(d1->mode & DALLOC)) { + ou->err = Ephase; + goto out; + } + } + + p = getbuf(f->fs->dev, f->addr, Bread); + d = getdir(p, f->slot); + if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) { + ou->err = Ealloc; + goto out; + } + if(ou->err = mkqidcmp(&f->qid, d)) + goto out; + + convM2D9p1(in->stat, &xd); + if(CHAT(cp)) { + print(" d.name = %s\n", xd.name); + print(" d.uid = %d\n", xd.uid); + print(" d.gid = %d\n", xd.gid); + print(" d.mode = %o\n", xd.mode); + } + + /* + * if user none, + * cant do anything + */ + if(f->uid == 0) { + ou->err = Eaccess; + goto out; + } + + /* + * if chown, + * must be god + */ + if(xd.uid != d->uid && !wstatallow) { /* set to allow chown during boot */ + ou->err = Ewstatu; + goto out; + } + + /* + * if chgroup, + * must be either + * a) owner and in new group + * b) leader of both groups + */ + if (xd.gid != d->gid && + (!wstatallow && !writeallow && /* set to allow chgrp during boot */ + (d->uid != f->uid || !ingroup(f->uid, xd.gid)) && + (!leadgroup(f->uid, xd.gid) || !leadgroup(f->uid, d->gid)))) { + ou->err = Ewstatg; + goto out; + } + + /* + * if rename, + * must have write permission in parent + */ + if (strncmp(d->name, xd.name, sizeof(d->name)) != 0) { + if (checkname(xd.name) || !d1 || + strcmp(xd.name, ".") == 0 || strcmp(xd.name, "..") == 0) { + ou->err = Ename; + goto out; + } + + /* + * drop entry to prevent lock, then + * check that destination name is unique, + */ + putbuf(p); + for(addr=0;; addr++) { + p = dnodebuf(p1, d1, addr, 0, f->uid); + if(!p) + break; + if(checktag(p, Tdir, d1->qid.path)) { + putbuf(p); + continue; + } + for(slot=0; slotmode & DALLOC)) + continue; + if(!strncmp(xd.name, d->name, sizeof(xd.name))) { + ou->err = Eexist; + goto out; + } + } + putbuf(p); + } + + /* + * reacquire entry + */ + p = getbuf(f->fs->dev, f->addr, Bread); + d = getdir(p, f->slot); + if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) { + ou->err = Ephase; + goto out; + } + + if (!wstatallow && !writeallow && /* set to allow rename during boot */ + (!d1 || iaccess(f, d1, DWRITE))) { + ou->err = Eaccess; + goto out; + } + } + + /* + * if mode/time, either + * a) owner + * b) leader of either group + */ + if (d->mtime != xd.mtime || + ((d->mode^xd.mode) & (DAPND|DLOCK|0777))) + if (!wstatallow && /* set to allow chmod during boot */ + d->uid != f->uid && + !leadgroup(f->uid, xd.gid) && + !leadgroup(f->uid, d->gid)) { + ou->err = Ewstatu; + goto out; + } + d->mtime = xd.mtime; + d->uid = xd.uid; + d->gid = xd.gid; + d->mode = (xd.mode & (DAPND|DLOCK|0777)) | (d->mode & (DALLOC|DDIR)); + + strncpy(d->name, xd.name, sizeof(d->name)); + accessdir(p, d, FREAD, f->uid); + +out: + if(p) + putbuf(p); + if(p1) + putbuf(p1); + if(f) + qunlock(f); + ou->fid = in->fid; +} + +static void +f_clwalk(Chan *cp, Fcall *in, Fcall *ou) +{ + int er, fid; + + if(CHAT(cp)) + print("c_clwalk macro\n"); + + f_clone(cp, in, ou); /* sets tag, fid */ + if(ou->err) + return; + fid = in->fid; + in->fid = in->newfid; + f_walk(cp, in, ou); /* sets tag, fid, qid */ + er = ou->err; + if(er == Eentry) { + /* + * if error is "no entry" + * return non error and fid + */ + ou->err = 0; + f_clunk(cp, in, ou); /* sets tag, fid */ + ou->err = 0; + ou->fid = fid; + if(CHAT(cp)) + print(" error: %s\n", errstr9p[er]); + } else if(er) { + /* + * if any other error + * return an error + */ + ou->err = 0; + f_clunk(cp, in, ou); /* sets tag, fid */ + ou->err = er; + } + /* + * non error + * return newfid + */ +} + +void +(*call9p1[MAXSYSCALL])(Chan*, Fcall*, Fcall*) = +{ + [Tnop] f_nop, + [Tosession] f_session, + [Tsession] f_session, + [Tflush] f_flush, + [Toattach] f_attach, + [Tattach] f_attach, + [Tclone] f_clone, + [Twalk] f_walk, + [Topen] f_open, + [Tcreate] f_create, + [Tread] f_read, + [Twrite] f_write, + [Tclunk] f_clunk, + [Tremove] f_remove, + [Tstat] f_stat, + [Twstat] f_wstat, + [Tclwalk] f_clwalk, +}; + +int +error9p1(Chan* cp, Msgbuf* mb) +{ + Msgbuf *mb1; + + print("type=%d count=%d\n", mb->data[0], mb->count); + print(" %.2x %.2x %.2x %.2x\n", + mb->data[1]&0xff, mb->data[2]&0xff, + mb->data[3]&0xff, mb->data[4]&0xff); + print(" %.2x %.2x %.2x %.2x\n", + mb->data[5]&0xff, mb->data[6]&0xff, + mb->data[7]&0xff, mb->data[8]&0xff); + print(" %.2x %.2x %.2x %.2x\n", + mb->data[9]&0xff, mb->data[10]&0xff, + mb->data[11]&0xff, mb->data[12]&0xff); + + mb1 = mballoc(3, cp, Mbreply4); + mb1->data[0] = Rnop; /* your nop was ok */ + mb1->data[1] = ~0; + mb1->data[2] = ~0; + mb1->count = 3; + mb1->param = mb->param; + send(cp->reply, mb1); + + return 1; +} + +int +serve9p1(Msgbuf* mb) +{ + int t, n; + Chan *cp; + Msgbuf *mb1; + Fcall fi, fo; + + cp = mb->chan; + if(convM2S9p1(mb->data, &fi, mb->count) == 0){ + if(cp->protocol == nil) + return 0; + print("9p1: bad M2S conversion\n"); + return error9p1(cp, mb); + } + + t = fi.type; + if(t < 0 || t >= MAXSYSCALL || (t&1) || !call9p1[t]) { + print("9p1: bad message type\n"); + return error9p1(cp, mb); + } + + /* + * allocate reply message + */ + if(t == Tread) { + mb1 = mballoc(MAXMSG+MAXDAT, cp, Mbreply2); + fo.data = (char*)(mb1->data + 8); + } else + mb1 = mballoc(MAXMSG, cp, Mbreply3); + + /* + * call the file system + */ + cons.work[0].count++; + cons.work[1].count++; + cons.work[2].count++; + cp->work.count++; + cons.rate[0].count += mb->count; + cons.rate[1].count += mb->count; + cons.rate[2].count += mb->count; + cp->rate.count += mb->count; + fo.err = 0; + + (*call9p1[t])(cp, &fi, &fo); + + fo.type = t+1; + fo.tag = fi.tag; + + if(fo.err) { + if(cons.flags&errorflag) + print(" type %d: error: %s\n", t, + errstr9p[fo.err]); + if(CHAT(cp)) + print(" error: %s\n", errstr9p[fo.err]); + fo.type = Rerror; + strncpy(fo.ename, errstr9p[fo.err], sizeof(fo.ename)); + } + + n = convS2M9p1(&fo, mb1->data); + if(n == 0) { + print("9p1: bad S2M conversion\n"); + mbfree(mb1); + return error9p1(cp, mb); + } + mb1->count = n; + mb1->param = mb->param; + cons.rate[0].count += n; + cons.rate[1].count += n; + cons.rate[2].count += n; + cp->rate.count += n; + send(cp->reply, mb1); + + return 1; +} diff -Nru /sys/src/fs/port/9p1.h /sys/src/fs/port/9p1.h --- /sys/src/fs/port/9p1.h Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/port/9p1.h Tue Nov 1 00:00:00 2011 @@ -0,0 +1,114 @@ +#include + +#define DIRREC 116 /* size of a directory ascii record */ +#define ERRREC 64 /* size of a error record */ + +typedef struct Fcall Fcall; + +struct Fcall +{ + char type; + ushort fid; + short err; + short tag; + union + { + struct + { + short uid; /* T-Userstr [obs.] */ + short oldtag; /* T-nFlush */ + Qid9p1 qid; /* R-Attach, R-Clwalk, R-Walk, + * R-Open, R-Create */ + char rauth[AUTHENTLEN]; /* R-attach */ + }; + struct + { + char uname[NAMELEN]; /* T-nAttach */ + char aname[NAMELEN]; /* T-nAttach */ + char ticket[TICKETLEN]; /* T-attach */ + char auth[AUTHENTLEN]; /* T-attach */ + }; + struct + { + char ename[ERRREC]; /* R-nError */ + char chal[CHALLEN]; /* T-session, R-session */ + char authid[NAMELEN]; /* R-session */ + char authdom[DOMLEN]; /* R-session */ + }; + struct + { + char name[NAMELEN]; /* T-Walk, T-Clwalk, T-Create, T-Remove */ + long perm; /* T-Create */ + ushort newfid; /* T-Clone, T-Clwalk */ + char mode; /* T-Create, T-Open */ + }; + struct + { + Off offset; /* T-Read, T-Write */ + long count; /* T-Read, T-Write, R-Read */ + char* data; /* T-Write, R-Read */ + }; + struct + { + char stat[DIRREC]; /* T-Wstat, R-Stat */ + }; + }; +}; + +/* + * P9 protocol message types + */ +enum +{ + Tnop = 50, + Rnop, + Tosession = 52, + Rosession, + Terror = 54, /* illegal */ + Rerror, + Tflush = 56, + Rflush, + Toattach = 58, + Roattach, + Tclone = 60, + Rclone, + Twalk = 62, + Rwalk, + Topen = 64, + Ropen, + Tcreate = 66, + Rcreate, + Tread = 68, + Rread, + Twrite = 70, + Rwrite, + Tclunk = 72, + Rclunk, + Tremove = 74, + Rremove, + Tstat = 76, + Rstat, + Twstat = 78, + Rwstat, + Tclwalk = 80, + Rclwalk, + Tauth = 82, /* illegal */ + Rauth, /* illegal */ + Tsession = 84, + Rsession, + Tattach = 86, + Rattach, + + MAXSYSCALL +}; + +int convA2M9p1(Authenticator*, char*, char*); +void convM2A9p1(char*, Authenticator*, char*); +void convM2T9p1(char*, Ticket*, char*); +int convD2M9p1(Dentry*, char*); +int convM2D9p1(char*, Dentry*); +int convM2S9p1(uchar*, Fcall*, int); +int convS2M9p1(Fcall*, uchar*); +void fcall9p1(Chan*, Fcall*, Fcall*); + +void (*call9p1[MAXSYSCALL])(Chan*, Fcall*, Fcall*); diff -Nru /sys/src/fs/port/9p1lib.c /sys/src/fs/port/9p1lib.c --- /sys/src/fs/port/9p1lib.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/port/9p1lib.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,525 @@ +#include "all.h" + +/* BUG transition */ +int client9p = 2; +int kernel9p = 2; + +#include "9p1.h" + +#define CHAR(x) *p++ = f->x +#define SHORT(x) { ulong vvv = f->x; *p++ = vvv; *p++ = vvv>>8; } +#define LONGINT(q) {*p++ = (q); *p++ = (q)>>8; *p++ = (q)>>16; *p++ = (q)>>24;} +#define LONG(x) { ulong vvv = f->x; LONGINT(vvv); } +#define VLONG(x) { \ + uvlong q = f->x; \ + *p++ = (q)>> 0; *p++ = (q)>> 8; *p++ = (q)>>16; *p++ = (q)>>24; \ + *p++ = (q)>>32; *p++ = (q)>>40; *p++ = (q)>>48; *p++ = (q)>>56; \ + } + +#define BYTES(x,n) memmove(p, f->x, n); p += n +#define STRING(x,n) strncpy((char*)p, f->x, n); p += n + +int +convS2M9p1(Fcall *f, uchar *ap) +{ + uchar *p; + int t; + + p = ap; + CHAR(type); + t = f->type; + SHORT(tag); + switch(t) + { + default: + print("convS2M9p1: bad type: %d\n", t); + return 0; + + case Tnop: + case Tosession: + break; + + case Tsession: + BYTES(chal, sizeof(f->chal)); + break; + + case Tflush: + SHORT(oldtag); + break; + + case Tattach: + SHORT(fid); + STRING(uname, sizeof(f->uname)); + STRING(aname, sizeof(f->aname)); + BYTES(ticket, sizeof(f->ticket)); + BYTES(auth, sizeof(f->auth)); + break; + + case Toattach: + SHORT(fid); + STRING(uname, sizeof(f->uname)); + STRING(aname, sizeof(f->aname)); + BYTES(ticket, NAMELEN); + break; + + case Tclone: + SHORT(fid); + SHORT(newfid); + break; + + case Twalk: + SHORT(fid); + STRING(name, sizeof(f->name)); + break; + + case Tclwalk: + SHORT(fid); + SHORT(newfid); + STRING(name, sizeof(f->name)); + break; + + case Topen: + SHORT(fid); + CHAR(mode); + break; + + case Tcreate: + SHORT(fid); + STRING(name, sizeof(f->name)); + LONG(perm); + CHAR(mode); + break; + + case Tread: + SHORT(fid); + VLONG(offset); + SHORT(count); + break; + + case Twrite: + SHORT(fid); + VLONG(offset); + SHORT(count); + p++; + if((uchar*)p == (uchar*)f->data) { + p += f->count; + break; + } + BYTES(data, f->count); + break; + + case Tclunk: + case Tremove: + case Tstat: + SHORT(fid); + break; + + case Twstat: + SHORT(fid); + BYTES(stat, sizeof(f->stat)); + break; +/* + */ + case Rnop: + case Rosession: + case Rflush: + break; + + case Rsession: + BYTES(chal, sizeof(f->chal)); + BYTES(authid, sizeof(f->authid)); + BYTES(authdom, sizeof(f->authdom)); + break; + + case Rerror: + STRING(ename, sizeof(f->ename)); + break; + + case Rclone: + case Rclunk: + case Rremove: + case Rwstat: + SHORT(fid); + break; + + case Rwalk: + case Ropen: + case Rcreate: + case Rclwalk: + SHORT(fid); + LONG(qid.path); + LONG(qid.version); + break; + + case Rattach: + SHORT(fid); + LONG(qid.path); + LONG(qid.version); + BYTES(rauth, sizeof(f->rauth)); + break; + + case Roattach: + SHORT(fid); + LONG(qid.path); + LONG(qid.version); + break; + + case Rread: + SHORT(fid); + SHORT(count); + p++; + if((uchar*)p == (uchar*)f->data) { + p += f->count; + break; + } + BYTES(data, f->count); + break; + + case Rwrite: + SHORT(fid); + SHORT(count); + break; + + case Rstat: + SHORT(fid); + BYTES(stat, sizeof(f->stat)); + break; + } + return p - (uchar*)ap; +} + +/* + * buggery to give false qid for + * the top 2 levels of the dump fs + */ +static ulong +fakeqid9p1(Dentry *f) +{ + ulong q; + int c; + + q = f->qid.path; + if(q == (QPROOT|QPDIR)) { + c = f->name[0]; + if(c >= '0' && c <= '9') { + q = 3|QPDIR; + c = (c-'0')*10 + (f->name[1]-'0'); + if(c >= 1 && c <= 12) + q = 4|QPDIR; + } + } + return q; +} + +int +convD2M9p1(Dentry *f, char *ap) +{ + uchar *p; + ulong q; + + p = (uchar*)ap; + STRING(name, sizeof(f->name)); + + memset(p, 0, 2*NAMELEN); + uidtostr((char*)p, f->uid, 1); + p += NAMELEN; + + uidtostr((char*)p, f->gid, 1); + p += NAMELEN; + + q = fakeqid9p1(f); + LONGINT(q); + LONG(qid.version); + { + q = f->mode & 0x0fff; + if(f->mode & DDIR) + q |= PDIR; + if(f->mode & DAPND) + q |= PAPND; + if(f->mode & DLOCK) + q |= PLOCK; + LONGINT(q); + } + LONG(atime); + LONG(mtime); + VLONG(size); + LONGINT(0); + return p - (uchar*)ap; +} + +int +convA2M9p1(Authenticator *f, char *ap, char *key) +{ + int n; + uchar *p; + + p = (uchar*)ap; + CHAR(num); + BYTES(chal, CHALLEN); + LONG(id); + n = p - (uchar*)ap; + if(key) + encrypt(key, ap, n); + return n; +} + +#undef CHAR +#undef SHORT +#undef LONG +#undef LONGINT +#undef VLONG +#undef BYTES +#undef STRING + +#define CHAR(x) f->x = *p++ +#define SHORT(x) f->x = (p[0] | (p[1]<<8)); p += 2 +#define LONG(x) f->x = p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24); p += 4 +#define VLONG(x) { \ + f->x = (p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24)) | \ + (uvlong)(p[4] | (p[5]<<8) | (p[6]<<16) | (p[7]<<24)) << 32; \ + p += 8; \ +} + +#define BYTES(x,n) memmove(f->x, p, n); p += n +#define STRING(x,n) memmove(f->x, p, n); p += n + +int +convM2S9p1(uchar *ap, Fcall *f, int n) +{ + uchar *p; + int t; + + p = ap; + CHAR(type); + t = f->type; + SHORT(tag); + switch(t) + { + default: + /* + * only whine if it couldn't be a 9P2000 Tversion. + */ + if(t != 19 || ap[4] != 100) + print("convM2S9p1: bad type: %d\n", f->type); + return 0; + + case Tnop: + case Tosession: + break; + + case Tsession: + BYTES(chal, sizeof(f->chal)); + break; + + case Tflush: + SHORT(oldtag); + break; + + case Tattach: + SHORT(fid); + BYTES(uname, sizeof(f->uname)); + BYTES(aname, sizeof(f->aname)); + BYTES(ticket, sizeof(f->ticket)); + BYTES(auth, sizeof(f->auth)); + break; + + case Toattach: + SHORT(fid); + BYTES(uname, sizeof(f->uname)); + BYTES(aname, sizeof(f->aname)); + BYTES(ticket, NAMELEN); + break; + + case Tclone: + SHORT(fid); + SHORT(newfid); + break; + + case Twalk: + SHORT(fid); + BYTES(name, sizeof(f->name)); + break; + + case Tclwalk: + SHORT(fid); + SHORT(newfid); + BYTES(name, sizeof(f->name)); + break; + + case Tremove: + SHORT(fid); + break; + + case Topen: + SHORT(fid); + CHAR(mode); + break; + + case Tcreate: + SHORT(fid); + BYTES(name, sizeof(f->name)); + LONG(perm); + CHAR(mode); + break; + + case Tread: + SHORT(fid); + VLONG(offset); + SHORT(count); + break; + + case Twrite: + SHORT(fid); + VLONG(offset); + SHORT(count); + p++; + f->data = (char*)p; p += f->count; + break; + + case Tclunk: + case Tstat: + SHORT(fid); + break; + + case Twstat: + SHORT(fid); + BYTES(stat, sizeof(f->stat)); + break; + +/* + */ + case Rnop: + case Rosession: + break; + + case Rsession: + BYTES(chal, sizeof(f->chal)); + BYTES(authid, sizeof(f->authid)); + BYTES(authdom, sizeof(f->authdom)); + break; + + case Rerror: + BYTES(ename, sizeof(f->ename)); + break; + + case Rflush: + break; + + case Rclone: + case Rclunk: + case Rremove: + case Rwstat: + SHORT(fid); + break; + + case Rwalk: + case Rclwalk: + case Ropen: + case Rcreate: + SHORT(fid); + LONG(qid.path); + LONG(qid.version); + break; + + case Rattach: + SHORT(fid); + LONG(qid.path); + LONG(qid.version); + BYTES(rauth, sizeof(f->rauth)); + break; + + case Roattach: + SHORT(fid); + LONG(qid.path); + LONG(qid.version); + break; + + case Rread: + SHORT(fid); + SHORT(count); + p++; + f->data = (char*)p; p += f->count; + break; + + case Rwrite: + SHORT(fid); + SHORT(count); + break; + + case Rstat: + SHORT(fid); + BYTES(stat, sizeof(f->stat)); + break; + } + if((uchar*)ap+n == p) + return n; + return 0; +} + +int +convM2D9p1(char *ap, Dentry *f) +{ + uchar *p; + char str[NAMELEN]; + + p = (uchar*)ap; + BYTES(name, sizeof(f->name)); + + memmove(str, p, NAMELEN); + p += NAMELEN; + f->uid = strtouid(str); + + memmove(str, p, NAMELEN); + p += NAMELEN; + f->gid = strtouid(str); + + LONG(qid.path); + LONG(qid.version); + { + LONG(atime); + f->mode = (f->atime & 0x0fff) | DALLOC; + if(f->atime & PDIR) + f->mode |= DDIR; + if(f->atime & PAPND) + f->mode |= DAPND; + if(f->atime & PLOCK) + f->mode |= DLOCK; + } + LONG(atime); + LONG(mtime); + VLONG(size); + p += 4; + return p - (uchar*)ap; +} + +void +convM2A9p1(char *ap, Authenticator *f, char *key) +{ + uchar *p; + + if(key) + decrypt(key, ap, AUTHENTLEN); + p = (uchar*)ap; + CHAR(num); + BYTES(chal, CHALLEN); + LONG(id); + USED(p); +} + +void +convM2T9p1(char *ap, Ticket *f, char *key) +{ + uchar *p; + + if(key) + decrypt(key, ap, TICKETLEN); + p = (uchar*)ap; + CHAR(num); + BYTES(chal, CHALLEN); + STRING(cuid, NAMELEN); + f->cuid[NAMELEN-1] = 0; + STRING(suid, NAMELEN); + f->suid[NAMELEN-1] = 0; + BYTES(key, DESKEYLEN); + USED(p); +}; diff -Nru /sys/src/fs/port/9p2.c /sys/src/fs/port/9p2.c --- /sys/src/fs/port/9p2.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/port/9p2.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,1911 @@ +#include "all.h" + +/* + * stuff from /sys/include/libc.h for 9P2000 + */ +#define STATMAX 65535U /* max length of machine-independent stat structure */ +#define DIRMAX (sizeof(Dir)+STATMAX) /* max length of Dir structure */ +#define ERRMAX 128 /* max length of error string */ + +/* bits in Dir.mode */ +#define DMDIR 0x80000000 /* mode bit for directories */ +#define DMAPPEND 0x40000000 /* mode bit for append only files */ +#define DMEXCL 0x20000000 /* mode bit for exclusive use files */ +#define DMMOUNT 0x10000000 /* mode bit for mounted channel */ +#define DMREAD 0x4 /* mode bit for read permission */ +#define DMWRITE 0x2 /* mode bit for write permission */ +#define DMEXEC 0x1 /* mode bit for execute permission */ + +typedef +struct Dir { + /* system-modified data */ + ushort type; /* server type */ + uint dev; /* server subtype */ + /* file data */ + Qid qid; /* unique id from server */ + ulong mode; /* permissions */ + ulong atime; /* last read time */ + ulong mtime; /* last write time */ + vlong length; /* file length: see */ + char *name; /* last element of path */ + char *uid; /* owner name */ + char *gid; /* group name */ + char *muid; /* last modifier name */ +} Dir; + +#define MSIZE (MAXDAT+MAXMSG) + +#include "fcall.h" + +static int +mkmode9p1(ulong mode9p2) +{ + int mode; + + /* + * Assume this is for an allocated entry. + */ + mode = DALLOC|(mode9p2 & 0777); + if(mode9p2 & DMEXCL) + mode |= DLOCK; + if(mode9p2 & DMAPPEND) + mode |= DAPND; + if(mode9p2 & DMDIR) + mode |= DDIR; + + return mode; +} + +void +mkqid9p1(Qid9p1* qid9p1, Qid* qid) +{ + if(qid->path & 0xFFFFFFFF00000000LL) + panic("mkqid9p1: path %lluX\n", (Wideoff)qid->path); + qid9p1->path = qid->path & 0xFFFFFFFF; + if(qid->type & QTDIR) + qid9p1->path |= QPDIR; + qid9p1->version = qid->vers; +} + +static int +mktype9p2(int mode9p1) +{ + int type; + + type = 0; + if(mode9p1 & DLOCK) + type |= QTEXCL; + if(mode9p1 & DAPND) + type |= QTAPPEND; + if(mode9p1 & DDIR) + type |= QTDIR; + + return type; +} + +static ulong +mkmode9p2(int mode9p1) +{ + ulong mode; + + mode = mode9p1 & 0777; + if(mode9p1 & DLOCK) + mode |= DMEXCL; + if(mode9p1 & DAPND) + mode |= DMAPPEND; + if(mode9p1 & DDIR) + mode |= DMDIR; + + return mode; +} + +void +mkqid9p2(Qid* qid, Qid9p1* qid9p1, int mode9p1) +{ + qid->path = (ulong)(qid9p1->path & ~QPDIR); + qid->vers = qid9p1->version; + qid->type = mktype9p2(mode9p1); +} + +static int +mkdir9p2(Dir* dir, Dentry* dentry, void* strs) +{ + char *op, *p; + + memset(dir, 0, sizeof(Dir)); + mkqid(&dir->qid, dentry, 1); + dir->mode = mkmode9p2(dentry->mode); + dir->atime = dentry->atime; + dir->mtime = dentry->mtime; + dir->length = dentry->size; + + op = p = strs; + dir->name = p; + p += sprint(p, "%s", dentry->name)+1; + + dir->uid = p; + uidtostr(p, dentry->uid, 1); + p += strlen(p)+1; + + dir->gid = p; + uidtostr(p, dentry->gid, 1); + p += strlen(p)+1; + + dir->muid = p; + uidtostr(p, dentry->muid, 1); + p += strlen(p)+1; + + return p-op; +} + +static int +checkname9p2(char* name) +{ + char *p; + + /* + * Return error or 0 if OK. + */ + if(name == nil || *name == 0) + return Ename; + + for(p = name; *p != 0; p++){ + if(p-name >= NAMELEN-1) + return Etoolong; + if((*p & 0xFF) <= 040) + return Ename; + } + if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) + return Edot; + + return 0; +} + +static int +version(Chan* chan, Fcall* f, Fcall* r) +{ + if(chan->protocol != nil) + return Eversion; + + if(f->msize < MSIZE) + r->msize = f->msize; + else + r->msize = MSIZE; + + /* + * Should check the '.' stuff here. + */ + if(strcmp(f->version, VERSION9P) == 0){ + r->version = VERSION9P; + chan->protocol = serve9p2; + chan->msize = r->msize; + } + else + r->version = "unknown"; + + fileinit(chan); + return 0; +} + +struct +{ + Lock; + ulong hi; +} authpath; + +static int +auth(Chan* chan, Fcall* f, Fcall* r) +{ + char *aname; + File *file; + Filsys *fs; + int error; + + if(cons.flags & authdisableflag) + return Eauthdisabled; + + error = 0; + aname = f->aname; + + if(strcmp(f->uname, "none") == 0) + return Eauthnone; + + if(!aname[0]) /* default */ + aname = "main"; + file = filep(chan, f->afid, 1); + if(file == nil){ + error = Efidinuse; + goto out; + } + fs = fsstr(aname); + if(fs == nil){ + error = Ebadspc; + goto out; + } + lock(&authpath); + file->qid.path = authpath.hi++; + unlock(&authpath); + file->qid.type = QTAUTH; + file->qid.vers = 0; + file->fs = fs; + file->open = FREAD+FWRITE; + freewp(file->wpath); + file->wpath = 0; + file->auth = authnew(f->uname, f->aname); + if(file->auth == nil){ + error = Eauthfile; + goto out; + } + r->aqid = file->qid; + +out: + if((cons.flags & attachflag) && error) + print("9p2: auth %s %T SUCK EGGS --- %s\n", + f->uname, time(), errstr9p[error]); + if(file != nil){ + qunlock(file); + if(error) + freefp(file); + } + return error; +} + +int +authorize(Chan* chan, Fcall* f) +{ + File* af; + int uid = -1; + int db; + + db = cons.flags & authdebugflag; + + if(strcmp(f->uname, "none") == 0){ + uid = strtouid(f->uname); + if(db) + print("permission granted to none: uid %s = %d\n", f->uname, uid); + return uid; + } + + if(cons.flags & authdisableflag){ + uid = strtouid(f->uname); + if(db) + print("permission granted by authdisable uid %s = %d\n", f->uname, uid); + return uid; + } + + af = filep(chan, f->afid, 0); + if(af == nil){ + if(db) + print("authorize: af == nil\n"); + return -1; + } + if(af->auth == nil){ + if(db) + print("authorize: af->auth == nil\n"); + goto out; + } + if(strcmp(f->uname, authuname(af->auth)) != 0){ + if(db) + print("authorize: strcmp(f->uname, authuname(af->auth)) != 0\n"); + goto out; + } + if(strcmp(f->aname, authaname(af->auth)) != 0){ + if(db) + print("authorize: strcmp(f->aname, authaname(af->auth)) != 0\n"); + goto out; + } + uid = authuid(af->auth); + if(db) + print("authorize: uid is %d\n", uid); +out: + qunlock(af); + return uid; +} + +static int +attach(Chan* chan, Fcall* f, Fcall* r) +{ + char *aname; + Iobuf *p; + Dentry *d; + File *file; + Filsys *fs; + Off raddr; + int error, u; + + aname = f->aname; + if(!aname[0]) /* default */ + aname = "main"; + p = nil; + error = 0; + file = filep(chan, f->fid, 1); + if(file == nil){ + error = Efidinuse; + goto out; + } + + u = -1; + if(chan != cons.chan){ + if(noattach && strcmp(f->uname, "none")) { + error = Enoattach; + goto out; + } + u = authorize(chan, f); + if(u < 0){ + error = Ebadu; + goto out; + } + } + file->uid = u; + + fs = fsstr(aname); + if(fs == nil){ + error = Ebadspc; + goto out; + } + raddr = getraddr(fs->dev); + p = getbuf(fs->dev, raddr, Bread); + if(p == nil || checktag(p, Tdir, QPROOT)){ + error = Ealloc; + goto out; + } + d = getdir(p, 0); + if(d == nil || !(d->mode & DALLOC)){ + error = Ealloc; + goto out; + } + if (iaccess(file, d, DEXEC) || + file->uid == 0 && fs->dev->type == Devro) { + /* + * 'none' not allowed on dump + */ + error = Eaccess; + goto out; + } + accessdir(p, d, FREAD, file->uid); + mkqid(&file->qid, d, 1); + file->fs = fs; + file->addr = raddr; + file->slot = 0; + file->open = 0; + freewp(file->wpath); + file->wpath = 0; + + r->qid = file->qid; + + strncpy(chan->whoname, f->uname, sizeof(chan->whoname)); + chan->whotime = time(); + if(cons.flags & attachflag) + print("9p2: attach %s %T to \"%s\" C%d\n", + chan->whoname, chan->whotime, fs->name, chan->chan); + +out: + if((cons.flags & attachflag) && error) + print("9p2: attach %s %T SUCK EGGS --- %s\n", + f->uname, time(), errstr9p[error]); + if(p != nil) + putbuf(p); + if(file != nil){ + qunlock(file); + if(error) + freefp(file); + } + return error; +} + +static int +flush(Chan* chan, Fcall*, Fcall*) +{ + runlock(&chan->reflock); + wlock(&chan->reflock); + wunlock(&chan->reflock); + rlock(&chan->reflock); + + return 0; +} + +static void +clone(File* nfile, File* file) +{ + Wpath *wpath; + + nfile->qid = file->qid; + + lock(&wpathlock); + nfile->wpath = file->wpath; + for(wpath = nfile->wpath; wpath != nil; wpath = wpath->up) + wpath->refs++; + unlock(&wpathlock); + + nfile->fs = file->fs; + nfile->addr = file->addr; + nfile->slot = file->slot; + nfile->uid = file->uid; + nfile->open = file->open & ~FREMOV; +} + +static int +walkname(File* file, char* wname, Qid* wqid) +{ + Wpath *w; + Iobuf *p, *p1; + Dentry *d, *d1; + int error, slot; + Off addr, qpath; + + p = p1 = nil; + + /* + * File must not have been opened for I/O by an open + * or create message and must represent a directory. + */ + if(file->open != 0){ + error = Emode; + goto out; + } + + p = getbuf(file->fs->dev, file->addr, Bread); + if(p == nil || checktag(p, Tdir, QPNONE)){ + error = Edir1; + goto out; + } + d = getdir(p, file->slot); + if(d == nil || !(d->mode & DALLOC)){ + error = Ealloc; + goto out; + } + if(!(d->mode & DDIR)){ + error = Edir1; + goto out; + } + if(error = mkqidcmp(&file->qid, d)) + goto out; + + /* + * For walked elements the implied user must + * have permission to search the directory. + */ + if(file->cp != cons.chan && iaccess(file, d, DEXEC)){ + error = Eaccess; + goto out; + } + accessdir(p, d, FREAD, file->uid); + + if(strcmp(wname, ".") == 0){ +setdot: + if(wqid != nil) + *wqid = file->qid; + goto out; + } + if(strcmp(wname, "..") == 0){ + if(file->wpath == 0) + goto setdot; + putbuf(p); + p = nil; + addr = file->wpath->addr; + slot = file->wpath->slot; + p1 = getbuf(file->fs->dev, addr, Bread); + if(p1 == nil || checktag(p1, Tdir, QPNONE)){ + error = Edir1; + goto out; + } + d1 = getdir(p1, slot); + if(d == nil || !(d1->mode & DALLOC)){ + error = Ephase; + goto out; + } + lock(&wpathlock); + file->wpath->refs--; + file->wpath = file->wpath->up; + unlock(&wpathlock); + goto found; + } + + for(addr = 0; ; addr++){ + if(p == nil){ + p = getbuf(file->fs->dev, file->addr, Bread); + if(p == nil || checktag(p, Tdir, QPNONE)){ + error = Ealloc; + goto out; + } + d = getdir(p, file->slot); + if(d == nil || !(d->mode & DALLOC)){ + error = Ealloc; + goto out; + } + } + qpath = d->qid.path; + p1 = dnodebuf1(p, d, addr, 0, file->uid); + p = nil; + if(p1 == nil || checktag(p1, Tdir, qpath)){ + error = Eentry; + goto out; + } + for(slot = 0; slot < DIRPERBUF; slot++){ + d1 = getdir(p1, slot); + if (!(d1->mode & DALLOC) || + strncmp(wname, d1->name, NAMELEN) != 0) + continue; + /* + * update walk path + */ + if((w = newwp()) == nil){ + error = Ewalk; + goto out; + } + w->addr = file->addr; + w->slot = file->slot; + w->up = file->wpath; + file->wpath = w; + slot += DIRPERBUF*addr; + goto found; + } + putbuf(p1); + p1 = nil; + } + +found: + file->addr = p1->addr; + mkqid(&file->qid, d1, 1); + putbuf(p1); + p1 = nil; + file->slot = slot; + if(wqid != nil) + *wqid = file->qid; + +out: + if(p1 != nil) + putbuf(p1); + if(p != nil) + putbuf(p); + + return error; +} + +static int +walk(Chan* chan, Fcall* f, Fcall* r) +{ + int error, nwname; + File *file, *nfile, tfile; + + /* + * The file identified by f->fid must be valid in the + * current session and must not have been opened for I/O + * by an open or create message. + */ + if((file = filep(chan, f->fid, 0)) == nil) + return Efid; + if(file->open != 0){ + qunlock(file); + return Emode; + } + + /* + * If newfid is not the same as fid, allocate a new file; + * a side effect is checking newfid is not already in use (error); + * if there are no names to walk this will be equivalent to a + * simple 'clone' operation. + * Otherwise, fid and newfid are the same and if there are names + * to walk make a copy of 'file' to be used during the walk as + * 'file' must only be updated on success. + * Finally, it's a no-op if newfid is the same as fid and f->nwname + * is 0. + */ + r->nwqid = 0; + if(f->newfid != f->fid){ + if((nfile = filep(chan, f->newfid, 1)) == nil){ + qunlock(file); + return Efidinuse; + } + } + else if(f->nwname != 0){ + nfile = &tfile; + memset(nfile, 0, sizeof(File)); + nfile->cp = chan; + nfile->fid = ~0; + } + else{ + qunlock(file); + return 0; + } + clone(nfile, file); + + /* + * Should check name is not too long. + */ + error = 0; + for(nwname = 0; nwname < f->nwname; nwname++){ + error = walkname(nfile, f->wname[nwname], &r->wqid[r->nwqid]); + if(error != 0 || ++r->nwqid >= MAXDAT/sizeof(Qid)) + break; + } + + if(f->nwname == 0){ + /* + * Newfid must be different to fid (see above) + * so this is a simple 'clone' operation - there's + * nothing to do except unlock unless there's + * an error. + */ + if(error){ + freewp(nfile->wpath); + qunlock(nfile); + freefp(nfile); + } + else + qunlock(nfile); + } + else if(r->nwqid < f->nwname){ + /* + * Didn't walk all elements, 'clunk' nfile + * and leave 'file' alone. + * Clear error if some of the elements were + * walked OK. + */ + freewp(nfile->wpath); + if(nfile != &tfile){ + qunlock(nfile); + freefp(nfile); + } + if(r->nwqid != 0) + error = 0; + } + else{ + /* + * Walked all elements. If newfid is the same + * as fid must update 'file' from the temporary + * copy used during the walk. + * Otherwise just unlock (when using tfile there's + * no need to unlock as it's a local). + */ + if(nfile == &tfile){ + file->qid = nfile->qid; + freewp(file->wpath); + file->wpath = nfile->wpath; + file->addr = nfile->addr; + file->slot = nfile->slot; + } + else + qunlock(nfile); + } + qunlock(file); + + return error; +} + +static int +open(Chan* chan, Fcall* f, Fcall* r) +{ + Iobuf *p; + Dentry *d; + File *file; + Tlock *t; + Qid qid; + int error, ro, fmod, wok; + + wok = 0; + p = nil; + + if(chan == cons.chan || writeallow) + wok = 1; + + if((file = filep(chan, f->fid, 0)) == nil){ + error = Efid; + goto out; + } + if(file->open != 0){ + error = Emode; + goto out; + } + + /* + * if remove on close, check access here + */ + ro = file->fs->dev->type == Devro; + if(f->mode & ORCLOSE){ + if(ro){ + error = Eronly; + goto out; + } + /* + * check on parent directory of file to be deleted + */ + if(file->wpath == 0 || file->wpath->addr == file->addr){ + error = Ephase; + goto out; + } + p = getbuf(file->fs->dev, file->wpath->addr, Bread); + if(p == nil || checktag(p, Tdir, QPNONE)){ + error = Ephase; + goto out; + } + d = getdir(p, file->wpath->slot); + if(d == nil || !(d->mode & DALLOC)){ + error = Ephase; + goto out; + } + if(iaccess(file, d, DWRITE)){ + error = Eaccess; + goto out; + } + putbuf(p); + } + p = getbuf(file->fs->dev, file->addr, Bread); + if(p == nil || checktag(p, Tdir, QPNONE)){ + error = Ealloc; + goto out; + } + d = getdir(p, file->slot); + if(d == nil || !(d->mode & DALLOC)){ + error = Ealloc; + goto out; + } + if(error = mkqidcmp(&file->qid, d)) + goto out; + mkqid(&qid, d, 1); + switch(f->mode & 7){ + + case OREAD: + if(iaccess(file, d, DREAD) && !wok) + goto badaccess; + fmod = FREAD; + break; + + case OWRITE: + if((d->mode & DDIR) || (iaccess(file, d, DWRITE) && !wok)) + goto badaccess; + if(ro){ + error = Eronly; + goto out; + } + fmod = FWRITE; + break; + + case ORDWR: + if((d->mode & DDIR) + || (iaccess(file, d, DREAD) && !wok) + || (iaccess(file, d, DWRITE) && !wok)) + goto badaccess; + if(ro){ + error = Eronly; + goto out; + } + fmod = FREAD+FWRITE; + break; + + case OEXEC: + if((d->mode & DDIR) || (iaccess(file, d, DEXEC) && !wok)) + goto badaccess; + fmod = FREAD; + break; + + default: + error = Emode; + goto out; + } + if(f->mode & OTRUNC){ + if((d->mode & DDIR) || (iaccess(file, d, DWRITE) && !wok)) + goto badaccess; + if(ro){ + error = Eronly; + goto out; + } + } + t = 0; + if(d->mode & DLOCK){ + if((t = tlocked(p, d)) == nil){ + error = Elocked; + goto out; + } + } + if(f->mode & ORCLOSE) + fmod |= FREMOV; + file->open = fmod; + if((f->mode & OTRUNC) && !(d->mode & DAPND)){ + dtrunc(p, d, file->uid); + qid.vers = d->qid.version; + } + r->qid = qid; + file->tlock = t; + if(t != nil) + t->file = file; + file->lastra = 1; + goto out; + +badaccess: + error = Eaccess; + file->open = 0; + +out: + if(p != nil) + putbuf(p); + if(file != nil) + qunlock(file); + + r->iounit = chan->msize-IOHDRSZ; + + return error; +} + +static int +create(Chan* chan, Fcall* f, Fcall* r) +{ + Iobuf *p, *p1; + Dentry *d, *d1; + File *file; + int error, slot, slot1, fmod, wok; + Off addr, addr1, path; + Tlock *t; + Wpath *w; + + wok = 0; + p = nil; + + if(chan == cons.chan || writeallow) + wok = 1; + + if((file = filep(chan, f->fid, 0)) == nil){ + error = Efid; + goto out; + } + if(file->fs->dev->type == Devro){ + error = Eronly; + goto out; + } + if(file->qid.type & QTAUTH){ + error = Emode; + goto out; + } + + p = getbuf(file->fs->dev, file->addr, Bread); + if(p == nil || checktag(p, Tdir, QPNONE)){ + error = Ealloc; + goto out; + } + d = getdir(p, file->slot); + if(d == nil || !(d->mode & DALLOC)){ + error = Ealloc; + goto out; + } + if(error = mkqidcmp(&file->qid, d)) + goto out; + if(!(d->mode & DDIR)){ + error = Edir2; + goto out; + } + if(iaccess(file, d, DWRITE) && !wok) { + error = Eaccess; + goto out; + } + accessdir(p, d, FREAD, file->uid); + + /* + * Check the name is valid (and will fit in an old + * directory entry for the moment). + */ + if(error = checkname9p2(f->name)) + goto out; + + addr1 = 0; + slot1 = 0; /* set */ + for(addr = 0; ; addr++){ + if((p1 = dnodebuf(p, d, addr, 0, file->uid)) == nil){ + if(addr1 != 0) + break; + p1 = dnodebuf(p, d, addr, Tdir, file->uid); + } + if(p1 == nil){ + error = Efull; + goto out; + } + if(checktag(p1, Tdir, d->qid.path)){ + putbuf(p1); + goto phase; + } + for(slot = 0; slot < DIRPERBUF; slot++){ + d1 = getdir(p1, slot); + if(!(d1->mode & DALLOC)){ + if(addr1 == 0){ + addr1 = p1->addr; + slot1 = slot + addr*DIRPERBUF; + } + continue; + } + if(strncmp(f->name, d1->name, sizeof(d1->name)) == 0){ + putbuf(p1); + error = Eexist; + goto out; + } + } + putbuf(p1); + } + + switch(f->mode & 7){ + case OEXEC: + case OREAD: /* seems only useful to make directories */ + fmod = FREAD; + break; + + case OWRITE: + fmod = FWRITE; + break; + + case ORDWR: + fmod = FREAD+FWRITE; + break; + + default: + error = Emode; + goto out; + } + if(f->perm & PDIR) + if((f->mode & OTRUNC) || (f->perm & PAPND) || (fmod & FWRITE)) + goto badaccess; + /* + * do it + */ + path = qidpathgen(file->fs->dev); + if((p1 = getbuf(file->fs->dev, addr1, Bread|Bimm|Bmod)) == nil) + goto phase; + d1 = getdir(p1, slot1); + if(d1 == nil || checktag(p1, Tdir, d->qid.path)) { + putbuf(p1); + goto phase; + } + if(d1->mode & DALLOC){ + putbuf(p1); + goto phase; + } + + strncpy(d1->name, f->name, sizeof(d1->name)); + if(chan == cons.chan){ + d1->uid = cons.uid; + d1->gid = cons.gid; + } + else{ + d1->uid = file->uid; + d1->gid = d->gid; + f->perm &= d->mode | ~0666; + if(f->perm & PDIR) + f->perm &= d->mode | ~0777; + } + d1->qid.path = path; + d1->qid.version = 0; + d1->mode = DALLOC | (f->perm & 0777); + if(f->perm & PDIR) { + d1->mode |= DDIR; + d1->qid.path |= QPDIR; + } + if(f->perm & PAPND) + d1->mode |= DAPND; + t = nil; + if(f->perm & PLOCK){ + d1->mode |= DLOCK; + t = tlocked(p1, d1); + /* if nil, out of tlock structures */ + } + accessdir(p1, d1, FWRITE, file->uid); + mkqid(&r->qid, d1, 0); + putbuf(p1); + accessdir(p, d, FWRITE, file->uid); + + /* + * do a walk to new directory entry + */ + if((w = newwp()) == nil){ + error = Ewalk; + goto out; + } + w->addr = file->addr; + w->slot = file->slot; + w->up = file->wpath; + file->wpath = w; + file->qid = r->qid; + file->tlock = t; + if(t != nil) + t->file = file; + file->lastra = 1; + if(f->mode & ORCLOSE) + fmod |= FREMOV; + file->open = fmod; + file->addr = addr1; + file->slot = slot1; + goto out; + +badaccess: + error = Eaccess; + goto out; + +phase: + error = Ephase; + +out: + if(p != nil) + putbuf(p); + if(file != nil) + qunlock(file); + + r->iounit = chan->msize-IOHDRSZ; + + return error; +} + +static int +read(Chan* chan, Fcall* f, Fcall* r, uchar* data) +{ + Iobuf *p, *p1; + File *file; + Dentry *d, *d1; + Tlock *t; + Off addr, offset, start; + Timet tim; + int error, iounit, nread, count, n, o, slot; + Msgbuf *dmb; + Dir dir; + + p = nil; + + error = 0; + count = f->count; + offset = f->offset; + nread = 0; + if((file = filep(chan, f->fid, 0)) == nil){ + error = Efid; + goto out; + } + if(!(file->open & FREAD)){ + error = Eopen; + goto out; + } + iounit = chan->msize-IOHDRSZ; + if(count < 0 || count > iounit){ + error = Ecount; + goto out; + } + if(offset < 0){ + error = Eoffset; + goto out; + } + if(file->qid.type & QTAUTH){ + nread = authread(file, (uchar*)data, count); + if(nread < 0) + error = Eauth2; + goto out; + } + p = getbuf(file->fs->dev, file->addr, Bread); + if(p == nil || checktag(p, Tdir, QPNONE)){ + error = Ealloc; + goto out; + } + d = getdir(p, file->slot); + if(d == nil || !(d->mode & DALLOC)){ + error = Ealloc; + goto out; + } + if(error = mkqidcmp(&file->qid, d)) + goto out; + if(t = file->tlock){ + tim = toytime(); + if(t->time < tim || t->file != file){ + error = Ebroken; + goto out; + } + /* renew the lock */ + t->time = tim + TLOCK; + } + accessdir(p, d, FREAD, file->uid); + if(d->mode & DDIR) + goto dread; + if(offset+count > d->size) + count = d->size - offset; + while(count > 0){ + if(p == nil){ + p = getbuf(file->fs->dev, file->addr, Bread); + if(p == nil || checktag(p, Tdir, QPNONE)){ + error = Ealloc; + goto out; + } + d = getdir(p, file->slot); + if(d == nil || !(d->mode & DALLOC)){ + error = Ealloc; + goto out; + } + } + addr = offset / BUFSIZE; + file->lastra = dbufread(p, d, addr, file->lastra, file->uid); + o = offset % BUFSIZE; + n = BUFSIZE - o; + if(n > count) + n = count; + p1 = dnodebuf1(p, d, addr, 0, file->uid); + p = nil; + if(p1 != nil){ + if(checktag(p1, Tfile, QPNONE)){ + error = Ephase; + putbuf(p1); + goto out; + } + memmove(data+nread, p1->iobuf+o, n); + putbuf(p1); + } + else + memset(data+nread, 0, n); + count -= n; + nread += n; + offset += n; + } + goto out; + +dread: + /* + * Pick up where we left off last time if nothing has changed, + * otherwise must scan from the beginning. + */ + if(offset == file->doffset /*&& file->qid.vers == file->dvers*/){ + addr = file->dslot/DIRPERBUF; + slot = file->dslot%DIRPERBUF; + start = offset; + } + else{ + addr = 0; + slot = 0; + start = 0; + } + + dmb = mballoc(iounit, chan, Mbreply1); + for (;;) { + if(p == nil){ + /* + * This is just a check to ensure the entry hasn't + * gone away during the read of each directory block. + */ + p = getbuf(file->fs->dev, file->addr, Bread); + if(p == nil || checktag(p, Tdir, QPNONE)){ + error = Ealloc; + goto out1; + } + d = getdir(p, file->slot); + if(d == nil || !(d->mode & DALLOC)){ + error = Ealloc; + goto out1; + } + } + p1 = dnodebuf1(p, d, addr, 0, file->uid); + p = nil; + if(p1 == nil) + goto out1; + if(checktag(p1, Tdir, QPNONE)){ + error = Ephase; + putbuf(p1); + goto out1; + } + + for(; slot < DIRPERBUF; slot++){ + d1 = getdir(p1, slot); + if(!(d1->mode & DALLOC)) + continue; + mkdir9p2(&dir, d1, dmb->data); + n = convD2M(&dir, data+nread, iounit - nread); + if(n <= BIT16SZ){ + putbuf(p1); + goto out1; + } + start += n; + if(start < offset) + continue; + if(count < n){ + putbuf(p1); + goto out1; + } + count -= n; + nread += n; + offset += n; + } + putbuf(p1); + slot = 0; + addr++; + } +out1: + mbfree(dmb); + if(error == 0){ + file->doffset = offset; + file->dvers = file->qid.vers; + file->dslot = slot+DIRPERBUF*addr; + } + +out: + /* + * Do we need this any more? + count = f->count - nread; + if(count > 0) + memset(data+nread, 0, count); + */ + if(p != nil) + putbuf(p); + if(file != nil) + qunlock(file); + r->count = nread; + r->data = (char*)data; + + return error; +} + +static int +write(Chan* chan, Fcall* f, Fcall* r) +{ + Iobuf *p, *p1; + Dentry *d; + File *file; + Tlock *t; + Off offset, addr, qpath; + Timet tim; + int count, error, nwrite, o, n; + + error = 0; + offset = f->offset; + count = f->count; + + nwrite = 0; + p = nil; + + if((file = filep(chan, f->fid, 0)) == nil){ + error = Efid; + goto out; + } + if(!(file->open & FWRITE)){ + error = Eopen; + goto out; + } + if(count < 0 || count > chan->msize-IOHDRSZ){ + error = Ecount; + goto out; + } + if(offset < 0) { + error = Eoffset; + goto out; + } + + if(file->qid.type & QTAUTH){ + nwrite = authwrite(file, (uchar*)f->data, count); + if(nwrite < 0) + error = Eauth2; + goto out; + } + else if(file->fs->dev->type == Devro){ + error = Eronly; + goto out; + } + + if ((p = getbuf(file->fs->dev, file->addr, Bread|Bmod)) == nil || + (d = getdir(p, file->slot)) == nil || !(d->mode & DALLOC)) { + error = Ealloc; + goto out; + } + if(error = mkqidcmp(&file->qid, d)) + goto out; + if(t = file->tlock) { + tim = toytime(); + if(t->time < tim || t->file != file){ + error = Ebroken; + goto out; + } + /* renew the lock */ + t->time = tim + TLOCK; + } + accessdir(p, d, FWRITE, file->uid); + if(d->mode & DAPND) + offset = d->size; + if(offset+count > d->size) + d->size = offset+count; + while(count > 0){ + if(p == nil){ + p = getbuf(file->fs->dev, file->addr, Bread|Bmod); + if(p == nil){ + error = Ealloc; + goto out; + } + d = getdir(p, file->slot); + if(d == nil || !(d->mode & DALLOC)){ + error = Ealloc; + goto out; + } + } + addr = offset / BUFSIZE; + o = offset % BUFSIZE; + n = BUFSIZE - o; + if(n > count) + n = count; + qpath = d->qid.path; + p1 = dnodebuf1(p, d, addr, Tfile, file->uid); + p = nil; + if(p1 == nil) { + error = Efull; + goto out; + } + if(checktag(p1, Tfile, qpath)){ + putbuf(p1); + error = Ephase; + goto out; + } + memmove(p1->iobuf+o, f->data+nwrite, n); + p1->flags |= Bmod; + putbuf(p1); + count -= n; + nwrite += n; + offset += n; + } + +out: + if(p != nil) + putbuf(p); + if(file != nil) + qunlock(file); + r->count = nwrite; + + return error; +} + +static int +_clunk(File* file, int remove, int wok) +{ + Tlock *t; + int error; + + error = 0; + if(t = file->tlock){ + if(t->file == file) + t->time = 0; /* free the lock */ + file->tlock = 0; + } + if(remove && (file->qid.type & QTAUTH) == 0) + error = doremove(file, wok); + file->open = 0; + freewp(file->wpath); + authfree(file->auth); + freefp(file); + qunlock(file); + + return error; +} + +static int +clunk(Chan* chan, Fcall* f, Fcall*) +{ + File *file; + + if((file = filep(chan, f->fid, 0)) == nil) + return Efid; + + _clunk(file, file->open & FREMOV, 0); + return 0; +} + +static int +remove(Chan* chan, Fcall* f, Fcall*) +{ + File *file; + + if((file = filep(chan, f->fid, 0)) == nil) + return Efid; + + return _clunk(file, 1, chan == cons.chan); +} + +static int +stat(Chan* chan, Fcall* f, Fcall* r, uchar* data) +{ + Dir dir; + Iobuf *p; + Dentry *d, dentry; + File *file; + int error, len; + + error = 0; + p = nil; + if((file = filep(chan, f->fid, 0)) == nil) + return Efid; + if(file->qid.type & QTAUTH){ + memset(&dentry, 0, sizeof dentry); + d = &dentry; + mkqid9p1(&d->qid, &file->qid); + strcpy(d->name, "#¿"); + d->uid = authuid(file->auth); + d->gid = d->uid; + d->muid = d->uid; + d->atime = time(); + d->mtime = d->atime; + d->size = 0; + } else { + p = getbuf(file->fs->dev, file->addr, Bread); + if(p == nil || checktag(p, Tdir, QPNONE)){ + error = Edir1; + goto out; + } + d = getdir(p, file->slot); + if(d == nil || !(d->mode & DALLOC)){ + error = Ealloc; + goto out; + } + if(error = mkqidcmp(&file->qid, d)) + goto out; + + if(d->qid.path == QPROOT) /* stat of root gives time */ + d->atime = time(); + } + len = mkdir9p2(&dir, d, data); + data += len; + + if((r->nstat = convD2M(&dir, data, chan->msize - len)) == 0) + error = Eedge; + r->stat = data; + +out: + if(p != nil) + putbuf(p); + if(file != nil) + qunlock(file); + + return error; +} + +static int +wstat(Chan* chan, Fcall* f, Fcall*, char* strs) +{ + Iobuf *p, *p1; + Dentry *d, *d1; + File *file; + int error, err, gid, gl, muid, op, slot, tsync, uid; + long addr; + Dir dir; + + if(convM2D(f->stat, f->nstat, &dir, strs) == 0) + return Econvert; + + /* + * Get the file. + * If user 'none' (uid == 0), can't do anything; + * if filesystem is read-only, can't change anything. + */ + if((file = filep(chan, f->fid, 0)) == nil) + return Efid; + p = p1 = nil; + if(file->uid == 0){ + error = Eaccess; + goto out; + } + if(file->fs->dev->type == Devro){ + error = Eronly; + goto out; + } + if(file->qid.type & QTAUTH){ + error = Emode; + goto out; + } + + /* + * Get the current entry and check it is still valid. + */ + p = getbuf(file->fs->dev, file->addr, Bread); + if(p == nil || checktag(p, Tdir, QPNONE)){ + error = Ealloc; + goto out; + } + d = getdir(p, file->slot); + if(d == nil || !(d->mode & DALLOC)){ + error = Ealloc; + goto out; + } + if(error = mkqidcmp(&file->qid, d)) + goto out; + + /* + * Run through each of the (sub-)fields in the provided Dir + * checking for validity and whether it's a default: + * .type, .dev and .atime are completely ignored and not checked; + * .qid.path, .qid.vers and .muid are checked for validity but + * any attempt to change them is an error. + * .qid.type/.mode, .mtime, .name, .length, .uid and .gid can + * possibly be changed (and .muid iff wstatallow). + * + * 'Op' flags there are changed fields, i.e. it's not a no-op. + * 'Tsync' flags all fields are defaulted. + * + * Wstatallow and writeallow are set to allow changes during the + * fileserver bootstrap phase. + */ + tsync = 1; + if(dir.qid.path != ~0){ + if(dir.qid.path != file->qid.path){ + error = Ewstatp; + goto out; + } + tsync = 0; + } + if(dir.qid.vers != ~0){ + if(dir.qid.vers != file->qid.vers){ + error = Ewstatv; + goto out; + } + tsync = 0; + } + + /* + * .qid.type and .mode have some bits in common. Only .mode + * is currently needed for comparisons with the old mode but + * if there are changes to the bits also encoded in .qid.type + * then file->qid must be updated appropriately later. + */ + if(dir.qid.type == (uchar)~0){ + if(dir.mode == ~0) + dir.qid.type = mktype9p2(d->mode); + else + dir.qid.type = dir.mode>>24; + } + else + tsync = 0; + if(dir.mode == ~0) + dir.mode = mkmode9p2(d->mode); + else + tsync = 0; + + /* + * Check dir.qid.type and dir.mode agree, check for any unknown + * type/mode bits, check for an attempt to change the directory bit. + */ + if(dir.qid.type != ((dir.mode>>24) & 0xFF)){ + error = Ewstatq; + goto out; + } + if(dir.mode & ~(DMDIR|DMAPPEND|DMEXCL|0777)){ + error = Ewstatb; + goto out; + } + + op = dir.mode^mkmode9p2(d->mode); + if(op & DMDIR){ + error = Ewstatd; + goto out; + } + + if(dir.mtime != ~0){ + if(dir.mtime != d->mtime) + op = 1; + tsync = 0; + } + else + dir.mtime = d->mtime; + + if(dir.length == ~(Off)0) + dir.length = d->size; + else { + if (dir.length < 0) { + error = Ewstatl; + goto out; + } else if(dir.length != d->size) + op = 1; + tsync = 0; + } + + /* + * Check for permission to change .mode, .mtime or .length, + * must be owner or leader of either group, for which test gid + * is needed; permission checks on gid will be done later. + * 'Gl' counts whether neither, one or both groups are led. + */ + if(dir.gid != nil && *dir.gid != '\0'){ + gid = strtouid(dir.gid); + tsync = 0; + } + else + gid = d->gid; + gl = leadgroup(file->uid, gid) != 0; + gl += leadgroup(file->uid, d->gid) != 0; + + if(op && !wstatallow && d->uid != file->uid && !gl){ + error = Ewstato; + goto out; + } + + /* + * Rename. + * Check .name is valid and different to the current. + */ + if(dir.name != nil && *dir.name != '\0'){ + if(error = checkname9p2(dir.name)) + goto out; + if(strncmp(dir.name, d->name, NAMELEN)) + op = 1; + else + dir.name = d->name; + tsync = 0; + } + else + dir.name = d->name; + + /* + * If the name is really to be changed check it's unique + * and there is write permission in the parent. + */ + if(dir.name != d->name){ + /* + * First get parent. + * Must drop current entry to prevent + * deadlock when searching that new name + * already exists below. + */ + putbuf(p); + p = nil; + + if(file->wpath == nil){ + error = Ephase; + goto out; + } + p1 = getbuf(file->fs->dev, file->wpath->addr, Bread); + if(p1 == nil || checktag(p1, Tdir, QPNONE)){ + error = Ephase; + goto out; + } + d1 = getdir(p1, file->wpath->slot); + if(d1 == nil || !(d1->mode & DALLOC)){ + error = Ephase; + goto out; + } + + /* + * Check entries in parent for new name. + */ + for(addr = 0; ; addr++){ + if((p = dnodebuf(p1, d1, addr, 0, file->uid)) == nil) + break; + if(checktag(p, Tdir, d1->qid.path)){ + putbuf(p); + continue; + } + for(slot = 0; slot < DIRPERBUF; slot++){ + d = getdir(p, slot); + if(!(d->mode & DALLOC) || + strncmp(dir.name, d->name, sizeof d->name)) + continue; + error = Eexist; + goto out; + } + putbuf(p); + } + + /* + * Reacquire entry and check it's still OK. + */ + p = getbuf(file->fs->dev, file->addr, Bread); + if(p == nil || checktag(p, Tdir, QPNONE)){ + error = Ephase; + goto out; + } + d = getdir(p, file->slot); + if(d == nil || !(d->mode & DALLOC)){ + error = Ephase; + goto out; + } + + /* + * Check write permission in the parent. + */ + if(!wstatallow && !writeallow && iaccess(file, d1, DWRITE)){ + error = Eaccess; + goto out; + } + } + + /* + * Check for permission to change owner - must be god. + */ + if(dir.uid != nil && *dir.uid != '\0'){ + uid = strtouid(dir.uid); + if(uid != d->uid){ + if(!wstatallow){ + error = Ewstatu; + goto out; + } + op = 1; + } + tsync = 0; + } + else + uid = d->uid; + if(dir.muid != nil && *dir.muid != '\0'){ + muid = strtouid(dir.muid); + if(muid != d->muid){ + if(!wstatallow){ + error = Ewstatm; + goto out; + } + op = 1; + } + tsync = 0; + } + else + muid = d->muid; + + /* + * Check for permission to change group, must be + * either owner and in new group or leader of both groups. + */ + if(gid != d->gid){ + if(!(wstatallow || writeallow) + && !(d->uid == file->uid && ingroup(file->uid, gid)) + && !(gl == 2)){ + error = Ewstatg; + goto out; + } + op = 1; + } + + /* + * Checks all done, update if necessary. + */ + if(op){ + d->mode = mkmode9p1(dir.mode); + file->qid.type = mktype9p2(d->mode); + d->mtime = dir.mtime; + if (dir.length < d->size) { + err = dtrunclen(p, d, dir.length, uid); + if (error == 0) + error = err; + } + d->size = dir.length; + if(dir.name != d->name) + strncpy(d->name, dir.name, sizeof(d->name)); + d->uid = uid; + d->gid = gid; + d->muid = muid; + } + if(!tsync) + accessdir(p, d, FREAD, file->uid); + +out: + if(p != nil) + putbuf(p); + if(p1 != nil) + putbuf(p1); + qunlock(file); + + return error; +} + +int +serve9p2(Msgbuf* mb) +{ + Chan *chan; + Fcall f, r; + Msgbuf *data, *rmb; + char ename[64]; + int error, n, type; + static int once; + + if(once == 0){ + fmtinstall('F', fcallfmt); + once = 1; + } + + /* + * 0 return means i don't understand this message, + * 1 return means i dealt with it, including error + * replies. + */ + if(convM2S(mb->data, mb->count, &f) != mb->count) +{ +print("didn't like %d byte message\n", mb->count); + return 0; +} + type = f.type; + if(type < Tversion || type >= Tmax || (type & 1) || type == Terror) + return 0; + + chan = mb->chan; + if(CHAT(chan)) + print("9p2: f %F\n", &f); + r.type = type+1; + r.tag = f.tag; + error = 0; + data = nil; + + switch(type){ + default: + r.type = Rerror; + snprint(ename, sizeof(ename), "unknown message: %F", &f); + r.ename = ename; + break; + case Tversion: + error = version(chan, &f, &r); + break; + case Tauth: + error = auth(chan, &f, &r); + break; + case Tattach: + error = attach(chan, &f, &r); + break; + case Tflush: + error = flush(chan, &f, &r); + break; + case Twalk: + error = walk(chan, &f, &r); + break; + case Topen: + error = open(chan, &f, &r); + break; + case Tcreate: + error = create(chan, &f, &r); + break; + case Tread: + data = mballoc(chan->msize, chan, Mbreply1); + error = read(chan, &f, &r, data->data); + break; + case Twrite: + error = write(chan, &f, &r); + break; + case Tclunk: + error = clunk(chan, &f, &r); + break; + case Tremove: + error = remove(chan, &f, &r); + break; + case Tstat: + data = mballoc(chan->msize, chan, Mbreply1); + error = stat(chan, &f, &r, data->data); + break; + case Twstat: + data = mballoc(chan->msize, chan, Mbreply1); + error = wstat(chan, &f, &r, (char*)data->data); + break; + } + + if(error != 0){ + r.type = Rerror; + if(error >= MAXERR){ + snprint(ename, sizeof(ename), "error %d", error); + r.ename = ename; + } + else + r.ename = errstr9p[error]; + } + if(CHAT(chan)) + print("9p2: r %F\n", &r); + + rmb = mballoc(chan->msize, chan, Mbreply2); + n = convS2M(&r, rmb->data, chan->msize); + if(data != nil) + mbfree(data); + if(n == 0){ + type = r.type; + r.type = Rerror; + + /* + * If a Tversion has not been seen on the chan then + * chan->msize will be 0. In that case craft a special + * Rerror message. It's fortunate that the mballoc above + * for rmb will have returned a Msgbuf of MAXMSG size + * when given a request with count of 0... + */ + if(chan->msize == 0){ + r.ename = "Tversion not seen"; + n = convS2M(&r, rmb->data, MAXMSG); + } + else{ + snprint(ename, sizeof(ename), "9p2: convS2M: type %d", type); + r.ename = ename; + n = convS2M(&r, rmb->data, chan->msize); + } + print("%s\n", r.ename); + if(n == 0){ + /* + * What to do here, the failure notification failed? + */ + mbfree(rmb); + return 1; + } + } + rmb->count = n; + rmb->param = mb->param; + send(chan->reply, rmb); + + return 1; +} diff -Nru /sys/src/fs/port/all.h /sys/src/fs/port/all.h --- /sys/src/fs/port/all.h Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/port/all.h Tue Nov 1 00:00:00 2011 @@ -0,0 +1,92 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" + +#define CHAT(cp) ((cons.flags&chatflag)||(cp&&(((Chan*)cp)->flags&chatflag))) +#define QID9P1(a,b) (Qid9p1){(a),(b)} + +#define QPDIR 0x80000000L +#define QPNONE 0 +#define QPROOT 1 +#define QPSUPER 2 + +/* + * perm argument in 9P create + */ +#define PDIR (1L<<31) /* is a directory */ +#define PAPND (1L<<30) /* is append only */ +#define PLOCK (1L<<29) /* is locked on open */ + +#define FID1 1 +#define FID2 2 + +#define SECOND(n) (n) +#define MINUTE(n) (n*SECOND(60)) +#define HOUR(n) (n*MINUTE(60)) +#define DAY(n) (n*HOUR(24)) +#define MAXBIAS SECOND(20) +#define TLOCK MINUTE(5) + +#define NQUEUE 20 + +Uid* uid; +Userid* gidspace; +Lock printing; +Time tim; +File* files; +Wpath* wpaths; +Lock wpathlock; +char* errstr9p[MAXERR]; +Chan* chans; +RWlock mainlock; +Timet mktime; +Timet boottime; +Queue* serveq; +Queue* raheadq; +Rabuf* rabuffree; +QLock reflock; +Lock rabuflock; +Tlock tlocks[NTLOCK]; +Lock tlocklock; +Device* devnone; +Startsb startsb[5]; +int predawn; /* set in early boot, causes polling ttyout */ +int mballocs[MAXCAT]; +Filsys filsys[10]; /* named file systems -- from config block */ +char service[50]; /* my name -- from config block */ +int aindex; + +ulong roflag; +ulong errorflag; +ulong chatflag; +ulong attachflag; +ulong authdebugflag; +ulong authdisableflag; +int noattach; +int echo; +int wstatallow; /* set to circumvent wstat permissions */ +int writeallow; /* set to circumvent write permissions */ +int duallow; /* single user to allow du */ +int readonly; /* disable writes if true */ + +int noauth; /* Debug */ + +int rawreadok; /* allow reading raw data */ + +File* flist[5003]; /* base of file structures */ +Lock flock; /* manipulate flist */ + +long growacct[1000]; + +struct +{ + RWlock uidlock; + Iobuf* uidbuf; + int flen; + int find; +} uidgc; + +extern char statecall[]; +extern char* wormscode[]; +extern char* tagnames[]; diff -Nru /sys/src/fs/port/auth.c /sys/src/fs/port/auth.c --- /sys/src/fs/port/auth.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/port/auth.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,417 @@ +#include "all.h" +#include "io.h" + +#include + +Nvrsafe nvr; + +char* +nvrgetconfig(void) +{ + return nvr.config; +} + +int +nvrsetconfig(char* word) +{ + int c; + + c = strlen(word); + if(c >= sizeof(nvr.config)) { + print("config string too long\n"); + return 1; + } + memset(nvr.config, 0, sizeof(nvr.config)); + memmove(nvr.config, word, c); + nvr.configsum = nvcsum(nvr.config, sizeof(nvr.config)); + nvwrite(NVRAUTHADDR, &nvr, sizeof(nvr)); + + return 0; +} + +int +nvrcheck(void) +{ + uchar csum; + + print("nvr read\n"); + nvread(NVRAUTHADDR, &nvr, sizeof(nvr)); + + csum = nvcsum(nvr.authkey, sizeof(nvr.authkey)); + if(csum != nvr.authsum) { + print("\n\n ** NVR key checksum is incorrect **\n"); + print(" ** set password to allow attaches **\n\n"); + memset(nvr.authkey, 0, sizeof(nvr.authkey)); + return 1; + } + + csum = nvcsum(nvr.config, sizeof(nvr.config)); + if(csum != nvr.configsum) { + print("\n\n ** NVR config checksum is incorrect **\n"); + memset(nvr.config, 0, sizeof(nvr.config)); + return 1; + } + + return 0; +} + +void +cmd_passwd(int, char *[]) +{ + char passwd[32]; + static char zeros[DESKEYLEN]; + char nkey1[DESKEYLEN], nkey2[DESKEYLEN]; + char authid[NAMELEN]; + char authdom[DOMLEN]; + + if(memcmp(nvr.authkey, zeros, sizeof(nvr.authkey))) { + print("Old password:"); + getstring(passwd, sizeof(passwd), 0); + memset(nkey1, 0, DESKEYLEN); + passtokey(nkey1, passwd); + if(memcmp(nkey1, nvr.authkey, DESKEYLEN)) { + print("Bad password\n"); + delay(1000); + return; + } + } + + print("New password:"); + getstring(passwd, sizeof(passwd), 0); + memset(nkey1, 0, DESKEYLEN); + passtokey(nkey1, passwd); + + print("Confirm password:"); + getstring(passwd, sizeof(passwd), 0); + memset(nkey2, 0, DESKEYLEN); + passtokey(nkey2, passwd); + + if(memcmp(nkey1, nkey2, DESKEYLEN)) { + print("don't match\n"); + return; + } + memmove(nvr.authkey, nkey1, DESKEYLEN); + nvr.authsum = nvcsum(nvr.authkey, DESKEYLEN); + + print("Authentication id:"); + getstring(authid, sizeof(authid), 1); + if(authid[0]){ + memset(nvr.authid, 0, NAMELEN); + strcpy(nvr.authid, authid); + nvr.authidsum = nvcsum(nvr.authid, NAMELEN); + } + + print("Authentication domain:"); + getstring(authdom, sizeof(authdom), 1); + if(authdom[0]){ + memset(nvr.authdom, 0, NAMELEN); + strcpy(nvr.authdom, authdom); + nvr.authdomsum = nvcsum(nvr.authdom, NAMELEN); + } + nvwrite(NVRAUTHADDR, &nvr, sizeof(nvr)); +} + +void +getstring(char *str, int n, int doecho) +{ + int c; + char *p, *e; + + memset(str, 0, n); + p = str; + e = str+n-1; + echo = doecho; + for(;;) { + if(p == e) { + *p = '\0'; + goto out; + } + c = getc(); + switch(c) { + case '\n': + *p = '\0'; + print("\n"); + goto out; + case '\b': + if(p > str) + p--; + break; + case 'U' - '@': + p = str; + break; + default: + *p++ = c; + } + } +out: + echo = 1; +} + +int +conslock(void) +{ + static char zeroes[DESKEYLEN]; + char passwd[128]; + char nkey1[DESKEYLEN]; + + if(memcmp(nvr.authkey, zeroes, DESKEYLEN) == 0) { + print("no password set\n"); + return 0; + } + + for(;;) { + print("%s password:", service); + getstring(passwd, sizeof(passwd), 0); + memset(nkey1, 0, DESKEYLEN); + passtokey(nkey1, passwd); + if(memcmp(nkey1, nvr.authkey, DESKEYLEN) == 0) { + prdate(); + break; + } + + print("Bad password\n"); + delay(1000); + } + return 1; +} + +/* + * authentication specific to 9P2000 + */ + +/* authentication states */ +enum +{ + HaveProtos=1, + NeedProto, + HaveOK, + NeedCchal, + HaveSinfo, + NeedTicket, + HaveSauthenticator, + SSuccess, +}; + +char *phasename[] = +{ +[HaveProtos] "HaveProtos", +[NeedProto] "NeedProto", +[HaveOK] "HaveOK", +[NeedCchal] "NeedCchal", +[HaveSinfo] "HaveSinfo", +[NeedTicket] "NeedTicket", +[HaveSauthenticator] "HaveSauthenticator", +[SSuccess] "SSuccess", +}; + +/* authentication structure */ +struct Auth +{ + int inuse; + char uname[NAMELEN]; /* requestor's remote user name */ + char aname[NAMELEN]; /* requested aname */ + Userid uid; /* uid decided on */ + int phase; + char cchal[CHALLEN]; + char tbuf[TICKETLEN+AUTHENTLEN]; /* server ticket */ + Ticket t; + Ticketreq tr; +}; + +Auth* auths; +Lock authlock; + +void +authinit(void) +{ + auths = ialloc(conf.nauth * sizeof(*auths), 0); +} + +static int +failure(Auth *s, char *why) +{ + int i; + +if(*why)print("authentication failed: %s: %s\n", phasename[s->phase], why); + srand((ulong)s + m->ticks); + for(i = 0; i < CHALLEN; i++) + s->tr.chal[i] = nrand(256); + s->uid = -1; + strncpy(s->tr.authid, nvr.authid, NAMELEN); + strncpy(s->tr.authdom, nvr.authdom, DOMLEN); + memmove(s->cchal, s->tr.chal, sizeof(s->cchal)); + s->phase = HaveProtos; + return -1; +} + +Auth* +authnew(char *uname, char *aname) +{ + static int si = 0; + int i, nwrap; + Auth *s; + + i = si; + nwrap = 0; + for(;;){ + if(i < 0 || i >= conf.nauth){ + if(++nwrap > 1) + return nil; + i = 0; + } + s = &auths[i++]; + if(s->inuse) + continue; + lock(&authlock); + if(s->inuse == 0){ + s->inuse = 1; + strncpy(s->uname, uname, NAMELEN-1); + strncpy(s->aname, aname, NAMELEN-1); + failure(s, ""); + si = i; + unlock(&authlock); + break; + } + unlock(&authlock); + } + return s; +} + +void +authfree(Auth *s) +{ + if(s != nil) + s->inuse = 0; +} + +int +authread(File* file, uchar* data, int n) +{ + Auth *s; + int m; + + s = file->auth; + if(s == nil) + return -1; + + switch(s->phase){ + default: + return failure(s, "unexpected phase"); + case HaveProtos: + m = snprint((char*)data, n, "v.2 p9sk1@%s", nvr.authdom) + 1; + s->phase = NeedProto; + break; + case HaveOK: + m = 3; + if(n < m) + return failure(s, "read too short"); + strcpy((char*)data, "OK"); + s->phase = NeedCchal; + break; + case HaveSinfo: + m = TICKREQLEN; + if(n < m) + return failure(s, "read too short"); + convTR2M(&s->tr, (char*)data); + s->phase = NeedTicket; + break; + case HaveSauthenticator: + m = AUTHENTLEN; + if(n < m) + return failure(s, "read too short"); + memmove(data, s->tbuf+TICKETLEN, m); + s->phase = SSuccess; + break; + } + return m; +} + +int +authwrite(File* file, uchar *data, int n) +{ + Auth *s; + int m; + char *p, *d; + Authenticator a; + + s = file->auth; + if(s == nil) + return -1; + + switch(s->phase){ + default: + return failure(s, "unknown phase"); + case NeedProto: + p = (char*)data; + if(p[n-1] != 0) + return failure(s, "proto missing terminator"); + d = strchr((char*)p, ' '); + if(d == nil) + return failure(s, "proto missing separator"); + *d++ = 0; + if(strcmp(p, "p9sk1") != 0) + return failure(s, "unknown proto"); + if(strcmp(d, nvr.authdom) != 0) + return failure(s, "unknown domain"); + s->phase = HaveOK; + m = n; + break; + case NeedCchal: + m = CHALLEN; + if(n < m) + return failure(s, "client challenge too short"); + memmove(s->cchal, data, sizeof(s->cchal)); + s->phase = HaveSinfo; + break; + case NeedTicket: + m = TICKETLEN+AUTHENTLEN; + if(n < m) + return failure(s, "ticket+auth too short"); + + convM2T((char*)data, &s->t, nvr.authkey); + if(s->t.num != AuthTs + || memcmp(s->t.chal, s->tr.chal, sizeof(s->t.chal)) != 0) + return failure(s, "bad ticket"); + + convM2A((char*)data+TICKETLEN, &a, s->t.key); + if(a.num != AuthAc + || memcmp(a.chal, s->tr.chal, sizeof(a.chal)) != 0 + || a.id != 0) + return failure(s, "bad authenticator"); + + /* at this point, we're convinced */ + s->uid = strtouid(s->t.suid); + if(s->uid < 0) + return failure(s, "unknown user"); + if(cons.flags & authdebugflag) + print("user %s = %d authenticated\n", s->t.suid, s->uid); + + /* create an authenticator to send back */ + a.num = AuthAs; + memmove(a.chal, s->cchal, sizeof(a.chal)); + a.id = 0; + convA2M(&a, s->tbuf+TICKETLEN, s->t.key); + + s->phase = HaveSauthenticator; + break; + } + return m; +} + +int +authuid(Auth* s) +{ + return s->uid; +} + +char* +authaname(Auth* s) +{ + return s->aname; +} + +char* +authuname(Auth* s) +{ + return s->uname; +} diff -Nru /sys/src/fs/port/chk.c /sys/src/fs/port/chk.c --- /sys/src/fs/port/chk.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/port/chk.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,886 @@ +#include "all.h" +#include "mem.h" /* for KZERO for PADDR */ + +/* copied from ../pc/etherif.h; should probably be in all.h */ +#define HOWMANY(x, y) (((x)+((y)-1))/(y)) +#define ROUNDUP(x, y) (HOWMANY((x), (y))*(y)) + +/* augmented Dentry */ +typedef struct { + Dentry *d; + Off qpath; + int ns; +} Extdentry; + +static char* abits; +static long sizabits; +static char* qbits; +static long sizqbits; + +static char* name; +static long sizname; + +static Off fstart; +static Off fsize; +static Off nfiles; +static Off maxq; +static char* calloc; +static Device* dev; +static Off ndup; +static Off nused; +static Off nfdup; +static Off nqbad; +static Off nfree; +static Off nbad; +static int mod; +static int flags; +static int ronly; +static int cwflag; +static Devsize sbaddr; +static Devsize oldblock; + +static int depth; +static int maxdepth; +static uchar *lowstack, *startstack; + +/* local prototypes */ +static int fsck(Dentry*); +static void ckfreelist(Superb*); +static void mkfreelist(Superb*); +static void trfreelist(Superb*); +static void xaddfree(Device*, Off, Superb*, Iobuf*); +static void xflush(Device*, Superb*, Iobuf*); +static Dentry* maked(Off, int, Off); +static void modd(Off, int, Dentry*); +static void xread(Off, Off); +static int amark(Off); +static int fmark(Off); +static int ftest(Off); +static void missing(void); +static void qmark(Off); +static void* malloc(ulong); +static Iobuf* xtag(Off, int, Off); + +static +void* +malloc(ulong n) +{ + char *p, *q; + + p = (char *)ROUNDUP((ulong)calloc, BY2WD); + q = p+n; + if(PADDR(q) >= conf.mem) + panic("check: mem size"); + calloc = q; + memset(p, 0, n); + return p; +} + +/* + * check flags + */ +enum +{ + Crdall = (1<<0), /* read all files */ + Ctag = (1<<1), /* rebuild tags */ + Cpfile = (1<<2), /* print files */ + Cpdir = (1<<3), /* print directories */ + Cfree = (1<<4), /* rebuild free list */ + Csetqid = (1<<5), /* resequence qids */ + Cream = (1<<6), /* clear all bad tags */ + Cbad = (1<<7), /* clear all bad blocks */ + Ctouch = (1<<8), /* touch old dir and indir */ + Ctrim = (1<<9), /* trim fsize back to fit when checking free list */ +}; + +static +struct +{ + char* option; + long flag; +} ckoption[] = +{ + "rdall", Crdall, + "tag", Ctag, + "pfile", Cpfile, + "pdir", Cpdir, + "free", Cfree, + "setqid", Csetqid, + "ream", Cream, + "bad", Cbad, + "touch", Ctouch, + "trim", Ctrim, + 0, +}; + +void +cmd_check(int argc, char *argv[]) +{ + long f, i; + long flag; + Off raddr; + Filsys *fs; + Iobuf *p; + Superb *sb; + Dentry *d; + + flag = 0; + for(i=1; idev; + ronly = (dev->type == Devro); + cwflag = (dev->type == Devcw) | (dev->type == Devro); + if(!ronly) + wlock(&mainlock); /* check */ + /* + * ialloc(0, 1) doesn't actually allocate any storage, and may + * return the same address each time. see iobufinit(). + * check assumes that the rest of memory, from ialloc(0, 1) + * up, is available to it, but does at least check in malloc(). + */ + calloc = (char*)ialloc(0, 1) + 100000; + flags = flag; + + sizqbits = ((1<<22) + 7) / 8; /* botch */ + qbits = malloc(sizqbits); + + sbaddr = superaddr(dev); + raddr = getraddr(dev); + p = xtag(sbaddr, Tsuper, QPSUPER); + if(!p) + goto out; + sb = (Superb*)p->iobuf; + fstart = 2; + cons.noage = 1; + + fsize = sb->fsize; + sizabits = (fsize-fstart + 7)/8; + abits = malloc(sizabits); + + sizname = 4000; + name = malloc(sizname); + sizname -= NAMELEN+10; /* for safety */ + + mod = 0; + nfree = 0; + nfdup = 0; + nused = 0; + nbad = 0; + ndup = 0; + nqbad = 0; + depth = 0; + maxdepth = 0; + + if(flags & Ctouch) { + /* round fsize down to start of current side */ + int s; + Devsize dsize; + + oldblock = 0; + for (s = 0; dsize = wormsizeside(dev, s), + dsize > 0 && oldblock + dsize < fsize; s++) + oldblock += dsize; + print("oldblock = %lld\n", (Wideoff)oldblock); + } + amark(sbaddr); + if(cwflag) { + amark(sb->roraddr); + amark(sb->next); + } + + print("checking filsys: %s\n", fs->name); + nfiles = 0; + maxq = 0; + + d = maked(raddr, 0, QPROOT); + if(d) { + amark(raddr); + if(fsck(d)) + modd(raddr, 0, d); + depth--; + calloc -= sizeof(Dentry); + if(depth) + print("depth not zero on return\n"); + } + + if(flags & Cfree) { + if(cwflag) + trfreelist(sb); + else + mkfreelist(sb); + } + + if(sb->qidgen < maxq) + print("qid generator low path=%lld maxq=%lld\n", + (Wideoff)sb->qidgen, (Wideoff)maxq); + if(!(flags & Cfree)) + ckfreelist(sb); + if(mod) { + sb->qidgen = maxq; + print("file system was modified\n"); + settag(p, Tsuper, QPNONE); + } + + print("nfiles = %lld\n", (Wideoff)nfiles); + print("fsize = %lld\n", (Wideoff)fsize); + print("nused = %lld\n", (Wideoff)nused); + print("ndup = %lld\n", (Wideoff)ndup); + print("nfree = %lld\n", (Wideoff)nfree); + print("tfree = %lld\n", (Wideoff)sb->tfree); + print("nfdup = %lld\n", (Wideoff)nfdup); + print("nmiss = %lld\n", (Wideoff)fsize-fstart-nused-nfree); + print("nbad = %lld\n", (Wideoff)nbad); + print("nqbad = %lld\n", (Wideoff)nqbad); + print("maxq = %lld\n", (Wideoff)maxq); + print("base stack=%ld\n", &u->stack[sizeof(u->stack)] - startstack); + print("high stack=%ld of %d\n", &u->stack[sizeof(u->stack)] - lowstack, + MAXSTACK); /* high-water mark of stack usage */ + print("deepest recursion=%d\n", maxdepth-1); /* one-origin */ + if(!cwflag) + missing(); + +out: + cons.noage = 0; + putbuf(p); + if(!ronly) + wunlock(&mainlock); +} + +/* + * if *blkp is already allocated and Cbad is set, zero it. + * returns *blkp if it's free, else 0. + */ +static Off +blkck(Off *blkp, int *flgp) +{ + Off a = *blkp; + + if(amark(a)) { + if(flags & Cbad) { + *blkp = 0; + *flgp |= Bmod; + } + a = 0; + } + return a; +} + +/* + * if a block address within a Dentry, *blkp, is already allocated + * and Cbad is set, zero it. + * stores 0 into *resp if already allocated, else stores *blkp. + * returns dmod count. + */ +static int +daddrck(Off *blkp, Off *resp) +{ + int dmod = 0; + + if(amark(*blkp)) { + if(flags & Cbad) { + *blkp = 0; + dmod++; + } + *resp = 0; + } else + *resp = *blkp; + return dmod; +} + +/* + * under Ctouch, read block `a' if it's in range. + * returns dmod count. + */ +static int +touch(Off a) +{ + if((flags&Ctouch) && a < oldblock) { + Iobuf *pd = getbuf(dev, a, Bread|Bmod); + + if(pd) + putbuf(pd); + return 1; + } + return 0; +} + +/* + * if d is a directory, touch it and check all its entries in block a. + * if not, under Crdall, read a. + * returns dmod count. + */ +static int +dirck(Extdentry *ed, Off a) +{ + int k, dmod = 0; + + if(ed->d->mode & DDIR) { + dmod += touch(a); + for(k=0; kqpath); + + if(nd == nil) + break; + if(fsck(nd)) { + modd(a, k, nd); + dmod++; + } + depth--; + calloc -= sizeof(Dentry); + name[ed->ns] = 0; + } + } else if(flags & Crdall) + xread(a, ed->qpath); + return dmod; +} + +/* + * touch a, check a's tag for Tind1, Tind2, etc. + * if the tag is right, validate each block number in the indirect block, + * and check each block (mostly in case we are reading a huge directory). + */ +static int +indirck(Extdentry *ed, Off a, int tag) +{ + int i, dmod = 0; + Iobuf *p1; + + if (a == 0) + return dmod; + dmod = touch(a); + if (p1 = xtag(a, tag, ed->qpath)) { + for(i=0; iiobuf)[i], &p1->flags); + if (a) + /* + * check each block named in this + * indirect(^n) block (a). + */ + if (tag == Tind1) + dmod += dirck(ed, a); + else + dmod += indirck(ed, a, tag-1); + } + putbuf(p1); + } + return dmod; +} + +static int +indiraddrck(Extdentry *ed, Off *indirp, int tag) +{ + int dmod; + Off a; + + dmod = daddrck(indirp, &a); + return dmod + indirck(ed, a, tag); +} + +/* if result is true, *d was modified */ +static +int +fsck(Dentry *d) +{ + int i, dmod; + Extdentry edent; + + depth++; + if(depth >= maxdepth) { + maxdepth = depth; + /* + * On a 386 each recursion costs ~100 bytes (more for + * directories with indirect blocks). Base stack usage + * is about ~320 bytes, leaving room for ~156 recursions + * (assuming a 16000-byte stack). Typical checks use + * around 12 recursions. + * Alternatives here might be to give the check process + * a much bigger stack or rewrite it without recursion, + * but it hardly seems worth expending effort on. + */ + if(maxdepth >= MAXSTACK/(100+10)) { + print("check: max depth exceeded: %s\n", name); + return 0; + } + } + if (lowstack == nil) + startstack = lowstack = (uchar *)&edent; + /* more precise check, assumes downward-growing stack */ + if ((uchar *)&edent < lowstack) + lowstack = (uchar *)&edent; + if ((uchar *)&edent < &u->stack[500]) { + print("check: stack nearly full: %s\n", name); + return 0; + } + + /* check that entry is allocated */ + if(!(d->mode & DALLOC)) + return 0; + nfiles++; + + /* we stash qpath & ns in an Extdentry for eventual use by dirck() */ + memset(&edent, 0, sizeof edent); + edent.d = d; + + /* check name */ + edent.ns = strlen(name); + i = strlen(d->name); + if(i >= NAMELEN) { + d->name[NAMELEN-1] = 0; + print("%s->name (%s) not terminated\n", name, d->name); + return 0; + } + edent.ns += i; + if(edent.ns >= sizname) { + print("%s->name (%s) name too large\n", name, d->name); + return 0; + } + strcat(name, d->name); + + if(d->mode & DDIR) { + if(edent.ns > 1) { + strcat(name, "/"); + edent.ns++; + } + if(flags & Cpdir) { + print("%s\n", name); + prflush(); + } + } else + if(flags & Cpfile) { + print("%s\n", name); + prflush(); + } + + /* check qid */ + edent.qpath = d->qid.path & ~QPDIR; + qmark(edent.qpath); + if(edent.qpath > maxq) + maxq = edent.qpath; + + /* check direct blocks (the common case) */ + dmod = 0; + { + Off a; + + for(i=0; idblock[i], &a); + if (a) + dmod += dirck(&edent, a); + } + } + /* check indirect^n blocks, if any */ + for (i = 0; i < NIBLOCK; i++) + dmod += indiraddrck(&edent, &d->iblocks[i], Tind1+i); + return dmod; +} + +#define XFEN (FEPERBUF+6) + +typedef struct { + int flag; + int count; + int next; + Off addr[XFEN]; +} Xfree; + +static +void +xaddfree(Device *dev, Off a, Superb *sb, Iobuf *p) +{ + Xfree *x; + + x = (Xfree*)p->iobuf; + if(x->count < XFEN) { + x->addr[x->count] = a; + x->count++; + return; + } + if(!x->flag) { + memset(&sb->fbuf, 0, sizeof(sb->fbuf)); + sb->fbuf.free[0] = 0L; + sb->fbuf.nfree = 1; + sb->tfree = 0; + x->flag = 1; + } + addfree(dev, a, sb); +} + +static +void +xflush(Device *dev, Superb *sb, Iobuf *p) +{ + int i; + Xfree *x; + + x = (Xfree*)p->iobuf; + if(!x->flag) { + memset(&sb->fbuf, 0, sizeof(sb->fbuf)); + sb->fbuf.free[0] = 0L; + sb->fbuf.nfree = 1; + sb->tfree = 0; + } + for(i=0; icount; i++) + addfree(dev, x->addr[i], sb); +} + +/* + * make freelist + * from existing freelist + * (cw devices) + */ +static +void +trfreelist(Superb *sb) +{ + Off a, n; + int i; + Iobuf *p, *xp; + Fbuf *fb; + + + xp = getbuf(devnone, Cckbuf, 0); + memset(xp->iobuf, 0, BUFSIZE); + fb = &sb->fbuf; + p = 0; + for(;;) { + n = fb->nfree; + if(n < 0 || n > FEPERBUF) + break; + for(i=1; ifree[i]; + if(a && !ftest(a)) + xaddfree(dev, a, sb, xp); + } + a = fb->free[0]; + if(!a) + break; + if(ftest(a)) + break; + xaddfree(dev, a, sb, xp); + if(p) + putbuf(p); + p = xtag(a, Tfree, QPNONE); + if(!p) + break; + fb = (Fbuf*)p->iobuf; + } + if(p) + putbuf(p); + xflush(dev, sb, xp); + putbuf(xp); + mod++; + print("%lld blocks free\n", (Wideoff)sb->tfree); +} + +static +void +ckfreelist(Superb *sb) +{ + Off a, lo, hi; + int n, i; + Iobuf *p; + Fbuf *fb; + + strcpy(name, "free list"); + print("check %s\n", name); + fb = &sb->fbuf; + a = sbaddr; + p = 0; + lo = 0; + hi = 0; + for(;;) { + n = fb->nfree; + if(n < 0 || n > FEPERBUF) { + print("check: nfree bad %lld\n", (Wideoff)a); + break; + } + for(i=1; ifree[i]; + if(a && !fmark(a)) { + if(!lo || lo > a) + lo = a; + if(!hi || hi < a) + hi = a; + } + } + a = fb->free[0]; + if(!a) + break; + if(fmark(a)) + break; + if(!lo || lo > a) + lo = a; + if(!hi || hi < a) + hi = a; + if(p) + putbuf(p); + p = xtag(a, Tfree, QPNONE); + if(!p) + break; + fb = (Fbuf*)p->iobuf; + } + if(p) + putbuf(p); + if (flags & Ctrim) { + fsize = hi--; /* fsize = hi + 1 */ + sb->fsize = fsize; + mod++; + print("set fsize to %lld\n", (Wideoff)fsize); + } + print("lo = %lld; hi = %lld\n", (Wideoff)lo, (Wideoff)hi); +} + +/* + * make freelist from scratch + */ +static +void +mkfreelist(Superb *sb) +{ + Off a; + int i, b; + + if(ronly) { + print("cant make freelist on ronly device\n"); + return; + } + strcpy(name, "free list"); + memset(&sb->fbuf, 0, sizeof(sb->fbuf)); + sb->fbuf.free[0] = 0L; + sb->fbuf.nfree = 1; + sb->tfree = 0; + for(a=fsize-fstart-1; a >= 0; a--) { + i = a/8; + if(i < 0 || i >= sizabits) + continue; + b = 1 << (a&7); + if(abits[i] & b) + continue; + addfree(dev, fstart+a, sb); + } + print("%lld blocks free\n", (Wideoff)sb->tfree); + mod++; +} + +static +Dentry* +maked(Off a, int s, Off qpath) +{ + Iobuf *p; + Dentry *d, *d1; + + p = xtag(a, Tdir, qpath); + if(!p) + return 0; + d = getdir(p, s); + d1 = malloc(sizeof(Dentry)); + memmove(d1, d, sizeof(Dentry)); + putbuf(p); + return d1; +} + +static +void +modd(Off a, int s, Dentry *d1) +{ + Iobuf *p; + Dentry *d; + + if(!(flags & Cbad)) + return; + p = getbuf(dev, a, Bread); + d = getdir(p, s); + if(!d) { + if(p) + putbuf(p); + return; + } + memmove(d, d1, sizeof(Dentry)); + p->flags |= Bmod; + putbuf(p); +} + +static +void +xread(Off a, Off qpath) +{ + Iobuf *p; + + p = xtag(a, Tfile, qpath); + if(p) + putbuf(p); +} + +static +Iobuf* +xtag(Off a, int tag, Off qpath) +{ + Iobuf *p; + + if(a == 0) + return 0; + p = getbuf(dev, a, Bread); + if(!p) { + print("check: \"%s\": xtag: p null\n", name); + if(flags & (Cream|Ctag)) { + p = getbuf(dev, a, Bmod); + if(p) { + memset(p->iobuf, 0, RBUFSIZE); + settag(p, tag, qpath); + mod++; + return p; + } + } + return 0; + } + if(checktag(p, tag, qpath)) { + print("check: \"%s\": xtag: checktag\n", name); + if(flags & (Cream|Ctag)) { + if(flags & Cream) + memset(p->iobuf, 0, RBUFSIZE); + settag(p, tag, qpath); + mod++; + return p; + } + return p; + } + return p; +} + +static +int +amark(Off a) +{ + Off i; + int b; + + if(a < fstart || a >= fsize) { + if(a == 0) + return 0; + print("check: \"%s\": range %lld\n", + name, (Wideoff)a); + nbad++; + return 1; + } + a -= fstart; + i = a/8; + b = 1 << (a&7); + if(abits[i] & b) { + if(!ronly) { + if(ndup < 10) + print("check: \"%s\": address dup %lld\n", + name, (Wideoff)fstart+a); + else + if(ndup == 10) + print("..."); + } + ndup++; + return 1; + } + abits[i] |= b; + nused++; + return 0; +} + +static +int +fmark(Off a) +{ + Off i; + int b; + + if(a < fstart || a >= fsize) { + print("check: \"%s\": range %lld\n", + name, (Wideoff)a); + nbad++; + return 1; + } + a -= fstart; + i = a/8; + b = 1 << (a&7); + if(abits[i] & b) { + print("check: \"%s\": address dup %lld\n", + name, (Wideoff)fstart+a); + nfdup++; + return 1; + } + abits[i] |= b; + nfree++; + return 0; +} + +static +int +ftest(Off a) +{ + Off i; + int b; + + if(a < fstart || a >= fsize) + return 1; + a -= fstart; + i = a/8; + b = 1 << (a&7); + if(abits[i] & b) + return 1; + abits[i] |= b; + return 0; +} + +static +void +missing(void) +{ + Off a, i; + int b, n; + + n = 0; + for(a=fsize-fstart-1; a>=0; a--) { + i = a/8; + b = 1 << (a&7); + if(!(abits[i] & b)) { + print("missing: %lld\n", (Wideoff)fstart+a); + n++; + } + if(n > 10) { + print(" ...\n"); + break; + } + } +} + +static +void +qmark(Off qpath) +{ + int b; + Off i; + + i = qpath/8; + b = 1 << (qpath&7); + if(i < 0 || i >= sizqbits) { + nqbad++; + if(nqbad < 20) + print("check: \"%s\": qid out of range %llux\n", + name, (Wideoff)qpath); + return; + } + if((qbits[i] & b) && !ronly) { + nqbad++; + if(nqbad < 20) + print("check: \"%s\": qid dup %llux\n", name, + (Wideoff)qpath); + } + qbits[i] |= b; +} diff -Nru /sys/src/fs/port/clock.c /sys/src/fs/port/clock.c --- /sys/src/fs/port/clock.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/port/clock.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,271 @@ +#include "all.h" +#include "mem.h" +#include "io.h" +#include "ureg.h" + +Alarm* alarmtab; +Talarm talarm; + +/* + * Insert new into list after where + */ +static void +insert(Alarm **head, Alarm *where, Alarm *new) +{ + if(where == 0){ + new->next = *head; + *head = new; + }else{ + new->next = where->next; + where->next = new; + } + +} + +/* + * Delete old from list. where->next is known to be old. + */ +static void +delete(Alarm **head, Alarm *where, Alarm *old) +{ + if(where == 0){ + *head = old->next; + return; + } + where->next = old->next; +} + +Alarm* +alarm(int ms, void (*f)(Alarm*, void*), void *arg) +{ + Alarm *a, *w, *pw; + ulong s; + + if(ms < 0) + ms = 0; + a = newalarm(); + a->dt = MS2TK(ms); + a->f = f; + a->arg = arg; + s = splhi(); + lock(&m->alarmlock); + pw = 0; + for(w=m->alarm; w; pw=w, w=w->next){ + if(w->dt <= a->dt){ + a->dt -= w->dt; + continue; + } + w->dt -= a->dt; + break; + } + insert(&m->alarm, pw, a); + unlock(&m->alarmlock); + splx(s); + return a; +} + +void +cancel(Alarm *a) +{ + a->f = 0; +} + +Alarm* +newalarm(void) +{ + int i; + Alarm *a; + + for(i=0,a=alarmtab; ibusy==0 && a->f==0 && canlock(a)){ + if(a->busy){ + unlock(a); + continue; + } + a->f = 0; + a->arg = 0; + a->busy = 1; + unlock(a); + return a; + } + panic("newalarm"); + return 0; +} + +void +alarminit(void) +{ + extern char etext[]; + + cons.minpc = KTZERO; + cons.maxpc = (ulong)etext; + cons.nprofbuf = (cons.maxpc-cons.minpc) >> LRES; + cons.profbuf = ialloc(cons.nprofbuf*sizeof(cons.profbuf[0]), 0); + + alarmtab = ialloc(conf.nalarm*sizeof(Alarm), 0); +} + +static +struct +{ + int nfilter; + Filter* filters[1000]; + int time; + int index; + int cons; +} f; + +void +dofilter(Filter *ft, int c1, int c2, int c3) +{ + int i; + + i = f.nfilter; + if(i >= nelem(f.filters)) { + print("dofilter: too many filters\n"); + return; + } + ft->c1 = c1; + ft->c2 = c2; + ft->c3 = c3; + f.filters[i] = ft; + f.nfilter = i+1; +} + +void +checkalarms(void) +{ + User *p; + Timet now; + + if(talarm.list == 0 || canlock(&talarm) == 0) + return; + + now = MACHP(0)->ticks; + for(;;) { + p = talarm.list; + if(p == 0) + break; + + if(p->twhen == 0) { + talarm.list = p->tlink; + p->trend = 0; + continue; + } + if(now < p->twhen) + break; + wakeup(p->trend); + talarm.list = p->tlink; + p->trend = 0; + } + + unlock(&talarm); +} + +void +processfilt(int n) +{ + int i; + Filter *ft; + ulong c0, c1; + + n += f.index; + if(n > f.nfilter) + n = f.nfilter; + + for(i=f.index; icount; + c1 = c0 - ft->oldcount; + ft->oldcount = c0; + if(ft->c2) + ft->filter = famd(ft->filter, c1, ft->c1, ft->c2); + } + f.index = n; +} + +void +clock(Timet n, ulong pc) +{ + int i; + Alarm *a; + void (*fn)(Alarm*, void*); + User *p; + + clockreload(n); + m->ticks++; + + if(cons.profile) { + cons.profbuf[0] += TK2MS(1); + if(cons.minpc<=pc && pc>= LRES; + cons.profbuf[pc] += TK2MS(1); + } else + cons.profbuf[1] += TK2MS(1); + } + + lights(Lreal, (m->ticks>>6)&1); + if(m->machno == 0){ + if(f.cons >= 0) { + (*consputc)(f.cons); + f.cons = -1; + } + p = m->proc; + if(p == 0) + p = m->intrp; + if(p) { + p->time[0].count += TK2MS(1); + p->time[1].count += TK2MS(1); + p->time[2].count += TK2MS(1); + } + for(i=1; iproc; + if(p && p != m->intrp) { + p->time[0].count += TK2MS(1); + p->time[1].count += TK2MS(1); + p->time[2].count += TK2MS(1); + } + } + } + m->intrp = 0; + + f.time += TK2MS(1); + processfilt(TK2SEC(f.nfilter)); + while(f.time >= 1000) { + f.time -= 1000; + processfilt(f.nfilter-f.index); + f.index = 0; + } + } + + if(active.exiting && active.machs&(1<machno)){ + print("someone's exiting\n"); + exit(); + } + + checkalarms(); + + if(canlock(&m->alarmlock)){ + if(m->alarm){ + a = m->alarm; + a->dt--; + while(a && a->dt<=0){ + fn = a->f; /* avoid race with cancel */ + if(fn) + (*fn)(a, a->arg); + delete(&m->alarm, 0, a); + a->busy = 0; + a = m->alarm; + } + } + unlock(&m->alarmlock); + } +} + +void +consstart(int c) +{ + f.cons = c; +} diff -Nru /sys/src/fs/port/con.c /sys/src/fs/port/con.c --- /sys/src/fs/port/con.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/port/con.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,925 @@ +#include "all.h" +#include "mem.h" + +static char conline[2*Maxword]; +static Command command[100]; +static Flag flag[35]; +static void installcmds(void); +static void consserve1(void); +static char statsdef[20]; /* default stats list */ +static int whoflag; + +void +consserve(void) +{ + int i; + + strncpy(cons.chan->whochan, "console", sizeof(cons.chan->whochan)); + installcmds(); + con_session(); + cmd_exec("cfs"); + cmd_exec("users"); + cmd_exec("version"); + + for(i = 0; command[i].arg0; i++){ + if(strcmp("cwcmd", command[i].arg0) == 0){ + cmd_exec("cwcmd touchsb"); + break; + } + } + + userinit(consserve1, 0, "con"); +} + +static +void +consserve1(void) +{ + int i, ch; + + /*conslock();*/ + +loop: + + print("%s: ", service); + for(i=0;;) { + ch = getc(); + switch(ch) { + default: + if(i < nelem(conline)-2) + conline[i++] = ch; + break; + case '\b': + if(i > 0) + i--; + break; + case 'U' - '@': + i = 0; + break; + case '\n': + conline[i] = 0; + cmd_exec(conline); + case -1: + goto loop; + case 'D' - '@': + print("\n"); + /*conslock();*/ + goto loop; + } + } +} + +static +int +cmdcmp(void *va, void *vb) +{ + Command *a, *b; + + a = va; + b = vb; + return strcmp(a->arg0, b->arg0); +} + +void +cmd_install(char *arg0, char *help, void (*func)(int, char*[])) +{ + int i; + + qlock(&cons); + for(i=0; command[i].arg0; i++) + ; + if(i >= nelem(command)-2) { + qunlock(&cons); + print("cmd_install: too many commands\n"); + return; + } + command[i+1].arg0 = 0; + command[i].help = help; + command[i].func = func; + command[i].arg0 = arg0; + qsort(command, i+1, sizeof(Command), cmdcmp); + qunlock(&cons); +} + +void +cmd_exec(char *arg) +{ + char line[2*Maxword], *s; + char *argv[10]; + int argc, i, c; + + if(strlen(arg) >= nelem(line)-2) { + print("cmd_exec: line too long\n"); + return; + } + strcpy(line, arg); + + argc = 0; + s = line; + c = *s++; + for(;;) { + while(c == ' ' || c == '\t') + c = *s++; + if(c == 0) + break; + if(argc >= nelem(argv)-2) { + print("cmd_exec: too many args\n"); + return; + } + argv[argc++] = s-1; + while(c != ' ' && c != '\t' && c != 0) + c = *s++; + s[-1] = 0; + } + if(argc <= 0) + return; + for(i=0; s=command[i].arg0; i++) + if(strcmp(argv[0], s) == 0) { + (*command[i].func)(argc, argv); + prflush(); + return; + } + print("cmd_exec: unknown command: %s\n", argv[0]); +} + +static +void +cmd_halt(int, char *[]) +{ + + wlock(&mainlock); /* halt */ + sync("halt"); + exit(); +} + +static +void +cmd_duallow(int argc, char *argv[]) +{ + int uid; + + if(argc <= 1) { + duallow = 0; + return; + } + + uid = strtouid(argv[1]); + if(uid < 0) + uid = number(argv[1], -2, 10); + if(uid < 0) { + print("bad uid %s\n", argv[1]); + return; + } + duallow = uid; +} + +static +void +cmd_stats(int argc, char *argv[]) +{ + int i, c; + char buf[30], *s, *p, *q; + + if(argc <= 1) { + if(statsdef[0] == 0) + strcpy(statsdef, "a"); + sprint(buf, "stats s%s", statsdef); + cmd_exec(buf); + return; + } + + strcpy(buf, "stat"); + p = strchr(buf, 0); + p[1] = 0; + + q = 0; + for(i=1; iarg0, b->arg0); +} + +ulong +flag_install(char *arg, char *help) +{ + int i; + + qlock(&cons); + for(i=0; flag[i].arg0; i++) + ; + if(i >= 32) { + qunlock(&cons); + print("flag_install: too many flags\n"); + return 0; + } + flag[i+1].arg0 = 0; + flag[i].arg0 = arg; + flag[i].help = help; + flag[i].flag = 1<next) + if(cp->flags) + print("flag[%3d] = %.4lux\n", cp->chan, cp->flags); + return; + } + + f = 0; + n = -1; + for(i=1; inext) { + if(cp->chan == n) { + cp->flags ^= f; + if(f == 0) + cp->flags = 0; + print("flag[%3d] = %.8lux\n", cp->chan, cp->flags); + return; + } + } + print("no such channel\n"); +} + +static +void +cmd_who(int argc, char *argv[]) +{ + Chan *cp; + int i, c; + + c = 0; + for(cp = chans; cp; cp = cp->next) { + if(cp->whotime == 0 && !(cons.flags & whoflag)) { + c++; + continue; + } + if(argc > 1) { + for(i=1; iwhoname) == 0) + break; + if(i >= argc) { + c++; + continue; + } + } + print("%3d: %10s %24s%7W%7W", + cp->chan, + cp->whoname ? cp->whoname: "", + cp->whochan, + &cp->work, + &cp->rate); + if(cp->whoprint) + cp->whoprint(cp); + print("\n"); + prflush(); + } + if(c > 0) + print("%d chans not listed\n", c); +} + +static +void +cmd_hangup(int argc, char *argv[]) +{ + Chan *cp; + int n; + + if(argc < 2) { + print("usage: hangup chan number\n"); + return; + } + n = number(argv[1], -1, 10); + for(cp = chans; cp; cp = cp->next) { + if(cp->whotime == 0) { + if(cp->chan == n) + print("that chan is hung up\n"); + continue; + } + if(cp->chan == n) + fileinit(cp); + } +} + +static +void +cmd_sync(int, char *[]) +{ + + wlock(&mainlock); /* sync */ + sync("command"); + wunlock(&mainlock); + print("\n"); +} + +static +void +cmd_help(int argc, char *argv[]) +{ + char *arg; + int i, j; + + for(i=0; arg=command[i].arg0; i++) { + if(argc > 1) { + for(j=1; j= NAMELEN) { + print("name too long %s\n", p); + return; + } + + memset(elem, 0, sizeof(elem)); + strcpy(elem, p); + + uid = strtouid(argv[2]); + if(uid < -1) + uid = number(argv[2], -2, 10); + if(uid < -1) { + print("bad uid %s\n", argv[2]); + return; + } + + gid = strtouid(argv[3]); + if(gid < -1) + gid = number(argv[3], -2, 10); + if(gid < -1) { + print("bad gid %s\n", argv[3]); + return; + } + + perm = number(argv[4], 0777, 8) & 0777; + + if(argc > 5) { + if(strchr(argv[5], 'l')) + perm |= PLOCK; + if(strchr(argv[5], 'a')) + perm |= PAPND; + if(strchr(argv[5], 'd')) + perm |= PDIR; + } + + if(con_create(FID2, elem, uid, gid, perm, 0)) + print("create failed: %s/%s\n", argv[1], p); +} + +static +void +cmd_clri(int argc, char *argv[]) +{ + int i; + + for(i=1; iqid.path; + typ = Tfile; + if(d->mode & DDIR) + typ = Tdir; + for(i=0; idblock[i]); + ckblock(p->dev, d->dblock[i], typ, qpath); + if(i == n) { + d->dblock[i] = a; + mod = 1; + print("dblock[%d] modified %lld\n", i, (Wideoff)a); + } + } + + /* add NDBLOCK so user can cite block address by index */ + for (i = 0; i < NIBLOCK; i++) { + print("iblocks[%d] = %lld\n", NDBLOCK+i, (Wideoff)d->iblocks[i]); + ckblock(p->dev, d->iblocks[i], Tind1+i, qpath); + if(NDBLOCK+i == n) { + d->iblocks[i] = a; + mod = 1; + print("iblocks[%d] modified %lld\n", NDBLOCK+i, (Wideoff)a); + } + } + + if(mod) + p->flags |= Bmod|Bimm; +} + +static +void +cmd_clean(int argc, char *argv[]) +{ + int n; + Off a; + Iobuf *p; + Dentry *d; + File *f; + + p = 0; + f = 0; + while(argc > 1) { + n = -1; + if(argc > 2) + n = number(argv[2], -1, 10); + a = 0; + if(argc > 3) + a = number(argv[3], 0, 10); + if(walkto(argv[1])) { + print("cant remove %s\n", argv[1]); + break; + } + f = filep(cons.chan, FID2, 0); + if(!f) + break; + if(n >= 0 && f->fs->dev->type == Devro) { + print("readonly %s\n", argv[1]); + break; + } + p = getbuf(f->fs->dev, f->addr, Bread); + d = getdir(p, f->slot); + if(!d || !(d->mode & DALLOC)) { + print("not alloc %s\n", argv[1]); + break; + } + doclean(p, d, n, a); + break; + } + if(f) + qunlock(f); + if(p) + putbuf(p); +} + +static +void +cmd_remove(int argc, char *argv[]) +{ + int i; + + for(i=1; i 1) + name = argv[1]; + fs = fsstr(name); + if(fs == 0) { + print("%s: unknown file system\n", name); + if(cons.curfs) + return; + fs = &filsys[0]; + } + if(con_attach(FID1, "adm", fs->name)) + panic("FID1 attach to root"); + cons.curfs = fs; + print("current fs is \"%s\"\n", cons.curfs->name); +} + +static +void +cmd_prof(int argc, char *argv[]) +{ + int n; + long m, o; + char *p; + + if(cons.profbuf == 0) { + print("no buffer\n"); + return; + } + n = !cons.profile; + if(argc > 1) + n = number(argv[1], n, 10); + if(n && !cons.profile) { + print("clr and start\n"); + memset(cons.profbuf, 0, cons.nprofbuf*sizeof(cons.profbuf[0])); + cons.profile = 1; + return; + } + if(!n && cons.profile) { + cons.profile = 0; + print("stop and write\n"); + if(walkto("/adm/kprofdata")) + goto bad; + if(con_open(FID2, OWRITE|OTRUNC)) { + bad: + print("cant open /adm/kprofdata\n"); + return; + } + p = (char*)cons.profbuf; + for(m=0; m>24; + p[1] = n>>16; + p[2] = n>>8; + p[3] = n>>0; + p += 4; + } + + m = cons.nprofbuf*sizeof(cons.profbuf[0]); + o = 0; + while(m > 0) { + n = 8192; + if(n > m) + n = m; + con_write(FID2, (char*)cons.profbuf+o, o, n); + m -= n; + o += n; + } + return; + } +} + +static +void +cmd_time(int argc, char *argv[]) +{ + Timet t1, t2; + int i; + + t1 = MACHP(0)->ticks; + conline[0] = 0; + for(i=1; iticks; + print("time = %ld ms\n", TK2MS(t2-t1)); +} + +void +cmd_noattach(int, char *[]) +{ + + noattach = !noattach; + if(noattach) + print("attaches are DISABLED\n"); +} + +void +cmd_files(int, char *[]) +{ + long i, n; + Chan *cp; + + for(cp = chans; cp; cp = cp->next) + cp->nfile = 0; + + lock(&flock); + n = 0; + for(i=0; infile++; + } + print("%ld out of %ld files used\n", n, conf.nfile); + unlock(&flock); + + n = 0; + for(cp = chans; cp; cp = cp->next) { + if(cp->nfile) { + print("%3d: %5d\n", + cp->chan, + cp->nfile); + prflush(); + n += cp->nfile; + } + } + print("%ld out of %ld files used\n", n, conf.nfile); +} + +static +void +installcmds(void) +{ + cmd_install("allow", "-- disable permission checking", cmd_allow); + cmd_install("cfs", "[file] -- set current filesystem", cmd_cfs); + cmd_install("clean", "file [bno [addr]] -- block print/fix", cmd_clean); + cmd_install("check", "[options]", cmd_check); + cmd_install("clri", "[file ...] -- purge files/dirs", cmd_clri); + cmd_install("create", "path uid gid perm [lad] -- make a file/dir", cmd_create); + cmd_install("date", "[[=+-]seconds] -- print/set date", cmd_date); + cmd_install("disallow", "-- enable permission checking", cmd_disallow); + cmd_install("duallow", "uid -- duallow", cmd_duallow); + cmd_install("flag", "-- print set flags", cmd_flag); + cmd_install("fstat", "path -- print info on a file/dir", cmd_fstat); + cmd_install("halt", "-- return to boot rom", cmd_halt); + cmd_install("help", "", cmd_help); + cmd_install("newuser", "username -- add user to /adm/users", cmd_newuser); + cmd_install("profile", "[01] -- kernel profile", cmd_prof); + cmd_install("remove", "[file ...] -- remove files/dirs", cmd_remove); + cmd_install("stata", "-- overall stats", cmd_stata); + cmd_install("stats", "[[-]flags ...] -- various stats", cmd_stats); + cmd_install("sync", "", cmd_sync); + cmd_install("time", "command -- time another command", cmd_time); + cmd_install("users", "[file] -- read /adm/users", cmd_users); + cmd_install("version", "-- print time of mk and boot", cmd_version); + cmd_install("who", "[user ...] -- print attaches", cmd_who); + cmd_install("hangup", "chan -- clunk files", cmd_hangup); + cmd_install("passwd", "passwd -- set passkey, id, and domain", cmd_passwd); + cmd_install("printconf", "-- print configuration", cmd_printconf); + cmd_install("noattach", "toggle noattach flag", cmd_noattach); + cmd_install("files", "report on files structure", cmd_files); + + attachflag = flag_install("attach", "-- attach calls"); + chatflag = flag_install("chat", "-- verbose"); + errorflag = flag_install("error", "-- on errors"); + whoflag = flag_install("allchans", "-- on who"); + authdebugflag = flag_install("authdebug", "-- report authentications"); + authdisableflag = flag_install("authdisable", "-- disable authentication"); +} + +int +walkto(char *name) +{ + char elem[NAMELEN], *p; + int n; + + if(con_clone(FID1, FID2)) + return 1; + + for(;;) { + p = utfrune(name, '/'); + if(p == 0) + p = strchr(name, 0); + if(p == name) { + if(*name == 0) + return 0; + name = p+1; + continue; + } + n = p-name; + if(n > NAMELEN) + return 1; + memset(elem, 0, sizeof(elem)); + memmove(elem, name, n); + if(con_walk(FID2, elem)) + return 1; + name = p; + } +} + +/* needs to parse and return vlongs to cope with new larger block numbers */ +vlong +number(char *arg, int def, int base) +{ + int c, sign, any; + vlong n; + + if(arg == nil) + return def; + + sign = any = 0; + n = 0; + + c = *arg; + while(c == ' ') { + arg++; + c = *arg; + } + if(c == '-') { + sign = 1; + arg++; + c = *arg; + } + while((c >= '0' && c <= '9') || + (base == 16 && c >= 'a' && c <= 'f') || + (base == 16 && c >= 'A' && c <= 'F')) { + n *= base; + if(c >= 'a' && c <= 'f') + n += c - 'a' + 10; + else if(c >= 'A' && c <= 'F') + n += c - 'A' + 10; + else + n += c - '0'; + arg++; + c = *arg; + any = 1; + } + if(!any) + return def; + if(sign) + n = -n; + return n; +} diff -Nru /sys/src/fs/port/config.c /sys/src/fs/port/config.c --- /sys/src/fs/port/config.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/port/config.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,1072 @@ +#include "all.h" +#include "io.h" + +static void dowormcopy(void); +static int dodevcopy(void); + +/* + * This is needed for IP configuration. + */ +#include "../ip/ip.h" + +struct +{ + char* icharp; + char* charp; + int error; + int newconf; /* clear befor start */ + int modconf; /* write back when done */ + int nextiter; + int lastiter; + int diriter; + int ipauthset; + Device* lastcw; + Device* devlist; +} f; + +static Device* confdev; +static int copyworm = 0, copydev = 0; +static char *src, *dest; + +int +devcmpr(Device *d1, Device *d2) +{ + while (d1 != d2) { + if(d1 == 0 || d2 == 0 || d1->type != d2->type) + return 1; + + switch(d1->type) { + default: + print("can't compare dev: %Z\n", d1); + panic("devcmp"); + return 1; + + case Devmcat: + case Devmlev: + case Devmirr: + d1 = d1->cat.first; + d2 = d2->cat.first; + while(d1 && d2) { + if(devcmpr(d1, d2)) + return 1; + d1 = d1->link; + d2 = d2->link; + } + break; + + case Devnone: + return 0; + + case Devro: + d1 = d1->ro.parent; + d2 = d2->ro.parent; + break; + + case Devjuke: + case Devcw: + if(devcmpr(d1->cw.c, d2->cw.c)) + return 1; + d1 = d1->cw.w; + d2 = d2->cw.w; + break; + + case Devfworm: + d1 = d1->fw.fw; + d2 = d2->fw.fw; + break; + + case Devwren: + case Devworm: + case Devlworm: + case Devide: + case Devmarvsata: + if(d1->wren.ctrl == d2->wren.ctrl) + if(d1->wren.targ == d2->wren.targ) + if(d1->wren.lun == d2->wren.lun) + return 0; + return 1; + + case Devpart: + if(d1->part.base == d2->part.base) + if(d1->part.size == d2->part.size) { + d1 = d1->part.d; + d2 = d2->part.d; + break; + } + return 1; + } + } + return 0; +} + +void +cdiag(char *s, int c1) +{ + + f.charp--; + if(f.error == 0) { + print("config diag: %s -- <%c>\n", s, c1); + f.error = 1; + } +} + +int +cnumb(void) +{ + int c, n; + + c = *f.charp++; + if(c == '<') { + n = f.nextiter; + if(n >= 0) { + f.nextiter = n+f.diriter; + if(n == f.lastiter) { + f.nextiter = -1; + f.lastiter = -1; + } + do { + c = *f.charp++; + } while (c != '>'); + return n; + } + n = cnumb(); + if(*f.charp++ != '-') { + cdiag("- expected", f.charp[-1]); + return 0; + } + c = cnumb(); + if(*f.charp++ != '>') { + cdiag("> expected", f.charp[-1]); + return 0; + } + f.lastiter = c; + f.diriter = 1; + if(n > c) + f.diriter = -1; + f.nextiter = n+f.diriter; + return n; + } + if(c < '0' || c > '9') { + cdiag("number expected", c); + return 0; + } + n = 0; + while(c >= '0' && c <= '9') { + n = n*10 + (c-'0'); + c = *f.charp++; + } + f.charp--; + return n; +} + +Device* +config1(int c) +{ + Device *d, *t; + int m; + + d = ialloc(sizeof(Device), 0); + for(;;) { + t = config(); + if(d->cat.first == 0) + d->cat.first = t; + else + d->cat.last->link = t; + d->cat.last = t; + if(f.error) + return devnone; + m = *f.charp; + if(c == '(' && m == ')') { + d->type = Devmcat; + break; + } + if(c == '[' && m == ']') { + d->type = Devmlev; + break; + } + if(c == '{' && m == '}') { + d->type = Devmirr; + break; + } + } + f.charp++; + if(d->cat.first == d->cat.last) + d = d->cat.first; + return d; +} + +Device* +config(void) +{ + int c, m; + Device *d; + char *icp; + + if(f.error) + return devnone; + d = ialloc(sizeof(Device), 0); + + c = *f.charp++; + switch(c) { + default: + cdiag("unknown type", c); + return devnone; + + case '(': /* (d+) one or multiple cat */ + case '[': /* [d+] one or multiple interleave */ + case '{': /* {d+} a mirrored device and optional mirrors */ + return config1(c); + + case 'f': /* fd fake worm */ + d->type = Devfworm; + d->fw.fw = config(); + break; + + case 'n': + d->type = Devnone; + break; + + case 'w': /* w[#.]#[.#] wren [ctrl] unit [lun] */ + case 'h': /* h[#.]# ide [ctlr] unit */ + case 'm': /* m[#.]# marvell sata [ctlr] unit */ + case 'r': /* r# worm side */ + case 'l': /* l# labelled-worm side */ + icp = f.charp; + if(c == 'm') + d->type = Devmarvsata; + else if(c == 'h') + d->type = Devide; + else + d->type = Devwren; + d->wren.ctrl = 0; + d->wren.targ = cnumb(); + d->wren.lun = 0; + m = *f.charp; + if(m == '.') { + f.charp++; + d->wren.lun = cnumb(); + m = *f.charp; + if(m == '.') { + f.charp++; + d->wren.ctrl = d->wren.targ; + d->wren.targ = d->wren.lun; + d->wren.lun = cnumb(); + } + } + if(f.nextiter >= 0) + f.charp = icp-1; + if(c == 'r') { /* worms are virtual and not uniqued */ + d->type = Devworm; + break; + } + if(c == 'l') { + d->type = Devlworm; + break; + } + break; + + case 'o': /* o ro part of last cw */ + if(f.lastcw == 0) { + cdiag("no cw to match", c); + return devnone; + } + return f.lastcw->cw.ro; + + case 'j': /* DD jukebox */ + d->type = Devjuke; + d->j.j = config(); + d->j.m = config(); + break; + + case 'c': /* cache/worm */ + d->type = Devcw; + d->cw.c = config(); + d->cw.w = config(); + d->cw.ro = ialloc(sizeof(Device), 0); + d->cw.ro->type = Devro; + d->cw.ro->ro.parent = d; + f.lastcw = d; + break; + + case 'p': /* pd#.# partition base% size% */ + d->type = Devpart; + d->part.d = config(); + d->part.base = cnumb(); + c = *f.charp++; + if(c != '.') + cdiag("dot expected", c); + d->part.size = cnumb(); + break; + + case 'x': /* xD swab a device's metadata */ + d->type = Devswab; + d->swab.d = config(); + break; + } + d->dlink = f.devlist; + f.devlist = d; + return d; +} + +char* +strdup(char *s) +{ + int n; + char *s1; + + n = strlen(s); + s1 = ialloc(n+1, 0); + strcpy(s1, s); + return s1; +} + +Device* +iconfig(char *s) +{ + Device *d; + + f.nextiter = -1; + f.lastiter = -1; + f.error = 0; + f.icharp = s; + f.charp = f.icharp; + d = config(); + if(*f.charp) { + cdiag("junk on end", *f.charp); + f.error = 1; + } + return d; +} + +int +testconfig(char *s) +{ + + iconfig(s); + return f.error; +} + +int +astrcmp(char *a, char *b) +{ + int n, c; + + n = strlen(b); + if(memcmp(a, b, n)) + return 1; + c = a[n]; + if(c == 0) { + aindex = 0; + return 0; + } + if(a[n+1]) + return 1; + if(c >= '0' && c <= '9') { + aindex = c - '0'; + return 0; + } + return 1; +} + +void +mergeconf(Iobuf *p) +{ + char word[Maxword+1]; + char *cp; + Filsys *fs; + + for (cp = p->iobuf; ; cp++) { + cp = getwd(word, cp); + if(strcmp(word, "") == 0) + return; + else if(strcmp(word, "service") == 0) { + cp = getwd(word, cp); + if(service[0] == 0) + strcpy(service, word); + } else if(strcmp(word, "ipauth") == 0) { + cp = getwd(word, cp); + if(!f.ipauthset) + if(chartoip(authip, word)) + goto bad; + } else if(astrcmp(word, "ip") == 0) { + cp = getwd(word, cp); + if(!isvalidip(ipaddr[aindex].sysip)) + if(chartoip(ipaddr[aindex].sysip, word)) + goto bad; + } else if(astrcmp(word, "ipgw") == 0) { + cp = getwd(word, cp); + if(!isvalidip(ipaddr[aindex].defgwip)) + if(chartoip(ipaddr[aindex].defgwip, word)) + goto bad; + } else if(astrcmp(word, "ipsntp") == 0) { + cp = getwd(word, cp); + if (!isvalidip(sntpip)) + if (chartoip(sntpip, word)) + goto bad; + } else if(astrcmp(word, "ipmask") == 0) { + cp = getwd(word, cp); + if(!isvalidip(ipaddr[aindex].defmask)) + if(chartoip(ipaddr[aindex].defmask, word)) + goto bad; + } else if(strcmp(word, "filsys") == 0) { + cp = getwd(word, cp); + for(fs=filsys; fs->name; fs++) + if(strcmp(fs->name, word) == 0) { + if(fs->flags & FEDIT) { + cp = getwd(word, cp); + goto loop; + } + break; + } + fs->name = strdup(word); + cp = getwd(word, cp); + fs->conf = strdup(word); + } else { +bad: + putbuf(p); + panic("unknown word in config block: %s", word); + } +loop: + if(*cp != '\n') + goto bad; + } +} + +void +cmd_printconf(int, char *[]) +{ + char *p, *s; + Iobuf *iob; + + iob = getbuf(confdev, 0, Bread); + if(iob == nil) + return; + if(checktag(iob, Tconfig, 0)){ + putbuf(iob); + return; + } + + print("config %s\n", nvrgetconfig()); + s = p = iob->iobuf; + while(*p != 0 && p < iob->iobuf+BUFSIZE){ + if(*p++ != '\n') + continue; + print("%.*s", (int)(p-s), s); + s = p; + } + if(p != s) + print("%.*s", (int)(p-s), s); + print("end\n"); + + putbuf(iob); +} + +extern void floppyhalt(void); + +void +sysinit(void) +{ + Filsys *fs; + int error, i; + Device *d; + Iobuf *p; + char *cp; + + dofilter(u->time+0, C0a, C0b, 1); + dofilter(u->time+1, C1a, C1b, 1); + dofilter(u->time+2, C2a, C2b, 1); + dofilter(cons.work+0, C0a, C0b, 1); + dofilter(cons.work+1, C1a, C1b, 1); + dofilter(cons.work+2, C2a, C2b, 1); + dofilter(cons.rate+0, C0a, C0b, 1000); + dofilter(cons.rate+1, C1a, C1b, 1000); + dofilter(cons.rate+2, C2a, C2b, 1000); + dofilter(cons.bhit+0, C0a, C0b, 1); + dofilter(cons.bhit+1, C1a, C1b, 1); + dofilter(cons.bhit+2, C2a, C2b, 1); + dofilter(cons.bread+0, C0a, C0b, 1); + dofilter(cons.bread+1, C1a, C1b, 1); + dofilter(cons.bread+2, C2a, C2b, 1); + dofilter(cons.brahead+0, C0a, C0b, 1); + dofilter(cons.brahead+1, C1a, C1b, 1); + dofilter(cons.brahead+2, C2a, C2b, 1); + dofilter(cons.binit+0, C0a, C0b, 1); + dofilter(cons.binit+1, C1a, C1b, 1); + dofilter(cons.binit+2, C2a, C2b, 1); + cons.chan = chaninit(Devcon, 1, 0); + +start: + /* + * part 1 -- read the config file + */ + devnone = iconfig("n"); + + cp = nvrgetconfig(); + print("config %s\n", cp); + + confdev = d = iconfig(cp); + devinit(d); + if(f.newconf) { + p = getbuf(d, 0, Bmod); + memset(p->iobuf, 0, RBUFSIZE); + settag(p, Tconfig, 0); + } else + p = getbuf(d, 0, Bread|Bmod); + if(!p || checktag(p, Tconfig, 0)) + panic("config io"); + mergeconf(p); + if(f.modconf) { + memset(p->iobuf, 0, BUFSIZE); + p->flags |= Bmod|Bimm; + if(service[0]) + sprint(strchr(p->iobuf, 0), "service %s\n", service); + for(fs=filsys; fs->name; fs++) + if(fs->conf) + sprint(strchr(p->iobuf, 0), + "filsys %s %s\n", fs->name, fs->conf); + sprint(strchr(p->iobuf, 0), "ipauth %I\n", authip); + sprint(strchr(p->iobuf, 0), "ipsntp %I\n", sntpip); + for(i=0; i<10; i++) { + if(isvalidip(ipaddr[i].sysip)) + sprint(strchr(p->iobuf, 0), + "ip%d %I\n", i, ipaddr[i].sysip); + if(isvalidip(ipaddr[i].defgwip)) + sprint(strchr(p->iobuf, 0), + "ipgw%d %I\n", i, ipaddr[i].defgwip); + if(isvalidip(ipaddr[i].defmask)) + sprint(strchr(p->iobuf, 0), + "ipmask%d %I\n", i, ipaddr[i].defmask); + } + putbuf(p); + f.modconf = 0; + f.newconf = 0; + print("config block written\n"); + goto start; + } + putbuf(p); + + print("service %s\n", service); + print("ipauth %I\n", authip); + print("ipsntp %I\n", sntpip); + for(i=0; i<10; i++) { + if(isvalidip(ipaddr[i].sysip)) { + print("ip%d %I\n", i, ipaddr[i].sysip); + print("ipgw%d %I\n", i, ipaddr[i].defgwip); + print("ipmask%d %I\n", i, ipaddr[i].defmask); + } + } + +loop: + /* + * part 2 -- squeeze out the deleted filesystems + */ + for(fs=filsys; fs->name; fs++) + if(fs->conf == 0) { + for(; fs->name; fs++) + *fs = *(fs+1); + goto loop; + } + if(filsys[0].name == 0) + panic("no filsys"); + + /* + * part 3 -- compile the device expression + */ + error = 0; + for(fs=filsys; fs->name; fs++) { + print("filsys %s %s\n", fs->name, fs->conf); + fs->dev = iconfig(fs->conf); + if(f.error) { + error = 1; + continue; + } + } + if(error) + panic("fs config"); + + /* + * part 4 -- initialize the devices + */ + for(fs=filsys; fs->name; fs++) { + delay(3000); + print("sysinit: %s\n", fs->name); + if(fs->flags & FREAM) + devream(fs->dev, 1); + if(fs->flags & FRECOVER) + devrecover(fs->dev); + devinit(fs->dev); + } + + floppyhalt(); /* don't wear out the floppy */ + if (copyworm) { + dowormcopy(); /* can return if user quits early */ + panic("copyworm bailed out!"); + } + if (copydev) + if (dodevcopy() < 0) + panic("copydev failed!"); + else + panic("copydev done."); +} + +/* an unfinished idea. a non-blocking rawchar() would help. */ +static int +userabort(char *msg) +{ +#ifdef IdeaIsFinished + if (consgetcifany() == 'q') { + print("aborting %s\n", msg); + return 1; + } +#else + USED(msg); +#endif /* IdeaIsFinished */ + return 0; +} + +static int +blockok(Device *d, Off a) +{ + Iobuf *p = getbuf(d, a, Bread); + + if (p == 0) { + print("i/o error reading %Z block %lld\n", d, (Wideoff)a); + return 0; + } + putbuf(p); + return 1; +} + +/* + * special case for fake worms only: + * we need to size the inner cw's worm device. + * in particular, we want to avoid copying the fake-worm bitmap + * at the end of the device. + * + * N.B.: for real worms (e.g. cw jukes), we need to compute devsize(cw(juke)), + * *NOT* devsize(juke). + */ +static Device * +wormof(Device *dev) +{ + Device *worm = dev, *cw; + + if (dev->type == Devfworm) { + cw = dev->fw.fw; + if (cw != nil && cw->type == Devcw) + worm = cw->cw.w; + } + // print("wormof(%Z)=%Z\n", dev, worm); + return worm; +} + +/* + * return the number of the highest-numbered block actually written, plus 1. + * 0 indicates an error. + */ +static Devsize +writtensize(Device *worm) +{ + Devsize lim = devsize(worm); + Iobuf *p; + + print("devsize(%Z) = %lld\n", worm, (Wideoff)lim); + if (!blockok(worm, 0) || !blockok(worm, lim-1)) + return 0; + delay(5*1000); + if (userabort("sanity checks")) + return 0; + + /* find worm's last valid block in case "worm" is an (f)worm */ + while (lim > 0) { + if (userabort("sizing")) { + lim = 0; /* you lose */ + break; + } + --lim; + p = getbuf(worm, lim, Bread); + if (p != 0) { /* actually read one okay? */ + putbuf(p); + break; + } + } + print("limit(%Z) = %lld\n", worm, (Wideoff)lim); + return lim <= 0? 0: lim + 1; +} + +/* copy worm fs from "main"'s inner worm to "output" */ +static void +dowormcopy(void) +{ + Filsys *f1, *f2; + Device *fdev, *from, *to = nil; + Iobuf *p; + Off a; + Devsize lim; + + /* + * convert file system names into Filsyss and Devices. + */ + + f1 = fsstr("main"); + if(f1 == nil) + panic("main file system missing"); + fdev = f1->dev; + from = wormof(fdev); /* fake worm special */ + if (from->type != Devfworm && from->type != Devcw) { + print("main file system is not a worm; copyworm may not do what you want!\n"); + print("waiting for 20 seconds...\n"); + delay(20000); + } + + f2 = fsstr("output"); + if(f2 == nil) { + print("no output file system - check only\n\n"); + print("reading worm from %Z (worm %Z)\n", fdev, from); + } else { + to = f2->dev; + print("\ncopying worm from %Z (worm %Z) to %Z, starting in 8 seconds\n", + fdev, from, to); + delay(8000); + } + if (userabort("preparing to copy")) + return; + + /* + * initialise devices, size them, more sanity checking. + */ + + devinit(from); + if (0 && fdev != from) { + devinit(fdev); + print("debugging, sizing %Z first\n", fdev); + writtensize(fdev); + } + lim = writtensize(from); + if(lim == 0) + panic("no blocks to copy on %Z", from); + if (to) { + print("reaming %Z in 8 seconds\n", to); + delay(8000); + if (userabort("preparing to ream & copy")) + return; + devream(to, 0); + devinit(to); + print("copying worm: %lld blocks from %Z to %Z\n", + (Wideoff)lim, from, to); + } + /* can't read to's blocks in case to is a real WORM device */ + + /* + * Copy written fs blocks, a block at a time (or just read + * if no "output" fs). + */ + + for (a = 0; a < lim; a++) { + if (userabort("copy")) + break; + p = getbuf(from, a, Bread); + /* + * if from is a real WORM device, we'll get errors trying to + * read unwritten blocks, but the unwritten blocks need not + * be contiguous. + */ + if (p == 0) { + print("%lld not written yet; can't read\n", (Wideoff)a); + continue; + } + if (to != 0 && devwrite(to, p->addr, p->iobuf) != 0) { + print("out block %lld: write error; bailing", + (Wideoff)a); + break; + } + putbuf(p); + if(a % 20000 == 0) + print("block %lld %T\n", (Wideoff)a, time()); + } + + /* + * wrap up: sync target, loop + */ + print("copied %lld blocks from %Z to %Z\n", (Wideoff)a, from, to); + sync("wormcopy"); + delay(2000); + print("looping; reset the machine at any time.\n"); + for (; ; ) + continue; /* await reset */ +} + +/* copy device from src to dest */ +static int +dodevcopy(void) +{ + Device *from, *to; + Iobuf *p; + Off a; + Devsize lim, tosize; + + /* + * convert config strings into Devices. + */ + from = iconfig(src); + if(f.error || from == nil) { + print("bad src device %s\n", src); + return -1; + } + to = iconfig(dest); + if(f.error || to == nil) { + print("bad dest device %s\n", dest); + return -1; + } + + /* + * initialise devices, size them, more sanity checking. + */ + + devinit(from); + lim = devsize(from); + if(lim == 0) + panic("no blocks to copy on %Z", from); + devinit(to); + tosize = devsize(to); + if(tosize == 0) + panic("no blocks to copy on %Z", to); + + /* use smaller of the device sizes */ + if (tosize < lim) + lim = tosize; + + print("copy %Z to %Z in 8 seconds\n", from, to); + delay(8000); + if (userabort("preparing to copy")) + return -1; + print("copying dev: %lld blocks from %Z to %Z\n", (Wideoff)lim, + from, to); + + /* + * Copy all blocks, a block at a time. + */ + + for (a = 0; a < lim; a++) { + if (userabort("copy")) + break; + p = getbuf(from, a, Bread); + /* + * if from is a real WORM device, we'll get errors trying to + * read unwritten blocks, but the unwritten blocks need not + * be contiguous. + */ + if (p == 0) { + print("%lld not written yet; can't read\n", (Wideoff)a); + continue; + } + if (to != 0 && devwrite(to, p->addr, p->iobuf) != 0) { + print("out block %lld: write error; bailing", + (Wideoff)a); + break; + } + putbuf(p); + if(a % 20000 == 0) + print("block %lld %T\n", (Wideoff)a, time()); + } + + /* + * wrap up: sync target + */ + print("copied %lld blocks from %Z to %Z\n", (Wideoff)a, from, to); + sync("devcopy"); + return 0; +} + +void +getline(char *line) +{ + char *p; + int c; + + p = line; + for(;;) { + c = rawchar(0); + if(c == 0 || c == '\n') { + *p = 0; + return; + } + if(c == '\b') { + p--; + continue; + } + *p++ = c; + } +} + +void +arginit(void) +{ + int verb, c; + char line[2*Maxword], word[Maxword+1], *cp; + uchar localip[Pasize]; + Filsys *fs; + + if(nvrcheck() == 0){ + print("for config mode hit a key within 5 seconds\n"); + c = rawchar(5); + if(c == 0) { + print(" no config\n"); + return; + } + } + + for (;;) { + print("config: "); + getline(line); + cp = getwd(word, line); + if (word[0] == '\0' || word[0] == '#') + continue; + if(strcmp(word, "end") == 0) + return; + if(strcmp(word, "halt") == 0) { + floppyhalt(); + exit(); + } + + if(strcmp(word, "allow") == 0) { + wstatallow = 1; + writeallow = 1; + continue; + } + if(strcmp(word, "copyworm") == 0) { + copyworm = 1; + continue; + } + if(strcmp(word, "copydev") == 0) { /* not yet documented */ + cp = getwd(word, cp); + if(testconfig(word)) + continue; + src = strdup(word); + getwd(word, cp); + if(testconfig(word)) + continue; + dest = strdup(word); + copydev = 1; + continue; + } + if(strcmp(word, "noauth") == 0) { + noauth = !noauth; + continue; + } + if(strcmp(word, "noattach") == 0) { + noattach = !noattach; + continue; + } + if(strcmp(word, "readonly") == 0) { + readonly = 1; + continue; + } + + if(strcmp(word, "ream") == 0) { + verb = FREAM; + goto gfsname; + } + if(strcmp(word, "recover") == 0) { + verb = FRECOVER; + goto gfsname; + } + if(strcmp(word, "filsys") == 0) { + verb = FEDIT; + goto gfsname; + } + + if(strcmp(word, "nvram") == 0) { + getwd(word, cp); + if(testconfig(word)) + continue; + /* if it fails, it will complain */ + nvrsetconfig(word); + continue; + } + if(strcmp(word, "config") == 0) { + getwd(word, cp); + if(!testconfig(word) && nvrsetconfig(word) == 0) + f.newconf = 1; + continue; + } + if(strcmp(word, "service") == 0) { + getwd(word, cp); + strcpy(service, word); + f.modconf = 1; + continue; + } + + if(strcmp(word, "ipauth") == 0) { + f.ipauthset = 1; + verb = 2; + } else if(astrcmp(word, "ip") == 0) + verb = 0; + else if(astrcmp(word, "ipgw") == 0) + verb = 1; + else if(astrcmp(word, "ipmask") == 0) + verb = 3; + else if(astrcmp(word, "ipsntp") == 0) + verb = 4; + else { + print("unknown config command\n"); + print(" type end to get out\n"); + continue; + } + + getwd(word, cp); + if(chartoip(localip, word)) { + print("bad ip address\n"); + continue; + } + switch(verb) { + case 0: + memmove(ipaddr[aindex].sysip, localip, + sizeof(ipaddr[aindex].sysip)); + break; + case 1: + memmove(ipaddr[aindex].defgwip, localip, + sizeof(ipaddr[aindex].defgwip)); + break; + case 2: + memmove(authip, localip, sizeof(authip)); + break; + case 3: + memmove(ipaddr[aindex].defmask, localip, + sizeof(ipaddr[aindex].defmask)); + break; + case 4: + memmove(sntpip, localip, sizeof(sntpip)); + break; + } + f.modconf = 1; + continue; + + gfsname: + cp = getwd(word, cp); + for(fs=filsys; fs->name; fs++) + if(strcmp(word, fs->name) == 0) + break; + if (fs->name == nil) { + memset(fs, 0, sizeof(*fs)); + fs->name = strdup(word); + } + switch(verb) { + case FREAM: + if(strcmp(fs->name, "main") == 0) + wstatallow = 1; /* only set, never reset */ + case FRECOVER: + fs->flags |= verb; + break; + case FEDIT: + f.modconf = 1; + getwd(word, cp); + fs->flags |= verb; + if(word[0] == 0) + fs->conf = nil; + else if(!testconfig(word)) + fs->conf = strdup(word); + break; + } + } +} diff -Nru /sys/src/fs/port/console.c /sys/src/fs/port/console.c --- /sys/src/fs/port/console.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/port/console.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,320 @@ +#include "all.h" + +#include "9p1.h" + +void +fcall9p1(Chan *cp, Fcall *in, Fcall *ou) +{ + int t; + + rlock(&mainlock); + t = in->type; + if(t < 0 || t >= MAXSYSCALL || (t&1) || !call9p1[t]) { + print("bad message type %d\n", t); + panic(""); + } + ou->type = t+1; + ou->err = 0; + + rlock(&cp->reflock); + (*call9p1[t])(cp, in, ou); + runlock(&cp->reflock); + + if(ou->err) + if(CHAT(cp)) + print(" error: %s\n", errstr9p[ou->err]); + cons.work[0].count++; + cons.work[1].count++; + cons.work[2].count++; + runlock(&mainlock); +} + +int +con_session(void) +{ + Fcall in, ou; + + in.type = Tsession; + fcall9p1(cons.chan, &in, &ou); + return ou.err; +} + +int +con_attach(int fid, char *uid, char *arg) +{ + Fcall in, ou; + + in.type = Tattach; + in.fid = fid; + strncpy(in.uname, uid, NAMELEN); + strncpy(in.aname, arg, NAMELEN); + fcall9p1(cons.chan, &in, &ou); + return ou.err; +} + +int +con_clone(int fid1, int fid2) +{ + Fcall in, ou; + + in.type = Tclone; + in.fid = fid1; + in.newfid = fid2; + fcall9p1(cons.chan, &in, &ou); + return ou.err; +} + +int +con_walk(int fid, char *name) +{ + Fcall in, ou; + + in.type = Twalk; + in.fid = fid; + strncpy(in.name, name, NAMELEN); + fcall9p1(cons.chan, &in, &ou); + return ou.err; +} + +int +con_open(int fid, int mode) +{ + Fcall in, ou; + + in.type = Topen; + in.fid = fid; + in.mode = mode; + fcall9p1(cons.chan, &in, &ou); + return ou.err; +} + +int +con_read(int fid, char *data, Off offset, int count) +{ + Fcall in, ou; + + in.type = Tread; + in.fid = fid; + in.offset = offset; + in.count = count; + ou.data = data; + fcall9p1(cons.chan, &in, &ou); + if(ou.err) + return 0; + return ou.count; +} + +int +con_write(int fid, char *data, Off offset, int count) +{ + Fcall in, ou; + + in.type = Twrite; + in.fid = fid; + in.data = data; + in.offset = offset; + in.count = count; + fcall9p1(cons.chan, &in, &ou); + if(ou.err) + return 0; + return ou.count; +} + +int +con_remove(int fid) +{ + Fcall in, ou; + + in.type = Tremove; + in.fid = fid; + fcall9p1(cons.chan, &in, &ou); + return ou.err; +} + +int +con_create(int fid, char *name, int uid, int gid, long perm, int mode) +{ + Fcall in, ou; + + in.type = Tcreate; + in.fid = fid; + strncpy(in.name, name, NAMELEN); + in.perm = perm; + in.mode = mode; + cons.uid = uid; /* beyond ugly */ + cons.gid = gid; + fcall9p1(cons.chan, &in, &ou); + return ou.err; +} + +int +doclri(File *f) +{ + Iobuf *p, *p1; + Dentry *d, *d1; + int err; + + err = 0; + p = 0; + p1 = 0; + if(f->fs->dev->type == Devro) { + err = Eronly; + goto out; + } + /* + * check on parent directory of file to be deleted + */ + if(f->wpath == 0 || f->wpath->addr == f->addr) { + err = Ephase; + goto out; + } + p1 = getbuf(f->fs->dev, f->wpath->addr, Bread); + d1 = getdir(p1, f->wpath->slot); + if(!d1 || checktag(p1, Tdir, QPNONE) || !(d1->mode & DALLOC)) { + err = Ephase; + goto out; + } + + accessdir(p1, d1, FWRITE, 0); + putbuf(p1); + p1 = 0; + + /* + * check on file to be deleted + */ + p = getbuf(f->fs->dev, f->addr, Bread); + d = getdir(p, f->slot); + + + /* + * do it + */ + memset(d, 0, sizeof(Dentry)); + settag(p, Tdir, QPNONE); + freewp(f->wpath); + freefp(f); + +out: + if(p1) + putbuf(p1); + if(p) + putbuf(p); + return err; +} + +void +f_fstat(Chan *cp, Fcall *in, Fcall *ou) +{ + File *f; + Iobuf *p; + Dentry *d; + int i; + + if(CHAT(cp)) { + print("c_fstat %d\n", cp->chan); + print(" fid = %d\n", in->fid); + } + + p = 0; + f = filep(cp, in->fid, 0); + if(!f) { + ou->err = Efid; + goto out; + } + p = getbuf(f->fs->dev, f->addr, Bread); + d = getdir(p, f->slot); + if(d == 0) + goto out; + + print("name = %.*s\n", NAMELEN, d->name); + print("uid = %d; gid = %d; muid = %d\n", d->uid, d->gid, d->muid); + print("size = %lld; qid = %llux/%lux\n", (Wideoff)d->size, + (Wideoff)d->qid.path, d->qid.version); + print("atime = %ld; mtime = %ld\n", d->atime, d->mtime); + print("dblock ="); + for(i=0; idblock[i]); + for (i = 0; i < NIBLOCK; i++) + print("; iblocks[%d] = %lld", i, (Wideoff)d->iblocks[i]); + print("\n\n"); + +out: + if(p) + putbuf(p); + ou->fid = in->fid; + if(f) + qunlock(f); +} + +void +f_clri(Chan *cp, Fcall *in, Fcall *ou) +{ + File *f; + + if(CHAT(cp)) { + print("c_clri %d\n", cp->chan); + print(" fid = %d\n", in->fid); + } + + f = filep(cp, in->fid, 0); + if(!f) { + ou->err = Efid; + goto out; + } + ou->err = doclri(f); + +out: + ou->fid = in->fid; + if(f) + qunlock(f); +} + +int +con_clri(int fid) +{ + Fcall in, ou; + Chan *cp; + + in.type = Tremove; + in.fid = fid; + cp = cons.chan; + + rlock(&mainlock); + ou.type = Tremove+1; + ou.err = 0; + + rlock(&cp->reflock); + f_clri(cp, &in, &ou); + runlock(&cp->reflock); + + cons.work[0].count++; + cons.work[1].count++; + cons.work[2].count++; + runlock(&mainlock); + return ou.err; +} + +int +con_fstat(int fid) +{ + Fcall in, ou; + Chan *cp; + + in.type = Tstat; + in.fid = fid; + cp = cons.chan; + + rlock(&mainlock); + ou.type = Tstat+1; + ou.err = 0; + + rlock(&cp->reflock); + f_fstat(cp, &in, &ou); + runlock(&cp->reflock); + + cons.work[0].count++; + cons.work[1].count++; + cons.work[2].count++; + runlock(&mainlock); + return ou.err; +} diff -Nru /sys/src/fs/port/data.c /sys/src/fs/port/data.c --- /sys/src/fs/port/data.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/port/data.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,150 @@ +#include "all.h" + +char *errstr9p[MAXERR] = +{ + [Ebadspc] "attach -- bad specifier", + [Efid] "unknown fid", + [Echar] "bad character in directory name", + [Eopen] "read/write -- on non open fid", + [Ecount] "read/write -- count too big", + [Ealloc] "phase error -- directory entry not allocated", + [Eqid] "phase error -- qid does not match", + [Eaccess] "access permission denied", + [Eentry] "directory entry not found", + [Emode] "open/create -- unknown mode", + [Edir1] "walk -- in a non-directory", + [Edir2] "create -- in a non-directory", + [Ephase] "phase error -- cannot happen", + [Eexist] "create/wstat -- file exists", + [Edot] "create/wstat -- . and .. illegal names", + [Eempty] "remove -- directory not empty", + [Ebadu] "attach -- unknown user or failed authentication", + [Enoattach] "attach -- system maintenance", + [Ewstatb] "wstat -- unknown bits in qid.type/mode", + [Ewstatd] "wstat -- attempt to change directory", + [Ewstatg] "wstat -- not in group", + [Ewstatl] "wstat -- attempt to make length negative", + [Ewstatm] "wstat -- attempt to change muid", + [Ewstato] "wstat -- not owner or group leader", + [Ewstatp] "wstat -- attempt to change qid.path", + [Ewstatq] "wstat -- qid.type/dir.mode mismatch", + [Ewstatu] "wstat -- not owner", + [Ewstatv] "wstat -- attempt to change qid.vers", + [Ename] "create/wstat -- bad character in file name", + [Ewalk] "walk -- too many (system wide)", + [Eronly] "file system read only", + [Efull] "file system full", + [Eoffset] "read/write -- offset negative", + [Elocked] "open/create -- file is locked", + [Ebroken] "read/write -- lock is broken", + [Eauth] "attach -- authentication failed", + [Eauth2] "read/write -- authentication unimplemented", + [Etoolong] "name too long", + [Efidinuse] "fid in use", + [Econvert] "protocol botch", + [Eversion] "version conversion", + [Eauthnone] "auth -- user 'none' requires no authentication", + [Eauthdisabled] "auth -- authentication disabled", /* development */ + [Eauthfile] "auth -- out of auth files", + [Eedge] "at the bleeding edge", /* development */ +}; + +char* wormscode[0x80] = +{ + [0x00] "no sense", + [0x01] "invalid command", + [0x02] "recovered error", + [0x03] "illegal request", + [0x06] "unit attention", + [0x07] "parity error", + [0x08] "message reject error", + [0x0a] "copy aborted", + [0x0b] "initiator detected error", + [0x0c] "select re-select failed", + [0x0e] "miscompare", + + [0x10] "ecc trouble occurred", + [0x11] "time out error", + [0x12] "controller error", + [0x13] "sony i/f II hardware/firmware error", + [0x14] "scsi hardware/firmware error", + [0x15] "rom version unmatched error", + [0x16] "logical block address out of range", + + [0x20] "command not terminated", + [0x21] "drive interface parity error", + [0x22] "loading trouble", + [0x23] "focus trouble", + [0x24] "tracking trouble", + [0x25] "spindle trouble", + [0x26] "slide trouble", + [0x27] "skew trouble", + [0x28] "head lead out", + [0x29] "write modulation trouble", + [0x2a] "under laser power", + [0x2b] "over laser power", + [0x2f] "drive error", + + [0x30] "drive power off", + [0x31] "no disk in drive", + [0x32] "drive not ready", + [0x38] "disk already exists in drive", + [0x39] "no disk in shelf", + [0x3a] "disk already exists in shelf", + + [0x40] "write warning", + [0x41] "write error", + [0x42] "disk error", + [0x43] "cannot read disk ID", + [0x44] "write protect error 1", + [0x45] "write protect error 2", + [0x46] "disk warning", + [0x47] "alternation trouble", + + [0x50] "specified address not found", + [0x51] "address block not found", + [0x52] "all address could not be read", + [0x53] "data could not be read", + [0x54] "uncorrectable read error", + [0x55] "tracking error", + [0x56] "write servo error", + [0x57] "write monitor error", + [0x58] "write verify error", + + [0x60] "no data in specified address", + [0x61] "blank check failed", + [0x62] "controller diagnostics failed", + [0x63] "drive diagnostice failed", + [0x64] "diagnostice aborted", + [0x67] "juke diagnostice failed", + [0x68] "z-axis servo failed", + [0x69] "roter servo error", + [0x6a] "hook servo error", + [0x6b] "I/O self error", + [0x6c] "drive 0 error", + [0x6d] "drive 1 error", + [0x6e] "shelf error", + [0x6f] "carrier error", + + [0x70] "rob made me do it", + [0x71] "out of range", +}; + +char* tagnames[] = +{ + [Tbuck] "Tbuck", + [Tdir] "Tdir", + [Tfile] "Tfile", + [Tfree] "Tfree", + [Tind1] "Tind1", + [Tind2] "Tind2", +#ifndef OLD + [Tind3] "Tind3", + [Tind4] "Tind4", + /* add more Tind tags here ... */ +#endif + [Tnone] "Tnone", + [Tsuper] "Tsuper", + [Tvirgo] "Tvirgo", + [Tcache] "Tcache", +}; diff -Nru /sys/src/fs/port/dentry.c /sys/src/fs/port/dentry.c --- /sys/src/fs/port/dentry.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/port/dentry.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,343 @@ +#include "all.h" + +Dentry* +getdir(Iobuf *p, int slot) +{ + Dentry *d; + + if(!p) + return 0; + d = (Dentry*)p->iobuf + slot%DIRPERBUF; + return d; +} + +void +accessdir(Iobuf *p, Dentry *d, int f, int uid) +{ + Timet t; + + if(p && p->dev->type != Devro) { + p->flags |= Bmod; + t = time(); + if(f & (FREAD|FWRITE)) + d->atime = t; + if(f & FWRITE) { + d->mtime = t; + d->muid = uid; + d->qid.version++; + } + } +} + +void +preread(Device *d, Off addr) +{ + Rabuf *rb; + + if(addr == 0) + return; + if(raheadq->count+10 >= raheadq->size) /* ugly knowing layout */ + return; + lock(&rabuflock); + rb = rabuffree; + if(rb == 0) { + unlock(&rabuflock); + return; + } + rabuffree = rb->link; + unlock(&rabuflock); + rb->dev = d; + rb->addr = addr; + send(raheadq, rb); + cons.brahead[0].count++; + cons.brahead[1].count++; + cons.brahead[2].count++; +} + +Off +rel2abs(Iobuf *p, Dentry *d, Off a, int tag, int putb, int uid) +{ + int i; + Off addr, qpath, indaddrs = 1, div; + Device *dev; + + if(a < 0) { + print("rel2abs: neg offset\n"); + if(putb) + putbuf(p); + return 0; + } + dev = p->dev; + qpath = d->qid.path; + + /* is `a' a direct block? */ + if(a < NDBLOCK) { + addr = d->dblock[a]; + if(!addr && tag) { + addr = bufalloc(dev, tag, qpath, uid); + d->dblock[a] = addr; + p->flags |= Bmod|Bimm; + } + if(putb) + putbuf(p); + return addr; + } + a -= NDBLOCK; + + /* + * loop through indirect block depths. + */ + for (i = 0; i < NIBLOCK; i++) { + indaddrs *= INDPERBUF; + /* is a's disk addr in this indir block or one of its kids? */ + if (a < indaddrs) { + addr = d->iblocks[i]; + if(!addr && tag) { + addr = bufalloc(dev, Tind1+i, qpath, uid); + d->iblocks[i] = addr; + p->flags |= Bmod|Bimm; + } + if(putb) + putbuf(p); + + div = indaddrs; + for (; i >= 0; i--) { + div /= INDPERBUF; + if (div <= 0) + panic("rel2abs: non-positive divisor"); + addr = indfetch(dev, qpath, addr, + (a/div)%INDPERBUF, Tind1+i, + (i == 0? tag: Tind1+i-1), uid); + } + return addr; + } + a -= indaddrs; + } + if(putb) + putbuf(p); + + /* quintuple-indirect blocks not implemented. */ + print("rel2abs: no %d-deep indirect\n", NIBLOCK+1); + return 0; +} + +/* + * read-ahead strategy + * on second block, read RAGAP blocks, + * thereafter, read RAGAP ahead of current pos + */ +Off +dbufread(Iobuf *p, Dentry *d, Off a, Off ra, int uid) +{ + Off addr; + + if(a == 0) + return 1; + if(a == 1 && ra == 1) { + while(ra < a+RAGAP) { + ra++; + addr = rel2abs(p, d, ra, 0, 0, uid); + if(!addr) + return 0; + preread(p->dev, addr); + } + return ra+1; + } + if(ra == a+RAGAP) { + addr = rel2abs(p, d, ra, 0, 0, uid); + if(!addr) + return 0; + preread(p->dev, addr); + return ra+1; + } + return ra; +} + +Iobuf* +dnodebuf(Iobuf *p, Dentry *d, Off a, int tag, int uid) +{ + Off addr; + + addr = rel2abs(p, d, a, tag, 0, uid); + if(addr) + return getbuf(p->dev, addr, Bread); + return 0; +} + +/* + * same as dnodebuf but it calls putbuf(p) + * to reduce interference. + */ +Iobuf* +dnodebuf1(Iobuf *p, Dentry *d, Off a, int tag, int uid) +{ + Off addr; + Device *dev; + + dev = p->dev; + addr = rel2abs(p, d, a, tag, 1, uid); + if(addr) + return getbuf(dev, addr, Bread); + return 0; + +} + +Off +indfetch(Device* d, Off qpath, Off addr, Off a, int itag, int tag, int uid) +{ + Iobuf *bp; + + if(!addr) + return 0; + bp = getbuf(d, addr, Bread); + if(!bp || checktag(bp, itag, qpath)) { + if(!bp) { + print("ind fetch bp = 0\n"); + return 0; + } + print("ind fetch tag\n"); + putbuf(bp); + return 0; + } + addr = ((Off *)bp->iobuf)[a]; + if(!addr && tag) { + addr = bufalloc(d, tag, qpath, uid); + if(addr) { + ((Off *)bp->iobuf)[a] = addr; + bp->flags |= Bmod; + if(tag == Tdir) + bp->flags |= Bimm; + settag(bp, itag, qpath); + } + } + putbuf(bp); + return addr; +} + +/* return INDPERBUF^exp */ +Off +ibbpow(int exp) +{ + static Off pows[] = { + 1, + INDPERBUF, + (Off)INDPERBUF*INDPERBUF, + (Off)INDPERBUF*(Off)INDPERBUF*INDPERBUF, + (Off)INDPERBUF*(Off)INDPERBUF*(Off)INDPERBUF*INDPERBUF, + }; + + if (exp < 0) + return 0; + else if (exp >= nelem(pows)) { /* not in table? do it long-hand */ + Off indpow = 1; + + while (exp-- > 0) + indpow *= INDPERBUF; + return indpow; + } else + return pows[exp]; +} + +/* return sum of INDPERBUF^n for 1 ≤ n ≤ exp */ +Off +ibbpowsum(int exp) +{ + Off indsum = 0; + + for (; exp > 0; exp--) + indsum += ibbpow(exp); + return indsum; +} + +/* zero bytes past new file length; return an error code */ +int +trunczero(Truncstate *ts) +{ + int blkoff = ts->newsize % BUFSIZE; + Iobuf *pd; + + pd = dnodebuf(ts->p, ts->d, ts->lastblk, Tfile, ts->uid); + if (pd == nil || checktag(pd, Tfile, QPNONE)) { + if (pd != nil) + putbuf(pd); + ts->err = Ephase; + return Ephase; + } + memset(pd->iobuf+blkoff, 0, BUFSIZE - blkoff); + putbuf(pd); + return 0; +} + +/* + * truncate d (in p) to length `newsize'. + * if larger, just increase size. + * if smaller, deallocate blocks after last one + * still in file at new size. last byte to keep + * is newsize-1, due to zero origin. + * we free in forward order because it's simpler to get right. + * if the final block at the new size is partially-filled, + * zero the remainder. + */ +int +dtrunclen(Iobuf *p, Dentry *d, Off newsize, int uid) +{ + int i, pastlast; + Truncstate trunc; + + if (newsize <= 0) { + dtrunc(p, d, uid); + return 0; + } + memset(&trunc, 0, sizeof trunc); + trunc.d = d; + trunc.p = p; + trunc.uid = uid; + trunc.newsize = newsize; + trunc.lastblk = newsize/BUFSIZE; + if (newsize % BUFSIZE == 0) + trunc.lastblk--; + else + trunczero(&trunc); + for (i = 0; i < NDBLOCK; i++) + if (trunc.pastlast) { + trunc.relblk = i; + buffree(p->dev, d->dblock[i], 0, &trunc); + d->dblock[i] = 0; + } else if (i == trunc.lastblk) + trunc.pastlast = 1; + trunc.relblk = NDBLOCK; + for (i = 0; i < NIBLOCK; i++) { + pastlast = trunc.pastlast; + buffree(p->dev, d->iblocks[i], i+1, &trunc); + if (pastlast) + d->iblocks[i] = 0; + } + + d->size = newsize; + p->flags |= Bmod|Bimm; + accessdir(p, d, FWRITE, uid); + return trunc.err; +} + +/* + * truncate d (in p) to zero length. + * freeing blocks in reverse order is traditional, from Unix, + * in an attempt to keep the free list contiguous. + */ +void +dtrunc(Iobuf *p, Dentry *d, int uid) +{ + int i; + + for (i = NIBLOCK-1; i >= 0; i--) { + buffree(p->dev, d->iblocks[i], i+1, nil); + d->iblocks[i] = 0; + } + for(i=NDBLOCK-1; i>=0; i--) { + buffree(p->dev, d->dblock[i], 0, nil); + d->dblock[i] = 0; + } + d->size = 0; + p->flags |= Bmod|Bimm; + accessdir(p, d, FWRITE, uid); +} diff -Nru /sys/src/fs/port/devcons.c /sys/src/fs/port/devcons.c --- /sys/src/fs/port/devcons.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/port/devcons.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,321 @@ +#include "all.h" + +/* from ../pc/8250.c */ +extern int uartcons; + +static +struct +{ + Lock; + uchar buf[4000]; + uchar *in; + uchar *out; + int printing; +} printq; + +static +struct +{ + Lock; + Rendez; + uchar buf[500]; + uchar *in; + uchar *out; + int reading; +} readq; + +int (*consgetc)(void); +void (*consputc)(int); +void (*consputs)(char*, int); + +/* + * Put a string on the console. + * n bytes of s are guaranteed to fit in the buffer and is ready to print. + * Must be called splhi() and with printq locked. + * If early in booting (predawn) poll output. + * This is the interrupt driven routine used for non- screen-based systems. + */ +static void +puts(char *s, int n) +{ + + if(predawn) { + while(n > 0) { + (*consputc)(*s++ & 0xFF); + delay(5); + n--; + } + return; + } + if(!printq.printing){ + printq.printing = 1; + consstart(*s++ & 0xFF); + n--; + } + memmove(printq.in, s, n); + printq.in += n; + if(printq.in >= printq.buf+sizeof(printq.buf)) + printq.in = printq.buf; +} + +void +printinit(void) +{ + lock(&printq); /* allocate lock */ + printq.in = printq.buf; + printq.out = printq.buf; + unlock(&printq); + + lock(&readq); /* allocate lock */ + readq.in = readq.buf; + readq.out = readq.buf; + unlock(&readq); + + consinit(puts); +} + +/* + * Print a string on the console. This is the high level routine + * with a queue to the interrupt handler. BUG: There is no check against + * overflow. + */ +void +putstrn(char *str, int n) +{ + int s, m; + char *t; + + s = splhi(); + lock(&printq); + while(n > 0){ + if(*str == '\n') + (*consputs)("\r", 1); + m = printq.buf+sizeof(printq.buf) - printq.in; + if(n < m) + m = n; + t = memchr(str+1, '\n', m-1); + if(t) + if(t-str < m) + m = t - str; + (*consputs)(str, m); + n -= m; + str += m; + } + unlock(&printq); + splx(s); +} + +/* + * get character to print at interrupt time + * always called splhi from proc 0 + */ +int +conschar(void) +{ + uchar *p; + int c, s; + + s = splhi(); + lock(&printq); + p = printq.out; + if(p == printq.in) { + printq.printing = 0; + c = -1; + } else { + c = *p++; + if(p >= printq.buf+sizeof(printq.buf)) + p = printq.buf; + printq.out = p; + } + unlock(&printq); + splx(s); + return c; +} + +#define ctl(c) ((c) & 037) + +/* + * dispose of input character at interrupt time + * always called splhi from proc 0 + */ +void +kbdchar(int c) +{ + int s; + uchar *p; + uchar ch; + char uparrow[2]; + + if(c == ctl('t')) + dotrace(0); + s = splhi(); + lock(&readq); + if(readq.reading) + goto out; + if(c == ctl('u')) { + if(echo) + putstrn("^U\n", 3); /* echo */ + readq.in = readq.out; + goto out; + } + if(c == '\r') + c = '\n'; + ch = c; + if(echo) + /* + * cga mode (for example) does a poor job of showing control + * chars, so echo them as ^X. this will mess up subsequent + * backspace erasures, alas. it also doesn't cope with utf. + */ + if (ch >= ' ' || ch == '\n' || ch == '\t' || ch == '\b') + putstrn((char*)&ch, 1); /* echo */ + else { + uparrow[0] = '^'; + uparrow[1] = ch + '@'; /* ctrl -> upper case */ + putstrn(uparrow, 2); + } + p = readq.in; + *p++ = c; + if(p >= readq.buf+sizeof(readq.buf)) + p = readq.buf; + readq.in = p; + if(c == '\n' || c == ctl('d')) { + readq.reading = 1; + wakeup(&readq); + } +out: + unlock(&readq); + splx(s); +} + +int +rawchar(int seconds) +{ + int c; + ulong s; + + if(seconds != 0) + seconds += toytime(); + + for(;;) { + c = (*consgetc)(); + if(c) { + if(c == '\r') + c = '\n'; + /* ugh! yet another place that does echoing */ + if(c == '\n') { + (*consputc)('\r'); + delay(10); + } + (*consputc)(c); + return c; + } + if(seconds) { + /* Allow MACHP(0)->ticks to run */ + s = spllo(); + if(toytime() > seconds) { + splx(s); + break; + } + splx(s); + } + delay(1); + } + return 0; +} + +int +reading(void*) +{ + + return readq.reading; +} + + +/* + * get a character from the console + * this is the high level routine with + * a queue to the interrupt handler + */ +int +getc(void) +{ + int c, s; + uchar *p; + + c = 0; + +loop: + sleep(&readq, reading, 0); + s = splhi(); + lock(&readq); + p = readq.out; + if(readq.in == p) { + readq.reading = 0; + unlock(&readq); + splx(s); + goto loop; + } + if(readq.in != p) { + c = *p++; + if(p == readq.buf+sizeof(readq.buf)) + p = readq.buf; + readq.out = p; + } + unlock(&readq); + splx(s); + return c; +} + +int +print(char *fmt, ...) +{ + int n; + va_list arg; + char buf[PRINTSIZE]; + + va_start(arg, fmt); + n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + putstrn(buf, n); + + return n; +} + +void +panic(char *fmt, ...) +{ + int n; + va_list arg; + char buf[PRINTSIZE]; + + lights(Lpanic, 1); + /* if the only console is vga, conserve it */ + if (uartcons) + dumpstack(u); + strcpy(buf, "panic: "); + va_start(arg, fmt); + n = vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + buf[n] = '\n'; + putstrn(buf, n+1); + if(!predawn){ + spllo(); + prflush(); + } + exit(); +} + +void +prflush(void) +{ + int i; + + if(predawn || !uartcons) + return; + for(i=0; i<50; i++) { + if(!printq.printing) + break; + delay(100); + } +} diff -Nru /sys/src/fs/port/devsd.c /sys/src/fs/port/devsd.c --- /sys/src/fs/port/devsd.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/port/devsd.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,787 @@ +/* + * Storage Device. + */ +#include "all.h" +#include "io.h" +#include "mem.h" + +#include "sd.h" +#include "fs.h" +#include "compat.h" +#undef error + +#define parttrace 0 + +extern SDifc sdataifc, sdmv50xxifc; + +SDifc* sdifc[] = { + &sdataifc, + &sdmv50xxifc, + nil, +}; + +static SDev* sdlist; +static SDunit** sdunit; +static int sdnunit; +static int _sdmask; +static int cdmask; +static int sdmask; +static QLock sdqlock; + +enum { + Rawcmd, + Rawdata, + Rawstatus, +}; + +void +sdaddpart(SDunit* unit, char* name, Devsize start, Devsize end) +{ + SDpart *pp; + int i, partno; + + if(parttrace) + print("add %d %s %s %lld %lld\n", unit->npart, unit->name, + name, start, end); + /* + * Check name not already used + * and look for a free slot. + */ + if(unit->part != nil){ + partno = -1; + for(i = 0; i < SDnpart; i++){ + pp = &unit->part[i]; + if(!pp->valid){ + if(partno == -1) + partno = i; + break; + } + if(strcmp(name, pp->name) == 0){ + if(pp->start == start && pp->end == end){ + if(parttrace) + print("already present\n"); + return; + } + } + } + }else{ + if((unit->part = malloc(sizeof(SDpart)*SDnpart)) == nil){ + if(parttrace) + print("malloc failed\n"); + return; + } + partno = 0; + } + + /* + * Check there is a free slot and size and extent are valid. + */ + if(partno == -1 || start > end || end > unit->sectors){ + print("cannot add %s!%s [%llud,%llud) to disk [0,%llud): %s\n", + unit->name, name, start, end, unit->sectors, + partno==-1 ? "no free partitions" : + "partition boundaries out of range"); + return; + } + pp = &unit->part[partno]; + pp->start = start; + pp->end = end; + strncpy(pp->name, name, NAMELEN); + pp->valid = 1; + unit->npart++; +} + +void +sddelpart(SDunit* unit, char* name) +{ + int i; + SDpart *pp; + + if(parttrace) + print("del %d %s %s\n", unit->npart, unit->name, name); + /* + * Look for the partition to delete. + * Can't delete if someone still has it open. + * If it's the last valid partition zap the + * whole table. + */ + pp = unit->part; + for(i = 0; i < SDnpart; i++){ + if(strncmp(name, pp->name, NAMELEN) == 0) + break; + pp++; + } + if(i >= SDnpart) + return; + pp->valid = 0; + + unit->npart--; + if(unit->npart == 0){ + free(unit->part); + unit->part = nil; + } +} + +static int +sdinitpart(SDunit* unit) +{ + unit->sectors = unit->secsize = 0; + unit->npart = 0; + if(unit->part){ + free(unit->part); + unit->part = nil; + } + + if(unit->inquiry[0] & 0xC0) + return 0; + switch(unit->inquiry[0] & 0x1F){ + case 0x00: /* DA */ + case 0x04: /* WORM */ + case 0x05: /* CD-ROM */ + case 0x07: /* MO */ + break; + default: + return 0; + } + + if(unit->dev->ifc->online == nil || unit->dev->ifc->online(unit) == 0) + return 0; + sdaddpart(unit, "data", 0, unit->sectors); + return 1; +} + +SDunit* +sdgetunit(SDev* sdev, int subno) +{ + int index; + SDunit *unit; + + /* + * Associate a unit with a given device and sub-unit + * number on that device. + * The device will be probed if it has not already been + * successfully accessed. + */ + qlock(&sdqlock); + index = sdev->index + subno; + unit = sdunit[index]; + if(unit == nil){ + if((unit = malloc(sizeof(SDunit))) == nil){ + qunlock(&sdqlock); + return nil; + } + + if(sdev->enabled == 0 && sdev->ifc->enable) + sdev->ifc->enable(sdev); + sdev->enabled = 1; + + snprint(unit->name, NAMELEN, "sd%c%d", sdev->idno, subno); + unit->subno = subno; + unit->dev = sdev; + + /* + * No need to lock anything here as this is only + * called before the unit is made available in the + * sdunit[] array. + */ + if(unit->dev->ifc->verify(unit) == 0){ + qunlock(&sdqlock); + free(unit); + return nil; + } + sdunit[index] = unit; + } + qunlock(&sdqlock); + return unit; +} + +SDunit* +sdindex2unit(int index) +{ + SDev *sdev; + + /* + * Associate a unit with a given index into the top-level + * device directory. + * The device will be probed if it has not already been + * successfully accessed. + */ + for(sdev = sdlist; sdev != nil; sdev = sdev->next) + if(index >= sdev->index && index < sdev->index + sdev->nunit) + return sdgetunit(sdev, index - sdev->index); + return nil; +} + +static void +_sddetach(void) +{ + SDev *sdev; + + for(sdev = sdlist; sdev != nil; sdev = sdev->next){ + if(sdev->enabled == 0) + continue; + if(sdev->ifc->disable) + sdev->ifc->disable(sdev); + sdev->enabled = 0; + } +} + +static int +_sdinit(void) +{ + ulong m; + int i; + SDev *sdev, *tail; + SDunit *unit; + + /* + * Probe all configured controllers and make a list + * of devices found, accumulating a possible maximum number + * of units attached and marking each device with an index + * into the linear top-level directory array of units. + */ + tail = nil; + for(i = 0; sdifc[i] != nil; i++){ + if((sdev = sdifc[i]->pnp()) == nil) + continue; + if(sdlist != nil) + tail->next = sdev; + else + sdlist = sdev; + for(tail = sdev; tail->next != nil; tail = tail->next){ + tail->index = sdnunit; + sdnunit += tail->nunit; + } + tail->index = sdnunit; + sdnunit += tail->nunit; + } + + /* + * Legacy and option code goes here. This will be hard... + */ + + /* + * The maximum number of possible units is known, allocate + * placeholders for their datastructures; the units will be + * probed and structures allocated when attached. + * Allocate controller names for the different types. + */ + if(sdnunit == 0) + return 0; + if((sdunit = malloc(sdnunit*sizeof(SDunit*))) == nil) + return 0; +// sddetach = _sddetach; + + for(i = 0; sdifc[i] != nil; i++){ + if(sdifc[i]->id) + sdifc[i]->id(sdlist); + } + + m = 0; + cdmask = sdmask = 0; + for(i=0; inpart > 0){ /* BUG */ + if((unit->inquiry[0] & 0x1F) == 0x05) + cdmask |= (1<name); +} + +void +sdprintdevs(int i) +{ + char *s; + SDunit *unit; + + unit = sdindex2unit(i); + for(i=0; inpart; i++){ + s = unit->part[i].name; + if(strncmp(s, "dos", 3) == 0 + || strncmp(s, "9fat", 4) == 0 + || strncmp(s, "fs", 2) == 0) + print(" %s!%s", unit->name, s); + } +} + +SDpart* +sdfindpart(SDunit *unit, char *name) +{ + int i; + + if(parttrace) + print("findpart %d %s %s\t\n", unit->npart, unit->name, name); + for(i=0; inpart; i++) { + if(parttrace) + print("%s...", unit->part[i].name); + if(strcmp(unit->part[i].name, name) == 0){ + if(parttrace) + print("\n"); + return &unit->part[i]; + } + } + if(parttrace) + print("not found\n"); + return nil; +} + +typedef struct Scsicrud Scsicrud; +struct Scsicrud { + Fs fs; + Off offset; + SDunit *unit; + SDpart *part; +}; + +long +sdread(Fs *vcrud, void *v, long n) +{ + Scsicrud *crud; + long x; + + crud = (Scsicrud*)vcrud; + x = sdbio(crud->unit, crud->part, v, n, crud->offset); + if(x > 0) + crud->offset += x; + return x; +} + +Off +sdseek(Fs *vcrud, Off seek) +{ + ((Scsicrud*)vcrud)->offset = seek; + return seek; +} + +#ifdef notdef +void* +sdgetfspart(int i, char *s, int chatty) +{ + SDunit *unit; + SDpart *p; + Scsicrud *crud; + + if(cdmask&(1<name, s); + return nil; + } + if(p->crud == nil) { + crud = malloc(sizeof(Scsicrud)); + crud->fs.dev = i; + crud->fs.diskread = sdread; + crud->fs.diskseek = sdseek; + // crud->start = 0; + crud->unit = unit; + crud->part = p; + if(dosinit(&crud->fs) < 0 && dosinit(&crud->fs) < 0 && kfsinit(&crud->fs) < 0){ + if(chatty) + print("partition %s!%s does not contain a DOS or KFS file system\n", + unit->name, s); + return nil; + } + p->crud = crud; + } + return p->crud; +} + +/* + * Leave partitions around for devsd to pick up. + * (Needed by boot process; more extensive + * partitioning is done by termrc or cpurc). + */ +void +sdaddconf(int i) +{ + SDunit *unit; + SDpart *pp; + + unit = sdindex2unit(i); + + /* + * If there were no partitions (just data and partition), don't bother. + */ + if(unit->npart<= 1 || (unit->npart==2 && strcmp(unit->part[1].name, "partition")==0)) + return; + + addconf("%spart=", unit->name); + for(i=1, pp=&unit->part[i]; inpart; i++, pp++) /* skip 0, which is "data" */ + addconf("%s%s %ld %ld", i==1 ? "" : "/", pp->name, + pp->start, pp->end); + addconf("\n"); +} + +int +sdboot(int dev, char *pname, Boot *b) +{ + char *file; + Fs *fs; + + if((file = strchr(pname, '!')) == nil) { + print("syntax is sdC0!partition!file\n"); + return -1; + } + *file++ = '\0'; + + fs = sdgetfspart(dev, pname, 1); + if(fs == nil) + return -1; + + return fsboot(fs, file, b); +} +#endif + +long +sdbio(SDunit *unit, SDpart *pp, void* va, long len, Off off) +{ + Off l; + Off bno, max, nb, offset; + char *a; + static uchar *b; + static ulong bsz; + + a = va; +memset(a, 0xDA, len); + qlock(&unit->ctl); + if(unit->changed){ + qunlock(&unit->ctl); + return 0; + } + + /* + * Check the request is within bounds. + * Removeable drives are locked throughout the I/O + * in case the media changes unexpectedly. + * Non-removeable drives are not locked during the I/O + * to allow the hardware to optimise if it can; this is + * a little fast and loose. + * It's assumed that non-removable media parameters + * (sectors, secsize) can't change once the drive has + * been brought online. + */ + bno = (off/unit->secsize) + pp->start; + nb = ((off+len+unit->secsize-1)/unit->secsize) + pp->start - bno; + max = SDmaxio/unit->secsize; + if(nb > max) + nb = max; + if(bno+nb > pp->end) + nb = pp->end - bno; + if(bno >= pp->end || nb == 0){ + qunlock(&unit->ctl); + return 0; + } + if(!(unit->inquiry[1] & 0x80)) + qunlock(&unit->ctl); + + if(bsz < nb*unit->secsize){ + b = malloc(nb*unit->secsize); + bsz = nb*unit->secsize; + } +// b = sdmalloc(nb*unit->secsize); +// if(b == nil) +// return 0; + + offset = off%unit->secsize; + l = unit->dev->ifc->bio(unit, 0, 0, b, nb, bno); + if(l < 0) { +// sdfree(b); + return 0; + } + + if(l < offset) + len = 0; + else if(len > l - offset) + len = l - offset; + if(len) + memmove(a, b+offset, len); +// sdfree(b); + + if(unit->inquiry[1] & 0x80) + qunlock(&unit->ctl); + + return len; +} + +#ifdef DMA +long +sdrio(SDreq *r, void* a, long n) +{ + if(n >= SDmaxio || n < 0) + return 0; + + r->data = nil; + if(n){ + if((r->data = malloc(n)) == nil) + return 0; + if(r->write) + memmove(r->data, a, n); + } + r->dlen = n; + + if(r->unit->dev->ifc->rio(r) != SDok){ +// cgascreenputs("1", 1); + if(r->data != nil){ + sdfree(r->data); + r->data = nil; + } + return 0; + } +// cgascreenputs("2", 1); + + if(!r->write && r->rlen > 0) + memmove(a, r->data, r->rlen); +// cgascreenputs("3", 1); + if(r->data != nil){ + sdfree(r->data); + r->data = nil; + } + +// cgascreenputs("4", 1); + return r->rlen; +} +#endif /* DMA */ + +void* +sdmalloc(void *p, ulong sz) +{ + if(p != nil) { + memset(p, 0, sz); + return p; + } + return malloc(sz); +} + +/* + * SCSI simulation for non-SCSI devices + */ +int +sdsetsense(SDreq *r, int status, int key, int asc, int ascq) +{ + int len; + SDunit *unit; + + unit = r->unit; + unit->sense[2] = key; + unit->sense[12] = asc; + unit->sense[13] = ascq; + + if(status == SDcheck && !(r->flags & SDnosense)){ + /* request sense case from sdfakescsi */ + len = sizeof unit->sense; + if(len > sizeof r->sense-1) + len = sizeof r->sense-1; + memmove(r->sense, unit->sense, len); + unit->sense[2] = 0; + unit->sense[12] = 0; + unit->sense[13] = 0; + r->flags |= SDvalidsense; + return SDok; + } + return status; +} + +int +sdfakescsi(SDreq *r, void *info, int ilen) +{ + uchar *cmd, *p; + uvlong len; + SDunit *unit; + + cmd = r->cmd; + r->rlen = 0; + unit = r->unit; + + /* + * Rewrite read(6)/write(6) into read(10)/write(10). + */ + switch(cmd[0]){ + case 0x08: /* read */ + case 0x0A: /* write */ + cmd[9] = 0; + cmd[8] = cmd[4]; + cmd[7] = 0; + cmd[6] = 0; + cmd[5] = cmd[3]; + cmd[4] = cmd[2]; + cmd[3] = cmd[1] & 0x0F; + cmd[2] = 0; + cmd[1] &= 0xE0; + cmd[0] |= 0x20; + break; + } + + /* + * Map SCSI commands into ATA commands for discs. + * Fail any command with a LUN except INQUIRY which + * will return 'logical unit not supported'. + */ + if((cmd[1]>>5) && cmd[0] != 0x12) + return sdsetsense(r, SDcheck, 0x05, 0x25, 0); + + switch(cmd[0]){ + default: + return sdsetsense(r, SDcheck, 0x05, 0x20, 0); + + case 0x00: /* test unit ready */ + return sdsetsense(r, SDok, 0, 0, 0); + + case 0x03: /* request sense */ + if(cmd[4] < sizeof unit->sense) + len = cmd[4]; + else + len = sizeof unit->sense; + if(r->data && r->dlen >= len){ + memmove(r->data, unit->sense, len); + r->rlen = len; + } + return sdsetsense(r, SDok, 0, 0, 0); + + case 0x12: /* inquiry */ + /* warning: useless or misleading comparison: UCHAR < 0x100 */ + if(cmd[4] < sizeof unit->inquiry) + len = cmd[4]; + else + len = sizeof unit->inquiry; + if(r->data && r->dlen >= len){ + memmove(r->data, r->sense, len); + r->rlen = len; + } + return sdsetsense(r, SDok, 0, 0, 0); + + case 0x1B: /* start/stop unit */ + /* + * nop for now, can use power management later. + */ + return sdsetsense(r, SDok, 0, 0, 0); + + case 0x25: /* read capacity */ + if((cmd[1] & 0x01) || cmd[2] || cmd[3]) + return sdsetsense(r, SDcheck, 0x05, 0x24, 0); + if(r->data == nil || r->dlen < 8) + return sdsetsense(r, SDcheck, 0x05, 0x20, 1); + + /* + * Read capacity returns the LBA of the last sector. + */ + len = unit->sectors - 1; + p = r->data; + *p++ = len>>24; + *p++ = len>>16; + *p++ = len>>8; + *p++ = len; + len = 512; + *p++ = len>>24; + *p++ = len>>16; + *p++ = len>>8; + *p++ = len; + r->rlen = p - (uchar*)r->data; + return sdsetsense(r, SDok, 0, 0, 0); + + case 0x9E: /* long read capacity */ + if((cmd[1] & 0x01) || cmd[2] || cmd[3]) + return sdsetsense(r, SDcheck, 0x05, 0x24, 0); + if(r->data == nil || r->dlen < 8) + return sdsetsense(r, SDcheck, 0x05, 0x20, 1); + /* + * Read capcity returns the LBA of the last sector. + */ + len = unit->sectors - 1; + p = r->data; + *p++ = len>>56; + *p++ = len>>48; + *p++ = len>>40; + *p++ = len>>32; + *p++ = len>>24; + *p++ = len>>16; + *p++ = len>>8; + *p++ = len; + len = 512; + *p++ = len>>24; + *p++ = len>>16; + *p++ = len>>8; + *p++ = len; + r->rlen = p - (uchar*)r->data; + return sdsetsense(r, SDok, 0, 0, 0); + + case 0x5A: /* mode sense */ + return sdmodesense(r, cmd, info, ilen); + + case 0x28: /* read */ + case 0x2A: /* write */ + return SDnostatus; + } +} + +int +sdmodesense(SDreq *r, uchar *cmd, void *info, int ilen) +{ + int len; + uchar *data; + + /* + * Fake a vendor-specific request with page code 0, + * return the drive info. + */ + if((cmd[2] & 0x3F) != 0 && (cmd[2] & 0x3F) != 0x3F) + return sdsetsense(r, SDcheck, 0x05, 0x24, 0); + len = (cmd[7]<<8)|cmd[8]; + if(len == 0) + return SDok; + if(len < 8+ilen) + return sdsetsense(r, SDcheck, 0x05, 0x1A, 0); + if(r->data == nil || r->dlen < len) + return sdsetsense(r, SDcheck, 0x05, 0x20, 1); + data = r->data; + memset(data, 0, 8); + data[0] = ilen>>8; + data[1] = ilen; + if(ilen) + memmove(data+8, info, ilen); + r->rlen = 8+ilen; + return sdsetsense(r, SDok, 0, 0, 0); +} diff -Nru /sys/src/fs/port/fcmd.c /sys/src/fs/port/fcmd.c --- /sys/src/fs/port/fcmd.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/port/fcmd.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,104 @@ +#include "all.h" + +int +fchar(void) +{ + int n; + + n = BUFSIZE; + if(n > MAXDAT) + n = MAXDAT; + if(uidgc.find >= uidgc.flen) { + uidgc.find = 0; + uidgc.flen = con_read(FID2, uidgc.uidbuf->iobuf, cons.offset, n); + if(uidgc.flen <= 0) + return -1; + cons.offset += uidgc.flen; + } + return uidgc.uidbuf->iobuf[uidgc.find++] & 0xff; +} + +int +fread(void *buf, int len) +{ + int n, c; + char *b; + + b = buf; + for(n = 0; n < len; n++) { + c = fchar(); + if(c < 0) + break; + b[n] = c; + } + + return n; +} + +int +fname(char *name) +{ + int i, c; + + /* + * read a name and return first char known not + * to be in the name. + */ + memset(name, 0, NAMELEN); + for(i=0;; i++) { + c = fchar(); + switch(c) { + case '#': + for(;;) { + c = fchar(); + if(c == -1 || c == '\n') + break; + } + + case ' ': + case '\n': + + case ':': + case ',': + case '=': + case 0: + return c; + + case -1: + return 0; + + case '\t': + return ' '; + } + if(i < NAMELEN-1) + name[i] = c; + } +} + +int +fpair(char *n1, char *n2) +{ + int c; + + do { + c = fname(n1); + if(c == 0) + return 1; + } while(*n1 == 0); + + + while(c != '=') { + c = fname(n2); + if(c == 0) + return 1; + if(*n2 != 0) + memmove(n1, n2, NAMELEN); + } + + do { + c = fname(n2); + if(c == 0) + return 1; + } while(*n2 == 0); + return 0; +} diff -Nru /sys/src/fs/port/fs.h /sys/src/fs/port/fs.h --- /sys/src/fs/port/fs.h Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/port/fs.h Tue Nov 1 00:00:00 2011 @@ -0,0 +1,36 @@ +typedef struct Nvfile Nvfile; +typedef struct Fs Fs; + +//#include "dosfs.h" +//#include "kfs.h" + +struct Nvfile{ + union{ +// Dosfile dos; +// Kfsfile kfs; + int walked; + }; + Fs *fs; + char *path; +}; + +struct Fs{ +// union { +// Dos dos; +// Kfs kfs; +// }; + int dev; /* device id */ + long (*diskread)(Fs*, void*, long); /* disk read routine */ + Off (*diskseek)(Fs*, Off); /* disk seek routine */ + long (*read)(Nvfile*, void*, long); + int (*walk)(Nvfile*, char*); + Nvfile root; +}; + +extern int chatty; +extern int dotini(Fs*); +extern int fswalk(Fs*, char*, Nvfile*); +extern int fsread(Nvfile*, void*, long); +extern int fsboot(Fs*, char*, void*); + +#define BADPTR(x) ((ulong)x < 0x80000000) diff -Nru /sys/src/fs/port/iobuf.c /sys/src/fs/port/iobuf.c --- /sys/src/fs/port/iobuf.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/port/iobuf.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,300 @@ +#include "all.h" +#include "io.h" + +#define DEBUG 0 + +extern long nhiob; +extern Hiob *hiob; + +Iobuf* +getbuf(Device *d, Off addr, int flag) +{ + Iobuf *p, *s; + Hiob *hp; + Off h; + + if(DEBUG) + print("getbuf %Z(%lld) f=%x\n", d, (Wideoff)addr, flag); + h = addr + (Off)d*1009; + if(h < 0) + h = ~h; + h %= nhiob; + hp = &hiob[h]; + +loop: + lock(hp); + +/* + * look for it in the active list + */ + s = hp->link; + for(p=s;;) { + if(p->addr == addr && p->dev == d) { + if(p != s) { + p->back->fore = p->fore; + p->fore->back = p->back; + p->fore = s; + p->back = s->back; + s->back = p; + p->back->fore = p; + hp->link = p; + } + unlock(hp); + qlock(p); + if(p->addr != addr || p->dev != d || iobufmap(p) == 0) { + qunlock(p); + goto loop; + } + p->flags |= flag; + cons.bhit[0].count++; + cons.bhit[1].count++; + cons.bhit[2].count++; + return p; + } + p = p->fore; + if(p == s) + break; + } + if(flag & Bprobe) { + unlock(hp); + return 0; + } + +/* + * not found + * take oldest unlocked entry in this queue + */ +xloop: + p = s->back; + if(!canqlock(p)) { + if(p == hp->link) { + unlock(hp); + print("iobuf all locked\n"); + goto loop; + } + s = p; + goto xloop; + } + + /* + * its dangerous to flush the pseudo + * devices since they recursively call + * getbuf/putbuf. deadlock! + */ + if(p->flags & Bres) { + qunlock(p); + if(p == hp->link) { + unlock(hp); + print("iobuf all reserved\n"); + goto loop; + } + s = p; + goto xloop; + } + if(p->flags & Bmod) { + unlock(hp); + if(iobufmap(p)) { + if(!devwrite(p->dev, p->addr, p->iobuf)) + p->flags &= ~(Bimm|Bmod); + iobufunmap(p); + } + qunlock(p); + goto loop; + } + hp->link = p; + p->addr = addr; + p->dev = d; + p->flags = flag; + p->pc = getcallerpc(&d); + unlock(hp); + if(iobufmap(p)) { + if(flag & Bread) { + if(!devread(p->dev, p->addr, p->iobuf)) { + cons.bread[0].count++; + cons.bread[1].count++; + cons.bread[2].count++; + return p; + } + iobufunmap(p); + } else { + cons.binit[0].count++; + cons.binit[1].count++; + cons.binit[2].count++; + return p; + } + } else + print("iobuf cant map buffer\n"); + p->flags = 0; + p->dev = devnone; + p->addr = -1; + qunlock(p); + return 0; +} + +/* + * syncblock tries to put out a block per hashline + * returns 0 all done, + * returns 1 if it missed something + */ +int +syncblock(void) +{ + Iobuf *p, *s, *q; + Hiob *hp; + long h; + int flag; + + flag = 0; + for(h=0; hlink; + for(p=s;;) { + if(p->flags & Bmod) { + if(q) + flag = 1; /* more than 1 mod/line */ + q = p; + } + p = p->fore; + if(p == s) + break; + } + unlock(hp); + if(q) { + if(!canqlock(q)) { + flag = 1; /* missed -- was locked */ + continue; + } + if(!(q->flags & Bmod)) { + qunlock(q); + continue; + } + if(iobufmap(q)) { + if(!devwrite(q->dev, q->addr, q->iobuf)) + q->flags &= ~(Bmod|Bimm); + iobufunmap(q); + } else + flag = 1; + qunlock(q); + } + } + return flag; +} + +void +sync(char *reason) +{ + long i; + + print("sync: %s\n", reason); + for(i=10*nhiob; i>0; i--) + if(!syncblock()) + return; + print("sync shorted\n"); +} + +void +putbuf(Iobuf *p) +{ + + if(canqlock(p)) + print("buffer not locked %Z(%lld)\n", p->dev, (Wideoff)p->addr); + if(p->flags & Bimm) { + if(!(p->flags & Bmod)) + print("imm and no mod %Z(%lld)\n", + p->dev, (Wideoff)p->addr); + if(!devwrite(p->dev, p->addr, p->iobuf)) + p->flags &= ~(Bmod|Bimm); + } + iobufunmap(p); + qunlock(p); +} + +int +checktag(Iobuf *p, int tag, Off qpath) +{ + Tag *t; + static Off lastaddr; + + t = (Tag*)(p->iobuf+BUFSIZE); + if(t->tag != tag) { + if(p->flags & Bmod) { + print(" tag = %d/%llud; expected %lld/%d -- not flushed\n", + t->tag, (Wideoff)t->path, (Wideoff)qpath, tag); + return 2; + } + if(p->dev != nil && p->dev->type == Devcw) + cwfree(p->dev, p->addr); + if(p->addr != lastaddr) + print(" tag = %G/%llud; expected %G/%lld -- flushed (%lld)\n", + t->tag, (Wideoff)t->path, tag, (Wideoff)qpath, + (Wideoff)p->addr); + lastaddr = p->addr; + p->dev = devnone; + p->addr = -1; + p->flags = 0; + return 2; + } + if(qpath != QPNONE) { + if((qpath ^ t->path) & ~QPDIR) { + if(1 || CHAT(0)) + print(" tag/path = %llud; expected %d/%llux\n", + (Wideoff)t->path, tag, (Wideoff)qpath); + return 0; + } + } + return 0; +} + +void +settag(Iobuf *p, int tag, long qpath) +{ + Tag *t; + + t = (Tag*)(p->iobuf+BUFSIZE); + t->tag = tag; + if(qpath != QPNONE) + t->path = qpath & ~QPDIR; + p->flags |= Bmod; +} + +int +qlmatch(QLock *q1, QLock *q2) +{ + + return q1 == q2; +} + +int +iobufql(QLock *q) +{ + Iobuf *p, *s; + Hiob *hp; + Tag *t; + long h; + int tag; + + for(h=0; hlink; + for(p=s;;) { + if(qlmatch(q, p)) { + t = (Tag*)(p->iobuf+BUFSIZE); + tag = t->tag; + if(tag < 0 || tag >= MAXTAG) + tag = Tnone; + print(" Iobuf %Z(%lld) t=%s\n", + p->dev, (Wideoff)p->addr, tagnames[tag]); + unlock(hp); + return 1; + } + p = p->fore; + if(p == s) + break; + } + unlock(hp); + } + return 0; +} diff -Nru /sys/src/fs/port/lib.h /sys/src/fs/port/lib.h --- /sys/src/fs/port/lib.h Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/port/lib.h Tue Nov 1 00:00:00 2011 @@ -0,0 +1,136 @@ +/* + * functions (possibly) linked in, complete, from libc. + */ +#define nelem(x) (sizeof(x)/sizeof((x)[0])) +#define offsetof(s, m) (ulong)(&(((s*)0)->m)) + +/* + * mem routines + */ +extern void* memset(void*, int, ulong); +extern int memcmp(void*, void*, ulong); +extern void* memmove(void*, void*, ulong); +extern void* memchr(void*, int, ulong); + +/* + * string routines + */ +extern char* strcat(char*, char*); +extern char* strchr(char*, int); +extern char* strrchr(char*, int); +extern int strcmp(char*, char*); +extern char* strcpy(char*, char*); +extern char* strncat(char*, char*, long); +extern char* strncpy(char*, char*, long); +extern int strncmp(char*, char*, long); +extern long strlen(char*); + +enum +{ + UTFmax = 3, /* maximum bytes per rune */ + Runesync = 0x80, /* cannot represent part of a UTF sequence */ + Runeself = 0x80, /* rune and UTF sequences are the same (<) */ + Runeerror = 0x80, /* decoding error in UTF */ +}; + +/* + * rune routines + */ +extern int runetochar(char*, Rune*); +extern int chartorune(Rune*, char*); +extern char* utfrune(char*, long); +extern char* utfrrune(char*, long); +extern int utflen(char*); +extern int runelen(long); + +/* + * math + */ +extern int abs(int); + +/* + * print routines + */ +typedef struct Fmt Fmt; +typedef int (*Fmts)(Fmt*); +struct Fmt{ + uchar runes; /* output buffer is runes or chars? */ + void *start; /* of buffer */ + void *to; /* current place in the buffer */ + void *stop; /* end of the buffer; overwritten if flush fails */ + int (*flush)(Fmt *); /* called when to == stop */ + void *farg; /* to make flush a closure */ + int nfmt; /* num chars formatted so far */ + va_list args; /* args passed to dofmt */ + int r; /* % format Rune */ + int width; + int prec; + ulong flags; +}; + +extern int print(char*, ...); +extern char* seprint(char*, char*, char*, ...); +extern char* vseprint(char*, char*, char*, va_list); +extern int snprint(char*, int, char*, ...); +extern int sprint(char*, char*, ...); + +extern int fmtinstall(int c, int (*f)(Fmt*)); +extern void quotefmtinstall(void); +extern int fmtit(Fmt *f, char *fmt, ...); +extern int fmtstrcpy(Fmt *f, char *s); + +#pragma varargck argpos fmtit 2 +#pragma varargck argpos print 1 +#pragma varargck argpos seprint 3 +#pragma varargck argpos snprint 3 +#pragma varargck argpos sprint 2 + +#pragma varargck type "lld" vlong +#pragma varargck type "llx" vlong +#pragma varargck type "lld" uvlong +#pragma varargck type "llx" uvlong +#pragma varargck type "ld" long +#pragma varargck type "lx" long +#pragma varargck type "ld" ulong +#pragma varargck type "lx" ulong +#pragma varargck type "d" int +#pragma varargck type "x" int +#pragma varargck type "c" int +#pragma varargck type "C" int +#pragma varargck type "d" uint +#pragma varargck type "x" uint +#pragma varargck type "c" uint +#pragma varargck type "C" uint +/* no floating-point verbs */ +#pragma varargck type "s" char* +#pragma varargck type "q" char* +#pragma varargck type "S" Rune* +#pragma varargck type "Q" Rune* +#pragma varargck type "r" void +#pragma varargck type "%" void +#pragma varargck type "n" int* +#pragma varargck type "p" void* +#pragma varargck flag ',' +#pragma varargck type "<" void* +#pragma varargck type "[" void* +#pragma varargck type "H" void* +#pragma varargck type "lH" void* + +/* + * one-of-a-kind + */ +extern void qsort(void*, long, long, int (*)(void*, void*)); +extern char end[]; + +extern int decrypt(void*, void*, int); +extern int encrypt(void*, void*, int); +extern int nrand(int); +extern void srand(long); + +#define OREAD 0 /* open for read */ +#define OWRITE 1 /* write */ +#define ORDWR 2 /* read and write */ +#define OEXEC 3 /* execute, == read but check execute permission */ +#define OTRUNC 16 /* or'ed in (except for exec), truncate file first */ +#define OCEXEC 32 /* or'ed in, close on exec */ +#define ORCLOSE 64 /* or'ed in, remove on close */ diff -Nru /sys/src/fs/port/lrand.c /sys/src/fs/port/lrand.c --- /sys/src/fs/port/lrand.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/port/lrand.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,82 @@ +#include "all.h" + +/* + * algorithm by + * D. P. Mitchell & J. A. Reeds + */ + +#define LEN 607 +#define TAP 273 +#define MASK 0x7fffffffL +#define A 48271 +#define M 2147483647 +#define Q 44488 +#define R 3399 +#define NORM (1.0/(1.0+MASK)) + +static ulong rng_vec[LEN]; +static ulong* rng_tap = rng_vec; +static ulong* rng_feed = 0; +static Lock lk; + +static void +isrand(long seed) +{ + long lo, hi, x; + int i; + + rng_tap = rng_vec; + rng_feed = rng_vec+LEN-TAP; + seed = seed%M; + if(seed < 0) + seed += M; + if(seed == 0) + seed = 89482311; + x = seed; + /* + * Initialize by x[n+1] = 48271 * x[n] mod (2**31 - 1) + */ + for(i = -20; i < LEN; i++) { + hi = x / Q; + lo = x % Q; + x = A*lo - R*hi; + if(x < 0) + x += M; + if(i >= 0) + rng_vec[i] = x; + } +} + +void +srand(long seed) +{ + lock(&lk); + isrand(seed); + unlock(&lk); +} + +long +lrand(void) +{ + ulong x; + + lock(&lk); + + rng_tap--; + if(rng_tap < rng_vec) { + if(rng_feed == 0) { + isrand(1); + rng_tap--; + } + rng_tap += LEN; + } + rng_feed--; + if(rng_feed < rng_vec) + rng_feed += LEN; + x = (*rng_feed + *rng_tap) & MASK; + *rng_feed = x; + + unlock(&lk); + + return x; +} diff -Nru /sys/src/fs/port/main.c /sys/src/fs/port/main.c --- /sys/src/fs/port/main.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/port/main.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,468 @@ +#include "all.h" +#include "mem.h" +#include "io.h" +#include "ureg.h" + +#include "9p1.h" + +Rendez dawnrend; + +void +machinit(void) +{ + int n; + + n = m->machno; + memset(m, 0, sizeof(Mach)); + m->machno = n; + m->mmask = 1<machno; + m->lights = 0; + + active.exiting = 0; + active.machs = 1; +} + +static +void +confinit(void) +{ + conf.nmach = 1; + conf.nproc = 40; + + conf.mem = meminit(); + conf.sparemem = conf.mem/12; /* 8% spare for chk etc */ + + conf.nalarm = 200; + conf.nuid = 1000; + conf.nserve = 15; + conf.nfile = 30000; + conf.nlgmsg = 100; + conf.nsmmsg = 500; + /* + * if you have trouble with IDE DMA or RWM (multi-sector transfers), + * perhaps due to old hardware, set idedma to zero in localconfinit(). + */ + conf.idedma = 1; + + localconfinit(); + + conf.nwpath = conf.nfile*8; + conf.nauth = conf.nfile/10; + conf.gidspace = conf.nuid*3; + + cons.flags = 0; +} + +/* + * compute BUFSIZE*(NDBLOCK+INDPERBUF+INDPERBUF⁲+INDPERBUF⁳+INDPERBUF⁴) + * while watching for overflow; in that case, return 0. + */ + +static uvlong +adduvlongov(uvlong a, uvlong b) +{ + uvlong r = a + b; + + return (r < a || r < b)? 0: r; +} + +static uvlong +muluvlongov(uvlong a, uvlong b) +{ + uvlong r = a * b; + + return (r < a || r < b)? 0: r; +} + +static uvlong +maxsize(void) +{ + int i; + uvlong max = NDBLOCK, ind = 1; + + for (i = 0; i < NIBLOCK; i++) { + ind = muluvlongov(ind, INDPERBUF); /* power of INDPERBUF */ + if (ind == 0) + return 0; + max = adduvlongov(max, ind); + if (max == 0) + return 0; + } + return muluvlongov(max, BUFSIZE); +} + +enum { + INDPERBUF⁲ = ((Off)INDPERBUF *INDPERBUF), + INDPERBUF⁴ = ((Off)INDPERBUF⁲*INDPERBUF⁲), +}; + +static void +printsizes(void) +{ + uvlong max = maxsize(); + + print("\tblock size = %d; ", RBUFSIZE); + if (max == 0) + print("max file size exceeds 2⁶⁴ bytes\n"); + else { + uvlong offlim = 1ULL << (sizeof(Off)*8 - 1); + + if (max >= offlim) + max = offlim - 1; + print("max file size = %,llud\n", (Wideoff)max); + } + print("\tINDPERBUF = %d, INDPERBUF^4 = %,lld, ", INDPERBUF, + (Wideoff)INDPERBUF⁴); + print("CEPERBK = %d\n", CEPERBK); + print("\tsizeofs: Dentry = %d, Cache = %d\n", + sizeof(Dentry), sizeof(Cache)); +} + +void +main(void) +{ + int i; + + echo = 1; + predawn = 1; + formatinit(); + machinit(); + vecinit(); + confinit(); + lockinit(); + printinit(); + procinit(); + clockinit(); + + print("\nPlan 9 %d-bit file server with %d-deep indir blks%s\n", + sizeof(Off)*8 - 1, NIBLOCK, + (conf.idedma? " and IDE DMA+RWM": "")); + printsizes(); + + alarminit(); + + mainlock.wr.name = "mainr"; + mainlock.rd.name = "mainw"; + reflock.name = "ref"; + + qlock(&reflock); + qunlock(&reflock); + serveq = newqueue(1000); + raheadq = newqueue(1000); + + mbinit(); + + sntpinit(); + otherinit(); + + files = ialloc(conf.nfile * sizeof(*files), 0); + for(i=0; idev > rb->dev) + return 1; + if(ra->dev < rb->dev) + return -1; + if(ra->addr > rb->addr) + return 1; + if(ra->addr < rb->addr) + return -1; + return 0; +} + +void +rahead(void) +{ + Rabuf *rb[50]; + Iobuf *p; + int i, n; + + for (;;) { + rb[0] = recv(raheadq, 0); + for(n=1; ncount <= 0) + break; + rb[n] = recv(raheadq, 0); + } + qsort(rb, n, sizeof(rb[0]), rbcmp); + for(i=0; idev, rb[i]->addr, Bread); + if(p) + putbuf(p); + lock(&rabuflock); + rb[i]->link = rabuffree; + rabuffree = rb[i]; + unlock(&rabuflock); + } + } +} + +/* + * main filesystem server loop. + * entered by many processes. + * they wait for message buffers and + * then process them. + */ +void +serve(void) +{ + int i; + Chan *cp; + Msgbuf *mb; + + for (;;) { + qlock(&reflock); + mb = recv(serveq, 0); + cp = mb->chan; + rlock(&cp->reflock); + qunlock(&reflock); + + rlock(&mainlock); + + if(cp->protocol == nil){ + /* do we recognise the protocol in this packet? */ + for(i = 0; fsprotocol[i] != nil; i++) + if(fsprotocol[i](mb) != 0) { + cp->protocol = fsprotocol[i]; + break; + } + if(cp->protocol == nil){ + print("no protocol for message\n"); + for(i = 0; i < 12; i++) + print(" %2.2uX", mb->data[i]); + print("\n"); + } + } + else + cp->protocol(mb); + + mbfree(mb); + runlock(&mainlock); + runlock(&cp->reflock); + } +} + +void +init0(void) +{ + m->proc = u; + u->state = Running; + u->mach = m; + spllo(); + + (*u->start)(); +} + +void* +getarg(void) +{ + return u->arg; +} + +enum { + Keydelay = 5*60, /* seconds to wait for key press */ +}; + +void +exit(void) +{ + long timeleft; + + u = 0; + lock(&active); + active.machs &= ~(1<machno); + active.exiting = 1; + unlock(&active); + if(!predawn) + spllo(); + print("cpu %d exiting\n", m->machno); + while(active.machs) + delay(1); + + print("halted at %T.\n", time()); + print("press the Enter key to reboot sooner than %d mins.\n", + Keydelay/60); + delay(500); /* time to drain print q */ + + splhi(); + /* reboot after delay (for debugging) or at newline */ + for (timeleft = Keydelay; timeleft > 0; timeleft--) + if (rawchar(1) == '\n') + break; + + spllo(); + delay(500); /* time to drain echo q */ + print("rebooting...\n"); + delay(500); /* time to drain print q */ + + splhi(); + consreset(); + firmware(); +} + +/* + * 1 sec timer + * process+alarm+rendez+rwlock + */ +static Rendez sec; + +static +void +callsec(Alarm *a, void *arg) +{ + User *u = arg; + + cancel(a); + wakeup(&u->tsleep); +} + +void +waitsec(int msec) +{ + alarm(msec, callsec, u); + sleep(&u->tsleep, no, 0); +} + +#define DUMPTIME 5 /* 5 am */ +#define WEEKMASK 0 /* every day (1=sun, 2=mon, 4=tue, etc.) */ + +/* + * calculate the next dump time. + * minimum delay is 100 minutes. + */ +Timet +nextdump(Timet t) +{ + Timet nddate = nextime(t+MINUTE(100), DUMPTIME, WEEKMASK); + + if(!conf.nodump) + print("next dump at %T\n", nddate); + return nddate; +} + +/* + * process to copy dump blocks from + * cache to worm. it runs flat out when + * it gets work, but only looks for + * work every 10 seconds. + */ +void +wormcopy(void) +{ + int f, dorecalc = 1; + Timet dt, t = 0, nddate = 0, ntoytime = 0; + Filsys *fs; + + for (;;) { + if (dorecalc) { + dorecalc = 0; + t = time(); + nddate = nextdump(t); /* chatters */ + ntoytime = time(); + } + dt = time() - t; + if(dt < 0 || dt > MINUTE(100)) { + if(dt < 0) + print("time went back\n"); + else + print("time jumped ahead\n"); + dorecalc = 1; + continue; + } + t += dt; + f = 0; + if(t > ntoytime) { + dt = time() - rtctime(); + if(dt < 0) + dt = -dt; + if(dt > 10) + print("rtc time more than 10 seconds out\n"); + else if(dt > 1) + settime(rtctime()); + ntoytime = time() + HOUR(1); + } else if(/* !f && */ t > nddate) { /* !f is always true here */ + if(!conf.nodump) { + print("automatic dump %T\n", t); + for(fs=filsys; fs->name; fs++) + if(fs->dev->type == Devcw) + cfsdump(fs); + } + dorecalc = 1; + } else { + rlock(&mainlock); + for(fs=filsys; fs->name; fs++) + if(fs->dev->type == Devcw) + f |= dumpblock(fs->dev); + runlock(&mainlock); + + if(0 && f == 0) + f = 0; /* f = dowcp(); */ + if(!f) + waitsec(10000); + wormprobe(); + } + } +} + +/* + * process to synch blocks + * it puts out a block/cache-line every second + * it waits 10 seconds if caught up. + * in both cases, it takes about 10 seconds + * to get up-to-date. + */ +void +synccopy(void) +{ + int f; + + for (;;) { + rlock(&mainlock); + f = syncblock(); + runlock(&mainlock); + if(!f) + waitsec(10000); + else + waitsec(1000); + /* pokewcp(); */ + } +} diff -Nru /sys/src/fs/port/mkfile /sys/src/fs/port/mkfile --- /sys/src/fs/port/mkfile Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/port/mkfile Tue Nov 1 00:00:00 2011 @@ -0,0 +1,6 @@ +PORTFILES=`{builtin cd ../port;echo *.c | sed 's/ /|/g; s/\.c//g'} +^($PORTFILES)\.$O:R: '../port/\1.c' + $CC $CFLAGS -I. ../port/$stem1.c + +9p1.$O 9p1lib.$O console.$O main.$O: ../port/9p1.h +devsd.$O: ../pc/compat.h diff -Nru /sys/src/fs/port/portdat.h /sys/src/fs/port/portdat.h --- /sys/src/fs/port/portdat.h Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/port/portdat.h Tue Nov 1 00:00:00 2011 @@ -0,0 +1,990 @@ +/* + * fundamental constants and types of the implementation + * changing any of these changes the layout on disk + */ + +#define SUPER_ADDR 2 /* block address of superblock */ +#define ROOT_ADDR 3 /* block address of root directory */ + +#ifdef OLD +/* + * compatible on disk with the old 32-bit file server. + * this lets people run this kernel on their old file systems. + */ +#define NAMELEN 28 /* max size of file name components */ +#define NDBLOCK 6 /* number of direct blocks in Dentry */ +#define NIBLOCK 2 /* max depth of indirect blocks */ + +typedef long Off; /* file offsets & sizes, in bytes & blocks */ + +#else /* OLD */ + +/* the glorious new, incompatible (on disk) 64-bit world */ + +/* keeping NAMELEN ≤ 50 bytes permits 3 Dentrys per mag disk sector */ +#define NAMELEN 56 /* max size of file name components */ +#define NDBLOCK 6 /* number of direct blocks in Dentry */ +#define NIBLOCK 4 /* max depth of indirect blocks */ + +/* + * file offsets & sizes, in bytes & blocks. typically long or vlong. + * vlong is used in the code where would be needed if Off were just long. + */ +typedef vlong Off; + +#endif /* OLD */ + +/* constants that don't affect disk layout */ +#define MAXDAT 8192 /* max allowable data message */ +#define MAXMSG 128 /* max size protocol message sans data */ +#define OFFMSG 60 /* offset of msg in buffer */ + +#define C0a 59 /* time constants for filters */ +#define C0b 60 +#define C1a 599 +#define C1b 600 +#define C2a 5999 +#define C2b 6000 + +/* more fundamental types */ +typedef vlong Wideoff; /* type to widen Off to for printing; ≥ as wide as Off */ +typedef short Userid; /* signed internal representation of user-id */ +typedef long Timet; /* in seconds since epoch */ +typedef vlong Devsize; /* in bytes */ + +/* + * tunable parameters + */ +enum { + Maxword = 200, /* max bytes per command-line word */ +}; +#define NDRIVE 16 /* size of drive structure */ +#define NTLOCK 200 /* number of active file Tlocks */ +#define LRES 3 /* profiling resolution */ +#define NATTID 10 /* the last 10 ID's in attaches */ + +/* + * derived constants + */ +#define BUFSIZE (RBUFSIZE-sizeof(Tag)) +#define DIRPERBUF (BUFSIZE/sizeof(Dentry)) +#define INDPERBUF (BUFSIZE/sizeof(Off)) +#define FEPERBUF ((BUFSIZE-sizeof(Super1)-sizeof(Off))/sizeof(Off)) +#define SMALLBUF (MAXMSG) +#define LARGEBUF (MAXMSG+MAXDAT+256) +#define RAGAP (300*1024)/BUFSIZE /* readahead parameter */ +#define CEPERBK ((BUFSIZE-BKPERBLK*sizeof(Off))/\ + (sizeof(Centry)*BKPERBLK)) +#define BKPERBLK 10 + +typedef struct Alarm Alarm; +typedef struct Auth Auth; +typedef struct Conf Conf; +typedef struct Label Label; +typedef struct Lock Lock; +typedef struct Mach Mach; +typedef struct QLock QLock; +typedef struct Ureg Ureg; +typedef struct User User; +typedef struct Fbuf Fbuf; +typedef struct Super1 Super1; +typedef struct Superb Superb; +typedef struct Filsys Filsys; +typedef struct Startsb Startsb; +typedef struct Dentry Dentry; +typedef struct Tag Tag; +typedef struct Talarm Talarm; +typedef struct Uid Uid; +typedef struct Device Device; +typedef struct Qid9p1 Qid9p1; +typedef struct Iobuf Iobuf; +typedef struct Wpath Wpath; +typedef struct File File; +typedef struct Chan Chan; +typedef struct Cons Cons; +typedef struct Time Time; +typedef struct Tm Tm; +typedef struct Rtc Rtc; +typedef struct Hiob Hiob; +typedef struct RWlock RWlock; +typedef struct Msgbuf Msgbuf; +typedef struct Queue Queue; +typedef struct Command Command; +typedef struct Flag Flag; +typedef struct Bp Bp; +typedef struct Rabuf Rabuf; +typedef struct Rendez Rendez; +typedef struct Filter Filter; +typedef ulong Float; +typedef struct Tlock Tlock; +typedef struct Cache Cache; +typedef struct Centry Centry; +typedef struct Bucket Bucket; + +#pragma incomplete Auth +#pragma incomplete Ureg + +struct Lock +{ + ulong* sbsem; /* addr of sync bus semaphore */ + ulong pc; + ulong sr; +}; + +struct Rendez +{ + Lock; + User* p; +}; + +struct Filter +{ + ulong count; /* count and old count kept separate */ + ulong oldcount; /* so interrput can read them */ + int c1; /* time const multiplier */ + int c2; /* time const divider */ + int c3; /* scale for printing */ + Float filter; /* filter */ +}; + +struct QLock +{ + Lock; /* to use object */ + User* head; /* next process waiting for object */ + User* tail; /* last process waiting for object */ + char* name; /* for diagnostics */ + int locked; /* flag, is locked */ +}; + +struct RWlock +{ + int nread; + QLock wr; + QLock rd; +}; + +/* + * send/recv queue structure + */ +struct Queue +{ + Lock; /* to manipulate values */ + int size; /* size of queue */ + int loc; /* circular pointer */ + int count; /* how many in queue */ + User* rhead; /* process's waiting for send */ + User* rtail; + User* whead; /* process's waiting for recv */ + User* wtail; + void* args[1]; /* list of saved pointers, [->size] */ +}; + +struct Tag +{ + short pad; /* make tag end at a long boundary */ + short tag; + Off path; +}; + +struct Device +{ + uchar type; + uchar init; + Device* link; /* link for mcat/mlev/mirror */ + Device* dlink; /* link all devices */ + void* private; + Devsize size; + union + { + struct /* wren, ide, (l)worm in targ */ + { + int ctrl; /* disks only */ + int targ; + int lun; /* wren only */ + } wren; + struct /* mcat mlev mirror */ + { + Device* first; + Device* last; + int ndev; + } cat; + struct /* cw */ + { + Device* c; /* cache device */ + Device* w; /* worm device */ + Device* ro; /* dump - readonly */ + } cw; + struct /* juke */ + { + Device* j; /* (robotics, worm drives) - wrens */ + Device* m; /* (sides) - r or l devices */ + } j; + struct /* ro */ + { + Device* parent; + } ro; + struct /* fworm */ + { + Device* fw; + } fw; + struct /* part */ + { + Device* d; + long base; /* percentages */ + long size; + } part; + struct /* byte-swapped */ + { + Device* d; + } swab; + }; +}; + +typedef struct Sidestarts { + Devsize sstart; /* blocks before start of side */ + Devsize s1start; /* blocks before start of next side */ +} Sidestarts; + +struct Rabuf +{ + union + { + struct + { + Device* dev; + Off addr; + }; + Rabuf* link; + }; +}; + +/* user-visible Qid, from */ +typedef +struct Qid +{ + uvlong path; /* Off */ + ulong vers; /* should be Off */ + uchar type; +} Qid; + +/* bits in Qid.type */ +#define QTDIR 0x80 /* type bit for directories */ +#define QTAPPEND 0x40 /* type bit for append only files */ +#define QTEXCL 0x20 /* type bit for exclusive use files */ +#define QTMOUNT 0x10 /* type bit for mounted channel */ +#define QTAUTH 0x08 /* type bit for authentication file */ +#define QTFILE 0x00 /* plain file */ + +/* DONT TOUCH, this is the disk structure */ +struct Qid9p1 +{ + Off path; /* was long */ + ulong version; /* should be Off */ +}; + +struct Hiob +{ + Iobuf* link; + Lock; +}; + +struct Chan +{ + char type; /* major driver type i.e. Dev* */ + int (*protocol)(Msgbuf*); /* version */ + int msize; /* version */ + char whochan[50]; + char whoname[NAMELEN]; + void (*whoprint)(Chan*); + ulong flags; + int chan; /* overall channel number, mostly for printing */ + int nmsgs; /* outstanding messages, set under flock -- for flush */ + Timet whotime; + Filter work; + Filter rate; + int nfile; /* used by cmd_files */ + RWlock reflock; + Chan* next; /* link list of chans */ + Queue* send; + Queue* reply; + + uchar authinfo[64]; + + void* ifc; + void* pdata; +}; + +struct Filsys +{ + char* name; /* name of filsys */ + char* conf; /* symbolic configuration */ + Device* dev; /* device that filsys is on */ + int flags; + #define FREAM (1<<0) /* mkfs */ + #define FRECOVER (1<<1) /* install last dump */ + #define FEDIT (1<<2) /* modified */ +}; + +struct Startsb +{ + char* name; + Off startsb; +}; + +struct Time +{ + Timet lasttoy; + Timet bias; + Timet offset; +}; + +/* + * array of qids that are locked + */ +struct Tlock +{ + Device* dev; + Timet time; + Off qpath; + File* file; +}; + +struct Cons +{ + ulong flags; /* overall flags for all channels */ + QLock; /* generic qlock for mutex */ + int uid; /* botch -- used to get uid on cons_create */ + int gid; /* botch -- used to get gid on cons_create */ + int nuid; /* number of uids */ + int ngid; /* number of gids */ + Off offset; /* used to read files, c.f. fchar */ + int chano; /* generator for channel numbers */ + Chan* chan; /* console channel */ + Filsys* curfs; /* current filesystem */ + + int profile; /* are we profiling? */ + long* profbuf; + ulong minpc; + ulong maxpc; + ulong nprofbuf; + + long nlarge; /* number of large message buffers */ + long nsmall; /* ... small ... */ + long nwormre; /* worm read errors */ + long nwormwe; /* worm write errors */ + long nwormhit; /* worm read cache hits */ + long nwormmiss; /* worm read cache non-hits */ + int noage; /* dont update cache age, dump and check */ + long nwrenre; /* disk read errors */ + long nwrenwe; /* disk write errors */ + long nreseq; /* cache bucket resequence */ + + Filter work[3]; /* thruput in messages */ + Filter rate[3]; /* thruput in bytes */ + Filter bhit[3]; /* getbufs that hit */ + Filter bread[3]; /* getbufs that miss and read */ + Filter brahead[3]; /* messages to readahead */ + Filter binit[3]; /* getbufs that miss and dont read */ +}; + +struct File +{ + QLock; + Qid qid; + Wpath* wpath; + Chan* cp; /* null means a free slot */ + Tlock* tlock; /* if file is locked */ + File* next; /* in cp->flist */ + Filsys* fs; + Off addr; + long slot; /* ordinal # of Dentry with a directory block */ + Off lastra; /* read ahead address */ + ulong fid; + Userid uid; + Auth *auth; + char open; + #define FREAD 1 + #define FWRITE 2 + #define FREMOV 4 + + Off doffset; /* directory reading */ + ulong dvers; + long dslot; +}; + +struct Wpath +{ + Wpath* up; /* pointer upwards in path */ + Off addr; /* directory entry addr */ + long slot; /* directory entry slot */ + short refs; /* number of files using this structure */ +}; + +struct Iobuf +{ + QLock; + Device* dev; + Iobuf* fore; /* for lru */ + Iobuf* back; /* for lru */ + char* iobuf; /* only active while locked */ + char* xiobuf; /* "real" buffer pointer */ + Off addr; + int flags; +}; + +struct Uid +{ + Userid uid; /* user id */ + Userid lead; /* leader of group */ + Userid *gtab; /* group table */ + int ngrp; /* number of group entries */ + char name[NAMELEN]; /* user name */ +}; + +/* DONT TOUCH, this is the disk structure */ +struct Dentry +{ + char name[NAMELEN]; + Userid uid; + Userid gid; + ushort mode; + #define DALLOC 0x8000 + #define DDIR 0x4000 + #define DAPND 0x2000 + #define DLOCK 0x1000 + #define DREAD 0x4 + #define DWRITE 0x2 + #define DEXEC 0x1 + Userid muid; + Qid9p1 qid; + Off size; + Off dblock[NDBLOCK]; + Off iblocks[NIBLOCK]; + long atime; + long mtime; +}; + +/* DONT TOUCH, this is the disk structure */ +struct Super1 +{ + Off fstart; + Off fsize; + Off tfree; + Off qidgen; /* generator for unique ids */ + /* + * Stuff for WWC device + */ + Off cwraddr; /* cfs root addr */ + Off roraddr; /* dump root addr */ + Off last; /* last super block addr */ + Off next; /* next super block addr */ +#ifdef AUTOSWAB + vlong magic; /* for byte-order detection */ + /* in memory only, not on disk (maybe) */ + int flags; +#endif +}; + +/* DONT TOUCH, this is the disk structure */ +struct Fbuf +{ + Off nfree; + Off free[FEPERBUF]; +}; + +/* DONT TOUCH, this is the disk structure */ +struct Superb +{ + Fbuf fbuf; + Super1; +}; + +struct Label +{ + ulong pc; + ulong sp; +}; + +struct Alarm +{ + Lock; + Alarm* next; + int busy; + int dt; /* in ticks */ + void (*f)(Alarm*, void*); + void* arg; +}; + +struct Talarm +{ + Lock; + User *list; +}; + +struct Conf +{ + ulong nmach; /* processors */ + ulong nproc; /* processes */ + ulong mem; /* total physical bytes of memory */ + ulong sparemem; /* memory left for check/dump and chans */ + ulong nalarm; /* alarms */ + ulong nuid; /* distinct uids */ + ulong nserve; /* server processes */ + ulong nfile; /* number of fid -- system wide */ + ulong nwpath; /* number of active paths, derived from nfile */ + ulong gidspace; /* space for gid names -- derived from nuid */ + ulong nlgmsg; /* number of large message buffers */ + ulong nsmmsg; /* number of small message buffers */ + Off recovcw; /* recover addresses */ + Off recovro; + Off firstsb; + Off recovsb; + ulong nauth; /* number of Auth structs */ + uchar nodump; /* no periodic dumps */ + uchar ripoff; + uchar dumpreread; /* read and compare in dump copy */ + + short minuteswest; /* minutes west of Greenwich */ + short dsttime; /* dst correction */ + + ulong npage0; /* total physical pages of memory */ + ulong npage1; /* total physical pages of memory */ + ulong base0; /* base of bank 0 */ + ulong base1; /* base of bank 1 */ + + ulong idedma; /* flag: use DMA & RWM on IDE disks? */ +}; + +/* + * message buffers + * 2 types, large and small + */ +/* flags from cpu kernel; not implemented in fs kernel yet */ +enum { + Bipck = (1<<6), /* ip checksum */ + Budpck = (1<<3), /* udp checksum */ + Btcpck = (1<<4), /* tcp checksum */ + Bpktck = (1<<5), /* packet checksum */ +}; +struct Msgbuf +{ + short count; + short flags; + #define LARGE (1<<0) + #define FREE (1<<1) + #define BFREE (1<<2) + #define BTRACE (1<<7) + #define Mbrcvbuf (1<<15) /* to free, call (*free)(this) */ + Chan* chan; + Msgbuf* next; + ulong param; + int category; + uchar* data; /* rp or wp: current processing point */ + uchar* xdata; /* base of allocation */ + /* added for cpu kernel compatibility - geoff */ + void (*free)(Msgbuf *); +}; + +/* + * message buffer categories + */ +enum +{ + Mxxx = 0, + Mbreply1, + Mbreply2, + Mbreply3, + Mbreply4, + Mbarp1, + Mbarp2, + Mbip1, + Mbip2, + Mbip3, + Mbil1, + Mbil2, + Mbil3, + Mbil4, + Mbilauth, + Maeth1, + Maeth2, + Maeth3, + Mbeth1, + Mbeth2, + Mbeth3, + Mbeth4, + Mbsntp, + MAXCAT, +}; + +struct Mach +{ + int machno; /* physical id of processor */ + int mmask; /* 1<machno */ + Timet ticks; /* of the clock since boot time */ + int lights; /* light lights, this processor */ + + User* proc; /* current process on this processor */ + Label sched; /* scheduler wakeup */ + Lock alarmlock; /* access to alarm list */ + void* alarm; /* alarms bound to this clock */ + + void (*intr)(Ureg*, ulong); /* pending interrupt */ + User* intrp; /* process that was interrupted */ + ulong cause; /* arg to intr */ + Ureg* ureg; /* arg to intr */ +#ifdef CPU + int loopconst; + + Lock apictimerlock; + int cpumhz; + uvlong cyclefreq; /* Frequency of user readable cycle counter */ + uvlong cpuhz; + int cpuidax; + int cpuiddx; + char cpuidid[16]; + char* cpuidtype; + int havetsc; + int havepge; + uvlong tscticks; +#endif + uchar stack[1]; +}; + +#define MAXSTACK 16000 +#define NHAS 300 +struct User +{ + Label sched; + Mach* mach; /* machine running this proc */ + User* rnext; /* next process in run queue */ + User* qnext; /* next process on queue for a QLock */ + void (*start)(void); /* startup function */ + char* text; /* name of this process */ + void* arg; + Filter time[3]; /* cpu time used */ + int exiting; + int pid; + int state; + Rendez tsleep; + + Timet twhen; + Rendez *trend; + User *tlink; + int (*tfn)(void*); + + struct + { + ulong pc[NHAS]; /* list of pcs for locks this process has */ + QLock* q[NHAS];/* list of locks this process has */ + QLock* want; /* lock waiting */ + } has; + uchar stack[MAXSTACK]; +}; + +#define PRINTSIZE 256 +struct +{ + Lock; + int machs; + int exiting; +} active; + +struct Command +{ + char* arg0; + char* help; + void (*func)(int, char*[]); +}; + +struct Flag +{ + char* arg0; + char* help; + ulong flag; +}; + +struct Tm +{ + /* see ctime(3) */ + int sec; + int min; + int hour; + int mday; + int mon; + int year; + int wday; + int yday; + int isdst; +}; + +struct Rtc +{ + int sec; + int min; + int hour; + int mday; + int mon; + int year; +}; + +typedef struct +{ + /* constants during a given truncation */ + Dentry *d; + Iobuf *p; /* the block containing *d */ + int uid; + Off newsize; + Off lastblk; /* last data block of file to keep */ + + /* variables */ + Off relblk; /* # of current data blk within file */ + int pastlast; /* have we walked past lastblk? */ + int err; +} Truncstate; + +/* + * cw device + */ + +/* DONT TOUCH, this is the disk structure */ +struct Cache +{ + Off maddr; /* cache map addr */ + Off msize; /* cache map size in buckets */ + Off caddr; /* cache addr */ + Off csize; /* cache size */ + Off fsize; /* current size of worm */ + Off wsize; /* max size of the worm */ + Off wmax; /* highwater write */ + + Off sbaddr; /* super block addr */ + Off cwraddr; /* cw root addr */ + Off roraddr; /* dump root addr */ + + Timet toytime; /* somewhere convienent */ + Timet time; +}; + +/* DONT TOUCH, this is the disk structure */ +struct Centry +{ + ushort age; + short state; + Off waddr; /* worm addr */ +}; + +/* DONT TOUCH, this is the disk structure */ +struct Bucket +{ + long agegen; /* generator for ages in this bkt */ + Centry entry[CEPERBK]; +}; + +/* + * scsi i/o + */ +enum +{ + SCSIread = 0, + SCSIwrite = 1, +}; + +/* + * Process states + */ +enum +{ + Dead = 0, + Moribund, + Zombie, + Ready, + Scheding, + Running, + Queueing, + Sending, + Recving, + MMUing, + Exiting, + Inwait, + Wakeme, + Broken, +}; + +/* + * Lights + */ +enum +{ + Lreal = 0, /* blink in clock interrupt */ + Lintr, /* on while in interrupt */ + Lpanic, /* in panic */ + Lcwmap, /* in cw lookup */ +}; + +/* + * devnone block numbers + */ +enum +{ + Cwio1 = 1, + Cwio2, + Cwxx1, + Cwxx2, + Cwxx3, + Cwxx4, + Cwdump1, + Cwdump2, + Cuidbuf, + Cckbuf, +}; + +/* + * error codes generated from the file server + */ +enum +{ + Ebadspc = 1, + Efid, + Echar, + Eopen, + Ecount, + Ealloc, + Eqid, + Eaccess, + Eentry, + Emode, + Edir1, + Edir2, + Ephase, + Eexist, + Edot, + Eempty, + Ebadu, + Enoattach, + Ewstatb, + Ewstatd, + Ewstatg, + Ewstatl, + Ewstatm, + Ewstato, + Ewstatp, + Ewstatq, + Ewstatu, + Ewstatv, + Ename, + Ewalk, + Eronly, + Efull, + Eoffset, + Elocked, + Ebroken, + Eauth, + Eauth2, + Efidinuse, + Etoolong, + Econvert, + Eversion, + Eauthdisabled, + Eauthnone, + Eauthfile, + Eedge, + MAXERR +}; + +/* + * device types + */ +enum +{ + Devnone = 0, + Devcon, /* console */ + Devwren, /* scsi disk drive */ + Devworm, /* scsi video drive */ + Devlworm, /* scsi video drive (labeled) */ + Devfworm, /* fake read-only device */ + Devjuke, /* jukebox */ + Devcw, /* cache with worm */ + Devro, /* readonly worm */ + Devmcat, /* multiple cat devices */ + Devmlev, /* multiple interleave devices */ + Devil, /* internet link */ + Devpart, /* partition */ + Devfloppy, /* floppy drive */ + Devide, /* IDE drive */ + Devswab, /* swab data between mem and device */ + Devmirr, /* mirror devices */ + Devmarvsata, /* Marvell sata disk drive */ + MAXDEV +}; + +/* + * tags on block + */ +/* DONT TOUCH, this is in disk structures */ +/* also, the order from Tdir to Tind4 (Tmaxind) is exploited in indirck() */ +enum +{ + Tnone = 0, + Tsuper, /* the super block */ +#ifdef OLD + Tdir, /* directory contents */ + Tind1, /* points to blocks */ + Tind2, /* points to Tind1 */ +#else + Tdirold, + Tind1old, + Tind2old, +#endif + Tfile, /* file contents */ + Tfree, /* in free list */ + Tbuck, /* cache fs bucket */ + Tvirgo, /* fake worm virgin bits */ + Tcache, /* cw cache things */ + Tconfig, /* configuration block */ +#ifndef OLD + /* Tdir & indirect blocks are last to allow for greater depth */ + Tdir, /* directory contents */ + Tind1, /* points to blocks */ + Tind2, /* points to Tind1 */ + Tind3, /* points to Tind2 */ + Tind4, /* points to Tind3 */ + Maxtind, +#endif + MAXTAG, + +#ifdef OLD + Tmaxind = Tind2, +#else + Tmaxind = Maxtind - 1, +#endif +}; + +/* + * flags to getbuf + */ +enum +{ + Bread = (1<<0), /* read the block if miss */ + Bprobe = (1<<1), /* return null if miss */ + Bmod = (1<<2), /* buffer is dirty, needs writing */ + Bimm = (1<<3), /* write immediately on putbuf */ + Bres = (1<<4), /* reserved, never renamed */ +}; + +extern register Mach* m; +extern register User* u; +extern Talarm talarm; + +Conf conf; +Cons cons; +#define MACHP(n) ((Mach*)(MACHADDR+n*BY2PG)) + +#pragma varargck type "Z" Device* +#pragma varargck type "T" Timet +#pragma varargck type "I" uchar* +#pragma varargck type "E" uchar* +#pragma varargck type "W" Filter* +#pragma varargck type "G" int + +extern int (*fsprotocol[])(Msgbuf*); +extern Rendez dawnrend; diff -Nru /sys/src/fs/port/portfns.h /sys/src/fs/port/portfns.h --- /sys/src/fs/port/portfns.h Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/port/portfns.h Tue Nov 1 00:00:00 2011 @@ -0,0 +1,286 @@ +void accessdir(Iobuf*, Dentry*, int, int); +void addfree(Device*, Off, Superb*); +Alarm* alarm(int, void(*)(Alarm*, void*), void*); +void alarminit(void); +void arpstart(void); +void arginit(void); +char* authaname(Auth*); +void authinit(void); +void authfree(Auth*); +Auth* authnew(char*, char*); +int authread(File*, uchar*, int); +int authuid(Auth*); +char* authuname(Auth*); +int authwrite(File*, uchar*, int); +void cdiag(char*, int); +int cnumb(void); +Device* config(void); +int rawchar(int); +Off bufalloc(Device*, int, long, int); +void buffree(Device*, Off, int, Truncstate *); +int byuid(void*, void*); +void cancel(Alarm*); +int canlock(Lock*); +int canqlock(QLock*); +void cfsdump(Filsys*); +Chan* chaninit(int, int, int); +void cmd_check(int, char*[]); +void cmd_users(int, char*[]); +void cmd_newuser(int, char*[]); +void cmd_netdb(int, char*[]); +void cmd_passwd(int, char*[]); +void cmd_printconf(int, char*[]); +int checkname(char*); +int checktag(Iobuf*, int, Off); +int cksum(void*, int, int); +int cksum0(int, int); +void clock(Timet, ulong); +void clockinit(void); +void clockreload(Timet); +void cyclstart(void); +void dotrace(int); +int conschar(void); +void consinit(void (*)(char*, int)); +void consreset(void); +void consstart(int); +int (*consgetc)(void); +void (*consputc)(int); +void (*consputs)(char*, int); +void consserve(void); +int conslock(void); +int con_attach(int, char*, char*); +int con_clone(int, int); +int con_create(int, char*, int, int, long, int); +int con_clri(int); +int con_fstat(int); +int con_open(int, int); +int con_read(int, char*, Off, int); +int con_remove(int); +void con_rintr(int); +int con_session(void); +int con_walk(int, char*); +int con_write(int, char*, Off, int); +int cwgrow(Device*, Superb*, int); +int cwfree(Device*, Off); +void cwinit(Device*); +Off cwraddr(Device*); +int cwread(Device*, Off, void*); +void cwream(Device*); +void cwrecover(Device*); +Off cwsaddr(Device*); +Devsize cwsize(Device*); +int cwwrite(Device*, Off, void*); +void datestr(char*, Timet); +Off dbufread(Iobuf*, Dentry*, Off, Off, int); +void delay(int); +int devcmpr(Device*, Device*); +void devream(Device*, int); +void devrecover(Device*); +void devinit(Device*); +int devread(Device*, Off, void*); +Devsize devsize(Device*); +int devwrite(Device*, Off, void*); +Iobuf* dnodebuf(Iobuf*, Dentry*, Off, int, int); +Iobuf* dnodebuf1(Iobuf*, Dentry*, Off, int, int); +void dofilter(Filter*, int, int, int); +int doremove(File*, int); +void dtrunc(Iobuf*, Dentry*, int); +int dtrunclen(Iobuf *p, Dentry *, Off newsize, int uid); +int dumpblock(Device*); +void dumpregs(Ureg*); +void dumpstack(User*); +void exit(void); +Float famd(Float, int, int, int); +ulong fdf(Float, int); +void fileinit(Chan*); +File* filep(Chan*, ulong, int); +void firmware(void); +int fname(char*); +int fpair(char*, char*); +void formatinit(void); +int fread(void*, int); +void freealarm(Alarm*); +void freefp(File*); +void freewp(Wpath*); +Filsys* fsstr(char*); +Devsize fwormsize(Device*); +void fwormream(Device*); +void fworminit(Device*); +int fwormread(Device*, Off, void*); +int fwormwrite(Device*, Off, void*); +char* getauthlist(void); +Iobuf* getbuf(Device*, Off, int); +void* getarg(void); +char* getwd(char*, char*); +int getc(void); +ulong getcallerpc(void*); +Dentry* getdir(Iobuf*, int); +Chan* getlcp(uchar*, long); +Off getraddr(Device*); +void getstring(char*, int, int); +void gotolabel(Label*); +void hexdump(void*, int); +int iaccess(File*, Dentry*, int); +Off ibbpow(int); +Off ibbpowsum(int); +void* ialloc(ulong, int); +void ilock(Lock*); +void iunlock(Lock*); +Off indfetch(Device*, Off, Off, Off , int, int, int); +int ingroup(int, int); +int inh(int, uchar*); +void init0(void); +void iobufinit(void); +void* iobufmap(Iobuf*); +void iobufunmap(Iobuf*); +int iobufql(QLock*); +int jukeread(Device*, Off, void*); +int jukewrite(Device*, Off, void*); +void jukeinit(Device*); +void jukeream(Device*); +void jukerecover(Device*); +Off jukesaddr(Device*); +Devsize jukesize(Device*); +void kbdchar(int); +void lights(int, int); +void launchinit(void); +void localconfinit(void); +int leadgroup(int, int); +void lock(Lock*); +void lockinit(void); +void machinit(void); +Msgbuf* mballoc(int, Chan*, int); +void mbinit(void); +void mbfree(Msgbuf*); +ulong meminit(void); +Iobuf* movebuf(Iobuf*); +void mcatinit(Device*); +int mcatread(Device*, Off, void*); +Devsize mcatsize(Device*); +int mcatwrite(Device*, Off, void*); +void mirrinit(Device*); +int mirrread(Device*, Off, void*); +Devsize mirrsize(Device*); +int mirrwrite(Device*, Off, void*); +void mkqid(Qid*, Dentry*, int); +int mkqidcmp(Qid*, Dentry*); +void mkqid9p1(Qid9p1*, Qid*); +void mkqid9p2(Qid*, Qid9p1*, int); +void mlevinit(Device*); +int mlevread(Device*, Off, void*); +Devsize mlevsize(Device*); +int mlevwrite(Device*, Off, void*); +int nametokey(char*, char*); +Alarm* newalarm(void); +File* newfp(void); +User* newproc(void); +Queue* newqueue(int); +void newstart(void); +Wpath* newwp(void); +Auth* newauth(void); +int nvrcheck(void); +int nvread(int, void*, int); +char* nvrgetconfig(void); +int nvrsetconfig(char*); +int nvwrite(int, void*, int); +int walkto(char*); +int no(void*); +vlong number(char*, int, int); +void online(void); +void otherinit(void); +void panic(char*, ...); +void partinit(Device*); +int partread(Device*, Off, void*); +Devsize partsize(Device*); +int partwrite(Device*, Off, void*); +void prdate(void); +void preread(Device*, Off); +void prflush(void); +int prime(vlong); +void printinit(void); +void procinit(void); +void putbuf(Iobuf*); +void putstrn(char *str, int n); +Off qidpathgen(Device*); +void qlock(QLock*); +void qunlock(QLock*); +void rahead(void); +void ready(User*); +void ream(Filsys*); +void* recv(Queue*, int); +void restartprint(Alarm*); +void rlock(RWlock*); +void rootream(Device*, Off); +int roread(Device*, Off, void*); +void rstate(Chan*, int); +Timet rtc2sec(Rtc *); +void runlock(RWlock*); +User* runproc(void); +void sched(void); +void schedinit(void); +int scsiio(Device*, int, uchar*, int, void*, int); +void sec2rtc(Timet, Rtc *); +void send(Queue*, void*); +void serve(void); +int serve9p1(Msgbuf*); +int serve9p2(Msgbuf*); +int setlabel(Label*); +void settag(Iobuf*, int, long); +void settime(Timet); +void sleep(Rendez*, int(*)(void*), void*); +void sntpinit(void); +int splhi(void); +int spllo(void); +void splx(int); +void startprint(void); +int strtouid(char*); +Off superaddr(Device*); +void superream(Device*, Off); +void swab(void*, int); +void sync(char*); +int syncblock(void); +long syscall(Ureg*); +void sysinit(void); +int Tfmt(Fmt*); +Timet time(void); +Timet nextime(Timet, int, int); +Tlock* tlocked(Iobuf*, Dentry*); +void tsleep(Rendez*, int(*)(void*), void*, int); +void touser(void); +Timet toytime(void); +Timet rtctime(void); +void setrtc(Timet); +void uidtostr(char*, int, int); +Uid* uidpstr(char*); +void unlock(Lock*); +void userinit(void(*)(void), void*, char*); +void vecinit(void); +void wakeup(Rendez*); +void wbflush(void); +void wlock(RWlock*); +void wormcopy(void); +void wormprobe(void); +void synccopy(void); +void waitsec(int); +long wormsearch(Device*, int, long, long); +int wormread(Device*, Off, void*); +Devsize wormsize(Device*); +Devsize wormsizeside(Device *, int side); +void wormsidestarts(Device *dev, int side, Sidestarts *stp); +int wormwrite(Device*, Off, void*); +void wreninit(Device*); +int wrenread(Device*, Off, void*); +Devsize wrensize(Device*); +int wrenwrite(Device*, Off, void*); +void wunlock(RWlock*); +void cmd_exec(char*); +void cmd_install(char*, char*, void (*)(int, char*[])); +ulong flag_install(char*, char*); + +int chartoip(uchar *, char *); +int isvalidip(uchar*); + +int nhgets(uchar*); +long nhgetl(uchar*); +void hnputs(uchar*, int); +void hnputl(uchar*, long); diff -Nru /sys/src/fs/port/print.c /sys/src/fs/port/print.c --- /sys/src/fs/port/print.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/port/print.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,21 @@ +#include "all.h" + +static Lock fmtl; + +void +_fmtlock(void) +{ + lock(&fmtl); +} + +void +_fmtunlock(void) +{ + unlock(&fmtl); +} + +int +_efgfmt(Fmt*) +{ + return -1; +} diff -Nru /sys/src/fs/port/proc.c /sys/src/fs/port/proc.c --- /sys/src/fs/port/proc.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/port/proc.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,372 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "mem.h" + +struct +{ + Lock; + ulong pid; +} pidalloc; + +struct +{ + Lock; + User *arena; + User *free; +} procalloc; + +volatile struct +{ + Lock; + User *head; + User *tail; +} runq; + +char *statename[] = /* BUG: generate automatically */ +{ + [Dead] "Dead", + [Moribund] "Moribund", + [Zombie] "Zombie", + [Ready] "Ready", + [Scheding] "Scheding", + [Running] "Running", + [Queueing] "Queueing", + [Sending] "Sending", + [Recving] "Recving", + [MMUing] "MMUing", + [Exiting] "Exiting", + [Inwait] "Inwait", + [Wakeme] "Wakeme", + [Broken] "Broken", +}; + +static +void +cmd_trace(int argc, char *argv[]) +{ + int n; + + n = 0; + if(argc > 1) + n = number(argv[1], n, 10); + dotrace(n); +} + +static +void +cmd_cpu(int argc, char *argv[]) +{ + int i, j; + User *p; + char *text; + + p = procalloc.arena; + for(i=0; itext; + if(!text) + continue; + for(j=1; j 1) + continue; + found: + print(" %2d %.3s %9s%7W%7W%7W\n", + p->pid, text, + statename[p->state], + p->time+0, p->time+1, p->time+2); + prflush(); + } +} + +/* + * Always splhi()'ed. + */ +void +schedinit(void) /* never returns */ +{ + User *p; + + setlabel(&m->sched); + if(u) { + m->proc = 0; + p = u; + u = 0; + if(p->state == Running) + ready(p); + p->mach = 0; + } + sched(); +} + +void +sched(void) +{ + User *p; + void (*f)(Ureg *, ulong); + + if(u) { + splhi(); + if(setlabel(&u->sched)) { /* woke up */ + if(u->mach) + panic("mach non zero"); + u->state = Running; + u->mach = m; + m->proc = u; + spllo(); + return; + } + gotolabel(&m->sched); + } + if(f = m->intr){ /* assign = */ + m->intr = 0; + (*f)(m->ureg, m->cause); + } + spllo(); + p = runproc(); + splhi(); + u = p; + gotolabel(&p->sched); +} + +void +ready(User *p) +{ + int s; + + s = splhi(); + lock(&runq); + p->rnext = 0; + if(runq.tail) + runq.tail->rnext = p; + else + runq.head = p; + runq.tail = p; + p->state = Ready; + unlock(&runq); + splx(s); +} + +/* + * Always called spllo + */ +User* +runproc(void) +{ + User *p; + + for (;;) { + while(runq.head == 0) /* if nobody to run, */ + ; /* idle with intrs enabled */ + splhi(); + lock(&runq); + p = runq.head; + if (p != nil && !p->mach) + break; + unlock(&runq); + spllo(); + } + if(p->rnext == 0) + runq.tail = 0; + runq.head = p->rnext; + if(p->state != Ready) + print("runproc %d %s\n", p->pid, statename[p->state]); + unlock(&runq); + p->state = Scheding; + spllo(); + return p; +} + +User* +newproc(void) +{ + User *p; + +loop: + lock(&procalloc); + if(p = procalloc.free) { /* assign = */ + procalloc.free = p->qnext; + p->state = Zombie; + unlock(&procalloc); + p->mach = 0; + p->qnext = 0; + p->exiting = 0; + lock(&pidalloc); + p->pid = ++pidalloc.pid; + unlock(&pidalloc); + return p; + } + unlock(&procalloc); + panic("no procs"); + goto loop; +} + +void +procinit(void) +{ + User *p; + int i; + + procalloc.free = ialloc(conf.nproc*sizeof(User), 0); + procalloc.arena = procalloc.free; + + p = procalloc.free; + for(i=0; iqnext = p+1; + wakeup(&p->tsleep); + } + p->qnext = 0; + wakeup(&p->tsleep); + + cmd_install("cpu", "[proc] -- process cpu time", cmd_cpu); /**/ + cmd_install("trace", "[number] -- stack trace/qlocks", cmd_trace); /**/ +} + +void +sleep(Rendez *r, int (*f)(void*), void *arg) +{ + User *p; + int s; + + /* + * spl is to allow lock to be called + * at interrupt time. lock is mutual exclusion + */ + s = splhi(); + lock(r); + + /* + * if condition happened, never mind + */ + if((*f)(arg)) { + unlock(r); + splx(s); + return; + } + + /* + * now we are committed to + * change state and call scheduler + */ + p = r->p; + if(p) + print("double sleep %d\n", p->pid); + u->state = Wakeme; + r->p = u; + + /* + * sched does the unlock(u) when it is safe + * it also allows to be called splhi. + */ + unlock(r); + sched(); +} + +int +tfn(void *arg) +{ + return MACHP(0)->ticks >= u->twhen || (*u->tfn)(arg); +} + +void +tsleep(Rendez *r, int (*fn)(void*), void *arg, int ms) +{ + Timet when; + User *f, **l; + + when = MS2TK(ms)+MACHP(0)->ticks; + + lock(&talarm); + /* take out of list if checkalarm didn't */ + if (u == nil) + panic("tsleep: nil u"); + if(u->trend) { + l = &talarm.list; + for(f = *l; f; f = f->tlink) { + if(f == u) { + *l = u->tlink; + break; + } + l = &f->tlink; + } + } + /* insert in increasing time order */ + l = &talarm.list; + for(f = *l; f; f = f->tlink) { + if(f->twhen >= when) + break; + l = &f->tlink; + } + u->trend = r; + u->twhen = when; + u->tfn = fn; + u->tlink = *l; + *l = u; + unlock(&talarm); + + sleep(r, tfn, arg); + u->twhen = 0; +} + +void +wakeup(Rendez *r) +{ + User *p; + int s; + + s = splhi(); + lock(r); + p = r->p; + if(p) { + r->p = 0; + if(p->state != Wakeme) + panic("wakeup: not Wakeme"); + ready(p); + } + unlock(r); + splx(s); +} + +void +dotrace(int n) +{ + User *p; + QLock *q; + int i, j, f; + + p = procalloc.arena; + for(i=0; ipid == n) + dumpstack(p); + continue; + } + if(q = p->has.want) { + if(q->name) + print("pid %d wants %.10s\n", + p->pid, q->name); + else + print("pid %d wants %p\n", + p->pid, q); + } + f = 0; + for(j=0; jhas.q[j]) { + if(f == 0) { + if(p->has.want == 0) + print("pid %d wants nothing\n", p->pid); + print(" has"); + f = 1; + } + if(q->name) + print(" %.10s", q->name); + else + print(" %p", q); + } + } + if(f) + print("\n"); + } +} diff -Nru /sys/src/fs/port/sd.h /sys/src/fs/port/sd.h --- /sys/src/fs/port/sd.h Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/port/sd.h Tue Nov 1 00:00:00 2011 @@ -0,0 +1,127 @@ +/* + * Storage Device. + */ +typedef struct SDev SDev; +typedef struct SDifc SDifc; +typedef struct SDpart SDpart; +typedef struct SDreq SDreq; +typedef struct SDunit SDunit; + +typedef struct SDpart { + Devsize start; + Devsize end; + char name[NAMELEN]; + char user[NAMELEN]; + ulong perm; + int valid; + void *crud; +} SDpart; + +typedef struct SDunit { + SDev* dev; + int subno; + uchar inquiry[256]; /* format follows SCSI spec */ + uchar sense[18]; /* format follows SCSI spec */ + char name[NAMELEN]; + Rendez rendez; + + QLock ctl; + Devsize sectors; /* not Off: even 32-bit fs can have big disks */ + ulong secsize; + SDpart* part; + int npart; /* of valid partitions */ + int changed; + + QLock raw; + int state; + ulong pid; + SDreq* req; +} SDunit; + +typedef struct SDev { + SDifc* ifc; /* pnp/legacy */ + void *ctlr; + int idno; + int index; /* into unit space */ + int nunit; + SDev* next; + + QLock; /* enable/disable */ + int enabled; +} SDev; + +typedef struct SDifc { + char* name; + + SDev* (*pnp)(void); + SDev* (*legacy)(int, int); + SDev* (*id)(SDev*); + int (*enable)(SDev*); + int (*disable)(SDev*); + + int (*verify)(SDunit*); + int (*online)(SDunit*); + int (*rio)(SDreq*); + int (*rctl)(SDunit*, char*, int); + int (*wctl)(SDunit*, void*); + + long (*bio)(SDunit*, int, int, void*, long, Off); +} SDifc; + +typedef struct SDreq { + SDunit* unit; + int lun; + int write; + uchar cmd[16]; + int clen; + void* data; + int dlen; + + int flags; + + int status; + long rlen; + uchar sense[256]; +} SDreq; + +enum { + SDnosense = 0x00000001, + SDvalidsense = 0x00010000, +}; + +enum { + SDretry = -5, /* internal to controllers */ + SDmalloc = -4, + SDeio = -3, + SDtimeout = -2, + SDnostatus = -1, + + SDok = 0, + + SDcheck = 0x02, /* check condition */ + SDbusy = 0x08, /* busy */ + + SDmaxio = 2048*1024, + SDnpart = 16, +}; + +/* sdscsi.c */ +int scsiverify(SDunit*); +int scsionline(SDunit*); +long scsibio(SDunit*, int, int, void*, long, Off); +SDev* scsiid(SDev*, SDifc*); + +/* devsd.c */ +int sdfakescsi(SDreq *r, void *info, int ilen); +int sdmodesense(SDreq *r, uchar *cmd, void *info, int ilen); +long sdbio(SDunit *unit, SDpart *pp, void *a, long len, Off off); +void partition(SDunit*); +void addpartconf(SDunit*); +SDpart* sdfindpart(SDunit*, char*); +void sdaddpart(SDunit*, char*, Devsize, Devsize); +void* sdmalloc(void*, ulong); + +enum { + IrqATA0 = 14, + IrqATA1, +}; diff -Nru /sys/src/fs/port/sub.c /sys/src/fs/port/sub.c --- /sys/src/fs/port/sub.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/port/sub.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,1658 @@ +#include "all.h" +#include "io.h" + +#ifdef OLD +#define swaboff swab4 +#else +#define swaboff swab8 +#endif + +Filsys* +fsstr(char *p) +{ + Filsys *fs; + + for(fs=filsys; fs->name; fs++) + if(strcmp(fs->name, p) == 0) + return fs; + return 0; +} + +Filsys* +dev2fs(Device *dev) +{ + Filsys *fs; + + for(fs=filsys; fs->name; fs++) + if(fs->dev == dev) + return fs; + return 0; +} + +/* + * allocate 'count' contiguous channels + * of type 'type' and return pointer to base + */ +Chan* +chaninit(int type, int count, int data) +{ + uchar *p; + Chan *cp, *icp; + int i; + + p = ialloc(count * (sizeof(Chan)+data), 0); + icp = (Chan*)p; + for(i=0; inext = chans; + chans = cp; + cp->type = type; + cp->chan = cons.chano; + cons.chano++; + strncpy(cp->whoname, "", sizeof(cp->whoname)); + dofilter(&cp->work, C0a, C0b, 1); + dofilter(&cp->rate, C0a, C0b, 1000); + wlock(&cp->reflock); + wunlock(&cp->reflock); + rlock(&cp->reflock); + runlock(&cp->reflock); + + p += sizeof(Chan); + if(data){ + cp->pdata = p; + p += data; + } + } + return icp; +} + +void +fileinit(Chan *cp) +{ + File *f, *prev; + Tlock *t; + int h; + +loop: + lock(&flock); + for (h=0; hnext) { + if(f->cp != cp) + continue; + if(prev) { + prev->next = f->next; + f->next = flist[h]; + flist[h] = f; + } + flist[h] = f->next; + unlock(&flock); + + qlock(f); + if(t = f->tlock) { + if(t->file == f) + t->time = 0; /* free the lock */ + f->tlock = 0; + } + if(f->open & FREMOV) + doremove(f, 0); + freewp(f->wpath); + f->open = 0; + authfree(f->auth); + f->auth = 0; + f->cp = 0; + qunlock(f); + goto loop; + } + unlock(&flock); +} + +#define NOFID (ulong)~0 + +/* + * returns a locked file structure + */ +File* +filep(Chan *cp, ulong fid, int flag) +{ + File *f; + int h; + + if(fid == NOFID) + return 0; + + h = (long)cp + fid; + if(h < 0) + h = ~h; + h %= nelem(flist); + +loop: + lock(&flock); + for(f=flist[h]; f; f=f->next) + if(f->fid == fid && f->cp == cp){ + /* + * Already in use is an error + * when called from attach or clone (walk + * in 9P2000). The console uses FID[12] and + * never clunks them so catch that case. + */ + if(flag == 0 || cp == cons.chan) + goto out; + unlock(&flock); + return 0; + } + + if(flag) { + f = newfp(); + if(f) { + f->fid = fid; + f->cp = cp; + f->wpath = 0; + f->tlock = 0; + f->doffset = 0; + f->dslot = 0; + f->auth = 0; + f->next = flist[h]; + flist[h] = f; + goto out; + } + } + unlock(&flock); + return 0; + +out: + unlock(&flock); + qlock(f); + if(f->fid == fid && f->cp == cp) + return f; + qunlock(f); + goto loop; +} + +/* + * always called with flock locked + */ +File* +newfp(void) +{ + static int first; + File *f; + int start, i; + + i = first; + start = i; + do { + f = &files[i]; + i++; + if(i >= conf.nfile) + i = 0; + if(f->cp) + continue; + first = i; + return f; + } while(i != start); + + print("out of files\n"); + return 0; +} + +void +freefp(File *fp) +{ + Chan *cp; + File *f, *prev; + int h; + + if(!fp || !(cp = fp->cp)) + return; + + h = (long)cp + fp->fid; + if(h < 0) + h = ~h; + h %= nelem(flist); + + lock(&flock); + for(prev=0,f=flist[h]; f; prev=f,f=f->next) + if(f == fp) { + if(prev) + prev->next = f->next; + else + flist[h] = f->next; + break; + } + fp->cp = 0; + unlock(&flock); +} + +int +iaccess(File *f, Dentry *d, int m) +{ + + /* uid none gets only other permissions */ + if(f->uid != 0) { + /* + * owner + */ + if(f->uid == d->uid) + if((m<<6) & d->mode) + return 0; + /* + * group membership + */ + if(ingroup(f->uid, d->gid)) + if((m<<3) & d->mode) + return 0; + } + + /* + * other + */ + if(m & d->mode) { + if((d->mode & DDIR) && (m == DEXEC)) + return 0; + if(!ingroup(f->uid, 9999)) + return 0; + } + + /* + * various forms of superuser + */ + if(wstatallow) + return 0; + if(duallow != 0 && duallow == f->uid) + if((d->mode & DDIR) && (m == DREAD || m == DEXEC)) + return 0; + + return 1; +} + +Tlock* +tlocked(Iobuf *p, Dentry *d) +{ + Tlock *t, *t1; + Off qpath; + Timet tim; + Device *dev; + + tim = toytime(); + qpath = d->qid.path; + dev = p->dev; + +again: + t1 = 0; + for(t=tlocks+NTLOCK-1; t>=tlocks; t--) { + if(t->qpath == qpath) + if(t->time >= tim) + if(t->dev == dev) + return nil; /* its locked */ + if(t1 != nil && t->time == 0) + t1 = t; /* remember free lock */ + } + if(t1 == 0) { + // reclaim old locks + lock(&tlocklock); + for(t=tlocks+NTLOCK-1; t>=tlocks; t--) + if(t->time < tim) { + t->time = 0; + t1 = t; + } + unlock(&tlocklock); + } + if(t1) { + lock(&tlocklock); + if(t1->time != 0) { + unlock(&tlocklock); + goto again; + } + t1->dev = dev; + t1->qpath = qpath; + t1->time = tim + TLOCK; + unlock(&tlocklock); + } + /* botch + * out of tlock nodes simulates + * a locked file + */ + return t1; +} + +Wpath* +newwp(void) +{ + static int si = 0; + int i; + Wpath *w, *sw, *ew; + + i = si + 1; + if(i < 0 || i >= conf.nwpath) + i = 0; + si = i; + sw = &wpaths[i]; + ew = &wpaths[conf.nwpath]; + for(w=sw;;) { + w++; + if(w >= ew) + w = &wpaths[0]; + if(w == sw) { + print("out of wpaths\n"); + return 0; + } + if(w->refs) + continue; + lock(&wpathlock); + if(w->refs) { + unlock(&wpathlock); + continue; + } + w->refs = 1; + w->up = 0; + unlock(&wpathlock); + return w; + } + +} + +void +freewp(Wpath *w) +{ + lock(&wpathlock); + for(; w; w=w->up) + w->refs--; + unlock(&wpathlock); +} + +Off +qidpathgen(Device *dev) +{ + Iobuf *p; + Superb *sb; + Off path; + + p = getbuf(dev, superaddr(dev), Bread|Bmod); + if(!p || checktag(p, Tsuper, QPSUPER)) + panic("newqid: super block"); + sb = (Superb*)p->iobuf; + sb->qidgen++; + path = sb->qidgen; + putbuf(p); + return path; +} + +/* truncating to length > 0 */ +static void +truncfree(Truncstate *ts, Device *dev, int d, Iobuf *p, int i) +{ + int pastlast; + Off a; + + pastlast = ts->pastlast; + a = ((Off *)p->iobuf)[i]; + if (d > 0 || pastlast) + buffree(dev, a, d, ts); + if (pastlast) { + ((Off *)p->iobuf)[i] = 0; + p->flags |= Bmod|Bimm; + } else if (d == 0 && ts->relblk == ts->lastblk) + ts->pastlast = 1; + if (d == 0) + ts->relblk++; +} + +/* + * free the block at `addr' on dev. + * if it's an indirect block (d [depth] > 0), + * first recursively free all the blocks it names. + * + * ts->relblk is the block number within the file of this + * block (or the first data block eventually pointed to via + * this indirect block). + */ +void +buffree(Device *dev, Off addr, int d, Truncstate *ts) +{ + Iobuf *p; + Off a; + int i, pastlast; + + if(!addr) + return; + pastlast = (ts == nil? 1: ts->pastlast); + /* + * if this is an indirect block, recurse and free any + * suitable blocks within it (possibly via further indirect blocks). + */ + if(d > 0) { + d--; + p = getbuf(dev, addr, Bread); + if(p) { + if (ts == nil) /* common case: create */ + for(i=INDPERBUF-1; i>=0; i--) { + a = ((Off *)p->iobuf)[i]; + buffree(dev, a, d, nil); + } + else /* wstat truncation */ + for (i = 0; i < INDPERBUF; i++) + truncfree(ts, dev, d, p, i); + putbuf(p); + } + } + if (!pastlast) + return; + /* + * having zeroed the pointer to this block, add it to the free list. + * stop outstanding i/o + */ + p = getbuf(dev, addr, Bprobe); + if(p) { + p->flags &= ~(Bmod|Bimm); + putbuf(p); + } + /* + * dont put written worm + * blocks into free list + */ + if(dev->type == Devcw) { + i = cwfree(dev, addr); + if(i) + return; + } + p = getbuf(dev, superaddr(dev), Bread|Bmod); + if(!p || checktag(p, Tsuper, QPSUPER)) + panic("buffree: super block"); + addfree(dev, addr, (Superb*)p->iobuf); + putbuf(p); +} + +Off +bufalloc(Device *dev, int tag, long qid, int uid) +{ + Iobuf *bp, *p; + Superb *sb; + Off a, n; + + p = getbuf(dev, superaddr(dev), Bread|Bmod); + if(!p || checktag(p, Tsuper, QPSUPER)) { + print("bufalloc: super block\n"); + if(p) + putbuf(p); + return 0; + } + sb = (Superb*)p->iobuf; + +loop: + n = --sb->fbuf.nfree; + sb->tfree--; + if(n < 0 || n >= FEPERBUF) { + print("bufalloc: %Z: bad freelist\n", dev); + n = 0; + sb->fbuf.free[0] = 0; + } + a = sb->fbuf.free[n]; + if(n <= 0) { + if(a == 0) { + sb->tfree = 0; + sb->fbuf.nfree = 1; + if(dev->type == Devcw) { + n = uid; + if(n < 0 || n >= nelem(growacct)) + n = 0; + growacct[n]++; + if(cwgrow(dev, sb, uid)) + goto loop; + } + putbuf(p); + print("fs %Z full uid=%d\n", dev, uid); + return 0; + } + bp = getbuf(dev, a, Bread); + if(!bp || checktag(bp, Tfree, QPNONE)) { + if(bp) + putbuf(bp); + putbuf(p); + return 0; + } + sb->fbuf = *(Fbuf*)bp->iobuf; + putbuf(bp); + } + + bp = getbuf(dev, a, Bmod); + memset(bp->iobuf, 0, RBUFSIZE); + settag(bp, tag, qid); + if(tag == Tind1 || tag == Tind2 || tag == Tdir) + bp->flags |= Bimm; + putbuf(bp); + putbuf(p); + return a; +} + +/* + * what are legal characters in a name? + * only disallow control characters. + * a) utf avoids control characters. + * b) '/' may not be the separator + */ +int +checkname(char *n) +{ + int i, c; + + for(i=0; ifbuf.nfree; + if(n < 0 || n > FEPERBUF) + panic("addfree: bad freelist"); + if(n >= FEPERBUF) { + p = getbuf(dev, addr, Bmod|Bimm); + if(p == 0) + panic("addfree: getbuf"); + *(Fbuf*)p->iobuf = sb->fbuf; + settag(p, Tfree, QPNONE); + putbuf(p); + n = 0; + } + sb->fbuf.free[n++] = addr; + sb->fbuf.nfree = n; + sb->tfree++; + if(addr >= sb->fsize) + sb->fsize = addr+1; +} + +static int +Yfmt(Fmt* fmt) +{ + Chan *cp; + char s[20]; + + cp = va_arg(fmt->args, Chan*); + sprint(s, "C%d.%.3d", cp->type, cp->chan); + + return fmtstrcpy(fmt, s); +} + +static int +Zfmt(Fmt* fmt) +{ + Device *d; + int c, c1; + char s[100]; + + d = va_arg(fmt->args, Device*); + if(d == 0) { + sprint(s, "Z***"); + goto out; + } + switch(d->type) { + default: + sprint(s, "D%d", d->type); + break; + case Devwren: + c = 'w'; + goto d1; + case Devide: + c = 'h'; + goto d1; + case Devmarvsata: + c = 'm'; + goto d1; + case Devworm: + c = 'r'; + goto d1; + case Devlworm: + c = 'l'; + goto d1; + d1: + if(d->wren.ctrl == 0 && d->wren.lun == 0) + sprint(s, "%c%d", c, d->wren.targ); + else + sprint(s, "%c%d.%d.%d", c, d->wren.ctrl, d->wren.targ, d->wren.lun); + break; + case Devmcat: + c = '('; + c1 = ')'; + goto d2; + case Devmlev: + c = '['; + c1 = ']'; + goto d2; + case Devmirr: + c = '{'; + c1 = '}'; + d2: + if(d->cat.first == d->cat.last) + sprint(s, "%c%Z%c", c, d->cat.first, c1); + else + if(d->cat.first->link == d->cat.last) + sprint(s, "%c%Z%Z%c", c, d->cat.first, d->cat.last, c1); + else + sprint(s, "%c%Z-%Z%c", c, d->cat.first, d->cat.last, c1); + break; + case Devro: + sprint(s, "o%Z%Z", d->ro.parent->cw.c, d->ro.parent->cw.w); + break; + case Devcw: + sprint(s, "c%Z%Z", d->cw.c, d->cw.w); + break; + case Devjuke: + sprint(s, "j%Z%Z", d->j.j, d->j.m); + break; + case Devfworm: + sprint(s, "f%Z", d->fw.fw); + break; + case Devpart: + sprint(s, "p(%Z)%ld.%ld", + d->part.d, d->part.base, d->part.size); + break; + case Devswab: + sprint(s, "x%Z", d->swab.d); + break; + case Devnone: + sprint(s, "n"); + break; + } +out: + return fmtstrcpy(fmt, s); +} + +static int +Wfmt(Fmt* fmt) +{ + Filter* a; + char s[30]; + + a = va_arg(fmt->args, Filter*); + snprint(s, sizeof s, "%lud", fdf(a->filter, a->c3*a->c1)); + + return fmtstrcpy(fmt, s); +} + +static int +Gfmt(Fmt* fmt) +{ + int t; + char *s; + + t = va_arg(fmt->args, int); + s = ""; + if(t >= 0 && t < MAXTAG) + s = tagnames[t]; + return fmtstrcpy(fmt, s); +} + +static int +Efmt(Fmt* fmt) +{ + char s[64]; + uchar *p; + + p = va_arg(fmt->args, uchar*); + sprint(s, "%.2ux%.2ux%.2ux%.2ux%.2ux%.2ux", + p[0], p[1], p[2], p[3], p[4], p[5]); + + return fmtstrcpy(fmt, s); +} + +static int +Ifmt(Fmt* fmt) +{ + char s[64]; + uchar *p; + + p = va_arg(fmt->args, uchar*); + sprint(s, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); + + return fmtstrcpy(fmt, s); +} + +void +formatinit(void) +{ + quotefmtinstall(); + fmtinstall('Y', Yfmt); /* print channels */ + fmtinstall('Z', Zfmt); /* print devices */ + fmtinstall('W', Wfmt); /* print filters */ + fmtinstall('G', Gfmt); /* print tags */ + fmtinstall('T', Tfmt); /* print times */ + fmtinstall('E', Efmt); /* print ether addresses */ + fmtinstall('I', Ifmt); /* print ip addresses */ +} + +void +rootream(Device *dev, Off addr) +{ + Iobuf *p; + Dentry *d; + + p = getbuf(dev, addr, Bmod|Bimm); + memset(p->iobuf, 0, RBUFSIZE); + settag(p, Tdir, QPROOT); + d = getdir(p, 0); + strcpy(d->name, "/"); + d->uid = -1; + d->gid = -1; + d->mode = DALLOC | DDIR | + ((DREAD|DEXEC) << 6) | + ((DREAD|DEXEC) << 3) | + ((DREAD|DEXEC) << 0); + d->qid = QID9P1(QPROOT|QPDIR,0); + d->atime = time(); + d->mtime = d->atime; + d->muid = 0; + putbuf(p); +} + +void +superream(Device *dev, Off addr) +{ + Iobuf *p; + Superb *s; + Off i; + + p = getbuf(dev, addr, Bmod|Bimm); + memset(p->iobuf, 0, RBUFSIZE); + settag(p, Tsuper, QPSUPER); + + s = (Superb*)p->iobuf; + s->fstart = 2; + s->fsize = devsize(dev); + s->fbuf.nfree = 1; + s->qidgen = 10; +#ifdef AUTOSWAB + s->magic = 0x123456789abcdef0; +#endif + for(i=s->fsize-1; i>=addr+2; i--) + addfree(dev, i, s); + putbuf(p); +} + +struct +{ + Lock; + Msgbuf *smsgbuf; + Msgbuf *lmsgbuf; +} msgalloc; + +/* + * pre-allocate some message buffers at boot time. + * if this supply is exhausted, more will be allocated as needed. + */ +void +mbinit(void) +{ + Msgbuf *mb; + Rabuf *rb; + int i; + + lock(&msgalloc); + unlock(&msgalloc); + msgalloc.lmsgbuf = 0; + msgalloc.smsgbuf = 0; + for(i=0; ixdata = ialloc(LARGEBUF+256, 256); + else + mb->xdata = ialloc(LARGEBUF+OFFMSG, LINESIZE); + mb->flags = LARGE; + mb->free = 0; + mbfree(mb); + cons.nlarge++; + } + for(i=0; ixdata = ialloc(SMALLBUF+256, 256); + else + mb->xdata = ialloc(SMALLBUF+OFFMSG, LINESIZE); + mb->flags = 0; + mb->free = 0; + mbfree(mb); + cons.nsmall++; + } + memset(mballocs, 0, sizeof(mballocs)); + + lock(&rabuflock); + unlock(&rabuflock); + rabuffree = 0; + for(i=0; i<1000; i++) { + rb = ialloc(sizeof(*rb), 0); + rb->link = rabuffree; + rabuffree = rb; + } +} + +Msgbuf* +mballoc(int count, Chan *cp, int category) +{ + Msgbuf *mb; + + ilock(&msgalloc); + if(count > SMALLBUF) { + if(count > LARGEBUF) + panic("msgbuf count"); + mb = msgalloc.lmsgbuf; + if(mb == 0) { + mb = ialloc(sizeof(Msgbuf), 0); + if(1) + mb->xdata = ialloc(LARGEBUF+256, 256); + else + mb->xdata = ialloc(LARGEBUF+OFFMSG, LINESIZE); + mb->free = 0; + cons.nlarge++; + } else + msgalloc.lmsgbuf = mb->next; + mb->flags = LARGE; + } else { + mb = msgalloc.smsgbuf; + if(mb == 0) { + mb = ialloc(sizeof(Msgbuf), 0); + if(1) + mb->xdata = ialloc(SMALLBUF+256, 256); + else + mb->xdata = ialloc(SMALLBUF+OFFMSG, LINESIZE); + mb->free = 0; + cons.nsmall++; + } else + msgalloc.smsgbuf = mb->next; + mb->flags = 0; + } + mballocs[category]++; + iunlock(&msgalloc); + mb->count = count; + mb->chan = cp; + mb->next = 0; + mb->param = 0; + mb->category = category; + if(1) + mb->data = mb->xdata+256; + else + mb->data = mb->xdata+OFFMSG; + mb->free = 0; + return mb; +} + +void +mbfree(Msgbuf *mb) +{ + if(mb == nil) + return; + if(mb->flags & BTRACE) + print("mbfree: BTRACE cat=%d flags=%ux, caller 0x%lux\n", + mb->category, mb->flags, getcallerpc(&mb)); + + /* + * drivers which perform non cache coherent DMA manage their own buffer + * pool of uncached buffers and provide their own free routine. + * this is provided mainly for ethernet drivers ported from cpu kernel. + */ + if(mb->flags&Mbrcvbuf) { + if (mb->free == nil) + panic("freeb: nil mb->free"); + (*mb->free)(mb); + return; + } + if(mb->flags & FREE) + panic("mbfree already free"); + + ilock(&msgalloc); + mballocs[mb->category]--; + mb->flags |= FREE; + if(mb->flags & LARGE) { + mb->next = msgalloc.lmsgbuf; + msgalloc.lmsgbuf = mb; + } else { + mb->next = msgalloc.smsgbuf; + msgalloc.smsgbuf = mb; + } + mb->data = 0; + mb->free = 0; + iunlock(&msgalloc); +} + +/* + * returns 1 if n is prime + * used for adjusting lengths + * of hashing things. + * there is no need to be clever + */ +int +prime(vlong n) +{ + long i; + + if((n%2) == 0) + return 0; + for(i=3;; i+=2) { + if((n%i) == 0) + return 0; + if((vlong)i*i >= n) + return 1; + } +} + +char* +getwd(char *word, char *line) +{ + int c, n; + + while(*line == ' ') + line++; + for(n=0; ncount; + if(c > 0) { + i = q->loc; + a = q->args[i]; + i++; + if(i >= q->size) + i = 0; + q->loc = i; + q->count = c-1; + p = q->whead; + if(p) { + q->whead = p->qnext; + if(q->whead == 0) + q->wtail = 0; + ready(p); + } + unlock(q); + return a; + } + p = q->rtail; + if(p == 0) + q->rhead = u; + else + p->qnext = u; + q->rtail = u; + u->qnext = 0; + s = splhi(); + u->state = Recving; + unlock(q); + sched(); + splx(s); + } +} + +void +send(Queue *q, void *a) +{ + User *p; + int i, c; + long s; + + if(q == 0) + panic("send null q"); + for(;;) { + lock(q); + c = q->count; + if(c < q->size) { + i = q->loc + c; + if(i >= q->size) + i -= q->size; + q->args[i] = a; + q->count = c+1; + p = q->rhead; + if(p) { + q->rhead = p->qnext; + if(q->rhead == 0) + q->rtail = 0; + ready(p); + } + unlock(q); + return; + } + p = q->wtail; + if(p == 0) + q->whead = u; + else + p->qnext = u; + q->wtail = u; + u->qnext = 0; + s = splhi(); + u->state = Sending; + unlock(q); + sched(); + splx(s); + } +} + +Queue* +newqueue(int size) +{ + Queue *q; + + q = ialloc(sizeof(Queue) + (size-1)*sizeof(void*), 0); + q->size = size; + lock(q); + unlock(q); + return q; +} + +no(void*) +{ + return 0; +} + +int +devread(Device *d, Off b, void *c) +{ + int e; + + for (;;) + switch(d->type) + { + case Devcw: + return cwread(d, b, c); + + case Devjuke: + d = d->j.m; + break; + + case Devro: + return roread(d, b, c); + + case Devwren: + return wrenread(d, b, c); + case Devide: + return ideread(d, b, c); + case Devmarvsata: + return mvideread(d, b, c); + + case Devworm: + case Devlworm: + return wormread(d, b, c); + + case Devfworm: + return fwormread(d, b, c); + + case Devmcat: + return mcatread(d, b, c); + + case Devmlev: + return mlevread(d, b, c); + + case Devmirr: + return mirrread(d, b, c); + + case Devpart: + return partread(d, b, c); + + case Devswab: + e = devread(d->swab.d, b, c); + if(e == 0) + swab(c, 0); + return e; + + case Devnone: + print("read from device none(%lld)\n", (Wideoff)b); + return 1; + default: + panic("illegal device in read: %Z %lld", d, (Wideoff)b); + return 1; + } +} + +int +devwrite(Device *d, Off b, void *c) +{ + int e; + + /* + * set readonly to non-0 to prevent all writes; + * mainly for trying dangerous experiments. + */ + if (readonly) + return 0; + for (;;) + switch(d->type) + { + case Devcw: + return cwwrite(d, b, c); + + case Devjuke: + d = d->j.m; + break; + + case Devro: + print("write to ro device %Z(%lld)\n", d, (Wideoff)b); + return 1; + + case Devwren: + return wrenwrite(d, b, c); + case Devide: + return idewrite(d, b, c); + case Devmarvsata: + return mvidewrite(d, b, c); + + case Devworm: + case Devlworm: + return wormwrite(d, b, c); + + case Devfworm: + return fwormwrite(d, b, c); + + case Devmcat: + return mcatwrite(d, b, c); + + case Devmlev: + return mlevwrite(d, b, c); + + case Devmirr: + return mirrwrite(d, b, c); + + case Devpart: + return partwrite(d, b, c); + + case Devswab: + swab(c, 1); + e = devwrite(d->swab.d, b, c); + swab(c, 0); + return e; + + case Devnone: + /* checktag() can generate blocks with type devnone */ + return 0; + default: + panic("illegal device in write: %Z %ld", d, b); + return 1; + } +} + +Devsize +devsize(Device *d) +{ + for (;;) + switch(d->type) + { + case Devcw: + case Devro: + return cwsize(d); + + case Devjuke: + d = d->j.m; + break; + + case Devwren: + return wrensize(d); + case Devide: + return idesize(d); + case Devmarvsata: + return mvidesize(d); + + case Devworm: + case Devlworm: + return wormsize(d); + + case Devfworm: + return fwormsize(d); + + case Devmcat: + return mcatsize(d); + + case Devmlev: + return mlevsize(d); + + case Devmirr: + return mirrsize(d); + + case Devpart: + return partsize(d); + + case Devswab: + d = d->swab.d; + break; + default: + panic("illegal device in dev_size: %Z", d); + return 0; + } +} + +Off +superaddr(Device *d) +{ + for (;;) + switch(d->type) { + default: + return SUPER_ADDR; + + case Devcw: + case Devro: + return cwsaddr(d); + + case Devswab: + d = d->swab.d; + break; + } +} + +Off +getraddr(Device *d) +{ + for (;;) + switch(d->type) { + default: + return ROOT_ADDR; + + case Devcw: + case Devro: + return cwraddr(d); + + case Devswab: + d = d->swab.d; + break; + } +} + +void +devream(Device *d, int top) +{ + Device *l; + +loop: + print(" devream: %Z %d\n", d, top); + switch(d->type) { + default: + print("ream: unknown dev type %Z\n", d); + return; + + case Devcw: + devream(d->cw.w, 0); + devream(d->cw.c, 0); + if(top) { + wlock(&mainlock); + cwream(d); + wunlock(&mainlock); + } + devinit(d); + return; + + case Devfworm: + devream(d->fw.fw, 0); + fwormream(d); + break; + + case Devpart: + devream(d->part.d, 0); + break; + + case Devmlev: + case Devmcat: + case Devmirr: + for(l=d->cat.first; l; l=l->link) + devream(l, 0); + break; + + case Devjuke: + case Devworm: + case Devlworm: + case Devwren: + case Devide: + case Devmarvsata: + break; + + case Devswab: + d = d->swab.d; + goto loop; + } + devinit(d); + if(top) { + wlock(&mainlock); + rootream(d, ROOT_ADDR); + superream(d, SUPER_ADDR); + wunlock(&mainlock); + } +} + +void +devrecover(Device *d) +{ + for (;;) { + print("recover: %Z\n", d); + switch(d->type) { + default: + print("recover: unknown dev type %Z\n", d); + return; + + case Devcw: + wlock(&mainlock); /* recover */ + cwrecover(d); + wunlock(&mainlock); + return; + + case Devswab: + d = d->swab.d; + break; + } + } +} + +void +devinit(Device *d) +{ + for (;;) { + if(d->init) + return; + d->init = 1; + print(" devinit %Z\n", d); + switch(d->type) { + default: + print("devinit unknown device %Z\n", d); + return; + + case Devro: + cwinit(d->ro.parent); + return; + + case Devcw: + cwinit(d); + return; + + case Devjuke: + jukeinit(d); + return; + + case Devwren: + wreninit(d); + return; + + case Devide: + ideinit(d); + return; + + case Devmarvsata: + mvideinit(d); + return; + + case Devworm: + case Devlworm: + return; + + case Devfworm: + fworminit(d); + return; + + case Devmcat: + mcatinit(d); + return; + + case Devmlev: + mlevinit(d); + return; + + case Devmirr: + mirrinit(d); + return; + + case Devpart: + partinit(d); + return; + + case Devswab: + d = d->swab.d; + break; + + case Devnone: + print("devinit of Devnone\n"); + return; + } + } +} + +void +swab2(void *c) +{ + uchar *p; + int t; + + p = c; + + t = p[0]; + p[0] = p[1]; + p[1] = t; +} + +void +swab4(void *c) +{ + uchar *p; + int t; + + p = c; + + t = p[0]; + p[0] = p[3]; + p[3] = t; + + t = p[1]; + p[1] = p[2]; + p[2] = t; +} + +void +swab8(void *c) +{ + uchar *p; + int t; + + p = c; + + t = p[0]; + p[0] = p[7]; + p[7] = t; + + t = p[1]; + p[1] = p[6]; + p[6] = t; + + t = p[2]; + p[2] = p[5]; + p[5] = t; + + t = p[3]; + p[3] = p[4]; + p[4] = t; +} + +/* + * swab a block + * flag = 0 -- convert from foreign to native + * flag = 1 -- convert from native to foreign + */ +void +swab(void *c, int flag) +{ + uchar *p; + Tag *t; + int i, j; + Dentry *d; + Cache *h; + Bucket *b; + Superb *s; + Fbuf *f; + Off *l; + + /* swab the tag */ + p = (uchar*)c; + t = (Tag*)(p + BUFSIZE); + if(!flag) { + swab2(&t->pad); + swab2(&t->tag); + swaboff(&t->path); + } + + /* swab each block type */ + switch(t->tag) { + + default: + print("no swab for tag=%G rw=%d\n", t->tag, flag); + for(j=0; j<16; j++) + print(" %.2x", p[BUFSIZE+j]); + print("\n"); + for(i=0; i<16; i++) { + print("%.4x", i*16); + for(j=0; j<16; j++) + print(" %.2x", p[i*16+j]); + print("\n"); + } + panic("swab"); + break; + + case Tsuper: + s = (Superb*)p; + swaboff(&s->fbuf.nfree); + for(i=0; ifbuf.free[i]); +#ifdef AUTOSWAB + swaboff(&s->magic); +#endif + swaboff(&s->fstart); + swaboff(&s->fsize); + swaboff(&s->tfree); + swaboff(&s->qidgen); + swaboff(&s->cwraddr); + swaboff(&s->roraddr); + swaboff(&s->last); + swaboff(&s->next); + break; + + case Tdir: + for(i=0; iuid); + swab2(&d->gid); + swab2(&d->mode); + swab2(&d->muid); + swaboff(&d->qid.path); + swab4(&d->qid.version); + swaboff(&d->size); + for(j=0; jdblock[j]); + for (j = 0; j < NIBLOCK; j++) + swaboff(&d->iblocks[j]); + swab4(&d->atime); + swab4(&d->mtime); + } + break; + + case Tind1: + case Tind2: +#ifndef OLD + case Tind3: + case Tind4: + /* add more Tind tags here ... */ +#endif + l = (Off *)p; + for(i=0; infree); + for(i=0; ifree[i]); + break; + + case Tbuck: + for(i=0; iagegen); + for(j=0; jentry[j].age); + swab2(&b->entry[j].state); + swaboff(&b->entry[j].waddr); + } + } + break; + + case Tcache: + h = (Cache*)p; + swaboff(&h->maddr); + swaboff(&h->msize); + swaboff(&h->caddr); + swaboff(&h->csize); + swaboff(&h->fsize); + swaboff(&h->wsize); + swaboff(&h->wmax); + swaboff(&h->sbaddr); + swaboff(&h->cwraddr); + swaboff(&h->roraddr); + swab4(&h->toytime); + swab4(&h->time); + break; + + case Tnone: // unitialized + case Tfile: // someone elses problem + case Tvirgo: // bit map -- all bytes + case Tconfig: // configuration string -- all bytes + break; + } + + /* swab the tag */ + if(flag) { + swab2(&t->pad); + swab2(&t->tag); + swaboff(&t->path); + } +} diff -Nru /sys/src/fs/port/time.c /sys/src/fs/port/time.c --- /sys/src/fs/port/time.c Thu Jan 1 00:00:00 1970 +++ /sys/src/fs/port/time.c Tue Nov 1 00:00:00 2011 @@ -0,0 +1,408 @@ +#include "all.h" +#include "mem.h" + +Timet +toytime(void) +{ + return mktime + TK2SEC(MACHP(0)->ticks); +} + +#define SEC2MIN 60L +#define SEC2HOUR (60L*SEC2MIN) +#define SEC2DAY (24L*SEC2HOUR) + +/* + * days per month plus days/year + */ +int rtdmsize[] = +{ + 365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; +int rtldmsize[] = +{ + 366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +/* + * return the days/month for the given year + */ +int* +yrsize(int y) +{ + if((y%4) == 0 && ((y%100) != 0 || (y%400) == 0)) + return rtldmsize; + else + return rtdmsize; +} + +void +sec2rtc(Timet secs, Rtc *rtc) +{ + int d, *d2m; + Timet hms, day; + + /* + * break initial number into days + */ + hms = (uvlong)secs % SEC2DAY; + day = (uvlong)secs / SEC2DAY; + if(hms < 0) { + hms += SEC2DAY; + day -= 1; + } + + /* + * generate hours:minutes:seconds + */ + rtc->sec = hms % 60; + d = hms / 60; + rtc->min = d % 60; + d /= 60; + rtc->hour = d; + + /* + * year number + */ + if(day >= 0) + for(d = 1970; day >= *yrsize(d); d++) + day -= *yrsize(d); + else + for(d = 1970; day < 0; d--) + day += *yrsize(d-1); + rtc->year = d; + + /* + * generate month + */ + d2m = yrsize(rtc->year); + for(d = 1; day >= d2m[d]; d++) + day -= d2m[d]; + rtc->mday = day + 1; + rtc->mon = d; +} + +Timet +rtc2sec(Rtc *rtc) +{ + Timet secs; + int i, *d2m; + + secs = 0; + + /* + * seconds per year + */ + for(i = 1970; i < rtc->year; i++) { + d2m = yrsize(i); + secs += d2m[0] * SEC2DAY; + } + + /* + * seconds per month + */ + d2m = yrsize(rtc->year); + for(i = 1; i < rtc->mon; i++) + secs += d2m[i] * SEC2DAY; + + secs += (rtc->mday-1) * SEC2DAY; + secs += rtc->hour * SEC2HOUR; + secs += rtc->min * SEC2MIN; + secs += rtc->sec; + + return secs; +} + +Timet +time(void) +{ + Timet t, dt; + + t = toytime(); + while(tim.bias != 0) { /* adjust at rate 1 sec/min */ + dt = t - tim.lasttoy; + if(dt < MINUTE(1)) + break; + if(tim.bias >= 0) { + tim.bias -= SECOND(1); + tim.offset += SECOND(1); + } else { + tim.bias += SECOND(1); + tim.offset -= SECOND(1); + } + tim.lasttoy += MINUTE(1); + } + return t + tim.offset; +} + +void +settime(Timet nt) +{ + Timet dt; + + dt = nt - time(); + tim.lasttoy = toytime(); + if(dt > MAXBIAS || dt < -MAXBIAS) { /* too much, just set it */ + tim.bias = 0; + tim.offset = nt - tim.lasttoy; + } else + tim.bias = dt; +} + +void +prdate(void) +{ + Timet t; + + t = time(); + if(tim.bias >= 0) + print("%T + %ld\n", t, tim.bias); + else + print("%T - %ld\n", t, -tim.bias); +} + +static int dysize(int); +static void ct_numb(char*, int); +void localtime(Timet, Tm*); +void gmtime(Timet, Tm*); + +static char dmsize[12] = +{ + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +/* + * The following table is used for 1974 and 1975 and + * gives the day number of the first day after the Sunday of the + * change. + */ +static struct +{ + short yrfrom; + short yrto; + short daylb; + short dayle; +} daytab[] = +{ + 107, ~(ushort)0>>1, 66, 310, + 87, 106, 90, 303, + 76, 86, 119, 303, + 75, 75, 58, 303, + 74, 74, 5, 333, + -1, 73, 119, 303, +}; + +static +prevsunday(Tm *t, int d) +{ + if(d >= 58) + d += dysize(t->year) - 365; + return d - (d - t->yday + t->wday + 700) % 7; +} + +static +succsunday(Tm *t, int d) +{ + int dd; + + if(d >= 58) + d += dysize(t->year) - 365; + dd = (d - t->yday + t->wday + 700) % 7; + if(dd == 0) + return d; + else + return d + 7 - dd; +} + +void +localtime(Timet tim, Tm *ct) +{ + int daylbegin, daylend, dayno, i; + Timet copyt; + + copyt = tim - conf.minuteswest*60L; + gmtime(copyt, ct); + dayno = ct->yday; + + /* enforce sane bounds for daytab */ + if (ct->year < -1) /* 1 jan 1970 00:00 GMT can be 31 dec 1969 locally */ + ct->year = -1; + else if (ct->year > 60000) + ct->year = 60000; + + for(i = 0; ; i++) + if(ct->year >= daytab[i].yrfrom && + ct->year <= daytab[i].yrto) { + daylbegin = succsunday(ct, daytab[i].daylb); + daylend = prevsunday(ct, daytab[i].dayle); + break; + } + if(conf.dsttime && + (dayno>daylbegin || (dayno==daylbegin && ct->hour >= 2)) && + (daynohour < 1))) { + copyt += 60L*60L; + gmtime(copyt, ct); + ct->isdst++; + } +} + +void +gmtime(Timet tim, Tm *ct) +{ + int d0, d1; + Timet hms, day; + + /* + * break initial number into days + */ + hms = (uvlong)tim % 86400L; + day = (uvlong)tim / 86400L; + if(hms < 0) { + hms += 86400L; + day -= 1; + } + + /* + * generate hours:minutes:seconds + */ + ct->sec = hms % 60; + d1 = hms / 60; + ct->min = d1 % 60; + d1 /= 60; + ct->hour = d1; + + /* + * day is the day number. + * generate day of the week. + * The addend is 4 mod 7 (1/1/1970 was Thursday) + */ + + ct->wday = (day + 7340036L) % 7; + + /* + * year number + */ + if(day >= 0) + for(d1 = 1970; day >= dysize(d1); d1++) + day -= dysize(d1); + else + for(d1 = 1970; day < 0; d1--) + day += dysize(d1-1); + ct->year = d1-1900; + ct->yday = d0 = day; + + /* + * generate month + */ + if(dysize(d1) == 366) + dmsize[1] = 29; + for(d1 = 0; d0 >= dmsize[d1]; d1++) + d0 -= dmsize[d1]; + dmsize[1] = 28; + ct->mday = d0 + 1; + ct->mon = d1; + ct->isdst = 0; +} + +void +datestr(char *s, Timet t) +{ + Tm tm; + + localtime(t, &tm); + sprint(s, "%.4d%.2d%.2d", tm.year+1900, tm.mon+1, tm.mday); +} + +int +Tfmt(Fmt* fmt) +{ + char s[30]; + char *cp; + Timet t; + Tm tm; + + t = va_arg(fmt->args, Timet); + if(t == 0) + return fmtstrcpy(fmt, "The Epoch"); + + localtime(t, &tm); + strcpy(s, "Day Mon 00 00:00:00 1900"); + cp = &"SunMonTueWedThuFriSat"[tm.wday*3]; + s[0] = cp[0]; + s[1] = cp[1]; + s[2] = cp[2]; + cp = &"JanFebMarAprMayJunJulAugSepOctNovDec"[tm.mon*3]; + s[4] = cp[0]; + s[5] = cp[1]; + s[6] = cp[2]; + ct_numb(s+8, tm.mday); + ct_numb(s+11, tm.hour+100); + ct_numb(s+14, tm.min+100); + ct_numb(s+17, tm.sec+100); + if(tm.year >= 100) { + s[20] = '2'; + s[21] = '0'; + } + ct_numb(s+22, tm.year+100); + + return fmtstrcpy(fmt, s); +} + +static +dysize(int y) +{ + if((y%4) == 0) + return 366; + return 365; +} + +static +void +ct_numb(char *cp, int n) +{ + if(n >= 10) + cp[0] = (n/10)%10 + '0'; + else + cp[0] = ' '; + cp[1] = n%10 + '0'; +} + +/* + * compute the next time after t + * that has hour hr and is not on + * day in bitpattern -- + * for automatic dumps + */ +Timet +nextime(Timet t, int hr, int day) +{ + int nhr; + Tm tm; + + if(hr < 0 || hr >= 24) + hr = 5; + if((day&0x7f) == 0x7f) + day = 0; + for (;;) { + localtime(t, &tm); + t -= tm.sec; + t -= tm.min*60; + nhr = tm.hour; + do { + t += 60*60; + nhr++; + } while(nhr%24 != hr); + localtime(t, &tm); + if(tm.hour != hr) { + t += 60*60; + localtime(t, &tm); + if(tm.hour != hr) { + t -= 60*60; + localtime(t, &tm); + } + } + if(day & (1< 1) + file = argv[1]; + + if(strcmp(file, "default") == 0) { + setminusers(); + return; + } + + uidgc.uidbuf = getbuf(devnone, Cuidbuf, 0); + if(walkto(file) || con_open(FID2, 0)) { + print("cmd_users: cannot access %s\n", file); + putbuf(uidgc.uidbuf); + return; + } + + uidgc.flen = 0; + uidgc.find = 0; + cons.offset = 0; + cons.nuid = 0; + + u = 0; + line = 0; + for(;;) { + line++; + n = readln(buf, sizeof(buf)); + if(n == 0) + break; + + p = getword(buf, L':', "no : after number", line); + if(p == 0) + continue; + ulead = getword(p, L':', "no : after name", line); + if(ulead == 0) + continue; + + if(strlen(p) > NAMELEN-1) { + print("%s: name too long\n", p); + continue; + } + strcpy(uid[u].name, p); + uid[u].uid = number(buf, 0, 10); + uid[u].lead = 0; + uid[u].ngrp = 0; + u++; + if(u >= conf.nuid) { + print("conf.nuid too small (%ld)\n", conf.nuid); + break; + } + } + + /* Sorted by uid for use in uidtostr */ + wlock(&uidgc.uidlock); + qsort(uid, u, sizeof(uid[0]), byuid); + cons.nuid = u; + wunlock(&uidgc.uidlock); + + /* Parse group table */ + uidgc.flen = 0; + uidgc.find = 0; + cons.offset = 0; + cons.ngid = 0; + + g = 0; + line = 0; + for(;;) { + line++; + n = readln(buf, sizeof(buf)); + if(n == 0) + break; + + uname = getword(buf, L':', 0, 0); /* skip number */ + if(uname == 0) + continue; + + ulead = getword(uname, L':', 0, 0); /* skip name */ + if(ulead == 0) + continue; + + p = getword(ulead, L':', "no : after leader", line); + if(p == 0) + continue; + + ui = uidpstr(uname); + if(ui == 0) + continue; + + /* set to owner if name not known */ + ui->lead = 0; + if(ulead[0]) { + o = strtouid(ulead); + if(o >= 0) + ui->lead = o; + else + ui->lead = ui->uid; + } + ui->gtab = &gidspace[g]; + ui->ngrp = 0; + for(;;) { + if(p == 0) + break; + unext = getword(p, L',', 0, 0); + o = strtouid(p); + if(o >= 0) { + gidspace[g++] = o; + ui->ngrp++; + } + p = unext; + } + } + + cons.ngid = g; + + putbuf(uidgc.uidbuf); + print("%d uids read, %d groups used\n", cons.nuid, cons.ngid); +} + +void +cmd_newuser(int argc, char *argv[]) +{ + if(argc <= 1) { + print("usage: newuser args\n"); + print(" name -- create a new user\n"); + print(" name : -- create a new group\n"); + print(" name ? -- show entry for user\n"); + print(" name name -- rename\n"); + print(" name =[name] -- add/alter/remove leader\n"); + print(" name +name -- add member\n"); + print(" name -name -- delete member\n"); + return; + } + do_newuser(argc, argv); +} + +void +do_newuser(int argc, char *argv[]) +{ + int i, l, n, nuid; + char *p, *md, *q; + Rune *r; + Userid *s; + Uid *ui, *u2; + + nuid = 10000; + md = 0; + if(argc == 2) { + nuid = 1; + argv[2] = ":"; + } + + for(r = ichar; *r; r++) + if(utfrune(argv[1], *r)) { + print("illegal character in name\n"); + return; + } + if(strlen(argv[1]) > NAMELEN-1) { + print("name %s too long\n", argv[1]); + return; + } + + p = argv[2]; + switch(*p) { + case '?': + ui = chkuid(argv[1], 1); + if(ui == 0) + return; + pentry(buf, ui); + n = strlen(buf); + p = buf; + while(n > PRINTSIZE-5) { + q = p; + p += PRINTSIZE-5; + n -= PRINTSIZE-5; + i = *p; + *p = 0; + print("%s", q); + *p = i; + } + print("%s\n", p); + return; + + case ':': + if(chkuid(argv[1], 0)) + return; + while(uidtop(nuid) != 0) + nuid++; + if(cons.nuid >= conf.nuid) { + print("conf.nuid too small (%ld)\n", conf.nuid); + return; + } + + wlock(&uidgc.uidlock); + ui = &uid[cons.nuid++]; + ui->uid = nuid; + ui->lead = 0; + if(nuid < 10000) { + ui->lead = ui->uid; + md = argv[1]; + } + strcpy(ui->name, argv[1]); + ui->ngrp = 0; + qsort(uid, cons.nuid, sizeof(uid[0]), byuid); + wunlock(&uidgc.uidlock); + break; + + case '=': + ui = chkuid(argv[1], 1); + if(ui == 0) + return; + p++; + if(*p == '\0') { + ui->lead = 0; + break; + } + u2 = chkuid(p, 1); + if(u2 == 0) + return; + ui->lead = u2->uid; + break; + + case '+': + ui = chkuid(argv[1], 1); + if(ui == 0) + return; + p++; + u2 = chkuid(p, 1); + if(u2 == 0) + return; + if(u2->uid == ui->uid) + return; + if(cons.ngid+ui->ngrp+1 >= conf.gidspace) { + print("conf.gidspace too small (%ld)\n", conf.gidspace); + return; + } + for(i = 0; i < ui->ngrp; i++) { + if(ui->gtab[i] == u2->uid) { + print("member already in group\n"); + return; + } + } + + wlock(&uidgc.uidlock); + s = gidspace+cons.ngid; + memmove(s, ui->gtab, ui->ngrp*sizeof(*s)); + ui->gtab = s; + s[ui->ngrp++] = u2->uid; + cons.ngid += ui->ngrp+1; + wunlock(&uidgc.uidlock); + break; + + case '-': + ui = chkuid(argv[1], 1); + if(ui == 0) + return; + p++; + u2 = chkuid(p, 1); + if(u2 == 0) + return; + for(i = 0; i < ui->ngrp; i++) + if(ui->gtab[i] == u2->uid) + break; + + if(i == ui->ngrp) { + print("%s not in group\n", p); + return; + } + + wlock(&uidgc.uidlock); + s = ui->gtab+i; + ui->ngrp--; + memmove(s, s+1, (ui->ngrp-i)*sizeof(*s)); + wunlock(&uidgc.uidlock); + break; + + default: + if(chkuid(argv[2], 0)) + return; + + for(r = ichar; *r; r++) + if(utfrune(argv[2], *r)) { + print("illegal character in name\n"); + return; + } + + ui = chkuid(argv[1], 1); + if(ui == 0) + return; + + if(strlen(argv[2]) > NAMELEN-1) { + print("name %s too long\n", argv[2]); + return; + } + + wlock(&uidgc.uidlock); + strcpy(ui->name, argv[2]); + wunlock(&uidgc.uidlock); + break; + } + + + if(walkto("/adm/users") || con_open(FID2, OWRITE|OTRUNC)) { + print("can't open /adm/users for write\n"); + return; + } + + cons.offset = 0; + for(i = 0; i < cons.nuid; i++) { + pentry(buf, &uid[i]); + l = strlen(buf); + n = con_write(FID2, buf, cons.offset, l); + if(l != n) + print("short write on /adm/users\n"); + cons.offset += n; + } + + if(md != 0) { + sprint(buf, "create /usr/%s %s %s 755 d", md, md, md); + print("%s\n", buf); + cmd_exec(buf); + } +} + +Uid* +chkuid(char *name, int chk) +{ + Uid *u; + + u = uidpstr(name); + if(chk == 1) { + if(u == 0) + print("%s does not exist\n", name); + } + else { + if(u != 0) + print("%s already exists\n", name); + } + return u; +} + +void +pentry(char *buf, Uid *u) +{ + int i, posn; + Uid *p; + + posn = sprint(buf, "%d:%s:", u->uid, u->name); + p = uidtop(u->lead); + if(p && u->lead != 0) + posn += sprint(buf+posn, "%s", p->name); + + posn += sprint(buf+posn, ":"); + for(i = 0; i < u->ngrp; i++) { + p = uidtop(u->gtab[i]); + if(i != 0) + posn += sprint(buf+posn, ","); + if(p != 0) + posn += sprint(buf+posn, "%s", p->name); + else + posn += sprint(buf+posn, "%d", u->gtab[i]); + } + sprint(buf+posn, "\n"); +} + +void +setminusers(void) +{ + int u; + + for(u = 0; minusers[u].name; u++) { + strcpy(uid[u].name, minusers[u].name); + uid[u].uid = minusers[u].uid; + uid[u].lead = minusers[u].lead; + } + cons.nuid = u; + qsort(uid, u, sizeof(uid[0]), byuid); +} + +Uid* +uidpstr(char *name) +{ + Uid *s, *e; + + s = uid; + for(e = s+cons.nuid; s < e; s++) { + if(strcmp(name, s->name) == 0) + return s; + } + return 0; +} + +char* +getword(char *buf, Rune delim, char *error, int line) +{ + char *p; + + p = utfrune(buf, delim); + if(p == 0) { + if(error) + print("cmd_users: %s line %d\n", error, line); + return 0; + } + *p = '\0'; + return p+1; +} + +int +strtouid(char *name) +{ + Uid *u; + int id; + + rlock(&uidgc.uidlock); + + u = uidpstr(name); + id = -2; + if(u != 0) + id = u->uid; + + runlock(&uidgc.uidlock); + + return id; +} + +Uid* +uidtop(int id) +{ + Uid *bot, *top, *new; + + bot = uid; + top = bot + cons.nuid-1; + + while(bot <= top){ + new = bot + (top - bot)/2; + if(new->uid == id) + return new; + if(new->uid < id) + bot = new + 1; + else + top = new - 1; + } + return 0; +} + +void +uidtostr(char *name, int id, int dolock) +{ + Uid *p; + + if(dolock) + rlock(&uidgc.uidlock); + + p = uidtop(id); + if(p == 0) + strcpy(name, "none"); + else + strcpy(name, p->name); + + if(dolock) + runlock(&uidgc.uidlock); +} + +int +ingroup(int u, int g) +{ + Uid *p; + Userid *s, *e; + + if(u == g) + return 1; + + rlock(&uidgc.uidlock); + p = uidtop(g); + if(p != 0) { + s = p->gtab; + for(e = s + p->ngrp; s < e; s++) { + if(*s == u) { + runlock(&uidgc.uidlock); + return 1; + } + } + } + runlock(&uidgc.uidlock); + return 0; +} + +int +leadgroup(int ui, int gi) +{ + int i; + Uid *u; + + /* user 'none' cannot be a group leader */ + if(ui == 0) + return 0; + + rlock(&uidgc.uidlock); + u = uidtop(gi); + if(u == 0) { + runlock(&uidgc.uidlock); + return 0; + } + i = u->lead; + runlock(&uidgc.uidlock); + if(i == ui) + return 1; + if(i == 0) + return ingroup(ui, gi); + + return 0; +} + +int +byuid(void *a1, void *a2) +{ + Uid *u1, *u2; + + u1 = a1; + u2 = a2; + return u1->uid - u2->uid; +} + +int +readln(char *p, int len) +{ + int n, c; + + n = 0; + while(len--) { + c = fchar(); + if(c == -1 || c == '\n') + break; + + n++; + *p++ = c; + } + *p = '\0'; + return n; +}