omap/fns.h 664 0 0 12345 12072511237 11132ustar00millersys#define checkmmu(a, b) #define countpagerefs(a, b) #include "../port/portfns.h" extern int led(int, int); extern void ledexit(int); extern void delay(int); extern void _uartputs(char*, int); extern int _uartprint(char*, ...); #pragma varargck argpos _uartprint 1 extern void archreboot(void); extern void archreset(void); extern void cachedinv(void); extern void cachedinvse(void*, int); extern void cachedwb(void); extern void cachedwbinv(void); extern void cachedwbinvse(void*, int); extern void cachedwbse(void*, int); extern void cacheiinv(void); extern void cacheinfo(int level, Memcache *cp); extern void cacheuwbinv(void); extern uintptr cankaddr(uintptr pa); extern void chkmissing(void); extern void clockshutdown(void); extern int clz(ulong); extern int cmpswap(long*, long, long); extern void coherence(void); extern void configscreengpio(void); extern u32int controlget(void); extern u32int cpctget(void); extern u32int cpidget(void); extern ulong cprd(int cp, int op1, int crn, int crm, int op2); extern ulong cprdsc(int op1, int crn, int crm, int op2); extern void cpuidprint(void); extern void cpwr(int cp, int op1, int crn, int crm, int op2, ulong val); extern void cpwrsc(int op1, int crn, int crm, int op2, ulong val); #define cycles(ip) *(ip) = lcycles() extern u32int dacget(void); extern void dacput(u32int); extern void dmainit(void); extern int dmastart(void *, int, void *, int, uint, Rendez *, int *); extern void dmatest(void); extern u32int farget(void); extern ulong fprd(int fpreg); extern void fpwr(int fpreg, ulong val); extern u32int fsrget(void); extern u32int getscr(void); extern u32int getpsr(void); extern ulong getwayssets(void); extern u32int ifsrget(void); extern void intrsoff(void); extern int isaconfig(char*, int, ISAConf*); extern int isdmadone(int); extern int ispow2(uvlong); extern void kbdenable(void); extern void l2cacheuinv(void); extern void l2cacheuwb(void); extern void l2cacheuwbinv(void); extern void lastresortprint(char *buf, long bp); extern int log2(ulong); extern void machinit(void); extern void mmuidmap(uintptr phys, int mbs); extern void mmuinvalidate(void); /* 'mmu' or 'tlb'? */ extern void mmuinvalidateaddr(u32int); /* 'mmu' or 'tlb'? */ extern void mousectl(Cmdbuf *cb); extern u32int pidget(void); extern void pidput(u32int); extern vlong probeaddr(uintptr); extern void procrestore(Proc *); extern void procsave(Proc*); extern void procsetup(Proc*); extern void _reset(void); extern void screenclockson(void); extern void screeninit(void); extern void serialputs(char* s, int n); extern void setcachelvl(int); extern void setr13(int, u32int*); extern int tas(void *); extern u32int ttbget(void); extern void ttbput(u32int); extern void watchdoginit(void); extern int irqenable(int, void (*)(Ureg*, void*), void*, char*); extern int irqdisable(int, void (*)(Ureg*, void*), void*, char*); #define intrenable(i, f, a, b, n) irqenable((i), (f), (a), (n)) #define intrdisable(i, f, a, b, n) irqdisable((i), (f), (a), (n)) extern void vectors(void); extern void vtable(void); /* dregs, going away */ extern int inb(int); extern void outb(int, int); /* * Things called in main. */ extern void archconfinit(void); extern void clockinit(void); extern int i8250console(void); extern void links(void); extern void mmuinit(void); extern void touser(uintptr); extern void trapinit(void); extern int fpiarm(Ureg*); extern int fpudevprocio(Proc*, void*, long, uintptr, int); extern void fpuinit(void); extern void fpunoted(void); extern void fpunotify(Ureg*); extern void fpuprocrestore(Proc*); extern void fpuprocsave(Proc*); extern void fpusysprocsetup(Proc*); extern void fpusysrfork(Ureg*); extern void fpusysrforkchild(Proc*, Ureg*, Proc*); extern int fpuemu(Ureg*); /* * Miscellaneous machine dependent stuff. */ extern char* getenv(char*, char*, int); char* getconf(char*); uintptr mmukmap(uintptr, uintptr, usize); uintptr mmukunmap(uintptr, uintptr, usize); extern void* mmuuncache(void*, usize); extern void* ucalloc(usize); extern Block* ucallocb(int); extern void* ucallocalign(usize size, int align, int span); extern void ucfree(void*); extern void ucfreeb(Block*); /* * Things called from port. */ extern void delay(int); /* only scheddump() */ extern int islo(void); extern void microdelay(int); /* only edf.c */ extern void evenaddr(uintptr); extern void idlehands(void); extern void setkernur(Ureg*, Proc*); /* only devproc.c */ extern void* sysexecregs(uintptr, ulong, int); extern void sysprocsetup(Proc*); /* * PCI stuff. */ int cas32(void*, u32int, u32int); int tas32(void*); #define CASU(p, e, n) cas32((p), (u32int)(e), (u32int)(n)) #define CASV(p, e, n) cas32((p), (u32int)(e), (u32int)(n)) #define CASW(addr, exp, new) cas32((addr), (exp), (new)) #define TAS(addr) tas32(addr) extern void forkret(void); extern int userureg(Ureg*); void* vmap(uintptr, usize); void vunmap(void*, usize); extern void kexit(Ureg*); #define getpgcolor(a) 0 #define kmapinval() #define PTR2UINT(p) ((uintptr)(p)) #define UINT2PTR(i) ((void*)(i)) #define waserror() (up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1])) #define KADDR(pa) UINT2PTR(KZERO | ((uintptr)(pa) & ~KSEGM)) #define PADDR(va) PTR2UINT(PHYSDRAM | ((uintptr)(va) & ~KSEGM)) #define wave(c) *(ulong *)PHYSCONS = (c) #define MASK(v) ((1UL << (v)) - 1) /* mask `v' bits wide */ omap/arm.h 664 0 0 20435 12072515131 11117ustar00millersys/* * arm-specific definitions for cortex-a8 * these are used in C and assembler * * `cortex' refers specifically to the cortex-a8. */ /* * Program Status Registers */ #define PsrMusr 0x00000010 /* mode */ #define PsrMfiq 0x00000011 #define PsrMirq 0x00000012 #define PsrMsvc 0x00000013 /* `protected mode for OS' */ #define PsrMmon 0x00000016 /* `secure monitor' (trustzone hyper) */ #define PsrMabt 0x00000017 #define PsrMund 0x0000001B #define PsrMsys 0x0000001F /* `privileged user mode for OS' (trustzone) */ #define PsrMask 0x0000001F #define PsrDfiq 0x00000040 /* disable FIQ interrupts */ #define PsrDirq 0x00000080 /* disable IRQ interrupts */ #define PsrV 0x10000000 /* overflow */ #define PsrC 0x20000000 /* carry/borrow/extend */ #define PsrZ 0x40000000 /* zero */ #define PsrN 0x80000000 /* negative/less than */ /* * Coprocessors */ #define CpFP 10 /* float FP, VFP cfg. */ #define CpDFP 11 /* double FP */ #define CpSC 15 /* System Control */ /* * Primary (CRn) CpSC registers. */ #define CpID 0 /* ID and cache type */ #define CpCONTROL 1 /* miscellaneous control */ #define CpTTB 2 /* Translation Table Base(s) */ #define CpDAC 3 /* Domain Access Control */ #define CpFSR 5 /* Fault Status */ #define CpFAR 6 /* Fault Address */ #define CpCACHE 7 /* cache/write buffer control */ #define CpTLB 8 /* TLB control */ #define CpCLD 9 /* L2 Cache Lockdown, op1==1 */ #define CpTLD 10 /* TLB Lockdown, with op2 */ #define CpVECS 12 /* vector bases, op1==0, Crm==0, op2s (cortex) */ #define CpPID 13 /* Process ID */ #define CpDTLB 15 /* TLB, L1 cache stuff (cortex) */ /* * CpTTB op1==0, Crm==0 opcode2 values. */ #define CpTTB0 0 #define CpTTB1 1 /* cortex */ #define CpTTBctl 2 /* cortex */ /* * CpFSR op1==0, Crm==0 opcode 2 values. */ #define CpDFSR 0 /* data fault status */ #define CpIFSR 1 /* instruction fault status */ /* * CpID Secondary (CRm) registers. */ #define CpIDidct 0 /* * CpID op1==0 opcode2 fields. * the cortex has more op1 codes for cache size, etc. */ #define CpIDid 0 /* main ID */ #define CpIDct 1 /* cache type */ #define CpIDtlb 3 /* tlb type (cortex) */ #define CpIDmpid 5 /* multiprocessor id (cortex) */ /* CpIDid op1 values */ #define CpIDcsize 1 /* cache size (cortex) */ #define CpIDcssel 2 /* cache size select (cortex) */ /* * CpCONTROL op2 codes, op1==0, Crm==0. */ #define CpMainctl 0 #define CpAuxctl 1 #define CpCPaccess 2 /* * CpCONTROL: op1==0, CRm==0, op2==CpMainctl. * main control register. * cortex/armv7 has more ops and CRm values. */ #define CpCmmu 0x00000001 /* M: MMU enable */ #define CpCalign 0x00000002 /* A: alignment fault enable */ #define CpCdcache 0x00000004 /* C: data cache on */ #define CpCsbo (3<<22|1<<18|1<<16|017<<3) /* must be 1 (armv7) */ #define CpCsbz (CpCtre|1<<26|CpCve|1<<15|7<<7) /* must be 0 (armv7) */ #define CpCsw (1<<10) /* SW: SWP(B) enable (deprecated in v7) */ #define CpCpredict 0x00000800 /* Z: branch prediction (armv7) */ #define CpCicache 0x00001000 /* I: instruction cache on */ #define CpChv 0x00002000 /* V: high vectors */ #define CpCrr (1<<14) /* RR: round robin vs random cache replacement */ #define CpCha (1<<17) /* HA: hw access flag enable */ #define CpCdz (1<<19) /* DZ: divide by zero fault enable */ #define CpCfi (1<<21) /* FI: fast intrs */ #define CpCve (1<<24) /* VE: intr vectors enable */ #define CpCee (1<<25) /* EE: exception endianness */ #define CpCnmfi (1<<27) /* NMFI: non-maskable fast intrs. */ #define CpCtre (1<<28) /* TRE: TEX remap enable */ #define CpCafe (1<<29) /* AFE: access flag (ttb) enable */ /* * CpCONTROL: op1==0, CRm==0, op2==CpAuxctl. * Auxiliary control register on cortex at least. */ #define CpACcachenopipe (1<<20) /* don't pipeline cache maint. */ #define CpACcp15serial (1<<18) /* serialise CP1[45] ops. */ #define CpACcp15waitidle (1<<17) /* CP1[45] wait-on-idle */ #define CpACcp15pipeflush (1<<16) /* CP1[45] flush pipeline */ #define CpACneonissue1 (1<<12) /* neon single issue */ #define CpACldstissue1 (1<<11) /* force single issue ld, st */ #define CpACissue1 (1<<10) /* force single issue */ #define CpACnobsm (1<<7) /* no branch size mispredicts */ #define CpACibe (1<<6) /* cp15 invalidate & btb enable */ #define CpACl1neon (1<<5) /* cache neon (FP) data in L1 cache */ #define CpACasa (1<<4) /* enable speculative accesses */ #define CpACl1pe (1<<3) /* l1 cache parity enable */ #define CpACl2en (1<<1) /* l2 cache enable; default 1 */ /* * CpCONTROL Secondary (CRm) registers and opcode2 fields. */ #define CpCONTROLscr 1 #define CpSCRscr 0 /* * CpCACHE Secondary (CRm) registers and opcode2 fields. op1==0. * In ARM-speak, 'flush' means invalidate and 'clean' means writeback. */ #define CpCACHEintr 0 /* interrupt (op2==4) */ #define CpCACHEisi 1 /* inner-sharable I cache (v7) */ #define CpCACHEpaddr 4 /* 0: phys. addr (cortex) */ #define CpCACHEinvi 5 /* instruction, branch table */ #define CpCACHEinvd 6 /* data or unified */ // #define CpCACHEinvu 7 /* unified (not on cortex) */ #define CpCACHEva2pa 8 /* va -> pa translation (cortex) */ #define CpCACHEwb 10 /* writeback */ #define CpCACHEinvdse 11 /* data or unified by mva */ #define CpCACHEwbi 14 /* writeback+invalidate */ #define CpCACHEall 0 /* entire (not for invd nor wb(i) on cortex) */ #define CpCACHEse 1 /* single entry */ #define CpCACHEsi 2 /* set/index (set/way) */ #define CpCACHEtest 3 /* test loop */ #define CpCACHEwait 4 /* wait (prefetch flush on cortex) */ #define CpCACHEdmbarr 5 /* wb only (cortex) */ #define CpCACHEflushbtc 6 /* flush branch-target cache (cortex) */ #define CpCACHEflushbtse 7 /* ⋯ or just one entry in it (cortex) */ /* * CpTLB Secondary (CRm) registers and opcode2 fields. */ #define CpTLBinvi 5 /* instruction */ #define CpTLBinvd 6 /* data */ #define CpTLBinvu 7 /* unified */ #define CpTLBinv 0 /* invalidate all */ #define CpTLBinvse 1 /* invalidate single entry */ #define CpTBLasid 2 /* by ASID (cortex) */ /* * CpCLD Secondary (CRm) registers and opcode2 fields for op1==0. (cortex) */ #define CpCLDena 12 /* enables */ #define CpCLDcyc 13 /* cycle counter */ #define CpCLDuser 14 /* user enable */ #define CpCLDenapmnc 0 #define CpCLDenacyc 1 /* * CpCLD Secondary (CRm) registers and opcode2 fields for op1==1. */ #define CpCLDl2 0 /* l2 cache */ #define CpCLDl2aux 2 /* auxiliary control */ /* * l2 cache aux. control */ #define CpCl2ecc (1<<28) /* use ecc, not parity */ #define CpCl2noldforw (1<<27) /* no ld forwarding */ #define CpCl2nowrcomb (1<<25) /* no write combining */ #define CpCl2nowralldel (1<<24) /* no write allocate delay */ #define CpCl2nowrallcomb (1<<23) /* no write allocate combine */ #define CpCl2nowralloc (1<<22) /* no write allocate */ #define CpCl2eccparity (1<<21) /* enable ecc or parity */ #define CpCl2inner (1<<16) /* inner cacheability */ /* other bits are tag ram & data ram latencies */ /* * CpTLD Secondary (CRm) registers and opcode2 fields. */ #define CpTLDlock 0 /* TLB lockdown registers */ #define CpTLDpreload 1 /* TLB preload */ #define CpTLDi 0 /* TLB instr. lockdown reg. */ #define CpTLDd 1 /* " data " " */ /* * CpVECS Secondary (CRm) registers and opcode2 fields. */ #define CpVECSbase 0 #define CpVECSnorm 0 /* (non-)secure base addr */ #define CpVECSmon 1 /* secure monitor base addr */ /* * MMU page table entries. * Mbz (0x10) bit is implementation-defined and must be 0 on the cortex. */ #define Mbz (0<<4) #define Fault 0x00000000 /* L[12] pte: unmapped */ #define Coarse (Mbz|1) /* L1 */ #define Section (Mbz|2) /* L1 1MB */ #define Fine (Mbz|3) /* L1 */ #define Large 0x00000001 /* L2 64KB */ #define Small 0x00000002 /* L2 4KB */ #define Tiny 0x00000003 /* L2 1KB: not in v7 */ #define Buffered 0x00000004 /* L[12]: write-back not -thru */ #define Cached 0x00000008 /* L[12] */ #define Dom0 0 #define Noaccess 0 /* AP, DAC */ #define Krw 1 /* AP */ /* armv7 deprecates AP[2] == 1 & AP[1:0] == 2 (Uro), prefers 3 (new in v7) */ #define Uro 2 /* AP */ #define Urw 3 /* AP */ #define Client 1 /* DAC */ #define Manager 3 /* DAC */ #define AP(n, v) F((v), ((n)*2)+4, 2) #define L1AP(ap) (AP(3, (ap))) #define L2AP(ap) (AP(0, (ap))) /* armv7 */ #define DAC(n, v) F((v), (n)*2, 2) #define HVECTORS 0xffff0000 d setkernur(Ureg*, Proc*); /* only devproc.c */ extern void* sysexecregs(uintptr, ulong, int); extern void sysprocsetup(Proc*); /* * PCI stuff. */ int cas32(void*, u32int, u32int); int tas32(void*); #define CASU(p, e, n) omap/l.s 664 0 0 30726 12072515162 10616ustar00millersys/* * ti omap3530 SoC machine assist * arm cortex-a8 processor * * loader uses R11 as scratch. * R9 and R10 are used for `extern register' variables. * * ARM v7 arch. ref. man. §B1.3.3 that we don't need barriers * around moves to CPSR. */ #include "arm.s" /* * MCR and MRC are counter-intuitively named. * MCR coproc, opcode1, Rd, CRn, CRm[, opcode2] # arm -> coproc * MRC coproc, opcode1, Rd, CRn, CRm[, opcode2] # coproc -> arm */ /* * Entered here from Das U-Boot or another Plan 9 kernel with MMU disabled. * Until the MMU is enabled it is OK to call functions provided * they are within ±32MiB relative and do not require any * local variables or more than one argument (i.e. there is * no stack). */ TEXT _start(SB), 1, $-4 MOVW $setR12(SB), R12 /* load the SB */ SUB $KZERO, R12 ADD $PHYSDRAM, R12 /* SVC mode, interrupts disabled */ MOVW $(PsrDirq|PsrDfiq|PsrMsvc), R1 MOVW R1, CPSR BARRIERS DELAY(printloopret, 1) PUTC('\r') DELAY(printloopnl, 1) PUTC('\n') /* * work around errata */ MRC CpSC, 0, R1, C(CpCONTROL), C(0), CpAuxctl ORR $(CpACissue1|CpACldstissue1), R1 /* fight omap35x errata 3.1.1.9 */ ORR $CpACibe, R1 /* enable cp15 invalidate */ ORR $CpACl1pe, R1 /* enable l1 parity checking */ ORR $CpCalign, R1 /* catch alignment errors */ BIC $CpACasa, R1 /* no speculative accesses */ /* go faster with fewer restrictions */ BIC $(CpACcachenopipe|CpACcp15serial|CpACcp15waitidle|CpACcp15pipeflush), R1 MCR CpSC, 0, R1, C(CpCONTROL), C(0), CpAuxctl ISB MRC CpSC, 1, R1, C(CpCLD), C(CpCLDl2), CpCLDl2aux ORR $CpCl2nowralloc, R1 /* fight cortex errata 460075 */ ORR $(CpCl2ecc|CpCl2eccparity), R1 #ifdef TEDIUM /* * I don't know why this clobbers the system, but I'm tired * of arguing with this fussy processor. To hell with it. */ MCR CpSC, 1, R1, C(CpCLD), C(CpCLDl2), CpCLDl2aux ISB #endif DELAY(printloops, 1) PUTC('P') /* * disable the MMU & caches */ MRC CpSC, 0, R1, C(CpCONTROL), C(0), CpMainctl BIC $(CpCdcache|CpCicache|CpCmmu), R1 ORR $CpCsbo, R1 BIC $CpCsbz, R1 MCR CpSC, 0, R1, C(CpCONTROL), C(0), CpMainctl ISB MRC CpSC, 0, R1, C(CpCONTROL), C(0), CpAuxctl BIC $CpACl2en, R1 /* turn l2 cache off */ MCR CpSC, 0, R1, C(CpCONTROL), C(0), CpAuxctl ISB PUTC('l') DELAY(printloop3, 1) PUTC('a') /* clear Mach */ MOVW $PADDR(MACHADDR), R4 /* address of Mach */ MOVW $0, R0 _machZ: MOVW R0, (R4) ADD $4, R4 CMP.S $PADDR(L1+L1X(0)), R4 /* end at top-level page table */ BNE _machZ /* * set up the MMU page table */ PUTC('n') /* clear all PTEs first, to provide a default */ // MOVW $PADDR(L1+L1X(0)), R4 /* address of PTE for 0 */ _ptenv0: ZEROPTE() CMP.S $PADDR(L1+16*KiB), R4 BNE _ptenv0 DELAY(printloop4, 2) PUTC(' ') /* * set up double map of PHYSDRAM, KZERO to PHYSDRAM for first few MBs, * but only if KZERO and PHYSDRAM differ. */ MOVW $PTEDRAM, R2 /* PTE bits */ MOVW $PHYSDRAM, R3 /* pa */ CMP $KZERO, R3 BEQ no2map MOVW $PADDR(L1+L1X(PHYSDRAM)), R4 /* address of PTE for PHYSDRAM */ MOVW $DOUBLEMAPMBS, R5 _ptdbl: FILLPTE() SUB.S $1, R5 BNE _ptdbl no2map: /* * back up and fill in PTEs for memory at KZERO. * beagle has 1 bank of 256MB of SDRAM at PHYSDRAM; * igepv2 has 1 bank of 512MB at PHYSDRAM. * Map the maximum (512MB). */ PUTC('9') MOVW $PTEDRAM, R2 /* PTE bits */ MOVW $PHYSDRAM, R3 MOVW $PADDR(L1+L1X(KZERO)), R4 /* start with PTE for KZERO */ MOVW $512, R5 /* inner loop count (MBs) */ _ptekrw: /* set PTEs */ FILLPTE() SUB.S $1, R5 /* decrement inner loop count */ BNE _ptekrw /* * back up and fill in PTEs for MMIO * stop somewhere after uarts */ PUTC(' ') MOVW $PTEIO, R2 /* PTE bits */ MOVW $PHYSIO, R3 MOVW $PADDR(L1+L1X(VIRTIO)), R4 /* start with PTE for VIRTIO */ _ptenv2: FILLPTE() CMP.S $PADDR(L1+L1X(PHYSIOEND)), R4 BNE _ptenv2 /* mmu.c sets up the trap vectors later */ /* * set up a temporary stack; avoid data & bss segments */ MOVW $(PHYSDRAM | (128*1024*1024)), R13 /* invalidate caches */ BL cachedinv(SB) MOVW $KZERO, R0 MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEinvi), CpCACHEall ISB MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEwb), CpCACHEwait BARRIERS PUTC('f') /* * turn caches on */ MRC CpSC, 0, R1, C(CpCONTROL), C(0), CpAuxctl ORR $CpACl2en, R1 /* turn l2 cache on */ MCR CpSC, 0, R1, C(CpCONTROL), C(0), CpAuxctl BARRIERS MRC CpSC, 0, R1, C(CpCONTROL), C(0), CpMainctl ORR $(CpCdcache|CpCicache), R1 MCR CpSC, 0, R1, C(CpCONTROL), C(0), CpMainctl BARRIERS PUTC('r') /* set the domain access control */ MOVW $Client, R0 BL dacput(SB) DELAY(printloop5, 2) PUTC('o') /* set the translation table base */ MOVW $PADDR(L1), R0 BL ttbput(SB) MOVW $0, R0 BL pidput(SB) /* paranoia */ PUTC('m') /* * the little dance to turn the MMU on */ BL cacheuwbinv(SB) BL mmuinvalidate(SB) BL mmuenable(SB) PUTC(' ') /* warp the PC into the virtual map */ MOVW $KZERO, R0 BL _r15warp(SB) /* * now running at KZERO+something! */ MOVW $setR12(SB), R12 /* reload the SB */ /* * set up temporary stack again, in case we've just switched * to a new register set. */ MOVW $(KZERO|(128*1024*1024)), R13 /* can now execute arbitrary C code */ BL cacheuwbinv(SB) PUTC('B') MOVW $PHYSDRAM, R3 /* pa */ CMP $KZERO, R3 BEQ no2unmap /* undo double map of PHYSDRAM, KZERO & first few MBs */ MOVW $(L1+L1X(PHYSDRAM)), R4 /* addr. of PTE for PHYSDRAM */ MOVW $0, R0 MOVW $DOUBLEMAPMBS, R5 _ptudbl: ZEROPTE() SUB.S $1, R5 BNE _ptudbl no2unmap: BARRIERS MOVW $KZERO, R0 MCR CpSC, 0, R0, C(CpTLB), C(CpTLBinvu), CpTLBinv BARRIERS #ifdef HIGH_SECURITY /* i.e., not GP omap */ /* hack: set `secure monitor' vector base addr for cortex */ // MOVW $HVECTORS, R0 MOVW $PADDR(L1), R0 SUB $(MACHSIZE+(2*1024)), R0 MCR CpSC, 0, R0, C(CpVECS), C(CpVECSbase), CpVECSmon ISB #endif /* * call main in C * pass Mach to main and set up the stack in it */ MOVW $(MACHADDR), R0 /* Mach */ MOVW R0, R13 ADD $(MACHSIZE), R13 /* stack pointer */ SUB $4, R13 /* space for link register */ MOVW R0, R10 /* m = MACHADDR */ PUTC('e') BL main(SB) /* void main(Mach*) */ /*FALLTHROUGH*/ /* * reset the system */ TEXT _reset(SB), 1, $-4 MOVW $(PsrDirq|PsrDfiq|PsrMsvc), R0 MOVW R0, CPSR BARRIERS DELAY(printloopr, 2) PUTC('!') PUTC('r') PUTC('e') PUTC('s') PUTC('e') PUTC('t') PUTC('!') PUTC('\r') PUTC('\n') /* turn the caches off */ BL cacheuwbinv(SB) MRC CpSC, 0, R0, C(CpCONTROL), C(0), CpMainctl BIC $(CpCicache|CpCdcache|CpCalign), R0 ORR $CpCsw, R0 /* enable SWP */ MCR CpSC, 0, R0, C(CpCONTROL), C(0), CpMainctl BARRIERS /* redo double map of PHYSDRAM, KZERO & first few MBs */ MOVW $PTEDRAM, R2 /* PTE bits */ MOVW $PHYSDRAM, R3 /* pa */ MOVW $(L1+L1X(PHYSDRAM)), R4 /* address of PHYSDRAM's PTE */ MOVW $DOUBLEMAPMBS, R5 _ptrdbl: FILLPTE() SUB.S $1, R5 BNE _ptrdbl MOVW $PHYSDRAM, R0 MCR CpSC, 0, R0, C(CpTLB), C(CpTLBinvu), CpTLBinv BARRIERS /* turn the MMU off */ MOVW $PHYSDRAM, R0 BL _r15warp(SB) BL mmuinvalidate(SB) BL mmudisable(SB) /* set new reset vector */ MOVW $HVECTORS, R2 MOVW $0xe59ff018, R3 /* MOVW 0x18(R15), R15 */ MOVW R3, (R2) BARRIERS // MOVW $PHYSFLASH, R3 /* TODO */ // MOVW R3, 0x20(R2) /* where $0xe59ff018 jumps to */ /* ...and jump to it */ // MOVW R2, R15 /* software reboot */ _limbo: /* should not get here... */ BL idlehands(SB) B _limbo /* ... and can't get out */ BL _div(SB) /* hack to load _div, etc. */ TEXT _r15warp(SB), 1, $-4 BIC $KSEGM, R14 /* link reg, will become PC */ ORR R0, R14 BIC $KSEGM, R13 /* SP too */ ORR R0, R13 RET /* * `single-element' cache operations. * in arm arch v7, they operate on all cache levels, so separate * l2 functions are unnecessary. */ TEXT cachedwbse(SB), $-4 /* D writeback SE */ MOVW R0, R2 MOVW CPSR, R3 CPSID /* splhi */ BARRIERS /* force outstanding stores to cache */ MOVW R2, R0 MOVW 4(FP), R1 ADD R0, R1 /* R1 is end address */ BIC $(CACHELINESZ-1), R0 /* cache line start */ _dwbse: MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEwb), CpCACHEse /* can't have a BARRIER here since it zeroes R0 */ ADD $CACHELINESZ, R0 CMP.S R0, R1 BGT _dwbse B _wait TEXT cachedwbinvse(SB), $-4 /* D writeback+invalidate SE */ MOVW R0, R2 MOVW CPSR, R3 CPSID /* splhi */ BARRIERS /* force outstanding stores to cache */ MOVW R2, R0 MOVW 4(FP), R1 ADD R0, R1 /* R1 is end address */ BIC $(CACHELINESZ-1), R0 /* cache line start */ _dwbinvse: MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEwbi), CpCACHEse /* can't have a BARRIER here since it zeroes R0 */ ADD $CACHELINESZ, R0 CMP.S R0, R1 BGT _dwbinvse _wait: /* drain write buffer */ BARRIERS /* drain L1 write buffer, also drains L2 eviction buffer on sheeva */ MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEwb), CpCACHEwait ISB MOVW R3, CPSR /* splx */ RET TEXT cachedinvse(SB), $-4 /* D invalidate SE */ MOVW R0, R2 MOVW CPSR, R3 CPSID /* splhi */ BARRIERS /* force outstanding stores to cache */ MOVW R2, R0 MOVW 4(FP), R1 ADD R0, R1 /* R1 is end address */ BIC $(CACHELINESZ-1), R0 /* cache line start */ _dinvse: MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEinvd), CpCACHEse /* can't have a BARRIER here since it zeroes R0 */ ADD $CACHELINESZ, R0 CMP.S R0, R1 BGT _dinvse B _wait /* * enable mmu and high vectors */ TEXT mmuenable(SB), 1, $-4 MRC CpSC, 0, R0, C(CpCONTROL), C(0), CpMainctl ORR $(CpChv|CpCmmu), R0 MCR CpSC, 0, R0, C(CpCONTROL), C(0), CpMainctl BARRIERS RET TEXT mmudisable(SB), 1, $-4 MRC CpSC, 0, R0, C(CpCONTROL), C(0), CpMainctl BIC $(CpChv|CpCmmu), R0 MCR CpSC, 0, R0, C(CpCONTROL), C(0), CpMainctl BARRIERS RET /* * If one of these MCR instructions crashes or hangs the machine, * check your Level 1 page table (at TTB) closely. */ TEXT mmuinvalidate(SB), $-4 /* invalidate all */ MOVW CPSR, R2 CPSID /* interrupts off */ BARRIERS MOVW PC, R0 /* some valid virtual address */ MCR CpSC, 0, R0, C(CpTLB), C(CpTLBinvu), CpTLBinv BARRIERS MOVW R2, CPSR /* interrupts restored */ RET TEXT mmuinvalidateaddr(SB), $-4 /* invalidate single entry */ MCR CpSC, 0, R0, C(CpTLB), C(CpTLBinvu), CpTLBinvse BARRIERS RET TEXT cpidget(SB), 1, $-4 /* main ID */ MRC CpSC, 0, R0, C(CpID), C(0), CpIDid RET TEXT cpctget(SB), 1, $-4 /* cache type */ MRC CpSC, 0, R0, C(CpID), C(0), CpIDct RET TEXT controlget(SB), 1, $-4 /* control */ MRC CpSC, 0, R0, C(CpCONTROL), C(0), CpMainctl RET TEXT ttbget(SB), 1, $-4 /* translation table base */ MRC CpSC, 0, R0, C(CpTTB), C(0), CpTTB0 RET TEXT ttbput(SB), 1, $-4 /* translation table base */ MCR CpSC, 0, R0, C(CpTTB), C(0), CpTTB0 MCR CpSC, 0, R0, C(CpTTB), C(0), CpTTB1 /* cortex has two */ ISB RET TEXT dacget(SB), 1, $-4 /* domain access control */ MRC CpSC, 0, R0, C(CpDAC), C(0) RET TEXT dacput(SB), 1, $-4 /* domain access control */ MCR CpSC, 0, R0, C(CpDAC), C(0) ISB RET TEXT fsrget(SB), 1, $-4 /* data fault status */ MRC CpSC, 0, R0, C(CpFSR), C(0), CpDFSR RET TEXT ifsrget(SB), 1, $-4 /* instruction fault status */ MRC CpSC, 0, R0, C(CpFSR), C(0), CpIFSR RET TEXT farget(SB), 1, $-4 /* fault address */ MRC CpSC, 0, R0, C(CpFAR), C(0x0) RET TEXT getpsr(SB), 1, $-4 MOVW CPSR, R0 RET TEXT getscr(SB), 1, $-4 MRC CpSC, 0, R0, C(CpCONTROL), C(CpCONTROLscr), CpSCRscr RET TEXT pidget(SB), 1, $-4 /* address translation pid */ MRC CpSC, 0, R0, C(CpPID), C(0x0) RET TEXT pidput(SB), 1, $-4 /* address translation pid */ MCR CpSC, 0, R0, C(CpPID), C(0x0) ISB RET TEXT splhi(SB), 1, $-4 MOVW CPSR, R0 CPSID /* turn off interrupts */ MOVW $(MACHADDR+4), R2 /* save caller pc in Mach */ MOVW R14, 0(R2) RET TEXT spllo(SB), 1, $-4 /* start marker for devkprof.c */ MOVW CPSR, R0 CPSIE RET TEXT splx(SB), 1, $-4 MOVW $(MACHADDR+0x04), R2 /* save caller pc in Mach */ MOVW R14, 0(R2) MOVW CPSR, R3 MOVW R0, CPSR /* reset interrupt level */ MOVW R3, R0 /* must return old CPSR */ RET TEXT spldone(SB), 1, $0 /* end marker for devkprof.c */ RET TEXT islo(SB), 1, $-4 MOVW CPSR, R0 AND $(PsrDirq), R0 EOR $(PsrDirq), R0 RET TEXT tas(SB), $-4 TEXT _tas(SB), $-4 MOVW R0,R1 MOVW $1,R0 SWPW R0,(R1) /* fix: deprecated in armv7 */ RET TEXT clz(SB), $-4 CLZ(0, 0) /* 0 is R0 */ RET TEXT setlabel(SB), 1, $-4 MOVW R13, 0(R0) /* sp */ MOVW R14, 4(R0) /* pc */ MOVW $0, R0 RET TEXT gotolabel(SB), 1, $-4 MOVW 0(R0), R13 /* sp */ MOVW 4(R0), R14 /* pc */ MOVW $1, R0 RET TEXT getcallerpc(SB), 1, $-4 MOVW 0(R13), R0 RET TEXT idlehands(SB), $-4 BARRIERS WFI RET TEXT coherence(SB), $-4 BARRIERS RET #include "cache.v7.s" t up the MMU page table */ PUTC('n') /omap/trap.c 664 0 0 40454 12072511103 11277ustar00millersys/* * omap3530 traps, exceptions, interrupts, system calls. */ #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "../port/error.h" #include "ureg.h" #include "arm.h" enum { Nirqs = 96, Nvec = 8, /* # of vectors at start of lexception.s */ Bi2long = BI2BY * sizeof(long), }; extern int notify(Ureg*); extern int ldrexvalid; /* omap35x intc (aka mpu_intc) */ typedef struct Intrregs Intrregs; struct Intrregs { /* * the manual inserts "INTCPS_" before each register name; * we'll just assume the prefix. */ uchar _pad0[4*4]; ulong sysconfig; ulong sysstatus; /* ro */ uchar _pad1[0x40 - 0x18]; ulong sir_irq; /* ro */ ulong sir_fiq; /* ro */ ulong control; ulong protection; ulong idle; uchar _pad2[0x60 - 0x54]; ulong irq_priority; ulong fiq_priority; ulong threshold; uchar _pad3[0x80 - 0x6c]; struct Bits { /* bitmaps */ ulong itr; /* ro: pending intrs (no mask) */ ulong mir; /* interrupt mask: 1 means masked */ ulong mir_clear; /* wo: 1 sets the bit */ ulong mir_set; /* wo: 1 clears the bit */ ulong isr_set; /* software interrupts */ ulong isr_clear; /* wo */ ulong pending_irq; /* ro */ ulong pending_fiq; /* ro */ } bits[3]; /* 3*32 = 96 (Nirqs) */ ulong ilr[Nirqs]; }; enum { /* sysconfig bits */ Softreset = 1<<1, /* sysstatus bits */ Resetdone = 1<<0, /* sir_irq/fiq bits */ Activeirq = MASK(7), /* control bits */ Newirqagr = 1<<0, /* protection bits */ Protection = 1<<0, /* irq/fiq_priority bits */ Irqpriority = MASK(6), /* threshold bits */ Prioritythreshold = MASK(8), /* ilr bits */ Priority = MASK(8) - MASK(2), }; typedef struct Vctl Vctl; typedef struct Vctl { Vctl* next; /* handlers on this vector */ char *name; /* of driver, xallocated */ void (*f)(Ureg*, void*); /* handler to call */ void* a; /* argument to call it with */ } Vctl; static Lock vctllock; static Vctl* vctl[Nirqs]; /* * Layout at virtual address 0. */ typedef struct Vpage0 { void (*vectors[Nvec])(void); u32int vtable[Nvec]; } Vpage0; static Vpage0 *vpage0; uvlong ninterrupt; uvlong ninterruptticks; int irqtooearly = 1; static volatile int probing, trapped; static int irqinuse(uint irq) { Intrregs *ip = (Intrregs *)PHYSINTC; /* * mir registers are odd: a 0 bit means intr unmasked (i.e., * we've unmasked it because it's in use). */ return (ip->bits[irq / Bi2long].mir & (1 << (irq % Bi2long))) == 0; } static void intcmask(uint irq) { Intrregs *ip = (Intrregs *)PHYSINTC; ip->bits[irq / Bi2long].mir_set = 1 << (irq % Bi2long); coherence(); } static void intcunmask(uint irq) { Intrregs *ip = (Intrregs *)PHYSINTC; ip->bits[irq / Bi2long].mir_clear = 1 << (irq % Bi2long); coherence(); } static void intcmaskall(void) { int i; Intrregs *ip = (Intrregs *)PHYSINTC; for (i = 0; i < 3; i++) ip->bits[i].mir_set = ~0; coherence(); } static void intcunmaskall(void) { int i; Intrregs *ip = (Intrregs *)PHYSINTC; for (i = 0; i < 3; i++) ip->bits[i].mir_clear = ~0; coherence(); } static void intcinvertall(void) { int i, s; ulong bits; Intrregs *ip = (Intrregs *)PHYSINTC; s = splhi(); for (i = 0; i < 3; i++) { bits = ip->bits[i].mir; ip->bits[i].mir_set = ~0; /* mask all */ coherence(); /* clearing enables only those intrs. that were disabled */ ip->bits[i].mir_clear = bits; } coherence(); splx(s); } static void intrsave(ulong buf[3]) { int i; Intrregs *ip = (Intrregs *)PHYSINTC; for (i = 0; i < nelem(buf); i++) buf[i] = ip->bits[i].mir; coherence(); } static void intrrestore(ulong buf[3]) { int i, s; Intrregs *ip = (Intrregs *)PHYSINTC; s = splhi(); for (i = 0; i < nelem(buf); i++) { ip->bits[i].mir_clear = ~0; /* unmask all */ coherence(); ip->bits[i].mir_set = buf[i]; /* mask previously disabled */ } coherence(); splx(s); } /* * set up for exceptions */ void trapinit(void) { int i; Intrregs *ip = (Intrregs *)PHYSINTC; /* set up the exception vectors */ vpage0 = (Vpage0*)HVECTORS; memmove(vpage0->vectors, vectors, sizeof(vpage0->vectors)); memmove(vpage0->vtable, vtable, sizeof(vpage0->vtable)); cacheuwbinv(); l2cacheuwbinv(); /* set up the stacks for the interrupt modes */ setr13(PsrMfiq, m->sfiq); setr13(PsrMirq, m->sirq); setr13(PsrMabt, m->sabt); setr13(PsrMund, m->sund); #ifdef HIGH_SECURITY setr13(PsrMmon, m->smon); #endif setr13(PsrMsys, m->ssys); intcmaskall(); ip->control = 0; ip->threshold = Prioritythreshold; /* disable threshold */ for (i = 0; i < Nirqs; i++) ip->ilr[i] = 0<<2 | 0; /* all intrs pri 0 & to irq, not fiq */ irqtooearly = 0; coherence(); } void intrsoff(void) { Intrregs *ip = (Intrregs *)PHYSINTC; intcmaskall(); ip->control = Newirqagr; /* dismiss interrupt */ coherence(); } /* * enable an irq interrupt */ int irqenable(int irq, void (*f)(Ureg*, void*), void* a, char *name) { Vctl *v; if(irq >= nelem(vctl) || irq < 0) panic("irqenable irq %d", irq); if (irqtooearly) { iprint("irqenable for %d %s called too early\n", irq, name); return -1; } if(irqinuse(irq)) print("irqenable: %s: irq %d already in use, chaining\n", name, irq); v = malloc(sizeof(Vctl)); if (v == nil) panic("irqenable: malloc Vctl"); v->f = f; v->a = a; v->name = malloc(strlen(name)+1); if (v->name == nil) panic("irqenable: malloc name"); strcpy(v->name, name); lock(&vctllock); v->next = vctl[irq]; vctl[irq] = v; intcunmask(irq); unlock(&vctllock); return 0; } /* * disable an irq interrupt */ int irqdisable(int irq, void (*f)(Ureg*, void*), void* a, char *name) { Vctl **vp, *v; if(irq >= nelem(vctl) || irq < 0) panic("irqdisable irq %d", irq); lock(&vctllock); for(vp = &vctl[irq]; v = *vp; vp = &v->next) if (v->f == f && v->a == a && strcmp(v->name, name) == 0){ print("irqdisable: remove %s\n", name); *vp = v->next; free(v); break; } if(v == nil) print("irqdisable: irq %d, name %s not enabled\n", irq, name); if(vctl[irq] == nil){ print("irqdisable: clear icmr bit %d\n", irq); intcmask(irq); } unlock(&vctllock); return 0; } /* * called by trap to handle access faults */ static void faultarm(Ureg *ureg, uintptr va, int user, int read) { int n, insyscall; char buf[ERRMAX]; if(up == nil) { dumpregs(ureg); panic("fault: nil up in faultarm, accessing %#p", va); } insyscall = up->insyscall; up->insyscall = 1; n = fault(va, read); if(n < 0){ if(!user){ dumpregs(ureg); panic("fault: kernel accessing %#p", va); } /* don't dump registers; programs suicide all the time */ snprint(buf, sizeof buf, "sys: trap: fault %s va=%#p", read? "read": "write", va); postnote(up, 1, buf, NDebug); } up->insyscall = insyscall; } /* * called by trap to handle interrupts. * returns true iff a clock interrupt, thus maybe reschedule. */ static int irq(Ureg* ureg) { int clockintr; uint irqno, handled, t, ticks = perfticks(); Intrregs *ip = (Intrregs *)PHYSINTC; Vctl *v; static int nesting, lastirq = -1; handled = 0; irqno = ip->sir_irq & Activeirq; if (irqno >= 37 && irqno <= 47) /* this is a clock intr? */ m->inclockintr++; /* yes, count nesting */ lastirq = irqno; if (irqno >= nelem(vctl)) { iprint("trap: irq %d >= # vectors (%d)\n", irqno, nelem(vctl)); ip->control = Newirqagr; /* dismiss interrupt */ return 0; } ++nesting; for(v = vctl[irqno]; v != nil; v = v->next) if (v->f) { if (islo()) panic("trap: pl0 before trap handler for %s", v->name); v->f(ureg, v->a); if (islo()) panic("trap: %s lowered pl", v->name); // splhi(); /* in case v->f lowered pl */ handled++; } if(!handled) { iprint("unexpected interrupt: irq %d", irqno); switch (irqno) { case 56: case 57: iprint(" (I⁲C)"); break; case 83: case 86: case 94: iprint(" (MMC)"); break; } if(irqno < nelem(vctl)) { intcmask(irqno); iprint(", now masked"); } iprint("\n"); } t = perfticks(); ninterrupt++; if(t < ticks) ninterruptticks += ticks-t; else ninterruptticks += t-ticks; ip->control = Newirqagr; /* dismiss interrupt */ coherence(); --nesting; clockintr = m->inclockintr == 1; if (irqno >= 37 && irqno <= 47) m->inclockintr--; return clockintr; } /* * returns 1 if the instruction writes memory, 0 otherwise */ int writetomem(ulong inst) { /* swap always write memory */ if((inst & 0x0FC00000) == 0x01000000) return 1; /* loads and stores are distinguished by bit 20 */ if(inst & (1<<20)) return 0; return 1; } void prgpmcerrs(void); /* * here on all exceptions other than syscall (SWI) */ void trap(Ureg *ureg) { int clockintr, user, x, rv, rem; ulong inst, fsr; uintptr va; char buf[ERRMAX]; splhi(); /* paranoia */ if(up != nil) rem = ((char*)ureg)-up->kstack; else rem = ((char*)ureg)-((char*)m+sizeof(Mach)); if(rem < 1024) { iprint("trap: %d stack bytes left, up %#p ureg %#p at pc %#lux\n", rem, up, ureg, ureg->pc); delay(1000); dumpstack(); panic("trap: %d stack bytes left, up %#p ureg %#p at pc %#lux", rem, up, ureg, ureg->pc); } user = (ureg->psr & PsrMask) == PsrMusr; if(user){ up->dbgreg = ureg; cycles(&up->kentry); } /* * All interrupts/exceptions should be resumed at ureg->pc-4, * except for Data Abort which resumes at ureg->pc-8. */ if(ureg->type == (PsrMabt+1)) ureg->pc -= 8; else ureg->pc -= 4; clockintr = 0; /* if set, may call sched() before return */ switch(ureg->type){ default: panic("unknown trap; type %#lux, psr mode %#lux", ureg->type, ureg->psr & PsrMask); break; case PsrMirq: ldrexvalid = 0; clockintr = irq(ureg); m->intr++; break; case PsrMabt: /* prefetch fault */ ldrexvalid = 0; x = ifsrget(); fsr = (x>>7) & 0x8 | x & 0x7; switch(fsr){ case 0x02: /* instruction debug event (BKPT) */ if(user){ snprint(buf, sizeof buf, "sys: breakpoint"); postnote(up, 1, buf, NDebug); }else{ iprint("kernel bkpt: pc %#lux inst %#ux\n", ureg->pc, *(u32int*)ureg->pc); panic("kernel bkpt"); } break; default: faultarm(ureg, ureg->pc, user, 1); break; } break; case PsrMabt+1: /* data fault */ ldrexvalid = 0; va = farget(); inst = *(ulong*)(ureg->pc); /* bits 12 and 10 have to be concatenated with status */ x = fsrget(); fsr = (x>>7) & 0x20 | (x>>6) & 0x10 | x & 0xf; if (probing && !user) { if (trapped++ > 0) panic("trap: recursive probe %#lux", va); ureg->pc += 4; /* continue at next instruction */ break; } switch(fsr){ default: case 0xa: /* ? was under external abort */ panic("unknown data fault, 6b fsr %#lux", fsr); break; case 0x0: panic("vector exception at %#lux", ureg->pc); break; case 0x1: /* alignment fault */ case 0x3: /* access flag fault (section) */ if(user){ snprint(buf, sizeof buf, "sys: alignment: pc %#lux va %#p\n", ureg->pc, va); postnote(up, 1, buf, NDebug); } else panic("kernel alignment: pc %#lux va %#p", ureg->pc, va); break; case 0x2: panic("terminal exception at %#lux", ureg->pc); break; case 0x4: /* icache maint fault */ case 0x6: /* access flag fault (page) */ case 0x8: /* precise external abort, non-xlat'n */ case 0x28: case 0xc: /* l1 translation, precise ext. abort */ case 0x2c: case 0xe: /* l2 translation, precise ext. abort */ case 0x2e: case 0x16: /* imprecise ext. abort, non-xlt'n */ case 0x36: panic("external abort %#lux pc %#lux addr %#p", fsr, ureg->pc, va); break; case 0x1c: /* l1 translation, precise parity err */ case 0x1e: /* l2 translation, precise parity err */ case 0x18: /* imprecise parity or ecc err */ panic("translation parity error %#lux pc %#lux addr %#p", fsr, ureg->pc, va); break; case 0x5: /* translation fault, no section entry */ case 0x7: /* translation fault, no page entry */ faultarm(ureg, va, user, !writetomem(inst)); break; case 0x9: case 0xb: /* domain fault, accessing something we shouldn't */ if(user){ snprint(buf, sizeof buf, "sys: access violation: pc %#lux va %#p\n", ureg->pc, va); postnote(up, 1, buf, NDebug); } else panic("kernel access violation: pc %#lux va %#p", ureg->pc, va); break; case 0xd: case 0xf: /* permission error, copy on write or real permission error */ faultarm(ureg, va, user, !writetomem(inst)); break; } break; case PsrMund: /* undefined instruction */ if(user){ if(seg(up, ureg->pc, 0) != nil && *(u32int*)ureg->pc == 0xD1200070){ snprint(buf, sizeof buf, "sys: breakpoint"); postnote(up, 1, buf, NDebug); }else{ /* look for floating point instructions to interpret */ x = spllo(); rv = fpiarm(ureg); splx(x); if(rv == 0){ ldrexvalid = 0; snprint(buf, sizeof buf, "undefined instruction: pc %#lux\n", ureg->pc); postnote(up, 1, buf, NDebug); } } }else{ if (ureg->pc & 3) { iprint("rounding fault pc %#lux down to word\n", ureg->pc); ureg->pc &= ~3; } iprint("undefined instruction: pc %#lux inst %#ux\n", ureg->pc, ((u32int*)ureg->pc)[-2]); panic("undefined instruction"); } break; } splhi(); /* delaysched set because we held a lock or because our quantum ended */ if(up && up->delaysched && clockintr){ ldrexvalid = 0; sched(); /* can cause more traps */ splhi(); } if(user){ if(up->procctl || up->nnote) notify(ureg); kexit(ureg); } } /* * Fill in enough of Ureg to get a stack trace, and call a function. * Used by debugging interface rdb. */ void callwithureg(void (*fn)(Ureg*)) { Ureg ureg; ureg.pc = getcallerpc(&fn); ureg.sp = PTR2UINT(&fn); fn(&ureg); } static void dumpstackwithureg(Ureg *ureg) { int x; uintptr l, v, i, estack; char *s; dumpregs(ureg); if((s = getconf("*nodumpstack")) != nil && strcmp(s, "0") != 0){ iprint("dumpstack disabled\n"); return; } iprint("dumpstack\n"); x = 0; x += iprint("ktrace /kernel/path %#.8lux %#.8lux %#.8lux # pc, sp, link\n", ureg->pc, ureg->sp, ureg->r14); delay(20); i = 0; if(up && (uintptr)&l >= (uintptr)up->kstack && (uintptr)&l <= (uintptr)up->kstack+KSTACK) estack = (uintptr)up->kstack+KSTACK; else if((uintptr)&l >= (uintptr)m->stack && (uintptr)&l <= (uintptr)m+MACHSIZE) estack = (uintptr)m+MACHSIZE; else return; x += iprint("estackx %p\n", estack); for(l = (uintptr)&l; l < estack; l += sizeof(uintptr)){ v = *(uintptr*)l; if((KTZERO < v && v < (uintptr)etext) || estack-l < 32){ x += iprint("%.8p ", v); delay(20); i++; } if(i == 8){ i = 0; x += iprint("\n"); delay(20); } } if(i) iprint("\n"); } void dumpstack(void) { callwithureg(dumpstackwithureg); } /* * dump system control coprocessor registers */ static void dumpscr(void) { iprint("0:\t%#8.8ux id\n", cpidget()); iprint("\t%8.8#ux ct\n", cpctget()); iprint("1:\t%#8.8ux control\n", controlget()); iprint("2:\t%#8.8ux ttb\n", ttbget()); iprint("3:\t%#8.8ux dac\n", dacget()); iprint("4:\t(reserved)\n"); iprint("5:\t%#8.8ux fsr\n", fsrget()); iprint("6:\t%#8.8ux far\n", farget()); iprint("7:\twrite-only cache\n"); iprint("8:\twrite-only tlb\n"); iprint("13:\t%#8.8ux pid\n", pidget()); delay(10); } /* * dump general registers */ static void dumpgpr(Ureg* ureg) { if(up != nil) iprint("cpu%d: registers for %s %lud\n", m->machno, up->text, up->pid); else iprint("cpu%d: registers for kernel\n", m->machno); delay(20); iprint("%#8.8lux\tr0\n", ureg->r0); iprint("%#8.8lux\tr1\n", ureg->r1); iprint("%#8.8lux\tr2\n", ureg->r2); delay(20); iprint("%#8.8lux\tr3\n", ureg->r3); iprint("%#8.8lux\tr4\n", ureg->r4); iprint("%#8.8lux\tr5\n", ureg->r5); delay(20); iprint("%#8.8lux\tr6\n", ureg->r6); iprint("%#8.8lux\tr7\n", ureg->r7); iprint("%#8.8lux\tr8\n", ureg->r8); delay(20); iprint("%#8.8lux\tr9 (up)\n", ureg->r9); iprint("%#8.8lux\tr10 (m)\n", ureg->r10); iprint("%#8.8lux\tr11 (loader temporary)\n", ureg->r11); iprint("%#8.8lux\tr12 (SB)\n", ureg->r12); delay(20); iprint("%#8.8lux\tr13 (sp)\n", ureg->r13); iprint("%#8.8lux\tr14 (link)\n", ureg->r14); iprint("%#8.8lux\tr15 (pc)\n", ureg->pc); delay(20); iprint("%10.10lud\ttype\n", ureg->type); iprint("%#8.8lux\tpsr\n", ureg->psr); delay(20); } void dumpregs(Ureg* ureg) { dumpgpr(ureg); dumpscr(); } vlong probeaddr(uintptr addr) { vlong v; static Lock fltlck; ilock(&fltlck); trapped = 0; probing = 1; coherence(); v = *(ulong *)addr; /* this may cause a fault */ USED(probing); coherence(); probing = 0; coherence(); if (trapped) v = -1; iunlock(&fltlck); return v; } if(!user){ dumpregs(ureg); panic("fault: kernel accessing %#p", va); } /* don't dump registers; programs suicide all the time */ snprint(buf, sizeof buf, "sys: trap: fault %s va=%#p", read? "reateg2/trap.c 664 0 0 56714 12072521754 11227ustar00millersys/* * arm mpcore generic interrupt controller (gic) v1 * traps, exceptions, interrupts, system calls. * * there are two pieces: the interrupt distributor and the cpu interface. * * memset or memmove on any of the distributor registers generates an * exception like this one: * panic: external abort 0x28 pc 0xc048bf68 addr 0x50041800 * * we use l1 and l2 cache ops to force vectors to be visible everywhere. * * apparently irqs 0—15 (SGIs) are always enabled. */ #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "../port/error.h" #include "ureg.h" #include "arm.h" #define ISSGI(irq) ((uint)(irq) < Nsgi) enum { Debug = 0, Nvec = 8, /* # of vectors at start of lexception.s */ Bi2long = BI2BY * sizeof(long), Nirqs = 1024, Nsgi = 16, /* software-generated (inter-processor) intrs */ Nppi = 32, /* sgis + other private peripheral intrs */ }; typedef struct Intrcpuregs Intrcpuregs; typedef struct Intrdistregs Intrdistregs; /* * almost this entire register set is buggered. * the distributor is supposed to be per-system, not per-cpu, * yet some registers are banked per-cpu, as marked. */ struct Intrdistregs { /* distributor */ ulong ctl; ulong ctlrtype; ulong distid; uchar _pad0[0x80 - 0xc]; /* botch: *[0] are banked per-cpu from here */ /* bit maps */ ulong grp[32]; /* in group 1 (non-secure) */ ulong setena[32]; /* forward to cpu interfaces */ ulong clrena[32]; ulong setpend[32]; ulong clrpend[32]; ulong setact[32]; /* active? */ ulong clract[32]; /* botch: *[0] are banked per-cpu until here */ uchar pri[1020]; /* botch: pri[0] — pri[7] are banked per-cpu */ ulong _rsrvd1; /* botch: targ[0] through targ[7] are banked per-cpu and RO */ uchar targ[1020]; /* byte bit maps: cpu targets indexed by intr */ ulong _rsrvd2; /* botch: cfg[1] is banked per-cpu */ ulong cfg[64]; /* bit pairs: edge? 1-N? */ ulong _pad1[64]; ulong nsac[64]; /* bit pairs (v2 only) */ /* software-generated intrs (a.k.a. sgi) */ ulong swgen; /* intr targets */ uchar _pad2[0xf10 - 0xf04]; uchar clrsgipend[16]; /* bit map (v2 only) */ uchar setsgipend[16]; /* bit map (v2 only) */ }; enum { /* ctl bits */ Forw2cpuif = 1, /* ctlrtype bits */ Cpunoshft = 5, Cpunomask = MASK(3), Intrlines = MASK(5), /* cfg bits */ Level = 0<<1, Edge = 1<<1, /* edge-, not level-sensitive */ Toall = 0<<0, To1 = 1<<0, /* vs. to all */ /* swgen bits */ Totargets = 0, Tonotme = 1<<24, Tome = 2<<24, }; /* each cpu sees its own registers at the same base address (soc.intr) */ struct Intrcpuregs { ulong ctl; ulong primask; ulong binpt; /* group pri vs subpri split */ ulong ack; ulong end; ulong runpri; ulong hipripend; /* aliased regs (secure, for group 1) */ ulong alibinpt; ulong aliack; /* (v2 only) */ ulong aliend; /* (v2 only) */ ulong alihipripend; /* (v2 only) */ uchar _pad0[0xd0 - 0x2c]; ulong actpri[4]; /* (v2 only) */ ulong nsactpri[4]; /* (v2 only) */ uchar _pad0[0xfc - 0xf0]; ulong ifid; /* ro */ uchar _pad0[0x1000 - 0x100]; ulong deact; /* wo (v2 only) */ }; enum { /* ctl bits */ Enable = 1, Eoinodeact = 1<<9, /* (v2 only) */ /* (ali) ack/end/hipriend/deact bits */ Intrmask = MASK(10), Cpuidshift = 10, Cpuidmask = MASK(3), /* ifid bits */ Archversshift = 16, Archversmask = MASK(4), }; typedef struct Vctl Vctl; typedef struct Vctl { Vctl* next; /* handlers on this vector */ char *name; /* of driver, xallocated */ void (*f)(Ureg*, void*); /* handler to call */ void* a; /* argument to call it with */ } Vctl; static Lock vctllock; static Vctl* vctl[Nirqs]; /* * Layout at virtual address 0. */ typedef struct Vpage0 { void (*vectors[Nvec])(void); u32int vtable[Nvec]; } Vpage0; enum { Ntimevec = 20 /* number of time buckets for each intr */ }; ulong intrtimes[Nirqs][Ntimevec]; uvlong ninterrupt; uvlong ninterruptticks; int irqtooearly = 1; static ulong shadena[32]; /* copy of enable bits, saved by intcmaskall */ static Lock distlock, nintrlock; extern int notify(Ureg*); static void dumpstackwithureg(Ureg *ureg); void printrs(int base, ulong word) { int bit; for (bit = 0; word; bit++, word >>= 1) if (word & 1) iprint(" %d", base + bit); } void dumpintrs(char *what, ulong *bits) { int i, first, some; ulong word; Intrdistregs *idp = (Intrdistregs *)soc.intrdist; first = 1; some = 0; USED(idp); for (i = 0; i < nelem(idp->setpend); i++) { word = bits[i]; if (word) { if (first) { first = 0; iprint("%s", what); } some = 1; printrs(i * Bi2long, word); } } if (!some) iprint("%s none", what); iprint("\n"); } void dumpintrpend(void) { Intrdistregs *idp = (Intrdistregs *)soc.intrdist; iprint("\ncpu%d gic regs:\n", m->machno); dumpintrs("group 1", idp->grp); dumpintrs("enabled", idp->setena); dumpintrs("pending", idp->setpend); dumpintrs("active ", idp->setact); } /* * keep histogram of interrupt service times */ void intrtime(Mach*, int vno) { ulong diff; ulong x; x = perfticks(); diff = x - m->perf.intrts; m->perf.intrts = x; m->perf.inintr += diff; if(up == nil && m->perf.inidle > diff) m->perf.inidle -= diff; if (m->cpumhz == 0) return; /* don't divide by zero */ diff /= m->cpumhz*100; /* quantum = 100µsec */ if(diff >= Ntimevec) diff = Ntimevec-1; if ((uint)vno >= Nirqs) vno = Nirqs-1; intrtimes[vno][diff]++; } static ulong intack(Intrcpuregs *icp) { return icp->ack & Intrmask; } static void intdismiss(Intrcpuregs *icp, ulong ack) { icp->end = ack; coherence(); } static int irqinuse(uint irq) { Intrdistregs *idp = (Intrdistregs *)soc.intrdist; return idp->setena[irq / Bi2long] & (1 << (irq % Bi2long)); } void intcunmask(uint irq) { Intrdistregs *idp = (Intrdistregs *)soc.intrdist; ilock(&distlock); idp->setena[irq / Bi2long] = 1 << (irq % Bi2long); iunlock(&distlock); } void intcmask(uint irq) { Intrdistregs *idp = (Intrdistregs *)soc.intrdist; ilock(&distlock); idp->clrena[irq / Bi2long] = 1 << (irq % Bi2long); iunlock(&distlock); } static void intcmaskall(Intrdistregs *idp) /* mask all intrs for all cpus */ { int i; for (i = 0; i < nelem(idp->setena); i++) shadena[i] = idp->setena[i]; for (i = 0; i < nelem(idp->clrena); i++) idp->clrena[i] = ~0; coherence(); } static void intcunmaskall(Intrdistregs *idp) /* unused */ { int i; for (i = 0; i < nelem(idp->setena); i++) idp->setena[i] = shadena[i]; coherence(); } static ulong permintrs(Intrdistregs *idp, int base, int r) { ulong perms; idp->clrena[r] = ~0; /* disable all */ coherence(); perms = idp->clrena[r]; if (perms) { iprint("perm intrs:"); printrs(base, perms); iprint("\n"); } return perms; } static void intrcfg(Intrdistregs *idp) { int i, cpumask; ulong pat; /* set up all interrupts as level-sensitive, to one cpu (0) */ pat = 0; for (i = 0; i < Bi2long; i += 2) pat |= (Level | To1) << i; if (m->machno == 0) { /* system-wide & cpu0 cfg */ for (i = 0; i < nelem(idp->grp); i++) idp->grp[i] = 0; /* secure */ for (i = 0; i < nelem(idp->pri); i++) idp->pri[i] = 0; /* highest priority */ /* set up all interrupts as level-sensitive, to one cpu (0) */ for (i = 0; i < nelem(idp->cfg); i++) idp->cfg[i] = pat; /* first Nppi are read-only for SGIs and PPIs */ cpumask = 1<<0; /* just cpu 0 */ navailcpus = getncpus(); for (i = Nppi; i < sizeof idp->targ; i++) idp->targ[i] = cpumask; coherence(); intcmaskall(idp); for (i = 0; i < nelem(idp->clrena); i++) { // permintrs(idp, i * Bi2long, i); idp->clrpend[i] = idp->clract[i] = idp->clrena[i] = ~0; } } else { /* per-cpu config */ idp->grp[0] = 0; /* secure */ for (i = 0; i < 8; i++) idp->pri[i] = 0; /* highest priority */ /* idp->targ[0 through Nppi-1] are supposed to be read-only */ for (i = 0; i < Nppi; i++) idp->targ[i] = 1<machno; idp->cfg[1] = pat; coherence(); // permintrs(idp, i * Bi2long, i); idp->clrpend[0] = idp->clract[0] = idp->clrena[0] = ~0; /* on cpu1, irq Extpmuirq (118) is always pending here */ } coherence(); } void intrto(int cpu, int irq) { Intrdistregs *idp = (Intrdistregs *)soc.intrdist; /* first Nppi are read-only for SGIs and the like */ ilock(&distlock); idp->targ[irq] = 1 << cpu; iunlock(&distlock); } void intrsto(int cpu) /* unused */ { int i; Intrdistregs *idp = (Intrdistregs *)soc.intrdist; /* first Nppi are read-only for SGIs and the like */ for (i = Nppi; i < sizeof idp->targ; i++) intrto(cpu, i); USED(idp); } void intrcpu(int cpu) { Intrdistregs *idp = (Intrdistregs *)soc.intrdist; ilock(&distlock); idp->swgen = Totargets | 1 << (cpu + 16) | m->machno; iunlock(&distlock); } /* * set up for exceptions */ void trapinit(void) { int s; Intrdistregs *idp = (Intrdistregs *)soc.intrdist; Intrcpuregs *icp = (Intrcpuregs *)soc.intr; Vpage0 *vpage0; enum { Vecsize = sizeof vpage0->vectors + sizeof vpage0->vtable, }; /* * set up the exception vectors, high and low. * * we can't use cache ops on HVECTORS address, since they * work on virtual addresses, and only those that have a * physical address == PADDR(virtual). */ if (m->machno == 0) { vpage0 = (Vpage0*)HVECTORS; memmove(vpage0->vectors, vectors, sizeof(vpage0->vectors)); memmove(vpage0->vtable, vtable, sizeof(vpage0->vtable)); vpage0 = (Vpage0*)KADDR(0); memmove(vpage0->vectors, vectors, sizeof(vpage0->vectors)); memmove(vpage0->vtable, vtable, sizeof(vpage0->vtable)); allcache->wbse(vpage0, Vecsize); cacheiinv(); } /* * set up the stack pointers for the exception modes for this cpu. * they point to small `save areas' in Mach, not actual stacks. */ s = splhi(); /* make these modes ignore intrs too */ setr13(PsrMfiq, m->sfiq); setr13(PsrMirq, m->sirq); setr13(PsrMmon, m->smon); setr13(PsrMabt, m->sabt); setr13(PsrMund, m->sund); setr13(PsrMsys, m->ssys); splx(s); assert((idp->distid & MASK(12)) == 0x43b); /* made by arm */ assert((icp->ifid & MASK(12)) == 0x43b); /* made by arm */ ilock(&distlock); idp->ctl = 0; icp->ctl = 0; coherence(); intrcfg(idp); /* some per-cpu cfg here */ icp->ctl = Enable; icp->primask = (uchar)~0; /* let all priorities through */ coherence(); idp->ctl = Forw2cpuif; iunlock(&distlock); } void intrsoff(void) { ilock(&distlock); intcmaskall((Intrdistregs *)soc.intrdist); iunlock(&distlock); } void intrcpushutdown(void) { Intrcpuregs *icp = (Intrcpuregs *)soc.intr; icp->ctl = 0; icp->primask = 0; /* let no priorities through */ coherence(); } /* called from cpu0 after other cpus are shutdown */ void intrshutdown(void) { Intrdistregs *idp = (Intrdistregs *)soc.intrdist; intrsoff(); idp->ctl = 0; intrcpushutdown(); } /* * enable an irq interrupt * note that the same private interrupt may be enabled on multiple cpus */ int irqenable(uint irq, void (*f)(Ureg*, void*), void* a, char *name) { Vctl *v; if(irq >= nelem(vctl)) panic("irqenable irq %d", irq); if (irqtooearly) { iprint("irqenable for %d %s called too early\n", irq, name); return -1; } /* * if in use, could be a private interrupt on a secondary cpu, * so don't add anything to the vector chain. irqs should * otherwise be one-to-one with devices. */ if(!ISSGI(irq) && irqinuse(irq)) { lock(&vctllock); if (vctl[irq] == nil) { dumpintrpend(); panic("non-sgi irq %d in use yet no Vctl allocated", irq); } unlock(&vctllock); } /* could be 1st use of this irq or could be an sgi (always in use) */ else if (vctl[irq] == nil) { v = malloc(sizeof(Vctl)); if (v == nil) panic("irqenable: malloc Vctl"); v->f = f; v->a = a; v->name = malloc(strlen(name)+1); if (v->name == nil) panic("irqenable: malloc name"); strcpy(v->name, name); lock(&vctllock); if (vctl[irq] != nil) { /* allocation race: someone else did it first */ free(v->name); free(v); } else { v->next = vctl[irq]; vctl[irq] = v; } unlock(&vctllock); } intcunmask(irq); return 0; } /* * disable an irq interrupt */ int irqdisable(uint irq, void (*f)(Ureg*, void*), void* a, char *name) { Vctl **vp, *v; if(irq >= nelem(vctl)) panic("irqdisable irq %d", irq); lock(&vctllock); for(vp = &vctl[irq]; v = *vp; vp = &v->next) if (v->f == f && v->a == a && strcmp(v->name, name) == 0){ print("irqdisable: remove %s\n", name); *vp = v->next; free(v->name); free(v); break; } if(v == nil) print("irqdisable: irq %d, name %s not enabled\n", irq, name); if(vctl[irq] == nil){ print("irqdisable: clear icmr bit %d\n", irq); intcmask(irq); } unlock(&vctllock); return 0; } /* * called by trap to handle access faults */ static void faultarm(Ureg *ureg, uintptr va, int user, int read) { int n, insyscall; if(up == nil) { dumpstackwithureg(ureg); panic("faultarm: cpu%d: nil up, %sing %#p at %#p", m->machno, (read? "read": "writ"), va, ureg->pc); } insyscall = up->insyscall; up->insyscall = 1; n = fault(va, read); /* goes spllo */ splhi(); if(n < 0){ char buf[ERRMAX]; if(!user){ dumpstackwithureg(ureg); panic("fault: cpu%d: kernel %sing %#p at %#p", m->machno, read? "read": "writ", va, ureg->pc); } /* don't dump registers; programs suicide all the time */ snprint(buf, sizeof buf, "sys: trap: fault %s va=%#p", read? "read": "write", va); postnote(up, 1, buf, NDebug); } up->insyscall = insyscall; } /* * called by trap to handle interrupts. * returns true iff a clock interrupt, thus maybe reschedule. */ static int irq(Ureg* ureg) { int clockintr, ack; uint irqno, handled, t, ticks; Intrcpuregs *icp = (Intrcpuregs *)soc.intr; Vctl *v; ticks = perfticks(); handled = 0; ack = intack(icp); irqno = ack & Intrmask; if (irqno >= nelem(vctl)) { iprint("trap: irq %d >= # vectors (%d)\n", irqno, nelem(vctl)); intdismiss(icp, ack); return 0; } if (irqno == Loctmrirq) /* this is a clock intr? */ m->inclockintr++; /* yes, count nesting */ if(m->machno && m->inclockintr > 1) { iprint("cpu%d: nested clock intrs\n", m->machno); m->inclockintr--; intdismiss(icp, ack); return 0; } for(v = vctl[irqno]; v != nil; v = v->next) if (v->f) { if (islo()) panic("trap: pl0 before trap handler for %s", v->name); v->f(ureg, v->a); if (islo()) panic("trap: %s lowered pl", v->name); // splhi(); /* in case v->f lowered pl */ handled++; } if(!handled) if (irqno >= 1022) iprint("cpu%d: ignoring spurious interrupt\n", m->machno); else { intcmask(irqno); iprint("cpu%d: unexpected interrupt %d, now masked\n", m->machno, irqno); } t = perfticks(); if (0) { /* left over from another port? */ ilock(&nintrlock); ninterrupt++; if(t < ticks) ninterruptticks += ticks-t; else ninterruptticks += t-ticks; iunlock(&nintrlock); } USED(t, ticks); clockintr = m->inclockintr == 1; if (irqno == Loctmrirq) m->inclockintr--; intdismiss(icp, ack); intrtime(m, irqno); return clockintr; } /* * returns 1 if the instruction writes memory, 0 otherwise */ int writetomem(ulong inst) { /* swap always write memory */ if((inst & 0x0FC00000) == 0x01000000) return 1; /* loads and stores are distinguished by bit 20 */ if(inst & (1<<20)) return 0; return 1; } static void datafault(Ureg *ureg, int user) { int x; ulong inst, fsr; uintptr va; va = farget(); if (m->probing && !user) { if (m->trapped++ > 0) { dumpstackwithureg(ureg); panic("trap: recursive probe %#lux", va); } ureg->pc += 4; /* continue after faulting instr'n */ return; } inst = *(ulong*)(ureg->pc); /* bits 12 and 10 have to be concatenated with status */ x = fsrget(); fsr = (x>>7) & 0x20 | (x>>6) & 0x10 | x & 0xf; switch(fsr){ default: case 0xa: /* ? was under external abort */ panic("unknown data fault, 6b fsr %#lux", fsr); break; case 0x0: panic("vector exception at %#lux", ureg->pc); break; case 0x1: /* alignment fault */ case 0x3: /* access flag fault (section) */ if(user){ char buf[ERRMAX]; snprint(buf, sizeof buf, "sys: alignment: pc %#lux va %#p\n", ureg->pc, va); postnote(up, 1, buf, NDebug); } else { dumpstackwithureg(ureg); panic("kernel alignment: pc %#lux va %#p", ureg->pc, va); } break; case 0x2: panic("terminal exception at %#lux", ureg->pc); break; case 0x4: /* icache maint fault */ case 0x6: /* access flag fault (page) */ case 0x8: /* precise external abort, non-xlat'n */ case 0x28: case 0x16: /* imprecise ext. abort, non-xlt'n */ case 0x36: panic("external non-translation abort %#lux pc %#lux addr %#p", fsr, ureg->pc, va); break; case 0xc: /* l1 translation, precise ext. abort */ case 0x2c: case 0xe: /* l2 translation, precise ext. abort */ case 0x2e: panic("external translation abort %#lux pc %#lux addr %#p", fsr, ureg->pc, va); break; case 0x1c: /* l1 translation, precise parity err */ case 0x1e: /* l2 translation, precise parity err */ case 0x18: /* imprecise parity or ecc err */ panic("translation parity error %#lux pc %#lux addr %#p", fsr, ureg->pc, va); break; case 0x5: /* translation fault, no section entry */ case 0x7: /* translation fault, no page entry */ faultarm(ureg, va, user, !writetomem(inst)); break; case 0x9: case 0xb: /* domain fault, accessing something we shouldn't */ if(user){ char buf[ERRMAX]; snprint(buf, sizeof buf, "sys: access violation: pc %#lux va %#p\n", ureg->pc, va); postnote(up, 1, buf, NDebug); } else panic("kernel access violation: pc %#lux va %#p", ureg->pc, va); break; case 0xd: case 0xf: /* permission error, copy on write or real permission error */ faultarm(ureg, va, user, !writetomem(inst)); break; } } /* * here on all exceptions other than syscall (SWI) and reset */ void trap(Ureg *ureg) { int clockintr, user, rem; uintptr va, ifar, ifsr; splhi(); /* paranoia */ if(up != nil) rem = ((char*)ureg)-up->kstack; else rem = ((char*)ureg)-((char*)m+sizeof(Mach)); if(rem < 1024) { iprint("trap: %d stack bytes left, up %#p ureg %#p m %#p cpu%d at pc %#lux\n", rem, up, ureg, m, m->machno, ureg->pc); dumpstackwithureg(ureg); panic("trap: %d stack bytes left, up %#p ureg %#p at pc %#lux", rem, up, ureg, ureg->pc); } m->perf.intrts = perfticks(); user = (ureg->psr & PsrMask) == PsrMusr; if(user){ up->dbgreg = ureg; cycles(&up->kentry); } /* * All interrupts/exceptions should be resumed at ureg->pc-4, * except for Data Abort which resumes at ureg->pc-8. */ if(ureg->type == (PsrMabt+1)) ureg->pc -= 8; else ureg->pc -= 4; clockintr = 0; /* if set, may call sched() before return */ switch(ureg->type){ default: panic("unknown trap; type %#lux, psr mode %#lux", ureg->type, ureg->psr & PsrMask); break; case PsrMirq: m->intr++; clockintr = irq(ureg); if(0 && up && !clockintr) preempted(); /* this causes spurious suicides */ break; case PsrMabt: /* prefetch (instruction) fault */ va = ureg->pc; ifsr = cprdsc(0, CpFSR, 0, CpIFSR); ifsr = (ifsr>>7) & 0x8 | ifsr & 0x7; switch(ifsr){ case 0x02: /* instruction debug event (BKPT) */ if(user) postnote(up, 1, "sys: breakpoint", NDebug); else{ iprint("kernel bkpt: pc %#lux inst %#ux\n", va, *(u32int*)va); panic("kernel bkpt"); } break; default: ifar = cprdsc(0, CpFAR, 0, CpIFAR); if (va != ifar) iprint("trap: cpu%d: i-fault va %#p != ifar %#p\n", m->machno, va, ifar); faultarm(ureg, va, user, 1); break; } break; case PsrMabt+1: /* data fault */ datafault(ureg, user); break; case PsrMund: /* undefined instruction */ if(!user) { if (ureg->pc & 3) { iprint("rounding fault pc %#lux down to word\n", ureg->pc); ureg->pc &= ~3; } if (Debug) iprint("mathemu: cpu%d fpon %d instr %#8.8lux at %#p\n", m->machno, m->fpon, *(ulong *)ureg->pc, ureg->pc); dumpstackwithureg(ureg); panic("cpu%d: undefined instruction: pc %#lux inst %#ux", m->machno, ureg->pc, ((u32int*)ureg->pc)[0]); } else if(seg(up, ureg->pc, 0) != nil && *(u32int*)ureg->pc == 0xD1200070) postnote(up, 1, "sys: breakpoint", NDebug); else if(fpuemu(ureg) == 0){ /* didn't find any FP instrs? */ char buf[ERRMAX]; snprint(buf, sizeof buf, "undefined instruction: pc %#lux instr %#8.8lux\n", ureg->pc, *(ulong *)ureg->pc); postnote(up, 1, buf, NDebug); } break; } splhi(); /* delaysched set because we held a lock or because our quantum ended */ if(up && up->delaysched && clockintr){ sched(); /* can cause more traps */ splhi(); } if(user){ if(up->procctl || up->nnote) notify(ureg); kexit(ureg); } } /* * Fill in enough of Ureg to get a stack trace, and call a function. * Used by debugging interface rdb. */ void callwithureg(void (*fn)(Ureg*)) { Ureg ureg; memset(&ureg, 0, sizeof ureg); ureg.pc = getcallerpc(&fn); ureg.sp = PTR2UINT(&fn); fn(&ureg); } static void dumpstackwithureg(Ureg *ureg) { int x; uintptr l, v, i, estack; char *s; dumpregs(ureg); if((s = getconf("*nodumpstack")) != nil && strcmp(s, "0") != 0){ iprint("dumpstack disabled\n"); return; } delay(1000); iprint("dumpstack\n"); x = 0; x += iprint("ktrace /kernel/path %#.8lux %#.8lux %#.8lux # pc, sp, link\n", ureg->pc, ureg->sp, ureg->r14); delay(20); i = 0; if(up && (uintptr)&l >= (uintptr)up->kstack && (uintptr)&l <= (uintptr)up->kstack+KSTACK) estack = (uintptr)up->kstack+KSTACK; else if((uintptr)&l >= (uintptr)m->stack && (uintptr)&l <= (uintptr)m+MACHSIZE) estack = (uintptr)m+MACHSIZE; else return; x += iprint("estackx %p\n", estack); for(l = (uintptr)&l; l < estack; l += sizeof(uintptr)){ v = *(uintptr*)l; if((KTZERO < v && v < (uintptr)etext) || estack-l < 32){ x += iprint("%.8p ", v); delay(20); i++; } if(i == 8){ i = 0; x += iprint("\n"); delay(20); } } if(i) iprint("\n"); delay(3000); } void dumpstack(void) { callwithureg(dumpstackwithureg); } /* * dump system control coprocessor registers */ static void dumpscr(void) { iprint("0:\t%#8.8ux id\n", cpidget()); iprint("\t%8.8#ux ct\n", cpctget()); iprint("1:\t%#8.8ux control\n", controlget()); iprint("2:\t%#8.8ux ttb\n", ttbget()); iprint("3:\t%#8.8ux dac\n", dacget()); iprint("4:\t(reserved)\n"); iprint("5:\t%#8.8ux fsr\n", fsrget()); iprint("6:\t%#8.8ux far\n", farget()); iprint("7:\twrite-only cache\n"); iprint("8:\twrite-only tlb\n"); iprint("13:\t%#8.8ux pid\n", pidget()); delay(10); } /* * dump general registers */ static void dumpgpr(Ureg* ureg) { if(up != nil) iprint("cpu%d: registers for %s %lud\n", m->machno, up->text, up->pid); else iprint("cpu%d: registers for kernel\n", m->machno); delay(20); iprint("%#8.8lux\tr0\n", ureg->r0); iprint("%#8.8lux\tr1\n", ureg->r1); iprint("%#8.8lux\tr2\n", ureg->r2); delay(20); iprint("%#8.8lux\tr3\n", ureg->r3); iprint("%#8.8lux\tr4\n", ureg->r4); iprint("%#8.8lux\tr5\n", ureg->r5); delay(20); iprint("%#8.8lux\tr6\n", ureg->r6); iprint("%#8.8lux\tr7\n", ureg->r7); iprint("%#8.8lux\tr8\n", ureg->r8); delay(20); iprint("%#8.8lux\tr9 (up)\n", ureg->r9); iprint("%#8.8lux\tr10 (m)\n", ureg->r10); iprint("%#8.8lux\tr11 (loader temporary)\n", ureg->r11); iprint("%#8.8lux\tr12 (SB)\n", ureg->r12); delay(20); iprint("%#8.8lux\tr13 (sp)\n", ureg->r13); iprint("%#8.8lux\tr14 (link)\n", ureg->r14); iprint("%#8.8lux\tr15 (pc)\n", ureg->pc); delay(20); iprint("%10.10lud\ttype\n", ureg->type); iprint("%#8.8lux\tpsr\n", ureg->psr); delay(500); } void dumpregs(Ureg* ureg) { dumpgpr(ureg); dumpscr(); } vlong probeaddr(uintptr addr) { vlong v; ilock(&m->probelock); m->trapped = 0; m->probing = 1; coherence(); v = *(ulong *)addr; /* this may cause a fault */ coherence(); m->probing = 0; if (m->trapped) v = -1; iunlock(&m->probelock); return v; } 0; ack = intack(icp); irqno = ack & Intrmask; i