From d0fa9c3a403a78cd94a71745aae2748e78e35de9 Mon Sep 17 00:00:00 2001 From: David du Colombier <0intro@gmail.com> Date: Fri, 10 Aug 2001 00:00:00 +0000 Subject: [PATCH] Plan 9 from Bell Labs 2001-08-10 --- bitsy/devuda1341.c | 19 +- bitsy/fns.h | 3 + bitsy/main.c | 18 +- bitsy/mmu.c | 9 +- bitsy/power.c | 76 +++-- bitsy/screen.c | 22 +- mtx/dat.h | 226 +++++++++++++ mtx/fns.h | 106 ++++++ mtx/inb.s | 119 +++++++ mtx/io.h | 159 +++++++++ mtx/l.s | 71 ++++ mtx/main.c | 22 ++ mtx/mem.h | 101 ++++++ mtx/trap.c | 828 +++++++++++++++++++++++++++++++++++++++++++++ mtx/uarti8250.c | 648 +++++++++++++++++++++++++++++++++++ 15 files changed, 2381 insertions(+), 46 deletions(-) create mode 100644 mtx/dat.h create mode 100644 mtx/fns.h create mode 100644 mtx/inb.s create mode 100644 mtx/io.h create mode 100644 mtx/l.s create mode 100644 mtx/main.c create mode 100644 mtx/mem.h create mode 100644 mtx/trap.c create mode 100644 mtx/uarti8250.c diff --git a/bitsy/devuda1341.c b/bitsy/devuda1341.c index 03e3471e2a7463c254db3cb873e5ccae2d94a586..0b9fc6034095ec4f6484e95102a98aba862ffe50 100644 --- a/bitsy/devuda1341.c +++ b/bitsy/devuda1341.c @@ -517,7 +517,8 @@ enable(void) sspregs->control0 = 0x039f; /* enable */ /* Enable the audio power */ - egpiobits(EGPIO_audio_ic_power | EGPIO_codec_reset, 1); + audioicpower(1); + egpiobits(EGPIO_codec_reset, 1); setspeed(0, 0, 0, 0, volumes[Vspeed].ilval); @@ -672,7 +673,7 @@ setreg(char *name, int val, int n) static void outenable(void) { /* turn on DAC, set output gain switch */ - egpiobits(EGPIO_audio_power, 1); + audioamppower(1); audiomute(0); status1[0] |= 0x41; L3_write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1); @@ -690,13 +691,12 @@ outdisable(void) { dmastop(audio.o.dma); /* turn off DAC, clear output gain switch */ audiomute(1); - egpiobits(EGPIO_audio_power, 0); status1[0] &= ~0x41; L3_write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1); if (debug) { print("outdisable: status1 = 0x%2.2ux\n", status1[0]); } - egpiobits(EGPIO_audio_power, 0); + audioamppower(0); } static void @@ -812,7 +812,9 @@ audiopower(int flag) { if (flag) { /* power on only when necessary */ if (audio.amode) { - egpiobits(EGPIO_audio_power | EGPIO_audio_ic_power | EGPIO_codec_reset, 1); + audioamppower(1); + audioicpower(1); + egpiobits(EGPIO_codec_reset, 1); enable(); if (audio.amode & Aread) { inenable(); @@ -834,7 +836,9 @@ audiopower(int flag) { indisable(); if (audio.amode & Awrite) outdisable(); - egpiobits(EGPIO_audio_ic_power | EGPIO_codec_reset | EGPIO_audio_power, 0); + egpiobits(EGPIO_codec_reset, 0); + audioamppower(0); + audioicpower(0); } } @@ -1020,7 +1024,8 @@ audioclose(Chan *c) } if (audio.amode == 0) { /* turn audio off */ - egpiobits(EGPIO_audio_ic_power | EGPIO_codec_reset, 0); + egpiobits(EGPIO_codec_reset, 0); + audioicpower(0); } qunlock(&audio); } diff --git a/bitsy/fns.h b/bitsy/fns.h index 0497f3e37487ff0f89530ae2afc8c7453b6e5f6e..b279eb74e50d3189346681749c31f3b98121107f 100644 --- a/bitsy/fns.h +++ b/bitsy/fns.h @@ -1,6 +1,8 @@ #include "../port/portfns.h" void audiopower(int); +void audioamppower(int); +void audioicpower(int); void cacheflush(void); void cachewb(void); void cachewbaddr(void*); @@ -55,6 +57,7 @@ void mmuenable(void); void mmudisable(void); void mmuinvalidate(void); void mmuinvalidateaddr(ulong); +void mmurestart(void); ulong mmu_kaddr(ulong); ulong mmu_paddr(ulong); int µcputc(Queue*, int); diff --git a/bitsy/main.c b/bitsy/main.c index 6e42a913acb3c2c8ec85a8d81ffadf72616cea62..54dcca450c456b5db163bc778ce7aaf45f17f7b0 100644 --- a/bitsy/main.c +++ b/bitsy/main.c @@ -36,7 +36,6 @@ main(void) machinit(); trapinit(); sa1110_uartsetup(1); - rs232power(1); dmainit(); screeninit(); printinit(); /* from here on, print works, before this we need iprint */ @@ -423,18 +422,35 @@ void rs232power(int on) { egpiobits(EGPIO_rs232_power, on); + delay(50); +} + +void +audioamppower(int on) +{ + egpiobits(EGPIO_audio_power, on); + delay(50); +} + +void +audioicpower(int on) +{ + egpiobits(EGPIO_audio_ic_power, on); + delay(50); } void irpower(int on) { egpiobits(EGPIO_ir_power, on); + delay(50); } void lcdpower(int on) { egpiobits(EGPIO_lcd_3v|EGPIO_lcd_ic_power|EGPIO_lcd_5v|EGPIO_lcd_9v, on); + delay(50); } void diff --git a/bitsy/mmu.c b/bitsy/mmu.c index 5d9050c3a2f141d45599696639f0d325c3987f92..d0213a230457fff75e1a66738dccdd0c59c81aaa 100644 --- a/bitsy/mmu.c +++ b/bitsy/mmu.c @@ -110,6 +110,13 @@ mmuinit(void) l1table[a>>20] = L1PageTable | L1Domain0 | (((ulong)t) & L1PTBaseMask); t[(a&0xfffff)>>PGSHIFT] = L2SmallPage | L2KernelRW | (PHYSDRAM0 & L2PageBaseMask); + mmurestart(); + + mmuinited = 1; +} + +void +mmurestart(void) { /* set up the domain register to cause all domains to obey pte access bits */ putdac(Dclient); @@ -121,8 +128,6 @@ mmuinit(void) mmuinvalidate(); mmuenable(); cacheflush(); - - mmuinited = 1; } /* diff --git a/bitsy/power.c b/bitsy/power.c index 295e96e9549c370c9f9ee135efd747347d84ff4a..e8ce165d116cc28d4f78e1e39c8a2b07e4b06432 100644 --- a/bitsy/power.c +++ b/bitsy/power.c @@ -64,14 +64,26 @@ intrcpy(Intrregs *to, Intrregs *from) } static void -gpiocpy(GPIOregs *to, GPIOregs *from) +gpiosave(GPIOregs *to, GPIOregs *from) { + to->level = from->level; to->rising = from->rising; // gpio intrs enabled to->falling= from->falling; // gpio intrs enabled to->altfunc = from->altfunc; to->direction = from->direction; } +static void +gpiorestore(GPIOregs *to, GPIOregs *from) +{ + to->rising = from->rising; // gpio intrs enabled + to->falling= from->falling; // gpio intrs enabled + to->altfunc = from->altfunc; + to->direction = from->direction; + to->set = from->level & 0x0fffffff; + to->clear = ~from->level & 0x0fffffff; +} + void (*restart)(void) = nil; static void @@ -90,14 +102,15 @@ sa1100_power_off(void) powerregs->pcfr = PCFR_opde | PCFR_fp | PCFR_fs; powerregs->pgsr = 0; /* set resume address. The loader jumps to it */ -// powerregs->pspr = (ulong)sa1100_power_resume; - powerregs->pspr = 0; - /* set lowest clock; delay to avoid resume hangs on fast sa1110 */ + powerregs->pspr = (ulong)sa1100_power_resume; +// powerregs->pspr = 0; -sa1100_power_resume(); - delay(90); - powerregs->ppcr = 0; - delay(90); +//sa1100_power_resume(); + + /* set lowest clock; delay to avoid resume hangs on fast sa1110 */ +// delay(90); +// powerregs->ppcr = 0; +// delay(90); /* set all GPIOs to input mode */ gpioregs->direction = 0; @@ -131,12 +144,30 @@ deepsleep(void) { void *xsp, *xlink; power_pl = splhi(); - cachewb(); - delay(500); + delay(50); + /* Power down */ + iprint("entering suspend mode\n"); + uartpower(0); + clockpower(0); + irpower(0); + audiopower(0); + screenpower(0); + µcpower(0); + rs232power(0); + gpiosave(&savedgpioregs, gpioregs); + intrcpy(&savedintrregs, intrregs); + cacheflush(); + delay(50); if (setpowerlabel()) { - mmuinvalidate(); - mmuenable(); - cacheflush(); + mmurestart(); + gpiorestore(gpioregs, &savedgpioregs); + delay(50); + intrcpy(intrregs, &savedintrregs); + if (intrregs->icip & (1<edgestatus = (1<icip = (1<r0 = 1<<0; - gpiocpy(gpioregs, &savedgpioregs); - intrcpy(intrregs, &savedintrregs); - if (intrregs->icip & (1<edgestatus = (1<icip = (1<direction |= + GPIO_LDD8_o|GPIO_LDD9_o|GPIO_LDD10_o|GPIO_LDD11_o + |GPIO_LDD12_o|GPIO_LDD13_o|GPIO_LDD14_o|GPIO_LDD15_o; + gpioregs->altfunc |= + GPIO_LDD8_o|GPIO_LDD9_o|GPIO_LDD10_o|GPIO_LDD11_o + |GPIO_LDD12_o|GPIO_LDD13_o|GPIO_LDD14_o|GPIO_LDD15_o; + framebuf->palette[0] = Pal0; lcd->dbar1 = framebuf->palette; lcd->lccr3 = pcd<lccr2 = (Wid-1)<palette[0] = Pal0; - lcdpower(1); lcdinit(); @@ -304,9 +309,18 @@ setcolor(ulong p, ulong r, ulong g, ulong b) void blankscreen(int blank) { + int cnt; + if (blank) { - lcd->lccr0 &= ~(0<lccr0 &= ~(1<lcsr & (1<pa|KZERO) +#define kunmap(k) + +/* + * Process Control Block, used by PALcode + */ +struct PCB { + uvlong ksp; + uvlong usp; + uvlong ptbr; + ulong asn; + ulong pcc; + uvlong unique; + ulong fen; + ulong dummy; + uvlong rsrv1; + uvlong rsrv2; +}; + +struct Mach +{ + /* OFFSETS OF THE FOLLOWING KNOWN BY l.s */ + int machno; /* physical id of processor */ + ulong splpc; /* pc that called splhi() */ + Proc *proc; /* current process on this processor */ + + /* ordering from here on irrelevant */ + + ulong ticks; /* of the clock since boot time */ + Label sched; /* scheduler wakeup */ + Lock alarmlock; /* access to alarm list */ + void *alarm; /* alarms bound to this clock */ + int inclockintr; + + ulong fairness; /* for runproc */ + + ulong cpuhz; /* hwrpb->cfreq */ + ulong pcclast; + uvlong fastclock; + vlong intrts; /* time stamp of last interrupt */ + + int tlbfault; /* only used by devproc; no access to tlb */ + int tlbpurge; /* ... */ + int pfault; + int cs; + int syscall; + int load; + int intr; + int flushmmu; /* make current proc flush it's mmu state */ + + ulong spuriousintr; + int lastintr; + + PCB; + + /* MUST BE LAST */ + int stack[1]; +}; + +struct +{ + Lock; + short machs; + short exiting; + short ispanic; +}active; + +/* + * Implementation-dependant functions (outside of Alpha architecture proper). + * Called PCArch because that's what mkdevc calls it (for the PC). + */ +struct PCArch +{ + char* id; + int (*ident)(void); + + void (*coreinit)(void); /* set up core logic, PCI mappings etc */ + void (*corehello)(void); /* identify core logic to user */ + void (*coredetach)(void); /* restore core logic before return to console */ + void *(*pcicfg)(int, int); /* map and point to PCI cfg space */ + void *(*pcimem)(int, int); /* map and point to PCI memory space */ + int (*intrenable)(Vctl*); + + int (*_inb)(int); + ushort (*_ins)(int); + ulong (*_inl)(int); + void (*_outb)(int, int); + void (*_outs)(int, ushort); + void (*_outl)(int, ulong); + void (*_insb)(int, void*, int); + void (*_inss)(int, void*, int); + void (*_insl)(int, void*, int); + void (*_outsb)(int, void*, int); + void (*_outss)(int, void*, int); + void (*_outsl)(int, void*, int); +}; + +/* + * a parsed plan9.ini line + */ +#define NISAOPT 8 + +struct ISAConf { + char *type; + ulong port; + ulong irq; + ulong dma; + ulong mem; + ulong size; + ulong freq; + + int nopt; + char *opt[NISAOPT]; +}; + +extern PCArch *arch; + +#define MACHP(n) ((Mach *)((int)&mach0+n*BY2PG)) +extern Mach mach0; + +extern register Mach *m; +extern register Proc *up; diff --git a/mtx/fns.h b/mtx/fns.h new file mode 100644 index 0000000000000000000000000000000000000000..a2f135226706fb5e36087692cea7caa7a6f8c637 --- /dev/null +++ b/mtx/fns.h @@ -0,0 +1,106 @@ +#include "../port/portfns.h" + +void archinit(void); +int brgalloc(void); +void brgfree(int); +ulong baudgen(int, int); +int cistrcmp(char*, char*); +int cistrncmp(char*, char*, int); +void clockcheck(void); +void clockinit(void); +void clockintr(Ureg*); +void clrfptrap(void); +#define coherence() +void cpminit(void); +int cpuidentify(void); +void cpuidprint(void); +void dcflush(void*, ulong); +void delay(int); +ulong draminit(ulong*); +void dtlbmiss(void); +void dtlberror(void); +void dumpregs(Ureg*); +void delayloopinit(void); +void eieio(void); +//#define eieio() +void evenaddr(ulong); +void faultpower(Ureg*, ulong addr, int read); +void firmware(int); +void fpinit(void); +int fpipower(Ureg*); +void fpoff(void); +ulong fpstatus(void); +char* getconf(char*); +ulong getdar(void); +ulong getdec(void); +ulong getdepn(void); +ulong getdsisr(void); +ulong getimmr(void); +ulong getmsr(void); +ulong getpvr(void); +ulong gettbl(void); +ulong gettbu(void); +void gotopc(ulong); +void i8250console(void); +void icflush(void*, ulong); +void idle(void); +#define idlehands() /* nothing to do in the runproc */ +int inb(int); +void insb(int, void*, int); +ushort ins(int); +void inss(int, void*, int); +ulong inl(int); +void insl(int, void*, int); +void intr(Ureg*); +void intrenable(int, void (*)(Ureg*, void*), void*, int, char*); +int intrstats(char*, int); +void intrvec(void); +int iprint(char*, ...); +void itlbmiss(void); +int isaconfig(char*, int, ISAConf*); +void kbdinit(void); +void kbdreset(void); +void kernelmmu(void); +void links(void); +void mathinit(void); +void mmuinit(void); +ulong* mmuwalk(ulong*, ulong, int); +void outb(int, int); +void outsb(int, void*, int); +void outs(int, ushort); +void outss(int, void*, int); +void outl(int, ulong); +void outsl(int, void*, int); +int pcmspecial(char*, ISAConf*); +void pcmspecialclose(int); +#define procrestore(p) +void powerdownled(void); +void powerupled(void); +void procsave(Proc*); +void procsetup(Proc*); +void putdec(ulong); +void putmsr(ulong); +void putcasid(ulong); +void screeninit(void); +void setpanic(void); +int screenprint(char*, ...); /* debugging */ +ulong sdraminit(ulong*); +int segflush(void*, ulong); +void spireset(void); +long spioutin(void*, long, void*); +int tas(void*); +uchar* tarlookup(uchar *addr, char *file, int *dlen); +void touser(void*); +void trapinit(void); +void trapvec(void); +void tlbflush(ulong); +void tlbflushall(void); +void uartinstall(void); +void uartwait(void); /* debugging */ +int unsac(uchar *dst, uchar *src, int n, int nsrc); +#define userureg(ur) ((ur)->status & KUSER) +void wbflush(void); + +#define waserror() (up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1])) +#define KADDR(a) ((void*)((ulong)(a)|KZERO)) +#define PADDR(a) ((ulong)(a)&~KZERO) diff --git a/mtx/inb.s b/mtx/inb.s new file mode 100644 index 0000000000000000000000000000000000000000..9f32a8de9004db06e77bfebad46fce467e7e8c27 --- /dev/null +++ b/mtx/inb.s @@ -0,0 +1,119 @@ +#include "mem.h" + +#define BDNZ BC 16,0, +#define BDNE BC 0,2, + +TEXT inb(SB), $0 + OR $IOMEM, R3 + MOVBZ (R3), R3 + RETURN + +TEXT insb(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + MOVW R5, CTR + OR $IOMEM, R3 + SUB $1, R4 +insb1: + MOVBZ (R3), R7 + MOVBU R7, 1(R4) + BDNZ insb1 + RETURN + +TEXT outb(SB), $0 + MOVW v+4(FP), R4 + OR $IOMEM, R3 + EIEIO + MOVB R4, (R3) + RETURN + +TEXT outsb(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + MOVW R5, CTR + OR $IOMEM, R3 + SUB $1, R4 +outsb1: + EIEIO + MOVBZU 1(R4), R7 + MOVB R7, (R3) + BDNZ outsb1 + RETURN + +TEXT ins(SB), $0 + OR $IOMEM, R3 + EIEIO + MOVHBR (R3), R3 + RETURN + +TEXT inss(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + MOVW R5, CTR + OR $IOMEM, R3 + SUB $2, R4 +inss1: + EIEIO + MOVHZ (R3), R7 + MOVHU R7, 2(R4) + BDNZ inss1 + RETURN + +TEXT outs(SB), $0 + MOVW v+4(FP), R4 + OR $IOMEM, R3 + EIEIO + MOVHBR R4, (R3) + RETURN + +TEXT outss(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + MOVW R5, CTR + OR $IOMEM, R3 + SUB $2, R4 +outss1: + EIEIO + MOVHZU 2(R4), R7 + MOVH R7, (R3) + BDNZ outss1 + RETURN + +TEXT inl(SB), $0 + OR $IOMEM, R3 + EIEIO + MOVWBR (R3), R3 + RETURN + +TEXT insl(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + MOVW R5, CTR + OR $IOMEM, R3 + SUB $4, R4 +insl1: + EIEIO + MOVW (R3), R7 + MOVWU R7, 4(R4) + BDNZ insl1 + RETURN + +TEXT outl(SB), $0 + MOVW v+4(FP), R4 + OR $IOMEM, R3 + EIEIO + MOVWBR R4, (R3) + RETURN + +TEXT outsl(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + MOVW R5, CTR + OR $IOMEM, R3 + SUB $4, R4 +outsl1: + EIEIO + MOVWU 4(R4), R7 + MOVW R7, (R3) + BDNZ outsl1 + RETURN diff --git a/mtx/io.h b/mtx/io.h new file mode 100644 index 0000000000000000000000000000000000000000..83bb404c04a5871b16f6db5338f733bfaa3f0b2f --- /dev/null +++ b/mtx/io.h @@ -0,0 +1,159 @@ +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 = 64, + MaxVectorPIC = VectorPIC+MaxIrqPIC, +}; + +typedef 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 */ +} Vctl; + +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 BUSDF(tbdf) ((tbdf)&0x000FF00) +#define BUSBDF(tbdf) ((tbdf)&0x0FFFF00) +#define BUSUNKNOWN (-1) + +enum { + MaxEISA = 16, + EISAconfig = 0xC80, +}; + +/* + * PCI support code. + */ +enum { /* type 0 and type 1 pre-defined header */ + PciVID = 0x00, /* vendor ID */ + PciDID = 0x02, /* device ID */ + PciPCR = 0x04, /* command */ + PciPSR = 0x06, /* status */ + PciRID = 0x08, /* revision ID */ + PciCCRp = 0x09, /* programming interface class code */ + PciCCRu = 0x0A, /* sub-class code */ + PciCCRb = 0x0B, /* base class code */ + PciCLS = 0x0C, /* cache line size */ + PciLTR = 0x0D, /* latency timer */ + PciHDT = 0x0E, /* header type */ + PciBST = 0x0F, /* BIST */ + + PciBAR0 = 0x10, /* base address */ + PciBAR1 = 0x14, + PciROM = 0x30, + + PciINTL = 0x3C, /* interrupt line */ + PciINTP = 0x3D, /* interrupt pin */ +}; + +enum { /* type 0 pre-defined header */ + PciBAR2 = 0x18, + PciBAR3 = 0x1C, + PciBAR4 = 0x20, + PciBAR5 = 0x24, + PciCIS = 0x28, /* cardbus CIS pointer */ + PciSVID = 0x2C, /* subsystem vendor ID */ + PciSID = 0x2E, /* cardbus CIS pointer */ + PciEBAR0 = 0x30, /* xpansion ROM base address */ + PciMGNT = 0x3E, /* burst period length */ + PciMLT = 0x3F, /* maximum latency between bursts */ +}; + +enum { /* type 1 pre-defined header */ + PciPBN = 0x18, /* primary bus number */ + PciSBN = 0x19, /* secondary bus number */ + PciUBN = 0x1A, /* subordinate bus number */ + PciSLTR = 0x1B, /* secondary latency timer */ + PciIBR = 0x1C, /* I/O base */ + PciILR = 0x1D, /* I/O limit */ + PciSPSR = 0x1E, /* secondary status */ + PciMBR = 0x20, /* memory base */ + PciMLR = 0x22, /* memory limit */ + PciPMBR = 0x24, /* prefetchable memory base */ + PciPMLR = 0x26, /* prefetchable memory limit */ + PciPUBR = 0x28, /* prefetchable base upper 32 bits */ + PciPULR = 0x2C, /* prefetchable limit upper 32 bits */ + PciIUBR = 0x30, /* I/O base upper 16 bits */ + PciIULR = 0x32, /* I/O limit upper 16 bits */ + PciEBAR1 = 0x28, /* expansion ROM base address */ + PciBCR = 0x3E, /* bridge control register */ +}; + +typedef struct Pcidev Pcidev; +typedef struct Pcidev { + int tbdf; /* type+bus+device+function */ + ushort vid; /* vendor ID */ + ushort did; /* device ID */ + + uchar rid; + uchar ccrp; + uchar ccru; + uchar ccrb; + + struct { + ulong bar; /* base address */ + int size; + } mem[6]; + + uchar intl; /* interrupt line */ + + Pcidev* list; + Pcidev* link; /* next device on this bno */ + + Pcidev* bridge; /* down a bus */ + struct { + ulong bar; + int size; + } ioa, mema; + ulong pcr; +}; + +//#define PCIWINDOW 0x40000000 +//#define PCIWADDR(va) (PADDR(va)+PCIWINDOW) +//#define ISAWINDOW 0x00800000 +//#define ISAWADDR(va) (PADDR(va)+ISAWINDOW) diff --git a/mtx/l.s b/mtx/l.s new file mode 100644 index 0000000000000000000000000000000000000000..7d6a48f5c6a09a4975c27bca70fd4368096c0521 --- /dev/null +++ b/mtx/l.s @@ -0,0 +1,71 @@ +#include "mem.h" +#include "mpc60x.h" + +#define BDNZ BC 16,0, +#define BDNE BC 0,2, +#define NOOP OR R0,R0,R0 +#define TLBIA WORD $(31<<26) + +/* Be-ware 603e chip bugs (mtmsr instruction) */ +#define FIX603e CROR 0,0,0 +#undef FIX603e +#define FIX603e ISYNC; SYNC + + TEXT start(SB), $-4 + MOVW $setSB(SB), R2 + + BL main(SB) + RETURN /* not reached */ + +TEXT splhi(SB), $0 + MOVW LR, R31 + MOVW R31, 4(R(MACH)) /* save PC in m->splpc */ + MOVW MSR, R3 + RLWNM $0, R3, $~MSR_EE, R4 + SYNC + MOVW R4, MSR + FIX603e + RETURN + +TEXT splx(SB), $0 + /* fall though */ + +TEXT splxpc(SB), $0 + MOVW LR, R31 + MOVW R31, 4(R(MACH)) /* save PC in m->splpc */ + MOVW MSR, R4 + RLWMI $0, R3, $MSR_EE, R4 + SYNC + MOVW R4, MSR + FIX603e + RETURN + +TEXT spllo(SB), $0 + MOVW MSR, R3 + OR $MSR_EE, R3, R4 + SYNC + MOVW R4, MSR + FIX603e + RETURN + +TEXT spldone(SB), $0 + RETURN + +TEXT islo(SB), $0 + MOVW MSR, R3 + RLWNM $0, R3, $MSR_EE, R3 + RETURN + +TEXT setlabel(SB), $-4 + MOVW LR, R31 + MOVW R1, 0(R3) + MOVW R31, 4(R3) + MOVW $0, R3 + RETURN + +TEXT gotolabel(SB), $-4 + MOVW 4(R3), R31 + MOVW R31, LR + MOVW 0(R3), R1 + MOVW $1, R3 + RETURN diff --git a/mtx/main.c b/mtx/main.c new file mode 100644 index 0000000000000000000000000000000000000000..b75474ae5078adf95f93a5a6d5772bb8ba0c1858 --- /dev/null +++ b/mtx/main.c @@ -0,0 +1,22 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "init.h" +#include "pool.h" + +Conf conf; +FPsave initfp; + +void +main(void) +{ +// xinit(); + printinit(); + i8250console(); + print("\nPlan 9\n"); + + for(;;); +} diff --git a/mtx/mem.h b/mtx/mem.h new file mode 100644 index 0000000000000000000000000000000000000000..3b608617040f933ea410816ba2b48766cccc207a --- /dev/null +++ b/mtx/mem.h @@ -0,0 +1,101 @@ +/* none of this is meant to be correct yet */ + +/* + * 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 4 /* bytes per word */ +#define BY2V 8 /* bytes per vlong */ +#define BY2PG 8192 /* bytes per page */ +#define WD2PG (BY2PG/BY2WD) /* words per page */ +#define PGSHIFT 13 /* log(BY2PG) */ +#define ROUND(s, sz) (((s)+(sz-1))&~(sz-1)) +#define PGROUND(s) ROUND(s, BY2PG) +#define BLOCKALIGN 8 + +#define BY2PTE 8 /* bytes per pte entry */ +#define PTE2PG (BY2PG/BY2PTE) /* pte entries per page */ + +#define MAXMACH 1 /* max # cpus system can run */ +#define KSTACK 4096 /* Size of kernel stack */ + +/* + * Time + */ +#define HZ 100 /* clock frequency */ +#define MS2HZ (1000/HZ) +#define TK2SEC(t) ((t)/HZ) /* ticks to seconds */ +#define MS2TK(t) (((t)*HZ+500)/1000) /* milliseconds to closest tick */ + +/* + * Magic registers + */ + +#define MACH 30 /* R30 is m-> */ +#define USER 29 /* R29 is up-> */ + + +/* + * Fundamental addresses + */ + +#define UREGSIZE ((8+32)*4) + +/* + * MMU + * + */ + +/* L1 table entry and Mx_TWC flags */ +#define PTEVALID (1<<0) +#define PTEWT (1<<1) /* write through */ +#define PTE4K (0<<2) +#define PTE512K (1<<2) +#define PTE8MB (3<<2) +#define PTEG (1<<4) /* guarded */ + +/* L2 table entry and Mx_RPN flags (also PTEVALID) */ +#define PTECI (1<<1) /* cache inhibit */ +#define PTESH (1<<2) /* page is shared; ASID ignored */ +#define PTELPS (1<<3) /* large page size */ +#define PTEWRITE 0x9F0 + +/* TLB and MxEPN flag */ +#define TLBVALID (1<<9) + +#define TLBSETS 32 /* number of tlb sets (603/603e) */ + +#define PTEMAPMEM (1024*1024) +#define PTEPERTAB (PTEMAPMEM/BY2PG) +#define SEGMAPSIZE 512 +#define SSEGMAPSIZE 16 + +/* + * Address spaces + */ + +#define UZERO 0 /* base of user address space */ +#define UTZERO (UZERO+BY2PG) /* first address in user text */ +#define USTKTOP (TSTKTOP-TSTKSIZ*BY2PG) /* byte just beyond user stack */ +#define TSTKTOP KZERO /* top of temporary stack */ +#define TSTKSIZ 100 +#define KZERO 0x80000000 /* base of kernel address space */ +#define KTZERO (KZERO+0x400000) /* first address in kernel text */ +#define USTKSIZE (4*1024*1024) /* size of user stack */ + +#define PCI1 0x40000000 +#define PCI0 0xfd000000 +#define IOMEM 0xfe000000 +#define FALCON 0xfef80000 +#define RAVEN 0xfeff0000 +#define FLASHA 0xff000000 +#define FLASHB 0xff800000 +#define FLASHAorB 0xfff00000 + +#define isphys(x) (((ulong)x&KZERO)!=0) diff --git a/mtx/trap.c b/mtx/trap.c new file mode 100644 index 0000000000000000000000000000000000000000..abc14d0e92d85aae8cd307c2d27bad05d10f2267 --- /dev/null +++ b/mtx/trap.c @@ -0,0 +1,828 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "ureg.h" +#include "io.h" +#include "../port/error.h" + +enum +{ + Maxhandler= 32+16, /* max number of interrupt handlers */ +}; + +typedef struct Handler Handler; +struct Handler +{ + void (*r)(Ureg*, void*); + void *arg; + Handler *next; + int edge; + int nintr; + ulong ticks; + int maxtick; +}; + +struct +{ + Handler *ivec[128]; + Handler h[Maxhandler]; + int free; +} halloc; + +void faultpower(Ureg *ur, ulong addr, int read); +void kernfault(Ureg*, int); +void syscall(Ureg* ureg); +void noted(Ureg*, ulong); +void reset(void); + +char *excname[] = +{ + "reserved 0", + "system reset", + "machine check", + "data access", + "instruction access", + "external interrupt", + "alignment", + "program exception", + "floating-point unavailable", + "decrementer", + "i/o controller interface error", + "reserved B", + "system call", + "trace trap", + "floating point assist", + "reserved F", + "software emulation", + "ITLB miss", + "DTLB miss", + "ITLB error", + "DTLB error", + "reserved 15", + "reserved 16", + "reserved 17", + "reserved 18", + "reserved 19", + "reserved 1A", + "reserved 1B", + "data breakpoint", + "instruction breakpoint", + "peripheral breakpoint", + "development port", + /* the following are made up on a program exception */ + "floating point exception", /* 20: FPEXC */ + "illegal instruction", /* 21 */ + "privileged instruction", /* 22 */ + "trap", /* 23 */ + "illegal operation", /* 24 */ + "breakpoint", /* 25 */ +}; + +char *fpcause[] = +{ + "inexact operation", + "division by zero", + "underflow", + "overflow", + "invalid operation", +}; +char *fpexcname(Ureg*, ulong, char*); +#define FPEXPMASK 0xfff80300 /* Floating exception bits in fpscr */ + + +char *regname[]={ + "CAUSE", "SRR1", + "PC", "GOK", + "LR", "CR", + "XER", "CTR", + "R0", "R1", + "R2", "R3", + "R4", "R5", + "R6", "R7", + "R8", "R9", + "R10", "R11", + "R12", "R13", + "R14", "R15", + "R16", "R17", + "R18", "R19", + "R20", "R21", + "R22", "R23", + "R24", "R25", + "R26", "R27", + "R28", "R29", + "R30", "R31", +}; + +void +sethvec(int v, void (*r)(void)) +{ + ulong *vp, pa, o; + + vp = KADDR(v); + vp[0] = 0x7c1043a6; /* MOVW R0, SPR(SPRG0) */ + vp[1] = 0x7c0802a6; /* MOVW LR, R0 */ + vp[2] = 0x7c1243a6; /* MOVW R0, SPR(SPRG2) */ + pa = PADDR(r); + o = pa >> 25; + if(o != 0 && o != 0x7F){ + /* a branch too far */ + vp[3] = (15<<26)|(pa>>16); /* MOVW $r&~0xFFFF, R0 */ + vp[4] = (24<<26)|(pa&0xFFFF); /* OR $r&0xFFFF, R0 */ + vp[5] = 0x7c0803a6; /* MOVW R0, LR */ + vp[6] = 0x4e800021; /* BL (LR) */ + }else + vp[3] = (18<<26)|(pa&0x3FFFFFC)|3; /* bla */ + dcflush(vp, 8*sizeof(ulong)); +} + +void +sethvec2(int v, void (*r)(void)) +{ + ulong *vp; + + vp = (ulong*)KADDR(v); + vp[0] = (18<<26)|(PADDR(r))|2; /* ba */ + dcflush(vp, sizeof(*vp)); +} + +void +trap(Ureg *ur) +{ + int ecode; + static struct {int callsched;} c = {1}; + int user = (ur->srr1 & MSR_PR) != 0; + char buf[ERRLEN]; + + m->intrts = fastticks(nil); + ecode = (ur->cause >> 8) & 0xff; + +if(ur->status & MSR_RI == 0) +print("double fault?: ecode = %d\n", ecode); + switch(ecode){ + case CEI: + intr(ur); + break; + + case CDEC: + clockintr(ur); + break; + case CIMISS: + faultpower(ur, ur->pc, 1); + break; + case CITLBE: + faultpower(ur, ur->pc, 1); + break; + case CDMISS: +if(0) print("CDMISS: %lux %lux\n", m->epn, m->cmp1); + faultpower(ur, m->dar, 1); + break; + case CDTLBE: + faultpower(ur, m->dar, !(m->dsisr & (1<<25))); + break; + + case CEMU: + print("CEMU: pc=#%lux op=#%8.8lux\n", ur->pc, *(ulong*)ur->pc); + sprint(buf, "sys: trap: illegal op addr=0x%lux", ur->pc); + postnote(up, 1, buf, NDebug); + break; + + case CSYSCALL: + syscall(ur); + break; + + case CPROG: + if(ur->status & (1<<19)) + ecode = 0x20; + if(ur->status & (1<<18)) + ecode = 0x21; + if(ur->status & (1<<17)) + ecode = 0x22; + goto Default; + + Default: + default: + spllo(); + print("ecode = %d\n", ecode); + if(ecode < 0 || ecode >= 0x1F) + ecode = 0x1F; + print("kernel %s pc=0x%lux\n", excname[ecode], ur->pc); + dumpregs(ur); + dumpstack(); + delay(100); + reset(); + } + + splhi(); + if(user) + notify(ur); +} + +void +faultpower(Ureg *ureg, ulong addr, int read) +{ + int user, insyscall, n; + char buf[ERRLEN]; + user = (ureg->srr1 & MSR_PR) != 0; +if(0)print("fault: pid=%ld pc = %lux, addr = %lux read = %d user = %d stack=%ulx\n", up->pid, ureg->pc, addr, read, user, &ureg); + insyscall = up->insyscall; + up->insyscall = 1; + spllo(); + n = fault(addr, read); + if(n < 0){ + if(!user){ + dumpregs(ureg); + panic("fault: 0x%lux\n", addr); + } +print("fault: pid=%ld pc = %lux, addr = %lux read = %d user = %d stack=%ulx\n", up->pid, ureg->pc, addr, read, user, &ureg); +dumpregs(ureg); + sprint(buf, "sys: trap: fault %s addr=0x%lux", + read? "read" : "write", addr); + postnote(up, 1, buf, NDebug); + } + up->insyscall = insyscall; +} + +void +spurious(Ureg *ur, void *a) +{ + USED(a); + print("SPURIOUS interrupt pc=0x%lux cause=0x%lux\n", + ur->pc, ur->cause); + panic("bad interrupt"); +} + +#define LEV(n) (((n)<<1)|1) +#define IRQ(n) (((n)<<1)|0) + +Lock veclock; + +void +trapinit(void) +{ + int i; + IMM *io; + + + io = m->iomem; + io->sypcr &= ~(1<<3); /* disable watchdog (821/823) */ + io->simask = 0; /* mask all */ + io->siel = ~0; /* edge sensitive, wake on all */ + io->cicr = 0; /* disable CPM interrupts */ + io->cipr = ~0; /* clear all interrupts */ + io->cimr = 0; /* mask all events */ + io->cicr = (0xE1<<16)|(CPIClevel<<13)|(0x1F<<8); + io->cicr |= 1 << 7; /* enable */ + io->tbscr = 1; /* TBE */ + io->simask |= 1<<(31-LEV(CPIClevel)); /* CPM's level */ + eieio(); + putdec(~0); + + /* + * set all exceptions to trap + */ + for(i = 0x0; i < 0x2000; i += 0x100) + sethvec(i, trapvec); + + //sethvec(CEI<<8, intrvec); + sethvec2(CIMISS<<8, itlbmiss); + sethvec2(CDMISS<<8, dtlbmiss); + sethvec2(CDTLBE<<8, dtlberror); +} + +void +intrenable(int v, void (*r)(Ureg*, void*), void *arg, int, char *name) +{ + Handler *h; + IMM *io; + + if(halloc.free >= Maxhandler) + panic("out of interrupt handlers"); + v -= VectorPIC; + if(v < 0 || v >= nelem(halloc.ivec)) + panic("intrenable(%d)", v+VectorPIC); + ilock(&veclock); + if(v < VectorCPIC || (h = halloc.ivec[v]) == nil){ + h = &halloc.h[halloc.free++]; + h->next = halloc.ivec[v]; + halloc.ivec[v] = h; + } + h->r = r; + h->arg = arg; + + /* + * enable corresponding interrupt in SIU/CPM + */ + + eieio(); + io = m->iomem; + if(v >= VectorCPIC){ + v -= VectorCPIC; + io->cimr |= 1<<(v&0x1F); + } + else if(v >= VectorIRQ) + io->simask |= 1<<(31-IRQ(v&7)); + else + io->simask |= 1<<(31-LEV(v)); + eieio(); + iunlock(&veclock); +} + +void +intr(Ureg *ur) +{ + int b, v; + IMM *io; + Handler *h; + long t0; + + ur->cause &= ~0xff; + io = m->iomem; + b = io->sivec>>2; + v = b>>1; + if(b & 1) { + if(v == CPIClevel){ + io->civr = 1; + eieio(); + v = VectorCPIC+(io->civr>>11); + } + }else + v += VectorIRQ; + ur->cause |= v; + h = halloc.ivec[v]; + if(h == nil){ + print("unknown interrupt %d pc=0x%lux\n", v, ur->pc); + uartwait(); + return; + } + if(h->edge){ + io->sipend |= 1<<(31-b); + eieio(); + } + /* + * call the interrupt handlers + */ + do { + h->nintr++; + t0 = getdec(); + (*h->r)(ur, h->arg); + t0 -= getdec(); + h->ticks += t0; + if(h->maxtick < t0) + h->maxtick = t0; + h = h->next; + } while(h != nil); + if(v >= VectorCPIC) + io->cisr |= 1<<(v-VectorCPIC); + eieio(); +} + +int +intrstats(char *buf, int bsize) +{ + Handler *h; + int i, n; + + n = 0; + for(i=0; inintr) + n += snprint(buf+n, bsize-n, "%3d %ud %uld %ud\n", i, h->nintr, h->ticks, h->maxtick); + return n; +} + +char* +fpexcname(Ureg *ur, ulong fpscr, char *buf) +{ + int i; + char *s; + ulong fppc; + + fppc = ur->pc; + s = 0; + fpscr >>= 3; /* trap enable bits */ + fpscr &= (fpscr>>22); /* anded with exceptions */ + for(i=0; i<5; i++) + if(fpscr & (1<status, ur->pc, ur->sp); + dumpregs(ur); + l.sp = ur->sp; + l.pc = ur->pc; + dumpstack(); + for(;;) + sched(); +} + +void +dumpstack(void) +{ + ulong l, v; + int i; + + if(up == 0) + return; + i = 0; + for(l=(ulong)&l; l<(ulong)(up->kstack+KSTACK); l+=4){ + v = *(ulong*)l; + if(KTZERO < v && v < (ulong)etext){ + print("%lux=%lux, ", l, v); + if(i++ == 4){ + print("\n"); + i = 0; + } + } + } +} + +void +dumpregs(Ureg *ur) +{ + int i; + ulong *l; + if(up) { + print("registers for %s %ld\n", up->text, up->pid); + if(ur->srr1 & MSR_PR == 0) + if(ur->usp < (ulong)up->kstack || ur->usp > (ulong)up->kstack+KSTACK) + print("invalid stack ptr\n"); + } + else + print("registers for kernel\n"); + + print("dsisr\t%.8lux\tdar\t%.8lux\n", getdsisr(), getdar()); + l = &ur->cause; + for(i=0; ikpfun)(up->kparg); + pexit("", 0); +} + +void +kprocchild(Proc *p, void (*func)(void*), void *arg) +{ + p->sched.pc = (ulong)linkproc; + p->sched.sp = (ulong)p->kstack+KSTACK; + + p->kpfun = func; + p->kparg = arg; +} + +int +isvalid_pc(ulong pc) +{ + return KERNPC(pc) && (pc&3) == 0; +} + +void +dumplongs(char*, ulong*, int) +{ +} + +void +reset(void) +{ + IMM *io; + + io = m->iomem; + io->plprcrk = KEEP_ALIVE_KEY; // unlock + io->plprcr |= IBIT(24); // enable checkstop reset + putmsr(getmsr() & ~(MSR_ME|MSR_RI)); +predawn = 1; +print("reset = %ulx\n", getmsr()); + delay(1000); + // cause checkstop -> causes reset + *(uchar*)(0xdeadbeef) = 0; + *(ulong*)(0) = 0; +} + +/* + * called in sysfile.c + */ +void +evenaddr(ulong addr) +{ + if(addr & 3){ + postnote(up, 1, "sys: odd address", NDebug); + error(Ebadarg); + } +} + +long +execregs(ulong entry, ulong ssize, ulong nargs) +{ + ulong *sp; + Ureg *ureg; + + sp = (ulong*)(USTKTOP - ssize); + *--sp = nargs; + + ureg = up->dbgreg; + ureg->usp = (ulong)sp; + ureg->pc = entry; + return USTKTOP-BY2WD; /* address of user-level clock */ +} + +void +forkchild(Proc *p, Ureg *ur) +{ + Ureg *cur; + + p->sched.sp = (ulong)p->kstack+KSTACK-UREGSIZE; + p->sched.pc = (ulong)forkret; + + cur = (Ureg*)(p->sched.sp+2*BY2WD); + memmove(cur, ur, sizeof(Ureg)); + cur->r3 = 0; + + /* Things from bottom of syscall we never got to execute */ + p->psstate = 0; + p->insyscall = 0; +} + +ulong +userpc(void) +{ + Ureg *ureg; + + ureg = (Ureg*)up->dbgreg; + return ureg->pc; +} + + +/* This routine must save the values of registers the user is not + * permitted to write from devproc and then restore the saved values + * before returning + */ +void +setregisters(Ureg *xp, char *pureg, char *uva, int n) +{ + ulong status; + + status = xp->status; + memmove(pureg, uva, n); + xp->status = status; +} + +/* Give enough context in the ureg to produce a kernel stack for + * a sleeping process + */ +void +setkernur(Ureg* ureg, Proc* p) +{ + ureg->pc = p->sched.pc; + ureg->sp = p->sched.sp+4; +} + +ulong +dbgpc(Proc *p) +{ + Ureg *ureg; + + ureg = p->dbgreg; + if(ureg == 0) + return 0; + + return ureg->pc; +} + +/* + * system calls + */ +#include "../port/systab.h" + +/* + * Syscall should be called directly from assembler without going through trap(). + */ +void +syscall(Ureg* ureg) +{ + ulong sp; + long ret; + int i, scallnr; + + if((ureg->srr1 & MSR_PR) == 0) + panic("syscall: srr1 0x%4.4uX\n", ureg->srr1); + + m->syscall++; + up->insyscall = 1; + up->pc = ureg->pc; + up->dbgreg = ureg; + + scallnr = ureg->r3; +//print("scall %s lr =%lux\n", sysctab[scallnr], ureg->lr); + up->scallnr = scallnr; + spllo(); + + sp = ureg->usp; + up->nerrlab = 0; + ret = -1; + if(!waserror()){ + if(scallnr >= nsyscall){ + pprint("bad sys call number %d pc %lux\n", scallnr, ureg->pc); + postnote(up, 1, "sys: bad sys call", NDebug); + error(Ebadarg); + } + + if(sp<(USTKTOP-BY2PG) || sp>(USTKTOP-sizeof(Sargs)-BY2WD)) + validaddr(sp, sizeof(Sargs)+BY2WD, 0); + + up->s = *((Sargs*)(sp+BY2WD)); + up->psstate = sysctab[scallnr]; + + ret = systab[scallnr](up->s.args); + poperror(); + } + if(up->nerrlab){ + print("bad errstack [%d]: %d extra\n", scallnr, up->nerrlab); + print("scall %s lr =%lux\n", sysctab[scallnr], ureg->lr); + for(i = 0; i < NERR; i++) + print("sp=%lux pc=%lux\n", up->errlab[i].sp, up->errlab[i].pc); + panic("error stack"); + } + + up->insyscall = 0; + up->psstate = 0; + + /* + * Put return value in frame. On the x86 the syscall is + * just another trap and the return value from syscall is + * ignored. On other machines the return value is put into + * the results register by caller of syscall. + */ + ureg->r3 = ret; + + if(scallnr == NOTED) + noted(ureg, *(ulong*)(sp+BY2WD)); + + if(scallnr!=RFORK && (up->procctl || up->nnote)){ + splhi(); + notify(ureg); + } +} +/* + * Call user, if necessary, with note. + * Pass user the Ureg struct and the note on his stack. + */ +int +notify(Ureg* ur) +{ + int l; + ulong s, sp; + Note *n; + + if(up->procctl) + procctl(up); + if(up->nnote == 0) + return 0; + + s = spllo(); + qlock(&up->debug); + up->notepending = 0; + n = &up->note[0]; + if(strncmp(n->msg, "sys:", 4) == 0){ + l = strlen(n->msg); + if(l > ERRLEN-15) /* " pc=0x12345678\0" */ + l = ERRLEN-15; + sprint(n->msg+l, " pc=0x%.8lux", ur->pc); + } + + if(n->flag!=NUser && (up->notified || up->notify==0)){ + if(n->flag == NDebug) + pprint("suicide: %s\n", n->msg); + qunlock(&up->debug); + pexit(n->msg, n->flag!=NDebug); + } + + if(up->notified) { + qunlock(&up->debug); + splhi(); + return 0; + } + + if(!up->notify) { + qunlock(&up->debug); + pexit(n->msg, n->flag!=NDebug); + } + sp = ur->usp & ~(BY2V-1); + sp -= sizeof(Ureg); + + if(!okaddr((ulong)up->notify, BY2WD, 0) || + !okaddr(sp-ERRLEN-4*BY2WD, sizeof(Ureg)+ERRLEN+4*BY2WD, 1)) { + pprint("suicide: bad address or sp in notify\n"); + qunlock(&up->debug); + pexit("Suicide", 0); + } + + memmove((Ureg*)sp, ur, sizeof(Ureg)); + *(Ureg**)(sp-BY2WD) = up->ureg; /* word under Ureg is old up->ureg */ + up->ureg = (void*)sp; + sp -= BY2WD+ERRLEN; + memmove((char*)sp, up->note[0].msg, ERRLEN); + sp -= 3*BY2WD; + *(ulong*)(sp+2*BY2WD) = sp+3*BY2WD; /* arg 2 is string */ + ur->r1 = (long)up->ureg; /* arg 1 is ureg* */ + ((ulong*)sp)[1] = (ulong)up->ureg; /* arg 1 0(FP) is ureg* */ + ((ulong*)sp)[0] = 0; /* arg 0 is pc */ + ur->usp = sp; + ur->pc = (ulong)up->notify; + up->notified = 1; + up->nnote--; + memmove(&up->lastnote, &up->note[0], sizeof(Note)); + memmove(&up->note[0], &up->note[1], up->nnote*sizeof(Note)); + + qunlock(&up->debug); + splx(s); + return 1; +} + + +/* + * Return user to state before notify() + */ +void +noted(Ureg* ureg, ulong arg0) +{ + Ureg *nureg; + ulong oureg, sp; + + qlock(&up->debug); + if(arg0!=NRSTR && !up->notified) { + qunlock(&up->debug); + pprint("call to noted() when not notified\n"); + pexit("Suicide", 0); + } + up->notified = 0; + + nureg = up->ureg; /* pointer to user returned Ureg struct */ + + /* sanity clause */ + oureg = (ulong)nureg; + if(!okaddr((ulong)oureg-BY2WD, BY2WD+sizeof(Ureg), 0)){ + pprint("bad ureg in noted or call to noted when not notified\n"); + qunlock(&up->debug); + pexit("Suicide", 0); + } + + memmove(ureg, nureg, sizeof(Ureg)); + + switch(arg0){ + case NCONT: + case NRSTR: + if(!okaddr(nureg->pc, 1, 0) || !okaddr(nureg->usp, BY2WD, 0)){ + pprint("suicide: trap in noted\n"); + qunlock(&up->debug); + pexit("Suicide", 0); + } + up->ureg = (Ureg*)(*(ulong*)(oureg-BY2WD)); + qunlock(&up->debug); + break; + + case NSAVE: + if(!okaddr(nureg->pc, BY2WD, 0) + || !okaddr(nureg->usp, BY2WD, 0)){ + pprint("suicide: trap in noted\n"); + qunlock(&up->debug); + pexit("Suicide", 0); + } + qunlock(&up->debug); + sp = oureg-4*BY2WD-ERRLEN; + splhi(); + ureg->sp = sp; + ((ulong*)sp)[1] = oureg; /* arg 1 0(FP) is ureg* */ + ((ulong*)sp)[0] = 0; /* arg 0 is pc */ + break; + + default: + pprint("unknown noted arg 0x%lux\n", arg0); + up->lastnote.flag = NDebug; + /* fall through */ + + case NDFLT: + if(up->lastnote.flag == NDebug) + pprint("suicide: %s\n", up->lastnote.msg); + qunlock(&up->debug); + pexit(up->lastnote.msg, up->lastnote.flag!=NDebug); + } +} diff --git a/mtx/uarti8250.c b/mtx/uarti8250.c new file mode 100644 index 0000000000000000000000000000000000000000..b2ca39d58a0a3a1f00f37dde8c9d5863308c41a3 --- /dev/null +++ b/mtx/uarti8250.c @@ -0,0 +1,648 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +/* + * 8250 UART and compatibles. + */ +enum { + Uart0 = 0x3F8, /* COM1 */ + Uart0IRQ = 4, + Uart1 = 0x2F8, /* COM2 */ + Uart1IRQ = 3, + + UartFREQ = 1843200, +}; + +enum { /* I/O ports */ + 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 */ + Dll = 0, /* Divisor Latch LSB */ + Dlm = 1, /* Divisor Latch MSB */ +}; + +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, + Ife = 0xC0, /* FIFOs enabled */ +}; + +enum { /* Fcr */ + FIFOena = 0x01, /* FIFO enable */ + FIFOrclr = 0x02, /* clear Rx FIFO */ + FIFOtclr = 0x04, /* clear Tx FIFO */ + 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 */ + 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, /* Tramsmitter 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, /* Data Set Ready */ +}; + +typedef struct Ctlr { + int io; + int irq; + int tbdf; + int iena; + + uchar sticky[8]; + + Lock; + int fifo; + int fena; +} Ctlr; + +extern PhysUart i8250physuart; + +static Ctlr i8250ctlr[2] = { +{ .io = Uart0, + .irq = Uart0IRQ, + .tbdf = BUSUNKNOWN, }, + +{ .io = Uart1, + .irq = Uart1IRQ, + .tbdf = BUSUNKNOWN, }, +}; + +static Uart i8250uart[2] = { +{ .regs = &i8250ctlr[0], + .name = "COM1", + .freq = UartFREQ, + .phys = &i8250physuart, + .special=0, + .next = &i8250uart[1], }, + +{ .regs = &i8250ctlr[1], + .name = "COM2", + .freq = UartFREQ, + .phys = &i8250physuart, + .special=0, + .next = nil, }, +}; + +static Uart* consuart; + +#define csr8r(c, r) inb((c)->io+(r)) +#define csr8w(c, r, v) outb((c)->io+(r), (c)->sticky[(r)]|(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 = malloc(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)%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, + (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 on) +{ + int i; + Ctlr *ctlr; + + /* + * Toggle FIFOs: + * if none, do nothing; + * reset the Rx and Tx FIFOs; + * empty the Rx buffer and clear any interrupt conditions; + * if enabling, try to turn them on. + */ + ctlr = uart->regs; + + ilock(ctlr); + if(!ctlr->fifo){ + csr8w(ctlr, Fcr, FIFOtclr|FIFOrclr); + for(i = 0; i < 16; i++){ + csr8r(ctlr, Iir); + csr8r(ctlr, Rbr); + } + + ctlr->fena = 0; + if(on){ + csr8w(ctlr, Fcr, FIFO4|FIFOena); + if(!(csr8r(ctlr, Iir) & Ife)) + ctlr->fifo = 1; + ctlr->fena = 1; + } + } + 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': + default: + break; + } + 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) +{ + ulong bgc; + Ctlr *ctlr; + + /* + * Set the Baud rate by calculating and setting the Baud rate + * Generator Constant. This will work with fairly non-standard + * Baud rates. + */ + if(uart->freq == 0 || baud <= 0) + return -1; + bgc = (uart->freq+8*baud-1)/(16*baud); + + ctlr = uart->regs; + csr8w(ctlr, Lcr, Dlab); + outb(ctlr->io+Dlm, bgc>>8); + outb(ctlr->io+Dll, bgc); + csr8w(ctlr, Lcr, 0); + + uart->baud = baud; + + return 0; +} + +static void +i8250break(Uart* uart, int ms) +{ + Ctlr *ctlr; + + /* + * 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 +i8250kick(Uart* uart) +{ + int i; + Ctlr *ctlr; + + if(uart->cts == 0 || uart->blocked) + 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 + */ + ctlr = uart->regs; + for(i = 0; i < 128; i++){ + if(!(csr8r(ctlr, Lsr) & Thre)) + break; + if(uart->op >= uart->oe && uartstageoutput(uart) == 0) + break; + outb(ctlr->io+Thr, *(uart->op++)); + } +} + +static void +i8250interrupt(Ureg*, void* arg) +{ + Ctlr *ctlr; + Uart *uart; + int iir, lsr, old, r; + + uart = arg; + + ctlr = uart->regs; + 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 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 & 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 0x%2.2uX\n", iir); + break; + } + } +} + +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); +} + +static void +i8250enable(Uart* uart, int ie) +{ + Ctlr *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. + */ + ctlr = uart->regs; + if(ie){ + if(ctlr->iena == 0){ + intrenable(ctlr->irq, i8250interrupt, uart, ctlr->tbdf, uart->name); + ctlr->iena = 1; + } + ctlr->sticky[Ier] = Ethre|Erda; + ctlr->sticky[Mcr] |= Ie; + } + else{ + ctlr->sticky[Ier] = 0; + ctlr->sticky[Mcr] = 0; + } + csr8w(ctlr, Ier, ctlr->sticky[Ier]); + csr8w(ctlr, Mcr, ctlr->sticky[Mcr]); + + (*uart->phys->dtr)(uart, 1); + (*uart->phys->rts)(uart, 1); +} + +static Uart* +i8250pnp(void) +{ + return i8250uart; +} + +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, +}; + +int +serialgetc(void) +{ + return -1; +} + +static void +i8250putc(Ctlr* ctlr, int c) +{ + int i; + + for(i = 0; i < 128; i++){ + if(csr8r(ctlr, Lsr) & Thre) + break; + delay(1); + } + outb(ctlr->io+Thr, c); +} + +static void +i8250puts(char* s, int n) +{ + Ctlr *ctlr; + Uart *uart; + + if((uart = consuart) == nil) + return; + + ctlr = uart->regs; + while(n--){ + if(*s == '\n') + i8250putc(ctlr, '\r'); + i8250putc(ctlr, *s++); + } +} + +void +i8250console(void) +{ + Uart *uart; + int n; + char *cmd, *p; + + if((p = getconf("console")) == nil) + return; + n = strtoul(p, &cmd, 0); + if(p == cmd) + return; + switch(n){ + default: + return; + case 0: + uart = &i8250uart[0]; + break; + case 1: + uart = &i8250uart[1]; + break; + } + + if(*cmd == 0) + cmd = "b9600 l8 pn s1"; + uartctl(uart, cmd); + (*uart->phys->enable)(uart, 0); + + consuart = uart; + serialputs = i8250puts; + uart->console = 1; +}