--- /dev/null +++ /sys/src/9/loongson64/c_fcr0.s @@ -0,0 +1,3 @@ +TEXT C_fcr0(SB), $-8 + MOVW $0x500, R1 /* claim to be an r4k, thus have ll/sc */ + RET --- /dev/null +++ /sys/src/9/loongson64/clock.c @@ -0,0 +1 @@ +#include "../loongson/clock.c" --- /dev/null +++ /sys/src/9/loongson64/dat.h @@ -0,0 +1,259 @@ +typedef struct Conf Conf; +typedef struct Confmem Confmem; +typedef struct FPsave FPsave; +typedef struct ISAConf ISAConf; +typedef struct KMap KMap; +typedef struct Lance Lance; +typedef struct Lancemem Lancemem; +typedef struct Label Label; +typedef struct Lock Lock; +typedef struct Mach Mach; +typedef struct MMU MMU; +typedef struct Notsave Notsave; +typedef struct Pcidev Pcidev; +typedef struct PMMU PMMU; +typedef struct Softtlb Softtlb; +typedef struct Ureg Ureg; +typedef struct Proc Proc; +typedef uvlong Tval; + +#pragma incomplete Pcidev + +#define MAXSYSARG 5 /* for mount(fd, afd, mpt, flag, arg) */ + +/* + * parameters for sysproc.c and rebootcmd.c + */ +#define AOUT_MAGIC N_MAGIC || magic==P_MAGIC +/* r3k or r4k boot images */ +#define BOOT_MAGIC (0x160<<16) || magic == ((0x160<<16)|3) + +/* + * machine dependent definitions used by ../port/dat.h + */ + +struct Lock +{ + ulong key; /* semaphore (non-zero = locked) */ + ulong sr; + uintptr pc; + Proc *p; + Mach *m; + ushort isilock; +}; + +struct Label +{ + uintptr sp; + uintptr pc; +}; + +struct Confmem +{ + uintptr base; + ulong npage; + uintptr kbase; + uintptr klimit; +}; + +struct Conf +{ + ulong nmach; /* processors */ + ulong nproc; /* processes */ + Confmem mem[2]; /* physical memory */ + ulong npage; /* total physical pages of memory */ + ulong upages; /* user page pool */ + ulong nimage; /* number of page cache image headers */ + ulong nswap; /* number of swap pages */ + int nswppo; /* max # of pageouts per segment pass */ + ulong copymode; /* 0 is copy on write, 1 is copy on reference */ + ulong ialloc; /* bytes available for interrupt-time allocation */ + ulong pipeqsize; /* size in bytes of pipe queues */ + int nuart; /* number of uart devices */ + int monitor; /* has monitor? */ +}; + +/* + * floating point registers + */ +enum +{ + /* floating point state */ + FPinit, + FPactive, + FPinactive, + FPemu, + + /* bit meaning floating point illegal */ + FPillegal= 0x100, +}; + +enum { + Nfpregs = 32, /* floats; half as many doubles */ +}; + +/* + * floating point + * fpstate is separate, kept in Proc + */ +struct FPsave +{ + /* /dev/proc expects the registers to be first in FPsave */ + ulong reg[Nfpregs]; /* the canonical bits */ + union { + ulong fpstatus; /* both are fcr31 */ + ulong fpcontrol; + }; + + int fpdelayexec; /* executing delay slot of branch */ + uintptr fpdelaypc; /* pc to resume at after */ + ulong fpdelaysts; /* save across user-mode delay-slot execution */ + + /* stuck-fault detection */ + uintptr fppc; /* addr of last fault */ + int fpcnt; /* how many consecutive at that addr */ +}; + +/* + * mmu goo in the Proc structure + */ +struct PMMU +{ + int pidonmach[MAXMACH]; +}; + +/* + * things saved in the Proc structure during a notify + */ +struct Notsave +{ + ulong nonempty; +}; + +#include "../port/portdat.h" + +/* First FIVE members' offsets known by l.s */ +struct Mach +{ + /* the following are all known by l.s and cannot be moved */ + int machno; /* physical id of processor FIRST @ 0 */ + Softtlb*stb; /* Software tlb simulation SECOND @ 8 */ + Proc* proc; /* process on this processor THIRD @ 16 */ + uintptr splpc; /* pc that called splhi() FOURTH @ 24 */ + ulong tlbfault; /* # of tlb faults FIFTH @ 32 */ + ulong ktlbfault; /* @ 36 */ + ulong utlbfault; /* @ 40 */ + + /* the following is safe to move */ + ulong tlbpurge; + ulong ticks; /* of the clock since boot time */ + Label sched; /* scheduler wakeup */ + void* alarm; /* alarms bound to this clock */ + int lastpid; /* last pid allocated on this machine */ + Proc* pidproc[NTLBPID]; /* proc that owns tlbpid on this mach */ + KMap* kactive; /* active on this machine */ + int knext; + uchar ktlbx[NTLB]; /* tlb index used for kmap */ + uchar ktlbnext; + int speed; /* cpu speed */ + ulong delayloop; /* for the delay() routine */ + ulong fairness; /* for runproc */ + int flushmmu; + int inclockintr; + int ilockdepth; + Perf perf; /* performance counters */ + uvlong cyclefreq; /* Frequency of user readable cycle counter */ + + /* for per-processor timers */ + ulong lastcount; + uvlong fastticks; + ulong hz; + ulong maxperiod; + ulong minperiod; + + Proc* readied; /* for runproc */ + ulong schedticks; /* next forced context switch */ + + int pfault; + int cs; + int syscall; + int load; + int intr; + int hashcoll; /* soft-tlb hash collisions */ + int paststartup; /* for putktlb */ + + uintptr stack[1]; +}; + +struct KMap +{ + Ref; + u64int virt; + u64int phys0; + u64int phys1; + KMap* next; + KMap* konmach[MAXMACH]; + Page* pg; + uintptr pc; /* of caller to kmap() */ +}; + +#define VA(k) ((k)->virt) +#define PPN(x) ((uintptr)(x)>>6) /* PPN in TLBPHYS0-1 */ + +/* offsets known by l.s */ +struct Softtlb +{ + u64int virt; + u64int phys0; + u64int phys1; +}; + +struct +{ + Lock; + long machs; /* bitmap of processors */ + short exiting; + int ispanic; +} active; + +extern KMap kpte[]; +extern register Mach *m; +extern register Proc *up; + +extern FPsave initfp; +extern ulong memsize; + +extern int normalprint; + +/* + * a parsed plan9.ini line + */ +#define NISAOPT 8 + +struct ISAConf { + char *type; + ulong port; + int irq; + ulong dma; + ulong mem; + ulong size; + ulong freq; + + int nopt; + char *opt[NISAOPT]; +}; + +typedef struct { + ulong port; + int size; +} Devport; + +struct DevConf +{ + ulong intnum; /* interrupt number */ + char *type; /* card type, malloced */ + int nports; /* Number of ports */ + Devport *ports; /* The ports themselves */ +}; + +typedef vlong regint; /* register sized int, ensure single instr'n */ --- /dev/null +++ /sys/src/9/loongson64/devether.c @@ -0,0 +1 @@ +#include "../loongson/devether.c" --- /dev/null +++ /sys/src/9/loongson64/devrtc.c @@ -0,0 +1 @@ +#include "../loongson/devrtc.c" --- /dev/null +++ /sys/src/9/loongson64/ether8139.c @@ -0,0 +1 @@ +#include "../loongson/ether8139.c" --- /dev/null +++ /sys/src/9/loongson64/etherif.h @@ -0,0 +1 @@ +#include "../loongson/etherif.h" --- /dev/null +++ /sys/src/9/loongson64/faultmips.c @@ -0,0 +1,289 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "ureg.h" +#include "../port/error.h" +#include "io.h" + +enum { + Debug = 0, +}; + +typedef struct Fault Fault; +struct Fault { + uintptr va; + ulong pid; + uintptr pc; + int cnt; + char *prog; + int code; +}; + +extern char *excname[]; + +static Fault lflt, maxflt; + +#define OFR(r) (uintptr)offsetof(Ureg, r) /* offset into Ureg of r */ +#define REG(ur, r) *acpureg(ur, r) /* cpu reg in Ureg */ +#define REGMASK MASK(5) + +static ulong dummyr0; + +static int roff[32] = { + 0, OFR(r1), OFR(r2), OFR(r3), + OFR(r4), OFR(r5), OFR(r6), OFR(r7), + OFR(r8), OFR(r9), OFR(r10), OFR(r11), + OFR(r12), OFR(r13), OFR(r14), OFR(r15), + OFR(r16), OFR(r17), OFR(r18), OFR(r19), + OFR(r20), OFR(r21), OFR(r22), OFR(r23), + OFR(r24), OFR(r25), OFR(r26), OFR(r27), + OFR(r28), OFR(sp), OFR(r30), OFR(r31), +}; + +static ulong * +acpureg(Ureg *ur, int r) +{ + r &= REGMASK; + if (r == 0 || roff[r] == 0) { + dummyr0 = 0; + return &dummyr0; + } + return (ulong *)((char*)ur + roff[r]); +} + +ulong * +reg(Ureg *ur, int r) +{ + return ®(ur, r); +} + +/* + * Ask if the instruction at EPC could have cause this badvaddr + */ +int +tstbadvaddr(Ureg *ur) +{ + int rn; + uintptr iw, off, ea; + + iw = ur->pc; + if(ur->cause & BD) + iw += 4; + + if(seg(up, iw, 0) == 0) + return 0; + + iw = *(ulong*)iw; + +/* print("iw: %#lux\n", iw); /**/ + + switch((iw>>26) & 0x3f) { + default: + return 1; + case 0x20: /* LB */ + case 0x24: /* LBU */ + /* LD */ + case 0x35: + case 0x36: + case 0x37: /* LDCz */ + case 0x1A: /* LDL */ + case 0x1B: /* LDR */ + case 0x21: /* LH */ + case 0x25: /* LHU */ + case 0x30: /* LL */ + case 0x34: /* LLD */ + case 0x23: /* LW */ + case 0x31: + case 0x32: /* LWCz possible 0x33 */ + case 0x27: /* LWU */ + case 0x22: /* LWL */ + case 0x26: /* LWR */ + break; + + case 0x28: /* SB */ + case 0x38: /* SC */ + case 0x3C: /* SCD */ + case 0x3D: + case 0x3E: + case 0x3F: /* SDCz */ + case 0x2C: /* SDL */ + case 0x2D: /* SDR */ + case 0x29: /* SH */ + case 0x2B: /* SW */ + case 0x39: + case 0x3A: /* SWCz */ + case 0x2A: /* SWL */ + case 0x2E: /* SWR */ + break; + } + + off = iw & 0xffff; + if(off & 0x8000) + off |= ~0xffff; + + rn = (iw>>21) & 0x1f; + ea = *reg(ur, rn); + if(rn == 0) + ea = 0; + ea += off; + + /* print("ea %#lux %#lux(R%d) bv %#lux pc %#lux\n", ea, off, rn, ur->badvaddr, ur->pc); /**/ + + if(ur->badvaddr == ea) + return 0; + + return 1; +} + +/* + * we think we get consecutive page faults from unlucky combinations of + * scheduling and stlb hashes, and they only happen with 16K pages. + * however, we also get page faults while servicing the exact same fault. + * more than 5 consecutive faults is unusual, now that we have a better + * hash function. + * + * this can be helpful during mmu and cache debugging. + */ +static int +ckfaultstuck(Ureg *ur, int read, int code) +{ + uintptr pc, va; + + va = ur->badvaddr; + pc = ur->pc; + if (va != lflt.va || up->pid != lflt.pid || pc != lflt.pc || + code != lflt.code) { + /* at least one address or cause is different from last time */ + lflt.cnt = 1; + lflt.va = va; + lflt.pid = up->pid; + lflt.pc = pc; + lflt.code = code; + return 0; + } + ++lflt.cnt; + if (lflt.cnt >= 1000) /* fixfault() isn't fixing underlying cause? */ + panic("fault: %d consecutive faults for va %#p", lflt.cnt, va); + if (lflt.cnt > maxflt.cnt) { + maxflt.cnt = lflt.cnt; + maxflt.va = va; + maxflt.pid = up->pid; + maxflt.pc = pc; + kstrdup(&maxflt.prog, up->text); + } + + /* we're servicing that fault now! */ + /* adjust the threshold and program name to suit */ + if (lflt.cnt < 5 || strncmp(up->text, "8l", 2) != 0) + return 0; + iprint("%d consecutive faults for va %#p at pc %#p in %s " + "pid %ld\n", lflt.cnt, lflt.va, pc, up->text, lflt.pid); + iprint("\t%s: %s%s r31 %#p tlbvirt %#p\n", + excname[code], va == pc? "[instruction] ": "", + (read? "read": "write"), ur->r31, tlbvirt()); + return 0; +} + +char * +faultsprint(char *p, char *ep) +{ + if (Debug) + p = seprint(p, ep, + "max consecutive faults %d for va %#p in %s\n", + maxflt.cnt, maxflt.va, maxflt.prog); + return p; +} + +/* + * find out fault address and type of access. + * Call common fault handler. + */ +void +faultmips(Ureg *ur, int user, int code) +{ + int read; + uintptr addr; + char *p, buf[ERRMAX]; + static int infault, printed; + + if (0 && infault && !printed) { + printed = 1; + print("fault: recursive fault (%d deep) pc %#p va %#p\n", + infault+1, ur->pc, ur->badvaddr); + } + infault++; + if(waserror()){ + infault--; + nexterror(); + } + + addr = ur->badvaddr; + addr &= ~(BY2PG-1); + + read = !(code==CTLBM || code==CTLBS); + +/* print("fault: %s code %d va %#p pc %#p r31 %#p tlbvirt %#p\n", + up->text, code, ur->badvaddr, ur->pc, ur->r31, tlbvirt());/**/ + + if (Debug && ckfaultstuck(ur, read, code) || fault(addr, read) == 0){ + infault--; + poperror(); + return; + } + + infault--; + poperror(); + + if(tstbadvaddr(ur)) { + print("fault: spurious badvaddr %#p in %s at pc %#p\n", + ur->badvaddr, up->text, ur->pc);/**/ + return; + } + + if(user) { + p = "store"; + if(read) + p = "load"; + snprint(buf, sizeof buf, "sys: trap: fault %s addr=%#p r31=%#p", + p, ur->badvaddr, ur->r31); + postnote(up, 1, buf, NDebug); + return; + } + + print("kernel %s vaddr=%#p\n", excname[code], ur->badvaddr); + print("st=%#lux pc=%#p r31=%#p sp=%#p\n", + (ulong)ur->status, ur->pc, ur->r31, ur->sp); + dumpregs(ur); + panic("fault"); +} + +/* + * called in syscallfmt.c, sysfile.c, sysproc.c + */ +void +validalign(uintptr addr, unsigned align) +{ + /* + * Plan 9 is a 32-bit O/S, and the hardware it runs on + * does not usually have instructions which move 64-bit + * quantities directly, synthesizing the operations + * with 32-bit move instructions. Therefore, the compiler + * (and hardware) usually only enforce 32-bit alignment, + * if at all. + * + * Take this out if the architecture warrants it. + */ +// if(align == sizeof(vlong)) +// align = sizeof(long); + + /* + * Check align is a power of 2, then addr alignment. + */ + if((align != 0 && !(align & (align-1))) && !(addr & (align-1))) + return; + postnote(up, 1, "sys: odd address", NDebug); + error(Ebadarg); + /*NOTREACHED*/ +} --- /dev/null +++ /sys/src/9/loongson64/fns.h @@ -0,0 +1,160 @@ +#include "../port/portfns.h" + +void arginit(void); +int busprobe(ulong); +uintptr cankaddr(uintptr); +void cleancache(void); +void clearmmucache(void); +void clock(Ureg*); +void clockinit(void); +void clockshutdown(void); +int cmpswap(long*, long, long); +void coherence(void); +void cycles(uvlong *); +void dcflush(void*, ulong); +void dcinvalid(void*, ulong); +void dumptlb(void); +void faultmips(Ureg*, int, int); +void* fbinit(void); +ulong fcr31(void); +void firmware(int); +void fpclear(void); +void fpsave(FPsave *); +void fptrap(Ureg*); +void fpwatch(Ureg *); +ulong getcause(void); +char* getconf(char*); +ulong getconfig(void); +ulong getpagemask(void); +ulong getstatus(void); +int gettlbp(u64int, u64int*); +u64int gettlbvirt(int); +void gettlbx(int, Softtlb*); +void gotopc(ulong); +void hinv(void); +int i8042auxcmd(int); +int i8042auxcmds(uchar*, int); +void i8042auxenable(void (*)(int, int)); +void i8042reset(void); +int i8250console(void); +int i8259disable(int); +int i8259enable(int); +void i8259init(void); +int i8259intack(void); +int i8259isr(int); +void icflush(void *, ulong); +void idle(void); +#define idlehands() /* no wait instruction, do nothing */ +#define inb(r) (*(uchar*)(IOBASE+(r))) +void insb(int, void*, int); +#define ins(r) (*(ushort*)(IOBASE+(r))) +void inss(int, void*, int); +#define inl(r) (*(ulong*)(IOBASE+(r))) +void insl(int, void*, int); +void intrenable(int, void(*)(Ureg*, void *), void *, int); +void introff(int); +void intron(int); +void intrshutdown(void); +void ioinit(void); +int isaconfig(char*, int, ISAConf*); +void kbdinit(void); +void kfault(Ureg*); +KMap* kmap(Page*); +void kmapinit(void); +void kmapinval(void); +void kunmap(KMap*); +void launchinit(void); +void launch(int); +void links(void); +ulong machstatus(void); +void newstart(void); +int newtlbpid(Proc*); +void online(void); +#define outb(r, v) (*(uchar*)(IOBASE+(r)) = v) +void outsb(int, void*, int); +#define outs(r, v) (*(ushort*)(IOBASE+(r)) = v) +void outss(int, void*, int); +#define outl(r, v) (*(ulong*)(IOBASE+(r)) = v) +void outsl(int, void*, int); +ulong pcibarsize(Pcidev*, int); +void pcibussize(Pcidev*, ulong*, ulong*); +int pcicfgr8(Pcidev*, int); +int pcicfgr16(Pcidev*, int); +int pcicfgr32(Pcidev*, int); +void pcicfgw8(Pcidev*, int, int); +void pcicfgw16(Pcidev*, int, int); +void pcicfgw32(Pcidev*, int, int); +void pciclrbme(Pcidev*); +void pciclrioe(Pcidev*); +void pciclrmwi(Pcidev*); +int pcigetpms(Pcidev*); +void pcihinv(Pcidev*); +uchar pciipin(Pcidev*, uchar); +Pcidev* pcimatch(Pcidev*, int, int); +Pcidev* pcimatchtbdf(int); +void pcireset(void); +int pciscan(int, Pcidev**); +void pcisetbme(Pcidev*); +void pcisetioe(Pcidev*); +void pcisetmwi(Pcidev*); +int pcisetpms(Pcidev*, int); +int pcisubirq(int); +ulong prid(void); +void procrestore(Proc *); +void procsave(Proc *); +#define procsetup(p) ((p)->fpstate = FPinit) +void purgetlb(int); +Softtlb* putstlb(u64int, u64int); +int puttlb(u64int, u64int, u64int); +void puttlbx(int, u64int, u64int, u64int, int); +ulong rdcompare(void); +ulong rdcount(void); +ulong* reg(Ureg*, int); +void restfpregs(FPsave*, ulong); +void screeninit(void); +void setleveldest(int, int, uvlong*); +void setpagemask(ulong); +void setsp(ulong); +void setstatus(ulong); +void setwatchhi0(ulong); +void setwatchlo0(ulong); +void setwired(ulong); +ulong stlbhash(ulong); +void swcursorinit(void); +void syncclock(void); +uintptr syscall(Ureg*); +void syscallfmt(int syscallno, uintptr pc, va_list list); +void sysretfmt(int syscallno, va_list list, uintptr ret, uvlong start, uvlong stop); +int tas(ulong*); +void tlbinit(void); +u64int tlbvirt(void); +void touser(uintptr); +void unleash(void); +#define userureg(ur) ((ur)->status & KUSER) +void validalign(uintptr, unsigned); +void vecinit(void); +void vector0(void); +void vector100(void); +void vector180(void); +void wrcompare(ulong); +void wrcount(ulong); +ulong wiredpte(vlong); +void machwire(void); +void _uartputs(char*, int); +int _uartprint(char*, ...); + +#define PTR2UINT(p) ((uintptr)(p)) +#define UINT2PTR(i) ((void*)(i)) + +#define waserror() (up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1])) + +#define KADDR(a) ((void*)((uintptr)(a)|KSEG0)) +#define PADDR(a) ((uintptr)(a)&~KSEGM) + +#define KSEG0ADDR(a) ((void*)(((ulong)(a)&~KSEGM)|KSEG0)) +#define KSEG1ADDR(a) ((void*)((ulong)(a)|KSEG1)) + +#define sdmalloc(n) mallocalign(n, CACHELINESZ, 0, 0) +#define sdfree(p) free(p) + +int pmonprint(char*, ...); --- /dev/null +++ /sys/src/9/loongson64/i8259.c @@ -0,0 +1 @@ +#include "../loongson/i8259.c" --- /dev/null +++ /sys/src/9/loongson64/init9.s @@ -0,0 +1,11 @@ +#include "spim64.s" + +TEXT _main(SB), $16 + MOVV $setR30(SB), R30 + + MOVV $boot(SB), R1 + ADDVU $24, R29, R2 /* get a pointer to 0(FP) */ + MOVV R1, 8(R29) + MOVV R2, 16(R29) + + JMP startboot(SB) --- /dev/null +++ /sys/src/9/loongson64/initcode.c @@ -0,0 +1,43 @@ +/* + * IMPORTANT! DO NOT ADD LIBRARY CALLS TO THIS FILE. + * The entire text image must fit on one page + * (and there's no data segment, so any read/write data must be on the stack). + */ + +#include +#include + +char cons[] = "#c/cons"; +char boot[] = "/boot/boot"; +char dev[] = "/dev"; +char c[] = "#c"; +char e[] = "#e"; +char ec[] = "#ec"; +char s[] = "#s"; +char srv[] = "/srv"; +char env[] = "/env"; + +void +startboot(char *argv0, char **argv) +{ + char buf[200]; /* keep this fairly large to capture error details */ + + /* in case boot is a shell script */ + open(cons, OREAD); + open(cons, OWRITE); + open(cons, OWRITE); + bind(c, dev, MAFTER); + bind(ec, env, MAFTER); + bind(e, env, MCREATE|MAFTER); + bind(s, srv, MREPL|MCREATE); + +// write(1, cons, 8); +// for(;;); + + USED(argv0); + exec(boot, argv); + + rerrstr(buf, sizeof buf); + buf[sizeof buf - 1] = '\0'; + _exits(buf); +} --- /dev/null +++ /sys/src/9/loongson64/io.h @@ -0,0 +1,342 @@ +enum { + Mhz = 1000*1000, +}; + +/* + * duarts, frequency and registers + */ +#define DUARTFREQ 1843200 + +/* + * interrupt levels on CPU boards. + */ +enum +{ + ILmin = 2, +/* + ILpci = 2, + ILunused3 = 3, + ILduart0 = 4, + IL8259 = 5, + ILperf = 6, +*/ + IL8259 = 2, + ILduart0 = 3, + ILunused4 = 4, + ILunused5 = 5, + ILpci = 6, + + ILclock = 7, + ILmax = 7, + + ILshift = 8, +}; + +/* + * PCI configuration regesters and interrupt bits + */ +#define Reset (ulong*)(PCICFG+0x104) +#define Rstcpucold (1<<2) /* cpu cold reset */ + +#define Pcimapcfg (ulong*)(PCICFG+0x118) +#define Pciintrsts (ulong*)(PCICFG+0x13c) +#define Pciintren (ulong*)(PCICFG+0x138) +#define Pciintrenset (ulong*)(PCICFG+0x130) +#define Pciintrenclr (ulong*)(PCICFG+0x134) +#define Pciintrpol (ulong*)(PCICFG+0x12c) +#define Pciintredge (ulong*)(PCICFG+0x124) + +#define Pciintrbase 25 + +/* + * i8259 interrupts + */ +enum { + IrqCLOCK = 0, + IrqKBD = 1, + IrqUART1 = 3, + IrqUART0 = 4, + IrqPCMCIA = 5, + IrqFLOPPY = 6, + IrqLPT = 7, + IrqIRQ7 = 7, + IrqAUX = 12, /* PS/2 port */ + IrqIRQ13 = 13, /* coprocessor on 386 */ + IrqATA0 = 14, + IrqATA1 = 15, + MaxIrqPIC = 15, + + VectorPIC = 0, + MaxVectorPIC = VectorPIC+MaxIrqPIC, +}; + + +/* + * mostly PCI from here on + */ + +typedef struct Pcisiz Pcisiz; +typedef struct Pcidev Pcidev; +typedef struct Vctl Vctl; + +struct Vctl { + Vctl* next; /* handlers on this vector */ + + char name[KNAMELEN]; /* of driver */ + int isintr; /* interrupt or fault/trap */ + int irq; + int tbdf; + int (*isr)(int); /* get isr bit for this irq */ + int (*eoi)(int); /* eoi */ + + void (*f)(Ureg*, void*); /* handler to call */ + void* a; /* argument to call it with */ +}; + +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) + +enum { + MaxEISA = 16, + CfgEISA = 0xC80, +}; + +/* + * PCI support code. + */ +enum { /* type 0 & 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 */ +}; + +/* ccrb (base class code) values; controller types */ +enum { + Pcibcpci1 = 0, /* pci 1.0; no class codes defined */ + Pcibcstore = 1, /* mass storage */ + Pcibcnet = 2, /* network */ + Pcibcdisp = 3, /* display */ + Pcibcmmedia = 4, /* multimedia */ + Pcibcmem = 5, /* memory */ + Pcibcbridge = 6, /* bridge */ + Pcibccomm = 7, /* simple comms (e.g., serial) */ + Pcibcbasesys = 8, /* base system */ + Pcibcinput = 9, /* input */ + Pcibcdock = 0xa, /* docking stations */ + Pcibcproc = 0xb, /* processors */ + Pcibcserial = 0xc, /* serial bus (e.g., USB) */ + Pcibcwireless = 0xd, /* wireless */ + Pcibcintell = 0xe, /* intelligent i/o */ + Pcibcsatcom = 0xf, /* satellite comms */ + Pcibccrypto = 0x10, /* encryption/decryption */ + Pcibcdacq = 0x11, /* data acquisition & signal proc. */ +}; + +/* ccru (sub-class code) values; common cases only */ +enum { + /* mass storage */ + Pciscscsi = 0, /* SCSI */ + Pciscide = 1, /* IDE (ATA) */ + Pciscsata = 6, /* SATA */ + + /* network */ + Pciscether = 0, /* Ethernet */ + + /* display */ + Pciscvga = 0, /* VGA */ + Pciscxga = 1, /* XGA */ + Pcisc3d = 2, /* 3D */ + + /* bridges */ + Pcischostpci = 0, /* host/pci */ + Pciscpcicpci = 1, /* pci/pci */ + + /* simple comms */ + Pciscserial = 0, /* 16450, etc. */ + Pciscmultiser = 1, /* multiport serial */ + + /* serial bus */ + Pciscusb = 3, /* USB */ +}; + +enum { /* type 0 pre-defined header */ + 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 */ +}; + +enum { /* type 2 pre-defined header */ + PciCBExCA = 0x10, + PciCBSPSR = 0x16, + PciCBPBN = 0x18, /* primary bus number */ + PciCBSBN = 0x19, /* secondary bus number */ + PciCBUBN = 0x1A, /* subordinate bus number */ + PciCBSLTR = 0x1B, /* secondary latency timer */ + PciCBMBR0 = 0x1C, + PciCBMLR0 = 0x20, + PciCBMBR1 = 0x24, + PciCBMLR1 = 0x28, + PciCBIBR0 = 0x2C, /* I/O base */ + PciCBILR0 = 0x30, /* I/O limit */ + PciCBIBR1 = 0x34, /* I/O base */ + PciCBILR1 = 0x38, /* I/O limit */ + PciCBSVID = 0x40, /* subsystem vendor ID */ + PciCBSID = 0x42, /* subsystem ID */ + PciCBLMBAR = 0x44, /* legacy mode base address */ +}; + +struct Pcisiz +{ + Pcidev* dev; + int siz; + int bar; +}; + +struct Pcidev +{ + int tbdf; /* type+bus+device+function */ + ushort vid; /* vendor ID */ + ushort did; /* device ID */ + + ushort pcr; + + uchar rid; + uchar ccrp; + uchar ccru; + uchar ccrb; + uchar cls; + uchar ltr; + + struct { + ulong bar; /* base address */ + int size; + } mem[6]; + + struct { + ulong bar; + int size; + } rom; + uchar intl; /* interrupt line */ + uchar intp; /* interrupt pin */ + + Pcidev* list; + Pcidev* link; /* next device on this bno */ + + Pcidev* bridge; /* down a bus */ + struct { + ulong bar; + int size; + } ioa, mema; + + int pmrb; /* power management register block */ +}; + +enum { + /* vendor ids */ + Vatiamd = 0x1002, + Vintel = 0x8086, + Vjmicron= 0x197b, + Vmarvell= 0x1b4b, + Vmyricom= 0x14c1, + Vrtl = 0x10ec, +}; + +#define PCIWINDOW 0x80000000 +#define PCIWADDR(va) (PADDR(va)+PCIWINDOW) +#define ISAWINDOW 0 +#define ISAWADDR(va) (PADDR(va)+ISAWINDOW) + +#define PCIMEMADDR(pa) ((pa)|PCIMEM) + +/* SMBus transactions */ +enum +{ + SMBquick, /* sends address only */ + + /* write */ + SMBsend, /* sends address and cmd */ + SMBbytewrite, /* sends address and cmd and 1 byte */ + SMBwordwrite, /* sends address and cmd and 2 bytes */ + + /* read */ + SMBrecv, /* sends address, recvs 1 byte */ + SMBbyteread, /* sends address and cmd, recv's byte */ + SMBwordread, /* sends address and cmd, recv's 2 bytes */ +}; + +typedef struct SMBus SMBus; +struct SMBus { + QLock; /* mutex */ + Rendez r; /* rendezvous point for completion interrupts */ + void *arg; /* implementation dependent */ + ulong base; /* port or memory base of smbus */ + int busy; + void (*transact)(SMBus*, int, int, int, uchar*); +}; + +#pragma varargck type "T" int +#pragma varargck type "T" uint --- /dev/null +++ /sys/src/9/loongson64/kbd.c @@ -0,0 +1 @@ +#include "../loongson/kbd.c" --- /dev/null +++ /sys/src/9/loongson64/l.s @@ -0,0 +1,1057 @@ +#include "mem.h" +#include "spim64.s" + +/* + * entrypoint. set SB, pass arguments to main(). + * PMON's calling convention: + * argc R4 + * argv R5 + * envp R6 + * callvec R7 + */ + +TEXT start(SB), $-8 + MOVV $setR30(SB), R30 + + PUTC('9', R1, R2) + + /* don't enable any interrupts, out of EXL mode */ + MOVW $(CU1|KX|UX), R1 + MOVW R1, M(STATUS) + EHB + MOVW R0, M(CAUSE) + EHB + MOVW R0, M(COMPARE) + EHB + MOVW R0, M(PERFCTL) + EHB + + MOVV R4, _argc(SB) + MOVV R5, _argv(SB) + MOVV R6, _env(SB) + MOVV R7, pmon_callvec(SB) + + MOVW $(FPOVFL|FPZDIV|FPINVAL|FPFLUSH), R1 + MOVW R1, FCR31 // permit only inexact and underflow + NOP + MOVD $0.5, F26 + SUBD F26, F26, F24 + ADDD F26, F26, F28 + ADDD F28, F28, F30 + + MOVD F24, F0 + MOVD F24, F2 + MOVD F24, F4 + MOVD F24, F6 + MOVD F24, F8 + MOVD F24, F10 + MOVD F24, F12 + MOVD F24, F14 + MOVD F24, F16 + MOVD F24, F18 + MOVD F24, F20 + MOVD F24, F22 + + MOVW $TLBROFF, R1 + MOVW R1, M(WIRED) + EHB + MOVV R0, M(CONTEXT) + EHB + MOVV R0, M(XCONTEXT) + EHB + + /* set KSEG0 cachability before trying LL/SC in lock code */ + TOKSEG1(11) + MOVW M(CONFIG), R1 + AND $(~CFG_K0), R1 + /* make kseg0 cachable */ + OR $(PTECACHABILITY>>3), R1 + MOVW R1, M(CONFIG) + EHB + TOKSEG0(11) + + MOVV $MACHADDR, R(MACH) + ADDVU $(MACHSIZE-BY2WD), R(MACH), R29 /* set up stack */ + + JAL main(SB) + + /* main() returned */; + PUTC('\r', R1, R2); DELAY(R2); PUTC('\n', R1, R2); DELAY(R2); + PUTC('m', R1, R2); DELAY(R2); PUTC('a', R1, R2); DELAY(R2); + PUTC('i', R1, R2); DELAY(R2); PUTC('n', R1, R2); DELAY(R2); + PUTC('(', R1, R2); DELAY(R2); PUTC(')', R1, R2); DELAY(R2); + PUTC(' ', R1, R2); DELAY(R2); PUTC('r', R1, R2); DELAY(R2); + PUTC('e', R1, R2); DELAY(R2); PUTC('t', R1, R2); DELAY(R2); + PUTC('u', R1, R2); DELAY(R2); PUTC('r', R1, R2); DELAY(R2); + PUTC('n', R1, R2); DELAY(R2); PUTC('e', R1, R2); DELAY(R2); + PUTC('d', R1, R2); DELAY(R2); PUTC('\r', R1, R2); DELAY(R2); + PUTC('\n', R1, R2); + JMP 0(PC) /* loop */ + +/* target for JALR in TOKSEG0/1 */ +TEXT ret0(SB), $-8 + AND $~KSEG1, R22 + OR $KSEG0, R22 + JMP (R22) + +TEXT ret1(SB), $-8 + OR $KSEG1, R22 + JMP (R22) + +/* print R1 in hex; clobbers R3—8 */ +TEXT printhex(SB), $-8 + MOVW $64, R5 + MOVW $9, R7 +prtop: + SUB $4, R5 + MOVV R1, R6 + SRLV R5, R6 + AND $0xf, R6 + SGTU R6, R7, R8 + BEQ R8, prdec /* branch if R6 <= 9 */ + ADD $('a'-10), R6 + JMP prchar +prdec: + ADD $'0', R6 +prchar: + PUTC(R6, R3, R4) + DELAY(R4) + BNE R5, prtop + RET + +/* + * Take first processor into user mode + * - argument is stack pointer to user + */ +TEXT touser(SB), $-8 + MOVV R1, R29 + MOVW $(UTZERO+32), R2 /* header appears in text */ + MOVW R0, M(CAUSE) + EHB + + MOVW M(STATUS), R4 + AND $(~KMODEMASK), R4 + OR $(KUSER|IE|EXL), R4 /* switch to user mode, intrs on, exc */ + MOVW R4, M(STATUS) + + MOVV R2, M(EPC) + ERET /* clears EXL */ + +TEXT _loop(SB), $-8 + MOVW M(STATUS), R1 + JAL printhex(SB) + JMP 0(PC) + +/* + * manipulate interrupts + */ + +/* enable an interrupt; bit is in R1 */ +TEXT intron(SB), $0 + MOVW M(STATUS), R2 + OR R1, R2 + MOVW R2, M(STATUS) + EHB + RET + +/* disable an interrupt; bit is in R1 */ +TEXT introff(SB), $0 + MOVW M(STATUS), R2 + XOR $-1, R1 + AND R1, R2 + MOVW R2, M(STATUS) + EHB + RET + +TEXT splhi(SB), $0 + MOVV R31, 24(R(MACH)) /* save PC in m->splpc */ + MOVW M(STATUS), R1 + EHB + AND $(~IE), R1, R2 + MOVW R2, M(STATUS) + EHB + RET + +TEXT splx(SB), $0 + MOVW R31, 24(R(MACH)) /* save PC in m->splpc */ + MOVW M(STATUS), R2 + EHB + AND $IE, R1 + AND $(~IE), R2 + OR R2, R1 + MOVW R1, M(STATUS) + EHB + RET + +TEXT spllo(SB), $0 + MOVW M(STATUS), R1 + EHB + OR $IE, R1, R2 + MOVW R2, M(STATUS) + EHB + RET + +TEXT spldone(SB), $0 + RET + +TEXT islo(SB), $0 + MOVW M(STATUS), R1 + AND $IE, R1 + RET + +TEXT coherence(SB), $-8 + SYNC + NOP + NOP + RET + +/* + * process switching + */ + +TEXT setlabel(SB), $-8 + MOVV R29, 0(R1) /* sp */ + MOVV R31, 8(R1) /* pc */ + MOVV R0, R1 + RET + +TEXT gotolabel(SB), $-8 + MOVV 0(R1), R29 /* sp */ + MOVV 8(R1), R31 /* pc */ + MOVW $1, R1 + RET + +TEXT machstatus(SB), $0 + MOVW M(STATUS), R1 + RET + +TEXT getstatus(SB), $0 + MOVW M(STATUS), R1 + RET + +TEXT setstatus(SB), $0 + MOVW R1, M(STATUS) + EHB + RET + +TEXT rdcount(SB), $0 + MOVW M(COUNT), R1 + RET + +TEXT wrcount(SB), $0 + MOVW R1, M(COUNT) + EHB + RET + +TEXT wrcompare(SB), $0 + MOVW R1, M(COMPARE) + EHB + RET + +TEXT rdcompare(SB), $0 + MOVW M(COMPARE), R1 + RET + +TEXT prid(SB), $0 + MOVW M(PRID), R1 + RET + +TEXT getconfig(SB), $0 + MOVW M(CONFIG), R1 + RET + +TEXT getcause(SB), $0 + MOVW M(CAUSE), R1 + RET + +/* + * the tlb routines need to be called at splhi. + */ + +TEXT puttlb(SB), $0 /* puttlb(virt, phys0, phys1) */ + MOVV R1, M(TLBVIRT) + EHB + MOVV 8(FP), R2 /* phys0 */ + MOVV 16(FP), R3 /* phys1 */ + MOVV R2, M(TLBPHYS0) + EHB + MOVW $PGSZ, R1 + MOVV R3, M(TLBPHYS1) + EHB + MOVW R1, M(PAGEMASK) + EHB + OR R2, R3, R4 + AND $PTEVALID, R4 + TLBP /* tlb probe */ + EHB + MOVW M(INDEX), R1 + BGEZ R1, index /* if tlb entry found, use it */ + BEQ R4, dont /* not valid? cf. kunmap */ + MOVW M(RANDOM), R1 /* write random tlb entry */ + MOVW R1, M(INDEX) + EHB +index: + TLBWI /* write indexed tlb entry */ + EHB +dont: + RET + +TEXT getwired(SB),$0 + MOVW M(WIRED), R1 + RET + +TEXT setwired(SB),$0 + MOVW R1, M(WIRED) + EHB + RET + +TEXT getrandom(SB),$0 + MOVW M(RANDOM), R1 + RET + +TEXT getpagemask(SB),$0 + MOVW M(PAGEMASK), R1 + RET + +TEXT setpagemask(SB),$0 + MOVW R1, M(PAGEMASK) + EHB + MOVV R0, R1 /* prevent accidents */ + RET + +TEXT puttlbx(SB), $0 /* puttlbx(index, virt, phys0, phys1, pagemask) */ + MOVV 8(FP), R2 + MOVV 16(FP), R3 + MOVV 24(FP), R4 + MOVW 32(FP), R5 + MOVV R2, M(TLBVIRT) + EHB + MOVV R3, M(TLBPHYS0) + EHB + MOVV R4, M(TLBPHYS1) + EHB + MOVW R5, M(PAGEMASK) + EHB + MOVW R1, M(INDEX) + EHB + TLBWI + EHB + RET + +TEXT tlbvirt(SB), $0 + MOVV M(TLBVIRT), R1 + RET + +TEXT gettlbvirt(SB), $0 /* gettlbvirt(index) */ + MOVV M(TLBVIRT), R10 /* save our asid */ + MOVW R1, M(INDEX) + EHB + TLBR /* read indexed tlb entry */ + EHB + MOVV M(TLBVIRT), R1 + MOVV R10, M(TLBVIRT) /* restore our asid */ + EHB + RET + +TEXT gettlbx(SB), $0 /* gettlbx(index, &entry) */ + MOVV 8(FP), R5 + MOVV M(TLBVIRT), R10 /* save our asid */ + MOVW R1, M(INDEX) + EHB + TLBR /* read indexed tlb entry */ + EHB + MOVV M(TLBVIRT), R2 + MOVV M(TLBPHYS0), R3 + MOVV M(TLBPHYS1), R4 + MOVV R2, 0(R5) + MOVV R3, 8(R5) + MOVV R4, 16(R5) + MOVV R10, M(TLBVIRT) /* restore our asid */ + EHB + RET + +TEXT gettlbp(SB), $0 /* gettlbp(tlbvirt, &entry) */ + MOVV 8(FP), R5 + MOVV M(TLBVIRT), R10 /* save our asid */ + MOVV R1, M(TLBVIRT) + EHB + TLBP /* probe tlb */ + EHB + MOVW M(INDEX), R1 + BLTZ R1, gettlbp1 /* if no tlb entry found, return */ + TLBR /* read indexed tlb entry */ + EHB + MOVV M(TLBVIRT), R2 + MOVV M(TLBPHYS0), R3 + MOVV M(TLBPHYS1), R4 + MOVW M(PAGEMASK), R6 + MOVV R2, 0(R5) + MOVV R3, 8(R5) + MOVV R4, 16(R5) + MOVW R6, 24(R5) +gettlbp1: + MOVV R10, M(TLBVIRT) /* restore our asid */ + EHB + RET + +/* + * exceptions. + * mips promises that there will be no current hazards upon entry + * to exception handlers. + */ + +/* vector at KSEG0+0x80, simple tlb refill */ +TEXT vector0(SB), $-8 + MOVV $utlbmiss(SB), R26 + JMP (R26) + +/* + * compute stlb hash index. + * must match index calculation in mmu.c/putstlb() + * + * M(TLBVIRT) [page & asid] in arg, result in arg. + * stir in swizzled asid; we get best results with asid in both high & low bits. + * + * page = tlbvirt >> (PGSHIFT+1); // ignoring even/odd bit + * R27 = ((tlbvirt<<(STLBLOG-8) ^ (uchar)tlbvirt ^ page ^ + * ((page & (MASK(HIPFNBITS) << STLBLOG)) >> HIPFNBITS)) & + * (STLBSIZE-1)) * 12; + */ +#define STLBHASH(arg, tmp, tmp2) \ + MOVV arg, tmp2; \ + SRLV $(PGSHIFT+1), arg; /* move low page # bits to low bits */ \ + CONST ((MASK(HIPFNBITS) << STLBLOG), tmp); \ + AND arg, tmp; /* extract high page # bits */ \ + SRLV $HIPFNBITS, tmp; /* position them */ \ + XOR tmp, arg; /* include them */ \ + MOVV tmp2, tmp; /* asid in low byte */ \ + SLLV $(STLBLOG-8), tmp; /* move asid to high bits */ \ + XOR tmp, arg; /* include asid in high bits too */ \ + AND $0xff, tmp2, tmp; /* asid in low byte */ \ + XOR tmp, arg; /* include asid in low bits */ \ + CONST (STLBSIZE-1, tmp); \ + AND tmp, arg /* chop to fit */ + +TEXT stlbhash(SB), $-8 + STLBHASH(R1, R2, R3) + RET + +TEXT utlbmiss(SB), $-8 +// PUTC('u', R26, R27); +// MOVV M(BADVADDR), R1 +// EHB +// JAL printhex(SB) +// JMP 0(PC) +// NOP + + /* + * don't use R28 by using constants that span both word halves, + * it's unsaved so far. avoid R24 (up in kernel) and R25 (m in kernel). + */ + /* update statistics */ + CONST (MACHADDR, R26) /* R26 = m-> */ + MOVW 32(R26), R27 + ADDU $1, R27 + MOVW R27, 32(R26) /* m->tlbfault++ */ + + MOVV R23, M(ERROREPC) /* save R23, M(ERROREPC) ok? */ + +#ifdef KUTLBSTATS + MOVW M(STATUS), R23 + AND $KUSER, R23 + BEQ R23, kmiss + + MOVW 40(R26), R27 + ADDU $1, R27 + MOVW R27, 40(R26) /* m->utlbfault++ */ + JMP either +kmiss: + MOVW 36(R26), R27 + ADDU $1, R27 + MOVW R27, 36(R26) /* m->ktlbfault++ */ +either: +#endif + + /* compute stlb index */ + EHB + MOVV M(TLBVIRT), R27 /* asid in low byte */ + STLBHASH(R27, R26, R23) + MOVV M(ERROREPC), R23 /* restore R23 */ + + /* scale to a byte index (multiply by 24) */ + SLL $1, R27, R26 /* × 2 */ + ADDU R26, R27 /* × 3 */ + SLL $3, R27 /* × 24 */ + + CONST (MACHADDR, R26) /* R26 = m-> */ + MOVV 8(R26), R26 /* R26 = m->stb */ + ADDVU R26, R27 /* R27 = &m->stb[hash] */ + + MOVV M(BADVADDR), R26 + AND $BY2PG, R26 + BNE R26, utlbodd /* odd page? */ + +utlbeven: + MOVV 8(R27), R26 /* R26 = m->stb[hash].phys0 */ + BEQ R26, stlbm /* nothing cached? do it the hard way */ + MOVV R26, M(TLBPHYS0) + EHB + MOVV 16(R27), R26 /* R26 = m->stb[hash].phys1 */ + MOVV R26, M(TLBPHYS1) + EHB + JMP utlbcom + +utlbodd: + MOVV 16(R27), R26 /* R26 = m->stb[hash].phys1 */ + BEQ R26, stlbm /* nothing cached? do it the hard way */ + MOVV R26, M(TLBPHYS1) + EHB + MOVV 8(R27), R26 /* R26 = m->stb[hash].phys0 */ + MOVV R26, M(TLBPHYS0) + EHB + +utlbcom: + MOVV M(TLBVIRT), R26 + MOVV (R27), R27 /* R27 = m->stb[hash].virt */ + BEQ R27, stlbm /* nothing cached? do it the hard way */ + + /* is the stlb entry for the right virtual address? */ + BNE R26, R27, stlbm /* M(TLBVIRT) != m->stb[hash].virt? */ + + /* if an entry exists, overwrite it, else write a random one */ + CONST (PGSZ, R27) + MOVW R27, M(PAGEMASK) /* select page size */ + EHB + TLBP /* probe tlb */ + EHB + MOVW M(INDEX), R26 + EHB + BGEZ R26, utlindex /* if tlb entry found, rewrite it */ + TLBWR /* else write random tlb entry */ + ERET +utlindex: + TLBWI /* write indexed tlb entry */ + ERET + +/* not in the stlb either; make trap.c figure it out */ +stlbm: + MOVV $exception(SB), R26 + JMP (R26) + +/* vector at KSEG1+0x100, cache error */ +TEXT vector100(SB), $-8 + MOVV $exception(SB), R26 + JMP (R26) + +/* vector at KSEG0+0x180, others */ +TEXT vector180(SB), $-8 + MOVV $exception(SB), R26 + JMP (R26) + +TEXT exception(SB), $-8 +// PUTC('t', R26, R27) +// MOVV R29, R1 +// JAL printhex(SB) +// JMP 0(PC) +// NOP + + MOVW M(STATUS), R26 + AND $KUSER, R26, R27 + BEQ R27, waskernel + +wasuser: + MOVV R29, R27 + CONST (MACHADDR, R29) /* m-> */ + MOVV 16(R29), R29 /* m->proc */ + MOVV 16(R29), R29 /* m->proc->kstack */ + MOVW M(STATUS), R26 /* redundant load */ + ADDVU $(KSTACK-UREGSIZE), R29 + MOVV R31, Ureg_r31(R29) + + JAL savereg1(SB) + + MOVV R30, Ureg_r30(R29) + MOVV R(MACH), Ureg_r25(R29) + MOVV R(USER), Ureg_r24(R29) + + MOVV $setR30(SB), R30 + CONST (MACHADDR, R(MACH)) /* R(MACH) = m-> */ + MOVV 16(R(MACH)), R(USER) /* up = m->proc */ + + AND $(EXCMASK<<2), R26, R1 /* R26 = M(CAUSE) from savereg1 */ + SUBU $(CSYS<<2), R1 + BNE R1, notsys + + MOVV R29, R1 /* first arg for syscall */ + SUBVU $Notuoffset, R29 + JAL syscall(SB) + +sysrestore: + ADDVU $Notuoffset, R29 + JAL restreg1(SB) + + MOVV Ureg_r31(R29), R31 + MOVW Ureg_status(R29), R26 + MOVV Ureg_r30(R29), R30 + MOVW R26, M(STATUS) + EHB + MOVV Ureg_pc(R29), R26 /* old pc */ + MOVV Ureg_sp(R29), R29 + MOVV R26, M(EPC) + ERET + +notsys: + JAL savereg2(SB) + + MOVV R29, R1 /* first arg for trap */ + SUBVU $Notuoffset, R29 + JAL trap(SB) + + ADDVU $Notuoffset, R29 + +restore: + JAL restreg1(SB) + JAL restreg2(SB) /* restores R28, among others, R26 = old pc */ + + MOVV Ureg_r30(R29), R30 + MOVV Ureg_r31(R29), R31 + MOVV Ureg_r25(R29), R(MACH) + MOVV Ureg_r24(R29), R(USER) + MOVV Ureg_sp(R29), R29 + MOVV R26, M(EPC) + ERET + +waskernel: + MOVV R29, R27 + SUBVU $UREGSIZE, R29 + OR $7, R29 /* conservative rounding */ + XOR $7, R29 + MOVV R31, Ureg_r31(R29) + + JAL savereg1(SB) + JAL savereg2(SB) + + MOVV R29, R1 /* first arg for trap */ + SUBVU $Notuoffset, R29 + JAL trap(SB) + + ADDVU $Notuoffset, R29 + + JAL restreg1(SB) + JAL restreg2(SB) /* restores R28, among others, R26 = old pc */ + + MOVV Ureg_r31(R29), R31 + MOVV Ureg_sp(R29), R29 + MOVV R26, M(EPC) + ERET + +TEXT forkret(SB), $0 + MOVV R0, R1 + JMP sysrestore + +/* + * save mandatory registers. + * called with old M(STATUS) in R26. + * called with old SP in R27 + * returns with M(CAUSE) in R26 + */ +TEXT savereg1(SB), $-8 + MOVV R1, Ureg_r1(R29) + + MOVW $(~KMODEMASK), R1 /* don't use R28, it's unsaved so far */ + AND R26, R1 + MOVW R1, M(STATUS) /* kernel mode, no interrupt */ + EHB + + MOVW R26, Ureg_status(R29) /* status */ + MOVV R27, Ureg_sp(R29) /* user SP */ + + MOVV M(EPC), R1 + MOVW M(CAUSE), R26 + + MOVV R23, Ureg_r23(R29) + MOVV R22, Ureg_r22(R29) + MOVV R21, Ureg_r21(R29) + MOVV R20, Ureg_r20(R29) + MOVV R19, Ureg_r19(R29) + MOVV R1, Ureg_pc(R29) + RET + +/* + * all other registers. + * called with M(CAUSE) in R26 + */ +TEXT savereg2(SB), $-8 + MOVV R2, Ureg_r2(R29) + + MOVV M(BADVADDR), R2 + MOVV R26, Ureg_cause(R29) + MOVV M(TLBVIRT), R1 + MOVV R2, Ureg_badvaddr(R29) + MOVV R1, Ureg_tlbvirt(R29) + MOVV HI, R1 + MOVV LO, R2 + MOVV R1, Ureg_hi(R29) + MOVV R2, Ureg_lo(R29) + /* LINK,SB,SP missing */ + MOVV R28, Ureg_r28(R29) + /* R27, R26 not saved */ + /* R25, R24 missing */ + /* R23- R19 saved in save1 */ + MOVV R18, Ureg_r18(R29) + MOVV R17, Ureg_r17(R29) + MOVV R16, Ureg_r16(R29) + MOVV R15, Ureg_r15(R29) + MOVV R14, Ureg_r14(R29) + MOVV R13, Ureg_r13(R29) + MOVV R12, Ureg_r12(R29) + MOVV R11, Ureg_r11(R29) + MOVV R10, Ureg_r10(R29) + MOVV R9, Ureg_r9(R29) + MOVV R8, Ureg_r8(R29) + MOVV R7, Ureg_r7(R29) + MOVV R6, Ureg_r6(R29) + MOVV R5, Ureg_r5(R29) + MOVV R4, Ureg_r4(R29) + MOVV R3, Ureg_r3(R29) + RET + +/* restore R23-R19 */ +TEXT restreg1(SB), $-8 + MOVV Ureg_r23(R29), R23 + MOVV Ureg_r22(R29), R22 + MOVV Ureg_r21(R29), R21 + MOVV Ureg_r20(R29), R20 + MOVV Ureg_r19(R29), R19 + RET + +/* + * all other registers. + * returns with pc in R26 + */ +TEXT restreg2(SB), $-8 + /* LINK,SB,SP missing */ + MOVV Ureg_r28(R29), R28 + /* R27, R26 not saved */ + /* R25, R24 missing */ + /* R19- R23 restored in rest1 */ + MOVV Ureg_r18(R29), R18 + MOVV Ureg_r17(R29), R17 + MOVV Ureg_r16(R29), R16 + MOVV Ureg_r15(R29), R15 + MOVV Ureg_r14(R29), R14 + MOVV Ureg_r13(R29), R13 + MOVV Ureg_r12(R29), R12 + MOVV Ureg_r11(R29), R11 + MOVV Ureg_r10(R29), R10 + MOVV Ureg_r9(R29), R9 + MOVV Ureg_r8(R29), R8 + MOVV Ureg_r7(R29), R7 + MOVV Ureg_r6(R29), R6 + MOVV Ureg_r5(R29), R5 + MOVV Ureg_r4(R29), R4 + MOVV Ureg_r3(R29), R3 + MOVV Ureg_lo(R29), R2 + MOVV Ureg_hi(R29), R1 + MOVV R2, LO + MOVV R1, HI + + MOVW Ureg_status(R29), R1 + MOVV Ureg_r2(R29), R2 + MOVW R1, M(STATUS) /* could change interruptibility */ + EHB + MOVV Ureg_r1(R29), R1 /* BOTCH */ + MOVV Ureg_pc(R29), R26 + RET + +/* + * floating point stuff + */ + +TEXT savefpregs(SB), $0 + MOVW FCR31, R2 + MOVW M(STATUS), R3 + AND $~FPEXCMASK, R2, R4 + MOVW R4, FCR31 + + MOVD F0, (0*8)(R1) + MOVD F2, (1*8)(R1) + MOVD F4, (2*8)(R1) + MOVD F6, (3*8)(R1) + MOVD F8, (4*8)(R1) + MOVD F10, (5*8)(R1) + MOVD F12, (6*8)(R1) + MOVD F14, (7*8)(R1) + MOVD F16, (8*8)(R1) + MOVD F18, (9*8)(R1) + MOVD F20, (10*8)(R1) + MOVD F22, (11*8)(R1) + MOVD F24, (12*8)(R1) + MOVD F26, (13*8)(R1) + MOVD F28, (14*8)(R1) + MOVD F30, (15*8)(R1) + + MOVW R2, (16*8)(R1) /* FCR31 */ + AND $~CU1, R3 + MOVW R3, M(STATUS) + EHB + RET + +TEXT restfpregs(SB), $0 /* restfpregs(fpsave, fpstatus) */ + MOVW M(STATUS), R3 + OR $CU1, R3 + MOVW R3, M(STATUS) + EHB + MOVW 8(FP), R2 + MOVW R2, FCR31 + NOP + + MOVD (0*8)(R1), F0 + MOVD (1*8)(R1), F2 + MOVD (2*8)(R1), F4 + MOVD (3*8)(R1), F6 + MOVD (4*8)(R1), F8 + MOVD (5*8)(R1), F10 + MOVD (6*8)(R1), F12 + MOVD (7*8)(R1), F14 + MOVD (8*8)(R1), F16 + MOVD (9*8)(R1), F18 + MOVD (10*8)(R1), F20 + MOVD (11*8)(R1), F22 + MOVD (12*8)(R1), F24 + MOVD (13*8)(R1), F26 + MOVD (14*8)(R1), F28 + MOVD (15*8)(R1), F30 + + AND $~CU1, R3 + MOVW R3, M(STATUS) + EHB + RET + +TEXT fcr31(SB), $0 + MOVW FCR31, R1 + MOVW M(STATUS), R3 + AND $~CU1, R3 + MOVW R3, M(STATUS) + EHB + RET + +TEXT clrfpintr(SB), $0 + MOVW M(STATUS), R3 + OR $CU1, R3 + MOVW R3, M(STATUS) + EHB + + MOVW FCR31, R1 + AND $~FPEXCMASK, R1, R2 + MOVW R2, FCR31 + + AND $~CU1, R3 + MOVW R3, M(STATUS) + EHB + RET + +/* + * Emulate 68020 test and set: load linked / store conditional + */ + +TEXT tas(SB), $0 + MOVV R1, R2 /* address of key */ +tas1: + MOVW $1, R3 + LL(2, 1) + NOP + SC(2, 3) + NOP + BEQ R3, tas1 + RET + +TEXT _xinc(SB), $0 + MOVV R1, R2 /* address of counter */ +loop: + MOVW $1, R3 + LL(2, 1) + NOP + ADDU R1, R3 + MOVV R3, R1 /* return new value */ + SC(2, 3) + NOP + BEQ R3, loop + RET + +TEXT _xdec(SB), $0 + SYNC + NOP + NOP + MOVV R1, R2 /* address of counter */ +loop1: + MOVW $-1, R3 + LL(2, 1) + NOP + ADDU R1, R3 + MOVV R3, R1 /* return new value */ + SC(2, 3) + NOP + BEQ R3, loop1 + RET + +TEXT cmpswap(SB), $0 + MOVV R1, R2 /* address of key */ + MOVW old+8(FP), R3 /* old value */ + MOVW new+16(FP), R4 /* new value */ + LL(2, 1) /* R1 = (R2) */ + NOP + BNE R1, R3, fail + MOVV R4, R1 + SC(2, 1) /* (R2) = R1 if (R2) hasn't changed; R1 = success */ + NOP + RET +fail: + MOVV R0, R1 + RET + +/* + * cache manipulation + */ + +/* + * we avoided using R4, R5, R6, and R7 so gotopc can call us without saving + * them, but gotopc is now gone. + */ +TEXT icflush(SB), $-8 /* icflush(virtaddr, count) */ + MOVW M(STATUS), R10 /* old status -> R10 */ + MOVW 8(FP), R9 + MOVW R0, M(STATUS) /* intrs off */ + EHB + + TOKSEG1(11) /* return to kseg1 (uncached) */ + ADDVU R1, R9 /* R9 = last address */ + MOVW $(~(CACHELINESZ-1)), R8 + AND R1, R8 /* R8 = first address, rounded down */ + ADDVU $(CACHELINESZ-1), R9 + AND $(~(CACHELINESZ-1)), R9 /* round last address up */ + SUBVU R8, R9 /* R9 = revised count */ +icflush1: +// CACHE PD+HWB, (R8) /* flush D to ram */ +// CACHE PI+HINV, (R8) /* invalidate in I */ + CACHE SD+HWBI, (R8) /* flush & invalidate thru L2 */ + SUBVU $CACHELINESZ, R9 + ADDVU $CACHELINESZ, R8 + BGTZ R9, icflush1 + + TOKSEG0(11) /* return to kseg0 (cached) */ + MOVW R10, M(STATUS) + EHB + RET + +TEXT dcflush(SB), $-8 /* dcflush(virtaddr, count) */ + MOVW M(STATUS), R10 /* old status -> R10 */ + MOVW 8(FP), R9 + MOVW R0, M(STATUS) /* intrs off */ + EHB + SYNC + + TOKSEG1(11) /* return to kseg1 (uncached) */ + ADDVU R1, R9 /* R9 = last address */ + MOVW $(~(CACHELINESZ-1)), R8 + AND R1, R8 /* R8 = first address, rounded down */ + ADDVU $(CACHELINESZ-1), R9 + AND $(~(CACHELINESZ-1)), R9 /* round last address up */ + SUBVU R8, R9 /* R9 = revised count */ +dcflush1: +// CACHE PI+HINV, (R8) /* invalidate in I */ +// CACHE PD+HWBI, (R8) /* flush & invalidate in D */ + CACHE SD+HWBI, (R8) /* flush & invalidate thru L2 */ + SUBVU $CACHELINESZ, R9 + ADDVU $CACHELINESZ, R8 + BGTZ R9, dcflush1 + SYNC + + TOKSEG0(11) /* return to kseg0 (cached) */ + MOVW R10, M(STATUS) + EHB + RET + +TEXT dcinvalid(SB), $-8 /* dcinvalid(virtaddr, count) */ + MOVW M(STATUS), R10 /* old status -> R10 */ + MOVW 8(FP), R9 + MOVW R0, M(STATUS) /* intrs off */ + EHB + SYNC + + TOKSEG1(11) /* return to kseg1 (uncached) */ + ADDVU R1, R9 /* R9 = last address */ + MOVW $(~(CACHELINESZ-1)), R8 + AND R1, R8 /* R8 = first address, rounded down */ + ADDVU $(CACHELINESZ-1), R9 + AND $(~(CACHELINESZ-1)), R9 /* round last address up */ + SUBVU R8, R9 /* R9 = revised count */ +dcinvalid1: +// CACHE PD+HINV, (R8) /* invalidate in D */ + CACHE SD+HINV, (R8) /* invalidate thru L2 */ + SUBVU $CACHELINESZ, R9 + ADDVU $CACHELINESZ, R8 + BGTZ R9, dcinvalid1 + SYNC + + TOKSEG0(11) /* return to kseg0 (cached) */ + MOVW R10, M(STATUS) + EHB + RET + +TEXT cleancache(SB), $-8 + MOVW M(STATUS), R10 /* old status -> R10 */ + MOVW R0, M(STATUS) /* intrs off */ + EHB + + TOKSEG1(11) /* return to kseg1 (uncached) */ + MOVW $KSEG0, R1 /* index, not address, kseg0 avoids tlb */ + MOVW $(SCACHESIZE/4), R9 /* 4-way cache */ +ccache: + CACHE SD+IWBI, 0(R1) /* flush & invalidate thru L2 by index */ + CACHE SD+IWBI, 1(R1) /* ways are least significant bits */ + CACHE SD+IWBI, 2(R1) + CACHE SD+IWBI, 3(R1) + SUBU $CACHELINESZ, R9 + ADDU $CACHELINESZ, R1 + BGTZ R9, ccache + SYNC + + TOKSEG0(11) /* return to kseg0 (cached) */ + MOVW R10, M(STATUS) + EHB + RET + +/* + * PMON routines, for early debugging. + * wrapper converts PMON's calling convention to 0c's. + * we need to leave CFSZ space to prevent stack corrupted + */ + +#define CFSZ 48 // XXX + +DATA pmon_callvec(SB)/4, $0 /* pmon call vector */ +GLOBL pmon_callvec(SB), $4 + +#define pmon_wrap(name, index) \ +TEXT name(SB), $((CFSZ+12+7)&(~7)); \ + MOVV R1, R4; \ + MOVW arg2+4(FP), R5; \ + MOVW arg3+8(FP), R6; \ + MOVW arg4+12(FP), R7; \ + MOVW R29, R2; \ + AND $~7, R29; /* pmon needs R29 8-aligned */ \ + MOVW R31, -4(SP); \ + MOVW R30, -8(SP); \ + MOVW R2, -12(SP); \ + MOVW pmon_callvec(SB), R8; \ + MOVW (4*(index))(R8), R8; \ + JAL (R8); \ + MOVW -8(SP), R30; \ + MOVW -4(SP), R31; \ + MOVW -12(SP), R29; \ + MOVV R2, R1; \ + MOVW R31, 0(R29); \ + RET + +pmon_wrap(pmonprint, 5) --- /dev/null +++ /sys/src/9/loongson64/ln64 @@ -0,0 +1,70 @@ +dev + root + cons + cap + dup + env + fs + kprof + mnt + pipe + pnp pci + proc + rtc +# sd + srv + ssl + tls + uart + + ether netif + ip arp chandial inferno ip ipv6 ipaux iproute netlog nullmedium pktmedium ptclbsum + + draw screen + kbin kbd latin1 + kbmap + mouse mouse + +# usb + +link + loopbackmedium + ethermedium + ether8139 pci + +misc +# uarti8250 + +ip + tcp + udp + ipifc + icmp + icmp6 + gre + ipmux + esp + +port + int cpuserver = 0; +# 2e +# int screenwid = 640; +# int screenht = 480; +# int screendepth = 16; +# ulong cpufreq = 667*Mhz; +# 2f + int screenwid = 1024; + int screenht = 600; + int screendepth = 16; + ulong cpufreq = 797*Mhz; + +boot boot + local + tcp + +bootdir + boot$CONF.out boot + /sys/lib/dist/bin/spim64/bzfs kfs + /sys/lib/dist/ln64/root.bz2 bzroot + /spim64/bin/ip/ipconfig + /spim64/bin/auth/factotum --- /dev/null +++ /sys/src/9/loongson64/main.c @@ -0,0 +1,753 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../ip/ip.h" +#include +#include +#include +#include "init.h" + +enum { + /* space for syscall args, return PC, top-of-stack struct */ + Stkheadroom = sizeof(Sargs) + sizeof(uintptr) + sizeof(Tos), +}; + +typedef struct mipsexec Mipsexec; + +#define MAXCONF 64 +#define CONFBUFLEN 4096 +#define MAXBOOTENV 256 + +/* only for reboot */ +#define BOOTARGS ((char*)(CONFADDR)) +#define BOOTARGSLEN CONFBUFLEN +#define CONFARGVLEN (MAXCONF*BY2WD) + +/* args passed by boot loader for main(), addr in 32 bit */ +int _argc; +long *_argv; +long *_env; +static char *bootenvname[MAXBOOTENV]; +static char *bootenvval[MAXBOOTENV]; +static int nenv; + +/* plan9.ini */ +static char confbuf[CONFBUFLEN]; +static char *confname[MAXCONF]; +static char *confval[MAXCONF]; +static int nconf; + +/* + * Option arguments from the command line. + * oargv[0] is the boot file. + */ +static int oargc; +static char* oargv[20]; +static char oargb[128]; +static int oargblen; + +static uintptr sp; /* XXX - must go - user stack of init proc */ + +/* + * software tlb simulation + */ +static Softtlb stlb[MAXMACH][STLBSIZE]; + +Mach *machaddr[MAXMACH]; +Conf conf; +ulong memsize; +FPsave initfp; + +int normalprint; +ulong cpufreq; + +/* + * initialize a processor's mach structure. each processor does this + * for itself. + */ +void +machinit(void) +{ + /* Ensure CU1 is off */ + clrfpintr(); + + m->stb = &stlb[m->machno][0]; + + clockinit(); +} + +static void +optionsinit(char* s) +{ + strecpy(oargb, oargb+sizeof(oargb), s); + + oargblen = strlen(oargb); + oargc = tokenize(oargb, oargv, nelem(oargv)-1); + oargv[oargc] = nil; +} + +void +plan9iniinit(void) +{ + char *cp; + int i; + + nconf = 0; + if(_argv != nil){ + memmove(confbuf, (char*)_argv[0], sizeof(confbuf)); + for(i=1; i<_argc && nconf= 0) + return confval[i]; + return nil; +} + +static int +findbootenv(char *name) +{ + int i; + + for(i = 0; i < nenv; i++) + if(cistrcmp(bootenvname[i], name) == 0) + return i; + return -1; +} + +static char* +getbootenv(char *name) +{ + int i; + + i = findbootenv(name); + if(i >= 0) + return bootenvval[i]; + return nil; +} + +void +confinit(void) +{ + char *p; + int i; + ulong kpages, ktop; + ulong maxmem, highmemsize; + + /* memory size */ + memsize = MEMSIZE; + if((p = getbootenv("memsize")) != nil) + memsize = strtoul(p, 0, 0) * MB; + highmemsize = 0; + if((p = getbootenv("highmemsize")) != nil) + highmemsize = strtoul(p, 0, 0) * MB; + if((p = getconf("*maxmem")) != nil){ + maxmem = strtoul(p, 0, 0); + memsize = MIN(memsize, maxmem); + maxmem -= memsize; + highmemsize = MIN(highmemsize, maxmem); + if(memsize < 16*MB) /* sanity */ + memsize = 16*MB; + } + + /* set up other configuration parameters */ + conf.nproc = 2000; + conf.nswap = 262144; + conf.nswppo = 4096; + conf.nimage = 200; + + /* + * divide memory to user pages and kernel. + */ + conf.mem[0].base = ktop = PADDR(PGROUND((ulong)end)); + conf.mem[0].npage = memsize/BY2PG - ktop/BY2PG; + + conf.mem[1].base = HIGHMEM; + conf.mem[1].npage = highmemsize/BY2PG; + + conf.npage = 0; + for(i=0; i (64*MB + conf.npage*sizeof(Page))/BY2PG){ + kpages = (64*MB + conf.npage*sizeof(Page))/BY2PG; + kpages += (conf.nproc*KSTACK)/BY2PG; + } + conf.upages = conf.npage - kpages; + conf.ialloc = (kpages/2)*BY2PG; + + kpages *= BY2PG; + kpages -= conf.upages*sizeof(Page) + + conf.nproc*sizeof(Proc) + + conf.nimage*sizeof(Image) + + conf.nswap + + conf.nswppo*sizeof(Page); + mainmem->maxsize = kpages; + + /* + * set up CPU's mach structure + * cpu0's was zeroed in main() and our stack is in Mach, so don't zero it. + */ + m->machno = 0; + m->hz = cpufreq; /* initial guess at MHz */ + m->speed = m->hz / Mhz; + conf.nmach = 1; + + conf.nuart = 1; + conf.copymode = 0; /* copy on write */ +} + +void +procrestore(Proc *p) +{ + uvlong t; + + if(p->kp) + return; + cycles(&t); + p->pcycles -= t; +} + +/* + * Save the mach dependent part of the process state. + */ +void +procsave(Proc *p) +{ + uvlong t; + + cycles(&t); + p->pcycles += t; + //fpuprocsave(); // XXX +} + +static void +fmtinit(void) +{ + printinit(); + quotefmtinstall(); + /* ipreset installs these when chandevreset runs */ + fmtinstall('i', eipfmt); + fmtinstall('I', eipfmt); + fmtinstall('E', eipfmt); + fmtinstall('V', eipfmt); + fmtinstall('M', eipfmt); +} + +/* + * setup MIPS trap vectors + */ +void +vecinit(void) +{ + memmove((ulong*)UTLBMISS, (ulong*)vector0, 0x80); + memmove((ulong*)XEXCEPTION, (ulong*)vector0, 0x80); + memmove((ulong*)CACHETRAP, (ulong*)vector100, 0x80); + memmove((ulong*)EXCEPTION, (ulong*)vector180, 0x80); + /* 0x200 not used in looongson 2e */ + icflush((ulong*)UTLBMISS, 4*1024); + + setstatus(getstatus() & ~BEV); +} + +static void +prcpuid(void) +{ + ulong cpuid, cfg; + char *cpu; + + cpuid = prid(); + cfg = getconfig(); + if (((cpuid>>16) & MASK(8)) == 0) /* vendor */ + cpu = "old mips"; + else if (((cpuid>>16) & MASK(8)) == 1) + switch ((cpuid>>8) & MASK(8)) { /* processor */ + case 0x93: + cpu = "mips 24k"; + break; + case 0x96: + cpu = "mips 24ke"; + break; + default: + cpu = "mips"; + break; + } + else + cpu = "other mips"; + delay(200); + print("cpu%d: %ldMHz %s %se 0x%ulx v%ld.%ld rev %ld has fpu\n", + m->machno, m->hz / Mhz, cpu, cfg & (1<<15)? "b": "l", + (cpuid>>8) & MASK(8), (cpuid>>5) & MASK(3), (cpuid>>2) & MASK(3), + cpuid & MASK(2)); + delay(200); + + print("cpu%d: %d tlb entries, using %dK pages\n", m->machno, + 64, BY2PG/1024); + delay(50); + print("cpu%d: l1 i cache: %d sets 4 ways 32 bytes/line\n", m->machno, + 32 << ((cfg>>9) & MASK(3))); + delay(50); + print("cpu%d: l1 d cache: %d sets 4 ways 32 bytes/line\n", m->machno, + 32 << ((cfg>>6) & MASK(3))); + delay(500); +} + +static int +ckpagemask(ulong mask, ulong size) +{ + int s; + ulong pm; + + s = splhi(); + setpagemask(mask); + pm = getpagemask(); + splx(s); + if(pm != mask){ + iprint("page size %ldK not supported on this cpu; " + "mask %#lux read back as %#lux\n", size/1024, mask, pm); + return -1; + } + return 0; +} + +void +init0(void) +{ + char buf[128]; + int i; + + up->nerrlab = 0; + + spllo(); + + /* + * These are o.k. because rootinit is null. + * Then early kproc's will have a root and dot. + */ + up->slash = namec("#/", Atodir, 0, 0); + pathclose(up->slash->path); + up->slash->path = newpath("/"); + up->dot = cclone(up->slash); + + chandevinit(); + + if(!waserror()){ + ksetenv("cputype", "spim64", 0); + snprint(buf, sizeof buf, "spim64 %s loongson 2f", conffile); + ksetenv("terminal", buf, 0); + if(cpuserver) + ksetenv("service", "cpu", 0); + else + ksetenv("service", "terminal", 0); +// ksetenv("nobootprompt", "local!/boot/bzroot", 0); // for bzroot + ksetenv("nvram", "/boot/nvram", 0); + /* convert plan9.ini variables to #e and #ec */ + for(i = 0; i < nconf; i++) { + if(*confname[i] == '*') + continue; + ksetenv(confname[i], confval[i], 0); + ksetenv(confname[i], confval[i], 1); + } + poperror(); + } + kproc("alarm", alarmkproc, 0); +// i8250console(); + touser(sp); +} + +static void +bootargs(uintptr base) +{ + int i; + uintptr ssize; + char **av, *p; + + /* + * Push the boot args onto the stack. + * The initial value of the user stack must be such + * that the total used is larger than the maximum size + * of the argument list checked in syscall. + */ + i = oargblen+1; + p = UINT2PTR(STACKALIGN(base + BY2PG - Stkheadroom - i)); + memmove(p, oargb, i); + + /* + * Now push the argv pointers. + * The code jumped to by touser in l.s expects arguments + * main(char* argv0, ...) + * and calls + * startboot("/boot/boot", &argv0) + * not the usual (int argc, char* argv[]) + */ + av = (char**)(p - (oargc+1)*sizeof(char*)); + ssize = base + BY2PG - PTR2UINT(av); + for(i = 0; i < oargc; i++) + *av++ = (oargv[i] - oargb) + (p - base) + (USTKTOP - BY2PG); + *av = nil; + sp = USTKTOP - ssize; +} + +void +userinit(void) +{ + Proc *p; + KMap *k; + Page *pg; + Segment *s; + + p = newproc(); + p->pgrp = newpgrp(); + p->egrp = smalloc(sizeof(Egrp)); + p->egrp->ref = 1; + p->fgrp = dupfgrp(nil); + p->rgrp = newrgrp(); + p->procmode = 0640; + + kstrdup(&eve, ""); + kstrdup(&p->text, "*init*"); + kstrdup(&p->user, eve); + + p->fpstate = FPinit; + p->fpsave.fpstatus = initfp.fpstatus; + + /* + * Kernel Stack + */ + p->sched.pc = (uintptr)init0; + p->sched.sp = (uintptr)p->kstack+KSTACK-Stkheadroom; + p->sched.sp = STACKALIGN(p->sched.sp); + + /* + * User Stack + * + * Technically, newpage can't be called here because it + * should only be called when in a user context as it may + * try to sleep if there are no pages available, but that + * shouldn't be the case here. + */ + s = newseg(SG_STACK, USTKTOP-USTKSIZE, USTKSIZE/BY2PG); + p->seg[SSEG] = s; + pg = newpage(1, 0, USTKTOP-BY2PG); + memset(pg->cachectl, PG_DATFLUSH, sizeof(pg->cachectl)); + segpage(s, pg); + k = kmap(pg); + bootargs(VA(k)); + kunmap(k); + + /* + * Text + */ + s = newseg(SG_TEXT, UTZERO, 1); + s->flushme++; + p->seg[TSEG] = s; + pg = newpage(1, 0, UTZERO); + memset(pg->cachectl, PG_TXTFLUSH, sizeof(pg->cachectl)); + segpage(s, pg); + k = kmap(s->map[0]->pages[0]); + memset((void*)VA(k), 0, BY2PG); + memmove((ulong*)VA(k), initcode, sizeof initcode); + kunmap(k); + + ready(p); +} + +/* called from rebootcmd() */ +int +parsemipsboothdr(Chan *c, ulong magic, Execvals *evp) +{ + long extra; + Mipsexec me; + + /* + * BOOT_MAGIC is sometimes defined like this: + * #define BOOT_MAGIC (0x160<<16) || magic == ((0x160<<16)|3) + * so we can only use it in a fairly stylized manner. + */ + if(magic == BOOT_MAGIC) { + c->offset = 0; /* back up */ + readn(c, &me, sizeof me); + /* if binary is -H1, read an extra long */ + if (l2be(me.amagic) == 0407 && me.nscns == 0) + readn(c, &extra, sizeof extra); + evp->entry = l2be(me.mentry); + evp->textsize = l2be(me.tsize); + evp->datasize = l2be(me.dsize); + return 0; + } else + return -1; +} + +void +main(void) +{ + extern char edata[], end[]; + + m = (Mach*)MACHADDR; + memset(m, 0, sizeof(Mach)); /* clear Mach */ + m->machno = 0; + machaddr[m->machno] = m; + up = nil; + memset(edata, 0, end-edata); /* clear bss */ + + optionsinit("/boot/boot boot"); + plan9iniinit(); + confinit(); + savefpregs(&initfp); + + machinit(); + active.exiting = 0; + active.machs = 1; + + kmapinit(); + xinit(); + screeninit(); + + ckpagemask(PGSZ, BY2PG); + tlbinit(); + machwire(); + + timersinit(); + fmtinit(); + vecinit(); + + normalprint = 1; + print("\nPlan 9\n"); + prcpuid(); + if(PTECACHABILITY == PTENONCOHERWT) + print("caches configured as write-through\n"); +// xsummary(); + + i8259init(); + kbdinit(); + if(conf.monitor) + swcursorinit(); + + pageinit(); + procinit0(); + initseg(); + links(); + chandevreset(); + + swapinit(); + userinit(); + parseboothdr = parsemipsboothdr; + schedinit(); + panic("schedinit returned"); +} + +static void +writeconf(void) +{ + char *p, *q; + int n; + + p = getconfenv(); + + if(waserror()) { + free(p); + nexterror(); + } + + /* convert to name=value\0 format */ + _argc = 1; /* skip _argv[0] */ + _argv = (void*)CONFARGV; + _argv[0] = (long)BOOTARGS; + for(q=p; *q; q++) { + _argv[_argc] = (long)((uintptr)BOOTARGS + (q - p)); + _argc++; + q += strlen(q); + *q = '='; + q += strlen(q); + } + n = q - p + 1; + _env = &_argv[_argc]; + + if(n >= BOOTARGSLEN || (uintptr)_env >= CONFARGV + CONFARGVLEN) + error("kernel configuration too large"); + memmove(BOOTARGS, p, n); + memset(BOOTARGS + n, '\0', BOOTARGSLEN - n); + memset(_env, 0, CONFARGV + CONFARGVLEN - (uintptr)_env); + + poperror(); + free(p); +} + +static void +shutdown(int ispanic) +{ + int ms, once; + + ilock(&active); + if(ispanic) + active.ispanic = ispanic; + else if(m->machno == 0 && (active.machs & (1<machno)) == 0) + active.ispanic = 0; + once = active.machs & (1<machno); + /* + * setting exiting will make hzclock() on each processor call exit(0), + * which calls shutdown(0) and idles non-bootstrap cpus and returns + * on bootstrap processors (to permit a reboot). clearing our bit + * in machs avoids calling exit(0) from hzclock() on this processor. + */ + active.machs &= ~(1<machno); + active.exiting = 1; + iunlock(&active); + + if(once) { + delay(m->machno*1000); /* stagger them */ + iprint("cpu%d: exiting\n", m->machno); + } + spllo(); + for(ms = MAXMACH * 1000; ms > 0; ms -= TK2MS(2)){ + delay(TK2MS(2)); + if(active.machs == 0 && consactive() == 0) + break; + } + delay(100); +} + +void +exit(int ispanic) +{ + int timer; + + delay(1000); + shutdown(ispanic); + timer = 0; + while(active.machs || consactive()) { + if(timer++ > 400) + break; + delay(10); + } + delay(1000); + splhi(); + + setstatus(BEV); + coherence(); + + iprint("exit: awaiting reset\n"); + delay(1000); /* await a reset */ + + if(!active.ispanic) { + iprint("exit: jumping to rom\n"); + *Reset |= Rstcpucold; /* cpu cold reset */ + } + + iprint("exit: looping\n"); + for(;;); +} + +/* + * the new kernel is already loaded at address `code' + * of size `size' and entry point `entry'. + */ +void +reboot(void *entry, void *code, ulong size) +{ + void (*f)(ulong, ulong, ulong, ulong); + + writeconf(); + + /* + * the boot processor is cpu0. execute this function on it + * so that the new kernel has the same cpu0. + */ + if(m->machno != 0) { + procwired(up, 0); + sched(); + } + if(m->machno != 0) + print("on cpu%d (not 0)!\n", m->machno); + + if(code == nil || entry == nil) + exit(1); + + shutdown(0); + + /* + * should be the only processor running now + */ + iprint("reboot: entry %#p code %#p size %ld\n", entry, code, size); + iprint("code[0] = %#lux\n", *(ulong *)code); + + /* turn off buffered serial console */ + serialoq = nil; + kprintoq = nil; + screenputs = nil; + + /* shutdown devices */ + chandevshutdown(); + + splhi(); + intrshutdown(); + + /* setup reboot trampoline function */ + f = (void*)REBOOTADDR; +// memmove(f, rebootcode, sizeof(rebootcode)); +// icflush(f, sizeof(rebootcode)); + + setstatus(BEV); /* also, kernel mode, no interrupts */ + coherence(); + + /* off we go - never to return */ + (*f)((ulong)entry, (ulong)code, size, _argc); + + panic("loaded kernel returned!"); +} + +/* + * stub for ../port/devpnp.c + */ +int +isaconfig(char *class, int ctlrno, ISAConf *isa) +{ + USED(class, ctlrno, isa); + return 0; +} --- /dev/null +++ /sys/src/9/loongson64/mem.h @@ -0,0 +1,350 @@ +/* + * Memory and machine-specific definitions. Used in C and assembler. + */ + +/* + * Sizes + */ + +#define BI2BY 8 /* bits per byte */ +#define BI2WD 32 /* bits per word */ +#define BY2WD 8 /* bytes per word */ +#define BY2V 8 /* bytes per vlong */ + +#define MAXBY2PG (16*1024) /* rounding for UTZERO in executables; see mkfile */ +#define UTROUND(t) ROUNDUP((t), MAXBY2PG) + +#define BIGPAGES /* use 16K page */ +#ifndef BIGPAGES +#define BY2PG 4096 /* bytes per page */ +#define PGSHIFT 12 /* log2(BY2PG) */ +#define PGSZ PGSZ4K +#define MACHSIZE (2*BY2PG) +#else +#define BY2PG (16*1024) /* bytes per page */ +#define PGSHIFT 14 /* log2(BY2PG) */ +#define PGSZ PGSZ16K +#define MACHSIZE (2*BY2PG) +#endif + +#define KSTACK 8192 /* Size of kernel stack */ +#define WD2PG (BY2PG/BY2WD) /* words per page */ + +#define MAXMACH 1 /* max # cpus system can run; see active.machs */ +#define STACKALIGN(sp) ((sp) & ~7) /* bug: assure with alloc */ +#define BLOCKALIGN 16 +#define CACHELINESZ 32 /* loongson 2e */ +#define ICACHESIZE (64*1024) /* loongson 2e */ +#define DCACHESIZE (64*1024) /* loongson 2e */ +#define SCACHESIZE (512*1024) /* L2 cache, loongson 2e */ + +#define MASK(w) FMASK(0, w) + +/* + * Time + */ +#define HZ 100 /* clock frequency */ +#define MS2HZ (1000/HZ) /* millisec per clock tick */ +#define TK2SEC(t) ((t)/HZ) /* ticks to seconds */ + +/* + * CP0 registers + */ + +#define INDEX 0 +#define RANDOM 1 +#define TLBPHYS0 2 /* aka ENTRYLO0, 64 bit */ +#define TLBPHYS1 3 /* aka ENTRYLO1, 64 bit */ +#define CONTEXT 4 /* 64 bit */ +#define PAGEMASK 5 +#define WIRED 6 +#define BADVADDR 8 /* 64 bit */ +#define COUNT 9 +#define TLBVIRT 10 /* aka ENTRYHI, 64 bit */ +#define COMPARE 11 +#define STATUS 12 +#define CAUSE 13 +#define EPC 14 /* 64 bit */ +#define PRID 15 +#define CONFIG 16 +#define LLADDR 17 +#define WATCHLO 18 +#define WATCHHI 19 +#define XCONTEXT 20 /* 64 bit */ +#define DIAGNOSE 22 /* loongson 2e */ +#define PERFCTL 24 +#define PERFCOUNT 25 +#define CACHEECC 26 +#define CACHEERR 27 +#define TAGLO 28 +#define TAGHI 29 +#define ERROREPC 30 /* 64 bit */ + +/* + * M(STATUS) bits + */ +#define KMODEMASK 0x0000001f +#define IE 0x00000001 /* master interrupt enable */ +#define EXL 0x00000002 /* exception level */ +#define ERL 0x00000004 /* error level */ +#define KSUPER 0x00000008 +#define KUSER 0x00000010 +#define KSU 0x00000018 +#define UX 0x00000020 +#define SX 0x00000040 +#define KX 0x00000080 +#define INTMASK 0x0000ff00 +#define INTR0 0x00000100 /* interrupt enable bits */ +#define INTR1 0x00000200 +#define INTR2 0x00000400 +#define INTR3 0x00000800 +#define INTR4 0x00001000 +#define INTR5 0x00002000 +#define INTR6 0x00004000 +#define INTR7 0x00008000 +//#define DE 0x00010000 /* not on 24k */ +#define TS 0x00200000 /* tlb shutdown; on 24k at least */ +#define BEV 0x00400000 /* bootstrap exception vectors */ +#define RE 0x02000000 /* reverse-endian in user mode */ +#define FR 0x04000000 /* enable 32 FP regs */ +#define CU0 0x10000000 +#define CU1 0x20000000 /* FPU enable */ + +/* + * M(CONFIG) bits + */ + +#define CFG_K0 7 /* kseg0 cachability */ + +/* + * M(CAUSE) bits + */ + +#define BD (1<<31) /* last excep'n occurred in branch delay slot */ + +/* + * Exception codes + */ +#define EXCMASK 0x1f /* mask of all causes */ +#define CINT 0 /* external interrupt */ +#define CTLBM 1 /* TLB modification: store to unwritable page */ +#define CTLBL 2 /* TLB miss (load or fetch) */ +#define CTLBS 3 /* TLB miss (store) */ +#define CADREL 4 /* address error (load or fetch) */ +#define CADRES 5 /* address error (store) */ +#define CBUSI 6 /* bus error (fetch) */ +#define CBUSD 7 /* bus error (data load or store) */ +#define CSYS 8 /* system call */ +#define CBRK 9 /* breakpoint */ +#define CRES 10 /* reserved instruction */ +#define CCPU 11 /* coprocessor unusable */ +#define COVF 12 /* arithmetic overflow */ +#define CTRAP 13 /* trap */ +#define CVCEI 14 /* virtual coherence exception (instruction) */ +#define CFPE 15 /* floating point exception */ +#define CTLBRI 19 /* tlb read-inhibit */ +#define CTLBXI 20 /* tlb execute-inhibit */ +#define CWATCH 23 /* watch exception */ +#define CMCHK 24 /* machine checkcore */ +#define CCACHERR 30 /* cache error */ +#define CVCED 31 /* virtual coherence exception (data) */ + +/* + * M(CACHEECC) a.k.a. ErrCtl bits + */ +#define PE (1<<31) +#define LBE (1<<25) +#define WABE (1<<24) + +/* + * FCR31 bits, complement to u.h + */ +#define FPCINEX (1<<12) /* causes */ +#define FPCOVFL (1<<13) +#define FPCUNFL (1<<14) +#define FPCZDIV (1<<15) +#define FPCINVAL (1<<16) +#define FPUNIMP (1<<17) +#define FPEXCMASK (0x3f<<12) + +#define FPFLUSH (1<<24) + +/* + * Trap vectors + */ + +#define UTLBMISS (KSEG0+0x000) +#define XEXCEPTION (KSEG0+0x080) +#define CACHETRAP (KSEG0+0x100) +#define EXCEPTION (KSEG0+0x180) + +/* + * Magic registers + */ + +#define USER 24 /* R24 is up-> */ +#define MACH 25 /* R25 is m-> */ + +/* + * offsets in ureg.h for l.s + */ +#define Ureg_status (Uoffset+0) +#define Ureg_pc (Uoffset+8) +#define Ureg_sp (Uoffset+16) +#define Ureg_cause (Uoffset+24) +#define Ureg_badvaddr (Uoffset+32) +#define Ureg_tlbvirt (Uoffset+40) + +#define Ureg_hi (Uoffset+48) +#define Ureg_lo (Uoffset+56) +#define Ureg_r31 (Uoffset+64) +#define Ureg_r30 (Uoffset+72) +#define Ureg_r28 (Uoffset+80) +#define Ureg_r27 (Uoffset+88) +#define Ureg_r26 (Uoffset+96) +#define Ureg_r25 (Uoffset+104) +#define Ureg_r24 (Uoffset+112) +#define Ureg_r23 (Uoffset+120) +#define Ureg_r22 (Uoffset+128) +#define Ureg_r21 (Uoffset+136) +#define Ureg_r20 (Uoffset+144) +#define Ureg_r19 (Uoffset+152) +#define Ureg_r18 (Uoffset+160) +#define Ureg_r17 (Uoffset+168) +#define Ureg_r16 (Uoffset+176) +#define Ureg_r15 (Uoffset+184) +#define Ureg_r14 (Uoffset+192) +#define Ureg_r13 (Uoffset+200) +#define Ureg_r12 (Uoffset+208) +#define Ureg_r11 (Uoffset+216) +#define Ureg_r10 (Uoffset+224) +#define Ureg_r9 (Uoffset+232) +#define Ureg_r8 (Uoffset+240) +#define Ureg_r7 (Uoffset+248) +#define Ureg_r6 (Uoffset+256) +#define Ureg_r5 (Uoffset+264) +#define Ureg_r4 (Uoffset+272) +#define Ureg_r3 (Uoffset+280) +#define Ureg_r2 (Uoffset+288) +#define Ureg_r1 (Uoffset+296) + +/* ch and carrera used these defs */ + /* Sizeof(Ureg) + (R5,R6) + 16 bytes slop + retpc + ur */ +// #define UREGSIZE ((Ureg_r1+4-Uoffset) + 2*BY2V + 16 + BY2WD + BY2WD) +// #define Uoffset 8 + +// #define UREGSIZE (Ureg_r1 + 4 - Uoffset) /* this ought to work */ +/* loongson 32-bit */ +// #define UREGSIZE ((Ureg_r1+4-Uoffset) + 2*BY2V + 16 + BY2WD + BY2WD) + +#define UREGSIZE ((Ureg_r1+8-Uoffset) + 2*BY2V + 16 + BY2WD + BY2WD) +#define Uoffset 0 +#define Notuoffset 16 + +/* + * MMU + */ +#define PGSZ4K (0x00<<13) +#define PGSZ16K (0x03<<13) +#define PGSZ64K (0x0F<<13) +#define PGSZ256K (0x3F<<13) +#define PGSZ1M (0xFF<<13) +#define PGSZ4M (0x3FF<<13) +#define PGSZ8M (0x7FF<<13) /* not on loongson 2e */ +#define PGSZ16M (0xFFF<<13) +#define PGSZ64M (0x3FFF<<13) /* not on loongson 2e */ +#define PGSZ256M (0xFFFF<<13) /* not on loongson 2e */ + +/* mips address spaces, tlb-mapped unless marked otherwise */ +#define KUSEG 0x0000000000000000ULL /* user process */ +#define KSEG0 0xFFFFFFFF80000000ULL /* kernel (direct mapped, cached) */ +#define KSEG1 0xFFFFFFFFA0000000ULL /* kernel (direct mapped, uncached: i/o) */ +#define KSEG2 0xFFFFFFFFC0000000ULL /* kernel, used for TSTKTOP */ +#define KSEG3 0xFFFFFFFFE0000000ULL /* kernel, used by kmap */ +#define KSEGM 0xFFFFFFFFE0000000ULL /* mask to check which seg */ + +/* + * Fundamental addresses + */ + +#define CONFADDR (KSEG0+0x1000) /* just above vectors, only for reboot */ +#define CONFARGV (KSEG0+0x2000) /* used by assembler */ +#define REBOOTADDR (KSEG0+0x3000) +#define MACHADDR (KTZERO-MAXMACH*MACHSIZE) /* Mach structures */ +#define MACHP(n) ((Mach *)(MACHADDR+(n)*MACHSIZE)) +#define ROM 0xFFFFFFFFBFC00000ULL +#define KMAPADDR 0xFFFFFFFFE0000000ULL /* kmap'd addresses */ +#define WIREDADDR 0xFFFFFFFFE2000000ULL /* address wired kernel space */ + +#define PHYSCONS (KSEG1|0x1fd003f8) /* i8250 uart */ + +#define PIDXSHFT 12 +#ifndef BIGPAGES +#define NCOLOR 8 +#define PIDX ((NCOLOR-1)<>PIDXSHFT) % NCOLOR) +#else +/* no cache aliases are possible with pages of 16K or larger */ +#define NCOLOR 1 +#define PIDX 0 +#define getpgcolor(a) 0 +#endif +#define KMAPSHIFT 15 + +#define PTEGLOBL (1<<0) +#define PTEVALID (1<<1) +#define PTEWRITE (1<<2) +#define PTERONLY 0 +#define PTEALGMASK (7<<3) +#define PTENONCOHERWT (0<<3) /* cached, write-through (slower) */ +#define PTEUNCACHED (2<<3) +#define PTENONCOHERWB (3<<3) /* cached, write-back */ +#define PTEUNCACHEDACC (7<<3) +/* rest are reserved on 24k */ +#define PTECOHERXCL (4<<3) +#define PTECOHERXCLW (5<<3) +#define PTECOHERUPDW (6<<3) + +#define PTECACHABILITY PTENONCOHERWB /* loongson 2E only allows this */ + +#define PTEPID(n) (n) +#define PTEMAPMEM (1024*1024) +#define PTEPERTAB (PTEMAPMEM/BY2PG) +#define SEGMAPSIZE 512 +#define SSEGMAPSIZE 16 +#define TLBVIRTMASK 0xC00000FFFFFFFFFFULL + +#define STLBLOG 15 +#define STLBSIZE (1< init.h + +l.$O: $objtype.s +main.$O: errstr.h init.h #reboot.h +devether.$O: ../port/netif.h etherif.h +clock.$O faultmips.$O mmu.$O trap.$O tmp.$O: /$objtype/include/ureg.h + +%.clean:V: + rm -f $stem.c [9bz]$stem [9bz]$stem.gz boot$stem.* *.list + +# override ../port/portmkfile +# create /boot/boot +boot$CONF.out: $CONF print.$O $BOOTDIR/boot.c $BOOTLIB + $BOOTDIR/mkboot $CONF > boot$CONF.c + $CC $CFLAGS boot$CONF.c + $CC $CFLAGS ../boot/printstub.c + $AS c_fcr0.s + $LD -a -o boot$CONF.out -T$UTZERO -R$MAXBY2PG boot$CONF.$O $BOOTLIB printstub.$O c_fcr0.$O > boot$CONF.list --- /dev/null +++ /sys/src/9/loongson64/mmu.c @@ -0,0 +1,503 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "ureg.h" + +#define TLBINVAL(x, pid) puttlbx(x, KSEG0|((x)<<(PGSHIFT+1))|(pid), 0, 0, PGSZ) + +enum { + Debugswitch = 0, + Debughash = 0, +}; + +void dumpstlb(void); + +static ulong ktime[8]; /* only for first 8 cpus */ + +void +tlbinit(void) +{ + int i; + + for(i=0; inext = k+1; + k->next = 0; + unlock(&kmaplock); + + m->ktlbnext = KTLBOFF; +} + +void +kmapinval(void) +{ + int mno, i, curpid; + KMap *k, *next; + uchar *ktlbx; + + if(m->machno < nelem(ktime)) + ktime[m->machno] = MACHP(0)->ticks; + if(m->kactive == 0) + return; + + curpid = PTEPID(TLBPID(tlbvirt())); + ktlbx = m->ktlbx; + for(i = 0; i < NTLB; i++, ktlbx++){ + if(*ktlbx == 0) + continue; + TLBINVAL(i, curpid); + *ktlbx = 0; + } + + mno = m->machno; + for(k = m->kactive; k; k = next) { + next = k->konmach[mno]; + kunmap(k); + } + + m->kactive = 0; + m->ktlbnext = KTLBOFF; +} + +static int +putktlb(KMap *k) +{ + int x; + u64int virt, tlbent[4]; + + virt = k->virt & ~BY2PG | TLBPID(tlbvirt()); + x = gettlbp(virt, tlbent); + if (!m->paststartup) + if (up) { /* startup just ended? */ + tlbroff = KTLBOFF; + setwired(tlbroff); + m->paststartup = 1; + } else if (x < 0) { /* no such entry? use next */ + x = m->ktlbnext++; + if(m->ktlbnext >= tlbroff) + m->ktlbnext = KTLBOFF; + } + if (x < 0) /* no entry for va? overwrite random one */ + x = puttlb(virt, k->phys0, k->phys1); + else + puttlbx(x, virt, k->phys0, k->phys1, PGSZ); + m->ktlbx[x] = 1; + return x; +} + +/* + * Arrange that the KMap'd virtual address will hit the same + * primary cache line as pg->va by making bits 14...12 of the + * tag the same as virtual address. These bits are the index + * into the primary cache and are checked whenever accessing + * the secondary cache through the primary. Violation causes + * a VCE trap. + */ +KMap * +kmap(Page *pg) +{ + int s, printed = 0; + uintptr pte, virt; + KMap *k; + + s = splhi(); + lock(&kmaplock); + + if(kmapfree == 0) { +retry: + unlock(&kmaplock); + kmapinval(); /* try and free some */ + lock(&kmaplock); + if(kmapfree == 0){ + unlock(&kmaplock); + splx(s); + if(printed++ == 0){ + /* using iprint here we get mixed up with other prints */ + print("%d KMAP RETRY %#p ktime %ld %ld %ld %ld %ld %ld %ld %ld\n", + m->machno, getcallerpc(&pg), + ktime[0], ktime[1], ktime[2], ktime[3], + ktime[4], ktime[5], ktime[6], ktime[7]); + delay(200); + } + splhi(); + lock(&kmaplock); + goto retry; + } + } + + k = kmapfree; + kmapfree = k->next; + + k->pg = pg; + /* + * One for the allocation, + * One for kactive + */ + k->pc = getcallerpc(&pg); + k->ref = 2; + k->konmach[m->machno] = m->kactive; + m->kactive = k; + + virt = pg->va; + /* bits 13..12 form the cache virtual index */ + virt &= PIDX; + virt |= KMAPADDR | ((k-kpte)<virt = virt; + pte = PPN(pg->pa)|PTECACHABILITY|PTEGLOBL|PTEWRITE|PTEVALID; + if(virt & BY2PG) { + k->phys0 = PTEGLOBL | PTECACHABILITY; + k->phys1 = pte; + } + else { + k->phys0 = pte; + k->phys1 = PTEGLOBL | PTECACHABILITY; + } + + putktlb(k); + unlock(&kmaplock); + + splx(s); + return k; +} + +void +kunmap(KMap *k) +{ + int s; + + s = splhi(); + if(decref(k) == 0) { + k->virt = 0; + k->phys0 = 0; + k->phys1 = 0; + k->pg = 0; + + lock(&kmaplock); + k->next = kmapfree; + kmapfree = k; +//nfree(); + unlock(&kmaplock); + } + splx(s); +} + +void +kfault(Ureg *ur) /* called from trap() */ +{ + ulong index; + uintptr addr; + KMap *k, *f; + + addr = ur->badvaddr; + index = (addr & ~KSEGM) >> KMAPSHIFT; + if(index >= KPTESIZE) { + dumpregs(ur); + dumptlb(); + dumpstlb(); + panic("kmapfault: %#p", addr); + } + + k = &kpte[index]; + if(k->virt == 0) { + dumptlb(); + dumpstlb(); + panic("kmapfault: unmapped %#p", addr); + } + + for(f = m->kactive; f; f = f->konmach[m->machno]) + if(f == k) + break; + if(f == 0) { + incref(k); + k->konmach[m->machno] = m->kactive; + m->kactive = k; + } + putktlb(k); +} + +struct +{ + u64int va; + u64int pl; + u64int ph; +} wired[NWTLB+1]; /* +1 to avoid zero size if NWTLB==0 */ + +void +machwire(void) +{ + int i; + +// if(m->machno == 0) +// return; + for(i = 0; i < NWTLB; i++) + if(wired[i].va) + puttlbx(i+WTLBOFF, wired[i].va, wired[i].pl, + wired[i].ph, PGSZ); +} + +/* + * Process must be splhi + */ +int +newtlbpid(Proc *p) +{ + int i, s; + Proc **h; + + i = m->lastpid; + h = m->pidproc; + for(s = 0; s < NTLBPID; s++) { + i++; + if(i >= NTLBPID) + i = 1; + if(h[i] == 0) + break; + } + + if(h[i]) + purgetlb(i); + if(h[i] != 0) + panic("newtlb"); + + m->pidproc[i] = p; + p->pidonmach[m->machno] = i; + m->lastpid = i; + + return i; +} + +void +mmuswitch(Proc *p) +{ + int tp; + static char lasttext[32]; + + if(Debugswitch && !p->kp){ + if(strncmp(lasttext, p->text, sizeof lasttext) != 0) + iprint("[%s]", p->text); + strncpy(lasttext, p->text, sizeof lasttext); + } + + if(p->newtlb) { + memset(p->pidonmach, 0, sizeof p->pidonmach); + p->newtlb = 0; + } + tp = p->pidonmach[m->machno]; + if(tp == 0) + tp = newtlbpid(p); + puttlbx(0, KSEG0|PTEPID(tp), 0, 0, PGSZ); +} + +void +mmurelease(Proc *p) +{ + memset(p->pidonmach, 0, sizeof p->pidonmach); +} + +void +putmmu(u64int tlbvirt, u64int tlbphys, Page *pg) +{ + short tp; + char *ctl; + Softtlb *entry; + int s; + + s = splhi(); + tp = up->pidonmach[m->machno]; + if(tp == 0) + tp = newtlbpid(up); + + tlbvirt |= PTEPID(tp); + if((tlbphys & PTEALGMASK) != PTEUNCACHED) { + tlbphys &= ~PTEALGMASK; + tlbphys |= PTECACHABILITY; + } + + entry = putstlb(tlbvirt, tlbphys); + puttlb(entry->virt, entry->phys0, entry->phys1); + + ctl = &pg->cachectl[m->machno]; + switch(*ctl) { + case PG_TXTFLUSH: + icflush((void*)pg->va, BY2PG); + *ctl = PG_NOFLUSH; + break; + case PG_DATFLUSH: + dcflush((void*)pg->va, BY2PG); + *ctl = PG_NOFLUSH; + break; + case PG_NEWCOL: + cleancache(); /* Too expensive */ + *ctl = PG_NOFLUSH; + break; + } + splx(s); +} + +void +purgetlb(int pid) +{ + int i, mno; + Proc *sp, **pidproc; + Softtlb *entry, *etab; + + m->tlbpurge++; + + /* + * find all pid entries that are no longer used by processes + */ + mno = m->machno; + pidproc = m->pidproc; + for(i=1; ipidonmach[mno] != i) + pidproc[i] = 0; + } + + /* + * shoot down the one we want + */ + sp = pidproc[pid]; + if(sp != 0) + sp->pidonmach[mno] = 0; + pidproc[pid] = 0; + + /* + * clean out all dead pids from the stlb; + */ + entry = m->stb; + for(etab = &entry[STLBSIZE]; entry < etab; entry++) + if(pidproc[TLBPID(entry->virt)] == 0) + entry->virt = 0; + + /* + * clean up the hardware + */ + for(i=tlbroff; inewtlb = 1; + mmuswitch(up); + splx(s); +} + +/* tlbvirt also has TLBPID() in its low byte as the asid */ +Softtlb* +putstlb(u64int tlbvirt, u64int tlbphys) +{ + int odd; + Softtlb *entry; + + /* identical calculation in l.s/utlbmiss */ + entry = &m->stb[stlbhash(tlbvirt)]; + odd = tlbvirt & BY2PG; /* even/odd bit */ + tlbvirt &= ~BY2PG; /* zero even/odd bit */ + if(entry->virt != (tlbvirt & TLBVIRTMASK)) { /* not my entry? overwrite it */ + if(entry->virt != 0) { + m->hashcoll++; + if (Debughash) + iprint("putstlb: hash collision: %#lx old virt " + "%#p new virt %#p page %#llux\n", + entry - m->stb, entry->virt, tlbvirt, + tlbvirt >> (PGSHIFT+1)); + } + entry->virt = tlbvirt & TLBVIRTMASK; + entry->phys0 = 0; + entry->phys1 = 0; + } + + if(odd) + entry->phys1 = tlbphys; + else + entry->phys0 = tlbphys; + + if(entry->phys0 == 0 && entry->phys1 == 0) + entry->virt = 0; + + return entry; +} + +void +checkmmu(ulong, ulong) +{ +} + +void +countpagerefs(ulong*, int) +{ +} + +/* + * Return the number of bytes that can be accessed via KADDR(pa). + * If pa is not a valid argument to KADDR, return 0. + */ +uintptr +cankaddr(uintptr pa) +{ + if(pa >= KZERO || pa >= memsize) + return 0; + return memsize - pa; +} + +/* print tlb entries for debug */ +#define TLBPHYS(x) ((((x)&~0x3f)<<6) | ((x)&0x3f)) /* phys addr & flags */ + +void +dumptlb(void) +{ + Softtlb entry; + int i; + + iprint("dump tlb\n"); + for(i=0; istb[i]; + if(entry->virt != 0) + iprint("stlb index %2d, virt %.16llux, phys0 %.16llux, phys1 %.16llux\n", + i, entry->virt, TLBPHYS(entry->phys0), TLBPHYS(entry->phys1)); + } +} --- /dev/null +++ /sys/src/9/loongson64/mouse.c @@ -0,0 +1 @@ +#include "../loongson/mouse.c" --- /dev/null +++ /sys/src/9/loongson64/pci.c @@ -0,0 +1 @@ +#include "../loongson/pci.c" --- /dev/null +++ /sys/src/9/loongson64/screen.c @@ -0,0 +1 @@ +#include "../loongson/screen.c" --- /dev/null +++ /sys/src/9/loongson64/screen.h @@ -0,0 +1 @@ +#include "../loongson/screen.h" --- /dev/null +++ /sys/src/9/loongson64/spim64.s @@ -0,0 +1,77 @@ +#undef MASK +#define MASK(w) ((1ull<<(w))-1) + +#define NOP NOR R0, R0, R0 +#define CONST(x,r) MOVW $((x)&0xffff0000), r; OR $((x)&0xffff), r + +#define LL(base, rt) WORD $((060<<26)|((base)<<21)|((rt)<<16)) +#define SC(base, rt) WORD $((070<<26)|((base)<<21)|((rt)<<16)) + +#define ERET WORD $0x42000018; NOP +#define SYNC WORD $0xf /* all sync barriers */ +#define EHB NOP /* seems work */ +#define WAIT /* loongson 2e does not have this */ + +#define JALR(d, r) WORD $(((r)<<21)|((d)<<11)|9); NOP + +#define TOKSEG0(r) MOVV $ret0(SB), R(r); JALR(22, r) +#define TOKSEG1(r) MOVV $ret1(SB), R(r); OR $KSEG1, R(r); JALR(22, r) + +/* + * cache manipulation + */ + +#define CACHE BREAK /* overloaded op-code */ + +#define PI R((0 /* primary I cache */ +#define PD R((1 /* primary D cache */ +#define TD R((2 /* tertiary I/D cache */ +#define SD R((3 /* secondary combined I/D cache */ + +#define IWBI (0<<2))) /* index write-back invalidate */ +#define ILT (1<<2))) /* index load tag */ +#define IST (2<<2))) /* index store tag */ +/* #define CDE (3<<2))) /* create dirty exclusive */ +#define HINV (4<<2))) /* hit invalidate */ +#define HWBI (5<<2))) /* hit write back invalidate */ +#define ILD (6<<2))) /* index load data (loongson 2e) */ +#define ISD (7<<2))) /* index store data (loongson 2e) */ + +/* + * serial output + */ + +#define PUTC(c, r1, r2) CONST(PHYSCONS, r1); MOVV $(c), r2; MOVBU r2, (r1); NOP + +#define DELAY(r) \ + CONST(34000000, r); \ + SUBU $1, r; \ + BNE r, -1(PC) + +/* + * FCR31 bits copied from u.h + */ + +/* FCR (FCR31) */ +#define FPINEX (1<<7) /* enables */ +#define FPUNFL (1<<8) +#define FPOVFL (1<<9) +#define FPZDIV (1<<10) +#define FPINVAL (1<<11) +#define FPRNR (0<<0) /* rounding modes */ +#define FPRZ (1<<0) +#define FPRPINF (2<<0) +#define FPRNINF (3<<0) +#define FPRMASK (3<<0) +#define FPPEXT 0 +#define FPPSGL 0 +#define FPPDBL 0 +#define FPPMASK 0 +#define FPCOND (1<<23) + +/* FSR (also FCR31) */ +#define FPAINEX (1<<2) /* flags */ +#define FPAOVFL (1<<4) +#define FPAUNFL (1<<3) +#define FPAZDIV (1<<5) +#define FPAINVAL (1<<6) --- /dev/null +++ /sys/src/9/loongson64/tmp.c @@ -0,0 +1,34 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "ureg.h" + +// archxxx.c +#include "../port/netif.h" +#include "etherif.h" + +int +archether(unsigned ctlrno, Ether *ether) +{ + switch(ctlrno) { + case 0: + ether->type = "rtl8139"; + ether->ctlrno = ctlrno; + ether->irq = ILpci; + ether->nopt = 0; + ether->mbps = 100; + return 1; + } + return -1; +} + +// fptrap.c +void +fptrap(Ureg*) +{ + return; +} --- /dev/null +++ /sys/src/9/loongson64/trap.c @@ -0,0 +1,1103 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "ureg.h" +#include "io.h" +#include +#include "../port/error.h" + +#define setstatus(v) /* experiment: delete this to enable recursive traps */ + +typedef struct Handler Handler; + +struct Handler { + void (*handler)(Ureg*, void*); + void *arg; + Handler *next; /* at this interrupt level */ + ulong intrs; +}; + +ulong offintrs; +ulong intrcauses[ILmax+1]; + +int intr(Ureg*); +void kernfault(Ureg*, int); +void noted(Ureg*, Ureg**, ulong); +void rfnote(Ureg**); + +char *excname[] = +{ + "trap: external interrupt", + "trap: TLB modification (store to unwritable)", + "trap: TLB miss (load or fetch)", + "trap: TLB miss (store)", + "trap: address error (load or fetch)", + "trap: address error (store)", + "trap: bus error (fetch)", + "trap: bus error (data load or store)", + "trap: system call", + "breakpoint", + "trap: reserved instruction", + "trap: coprocessor unusable", + "trap: arithmetic overflow", + "trap: TRAP exception", + "trap: VCE (instruction)", + "trap: floating-point exception", + "trap: coprocessor 2 implementation-specific", /* used as sys call for debugger */ + "trap: corextend unusable", + "trap: precise coprocessor 2 exception", + "trap: TLB read-inhibit", + "trap: TLB execute-inhibit", + "trap: undefined 21", + "trap: undefined 22", + "trap: WATCH exception", + "trap: machine checkcore", + "trap: undefined 25", + "trap: undefined 26", + "trap: undefined 27", + "trap: undefined 28", + "trap: undefined 29", + "trap: cache error", + "trap: VCE (data)", +}; + +char *fpcause[] = +{ + "inexact operation", + "underflow", + "overflow", + "division by zero", + "invalid operation", +}; + +struct { + char *name; + uint off; +} regname[] = { + "STATUS", Ureg_status, + "PC", Ureg_pc, + "SP", Ureg_sp, + "CAUSE",Ureg_cause, + "BADADDR", Ureg_badvaddr, + "TLBVIRT", Ureg_tlbvirt, + "HI", Ureg_hi, + "LO", Ureg_lo, + "R31", Ureg_r31, + "R30", Ureg_r30, + "R28", Ureg_r28, + "R27", Ureg_r27, + "R26", Ureg_r26, + "R25", Ureg_r25, + "R24", Ureg_r24, + "R23", Ureg_r23, + "R22", Ureg_r22, + "R21", Ureg_r21, + "R20", Ureg_r20, + "R19", Ureg_r19, + "R18", Ureg_r18, + "R17", Ureg_r17, + "R16", Ureg_r16, + "R15", Ureg_r15, + "R14", Ureg_r14, + "R13", Ureg_r13, + "R12", Ureg_r12, + "R11", Ureg_r11, + "R10", Ureg_r10, + "R9", Ureg_r9, + "R8", Ureg_r8, + "R7", Ureg_r7, + "R6", Ureg_r6, + "R5", Ureg_r5, + "R4", Ureg_r4, + "R3", Ureg_r3, + "R2", Ureg_r2, + "R1", Ureg_r1, +}; + +static Lock intrlock; +static Handler handlers[ILmax+1]; +static ulong pciintrmask; +static ulong i8259intrmask; + +static char * +ptlb(u64int phys) +{ + static char buf[4][32]; + static int k; + char *p; + + k = (k+1)&3; + p = buf[k]; + p += snprint(p, sizeof buf[k] - (p - buf[k]), "(%#llux %llud ", + (phys<<6) & ~(BY2PG-1), (phys>>3)&7); + if(phys & 4) + *p++ = 'd'; + if(phys & 2) + *p++ = 'v'; + if(phys & 1) + *p++ = 'g'; + *p++ = ')'; + *p = 0; + return buf[k]; +} + +static void +kpteprint(Ureg *ur) +{ + u64int i, tlbstuff[4]; + KMap *k; + + i = (ur->badvaddr & ~(2*BY2PG-1)) | TLBPID(tlbvirt()); + print("tlbvirt=%#llux\n", i); + i = gettlbp(i, tlbstuff); + print("i=%llud v=%#llux p0=%s p1=%s\n", + i, tlbstuff[0], ptlb(tlbstuff[1]), ptlb(tlbstuff[2])); + + i = (ur->badvaddr & ~KMAPADDR)>>15; + if(i > KPTESIZE){ + print("kpte index = %llud ?\n", i); + return; + } + k = &kpte[i]; + print("i=%llud, &k=%#p, k={v=%#p, p0=%s, p1=%s, pg=%#p}\n", + i, k, k->virt, ptlb(k->phys0), ptlb(k->phys1), k->pg); + print("pg={pa=%#p, va=%#p}\n", k->pg->pa, k->pg->va); +} + +void +kvce(Ureg *ur, int ecode) +{ + char c; + Pte **p; + Page **pg; + Segment *s; + uintptr addr, soff; + + c = 'D'; + if(ecode == CVCEI) + c = 'I'; + print("Trap: VCE%c: addr=%#p\n", c, ur->badvaddr); + if((ur->badvaddr & KSEGM) == KSEG3) { + kpteprint(ur); + return; + } + if(up && !(ur->badvaddr & KSEGM)) { + addr = ur->badvaddr; + s = seg(up, addr, 0); + if(s == 0){ + print("kvce: no seg for %#p\n", addr); + for(;;) + ; + } + addr &= ~(BY2PG-1); + soff = addr - s->base; + p = &s->map[soff/PTEMAPMEM]; + if(*p){ + pg = &(*p)->pages[(soff&(PTEMAPMEM-1))/BY2PG]; + if(*pg) + print("kvce: pa=%#p, va=%#p\n", + (*pg)->pa, (*pg)->va); + else + print("kvce: no *pg\n"); + }else + print("kvce: no *p\n"); + } +} + +/* prepare to go to user space */ +void +kexit(Ureg*) +{ + Tos *tos; + + /* precise time accounting, kernel exit */ + tos = (Tos*)(USTKTOP-sizeof(Tos)); + tos->kcycles += fastticks(&tos->cyclefreq) - up->kentry; + tos->pcycles = up->pcycles; + tos->pid = up->pid; +} + +char* +fpexcname(Ureg *ur, ulong fcr31, char *buf, uint size) +{ + int i; + char *s; + uintptr fppc; + + fppc = ur->pc; + if(ur->cause & BD) /* branch delay */ + fppc += 4; + s = 0; + if(fcr31 & FPUNIMP) + s = "unimplemented operation"; + else { + fcr31 >>= 7; /* trap enable bits */ + fcr31 &= (fcr31>>5); /* anded with exceptions */ + for(i=0; i<5; i++) + if(fcr31 & (1<cause>>2)&EXCMASK; + user = ur->status&KUSER; + if (ur->cause & TS) + panic("trap: tlb shutdown"); + + fpchk = 0; + if(user){ + up->dbgreg = ur; + cycles(&up->kentry); + if(up && up->fpstate == FPactive) { + if((ur->status & CU1) == 0) + panic("FPactive but no CU1"); + ur->status &= ~CU1; + up->fpstate = FPinactive; + savefpregs(&up->fpsave); + } + } + + if (up && (char*)(ur) - up->kstack < 1024 && dumps++ == 0) { + iprint("trap: proc %ld kernel stack getting full\n", up->pid); + dumpregs(ur); + dumpstack(); + } + if (up == nil && + (char*)(ur) - (char*)m->stack < 1024 && dumps++ == 0) { + iprint("trap: cpu%d kernel stack getting full\n", m->machno); + dumpregs(ur); + dumpstack(); + } + +// splhi(); /* for the experiment: make it explicit */ + /* clear EXL in status */ + setstatus(getstatus() & ~EXL); + + clockintr = 0; + switch(ecode){ + case CINT: + clockintr = intr(ur); + break; + + case CFPE: + fptrap(ur); + clrfpintr(); + fpchk = 1; + break; + + case CTLBM: + case CTLBL: + case CTLBS: + /* user tlb entries assumed not overwritten during startup */ + if(up == 0) + kernfault(ur, ecode); + + if(!user && (ur->badvaddr & KSEGM) == KSEG3) { + kfault(ur); + break; + } + x = up->insyscall; + up->insyscall = 1; + spllo(); + faultmips(ur, user, ecode); + up->insyscall = x; + break; + + case CVCEI: + case CVCED: + kvce(ur, ecode); + goto Default; + + case CWATCH: + if(!user) + panic("watchpoint trap from kernel mode pc=%#p", + ur->pc); + //fpwatch(ur); XXX + break; + + case CCPU: + cop = (ur->cause>>28)&3; + if(user && up && cop == 1) { + if(up->fpstate & FPillegal) { + /* someone used floating point in a note handler */ + postnote(up, 1, + "sys: floating point in note handler", + NDebug); + break; + } + if(up->fpstate == FPinit) { + up->fpstate = FPinactive; + fpfcr31 = up->fpsave.fpstatus; + up->fpsave = initfp; + up->fpsave.fpstatus = fpfcr31; + break; + } + if(up->fpstate == FPinactive) + break; + } + /* Fallthrough */ + + Default: + default: + if(user) { + spllo(); + snprint(buf, sizeof buf, "sys: %s", excname[ecode]); + postnote(up, 1, buf, NDebug); + break; + } + if (ecode == CADREL || ecode == CADRES) + iprint("kernel addr exception for va %#p pid %#ld %s\n", + ur->badvaddr, (up? up->pid: 0), + (up? up->text: "")); + print("cpu%d: kernel %s pc=%#p\n", + m->machno, excname[ecode], ur->pc); + dumpregs(ur); + dumpstack(); + if(m->machno == 0) + spllo(); + exit(1); + } + + if(fpchk) { + fpfcr31 = up->fpsave.fpstatus; + if((fpfcr31>>12) & ((fpfcr31>>7)|0x20) & 0x3f) { + spllo(); + fpexcep = fpexcname(ur, fpfcr31, buf1, sizeof buf1); + snprint(buf, sizeof buf, "sys: fp: %s", fpexcep); + postnote(up, 1, buf, NDebug); + } + } + + splhi(); + + /* delaysched set because we held a lock or because our quantum ended */ + if(up && up->delaysched && clockintr){ + sched(); + splhi(); + } + + if(user){ + notify(ur); + if(up->fpstate == FPinactive) { + restfpregs(&up->fpsave, up->fpsave.fpstatus&~FPEXCMASK); + up->fpstate = FPactive; + ur->status |= CU1; + } + kexit(ur); + } + + /* restore EXL in status */ + setstatus(getstatus() | EXL); +} + +/* periodically zero all the interrupt counts */ +static void +resetcounts(void) +{ + int i; + Handler *hp; + + ilock(&intrlock); + for (i = 0; i < nelem(handlers); i++) + for (hp = &handlers[i]; hp != nil; hp = hp->next) + hp->intrs = 0; + iunlock(&intrlock); +} + +/* + * set handlers + */ +void +intrenable(int irq, void (*h)(Ureg*, void*), void *arg, int subirq) +{ + Handler *hp; + static int resetclock; + + if (h == nil) + panic("intrenable: nil handler intr %d", irq); + if(irq < ILmin || irq >= nelem(handlers)) + panic("intrenable: bad handler intr %d %#p", irq, h); + + hp = &handlers[irq]; + ilock(&intrlock); + if (hp->handler != nil) { /* occupied? */ + /* add a new one at the end of the chain */ + for (; hp->next != nil; hp = hp->next) + ; + hp->next = smalloc(sizeof *hp); + hp = hp->next; + hp->next = nil; + } + hp->handler = h; + hp->arg = arg; + iunlock(&intrlock); + + if (irq == ILpci) { // enable pci sub-interrupt + *Pciintrsts = 0; + *Pciintrenset = 1 << subirq; + coherence(); + pciintrmask |= 1 << subirq; + } + + if (irq == IL8259) + { + i8259enable(subirq); + i8259intrmask |= 1 << subirq; + } + + intron(1 << (ILshift + irq)); + if (!resetclock) { + resetclock = 1; + addclock0link(resetcounts, 100); + } +} + +void +intrshutdown(void) +{ + introff(INTMASK); +} + +static void +jabberoff(Ureg *ur, int irq, ulong bit) +{ + introff(bit); /* interrupt off now ... */ + if (ur) + ur->status &= ~bit; /* ... and upon return */ + offintrs |= bit; + iprint("irq %d jabbering; shutting it down\n", irq); +} + +ulong +pollall(Ureg *ur, ulong cause) /* must be called splhi */ +{ + int i, intrs, sts, subirq; + ulong bit; + Handler *hp; + + /* exclude clock and sw intrs */ + intrs = cause & (INTR6|INTR5|INTR4|INTR3|INTR2) & getstatus(); + if(intrs == 0) + return cause; + + ilock(&intrlock); + for (i = ILmax; i >= ILmin; i--) { + bit = 1 << (ILshift + i); + if (!(intrs & bit)) + continue; + intrcauses[i]++; + for (hp = &handlers[i]; hp != nil; hp = hp->next) + if (hp->handler) { + if (i == ILpci) { + sts = *Pciintrsts & *Pciintren; + if((sts & pciintrmask) == 0) + continue; + // XXX need to clear sub-intr bits ? + //*Pciintrsts &= ~(1 << Pciintrether); + *Pciintrsts = 0; + } + + if (i == IL8259) { + subirq = i8259intack(); + if((1 << subirq) & i8259intrmask == 0) + continue; + i8259isr(subirq); + } + SET(subirq); USED(subirq); + + (*hp->handler)(ur, hp->arg); + splhi(); + if (++hp->intrs > 25000) { + jabberoff(ur, i, bit); + intrs &= ~bit; + hp->intrs = 0; + } + } else if (ur) + iprint("no handler for interrupt %d\n", i); + cause &= ~bit; + } + iunlock(&intrlock); + return cause; +} + +int +intr(Ureg *ur) +{ + int clockintr; + ulong cause; + + m->intr++; + clockintr = 0; + /* + * ignore interrupts that we have disabled, even if their cause bits + * are set. + */ + cause = ur->cause & ur->status & INTMASK; + cause &= ~(INTR1|INTR0); /* ignore sw interrupts */ +// if(cause == 0) +// print("spurious interrupt\n"); + if(cause & INTR7){ + clock(ur); + intrcauses[ILclock]++; + cause &= ~(1 << (ILclock + ILshift)); + clockintr = 1; + } + cause = pollall(ur, cause); + if(cause){ + print("intr: cause %#lux not handled\n", cause); + exit(1); + } + + /* preemptive scheduling */ + if(up && !clockintr) + preempted(); + /* if it was a clockintr, sched will be called at end of trap() */ + return clockintr; +} + +void +kernfault(Ureg *ur, int code) +{ + print("panic: kfault %s badvaddr=%#p", excname[code], ur->badvaddr); + kpteprint(ur); + print("u=%#p status=%#lux pc=%#p sp=%#p\n", + up, (ulong)ur->status, ur->pc, ur->sp); + delay(500); + panic("kfault"); +} + +static void +getpcsp(uintptr *pc, uintptr *sp) +{ + *pc = getcallerpc(&pc); + *sp = (uintptr)&pc-BY2WD; +} + +void +callwithureg(void (*fn)(Ureg*)) +{ + Ureg ureg; + + memset(&ureg, 0, sizeof ureg); + getpcsp((uintptr*)&ureg.pc, (uintptr*)&ureg.sp); + ureg.r31 = getcallerpc(&fn); + fn(&ureg); +} + +static void +_dumpstack(Ureg *ureg) +{ + ulong i; + uintptr l, v, top; + extern ulong etext; + + if(up == 0) + return; + + print("ktrace /kernel/path %#.8lux %#.8lux %#.8lux\n", + (ulong)ureg->pc, (ulong)ureg->sp, (ulong)ureg->r31); + top = (uintptr)up->kstack + KSTACK; + i = 0; + for(l=ureg->sp; l < top; l += BY2WD) { + v = *(uintptr*)l; + if(KTZERO < v && v < (uintptr)&etext) { + iprint("%#.8lux=%#.8lux ", (ulong)l, (ulong)v); + if((++i%4) == 0){ + print("\n"); + delay(200); + } + } + } + print("\n"); +} + +void +dumpstack(void) +{ + callwithureg(_dumpstack); +} + +static u64int +R(Ureg *ur, int i) +{ + uchar *s; + + s = (uchar*)ur; + return *(u64int*)(s + regname[i].off - Uoffset); +} + +void +dumpregs(Ureg *ur) +{ + int i; + + if(up) + print("registers for %s %lud\n", up->text, up->pid); + else + print("registers for kernel\n"); + + delay(200); + for(i = 0; i < nelem(regname); i += 2) + { + print("%s\t%#.16llux\t%s\t%#.16llux\n", + regname[i].name, R(ur, i), + regname[i+1].name, R(ur, i+1)); +// delay(100); + } +} + +int +notify(Ureg *ur) +{ + int l, s; + uintptr sp; + Note *n; + + if(up->procctl) + procctl(up); + if(up->nnote == 0) + return 0; + + s = spllo(); + qlock(&up->debug); + up->fpstate |= FPillegal; + up->notepending = 0; + n = &up->note[0]; + if(strncmp(n->msg, "sys:", 4) == 0) { + l = strlen(n->msg); + if(l > ERRMAX-23) /* " pc=0x1234567890abcdef\0" */ + l = ERRMAX-23; + + seprint(n->msg+l, &n->msg[sizeof n->msg], " pc=%#p", ur->pc); + } + + if(n->flag != NUser && (up->notified || up->notify==0)) { + if(n->flag == NDebug) + pprint("suicide: %s\n", n->msg); + + qunlock(&up->debug); + pexit(n->msg, n->flag!=NDebug); + } + + if(up->notified) { + qunlock(&up->debug); + splx(s); + return 0; + } + + if(!up->notify) { + qunlock(&up->debug); + pexit(n->msg, n->flag!=NDebug); + } + sp = ur->usp - sizeof(Ureg) - BY2WD; /* libc adds BY2WD to usp */ + + if(!okaddr((uintptr)up->notify, BY2WD, 0) || + !okaddr(sp-ERRMAX-4*BY2WD, sizeof(Ureg)+ERRMAX+4*BY2WD, 1)) { + pprint("suicide: bad address or sp in notify\n"); + qunlock(&up->debug); + pexit("Suicide", 0); + } + + memmove((Ureg*)sp, ur, sizeof(Ureg)); /* push user regs */ + *(Ureg**)(sp-BY2WD) = up->ureg; /* word under Ureg is old up->ureg */ + up->ureg = (void*)sp; + + sp -= BY2WD+ERRMAX; + memmove((char*)sp, up->note[0].msg, ERRMAX); /* push err string */ + + sp -= 3*BY2WD; + *(uintptr*)(sp+2*BY2WD) = sp+3*BY2WD; /* arg 2 is string */ + ur->r1 = (uintptr)up->ureg; /* arg 1 is ureg* */ + ((uintptr*)sp)[1] = (uintptr)up->ureg; /* arg 1 0(FP) is ureg* */ + ((uintptr*)sp)[0] = 0; /* arg 0 is pc */ + ur->usp = sp; + /* + * arrange to resume at user's handler as if handler(ureg, errstr) + * were being called. + */ + ur->pc = (uintptr)up->notify; + + up->notified = 1; + up->nnote--; + memmove(&up->lastnote, &up->note[0], sizeof(Note)); + memmove(&up->note[0], &up->note[1], up->nnote*sizeof(Note)); + + qunlock(&up->debug); + splx(s); + return 1; +} + +/* + * Check that status is OK to return from note. + */ +int +validstatus(ulong kstatus, ulong ustatus) +{ +// if((kstatus & (INTMASK|KX|SX|UX)) != (ustatus & (INTMASK|KX|SX|UX))) + if((kstatus & INTMASK) != (ustatus & INTMASK)) + return 0; + if((ustatus&(KSU|ERL|EXL|IE)) != (KUSER|EXL|IE)) + return 0; + if(ustatus & (0xFFFF0000&~CU1)) /* no CU3, CU2, CU0, RP, FR, RE, DS */ + return 0; + return 1; +} + +/* + * Return user to state before notify(); called from user's handler. + */ +void +noted(Ureg *kur, Ureg **urp, ulong arg0) +{ + Ureg *nur; + uintptr oureg, sp; + + qlock(&up->debug); + if(arg0!=NRSTR && !up->notified) { + qunlock(&up->debug); + pprint("call to noted() when not notified\n"); + pexit("Suicide", 0); + } + up->notified = 0; + + up->fpstate &= ~FPillegal; + + nur = up->ureg; + + oureg = (uintptr)nur; + if((oureg & (BY2WD-1)) + || !okaddr((uintptr)oureg-BY2WD, BY2WD+sizeof(Ureg), 0)){ + pprint("bad up->ureg in noted or call to noted() when not notified\n"); + qunlock(&up->debug); + pexit("Suicide", 0); + } + + if(!validstatus(kur->status, nur->status)) { + qunlock(&up->debug); + pprint("bad noted ureg status %#lux\n", (ulong)nur->status); + pexit("Suicide", 0); + } + + memmove(*urp, up->ureg, sizeof(Ureg)); + switch(arg0) { + case NCONT: + case NRSTR: /* only used by APE */ + if(!okaddr(nur->pc, BY2WD, 0) || !okaddr(nur->usp, BY2WD, 0)){ + pprint("suicide: trap in noted\n"); + qunlock(&up->debug); + pexit("Suicide", 0); + } + up->ureg = (Ureg*)(*(uintptr*)(oureg-BY2WD)); + qunlock(&up->debug); + splhi(); + /* + * the old challenge and carrera ports called rfnote here, + * but newer ports do not, and notes seem to work only + * without this call. + */ + // rfnote(urp); /* return from note with SP=urp */ + break; + + case NSAVE: /* only used by APE */ + if(!okaddr(nur->pc, BY2WD, 0) || !okaddr(nur->usp, BY2WD, 0)){ + pprint("suicide: trap in noted\n"); + qunlock(&up->debug); + pexit("Suicide", 0); + } + qunlock(&up->debug); + sp = oureg-4*BY2WD-ERRMAX; + + splhi(); + (*urp)->sp = sp; + ((uintptr*)sp)[1] = oureg; /* arg 1 0(FP) is ureg* */ + ((uintptr*)sp)[0] = 0; /* arg 0 is pc */ + (*urp)->r1 = oureg; /* arg 1 is ureg* */ + + // rfnote(urp); /* return from note with SP=urp */ + break; + + default: + pprint("unknown noted arg %#lux\n", arg0); + up->lastnote.flag = NDebug; + /* fall through */ + + case NDFLT: + if(up->lastnote.flag == NDebug) + pprint("suicide: %s\n", up->lastnote.msg); + qunlock(&up->debug); + pexit(up->lastnote.msg, up->lastnote.flag!=NDebug); + } +} + +#include "../port/systab.h" + +static Ref goodsyscall; +static Ref totalsyscall; + +static void +sctracesetup(ulong scallnr, uintptr sp, uintptr pc, vlong *startnsp) +{ + if(up->procctl == Proc_tracesyscall){ + /* + * Redundant validaddr. Do we care? + * Tracing syscalls is not exactly a fast path... + * Beware, validaddr currently does a pexit rather + * than an error if there's a problem; that might + * change in the future. + */ + if(sp < (USTKTOP-BY2PG) || sp > (USTKTOP-sizeof(Sargs))) + validaddr(sp, sizeof(Sargs), 0); + + syscallfmt(scallnr, pc, (va_list)sp); + up->procctl = Proc_stopme; + procctl(up); + if(up->syscalltrace) + free(up->syscalltrace); + up->syscalltrace = nil; + *startnsp = todget(nil); + } +} + +static void +sctracefinish(ulong scallnr, uintptr sp, uintptr ret, vlong startns) +{ + int s; + + if(up->procctl == Proc_tracesyscall){ + up->procctl = Proc_stopme; + sysretfmt(scallnr, (va_list)sp, ret, + startns, todget(nil)); + s = splhi(); + procctl(up); + splx(s); + if(up->syscalltrace) + free(up->syscalltrace); + up->syscalltrace = nil; + } +} + +/* + * called directly from assembler, not via trap() + */ +uintptr +syscall(Ureg *aur) +{ + int i; + volatile uintptr ret; + uintptr sp; + ulong scallnr; + vlong startns; + char *e; + Ureg *ur; + + cycles(&up->kentry); + + incref(&totalsyscall); + m->syscall++; + up->insyscall = 1; + ur = aur; + up->pc = ur->pc; + up->dbgreg = aur; + ur->cause = 16<<2; /* for debugging: system call is undef 16 */ + + scallnr = ur->r1; + up->scallnr = ur->r1; + sp = ur->sp; + sctracesetup(scallnr, sp, ur->pc, &startns); + + /* clear EXL in status */ + setstatus(getstatus() & ~EXL); + + if(up->fpstate == FPactive) { + if((ur->status & CU1) == 0) + panic("syscall: FPactive but no CU1"); + up->fpsave.fpstatus = fcr31(); + up->fpstate = FPinit; + ur->status &= ~CU1; + } + spllo(); + + up->nerrlab = 0; + ret = -1; + if(!waserror()) { + if(scallnr >= nsyscall || systab[scallnr] == 0){ + pprint("bad sys call number %ld pc %#p\n", + scallnr, ur->pc); + postnote(up, 1, "sys: bad sys call", NDebug); + error(Ebadarg); + } + + if(sp & (BY2WD-1)){ + pprint("odd sp in sys call pc %#p sp %#p\n", + ur->pc, ur->sp); + postnote(up, 1, "sys: odd stack", NDebug); + error(Ebadarg); + } + + if(sp<(USTKTOP-BY2PG) || sp>(USTKTOP-sizeof(Sargs))) + validaddr(sp, sizeof(Sargs), 0); + + up->s = *(Sargs*)sp; /* spim's libc is different to mips ... */ + up->psstate = sysctab[scallnr]; + + ret = systab[scallnr](up->s.args); + poperror(); + }else{ + /* failure: save the error buffer for errstr */ + e = up->syserrstr; + up->syserrstr = up->errstr; + up->errstr = e; + } + if(up->nerrlab){ + print("bad errstack [%lud]: %d extra\n", scallnr, up->nerrlab); + for(i = 0; i < NERR; i++) + print("sp=%#p pc=%#p\n", + up->errlab[i].sp, up->errlab[i].pc); + panic("error stack"); + } + sctracefinish(scallnr, sp, ret, startns); + + ur->pc += 4; + ur->r1 = ret; + + up->psstate = 0; + up->insyscall = 0; + + if(scallnr == NOTED) { /* ugly hack */ + noted(ur, &aur, *(uintptr*)sp); /* may return */ + ret = ur->r1; + } + incref(&goodsyscall); + splhi(); + if(scallnr!=RFORK && (up->procctl || up->nnote)){ + ur->r1 = ret; /* load up for noted() above */ + notify(ur); + } + /* if we delayed sched because we held a lock, sched now */ + if(up->delaysched) + sched(); + kexit(ur); + + /* restore EXL in status */ + setstatus(getstatus() | EXL); + + return ret; +} + +void +forkchild(Proc *p, Ureg *ur) +{ + Ureg *cur; + + p->sched.sp = (uintptr)p->kstack+KSTACK-UREGSIZE; + p->sched.pc = (uintptr)forkret; + + cur = (Ureg*)(p->sched.sp+2*BY2WD); + memmove(cur, ur, sizeof(Ureg)); + + cur->pc += 4; + + /* Things from bottom of syscall we never got to execute */ + p->psstate = 0; + p->insyscall = 0; +} + +static void +linkproc(void) +{ + spllo(); + up->kpfun(up->kparg); + pexit("kproc exiting", 0); +} + +void +kprocchild(Proc *p, void (*func)(void*), void *arg) +{ + p->sched.pc = (uintptr)linkproc; + p->sched.sp = (uintptr)p->kstack+KSTACK; + + p->kpfun = func; + p->kparg = arg; +} + +/* set up user registers before return from exec() */ +uintptr +execregs(uintptr entry, ulong ssize, ulong nargs) +{ + Ureg *ur; + uintptr *sp; + + sp = (uintptr*)(USTKTOP - ssize); + *--sp = nargs; + + ur = (Ureg*)up->dbgreg; + ur->usp = (uintptr)sp; + ur->pc = entry - 4; /* syscall advances it */ + up->fpsave.fpstatus = initfp.fpstatus; + return USTKTOP-sizeof(Tos); /* address of kernel/user shared data */ +} + +uintptr +userpc(void) +{ + Ureg *ur; + + ur = (Ureg*)up->dbgreg; + return ur->pc; +} + +/* + * This routine must save the values of registers the user is not + * permitted to write from devproc and then restore the saved values + * before returning + */ +void +setregisters(Ureg *xp, char *pureg, char *uva, int n) +{ + ulong status; + + status = xp->status; + memmove(pureg, uva, n); + xp->status = status; +} + +/* + * Give enough context in the ureg to produce a kernel stack for + * a sleeping process + */ +void +setkernur(Ureg *xp, Proc *p) +{ + xp->pc = p->sched.pc; + xp->sp = p->sched.sp; + xp->r24 = (uintptr)p; /* up */ + xp->r31 = (uintptr)sched; +} + +uintptr +dbgpc(Proc *p) +{ + Ureg *ur; + + ur = p->dbgreg; + if(ur == 0) + return 0; + + return ur->pc; +} --- /dev/null +++ /sys/src/9/loongson64/uarti8250.c @@ -0,0 +1,854 @@ +/* + * 8250-like UART + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +enum { + CONSOLE = 0, /* first uart */ + Pollstuckoutput = 1, +}; + +enum { /* registers */ + Rbr = 0, /* Receiver Buffer (RO) */ + Thr = 0, /* Transmitter Holding (WO) */ + Ier = 1, /* Interrupt Enable */ + Iir = 2, /* Interrupt Identification (RO) */ + Fcr = 2, /* FIFO Control (WO) */ + Lcr = 3, /* Line Control */ + Mcr = 4, /* Modem Control */ + Lsr = 5, /* Line Status */ + Msr = 6, /* Modem Status */ + + Scr = 7, /* Scratch Pad */ +// Mdr = 8, /* Mode Def'n (omap rw) */ + Usr = 31, /* Uart Status Register */ + Stickyend, + + Dll = 0, /* Divisor Latch LSB */ + Dlm = 1, /* Divisor Latch MSB */ +}; + +enum { /* Usr */ + Busy = 0x01, +}; + +enum { /* Ier */ + Erda = 0x01, /* Enable Received Data Available */ + Ethre = 0x02, /* Enable Thr Empty */ + Erls = 0x04, /* Enable Receiver Line Status */ + Ems = 0x08, /* Enable Modem Status */ +}; + +enum { /* Iir */ + Ims = 0x00, /* Ms interrupt */ + Ip = 0x01, /* Interrupt Pending (not) */ + Ithre = 0x02, /* Thr Empty */ + Irda = 0x04, /* Received Data Available */ + Irls = 0x06, /* Receiver Line Status */ + Ictoi = 0x0C, /* Character Time-out Indication */ + IirMASK = 0x3F, + Ifena = 0xC0, /* FIFOs enabled */ +}; + +enum { /* Fcr */ + FIFOena = 0x01, /* FIFO enable */ + FIFOrclr = 0x02, /* clear Rx FIFO */ + FIFOtclr = 0x04, /* clear Tx FIFO */ +// FIFOdma = 0x08, + FIFO1 = 0x00, /* Rx FIFO trigger level 1 byte */ + FIFO4 = 0x40, /* 4 bytes */ + FIFO8 = 0x80, /* 8 bytes */ + FIFO14 = 0xC0, /* 14 bytes */ +}; + +enum { /* Lcr */ + Wls5 = 0x00, /* Word Length Select 5 bits/byte */ + Wls6 = 0x01, /* 6 bits/byte */ + Wls7 = 0x02, /* 7 bits/byte */ + Wls8 = 0x03, /* 8 bits/byte */ + WlsMASK = 0x03, + Stb = 0x04, /* 2 stop bits */ + Pen = 0x08, /* Parity Enable */ + Eps = 0x10, /* Even Parity Select */ + Stp = 0x20, /* Stick Parity */ + Brk = 0x40, /* Break */ + Dlab = 0x80, /* Divisor Latch Access Bit */ +}; + +enum { /* Mcr */ + Dtr = 0x01, /* Data Terminal Ready */ + Rts = 0x02, /* Ready To Send */ + Out1 = 0x04, /* no longer in use */ + Ie = 0x08, /* IRQ Enable (cd_sts_ch on omap) */ + Dm = 0x10, /* Diagnostic Mode loopback */ +}; + +enum { /* Lsr */ + Dr = 0x01, /* Data Ready */ + Oe = 0x02, /* Overrun Error */ + Pe = 0x04, /* Parity Error */ + Fe = 0x08, /* Framing Error */ + Bi = 0x10, /* Break Interrupt */ + Thre = 0x20, /* Thr Empty */ + Temt = 0x40, /* Transmitter Empty */ + FIFOerr = 0x80, /* error in receiver FIFO */ +}; + +enum { /* Msr */ + Dcts = 0x01, /* Delta Cts */ + Ddsr = 0x02, /* Delta Dsr */ + Teri = 0x04, /* Trailing Edge of Ri */ + Ddcd = 0x08, /* Delta Dcd */ + Cts = 0x10, /* Clear To Send */ + Dsr = 0x20, /* Data Set Ready */ + Ri = 0x40, /* Ring Indicator */ + Dcd = 0x80, /* Carrier Detect */ +}; + +enum { /* Mdr */ + Modemask = 7, + Modeuart = 0, +}; + + +typedef struct Ctlr { + uchar* io; + int irq; + int tbdf; + int iena; + int poll; + + uchar sticky[Stickyend]; + + Lock; + int hasfifo; + int checkfifo; + int fena; +} Ctlr; + +extern PhysUart i8250physuart; + +static Ctlr i8250ctlr[] = { +{ .io = (uchar*)PHYSCONS, + .irq = ILduart0, + .tbdf = -1, + .poll = 0, }, +}; + +static Uart i8250uart[] = { +{ .regs = &i8250ctlr[0], + .name = "cons", + .freq = 1843200, /* Not used, we use the global i8250freq */ + .phys = &i8250physuart, + .console= 1, + .next = nil, }, +}; + +#define csr8r(c, r) ((c)->io[r]) +#define csr8w(c, r, v) ((c)->io[r] = (uchar)((c)->sticky[r] | (v))) +#define csr8o(c, r, v) ((c)->io[r] = (uchar)(v)) + +static long +i8250status(Uart* uart, void* buf, long n, long offset) +{ + char *p; + Ctlr *ctlr; + uchar ier, lcr, mcr, msr; + + ctlr = uart->regs; + p = smalloc(READSTR); + mcr = ctlr->sticky[Mcr]; + msr = csr8r(ctlr, Msr); + ier = ctlr->sticky[Ier]; + lcr = ctlr->sticky[Lcr]; + snprint(p, READSTR, + "b%d c%d d%d e%d l%d m%d p%c r%d s%d i%d\n" + "dev(%d) type(%d) framing(%d) overruns(%d) " + "berr(%d) serr(%d)%s%s%s%s\n", + + uart->baud, + uart->hup_dcd, + (msr & Dsr) != 0, + uart->hup_dsr, + (lcr & WlsMASK) + 5, + (ier & Ems) != 0, + (lcr & Pen) ? ((lcr & Eps) ? 'e': 'o'): 'n', + (mcr & Rts) != 0, + (lcr & Stb) ? 2: 1, + ctlr->fena, + + uart->dev, + uart->type, + uart->ferr, + uart->oerr, + uart->berr, + uart->serr, + (msr & Cts) ? " cts": "", + (msr & Dsr) ? " dsr": "", + (msr & Dcd) ? " dcd": "", + (msr & Ri) ? " ring": "" + ); + n = readstr(offset, buf, n, p); + free(p); + + return n; +} + +static void +i8250fifo(Uart* uart, int level) +{ + Ctlr *ctlr; + + ctlr = uart->regs; + if(ctlr->hasfifo == 0) + return; + + /* + * Changing the FIFOena bit in Fcr flushes data + * from both receive and transmit FIFOs; there's + * no easy way to guarantee not losing data on + * the receive side, but it's possible to wait until + * the transmitter is really empty. + */ + ilock(ctlr); + while(!(csr8r(ctlr, Lsr) & Temt)) + ; + + /* + * Set the trigger level, default is the max. + * value. + * Some UARTs require FIFOena to be set before + * other bits can take effect, so set it twice. + */ + ctlr->fena = level; + switch(level){ + case 0: + break; + case 1: + level = FIFO1|FIFOena; + break; + case 4: + level = FIFO4|FIFOena; + break; + case 8: + level = FIFO8|FIFOena; + break; + default: + level = FIFO14|FIFOena; + break; + } + csr8w(ctlr, Fcr, level); + csr8w(ctlr, Fcr, level); + iunlock(ctlr); +} + +static void +i8250dtr(Uart* uart, int on) +{ + Ctlr *ctlr; + + /* + * Toggle DTR. + */ + ctlr = uart->regs; + if(on) + ctlr->sticky[Mcr] |= Dtr; + else + ctlr->sticky[Mcr] &= ~Dtr; + csr8w(ctlr, Mcr, 0); +} + +static void +i8250rts(Uart* uart, int on) +{ + Ctlr *ctlr; + + /* + * Toggle RTS. + */ + ctlr = uart->regs; + if(on) + ctlr->sticky[Mcr] |= Rts; + else + ctlr->sticky[Mcr] &= ~Rts; + csr8w(ctlr, Mcr, 0); +} + +static void +i8250modemctl(Uart* uart, int on) +{ + Ctlr *ctlr; + + ctlr = uart->regs; + ilock(&uart->tlock); + if(on){ + ctlr->sticky[Ier] |= Ems; + csr8w(ctlr, Ier, 0); + uart->modem = 1; + uart->cts = csr8r(ctlr, Msr) & Cts; + } + else{ + ctlr->sticky[Ier] &= ~Ems; + csr8w(ctlr, Ier, 0); + uart->modem = 0; + uart->cts = 1; + } + iunlock(&uart->tlock); + + /* modem needs fifo */ + (*uart->phys->fifo)(uart, on); +} + +static int +i8250parity(Uart* uart, int parity) +{ + int lcr; + Ctlr *ctlr; + + ctlr = uart->regs; + lcr = ctlr->sticky[Lcr] & ~(Eps|Pen); + + switch(parity){ + case 'e': + lcr |= Eps|Pen; + break; + case 'o': + lcr |= Pen; + break; + case 'n': + break; + default: + return -1; + } + ctlr->sticky[Lcr] = lcr; + csr8w(ctlr, Lcr, 0); + + uart->parity = parity; + + return 0; +} + +static int +i8250stop(Uart* uart, int stop) +{ + int lcr; + Ctlr *ctlr; + + ctlr = uart->regs; + lcr = ctlr->sticky[Lcr] & ~Stb; + + switch(stop){ + case 1: + break; + case 2: + lcr |= Stb; + break; + default: + return -1; + } + ctlr->sticky[Lcr] = lcr; + csr8w(ctlr, Lcr, 0); + + uart->stop = stop; + + return 0; +} + +static int +i8250bits(Uart* uart, int bits) +{ + int lcr; + Ctlr *ctlr; + + ctlr = uart->regs; + lcr = ctlr->sticky[Lcr] & ~WlsMASK; + + switch(bits){ + case 5: + lcr |= Wls5; + break; + case 6: + lcr |= Wls6; + break; + case 7: + lcr |= Wls7; + break; + case 8: + lcr |= Wls8; + break; + default: + return -1; + } + ctlr->sticky[Lcr] = lcr; + csr8w(ctlr, Lcr, 0); + + uart->bits = bits; + + return 0; +} + +static int +i8250baud(Uart* uart, int baud) +{ +#ifdef notdef /* don't change the speed */ + ulong bgc; + Ctlr *ctlr; + extern int i8250freq; /* In the config file */ + + /* + * Set the Baud rate by calculating and setting the Baud rate + * Generator Constant. This will work with fairly non-standard + * Baud rates. + */ + if(i8250freq == 0 || baud <= 0) + return -1; + bgc = (i8250freq+8*baud-1)/(16*baud); + + ctlr = uart->regs; + while(csr8r(ctlr, Usr) & Busy) + delay(1); + csr8w(ctlr, Lcr, Dlab); /* begin kludge */ + csr8o(ctlr, Dlm, bgc>>8); + csr8o(ctlr, Dll, bgc); + csr8w(ctlr, Lcr, 0); +#endif + uart->baud = baud; + return 0; +} + +static void +i8250break(Uart* uart, int ms) +{ + Ctlr *ctlr; + + if (up == nil) + panic("i8250break: nil up"); + /* + * Send a break. + */ + if(ms <= 0) + ms = 200; + + ctlr = uart->regs; + csr8w(ctlr, Lcr, Brk); + tsleep(&up->sleep, return0, 0, ms); + csr8w(ctlr, Lcr, 0); +} + +static void +emptyoutstage(Uart *uart, int n) +{ + _uartputs((char *)uart->op, n); + uart->op = uart->oe = uart->ostage; +} + +static void +i8250kick(Uart* uart) +{ + int i; + Ctlr *ctlr; + + if(/* uart->cts == 0 || */ uart->blocked) + return; + + if(!normalprint || 1) { /* early */ // XXX + if (uart->op < uart->oe) + emptyoutstage(uart, uart->oe - uart->op); + while ((i = uartstageoutput(uart)) > 0) + emptyoutstage(uart, i); + return; + } + + /* nothing more to send? then disable xmit intr */ + ctlr = uart->regs; + if (uart->op >= uart->oe && qlen(uart->oq) == 0 && + (1 || csr8r(ctlr, Lsr) & Temt)) { /* could try ignoring Temt */ + ctlr->sticky[Ier] &= ~Ethre; + csr8w(ctlr, Ier, 0); + return; + } + + /* + * 128 here is an arbitrary limit to make sure + * we don't stay in this loop too long. If the + * chip's output queue is longer than 128, too + * bad -- presotto + */ + for(i = 0; i < 128; i++){ + if(!(csr8r(ctlr, Lsr) & Thre)) + continue; // break; XXX + if(uart->op >= uart->oe && uartstageoutput(uart) == 0) + break; + csr8o(ctlr, Thr, *uart->op++); /* start tx */ + ctlr->sticky[Ier] |= Ethre; + csr8w(ctlr, Ier, 0); /* intr when done */ + } +} + +void +serialkick(void) +{ + uartkick(&i8250uart[CONSOLE]); +} + +static Lock i8250intrlock; + +static void +i8250interrupt(Ureg*, void* arg) +{ + Ctlr *ctlr; + Uart *uart; + int iir, lsr, old, r; + + uart = arg; + ctlr = uart->regs; + ilock(&i8250intrlock); + + /* force Ethre on. don't know why this is needed, but it is. */ + ctlr->sticky[Ier] |= Ethre; + csr8w(ctlr, Ier, 0); + /* this is probably optional. maybe it helps fast input. */ + ctlr->sticky[Mcr] |= Ie; + csr8w(ctlr, Mcr, 0); + + for(iir = csr8r(ctlr, Iir); !(iir & Ip); iir = csr8r(ctlr, Iir)){ + switch(iir & IirMASK){ + case Ims: /* Ms interrupt */ + r = csr8r(ctlr, Msr); + if(r & Dcts){ + ilock(&uart->tlock); + old = uart->cts; + uart->cts = r & Cts; + if(old == 0 && uart->cts) + uart->ctsbackoff = 2; + iunlock(&uart->tlock); + } + if(r & Ddsr){ + old = r & Dsr; + if(uart->hup_dsr && uart->dsr && !old) + uart->dohup = 1; + uart->dsr = old; + } + if(r & Ddcd){ + old = r & Dcd; + if(uart->hup_dcd && uart->dcd && !old) + uart->dohup = 1; + uart->dcd = old; + } + break; + case Ithre: /* Thr Empty */ + uartkick(uart); + break; + case Irda: /* Received Data Available */ + case Irls: /* Receiver Line Status */ + case Ictoi: /* Character Time-out Indication */ + /* + * Consume any received data. + * If the received byte came in with a break, + * parity or framing error, throw it away; + * overrun is an indication that something has + * already been tossed. + */ + while((lsr = csr8r(ctlr, Lsr)) & Dr){ + if(lsr & (FIFOerr|Oe)) + uart->oerr++; + if(lsr & Pe) + uart->perr++; + if(lsr & Fe) + uart->ferr++; + r = csr8r(ctlr, Rbr); + if(!(lsr & (Bi|Fe|Pe))) + uartrecv(uart, r); + } + break; + + default: + iprint("weird uart interrupt type %#2.2uX\n", iir); + break; + } + } + iunlock(&i8250intrlock); +} + +static void +i8250disable(Uart* uart) +{ + Ctlr *ctlr; + + /* + * Turn off DTR and RTS, disable interrupts and fifos. + */ + (*uart->phys->dtr)(uart, 0); + (*uart->phys->rts)(uart, 0); + (*uart->phys->fifo)(uart, 0); + + ctlr = uart->regs; + ctlr->sticky[Ier] = 0; + csr8w(ctlr, Ier, 0); + + if(ctlr->iena != 0){ + /* bad idea if the IRQ is shared */ +// introff(1 << (ILshift + ctlr->irq)); + ctlr->iena = 0; + } +} + +static void +i8250clock(void) +{ + i8250interrupt(nil, &i8250uart[CONSOLE]); +} + +static void +i8250enable(Uart* uart, int ie) +{ + Ctlr *ctlr; + + ctlr = uart->regs; + ctlr->sticky[Lcr] = Wls8; /* no parity */ + csr8w(ctlr, Lcr, 0); + + /* + * Check if there is a FIFO. + * Changing the FIFOena bit in Fcr flushes data + * from both receive and transmit FIFOs; there's + * no easy way to guarantee not losing data on + * the receive side, but it's possible to wait until + * the transmitter is really empty. + * Also, reading the Iir outwith i8250interrupt() + * can be dangerous, but this should only happen + * once, before interrupts are enabled. + */ + ilock(ctlr); + if(!ctlr->checkfifo){ + /* + * Wait until the transmitter is really empty. + */ + while(!(csr8r(ctlr, Lsr) & Temt)) + ; + csr8w(ctlr, Fcr, FIFOena); + if(csr8r(ctlr, Iir) & Ifena) + ctlr->hasfifo = 1; + csr8w(ctlr, Fcr, 0); + ctlr->checkfifo = 1; + } + iunlock(ctlr); + + /* + * Enable interrupts and turn on DTR and RTS. + * Be careful if this is called to set up a polled serial line + * early on not to try to enable interrupts as interrupt- + * -enabling mechanisms might not be set up yet. + */ + if(ie){ + if(ctlr->iena == 0 && !ctlr->poll){ + intrenable(ctlr->irq, i8250interrupt, uart, 0); + ctlr->iena = 1; + } + ctlr->sticky[Ier] = Erda; + ctlr->sticky[Mcr] |= Ie; + } + else{ + ctlr->sticky[Ier] = 0; + ctlr->sticky[Mcr] = 0; + } + csr8w(ctlr, Ier, 0); + csr8w(ctlr, Mcr, 0); + + (*uart->phys->dtr)(uart, 1); + (*uart->phys->rts)(uart, 1); + + /* + * During startup, the i8259 interrupt controller is reset. + * This may result in a lost interrupt from the i8250 uart. + * The i8250 thinks the interrupt is still outstanding and does not + * generate any further interrupts. The workaround is to call the + * interrupt handler to clear any pending interrupt events. + * Note: this must be done after setting Ier. + */ + if(ie) { + i8250interrupt(nil, uart); + /* + * force output to resume if stuck. shouldn't be needed. + */ + if (Pollstuckoutput) + addclock0link(i8250clock, 10); + } +} + +static Uart* +i8250pnp(void) +{ + return i8250uart; +} + +static int +i8250getc(Uart* uart) +{ + Ctlr *ctlr; + + ctlr = uart->regs; + while(!(csr8r(ctlr, Lsr) & Dr)) + delay(1); + return csr8r(ctlr, Rbr); +} + +static void +i8250putc(Uart* uart, int c) +{ + int i, s; + Ctlr *ctlr; + + if (!normalprint) { /* too early; use brute force */ + s = splhi(); + while (!(((uchar*)PHYSCONS)[Lsr] & Thre)) + ; + ((uchar*)PHYSCONS)[Thr] = (uchar)c; + splx(s); + return; + } + + ctlr = uart->regs; + s = splhi(); + for(i = 0; !(csr8r(ctlr, Lsr) & Thre) && i < 200; i++) + delay(5); + csr8o(ctlr, Thr, c); + for(i = 0; !(csr8r(ctlr, Lsr) & Thre) && i < 200; i++) + delay(5); + splx(s); +} + +void +serialputc(int c) +{ + i8250putc(&i8250uart[CONSOLE], c); +} + +void +serialputs(char* s, int n) +{ + _uartputs(s, n); +} + +#ifdef PLAN9K +static void +i8250poll(Uart* uart) +{ + Ctlr *ctlr; + + /* + * If PhysUart has a non-nil .poll member, this + * routine will be called from the uartclock timer. + * If the Ctlr .poll member is non-zero, when the + * Uart is enabled interrupts will not be enabled + * and the result is polled input and output. + * Not very useful here, but ports to new hardware + * or simulators can use this to get serial I/O + * without setting up the interrupt mechanism. + */ + ctlr = uart->regs; + if(ctlr->iena || !ctlr->poll) + return; + i8250interrupt(nil, uart); +} +#endif + +PhysUart i8250physuart = { + .name = "i8250", + .pnp = i8250pnp, + .enable = i8250enable, + .disable = i8250disable, + .kick = i8250kick, + .dobreak = i8250break, + .baud = i8250baud, + .bits = i8250bits, + .stop = i8250stop, + .parity = i8250parity, + .modemctl = i8250modemctl, + .rts = i8250rts, + .dtr = i8250dtr, + .status = i8250status, + .fifo = i8250fifo, + .getc = i8250getc, + .putc = i8250putc, +#ifdef PLAN9K + .poll = i8250poll, +#endif +}; + +static void +i8250dumpregs(Ctlr* ctlr) +{ + int dlm, dll; + int _uartprint(char*, ...); + + csr8w(ctlr, Lcr, Dlab); + dlm = csr8r(ctlr, Dlm); + dll = csr8r(ctlr, Dll); + csr8w(ctlr, Lcr, 0); + + _uartprint("dlm %#ux dll %#ux\n", dlm, dll); +} + +Uart* uartenable(Uart *p); + +/* must call this from a process's context */ +int +i8250console(void) +{ + Uart *uart; + + if (up == nil) + return -1; /* too early */ + + uart = &i8250uart[CONSOLE]; + if(uartenable(uart) != nil && uart->console){ + kbdq = uart->iq; + assert(kbdq); + serialoq = uart->oq; + assert(serialoq); + uart->putc = kbdcr2nl; + uart->opens++; + consuart = uart; + /* up wasn't set when chandevreset ran, so enable it now */ + i8250disable(uart); + i8250enable(uart, 1); + } + uartctl(uart, "b115200 l8 pn m0 s1 i128 w100"); + return 0; +} + +void +_uartputs(char* s, int n) +{ + char *e; + + for(e = s+n; s < e; s++){ + if(*s == '\n') + i8250putc(&i8250uart[CONSOLE], '\r'); + i8250putc(&i8250uart[CONSOLE], *s); + } +} + +int +_uartprint(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); + _uartputs(buf, n); + + return n; +} + +void (*lprint)(char *, int) = _uartputs;