ref: fff070f2cbb01b7c0879e9dcb13ee4e3ed2497f0
parent: 9126ee3eea90d639f4e877c01400248581d10f65
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Sun May 8 12:50:29 EDT 2022
imx8: add work in progress i.MX8MQ kernel for the mntreform2 laptop This is a work in progress port to the mntreform2 laptop. Working so far: - mmu (same as raspberry pi 3b+) - arm generic timer - gicv3 - uart1 - enet With access to the uart, one can netboot this kernel in u-boot using the following commands: > dhcp > bootm
--- /dev/null
+++ b/sys/src/9/imx8/cache.v8.s
@@ -1,0 +1,212 @@
+#include "sysreg.h"
+
+#undef SYSREG
+#define SYSREG(op0,op1,Cn,Cm,op2) SPR(((op0)<<19|(op1)<<16|(Cn)<<12|(Cm)<<8|(op2)<<5))
+
+/*
+ * instruction cache operations
+ */
+TEXT cacheiinvse(SB), 1, $-4
+ MOVWU len+8(FP), R2
+ ADD R0, R2
+
+ MRS DAIF, R11
+ MSR $0x2, DAIFSet
+ MOVWU $1, R10
+ MSR R10, CSSELR_EL1
+ ISB $SY
+ MRS CCSIDR_EL1, R4
+
+ ANDW $7, R4
+ ADDW $4, R4 // log2(linelen)
+ LSL R4, R10
+ LSR R4, R0
+ LSL R4, R0
+
+_iinvse:
+ IC R0, 3,7,5,1 // IVAU
+ ADD R10, R0
+ CMP R0, R2
+ BGT _iinvse
+ DSB $NSH
+ ISB $SY
+ MSR R11, DAIF
+ RETURN
+
+TEXT cacheiinv(SB), 1, $-4
+ IC R0, 0,7,5,0 // IALLU
+ DSB $NSH
+ ISB $SY
+ RETURN
+
+TEXT cacheuwbinv(SB), 1, $0
+ BL cachedwbinv(SB)
+ BL cacheiinv(SB)
+ RETURN
+
+/*
+ * data cache operations
+ */
+TEXT cachedwbse(SB), 1, $-4
+ MOV LR, R29
+ BL cachedva<>(SB)
+TEXT dccvac(SB), 1, $-4
+ DC R0, 3,7,10,1 // CVAC
+ RETURN
+
+TEXT cacheduwbse(SB), 1, $-4
+ MOV LR, R29
+ BL cachedva<>(SB)
+TEXT dccvau(SB), 1, $-4
+ DC R0, 3,7,11,1 // CVAU
+ RETURN
+
+TEXT cachedinvse(SB), 1, $-4
+ MOV LR, R29
+ BL cachedva<>(SB)
+TEXT dcivac(SB), 1, $-4
+ DC R0, 0,7,6,1 // IVAC
+ RETURN
+
+TEXT cachedwbinvse(SB), 1, $-4
+ MOV LR, R29
+ BL cachedva<>(SB)
+TEXT dccivac(SB), 1, $-4
+ DC R0, 3,7,14,1 // CIVAC
+ RETURN
+
+TEXT cachedva<>(SB), 1, $-4
+ MOV LR, R1
+ MOVWU len+8(FP), R2
+ ADD R0, R2
+
+ MRS DAIF, R11
+ MSR $0x2, DAIFSet
+ MOVWU $0, R10
+ MSR R10, CSSELR_EL1
+ ISB $SY
+ MRS CCSIDR_EL1, R4
+
+ ANDW $7, R4
+ ADDW $4, R4 // log2(linelen)
+ MOVWU $1, R10
+ LSL R4, R10
+ LSR R4, R0
+ LSL R4, R0
+
+ DSB $SY
+ ISB $SY
+_cachedva:
+ BL (R1)
+ ADD R10, R0
+ CMP R0, R2
+ BGT _cachedva
+ DSB $SY
+ ISB $SY
+ MSR R11, DAIF
+ RET R29
+
+/*
+ * l1 cache operations
+ */
+TEXT cachedwb(SB), 1, $-4
+ MOVWU $0, R0
+_cachedwb:
+ MOV LR, R29
+ BL cachedsw<>(SB)
+TEXT dccsw(SB), 1, $-4
+ DC R0, 0,7,10,2 // CSW
+ RETURN
+
+TEXT cachedinv(SB), 1, $-4
+ MOVWU $0, R0
+_cachedinv:
+ MOV LR, R29
+ BL cachedsw<>(SB)
+TEXT dcisw(SB), 1, $-4
+ DC R0, 0,7,6,2 // ISW
+ RETURN
+
+TEXT cachedwbinv(SB), 1, $-4
+ MOVWU $0, R0
+_cachedwbinv:
+ MOV LR, R29
+ BL cachedsw<>(SB)
+TEXT dccisw(SB), 1, $-4
+ DC R0, 0,7,14,2 // CISW
+ RETURN
+
+/*
+ * l2 cache operations
+ */
+TEXT l2cacheuwb(SB), 1, $-4
+ MOVWU $1, R0
+ B _cachedwb
+TEXT l2cacheuinv(SB), 1, $-4
+ MOVWU $1, R0
+ B _cachedinv
+TEXT l2cacheuwbinv(SB), 1, $-4
+ MOVWU $1, R0
+ B _cachedwbinv
+
+TEXT cachesize(SB), 1, $-4
+ MRS DAIF, R11
+ MSR $0x2, DAIFSet
+ MSR R0, CSSELR_EL1
+ ISB $SY
+ MRS CCSIDR_EL1, R0
+ MSR R11, DAIF
+ RETURN
+
+TEXT cachedsw<>(SB), 1, $-4
+ MOV LR, R1
+
+ MRS DAIF, R11
+ MSR $0x2, DAIFSet
+ ADDW R0, R0, R8
+ MSR R8, CSSELR_EL1
+ ISB $SY
+ MRS CCSIDR_EL1, R4
+
+ LSR $3, R4, R7
+ ANDW $1023, R7 // lastway
+ ADDW $1, R7, R5 // #ways
+
+ LSR $13, R4, R2
+ ANDW $32767, R2 // lastset
+ ADDW $1, R2 // #sets
+
+ ANDW $7, R4
+ ADDW $4, R4 // log2(linelen)
+
+ MOVWU $32, R3 // wayshift = 32 - log2(#ways)
+_countlog2ways:
+ CBZ R7, _loop // lastway == 0?
+ LSR $1, R7 // lastway >>= 1
+ SUB $1, R3 // wayshift--
+ B _countlog2ways
+_loop:
+ DSB $SY
+ ISB $SY
+_nextway:
+ MOVWU $0, R6 // set
+_nextset:
+ LSL R3, R7, R0 // way<<wayshift
+ LSL R4, R6, R9 // set<<log2(linelen)
+ ORRW R8, R0 // level
+ ORRW R9, R0 // setway
+
+ BL (R1) // op(setway)
+
+ ADDW $1, R6 // set++
+ CMPW R2, R6
+ BLT _nextset
+
+ ADDW $1, R7 // way++
+ CMPW R5, R7
+ BLT _nextway
+
+ DSB $SY
+ ISB $SY
+ MSR R11, DAIF
+ RET R29
--- /dev/null
+++ b/sys/src/9/imx8/clock.c
@@ -1,0 +1,114 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "sysreg.h"
+
+static uvlong freq;
+
+enum {
+ Enable = 1<<0,
+ Imask = 1<<1,
+ Istatus = 1<<2,
+};
+
+void
+clockshutdown(void)
+{
+}
+
+static void
+localclockintr(Ureg *ureg, void *)
+{
+ timerintr(ureg, 0);
+}
+
+void
+clockinit(void)
+{
+ syswr(PMCR_EL0, 1<<6 | 7);
+ syswr(PMCNTENSET, 1<<31);
+ syswr(PMUSERENR_EL0, 1<<2);
+ syswr(CNTKCTL_EL1, 1<<1);
+
+ syswr(CNTP_TVAL_EL0, ~0UL);
+ syswr(CNTP_CTL_EL0, Enable);
+
+ if(m->machno == 0){
+ freq = sysrd(CNTFRQ_EL0);
+ print("timer frequency %lld Hz\n", freq);
+ }
+
+ intrenable(IRQcntpns, localclockintr, nil, BUSUNKNOWN, "clock");
+}
+
+void
+timerset(uvlong next)
+{
+ uvlong now;
+ long period;
+
+ now = fastticks(nil);
+ period = next - now;
+ syswr(CNTP_TVAL_EL0, period);
+}
+
+uvlong
+fastticks(uvlong *hz)
+{
+ if(hz)
+ *hz = freq;
+ return sysrd(CNTPCT_EL0);
+}
+
+ulong
+perfticks(void)
+{
+ return fastticks(nil);
+}
+
+ulong
+µs(void)
+{
+ uvlong hz;
+ uvlong t = fastticks(&hz);
+ return (t * 1000000ULL) / hz;
+}
+
+void
+microdelay(int n)
+{
+ ulong now;
+
+ now = µs();
+ while(µs() - now < n);
+}
+
+void
+delay(int n)
+{
+ while(--n >= 0)
+ microdelay(1000);
+}
+
+void
+synccycles(void)
+{
+ static Ref r1, r2;
+ int s;
+
+ s = splhi();
+ r2.ref = 0;
+ incref(&r1);
+ while(r1.ref != conf.nmach)
+ ;
+// syswr(PMCR_EL0, 1<<6 | 7);
+ incref(&r2);
+ while(r2.ref != conf.nmach)
+ ;
+ r1.ref = 0;
+ splx(s);
+}
--- /dev/null
+++ b/sys/src/9/imx8/dat.h
@@ -1,0 +1,218 @@
+/*
+ * Time.
+ *
+ * HZ should divide 1000 evenly, ideally.
+ * 100, 125, 200, 250 and 333 are okay.
+ */
+#define HZ 100 /* clock frequency */
+#define MS2HZ (1000/HZ) /* millisec per clock tick */
+#define TK2SEC(t) ((t)/HZ) /* ticks to seconds */
+
+enum {
+ Mhz = 1000 * 1000,
+};
+
+typedef struct Conf Conf;
+typedef struct Confmem Confmem;
+typedef struct FPsave FPsave;
+typedef struct PFPU PFPU;
+typedef struct ISAConf ISAConf;
+typedef struct Label Label;
+typedef struct Lock Lock;
+typedef struct Memcache Memcache;
+typedef struct MMMU MMMU;
+typedef struct Mach Mach;
+typedef struct Page Page;
+typedef struct PhysUart PhysUart;
+typedef struct Pcidev Pcidev;
+typedef struct PMMU PMMU;
+typedef struct Proc Proc;
+typedef u64int PTE;
+typedef struct Soc Soc;
+typedef struct Uart Uart;
+typedef struct Ureg Ureg;
+typedef uvlong Tval;
+typedef void KMap;
+
+#pragma incomplete Pcidev
+#pragma incomplete Ureg
+
+#define MAXSYSARG 5 /* for mount(fd, mpt, flag, arg, srv) */
+
+/*
+ * parameters for sysproc.c
+ */
+#define AOUT_MAGIC (R_MAGIC)
+
+struct Lock
+{
+ ulong key;
+ u32int sr;
+ uintptr pc;
+ Proc* p;
+ Mach* m;
+ int isilock;
+};
+
+struct Label
+{
+ uintptr sp;
+ uintptr pc;
+};
+
+struct FPsave
+{
+ uvlong regs[32][2];
+
+ ulong control;
+ ulong status;
+};
+
+struct PFPU
+{
+ FPsave fpsave[1];
+
+ int fpstate;
+};
+
+enum
+{
+ FPinit,
+ FPactive,
+ FPinactive,
+
+ /* bits or'd with the state */
+ FPillegal= 0x100,
+};
+
+struct Confmem
+{
+ uintptr base;
+ ulong npage;
+ uintptr limit;
+ uintptr kbase;
+ uintptr klimit;
+};
+
+struct Conf
+{
+ ulong nmach; /* processors */
+ ulong nproc; /* processes */
+ Confmem mem[3]; /* physical memory */
+ ulong npage; /* total physical pages of memory */
+ ulong upages; /* user page pool */
+ ulong copymode; /* 0 is copy on write, 1 is copy on reference */
+ ulong ialloc; /* max interrupt time allocation in bytes */
+ ulong pipeqsize; /* size in bytes of pipe queues */
+ ulong nimage; /* number of page cache image headers */
+ ulong nswap; /* number of swap pages */
+ int nswppo; /* max # of pageouts per segment pass */
+ ulong hz; /* processor cycle freq */
+ ulong mhz;
+ int monitor; /* flag */
+};
+
+/*
+ * MMU stuff in Mach.
+ */
+struct MMMU
+{
+ PTE* mmutop; /* first level user page table */
+};
+
+/*
+ * MMU stuff in proc
+ */
+#define NCOLOR 1 /* 1 level cache, don't worry about VCE's */
+
+struct PMMU
+{
+ union {
+ Page *mmufree; /* mmuhead[0] is freelist head */
+ Page *mmuhead[PTLEVELS];
+ };
+ Page *mmutail[PTLEVELS];
+ int asid;
+ uintptr tpidr;
+};
+
+#include "../port/portdat.h"
+
+struct Mach
+{
+ int machno; /* physical id of processor */
+ uintptr splpc; /* pc of last caller to splhi */
+ Proc* proc; /* current process on this processor */
+ /* end of offsets known to asm */
+
+ MMMU;
+
+ PMach;
+
+ int cputype;
+ ulong delayloop;
+ int cpumhz;
+ uvlong cpuhz; /* speed of cpu */
+
+ int stack[1];
+};
+
+struct
+{
+ char machs[MAXMACH]; /* active CPUs */
+ int exiting; /* shutdown */
+}active;
+
+#define MACHP(n) ((Mach*)MACHADDR(n))
+
+extern register Mach* m; /* R27 */
+extern register Proc* up; /* R26 */
+extern int normalprint;
+
+/*
+ * a parsed plan9.ini line
+ */
+#define NISAOPT 8
+
+struct ISAConf {
+ char *type;
+ uvlong port;
+ int irq;
+ ulong dma;
+ ulong mem;
+ ulong size;
+ ulong freq;
+
+ int nopt;
+ char *opt[NISAOPT];
+};
+
+/*
+ * Horrid. But the alternative is 'defined'.
+ */
+#ifdef _DBGC_
+#define DBGFLG (dbgflg[_DBGC_])
+#else
+#define DBGFLG (0)
+#endif /* _DBGC_ */
+
+int vflag;
+extern char dbgflg[256];
+
+#define dbgprint print /* for now */
+
+/*
+ * hardware info about a device
+ */
+typedef struct {
+ ulong port;
+ int size;
+} Devport;
+
+struct DevConf
+{
+ ulong intnum; /* interrupt number */
+ char *type; /* card type, malloced */
+ int nports; /* Number of ports */
+ Devport *ports; /* The ports themselves */
+};
--- /dev/null
+++ b/sys/src/9/imx8/etherimx.c
@@ -1,0 +1,706 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/netif.h"
+#include "../port/etherif.h"
+#include "../port/ethermii.h"
+
+enum {
+ Moduleclk = 125000000, /* 125Mhz */
+ Maxtu = 1518,
+
+ R_BUF_SIZE = ((Maxtu+BLOCKALIGN-1)&~BLOCKALIGN),
+};
+
+enum {
+ ENET_EIR = 0x004/4, /* Interrupt Event Register */
+ ENET_EIMR = 0x008/4, /* Interrupt Mask Register */
+ INT_BABR =1<<30, /* Babbling Receive Error */
+ INT_BABT =1<<31, /* Babbling Transmit Error */
+ INT_GRA =1<<28, /* Graceful Stop Complete */
+ INT_TXF =1<<27, /* Transmit Frame Interrupt */
+ INT_TXB =1<<26, /* Transmit Buffer Interrupt */
+ INT_RXF =1<<25, /* Receive Frame Interrupt */
+ INT_RXB =1<<24, /* Receive Buffer Interrupt */
+ INT_MII =1<<23, /* MII Interrupt */
+ INT_EBERR =1<<22, /* Ethernet Bus Error */
+ INT_LC =1<<21, /* Late Collision */
+ INT_RL =1<<20, /* Collision Retry Limit */
+ INT_UN =1<<19, /* Transmit FIFO Underrun */
+ INT_PLR =1<<18, /* Payload Receive Error */
+ INT_WAKEUP =1<<17, /* Node Wakeup Request Indication */
+ INT_TS_AVAIL =1<<16, /* Transmit Timestamp Available */
+ INT_TS_TIMER =1<<15, /* Timestamp Timer */
+ INT_RXFLUSH_2 =1<<14, /* RX DMA Ring 2 flush indication */
+ INT_RXFLUSH_1 =1<<13, /* RX DMA Ring 1 flush indication */
+ INT_RXFLUSH_0 =1<<12, /* RX DMA Ring 0 flush indication */
+ INT_TXF2 =1<<7, /* Transmit frame interrupt, class 2 */
+ INT_TXB2 =1<<6, /* Transmit buffer interrupt, class 2 */
+ INT_RXF2 =1<<5, /* Receive frame interrupt, class 2 */
+ INT_RXB2 =1<<4, /* Receive buffer interrupt, class 2 */
+ INT_TXF1 =1<<3, /* Transmit frame interrupt, class 1 */
+ INT_TXB1 =1<<2, /* Transmit buffer interrupt, class 1 */
+ INT_RXF1 =1<<1, /* Receive frame interrupt, class 1 */
+ INT_RXB1 =1<<0, /* Receive buffer interrupt, class 1 */
+
+ ENET_RDAR = 0x010/4, /* Receive Descriptor Active Register */
+ RDAR_ACTIVE =1<<24, /* Descriptor Active */
+ ENET_TDAR = 0x014/4, /* Transmit Descriptor Active Register */
+ TDAR_ACTIVE =1<<24, /* Descriptor Active */
+
+ ENET_ECR = 0x024/4, /* Ethernet Control Register */
+ ECR_RESERVED =7<<28,
+ ECR_SVLANDBL =1<<11, /* S-VLAN double tag */
+ ECR_VLANUSE2ND =1<<10, /* VLAN use second tag */
+ ECR_SVLANEN =1<<9, /* S-VLAN enable */
+ ECR_DBSWP =1<<8, /* Descriptor Byte Swapping Enable */
+ ECR_DBGEN =1<<6, /* Debug Enable */
+ ECR_SPEED_100M =0<<5,
+ ECR_SPEED_1000M =1<<5,
+ ECR_EN1588 =1<<4, /* Enables enhanced functionality of the MAC */
+ ECR_SLEEP =1<<3, /* Sleep Mode Enable */
+ ECR_MAGICEN =1<<2, /* Magic Packet Detection Enable */
+ ECR_ETHEREN =1<<1, /* Ethernet Enable */
+ ECR_RESET =1<<0, /* Ethernet MAC Reset */
+
+ ENET_MMFR = 0x040/4, /* MII Management Frame Register */
+ MMFR_ST =1<<30,
+ MMFR_RD =2<<28,
+ MMFR_WR =1<<28,
+ MMFR_PA_SHIFT =23,
+ MMFR_TA =2<<16,
+ MMFR_RA_SHIFT =18,
+
+ ENET_MSCR = 0x044/4, /* MII Speed Control Register */
+ MSCR_SPEED_SHIFT=1, /* MII speed = module_clock/((SPEED+1)*2) */
+ MSCR_DIS_PRE =1<<7, /* disable preamble */
+ MSCR_HOLD_SHIFT =8, /* hold cycles in module_clock */
+
+ ENET_MIBC = 0x064/4, /* MIB Control Register */
+ ENET_RCR = 0x084/4, /* Receive Control Register */
+ RCR_GRS =1<<31, /* Gracefull Receive Stopped */
+ RCR_NLC =1<<30, /* Payload Length Check Disable */
+ RCR_MAX_FL_SHIFT=16, /* Maximum Frame Length */
+ RCR_CFEN =1<<15, /* MAC Control Frame Enable */
+ RCR_CRCFWD =1<<14, /* Forward Received CRC */
+ RCR_PAUFWD =1<<13, /* Forward Pause Frames */
+ RCR_PADEN =1<<12, /* Enable Frame Padding Remove */
+ RCR_RMII_10T =1<<9, /* Enables 10-Mbit/s mode of the RMII/RGMII */
+ RCR_RMII_MODE =1<<8, /* RMII Mode Enable */
+ RCR_RGMII_EN =1<<6, /* RGMII Mode Enable */
+ RCR_FCE =1<<5, /* Flow Control Enable */
+ RCR_REJ =1<<4, /* Broadcast Frame Reject */
+ RCR_PROM =1<<3, /* Promiscuous Mode */
+ RCR_MII_MODE =1<<2, /* Media Independent Interface Mode (must always be set) */
+ RCR_DRT =1<<1, /* Disable Receive On Timeout */
+ RCR_LOOP =1<<0, /* Internal Loopback */
+
+ ENET_TCR = 0x0C4/4, /* Transmit Control Register */
+ TCR_CRCFWD =1<<9, /* Foward Frame From Application With CRC */
+ TCR_ADDINS =1<<8, /* Set MAC Address on Transmit */
+ TCR_RFC_PAUSE =1<<4, /* Receive Frame Control Pause */
+ TCR_TFC_PAUSE =1<<3, /* Transmit Frame Control Pause */
+ TCR_FDEN =1<<2, /* Full-Duplex Enable */
+ TCR_GTS =1<<0, /* Graceful Transmit Stop */
+
+ ENET_PALR = 0x0E4/4, /* Physical Address Lower Register */
+ ENET_PAUR = 0x0E8/4, /* Physical Address Upper Register */
+
+ ENET_OPD = 0x0EC/4, /* Opcode/Pause Duration Register */
+
+ ENET_TXIC0 = 0x0F0/4, /* Transmit Interrupt Coalescing Register */
+ ENET_TXIC1 = 0x0F4/4, /* Transmit Interrupt Coalescing Register */
+ ENET_TXIC2 = 0x0F8/4, /* Transmit Interrupt Coalescing Register */
+ ENET_RXIC0 = 0x100/4, /* Receive Interrupt Coalescing Register */
+ ENET_RXIC1 = 0x104/4, /* Receive Interrupt Coalescing Register */
+ ENET_RXIC2 = 0x108/4, /* Receive Interrupt Coalescing Register */
+ IC_EN = 1<<31,
+ IC_CS = 1<<30,
+ IC_FT_SHIFT = 20,
+ IC_TT_SHIFT = 0,
+
+ ENET_IAUR = 0x118/4, /* Descriptor Individual Upper Address Register */
+ ENET_IALR = 0x11C/4, /* Descriptor Individual Lower Address Register */
+ ENET_GAUR = 0x120/4, /* Descriptor Group Upper Address Register */
+ ENET_GALR = 0x124/4, /* Descriptor Group Lower Address Register */
+ ENET_TFWR = 0x144/4, /* Transmit FIFO Watermark Register */
+ TFWR_STRFWD = 1<<8,
+
+ ENET_RDSR1 = 0x160/4, /* Receive Descriptor Ring 1 Start Register */
+ ENET_TDSR1 = 0x164/4, /* Transmit Buffer Descriptor Ring 1 Start Register */
+ ENET_MRBR1 = 0x168/4, /* Maximum Receive Buffer Size Register Ring 1 */
+
+ ENET_RDSR2 = 0x16C/4, /* Receive Descriptor Ring 2 Start Register */
+ ENET_TDSR2 = 0x170/4, /* Transmit Buffer Descriptor Ring 2 Start Register */
+ ENET_MRBR2 = 0x174/4, /* Maximum Receive Buffer Size Register Ring 2 */
+
+ ENET_RDSR = 0x180/4, /* Receive Descriptor Ring 0 Start Register */
+ ENET_TDSR = 0x184/4, /* Transmit Buffer Descriptor Ring 0 Start Register */
+ ENET_MRBR = 0x188/4, /* Maximum Receive Buffer Size Register Ring 0 */
+
+ ENET_RSFL = 0x190/4, /* Receive FIFO Section Full Threshold */
+ ENET_RSEM = 0x194/4, /* Receive FIFO Section Empty Threshold */
+ ENET_RAEM = 0x198/4, /* Receive FIFO Almost Empty Threshold */
+ ENET_RAFL = 0x19C/4, /* Receive FIFO Almost Full Threshold */
+
+ ENET_TSEM = 0x1A0/4, /* Transmit FIFO Section Empty Threshold */
+ ENET_TAEM = 0x1A4/4, /* Transmit FIFO Almost Empty Threshold */
+ ENET_TAFL = 0x1A8/4, /* Transmit FIFO Almost Full Threshold */
+
+ ENET_TIPG = 0x1AC/4, /* Transmit Inter-Packet Gap */
+ ENET_FTRL = 0x1B0/4, /* Frame Truncation Length */
+ ENET_TACC = 0x1C0/4, /* Transmit Accelerator Function Configuration */
+ ENET_RACC = 0x1C4/4, /* Receive Accelerator Function Configuration */
+
+ ENET_RCMR1 = 0x1C8/4, /* Receive Classification Match Register */
+ ENET_RCMR2 = 0x1CC/4, /* Receive Classification Match Register */
+
+ ENET_DMA1CFG = 0x1D8/4, /* DMA Class Based Configuration */
+ ENET_DMA2CFG = 0x1DC/4, /* DMA Class Based Configuration */
+
+ ENET_RDAR1 = 0x1E0/4, /* Receive Descriptor Active Register - Ring 1 */
+ ENET_TDAR1 = 0x1E4/4, /* Transmit Descriptor Active Register - Ring 1 */
+ ENET_RDAR2 = 0x1E8/4, /* Receive Descriptor Active Register - Ring 2 */
+ ENET_TDAR2 = 0x1EC/4, /* Transmit Descriptor Active Register - Ring 2 */
+
+ ENET_QOS = 0x1F0/4, /* QOS Scheme */
+};
+
+enum {
+ /* transmit descriptor status bits */
+ TD_R = 1<<(15+16), /* Ready */
+ TD_OWN = 1<<(14+16), /* Ownership */
+ TD_W = 1<<(13+16), /* Wrap */
+ TD_L = 1<<(11+16), /* Last in a frame */
+
+ TD_TC = 1<<(10+16), /* Transmit CRC */
+ TD_ERR = TD_TC,
+
+ TD_LEN = 0xFFFF,
+
+ /* receive desctriptor status bits */
+ RD_E = 1<<(15+16), /* Empty */
+ RD_W = 1<<(13+16), /* Wrap */
+ RD_L = 1<<(11+16), /* Last in a frame */
+
+ RD_M = 1<<(8+16), /* Miss */
+ RD_BC = 1<<(7+16), /* broadcast */
+ RD_MC = 1<<(6+16), /* multicast */
+
+ RD_LG = 1<<(5+16), /* length violation */
+ RD_NO = 1<<(4+16), /* non octet aligned frame */
+ RD_CR = 1<<(2+16), /* crc error */
+ RD_OV = 1<<(1+16), /* overrun */
+ RD_TR = 1<<(0+16), /* truncated */
+ RD_ERR = RD_LG | RD_NO | RD_CR | RD_OV | RD_TR,
+
+ RD_LEN = 0xFFFF,
+};
+
+typedef struct Descr Descr;
+struct Descr
+{
+ u32int status;
+ u32int addr;
+};
+
+typedef struct Ctlr Ctlr;
+struct Ctlr
+{
+ u32int *regs;
+ u32int intmask;
+
+ struct {
+ Block *b[256];
+ Descr *d;
+ Rendez;
+ } rx[1];
+
+ struct {
+ Block *b[256];
+ Descr *d;
+ Rendez;
+ } tx[1];
+
+ struct {
+ Rendez;
+ } free[1];
+
+ struct {
+ Mii;
+ Rendez;
+ } mii[1];
+
+ int attached;
+ QLock;
+};
+
+#define rr(c, r) ((c)->regs[r])
+#define wr(c, r, v) ((c)->regs[r] = (v))
+
+static int
+mdiodone(void *arg)
+{
+ Ctlr *ctlr = arg;
+ return rr(ctlr, ENET_EIR) & INT_MII;
+}
+static int
+mdiowait(Ctlr *ctlr)
+{
+ int i;
+
+ for(i = 0; i < 200; i++){
+ tsleep(ctlr->mii, mdiodone, ctlr, 5);
+ if(mdiodone(ctlr))
+ return 0;
+ }
+ return -1;
+}
+static int
+mdiow(Mii* mii, int phy, int addr, int data)
+{
+ Ctlr *ctlr = mii->ctlr;
+
+ data &= 0xFFFF;
+ wr(ctlr, ENET_EIR, INT_MII);
+ wr(ctlr, ENET_MMFR, MMFR_WR | MMFR_ST | MMFR_TA | phy<<MMFR_PA_SHIFT | addr<<MMFR_RA_SHIFT | data);
+ if(mdiowait(ctlr) < 0) return -1;
+ return data;
+}
+static int
+mdior(Mii* mii, int phy, int addr)
+{
+ Ctlr *ctlr = mii->ctlr;
+
+ wr(ctlr, ENET_EIR, INT_MII);
+ wr(ctlr, ENET_MMFR, MMFR_RD | MMFR_ST | MMFR_TA | phy<<MMFR_PA_SHIFT | addr<<MMFR_RA_SHIFT);
+ if(mdiowait(ctlr) < 0) return -1;
+ return rr(ctlr, ENET_MMFR) & 0xFFFF;
+}
+
+static void
+interrupt(Ureg*, void *arg)
+{
+ Ether *edev = arg;
+ Ctlr *ctlr = edev->ctlr;
+ u32int e;
+
+ e = rr(ctlr, ENET_EIR);
+ wr(ctlr, ENET_EIR, e);
+
+ if(e & INT_RXF) wakeup(ctlr->rx);
+ if(e & INT_TXF) wakeup(ctlr->tx);
+ if(e & INT_MII) wakeup(ctlr->mii);
+}
+
+static void
+shutdown(Ether *edev)
+{
+ Ctlr *ctlr = edev->ctlr;
+ coherence();
+
+ wr(ctlr, ENET_ECR, ECR_RESERVED | ECR_RESET);
+ while(rr(ctlr, ENET_ECR) & ECR_RESET) delay(1);
+
+ /* mask and clear interrupt events */
+ wr(ctlr, ENET_EIMR, 0);
+ wr(ctlr, ENET_EIR, ~0);
+}
+
+static int
+tdfree(void *arg)
+{
+ Descr *d = arg;
+ return (d->status & (TD_OWN|TD_R)) == 0;
+}
+
+static void
+txproc(void *arg)
+{
+ Ether *edev = arg;
+ Ctlr *ctlr = edev->ctlr;
+ Block *b;
+ Descr *d;
+ uint i = 0;
+
+ while(waserror())
+ ;
+
+ for(;;){
+ if((b = qbread(edev->oq, 100000)) == nil)
+ break;
+
+ d = &ctlr->tx->d[i];
+ while(!tdfree(d))
+ sleep(ctlr->free, tdfree, d);
+
+ ctlr->tx->b[i] = b;
+
+ dmaflush(1, b->rp, BLEN(b));
+ d->addr = PADDR(b->rp);
+ coherence();
+ if(i == nelem(ctlr->tx->b)-1){
+ d->status = BLEN(b) | TD_OWN | TD_R | TD_L | TD_TC | TD_W;
+ i = 0;
+ } else {
+ d->status = BLEN(b) | TD_OWN | TD_R | TD_L | TD_TC;
+ i++;
+ }
+ wr(ctlr, ENET_TDAR, TDAR_ACTIVE);
+ }
+}
+
+static int
+tddone(void *arg)
+{
+ Descr *d = arg;
+ return (d->status & (TD_OWN|TD_R)) == TD_OWN;
+}
+
+static void
+frproc(void *arg)
+{
+ Ether *edev = arg;
+ Ctlr *ctlr = edev->ctlr;
+ Block *b;
+ Descr *d;
+ uint i = 0;
+
+ while(waserror())
+ ;
+
+ for(;;){
+ d = &ctlr->tx->d[i];
+ while(!tddone(d))
+ sleep(ctlr->tx, tddone, d);
+
+ b = ctlr->tx->b[i];
+ ctlr->tx->b[i] = nil;
+ coherence();
+
+ if(i == nelem(ctlr->tx->b)-1){
+ d->status = TD_W;
+ i = 0;
+ } else {
+ d->status = 0;
+ i++;
+ }
+
+ wakeup(ctlr->free);
+ freeb(b);
+ }
+}
+
+static int
+rdfull(void *arg)
+{
+ Descr *d = arg;
+ return (d->status & RD_E) == 0;
+}
+
+static void
+rxproc(void *arg)
+{
+ Ether *edev = arg;
+ Ctlr *ctlr = edev->ctlr;
+ Block *b;
+ Descr *d;
+ uint s, i = 0;
+
+ while(waserror())
+ ;
+
+ for(;;){
+ d = &ctlr->rx->d[i];
+ s = d->status;
+ if(s & RD_E){
+ sleep(ctlr->rx, rdfull, d);
+ continue;
+ }
+ if(((s^RD_L) & (RD_L|RD_ERR)) == 0){
+ b = ctlr->rx->b[i];
+ b->wp = b->rp + (s & RD_LEN);
+ dmaflush(0, b->rp, BLEN(b));
+ etheriq(edev, b);
+
+ /* replenish */
+ b = allocb(R_BUF_SIZE);
+ ctlr->rx->b[i] = b;
+ dmaflush(1, b->rp, R_BUF_SIZE);
+ d->addr = PADDR(b->rp);
+ coherence();
+ }
+ if(i == nelem(ctlr->rx->b)-1) {
+ d->status = RD_E | RD_W;
+ i = 0;
+ } else {
+ d->status = RD_E;
+ i++;
+ }
+ wr(ctlr, ENET_RDAR, RDAR_ACTIVE);
+ }
+}
+
+static void
+linkproc(void *arg)
+{
+ Ether *edev = arg;
+ Ctlr *ctlr = edev->ctlr;
+ MiiPhy *phy;
+ int link = -1;
+
+ while(waserror())
+ ;
+
+ miiane(ctlr->mii, ~0, AnaAP|AnaP, ~0);
+
+ for(;;){
+ miistatus(ctlr->mii);
+ phy = ctlr->mii->curphy;
+ if(phy->link == link){
+ tsleep(ctlr->mii, return0, nil, 5000);
+ continue;
+ }
+ link = phy->link;
+ if(link){
+ u32int ecr = rr(ctlr, ENET_ECR) & ~ECR_SPEED_1000M;
+ u32int rcr = rr(ctlr, ENET_RCR) & ~(RCR_RMII_10T|RCR_FCE);
+ u32int tcr = rr(ctlr, ENET_TCR) & ~(TCR_RFC_PAUSE|TCR_TFC_PAUSE|TCR_FDEN);
+
+ switch(phy->speed){
+ case 1000:
+ ecr |= ECR_SPEED_1000M;
+ rcr |= RCR_FCE;
+
+ /* receive fifo thresholds */
+ wr(ctlr, ENET_RSFL, 16);
+ wr(ctlr, ENET_RSEM, 132);
+ wr(ctlr, ENET_RAEM, 8);
+ wr(ctlr, ENET_RAFL, 8);
+
+ /* opcode/pause duration */
+ wr(ctlr, ENET_OPD, 0xFFF0);
+ break;
+ case 100:
+ ecr |= ECR_SPEED_100M;
+ break;
+ case 10:
+ rcr |= RCR_RMII_10T;
+ break;
+ }
+ if(phy->fd)
+ tcr |= TCR_FDEN;
+ if(phy->rfc)
+ tcr |= TCR_RFC_PAUSE;
+ if(phy->tfc)
+ tcr |= TCR_TFC_PAUSE;
+
+ wr(ctlr, ENET_ECR, ecr);
+ wr(ctlr, ENET_RCR, rcr);
+ wr(ctlr, ENET_TCR, tcr);
+
+ edev->mbps = phy->speed;
+
+ wr(ctlr, ENET_RDAR, RDAR_ACTIVE);
+ }
+ edev->link = link;
+ print("#l%d: link %d speed %d\n", edev->ctlrno, edev->link, edev->mbps);
+ }
+}
+
+static void
+attach(Ether *edev)
+{
+ Ctlr *ctlr = edev->ctlr;
+ Descr *d;
+ int i;
+
+ eqlock(ctlr);
+ if(ctlr->attached){
+ qunlock(ctlr);
+ return;
+ }
+ if(waserror()){
+ qunlock(ctlr);
+ nexterror();
+ }
+
+ /* RGMII mode, max frame length */
+ wr(ctlr, ENET_RCR, RCR_MII_MODE | RCR_RGMII_EN | Maxtu<<RCR_MAX_FL_SHIFT);
+
+ /* set MII clock to 2.5Mhz, 10ns hold time */
+ wr(ctlr, ENET_MSCR, ((Moduleclk/(2*2500000))-1)<<MSCR_SPEED_SHIFT | ((Moduleclk/10000000)-1)<<MSCR_HOLD_SHIFT);
+
+ ctlr->intmask |= INT_MII;
+ wr(ctlr, ENET_EIMR, ctlr->intmask);
+ mii(ctlr->mii, ~0);
+
+ if(ctlr->mii->curphy == nil)
+ error("no phy");
+
+ print("#l%d: phy%d id %.8ux oui %x\n",
+ edev->ctlrno, ctlr->mii->curphy->phyno,
+ ctlr->mii->curphy->id, ctlr->mii->curphy->oui);
+
+ /* clear mac filter hash table */
+ wr(ctlr, ENET_IALR, 0);
+ wr(ctlr, ENET_IAUR, 0);
+ wr(ctlr, ENET_GALR, 0);
+ wr(ctlr, ENET_GAUR, 0);
+
+ /* set MAC address */
+ wr(ctlr, ENET_PALR, (u32int)edev->ea[0]<<24 | (u32int)edev->ea[1]<<16 | (u32int)edev->ea[2]<<8 | edev->ea[3]<<0);
+ wr(ctlr, ENET_PAUR, (u32int)edev->ea[4]<<24 | (u32int)edev->ea[5]<<16);
+
+ if(ctlr->rx->d == nil)
+ ctlr->rx->d = ucalloc(sizeof(Descr) * nelem(ctlr->rx->b));
+ for(i=0; i<nelem(ctlr->rx->b); i++){
+ Block *b = allocb(R_BUF_SIZE);
+ ctlr->rx->b[i] = b;
+ d = &ctlr->rx->d[i];
+ dmaflush(1, b->rp, R_BUF_SIZE);
+ d->addr = PADDR(b->rp);
+ d->status = RD_E;
+ }
+ ctlr->rx->d[nelem(ctlr->rx->b)-1].status = RD_E | RD_W;
+ wr(ctlr, ENET_MRBR, R_BUF_SIZE);
+ coherence();
+ wr(ctlr, ENET_RDSR, PADDR(ctlr->rx->d));
+
+ if(ctlr->tx->d == nil)
+ ctlr->tx->d = ucalloc(sizeof(Descr) * nelem(ctlr->tx->b));
+ for(i=0; i<nelem(ctlr->tx->b); i++){
+ ctlr->tx->b[i] = nil;
+ d = &ctlr->tx->d[i];
+ d->addr = 0;
+ d->status = 0;
+ }
+ ctlr->tx->d[nelem(ctlr->tx->b)-1].status = TD_W;
+ coherence();
+ wr(ctlr, ENET_TDSR, PADDR(ctlr->tx->d));
+
+ /* store and forward tx fifo */
+ wr(ctlr, ENET_TFWR, TFWR_STRFWD);
+
+ /* interrupt coalescing: 200 pkts, 1000 µs */
+ wr(ctlr, ENET_RXIC0, IC_EN | 200<<IC_FT_SHIFT | ((1000*Moduleclk)/64000000)<<IC_TT_SHIFT);
+ wr(ctlr, ENET_TXIC0, IC_EN | 200<<IC_FT_SHIFT | ((1000*Moduleclk)/64000000)<<IC_TT_SHIFT);
+
+ ctlr->intmask |= INT_TXF | INT_RXF;
+ wr(ctlr, ENET_EIMR, ctlr->intmask);
+
+ /* enable ethernet */
+ wr(ctlr, ENET_ECR, rr(ctlr, ENET_ECR) | ECR_ETHEREN | ECR_DBSWP);
+
+ ctlr->attached = 1;
+
+ kproc("ether-rx", rxproc, edev);
+ kproc("ether-tx", txproc, edev);
+ kproc("ether-fr", frproc, edev);
+
+ kproc("ether-link", linkproc, edev);
+
+ qunlock(ctlr);
+ poperror();
+}
+
+static void
+prom(void *arg, int on)
+{
+ Ether *edev = arg;
+ Ctlr *ctlr = edev->ctlr;
+
+ if(on)
+ wr(ctlr, ENET_RCR, rr(ctlr, ENET_RCR) | RCR_PROM);
+ else
+ wr(ctlr, ENET_RCR, rr(ctlr, ENET_RCR) & ~RCR_PROM);
+}
+
+static void
+multi(void *arg, uchar*, int)
+{
+ Ether *edev = arg;
+ Ctlr *ctlr = edev->ctlr;
+ Netaddr *a;
+ u64int hash;
+
+ hash = 0;
+ for(a = edev->maddr; a != nil; a = a->next)
+ hash |= 1ULL << ((ethercrc(a->addr, edev->alen) >> (32 - 6)) & 0x3F);
+
+ wr(ctlr, ENET_GALR, hash & 0xFFFFFFFF);
+ wr(ctlr, ENET_GAUR, hash >> 32);
+}
+
+static long
+ctl(Ether*, void*, long len)
+{
+ return len;
+}
+
+static int
+reset(Ether *edev)
+{
+ Ctlr *ctlr = edev->ctlr;
+ u32int paddr1, paddr2;
+
+ /* steal mac address from uboot */
+ paddr1 = rr(ctlr, ENET_PALR);
+ paddr2 = rr(ctlr, ENET_PAUR);
+ edev->ea[0] = paddr1>>24;
+ edev->ea[1] = paddr1>>16;
+ edev->ea[2] = paddr1>>8;
+ edev->ea[3] = paddr1>>0;
+ edev->ea[4] = paddr2>>24;
+ edev->ea[5] = paddr2>>16;
+
+ shutdown(edev);
+
+ return 0;
+}
+
+static int
+pnp(Ether *edev)
+{
+ static Ctlr ctlr[1];
+
+ if(ctlr->regs != nil)
+ return -1;
+
+ ctlr->regs = (u32int*)(VIRTIO + 0xbe0000);
+
+ ctlr->mii->ctlr = ctlr;
+ ctlr->mii->mir = mdior;
+ ctlr->mii->miw = mdiow;
+
+ edev->port = (uintptr)ctlr->regs - KZERO;
+ edev->irq = IRQenet1;
+ edev->ctlr = ctlr;
+ edev->attach = attach;
+ edev->shutdown = shutdown;
+ edev->promiscuous = prom;
+ edev->multicast = multi;
+ edev->ctl = ctl;
+ edev->arg = edev;
+ edev->mbps = 1000;
+ edev->maxmtu = Maxtu;
+
+ if(reset(edev) < 0)
+ return -1;
+
+ intrenable(edev->irq+0, interrupt, edev, BUSUNKNOWN, edev->name);
+ intrenable(edev->irq+1, interrupt, edev, BUSUNKNOWN, edev->name);
+ intrenable(edev->irq+2, interrupt, edev, BUSUNKNOWN, edev->name);
+ intrenable(edev->irq+3, interrupt, edev, BUSUNKNOWN, edev->name);
+
+ return 0;
+}
+
+void
+etherimxlink(void)
+{
+ addethercard("imx", pnp);
+}
--- /dev/null
+++ b/sys/src/9/imx8/fns.h
@@ -1,0 +1,139 @@
+#include "../port/portfns.h"
+
+/* l.s */
+extern void sev(void);
+extern int tas(void *);
+extern int cmpswap(long*, long, long);
+extern void coherence(void);
+extern void idlehands(void);
+extern uvlong vcycles(void);
+#define cycles(ip) *(ip) = vcycles()
+extern int splfhi(void);
+extern void splflo(void);
+extern void touser(uintptr sp);
+extern void forkret(void);
+extern void noteret(void);
+extern void returnto(void*);
+extern void fpsaveregs(void*);
+extern void fploadregs(void*);
+
+extern void setttbr(uintptr pa);
+extern uintptr getfar(void);
+
+extern void flushasidva(uintptr asidva);
+extern void tlbivae1is(uintptr asidva);
+
+extern void flushasidvall(uintptr asidva);
+extern void tlbivale1is(uintptr asidva);
+
+extern void flushasid(uintptr asid);
+extern void tlbiaside1is(uintptr asid);
+
+extern void flushtlb(void);
+extern void tlbivmalle1(void);
+
+extern void flushlocaltlb(void);
+extern void tlbivmalle1(void);
+
+/* cache */
+extern ulong cachesize(int level);
+
+extern void cacheiinvse(void*, int);
+extern void cacheuwbinv(void);
+extern void cacheiinv(void);
+
+extern void cachedwbse(void*, int);
+extern void cacheduwbse(void*, int);
+extern void cachedinvse(void*, int);
+extern void cachedwbinvse(void*, int);
+
+extern void cachedwb(void);
+extern void cachedinv(void);
+extern void cachedwbinv(void);
+
+extern void l2cacheuwb(void);
+extern void l2cacheuinv(void);
+extern void l2cacheuwbinv(void);
+
+/* mmu */
+#define getpgcolor(a) 0
+extern uintptr paddr(void*);
+#define PADDR(a) paddr((void*)(a))
+extern uintptr cankaddr(uintptr);
+extern void* kaddr(uintptr);
+#define KADDR(a) kaddr(a)
+extern void kmapinval(void);
+#define VA(k) ((uintptr)(k))
+extern KMap *kmap(Page*);
+extern void kunmap(KMap*);
+extern uintptr mmukmap(uintptr, uintptr, usize);
+extern void* vmap(uvlong, vlong);
+extern void vunmap(void*, vlong);
+
+extern void mmu0init(uintptr*);
+extern void mmu0clear(uintptr*);
+extern void mmuidmap(uintptr*);
+extern void mmu1init(void);
+extern void meminit(void);
+
+extern void putasid(Proc*);
+
+extern void* ucalloc(usize);
+
+/* clock */
+extern void clockinit(void);
+extern void synccycles(void);
+extern void armtimerset(int);
+extern void clockshutdown(void);
+
+/* fpu */
+extern void fpuinit(void);
+extern void fpoff(void);
+extern void fpinit(void);
+extern void fpclear(void);
+extern void fpsave(FPsave*);
+extern void fprestore(FPsave*);
+extern void mathtrap(Ureg*);
+
+/* trap */
+extern void trapinit(void);
+extern int userureg(Ureg*);
+extern void evenaddr(uintptr);
+extern void setkernur(Ureg*, Proc*);
+extern void procfork(Proc*);
+extern void procsetup(Proc*);
+extern void procsave(Proc*);
+extern void procrestore(Proc *);
+extern void trap(Ureg*);
+extern void syscall(Ureg*);
+extern void noted(Ureg*, ulong);
+extern void faultarm64(Ureg*);
+extern void dumpstack(void);
+extern void dumpregs(Ureg*);
+
+/* irq */
+extern void intrinit(void);
+extern void intrcpushutdown(void);
+extern void intrsoff(void);
+extern void intrenable(int, void (*)(Ureg*, void*), void*, int, char*);
+extern void intrdisable(int, void (*)(Ureg*, void*), void*, int, char*);
+extern int irq(Ureg*);
+extern void fiq(Ureg*);
+
+/* sysreg */
+extern uvlong sysrd(ulong);
+extern void syswr(ulong, uvlong);
+
+/* uartimx */
+extern void uartconsinit(void);
+
+/* dma */
+extern void dmaflush(int, void*, ulong);
+
+/* main */
+extern char *getconf(char *name);
+extern void setconfenv(void);
+extern void writeconf(void);
+
+extern int isaconfig(char*, int, ISAConf*);
+extern void links(void);
\ No newline at end of file
--- /dev/null
+++ b/sys/src/9/imx8/fpu.c
@@ -1,0 +1,92 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+#include "ureg.h"
+#include "sysreg.h"
+
+/* libc */
+extern ulong getfcr(void);
+extern void setfcr(ulong fcr);
+extern ulong getfsr(void);
+extern void setfsr(ulong fsr);
+
+void
+fpuinit(void)
+{
+ fpoff();
+}
+
+void
+fpon(void)
+{
+ syswr(CPACR_EL1, 3<<20);
+}
+
+void
+fpoff(void)
+{
+ syswr(CPACR_EL1, 0<<20);
+}
+
+void
+fpinit(void)
+{
+ fpon();
+ setfcr(0);
+ setfsr(0);
+}
+
+void
+fpclear(void)
+{
+ fpoff();
+}
+
+void
+fpsave(FPsave *p)
+{
+ p->control = getfcr();
+ p->status = getfsr();
+ fpsaveregs(p->regs);
+ fpoff();
+}
+
+void
+fprestore(FPsave *p)
+{
+ fpon();
+ setfcr(p->control);
+ setfsr(p->status);
+ fploadregs(p->regs);
+}
+
+void
+mathtrap(Ureg*)
+{
+ int s;
+
+ if((up->fpstate & FPillegal) != 0){
+ postnote(up, 1, "sys: floating point in note handler", NDebug);
+ return;
+ }
+ switch(up->fpstate){
+ case FPinit:
+ s = splhi();
+ fpinit();
+ up->fpstate = FPactive;
+ splx(s);
+ break;
+ case FPinactive:
+ s = splhi();
+ fprestore(up->fpsave);
+ up->fpstate = FPactive;
+ splx(s);
+ break;
+ case FPactive:
+ postnote(up, 1, "sys: floating point error", NDebug);
+ break;
+ }
+}
--- /dev/null
+++ b/sys/src/9/imx8/gic.c
@@ -1,0 +1,316 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/pci.h"
+#include "ureg.h"
+#include "sysreg.h"
+#include "../port/error.h"
+
+enum {
+ GICD_CTLR = 0x000/4, /* RW, Distributor Control Register */
+ GICD_TYPER = 0x004/4, /* RO, Interrupt Controller Type */
+ GICD_IIDR = 0x008/4, /* RO, Distributor Implementer Identification Register */
+
+ GICD_IGROUPR0 = 0x080/4, /* RW, Interrupt Group Registers (0x80-0xBC) */
+
+ GICD_ISENABLER0 = 0x100/4, /* RW, Interrupt Set-Enable Registers (0x100-0x13C) */
+ GICD_ICENABLER0 = 0x180/4, /* RW, Interrupt Clear-Enable Registers (0x180-0x1BC) */
+
+ GICD_ISPENDR0 = 0x200/4, /* RW, Interrupt Set-Pending Registers (0x200-0x23C) */
+ GICD_ICPENDR0 = 0x280/4, /* RW, Interrupt Clear-Pending Registers (0x280-0x2BC) */
+
+ GICD_ISACTIVER0 = 0x300/4, /* RW, Interrupt Set-Active Registers (0x300-0x33C) */
+ GICD_ICACTIVER0 = 0x380/4, /* RW, Interrupt Clear-Active Registers (0x380-0x3BC) */
+
+ GICD_IPRIORITYR0= 0x400/4, /* RW, Interrupt Priority Registers (0x400-0x5FC) */
+ GICD_TARGETSR0 = 0x800/4, /* RW, Interrupt Target Registers (0x800-0x9FC) */
+ GICD_ICFGR0 = 0xC00/4, /* RW, Interrupt Configuration Registers (0xC00-0xC7C) */
+
+ GICD_ISR0 = 0xD00/4,
+ GICD_PPISR = GICD_ISR0, /* RO, Private Peripheral Interrupt Status Register */
+ GICD_SPISR0 = GICD_ISR0+1, /* RO, Shared Peripheral Interrupt Status Register */
+ GICD_SGIR = 0xF00/4, /* WO, Software Generated Interrupt Register */
+
+ GICD_CPENDSGIR0 = 0xF10/4, /* RW, SGI Clear-Pending Registers (0xF10-0xF1C) */
+ GICD_SPENDSGIR0 = 0xF20/4, /* RW, SGI Set-Pending Registers (0xF20-0xF2C) */
+
+ GICD_PIDR4 = 0xFD0/4, /* RO, Perpheral ID Registers */
+ GICD_PIDR5 = 0xFD4/4,
+ GICD_PIDR6 = 0xFD8/4,
+ GICD_PIDR7 = 0xFDC/4,
+ GICD_PIDR0 = 0xFE0/4,
+ GICD_PIDR1 = 0xFE4/4,
+ GICD_PIDR2 = 0xFE8/4,
+ GICD_PIDR3 = 0xFEC/4,
+
+ GICD_CIDR0 = 0xFF0/4, /* RO, Component ID Registers */
+ GICD_CIDR1 = 0xFF4/4,
+ GICD_CIDR2 = 0xFF8/4,
+ GICD_CIDR3 = 0xFFC/4,
+
+ RD_base = 0x00000,
+ GICR_CTLR = (RD_base+0x000)/4,
+ GICR_IIDR = (RD_base+0x004)/4,
+ GICR_TYPER = (RD_base+0x008)/4,
+ GICR_STATUSR = (RD_base+0x010)/4,
+ GICR_WAKER = (RD_base+0x014)/4,
+ GICR_SETLPIR = (RD_base+0x040)/4,
+ GICR_CLRLPIR = (RD_base+0x048)/4,
+ GICR_PROPBASER = (RD_base+0x070)/4,
+ GICR_PENDBASER = (RD_base+0x078)/4,
+ GICR_INVLPIR = (RD_base+0x0A0)/4,
+ GICR_INVALLR = (RD_base+0x0B0)/4,
+ GICR_SYNCR = (RD_base+0x0C0)/4,
+
+ SGI_base = 0x10000,
+ GICR_IGROUPR0 = (SGI_base+0x080)/4,
+ GICR_ISENABLER0 = (SGI_base+0x100)/4,
+ GICR_ICENABLER0 = (SGI_base+0x180)/4,
+ GICR_ISPENDR0 = (SGI_base+0x200)/4,
+ GICR_ICPENDR0 = (SGI_base+0x280)/4,
+ GICR_ISACTIVER0 = (SGI_base+0x300)/4,
+ GICR_ICACTIVER0 = (SGI_base+0x380)/4,
+ GICR_IPRIORITYR0= (SGI_base+0x400)/4,
+ GICR_ICFGR0 = (SGI_base+0xC00)/4,
+ GICR_ICFGR1 = (SGI_base+0xC04)/4,
+ GICR_IGRPMODR0 = (SGI_base+0xD00)/4,
+ GICR_NSACR = (SGI_base+0xE00)/4,
+};
+
+typedef struct Vctl Vctl;
+struct Vctl {
+ Vctl *next;
+ void (*f)(Ureg*, void*);
+ void *a;
+ int irq;
+ u32int intid;
+};
+
+static Lock vctllock;
+static Vctl *vctl[MAXMACH][32], *vfiq;
+static u32int *dregs = (u32int*)(VIRTIO + 0x8800000);
+
+static u32int*
+getrregs(int machno)
+{
+ u32int *rregs = (u32int*)(VIRTIO + 0x8880000);
+
+ for(;;){
+ if((rregs[GICR_TYPER] & 0xFFFF00) == (machno << 8))
+ return rregs;
+ if(rregs[GICR_TYPER] & (1<<4))
+ break;
+ rregs += (0x20000/4);
+ }
+ panic("getrregs: no re-distributor for cpu %d\n", machno);
+ return nil;
+}
+
+void
+intrcpushutdown(void)
+{
+ /* disable cpu interface */
+ syswr(ICC_IGRPEN0_EL1, 0);
+ syswr(ICC_IGRPEN1_EL1, 0);
+ coherence();
+}
+
+void
+intrsoff(void)
+{
+ /* disable distributor */
+ dregs[GICD_CTLR] = 0;
+ coherence();
+ while(dregs[GICD_CTLR]&(1<<31))
+ ;
+}
+
+void
+intrinit(void)
+{
+ u32int *rregs;
+ int i, n;
+
+ if(m->machno == 0){
+ intrsoff();
+
+ /* clear all interrupts */
+ n = ((dregs[GICD_TYPER] & 0x1F)+1) << 5;
+print("nirq %d\n", n);
+ for(i = 32; i < n; i += 32){
+ dregs[GICD_IGROUPR0 + (i/32)] = -1;
+
+ dregs[GICD_ISENABLER0 + (i/32)] = -1;
+ while(dregs[GICD_CTLR]&(1<<31))
+ ;
+print("%d: distributor stuck disabled: %.8ux\n", i, ~dregs[GICD_ISENABLER0 + (i/32)]);
+ dregs[GICD_ICENABLER0 + (i/32)] = -1;
+ while(dregs[GICD_CTLR]&(1<<31))
+ ;
+print("%d: distributor stuck enabled: %.8ux\n", i, dregs[GICD_ISENABLER0 + (i/32)]);
+ dregs[GICD_ICACTIVER0 + (i/32)] = -1;
+ }
+ for(i = 0; i < n; i += 4){
+ dregs[GICD_IPRIORITYR0 + (i/4)] = 0;
+ dregs[GICD_TARGETSR0 + (i/4)] = 0;
+ }
+ for(i = 32; i < n; i += 16){
+ dregs[GICD_ICFGR0 + (i/16)] = 0;
+ }
+ coherence();
+ while(dregs[GICD_CTLR]&(1<<31))
+ ;
+ dregs[GICD_CTLR] = (1<<0) | (1<<1) | (1<<4);
+ }
+
+ rregs = getrregs(m->machno);
+ n = 32;
+ for(i = 0; i < n; i += 32){
+ rregs[GICR_IGROUPR0 + (i/32)] = -1;
+
+ rregs[GICR_ISENABLER0 + (i/32)] = -1;
+ while(rregs[GICR_CTLR]&(1<<3))
+ ;
+print("%d: re-distributor stuck disabled: %.8ux\n", i, ~rregs[GICR_ISENABLER0 + (i/32)]);
+ rregs[GICR_ICENABLER0 + (i/32)] = -1;
+ while(dregs[GICD_CTLR]&(1<<31))
+ ;
+print("%d: re-distributor stuck enabled: %.8ux\n", i, rregs[GICR_ISENABLER0 + (i/32)]);
+ rregs[GICR_ICACTIVER0 + (i/32)] = -1;
+ }
+ for(i = 0; i < n; i += 4){
+ rregs[GICR_IPRIORITYR0 + (i/4)] = 0;
+ }
+ coherence();
+ while(rregs[GICR_CTLR]&(1<<3))
+ ;
+
+ coherence();
+
+ /* enable cpu interface */
+ syswr(ICC_CTLR_EL1, 0);
+ syswr(ICC_BPR1_EL1, 7);
+ syswr(ICC_PMR_EL1, 0xFF);
+
+ coherence();
+}
+
+
+/*
+ * called by trap to handle irq interrupts.
+ * returns true iff a clock interrupt, thus maybe reschedule.
+ */
+int
+irq(Ureg* ureg)
+{
+ Vctl *v;
+ int clockintr;
+ u32int intid;
+
+ m->intr++;
+ intid = sysrd(ICC_IAR1_EL1) & 0xFFFFFF;
+// iprint("i<%d>", intid);
+ if((intid & ~3) == 1020)
+ return 0; // spurious
+ clockintr = 0;
+ for(v = vctl[m->machno][intid%32]; v != nil; v = v->next)
+ if(v->intid == intid){
+ coherence();
+ v->f(ureg, v->a);
+ coherence();
+ if(v->irq == IRQcntpns)
+ clockintr = 1;
+ }
+ coherence();
+ syswr(ICC_EOIR1_EL1, intid);
+ return clockintr;
+}
+
+/*
+ * called direct from lexception.s to handle fiq interrupt.
+ */
+void
+fiq(Ureg *ureg)
+{
+ Vctl *v;
+ u32int intid;
+
+ m->intr++;
+ intid = sysrd(ICC_IAR1_EL1) & 0xFFFFFF;
+// iprint("f<%d>", intid);
+ if((intid & ~3) == 1020)
+ return; // spurious
+ v = vfiq;
+ if(v != nil && v->intid == intid && m->machno == 0){
+ coherence();
+ v->f(ureg, v->a);
+ coherence();
+ }
+ syswr(ICC_EOIR1_EL1, intid);
+}
+
+void
+intrenable(int irq, void (*f)(Ureg*, void*), void *a, int tbdf, char *)
+{
+ Vctl *v;
+ u32int intid;
+ int cpu, prio;
+
+ if(tbdf != BUSUNKNOWN)
+ return;
+
+ prio = 0x80;
+ intid = irq;
+ if((v = xalloc(sizeof(Vctl))) == nil)
+ panic("irqenable: no mem");
+ v->irq = irq;
+ v->intid = intid;
+ v->f = f;
+ v->a = a;
+
+ lock(&vctllock);
+ if(intid < SPI)
+ cpu = m->machno;
+ else
+ cpu = 0;
+ if(irq == IRQfiq){
+ vfiq = v;
+ prio = 0;
+ }else{
+ v->next = vctl[cpu][intid%32];
+ vctl[cpu][intid%32] = v;
+ }
+ syswr(ICC_IGRPEN1_EL1, sysrd(ICC_IGRPEN1_EL1)|1);
+ coherence();
+
+ syswr(ICC_EOIR1_EL1, intid);
+ coherence();
+
+ /* setup */
+ if(intid < 32){
+ u32int *rregs = getrregs(cpu);
+ rregs[GICR_IPRIORITYR0 + (intid/4)] |= prio << ((intid%4) << 3);
+ coherence();
+ rregs[GICR_ISENABLER0] = 1 << (intid%32);
+ coherence();
+ while(rregs[GICR_CTLR]&(1<<3))
+ ;
+ } else {
+ dregs[GICD_IPRIORITYR0 + (intid/4)] |= prio << ((intid%4) << 3);
+ dregs[GICD_TARGETSR0 + (intid/4)] |= (1<<cpu) << ((intid%4) << 3);
+ coherence();
+ dregs[GICD_ISENABLER0 + (intid/32)] = 1 << (intid%32);
+ coherence();
+ while(dregs[GICD_CTLR]&(1<<31))
+ ;
+ }
+ unlock(&vctllock);
+}
+
+void
+intrdisable(int, void (*)(Ureg*, void*), void *, int, char*)
+{
+}
--- /dev/null
+++ b/sys/src/9/imx8/init9.s
@@ -1,0 +1,4 @@
+TEXT main(SB), 1, $8
+ MOV $setSB(SB), R28 /* load the SB */
+ MOV $boot(SB), R0
+ B startboot(SB)
--- /dev/null
+++ b/sys/src/9/imx8/io.h
@@ -1,0 +1,31 @@
+enum {
+ IRQfiq = -1,
+
+ PPI = 16,
+ SPI = 32,
+
+ IRQcntps = PPI+13,
+ IRQcntpns = PPI+14,
+
+ IRQuart1 = SPI+26,
+ IRQuart2 = SPI+27,
+ IRQuart3 = SPI+28,
+ IRQuart4 = SPI+29,
+
+ IRQi2c1 = SPI+35,
+ IRQi2c2 = SPI+36,
+ IRQi2c3 = SPI+37,
+ IRQi2c4 = SPI+38,
+
+ IRQrdc = SPI+39,
+
+ IRQusb1 = SPI+40,
+ IRQusb2 = SPI+41,
+
+ IRQsctr0 = SPI+47,
+ IRQsctr1 = SPI+48,
+
+ IRQenet1 = SPI+118,
+};
+
+#define BUSUNKNOWN (-1)
--- /dev/null
+++ b/sys/src/9/imx8/l.s
@@ -1,0 +1,681 @@
+#include "mem.h"
+#include "sysreg.h"
+
+#undef SYSREG
+#define SYSREG(op0,op1,Cn,Cm,op2) SPR(((op0)<<19|(op1)<<16|(Cn)<<12|(Cm)<<8|(op2)<<5))
+
+TEXT _start(SB), 1, $-4
+ MOV R0, R26 /* save */
+
+ MOV $setSB-KZERO(SB), R28
+ BL svcmode<>(SB)
+
+ /* use dedicated stack pointer per exception level */
+ MOVWU $1, R1
+ MSR R1, SPSel
+
+ BL mmudisable<>(SB)
+
+ /* invalidate local caches */
+ BL cachedwbinv(SB)
+ BL cacheiinv(SB)
+
+ MOV $(MACHADDR(0)-KZERO), R27
+ MRS MPIDR_EL1, R1
+ ANDW $(MAXMACH-1), R1
+ MOVWU $MACHSIZE, R2
+ MULW R1, R2, R2
+ SUB R2, R27
+
+ ADD $(MACHSIZE-16), R27, R2
+ MOV R2, SP
+
+ CBNZ R1, _startup
+
+ /* clear page table and machs */
+ MOV $(L1-KZERO), R1
+ MOV $(MACHADDR(-1)-KZERO), R2
+_zerol1:
+ MOV ZR, (R1)8!
+ CMP R1, R2
+ BNE _zerol1
+
+ /* clear BSS */
+ MOV $edata-KZERO(SB), R1
+ MOV $end-KZERO(SB), R2
+_zerobss:
+ MOV ZR, (R1)8!
+ CMP R1, R2
+ BNE _zerobss
+
+ /* setup page tables */
+ MOV $(L1-KZERO), R0
+ BL mmu0init(SB)
+
+ SEVL
+_startup:
+ WFE
+ BL mmuenable<>(SB)
+
+ MOV R26, R0
+ MOV $0, R26
+ ORR $KZERO, R27
+ MSR R27, TPIDR_EL1
+ MOV $setSB(SB), R28
+
+ BL main(SB)
+
+TEXT stop<>(SB), 1, $-4
+_stop:
+ WFE
+ B _stop
+
+TEXT aaa<>(SB), 1, $-4
+xxx:
+ MOV $(0x860040+VIRTIO), R1
+ MOVW $'A', R2
+ MOVW R2, (R1)
+ B xxx
+
+TEXT sev(SB), 1, $-4
+ SEV
+ WFE
+ RETURN
+
+TEXT svcmode<>(SB), 1, $-4
+ MSR $0xF, DAIFSet
+ MRS CurrentEL, R0
+ ANDW $(3<<2), R0
+ CMPW $(1<<2), R0
+ BEQ el1
+ CMPW $(2<<2), R0
+ BEQ el2
+ B stop<>(SB)
+el2:
+ MOV $0, R0
+ MSR R0, MDCR_EL2
+ ISB $SY
+
+ /* HCR = RW, HCD, SWIO, BSU, FB */
+ MOVWU $(1<<31 | 1<<29 | 1<<2 | 0<<10 | 0<<9), R0
+ MSR R0, HCR_EL2
+ ISB $SY
+
+ /* SCTLR = RES1 */
+ MOVWU $(3<<4 | 1<<11 | 1<<16 | 1<<18 | 3<<22 | 3<<28), R0
+ ISB $SY
+ MSR R0, SCTLR_EL2
+ ISB $SY
+
+ /* set VMID to zero */
+ MOV $0, R0
+ MSR R0, VTTBR_EL2
+ ISB $SY
+
+ MOVWU $(0xF<<6 | 4), R0
+ MSR R0, SPSR_EL2
+ MSR LR, ELR_EL2
+ ERET
+el1:
+ RETURN
+
+TEXT mmudisable<>(SB), 1, $-4
+#define SCTLRCLR \
+ /* RES0 */ ( 3<<30 \
+ /* RES0 */ | 1<<27 \
+ /* UCI */ | 1<<26 \
+ /* EE */ | 1<<25 \
+ /* RES0 */ | 1<<21 \
+ /* E0E */ | 1<<24 \
+ /* WXN */ | 1<<19 \
+ /* nTWE */ | 1<<18 \
+ /* RES0 */ | 1<<17 \
+ /* nTWI */ | 1<<16 \
+ /* UCT */ | 1<<15 \
+ /* DZE */ | 1<<14 \
+ /* RES0 */ | 1<<13 \
+ /* RES0 */ | 1<<10 \
+ /* UMA */ | 1<<9 \
+ /* SA0 */ | 1<<4 \
+ /* SA */ | 1<<3 \
+ /* A */ | 1<<1 )
+#define SCTLRSET \
+ /* RES1 */ ( 3<<28 \
+ /* RES1 */ | 3<<22 \
+ /* RES1 */ | 1<<20 \
+ /* RES1 */ | 1<<11 )
+#define SCTLRMMU \
+ /* I */ ( 1<<12 \
+ /* C */ | 1<<2 \
+ /* M */ | 1<<0 )
+
+ /* initialise SCTLR, MMU and caches off */
+ ISB $SY
+ MRS SCTLR_EL1, R0
+ BIC $(SCTLRCLR | SCTLRMMU), R0
+ ORR $SCTLRSET, R0
+ ISB $SY
+ MSR R0, SCTLR_EL1
+ ISB $SY
+
+ B flushlocaltlb(SB)
+
+TEXT mmuenable<>(SB), 1, $-4
+ /* return to virtual */
+ ORR $KZERO, LR
+ MOV LR, -16(RSP)!
+
+ BL flushlocaltlb(SB)
+
+ /* memory attributes */
+#define MAIRINIT \
+ ( 0xFF << MA_MEM_WB*8 \
+ | 0x33 << MA_MEM_WT*8 \
+ | 0x44 << MA_MEM_UC*8 \
+ | 0x00 << MA_DEV_nGnRnE*8 \
+ | 0x04 << MA_DEV_nGnRE*8 \
+ | 0x08 << MA_DEV_nGRE*8 \
+ | 0x0C << MA_DEV_GRE*8 )
+ MOV $MAIRINIT, R1
+ MSR R1, MAIR_EL1
+ ISB $SY
+
+ /* translation control */
+#define TCRINIT \
+ /* TBI1 */ ( 0<<38 \
+ /* TBI0 */ | 0<<37 \
+ /* AS */ | 0<<36 \
+ /* TG1 */ | (((3<<16|1<<14|2<<12)>>PGSHIFT)&3)<<30 \
+ /* SH1 */ | SHARE_INNER<<28 \
+ /* ORGN1 */ | CACHE_WB<<26 \
+ /* IRGN1 */ | CACHE_WB<<24 \
+ /* EPD1 */ | 0<<23 \
+ /* A1 */ | 0<<22 \
+ /* T1SZ */ | (64-EVASHIFT)<<16 \
+ /* TG0 */ | (((1<<16|2<<14|0<<12)>>PGSHIFT)&3)<<14 \
+ /* SH0 */ | SHARE_INNER<<12 \
+ /* ORGN0 */ | CACHE_WB<<10 \
+ /* IRGN0 */ | CACHE_WB<<8 \
+ /* EPD0 */ | 0<<7 \
+ /* T0SZ */ | (64-EVASHIFT)<<0 )
+ MOV $TCRINIT, R1
+ MRS ID_AA64MMFR0_EL1, R2
+ ANDW $0x7, R2 // PARange
+ ADD R2<<32, R1 // IPS
+ MSR R1, TCR_EL1
+ ISB $SY
+
+ /* load the page tables */
+ MOV $(L1TOP-KZERO), R0
+ ISB $SY
+ MSR R0, TTBR0_EL1
+ MSR R0, TTBR1_EL1
+ ISB $SY
+
+ /* enable MMU and caches */
+ MRS SCTLR_EL1, R1
+ ORR $SCTLRMMU, R1
+ ISB $SY
+ MSR R1, SCTLR_EL1
+ ISB $SY
+
+ MOV RSP, R1
+ ORR $KZERO, R1
+ MOV R1, RSP
+ MOV (RSP)16!, LR
+ B cacheiinv(SB)
+
+TEXT touser(SB), 1, $-4
+ MSR $0x3, DAIFSet // interrupts off
+ MOVWU $0x10028, R1 // entry
+ MOVWU $0, R2 // psr
+ MSR R0, SP_EL0 // sp
+ MSR R1, ELR_EL1
+ MSR R2, SPSR_EL1
+ ERET
+
+TEXT cas(SB), 1, $-4
+TEXT cmpswap(SB), 1, $-4
+ MOVWU ov+8(FP), R1
+ MOVWU nv+16(FP), R2
+_cas1:
+ LDXRW (R0), R3
+ CMP R3, R1
+ BNE _cas0
+ STXRW R2, (R0), R4
+ CBNZ R4, _cas1
+ MOVW $1, R0
+ DMB $ISH
+ RETURN
+_cas0:
+ CLREX
+ MOVW $0, R0
+ RETURN
+
+TEXT tas(SB), 1, $-4
+TEXT _tas(SB), 1, $-4
+ MOVW $0xdeaddead, R2
+_tas1:
+ LDXRW (R0), R1
+ STXRW R2, (R0), R3
+ CBNZ R3, _tas1
+ MOVW R1, R0
+
+TEXT coherence(SB), 1, $-4
+ DMB $ISH
+ RETURN
+
+TEXT islo(SB), 1, $-4
+ MRS DAIF, R0
+ AND $(0x2<<6), R0
+ EOR $(0x2<<6), R0
+ RETURN
+
+TEXT splhi(SB), 1, $-4
+ MRS DAIF, R0
+ MSR $0x2, DAIFSet
+ RETURN
+
+TEXT splfhi(SB), 1, $-4
+ MRS DAIF, R0
+ MSR $0x3, DAIFSet
+ RETURN
+
+TEXT spllo(SB), 1, $-4
+ MSR $0x3, DAIFClr
+ RETURN
+
+TEXT splflo(SB), 1, $-4
+ MSR $0x1, DAIFClr
+ RETURN
+
+TEXT splx(SB), 1, $-4
+ MSR R0, DAIF
+ RETURN
+
+TEXT idlehands(SB), 1, $-4
+ DMB $ISH
+ MOV $nrdy(SB), R1
+ LDXRW (R1), R0
+ CBZ R0, _goodnight
+ CLREX
+ SEVL
+_goodnight:
+ WFE
+ RETURN
+
+TEXT vcycles(SB), 1, $-4
+ MRS CNTVCT_EL0, R0
+ RETURN
+
+TEXT lcycles(SB), 1, $-4
+ MRS PMCCNTR_EL0, R0
+ RETURN
+
+TEXT setlabel(SB), 1, $-4
+ MOV LR, 8(R0)
+ MOV SP, R1
+ MOV R1, 0(R0)
+ MOVW $0, R0
+ RETURN
+
+TEXT gotolabel(SB), 1, $-4
+ MOV 8(R0), LR /* link */
+ MOV 0(R0), R1 /* sp */
+ MOV R1, SP
+ MOVW $1, R0
+ RETURN
+
+TEXT returnto(SB), 1, $-4
+ MOV R0, 0(SP)
+ RETURN
+
+TEXT getfar(SB), 1, $-4
+ MRS FAR_EL1, R0
+ RETURN
+
+TEXT setttbr(SB), 1, $-4
+ DSB $ISHST
+ MSR R0, TTBR0_EL1
+ DSB $ISH
+ ISB $SY
+ RETURN
+
+/*
+ * TLB maintenance operations.
+ * these broadcast to all cpu's in the cluser
+ * (inner sharable domain).
+ */
+TEXT flushasidva(SB), 1, $-4
+TEXT tlbivae1is(SB), 1, $-4
+ DSB $ISHST
+ TLBI R0, 0,8,3,1 /* VAE1IS */
+ DSB $ISH
+ ISB $SY
+ RETURN
+
+TEXT flushasidvall(SB), 1, $-4
+TEXT tlbivale1is(SB), 1, $-4
+ DSB $ISHST
+ TLBI R0, 0,8,3,5 /* VALE1IS */
+ DSB $ISH
+ ISB $SY
+ RETURN
+
+TEXT flushasid(SB), 1, $-4
+TEXT tlbiaside1is(SB), 1, $-4
+ DSB $ISHST
+ TLBI R0, 0,8,3,2 /* ASIDE1IS */
+ DSB $ISH
+ ISB $SY
+ RETURN
+
+TEXT flushtlb(SB), 1, $-4
+TEXT tlbivmalle1is(SB), 1, $-4
+ DSB $ISHST
+ TLBI R0, 0,8,3,0 /* VMALLE1IS */
+ DSB $ISH
+ ISB $SY
+ RETURN
+
+/*
+ * flush the tlb of this cpu. no broadcast.
+ */
+TEXT flushlocaltlb(SB), 1, $-4
+TEXT tlbivmalle1(SB), 1, $-4
+ DSB $NSHST
+ TLBI R0, 0,8,7,0 /* VMALLE1 */
+ DSB $NSH
+ ISB $SY
+ RETURN
+
+TEXT fpsaveregs(SB), 1, $-4
+ WORD $(1<<30 | 3 << 26 | 2<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 0) /* MOV { V0, V1, V2, V3 }, (R0)64! */
+ WORD $(1<<30 | 3 << 26 | 2<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 4) /* MOV { V4, V5, V6, V7 }, (R0)64! */
+ WORD $(1<<30 | 3 << 26 | 2<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 8) /* MOV { V8, V9, V10,V11 }, (R0)64! */
+ WORD $(1<<30 | 3 << 26 | 2<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 12) /* MOV { V12,V13,V14,V15 }, (R0)64! */
+ WORD $(1<<30 | 3 << 26 | 2<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 16) /* MOV { V16,V17,V18,V19 }, (R0)64! */
+ WORD $(1<<30 | 3 << 26 | 2<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 20) /* MOV { V20,V21,V22,V23 }, (R0)64! */
+ WORD $(1<<30 | 3 << 26 | 2<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 24) /* MOV { V24,V25,V26,V27 }, (R0)64! */
+ WORD $(1<<30 | 3 << 26 | 2<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 28) /* MOV { V28,V29,V30,V31 }, (R0)64! */
+ RETURN
+
+TEXT fploadregs(SB), 1, $-4
+ WORD $(1<<30 | 3 << 26 | 3<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 0) /* MOV (R0)64!, { V0, V1, V2, V3 } */
+ WORD $(1<<30 | 3 << 26 | 3<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 4) /* MOV (R0)64!, { V4, V5, V6, V7 } */
+ WORD $(1<<30 | 3 << 26 | 3<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 8) /* MOV (R0)64!, { V8, V9, V10,V11 } */
+ WORD $(1<<30 | 3 << 26 | 3<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 12) /* MOV (R0)64!, { V12,V13,V14,V15 } */
+ WORD $(1<<30 | 3 << 26 | 3<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 16) /* MOV (R0)64!, { V16,V17,V18,V19 } */
+ WORD $(1<<30 | 3 << 26 | 3<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 20) /* MOV (R0)64!, { V20,V21,V22,V23 } */
+ WORD $(1<<30 | 3 << 26 | 3<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 24) /* MOV (R0)64!, { V24,V25,V26,V27 } */
+ WORD $(1<<30 | 3 << 26 | 3<<22 | 0x1F<<16 | 3<<10 | 0<<5 | 28) /* MOV (R0)64!, { V28,V29,V30,V31 } */
+ RETURN
+
+// syscall or trap from EL0
+TEXT vsys0(SB), 1, $-4
+ LSRW $26, R0, R17 // ec
+ CMPW $0x15, R17 // SVC trap?
+ BNE _itsatrap // nope.
+
+ MOVP R26, R27, 224(RSP)
+ MOVP R28, R29, 240(RSP)
+
+ MRS SP_EL0, R1
+ MRS ELR_EL1, R2
+ MRS SPSR_EL1, R3
+
+ MOV R0, 288(RSP) // type
+ MOV R1, 264(RSP) // sp
+ MOV R2, 272(RSP) // pc
+ MOV R3, 280(RSP) // psr
+
+ MOV $setSB(SB), R28
+ MRS TPIDR_EL1, R27
+ MOV 16(R27), R26
+
+ ADD $16, RSP, R0 // ureg
+ BL syscall(SB)
+
+TEXT forkret(SB), 1, $-4
+ MSR $0x3, DAIFSet // interrupts off
+
+ ADD $16, RSP, R0 // ureg
+
+ MOV 16(RSP), R0 // ret
+ MOV 264(RSP), R1 // sp
+ MOV 272(RSP), R2 // pc
+ MOV 280(RSP), R3 // psr
+
+ MSR R1, SP_EL0
+ MSR R2, ELR_EL1
+ MSR R3, SPSR_EL1
+
+ MOVP 224(RSP), R26, R27
+ MOVP 240(RSP), R28, R29
+
+ MOV 256(RSP), R30 // link
+
+ ADD $TRAPFRAMESIZE, RSP
+ ERET
+
+TEXT itsatrap<>(SB), 1, $-4
+_itsatrap:
+ MOVP R1, R2, 24(RSP)
+ MOVP R3, R4, 40(RSP)
+ MOVP R5, R6, 56(RSP)
+ MOVP R7, R8, 72(RSP)
+ MOVP R9, R10, 88(RSP)
+ MOVP R11, R12, 104(RSP)
+ MOVP R13, R14, 120(RSP)
+ MOVP R15, R16, 136(RSP)
+
+ MOVP R18, R19, 160(RSP)
+ MOVP R20, R21, 176(RSP)
+ MOVP R22, R23, 192(RSP)
+ MOVP R24, R25, 208(RSP)
+
+// trap/irq/fiq/serr from EL0
+TEXT vtrap0(SB), 1, $-4
+ MOVP R26, R27, 224(RSP)
+ MOVP R28, R29, 240(RSP)
+
+ MRS SP_EL0, R1
+ MRS ELR_EL1, R2
+ MRS SPSR_EL1, R3
+
+ MOV R0, 288(RSP) // type
+ MOV R1, 264(RSP) // sp
+ MOV R2, 272(RSP) // pc
+ MOV R3, 280(RSP) // psr
+
+ MOV $setSB(SB), R28
+ MRS TPIDR_EL1, R27
+ MOV 16(R27), R26
+
+ ADD $16, RSP, R0 // ureg
+ BL trap(SB)
+
+TEXT noteret(SB), 1, $-4
+ MSR $0x3, DAIFSet // interrupts off
+
+ ADD $16, RSP, R0 // ureg
+
+ MOV 264(RSP), R1 // sp
+ MOV 272(RSP), R2 // pc
+ MOV 280(RSP), R3 // psr
+
+ MSR R1, SP_EL0
+ MSR R2, ELR_EL1
+ MSR R3, SPSR_EL1
+
+ MOVP 224(RSP), R26, R27
+ MOVP 240(RSP), R28, R29
+
+_intrreturn:
+ MOVP 16(RSP), R0, R1
+ MOVP 32(RSP), R2, R3
+ MOVP 48(RSP), R4, R5
+ MOVP 64(RSP), R6, R7
+ MOVP 80(RSP), R8, R9
+ MOVP 96(RSP), R10, R11
+ MOVP 112(RSP), R12, R13
+ MOVP 128(RSP), R14, R15
+ MOVP 144(RSP), R16, R17
+ MOVP 160(RSP), R18, R19
+ MOVP 176(RSP), R20, R21
+ MOVP 192(RSP), R22, R23
+ MOVP 208(RSP), R24, R25
+
+ MOV 256(RSP), R30 // link
+
+ ADD $TRAPFRAMESIZE, RSP
+ ERET
+
+// irq/fiq/trap/serr from EL1
+TEXT vtrap1(SB), 1, $-4
+ MOV R29, 248(RSP) // special
+
+ ADD $TRAPFRAMESIZE, RSP, R1
+ MRS ELR_EL1, R2
+ MRS SPSR_EL1, R3
+
+ MOV R0, 288(RSP) // type
+ MOV R1, 264(RSP) // sp
+ MOV R2, 272(RSP) // pc
+ MOV R3, 280(RSP) // psr
+
+ ADD $16, RSP, R0 // ureg
+ BL trap(SB)
+
+ MSR $0x3, DAIFSet // interrupts off
+
+ MOV 272(RSP), R2 // pc
+ MOV 280(RSP), R3 // psr
+
+ MSR R2, ELR_EL1
+ MSR R3, SPSR_EL1
+
+ MOV 248(RSP), R29 // special
+ B _intrreturn
+
+// vector tables
+TEXT vsys(SB), 1, $-4
+ SUB $TRAPFRAMESIZE, RSP
+
+ MOV R0, 16(RSP)
+ MOV R30, 256(RSP) // link
+
+ MOV R17, 152(RSP) // temp
+
+ MRS ESR_EL1, R0 // type
+
+_vsyspatch:
+ B _vsyspatch // branch to vsys0() patched in
+
+TEXT vtrap(SB), 1, $-4
+ SUB $TRAPFRAMESIZE, RSP
+
+ MOVP R0, R1, 16(RSP)
+ MOVP R2, R3, 32(RSP)
+ MOVP R4, R5, 48(RSP)
+ MOVP R6, R7, 64(RSP)
+ MOVP R8, R9, 80(RSP)
+ MOVP R10, R11, 96(RSP)
+ MOVP R12, R13, 112(RSP)
+ MOVP R14, R15, 128(RSP)
+ MOVP R16, R17, 144(RSP)
+ MOVP R18, R19, 160(RSP)
+ MOVP R20, R21, 176(RSP)
+ MOVP R22, R23, 192(RSP)
+ MOVP R24, R25, 208(RSP)
+
+ MOV R30, 256(RSP) // link
+
+ MRS ESR_EL1, R0 // type
+
+_vtrappatch:
+ B _vtrappatch // branch to vtrapX() patched in
+
+TEXT virq(SB), 1, $-4
+ SUB $TRAPFRAMESIZE, RSP
+
+ MOVP R0, R1, 16(RSP)
+ MOVP R2, R3, 32(RSP)
+ MOVP R4, R5, 48(RSP)
+ MOVP R6, R7, 64(RSP)
+ MOVP R8, R9, 80(RSP)
+ MOVP R10, R11, 96(RSP)
+ MOVP R12, R13, 112(RSP)
+ MOVP R14, R15, 128(RSP)
+ MOVP R16, R17, 144(RSP)
+ MOVP R18, R19, 160(RSP)
+ MOVP R20, R21, 176(RSP)
+ MOVP R22, R23, 192(RSP)
+ MOVP R24, R25, 208(RSP)
+
+ MOV R30, 256(RSP) // link
+
+ MOV $(1<<32), R0 // type irq
+
+_virqpatch:
+ B _virqpatch // branch to vtrapX() patched in
+
+TEXT vfiq(SB), 1, $-4
+ SUB $TRAPFRAMESIZE, RSP
+
+ MOVP R0, R1, 16(RSP)
+ MOVP R2, R3, 32(RSP)
+ MOVP R4, R5, 48(RSP)
+ MOVP R6, R7, 64(RSP)
+ MOVP R8, R9, 80(RSP)
+ MOVP R10, R11, 96(RSP)
+ MOVP R12, R13, 112(RSP)
+ MOVP R14, R15, 128(RSP)
+ MOVP R16, R17, 144(RSP)
+ MOVP R18, R19, 160(RSP)
+ MOVP R20, R21, 176(RSP)
+ MOVP R22, R23, 192(RSP)
+ MOVP R24, R25, 208(RSP)
+
+ MOV R30, 256(RSP) // link
+ MOV $(2<<32), R0 // type fiq
+
+_vfiqpatch:
+ B _vfiqpatch // branch to vtrapX() patched in
+
+TEXT vserr(SB), 1, $-4
+ SUB $TRAPFRAMESIZE, RSP
+
+ MOVP R0, R1, 16(RSP)
+ MOVP R2, R3, 32(RSP)
+ MOVP R4, R5, 48(RSP)
+ MOVP R6, R7, 64(RSP)
+ MOVP R8, R9, 80(RSP)
+ MOVP R10, R11, 96(RSP)
+ MOVP R12, R13, 112(RSP)
+ MOVP R14, R15, 128(RSP)
+ MOVP R16, R17, 144(RSP)
+ MOVP R18, R19, 160(RSP)
+ MOVP R20, R21, 176(RSP)
+ MOVP R22, R23, 192(RSP)
+ MOVP R24, R25, 208(RSP)
+
+ MOV R30, 256(RSP) // link
+
+ MRS ESR_EL1, R0
+ ORR $(3<<32), R0 // type
+_vserrpatch:
+ B _vserrpatch // branch to vtrapX() patched in
+
+/* fault-proof memcpy */
+TEXT peek(SB), 1, $-4
+ MOV R0, R1
+ MOV dst+8(FP), R2
+ MOVWU len+16(FP), R0
+TEXT _peekinst(SB), 1, $-4
+_peekloop:
+ MOVBU (R1)1!, R3
+ MOVBU R3, (R2)1!
+ SUBS $1, R0
+ BNE _peekloop
+ RETURN
--- /dev/null
+++ b/sys/src/9/imx8/main.c
@@ -1,0 +1,218 @@
+#include "u.h"
+#include "tos.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "pool.h"
+#include "io.h"
+#include "sysreg.h"
+
+Conf conf;
+
+/*
+ * starting place for first process
+ */
+void
+init0(void)
+{
+ char **sp;
+
+ chandevinit();
+
+ if(!waserror()){
+ ksetenv("cputype", "arm64", 0);
+ if(cpuserver)
+ ksetenv("service", "cpu", 0);
+ else
+ ksetenv("service", "terminal", 0);
+ ksetenv("console", "0", 0);
+ poperror();
+ }
+ kproc("alarm", alarmkproc, 0);
+ sp = (char**)(USTKTOP-sizeof(Tos) - 8 - sizeof(sp[0])*4);
+ sp[3] = sp[2] = sp[1] = nil;
+ strcpy(sp[1] = (char*)&sp[4], "boot");
+ sp[0] = (void*)&sp[1];
+ touser((uintptr)sp);
+}
+
+void
+confinit(void)
+{
+ int userpcnt;
+ ulong kpages;
+ char *p;
+ int i;
+
+ conf.nmach = 1;
+
+ if(p = getconf("service")){
+ if(strcmp(p, "cpu") == 0)
+ cpuserver = 1;
+ else if(strcmp(p,"terminal") == 0)
+ cpuserver = 0;
+ }
+
+ if(p = getconf("*kernelpercent"))
+ userpcnt = 100 - strtol(p, 0, 0);
+ else
+ userpcnt = 0;
+
+ if(userpcnt < 10)
+ userpcnt = 60 + cpuserver*10;
+
+ conf.npage = 0;
+ for(i = 0; i < nelem(conf.mem); i++)
+ conf.npage += conf.mem[i].npage;
+
+ kpages = conf.npage - (conf.npage*userpcnt)/100;
+ if(kpages > ((uintptr)-VDRAM)/BY2PG)
+ kpages = ((uintptr)-VDRAM)/BY2PG;
+
+ conf.upages = conf.npage - kpages;
+ conf.ialloc = (kpages/2)*BY2PG;
+
+ /* set up other configuration parameters */
+ conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5;
+ if(cpuserver)
+ conf.nproc *= 3;
+ if(conf.nproc > 2000)
+ conf.nproc = 2000;
+ conf.nswap = conf.npage*3;
+ conf.nswppo = 4096;
+ conf.nimage = 200;
+
+ conf.copymode = conf.nmach > 1;
+
+ /*
+ * Guess how much is taken by the large permanent
+ * datastructures. Mntcache and Mntrpc are not accounted for.
+ */
+ kpages = conf.npage - conf.upages;
+ kpages *= BY2PG;
+ kpages -= conf.upages*sizeof(Page)
+ + conf.nproc*sizeof(Proc)
+ + conf.nimage*sizeof(Image)
+ + conf.nswap
+ + conf.nswppo*sizeof(Page*);
+ mainmem->maxsize = kpages;
+ if(!cpuserver)
+ /*
+ * give terminals lots of image memory, too; the dynamic
+ * allocation will balance the load properly, hopefully.
+ * be careful with 32-bit overflow.
+ */
+ imagmem->maxsize = kpages;
+
+}
+
+void
+machinit(void)
+{
+ m->ticks = 1;
+ m->perf.period = 1;
+ active.machs[m->machno] = 1;
+}
+
+void
+main(void)
+{
+ machinit();
+ if(m->machno){
+ trapinit();
+ fpuinit();
+ intrinit();
+ clockinit();
+ // cpuidprint();
+ synccycles();
+ timersinit();
+ flushtlb();
+ mmu1init();
+ m->ticks = MACHP(0)->ticks;
+ schedinit();
+ return;
+ }
+ quotefmtinstall();
+ meminit();
+ confinit();
+ xinit();
+ uartconsinit();
+ printinit();
+ print("\nPlan 9\n");
+ trapinit();
+ fpuinit();
+ intrinit();
+ clockinit();
+ timersinit();
+ pageinit();
+ procinit0();
+ initseg();
+ links();
+ chandevreset();
+ userinit();
+// mpinit();
+ mmu0clear((uintptr*)L1);
+ flushtlb();
+ mmu1init();
+ schedinit();
+}
+
+void
+exit(int)
+{
+ cpushutdown();
+ splfhi();
+ for(;;);
+}
+
+int
+isaconfig(char *, int, ISAConf *)
+{
+ return 0;
+}
+
+char*
+getconf(char *)
+{
+ return nil;
+}
+
+void
+writeconf(void)
+{
+}
+
+void
+reboot(void *, void *, ulong)
+{
+}
+
+void
+dmaflush(int clean, void *p, ulong len)
+{
+ uintptr s = (uintptr)p;
+ uintptr e = (uintptr)p + len;
+
+ if(clean){
+ s &= ~(BLOCKALIGN-1);
+ e += BLOCKALIGN-1;
+ e &= ~(BLOCKALIGN-1);
+ cachedwbse((void*)s, e - s);
+ return;
+ }
+ if(s & BLOCKALIGN-1){
+ s &= ~(BLOCKALIGN-1);
+ cachedwbinvse((void*)s, BLOCKALIGN);
+ s += BLOCKALIGN;
+ }
+ if(e & BLOCKALIGN-1){
+ e &= ~(BLOCKALIGN-1);
+ if(e < s)
+ return;
+ cachedwbinvse((void*)e, BLOCKALIGN);
+ }
+ if(s < e)
+ cachedinvse((void*)s, e - s);
+}
--- /dev/null
+++ b/sys/src/9/imx8/mem.h
@@ -1,0 +1,138 @@
+/*
+ * Memory and machine-specific definitions. Used in C and assembler.
+ */
+#define KiB 1024u /* Kibi 0x0000000000000400 */
+#define MiB 1048576u /* Mebi 0x0000000000100000 */
+#define GiB 1073741824u /* Gibi 000000000040000000 */
+
+/*
+ * Sizes:
+ * L0 L1 L2 L3
+ * 4K 2M 1G 512G
+ * 16K 32M 64G 128T
+ * 64K 512M 4T -
+ */
+#define PGSHIFT 16 /* log(BY2PG) */
+#define BY2PG (1ULL<<PGSHIFT) /* bytes per page */
+#define ROUND(s, sz) (((s)+(sz-1))&~(sz-1))
+#define PGROUND(s) ROUND(s, BY2PG)
+
+/* effective virtual address space */
+#define EVASHIFT 34
+#define EVAMASK ((1ULL<<EVASHIFT)-1)
+
+#define PTSHIFT (PGSHIFT-3)
+#define PTLEVELS (((EVASHIFT-PGSHIFT)+PTSHIFT-1)/PTSHIFT)
+#define PTLX(v, l) ((((v) & EVAMASK) >> (PGSHIFT + (l)*PTSHIFT)) & ((1 << PTSHIFT)-1))
+#define PGLSZ(l) (1ULL << (PGSHIFT + (l)*PTSHIFT))
+
+#define PTL1X(v, l) (L1TABLEX(v, l) | PTLX(v, l))
+#define L1TABLEX(v, l) (L1TABLE(v, l) << PTSHIFT)
+#define L1TABLES ((-KSEG0+PGLSZ(2)-1)/PGLSZ(2))
+#define L1TABLE(v, l) (L1TABLES - ((PTLX(v, 2) % L1TABLES) >> (((l)-1)*PTSHIFT)) + (l)-1)
+#define L1TOPSIZE (1ULL << (EVASHIFT - PTLEVELS*PTSHIFT))
+
+#define MAXMACH 4 /* max # cpus system can run */
+#define MACHSIZE (8*KiB)
+
+#define KSTACK (8*KiB)
+#define STACKALIGN(sp) ((sp) & ~7) /* bug: assure with alloc */
+#define TRAPFRAMESIZE (38*8)
+
+/* reserved dram for ucalloc() at the end of KZERO (physical) */
+#define UCRAMBASE (-KZERO - UCRAMSIZE)
+#define UCRAMSIZE (1*MiB)
+
+#define VDRAM (0xFFFFFFFFC0000000ULL) /* 0x40000000 - 0x80000000 */
+#define KTZERO (VDRAM + 0x100000) /* kernel text start */
+
+#define ARMLOCAL (0xFFFFFFFFB1000000ULL) /* 0x31000000 */
+#define VIRTIO (0xFFFFFFFFB0000000ULL) /* 0x30000000 */
+
+#define KZERO (0xFFFFFFFF80000000ULL) /* 0x00000000 - kernel address space */
+
+#define VMAP (0xFFFFFFFF00000000ULL) /* 0x00000000 - 0x40000000 */
+
+#define KMAPEND (0xFFFFFFFF00000000ULL) /* 0x140000000 */
+#define KMAP (0xFFFFFFFE00000000ULL) /* 0x40000000 */
+
+#define KSEG0 (0xFFFFFFFE00000000ULL)
+
+#define L1 (L1TOP-L1SIZE)
+#define L1SIZE ((L1TABLES+PTLEVELS-2)*BY2PG)
+#define L1TOP ((MACHADDR(MAXMACH-1)-L1TOPSIZE)&-BY2PG)
+
+#define MACHADDR(n) (KTZERO-((n)+1)*MACHSIZE)
+
+#define UZERO 0ULL /* user segment */
+#define UTZERO (UZERO+0x10000) /* user text start */
+#define USTKTOP ((EVAMASK>>1)-0xFFFF) /* user segment end +1 */
+#define USTKSIZE (16*1024*1024) /* user stack size */
+
+#define BLOCKALIGN 64 /* only used in allocb.c */
+
+/*
+ * Sizes
+ */
+#define BI2BY 8 /* bits per byte */
+#define BY2SE 4
+#define BY2WD 8
+#define BY2V 8 /* only used in xalloc.c */
+
+#define PTEMAPMEM (1024*1024)
+#define PTEPERTAB (PTEMAPMEM/BY2PG)
+#define SEGMAPSIZE 8192
+#define SSEGMAPSIZE 16
+#define PPN(x) ((x)&~(BY2PG-1))
+
+#define SHARE_NONE 0
+#define SHARE_OUTER 2
+#define SHARE_INNER 3
+
+#define CACHE_UC 0
+#define CACHE_WB 1
+#define CACHE_WT 2
+#define CACHE_WB_NA 3
+
+#define MA_MEM_WB 0
+#define MA_MEM_WT 1
+#define MA_MEM_UC 2
+#define MA_DEV_nGnRnE 3
+#define MA_DEV_nGnRE 4
+#define MA_DEV_nGRE 5
+#define MA_DEV_GRE 6
+
+#define PTEVALID 1
+#define PTEBLOCK 0
+#define PTETABLE 2
+#define PTEPAGE 2
+
+#define PTEMA(x) ((x)<<2)
+#define PTEAP(x) ((x)<<6)
+#define PTESH(x) ((x)<<8)
+
+#define PTEAF (1<<10)
+#define PTENG (1<<11)
+#define PTEPXN (1ULL<<53)
+#define PTEUXN (1ULL<<54)
+
+#define PTEKERNEL PTEAP(0)
+#define PTEUSER PTEAP(1)
+#define PTEWRITE PTEAP(0)
+#define PTERONLY PTEAP(2)
+#define PTENOEXEC (PTEPXN|PTEUXN)
+
+#define PTECACHED PTEMA(MA_MEM_WB)
+#define PTEWT PTEMA(MA_MEM_WT)
+#define PTEUNCACHED PTEMA(MA_MEM_UC)
+#define PTEDEVICE PTEMA(MA_DEV_nGnRE)
+
+/*
+ * Physical machine information from here on.
+ * PHYS addresses as seen from the arm cpu.
+ * BUS addresses as seen from peripherals
+ */
+#define PHYSDRAM 0
+
+#define MIN(a, b) ((a) < (b)? (a): (b))
+#define MAX(a, b) ((a) > (b)? (a): (b))
--- /dev/null
+++ b/sys/src/9/imx8/mkfile
@@ -1,0 +1,94 @@
+CONF=reform
+CONFLIST=reform
+
+kzero=0xffffffff80000000
+loadaddr=0xffffffffc0100000
+
+objtype=arm64
+</$objtype/mkfile
+p=9
+
+DEVS=`{rc ../port/mkdevlist $CONF}
+
+PORT=\
+ alarm.$O\
+ alloc.$O\
+ allocb.$O\
+ auth.$O\
+ cache.$O\
+ chan.$O\
+ dev.$O\
+ edf.$O\
+ fault.$O\
+ mul64fract.$O\
+ page.$O\
+ parse.$O\
+ pgrp.$O\
+ portclock.$O\
+ print.$O\
+ proc.$O\
+ qio.$O\
+ qlock.$O\
+ rdb.$O\
+ rebootcmd.$O\
+ segment.$O\
+ syscallfmt.$O\
+ sysfile.$O\
+ sysproc.$O\
+ taslock.$O\
+ tod.$O\
+ xalloc.$O\
+ userinit.$O\
+
+OBJ=\
+ l.$O\
+ cache.v8.$O\
+ clock.$O\
+ fpu.$O\
+ main.$O\
+ mmu.$O\
+ sysreg.$O\
+ random.$O\
+ trap.$O\
+ $CONF.root.$O\
+ $CONF.rootc.$O\
+ $DEVS\
+ $PORT\
+
+# HFILES=
+
+LIB=\
+# /$objtype/lib/libmemlayer.a\
+# /$objtype/lib/libmemdraw.a\
+# /$objtype/lib/libdraw.a\
+ /$objtype/lib/libip.a\
+ /$objtype/lib/libsec.a\
+# /$objtype/lib/libmp.a\
+ /$objtype/lib/libc.a\
+# /$objtype/lib/libdtracy.a\
+
+9:V: $p$CONF $p$CONF.u
+
+$p$CONF.u:D: $p$CONF
+ aux/aout2uimage -Z$kzero $p$CONF
+
+$p$CONF:D: $OBJ $CONF.$O $LIB
+ $LD -o $target -T$loadaddr -l $prereq
+ size $target
+
+$OBJ: $HFILES
+
+install:V: /$objtype/$p$CONF
+
+/$objtype/$p$CONF:D: $p$CONF s$p$CONF
+ cp -x $p$CONF $p$CONF.u /$objtype/
+
+<../boot/bootmkfile
+<../port/portmkfile
+<|../port/mkbootrules $CONF
+
+initcode.out: init9.$O initcode.$O /$objtype/lib/libc.a
+ $LD -l -R1 -s -o $target $prereq
+
+$CONF.clean:
+ rm -rf $p$CONF $p$CONF.u errstr.h $CONF.c boot$CONF.c
--- /dev/null
+++ b/sys/src/9/imx8/mmu.c
@@ -1,0 +1,492 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "sysreg.h"
+
+void
+mmu0init(uintptr *l1)
+{
+ uintptr va, pa, pe, attr;
+
+ /* VDRAM */
+ attr = PTEWRITE | PTEAF | PTEKERNEL | PTEUXN | PTESH(SHARE_INNER);
+ pe = -KZERO;
+ for(pa = VDRAM - KZERO, va = VDRAM; pa < pe; pa += PGLSZ(1), va += PGLSZ(1)){
+ l1[PTL1X(va, 1)] = pa | PTEVALID | PTEBLOCK | attr;
+ l1[PTL1X(pa, 1)] = pa | PTEVALID | PTEBLOCK | attr;
+ }
+
+ attr = PTEWRITE | PTEAF | PTEKERNEL | PTEUXN | PTEPXN | PTESH(SHARE_OUTER) | PTEDEVICE;
+ pe = VDRAM - KZERO;
+ for(pa = VIRTIO - KZERO, va = VIRTIO; pa < pe; pa += PGLSZ(1), va += PGLSZ(1)){
+ if(((pa|va) & PGLSZ(1)-1) != 0){
+ l1[PTL1X(va, 1)] = (uintptr)l1 | PTEVALID | PTETABLE;
+ for(; pa < pe && ((va|pa) & PGLSZ(1)-1) != 0; pa += PGLSZ(0), va += PGLSZ(0)){
+ assert(l1[PTLX(va, 0)] == 0);
+ l1[PTLX(va, 0)] = pa | PTEVALID | PTEPAGE | attr;
+ }
+ break;
+ }
+ l1[PTL1X(va, 1)] = pa | PTEVALID | PTEBLOCK | attr;
+ }
+
+ if(PTLEVELS > 2)
+ for(va = KSEG0; va != 0; va += PGLSZ(2))
+ l1[PTL1X(va, 2)] = (uintptr)&l1[L1TABLEX(va, 1)] | PTEVALID | PTETABLE;
+ if(PTLEVELS > 3)
+ for(va = KSEG0; va != 0; va += PGLSZ(3))
+ l1[PTL1X(va, 3)] = (uintptr)&l1[L1TABLEX(va, 2)] | PTEVALID | PTETABLE;
+}
+
+void
+mmu0clear(uintptr *l1)
+{
+ uintptr va, pa, pe;
+
+ pe = -VDRAM;
+ for(pa = VDRAM - KZERO, va = VDRAM; pa < pe; pa += PGLSZ(1), va += PGLSZ(1))
+ if(PTL1X(pa, 1) != PTL1X(va, 1))
+ l1[PTL1X(pa, 1)] = 0;
+
+ if(PTLEVELS > 2)
+ for(pa = VDRAM - KZERO, va = VDRAM; pa < pe; pa += PGLSZ(2), va += PGLSZ(2))
+ if(PTL1X(pa, 2) != PTL1X(va, 2))
+ l1[PTL1X(pa, 2)] = 0;
+ if(PTLEVELS > 3)
+ for(pa = VDRAM - KZERO, va = VDRAM; pa < pe; pa += PGLSZ(3), va += PGLSZ(3))
+ if(PTL1X(pa, 3) != PTL1X(va, 3))
+ l1[PTL1X(pa, 3)] = 0;
+}
+
+void
+mmu1init(void)
+{
+ m->mmutop = mallocalign(L1TOPSIZE, BY2PG, 0, 0);
+ if(m->mmutop == nil)
+ panic("mmu1init: no memory for mmutop");
+ memset(m->mmutop, 0, L1TOPSIZE);
+ mmuswitch(nil);
+}
+
+/* KZERO maps the first 1GB of ram */
+uintptr
+paddr(void *va)
+{
+ if((uintptr)va >= KZERO)
+ return (uintptr)va-KZERO;
+ panic("paddr: va=%#p pc=%#p", va, getcallerpc(&va));
+ return 0;
+}
+
+uintptr
+cankaddr(uintptr pa)
+{
+ if(pa < (uintptr)-KZERO)
+ return -KZERO - pa;
+ return 0;
+}
+
+void*
+kaddr(uintptr pa)
+{
+ if(pa < (uintptr)-KZERO)
+ return (void*)(pa + KZERO);
+ panic("kaddr: pa=%#p pc=%#p", pa, getcallerpc(&pa));
+ return nil;
+}
+
+static void*
+kmapaddr(uintptr pa)
+{
+ if(pa < (uintptr)-KZERO)
+ return (void*)(pa + KZERO);
+ if(pa < (VDRAM - KZERO) || pa >= (VDRAM - KZERO) + (KMAPEND - KMAP))
+ panic("kmapaddr: pa=%#p pc=%#p", pa, getcallerpc(&pa));
+ return (void*)(pa + KMAP - (VDRAM - KZERO));
+}
+
+KMap*
+kmap(Page *p)
+{
+ return kmapaddr(p->pa);
+}
+
+void
+kunmap(KMap*)
+{
+}
+
+void
+kmapinval(void)
+{
+}
+
+#define INITMAP (ROUND((uintptr)end + BY2PG, PGLSZ(1))-KZERO)
+
+static void*
+rampage(void)
+{
+ uintptr pa;
+
+ if(conf.npage)
+ return mallocalign(BY2PG, BY2PG, 0, 0);
+
+ pa = conf.mem[0].base;
+ assert((pa % BY2PG) == 0);
+ assert(pa < INITMAP);
+ conf.mem[0].base += BY2PG;
+ return KADDR(pa);
+}
+
+static void
+l1map(uintptr va, uintptr pa, uintptr pe, uintptr attr)
+{
+ uintptr *l1, *l0;
+
+ assert(pa < pe);
+
+ va &= -BY2PG;
+ pa &= -BY2PG;
+ pe = PGROUND(pe);
+
+ attr |= PTEKERNEL | PTEAF;
+
+ l1 = (uintptr*)L1;
+
+ while(pa < pe){
+ if(l1[PTL1X(va, 1)] == 0 && (pe-pa) >= PGLSZ(1) && ((va|pa) & PGLSZ(1)-1) == 0){
+ l1[PTL1X(va, 1)] = PTEVALID | PTEBLOCK | pa | attr;
+ va += PGLSZ(1);
+ pa += PGLSZ(1);
+ continue;
+ }
+ if(l1[PTL1X(va, 1)] & PTEVALID) {
+ assert((l1[PTL1X(va, 1)] & PTETABLE) == PTETABLE);
+ l0 = KADDR(l1[PTL1X(va, 1)] & -PGLSZ(0));
+ } else {
+ l0 = rampage();
+ memset(l0, 0, BY2PG);
+ l1[PTL1X(va, 1)] = PTEVALID | PTETABLE | PADDR(l0);
+ }
+ assert(l0[PTLX(va, 0)] == 0);
+ l0[PTLX(va, 0)] = PTEVALID | PTEPAGE | pa | attr;
+ va += BY2PG;
+ pa += BY2PG;
+ }
+}
+
+static void
+kmapram(uintptr base, uintptr limit)
+{
+ if(base < (uintptr)-KZERO && limit > (uintptr)-KZERO){
+ kmapram(base, (uintptr)-KZERO);
+ kmapram((uintptr)-KZERO, limit);
+ return;
+ }
+ if(base < INITMAP)
+ base = INITMAP;
+ if(base >= limit || limit <= INITMAP)
+ return;
+
+ l1map((uintptr)kmapaddr(base), base, limit,
+ PTEWRITE | PTEPXN | PTEUXN | PTESH(SHARE_INNER));
+}
+
+void
+meminit(void)
+{
+ uintptr va, pa;
+
+ /*
+ * now we know the real memory regions, unmap
+ * everything above INITMAP and map again with
+ * the proper sizes.
+ */
+ coherence();
+ for(va = INITMAP+KZERO; va != 0; va += PGLSZ(1)){
+ pa = va-KZERO;
+ ((uintptr*)L1)[PTL1X(pa, 1)] = 0;
+ ((uintptr*)L1)[PTL1X(va, 1)] = 0;
+ }
+ flushtlb();
+
+ /* DDR Memory (All modules) */
+ conf.mem[0].base = PGROUND((uintptr)end - KZERO);
+
+ /* exclude uncached dram for ucalloc() */
+ conf.mem[0].limit = UCRAMBASE;
+ conf.mem[1].base = UCRAMBASE+UCRAMSIZE;
+
+ conf.mem[1].limit = 0x100000000ULL;
+
+ /* DDR Memory (Quad-A53 only) */
+ conf.mem[2].base = 0x100000000ULL;
+ conf.mem[2].limit = 0x140000000ULL;
+
+ kmapram(conf.mem[0].base, conf.mem[0].limit);
+ kmapram(conf.mem[1].base, conf.mem[1].limit);
+ kmapram(conf.mem[2].base, conf.mem[2].limit);
+
+ conf.mem[0].npage = (conf.mem[0].limit - conf.mem[0].base)/BY2PG;
+ conf.mem[1].npage = (conf.mem[1].limit - conf.mem[1].base)/BY2PG;
+ conf.mem[2].npage = (conf.mem[2].limit - conf.mem[2].base)/BY2PG;
+}
+
+uintptr
+mmukmap(uintptr va, uintptr pa, usize size)
+{
+ uintptr attr, off;
+
+ if(va == 0)
+ return 0;
+
+ off = pa & BY2PG-1;
+
+ attr = va & PTEMA(7);
+ attr |= PTEWRITE | PTEUXN | PTEPXN | PTESH(SHARE_OUTER);
+
+ va &= -BY2PG;
+ pa &= -BY2PG;
+
+ l1map(va, pa, pa + off + size, attr);
+ flushtlb();
+
+ return va + off;
+}
+
+void*
+vmap(uvlong pa, vlong size)
+{
+ static uintptr base = VMAP;
+ uvlong pe = pa + size;
+ uintptr va;
+
+ va = base;
+ base += PGROUND(pe) - (pa & -BY2PG);
+
+ return (void*)mmukmap(va | PTEDEVICE, pa, size);
+}
+
+void
+vunmap(void *, vlong)
+{
+}
+
+static uintptr*
+mmuwalk(uintptr va, int level)
+{
+ uintptr *table, pte;
+ Page *pg;
+ int i, x;
+
+ x = PTLX(va, PTLEVELS-1);
+ table = m->mmutop;
+ for(i = PTLEVELS-2; i >= level; i--){
+ pte = table[x];
+ if(pte & PTEVALID) {
+ if(pte & (0xFFFFULL<<48))
+ iprint("strange pte %#p va %#p\n", pte, va);
+ pte &= ~(0xFFFFULL<<48 | BY2PG-1);
+ } else {
+ pg = up->mmufree;
+ if(pg == nil)
+ return nil;
+ up->mmufree = pg->next;
+ pg->va = va & -PGLSZ(i+1);
+ if((pg->next = up->mmuhead[i+1]) == nil)
+ up->mmutail[i+1] = pg;
+ up->mmuhead[i+1] = pg;
+ pte = pg->pa;
+ memset(kmapaddr(pte), 0, BY2PG);
+ coherence();
+ table[x] = pte | PTEVALID | PTETABLE;
+ }
+ table = kmapaddr(pte);
+ x = PTLX(va, (uintptr)i);
+ }
+ return &table[x];
+}
+
+static Proc *asidlist[256];
+
+static int
+allocasid(Proc *p)
+{
+ static Lock lk;
+ Proc *x;
+ int a;
+
+ lock(&lk);
+ a = p->asid;
+ if(a < 0)
+ a = -a;
+ if(a == 0)
+ a = p->pid;
+ for(;; a++){
+ a %= nelem(asidlist);
+ if(a == 0)
+ continue; // reserved
+ x = asidlist[a];
+ if(x == p || x == nil || (x->asid < 0 && x->mach == nil))
+ break;
+ }
+ p->asid = a;
+ asidlist[a] = p;
+ unlock(&lk);
+
+ return x != p;
+}
+
+static void
+freeasid(Proc *p)
+{
+ int a;
+
+ a = p->asid;
+ if(a < 0)
+ a = -a;
+ if(a > 0 && asidlist[a] == p)
+ asidlist[a] = nil;
+ p->asid = 0;
+}
+
+void
+putasid(Proc *p)
+{
+ /*
+ * Prevent the following scenario:
+ * pX sleeps on cpuA, leaving its page tables in mmutop
+ * pX wakes up on cpuB, and exits, freeing its page tables
+ * pY on cpuB allocates a freed page table page and overwrites with data
+ * cpuA takes an interrupt, and is now running with bad page tables
+ * In theory this shouldn't hurt because only user address space tables
+ * are affected, and mmuswitch will clear mmutop before a user process is
+ * dispatched. But empirically it correlates with weird problems, eg
+ * resetting of the core clock at 0x4000001C which confuses local timers.
+ */
+ if(conf.nmach > 1)
+ mmuswitch(nil);
+
+ if(p->asid > 0)
+ p->asid = -p->asid;
+}
+
+void
+putmmu(uintptr va, uintptr pa, Page *pg)
+{
+ uintptr *pte, old;
+ int s;
+
+ s = splhi();
+ while((pte = mmuwalk(va, 0)) == nil){
+ spllo();
+ up->mmufree = newpage(0, nil, 0);
+ splhi();
+ }
+ old = *pte;
+ *pte = 0;
+ if((old & PTEVALID) != 0)
+ flushasidvall((uvlong)up->asid<<48 | va>>12);
+ else
+ flushasidva((uvlong)up->asid<<48 | va>>12);
+ *pte = pa | PTEPAGE | PTEUSER | PTEPXN | PTENG | PTEAF |
+ (((pa & PTEMA(7)) == PTECACHED)? PTESH(SHARE_INNER): PTESH(SHARE_OUTER));
+ if(needtxtflush(pg)){
+ cachedwbinvse(kmap(pg), BY2PG);
+ cacheiinvse((void*)va, BY2PG);
+ donetxtflush(pg);
+ }
+ splx(s);
+}
+
+static void
+mmufree(Proc *p)
+{
+ int i;
+
+ freeasid(p);
+
+ for(i=1; i<PTLEVELS; i++){
+ if(p->mmuhead[i] == nil)
+ break;
+ p->mmutail[i]->next = p->mmufree;
+ p->mmufree = p->mmuhead[i];
+ p->mmuhead[i] = p->mmutail[i] = nil;
+ }
+}
+
+void
+mmuswitch(Proc *p)
+{
+ uintptr va;
+ Page *t;
+
+ for(va = UZERO; va < USTKTOP; va += PGLSZ(PTLEVELS-1))
+ m->mmutop[PTLX(va, PTLEVELS-1)] = 0;
+
+ if(p == nil){
+ setttbr(PADDR(m->mmutop));
+ return;
+ }
+
+ if(p->newtlb){
+ mmufree(p);
+ p->newtlb = 0;
+ }
+
+ if(allocasid(p))
+ flushasid((uvlong)p->asid<<48);
+
+ setttbr((uvlong)p->asid<<48 | PADDR(m->mmutop));
+
+ for(t = p->mmuhead[PTLEVELS-1]; t != nil; t = t->next){
+ va = t->va;
+ m->mmutop[PTLX(va, PTLEVELS-1)] = t->pa | PTEVALID | PTETABLE;
+ }
+}
+
+void
+mmurelease(Proc *p)
+{
+ mmuswitch(nil);
+ mmufree(p);
+ freepages(p->mmufree, nil, 0);
+ p->mmufree = nil;
+}
+
+void
+flushmmu(void)
+{
+ int x;
+
+ x = splhi();
+ up->newtlb = 1;
+ mmuswitch(up);
+ splx(x);
+}
+
+void
+checkmmu(uintptr, uintptr)
+{
+}
+
+void*
+ucalloc(usize size)
+{
+ static uintptr top = UCRAMBASE + UCRAMSIZE;
+ static Lock lk;
+ uintptr va;
+
+ size = PGROUND(size);
+
+ lock(&lk);
+ top -= size;
+ if(top < UCRAMBASE)
+ panic("ucalloc: %p needs %zd bytes\n", getcallerpc(&size), size);
+ va = KZERO + top;
+ unlock(&lk);
+
+ return (void*)mmukmap(va | PTEUNCACHED, PADDR(va), size);
+}
--- /dev/null
+++ b/sys/src/9/imx8/reform
@@ -1,0 +1,39 @@
+dev
+ root
+ cons
+ swap
+ env
+ pipe
+ proc
+ mnt
+ srv
+ shr
+ dup
+ tls
+ cap
+ fs
+ ether netif
+ ip arp chandial ip ipv6 ipaux iproute netlog nullmedium pktmedium inferno
+ uart
+link
+ etherimx ethermii
+ ethermedium
+ loopbackmedium
+ip
+ tcp
+ udp
+ il
+ ipifc
+ icmp
+ icmp6
+ ipmux
+misc
+ gic
+ uartimx
+port
+ int cpuserver = 0;
+bootdir
+ /$objtype/bin/paqfs
+ /$objtype/bin/auth/factotum
+ bootfs.paq
+ boot
--- /dev/null
+++ b/sys/src/9/imx8/sysreg.c
@@ -1,0 +1,58 @@
+/*
+ * ARMv8 system registers
+ * mainly to cope with arm hard-wiring register numbers into instructions.
+ *
+ * these routines must be callable from KZERO.
+ *
+ * on a multiprocessor, process switching to another cpu is assumed
+ * to be inhibited by the caller as these registers are local to the cpu.
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+static void*
+mkinstr(ulong wd)
+{
+ static ulong ib[256], *ep[MAXMACH+1];
+ static Lock lk;
+ ulong *ip, *ie;
+
+ ie = ep[m->machno];
+ for(ip = ib; ip < ie; ip += 2)
+ if(*ip == wd)
+ return ip;
+
+ ilock(&lk);
+ ie = ep[MAXMACH];
+ for(; ip < ie; ip += 2)
+ if(*ip == wd)
+ goto Found;
+ if(ip >= &ib[nelem(ib)])
+ panic("mkinstr: out of instrucuction buffer");
+ ip[0] = wd;
+ ip[1] = 0xd65f03c0; // RETURN
+ ep[MAXMACH] = ie = ip + 2;
+ cachedwbinvse(ip, 2*sizeof(*ip));
+Found:
+ iunlock(&lk);
+ cacheiinv();
+ ep[m->machno] = ie;
+ return ip;
+}
+
+uvlong
+sysrd(ulong spr)
+{
+ uvlong (*fp)(void) = mkinstr(0xd5380000UL | spr);
+ return fp();
+}
+
+void
+syswr(ulong spr, uvlong val)
+{
+ void (*fp)(uvlong) = mkinstr(0xd5180000UL | spr);
+ fp(val);
+}
--- /dev/null
+++ b/sys/src/9/imx8/sysreg.h
@@ -1,0 +1,89 @@
+#define MIDR_EL1 SYSREG(3,0,0,0,0)
+#define MPIDR_EL1 SYSREG(3,0,0,0,5)
+#define ID_AA64AFR0_EL1 SYSREG(3,0,0,5,4)
+#define ID_AA64AFR1_EL1 SYSREG(3,0,0,5,5)
+#define ID_AA64DFR0_EL1 SYSREG(3,0,0,5,0)
+#define ID_AA64DFR1_EL1 SYSREG(3,0,0,5,1)
+#define ID_AA64ISAR0_EL1 SYSREG(3,0,0,6,0)
+#define ID_AA64ISAR1_EL1 SYSREG(3,0,0,6,1)
+#define ID_AA64MMFR0_EL1 SYSREG(3,0,0,7,0)
+#define ID_AA64MMFR1_EL1 SYSREG(3,0,0,7,1)
+#define ID_AA64PFR0_EL1 SYSREG(3,0,0,4,0)
+#define ID_AA64PFR1_EL1 SYSREG(3,0,0,4,1)
+#define SCTLR_EL1 SYSREG(3,0,1,0,0)
+#define CPACR_EL1 SYSREG(3,0,1,0,2)
+#define MAIR_EL1 SYSREG(3,0,10,2,0)
+#define TCR_EL1 SYSREG(3,0,2,0,2)
+#define TTBR0_EL1 SYSREG(3,0,2,0,0)
+#define TTBR1_EL1 SYSREG(3,0,2,0,1)
+#define ESR_EL1 SYSREG(3,0,5,2,0)
+#define FAR_EL1 SYSREG(3,0,6,0,0)
+#define VBAR_EL1 SYSREG(3,0,12,0,0)
+#define VTTBR_EL2 SYSREG(3,4,2,1,0)
+#define SP_EL0 SYSREG(3,0,4,1,0)
+#define SP_EL1 SYSREG(3,4,4,1,0)
+#define SP_EL2 SYSREG(3,6,4,1,0)
+#define SCTLR_EL2 SYSREG(3,4,1,0,0)
+#define HCR_EL2 SYSREG(3,4,1,1,0)
+#define MDCR_EL2 SYSREG(3,4,1,1,1)
+#define PMCR_EL0 SYSREG(3,3,9,12,0)
+#define PMCNTENSET SYSREG(3,3,9,12,1)
+#define PMCCNTR_EL0 SYSREG(3,3,9,13,0)
+#define PMUSERENR_EL0 SYSREG(3,3,9,14,0)
+
+#define CNTPCT_EL0 SYSREG(3,3,14,0,1)
+#define CNTVCT_EL0 SYSREG(3,3,14,0,2)
+#define CNTKCTL_EL1 SYSREG(3,0,14,1,0)
+#define CNTFRQ_EL0 SYSREG(3,3,14,0,0)
+#define CNTP_TVAL_EL0 SYSREG(3,3,14,2,0)
+#define CNTP_CTL_EL0 SYSREG(3,3,14,2,1)
+#define CNTP_CVAL_EL0 SYSREG(3,3,14,2,2)
+
+#define TPIDR_EL0 SYSREG(3,3,13,0,2)
+#define TPIDR_EL1 SYSREG(3,0,13,0,4)
+
+#define CCSIDR_EL1 SYSREG(3,1,0,0,0)
+#define CSSELR_EL1 SYSREG(3,2,0,0,0)
+
+#define ACTLR_EL2 SYSREG(3,4,1,0,1)
+#define CPUACTLR_EL1 SYSREG(3,1,15,2,0)
+#define CPUECTLR_EL1 SYSREG(3,1,15,2,1)
+#define CBAR_EL1 SYSREG(3,1,15,3,0)
+
+#define ICC_AP0R_EL1(m) SYSREG(3,0,12,8,4|(m))
+#define ICC_AP1R_EL1(m) SYSREG(3,0,12,9,0|(m))
+#define ICC_ASGI1R_EL1 SYSREG(3,0,12,11,6)
+#define ICC_BPR0_EL1 SYSREG(3,0,12,8,3)
+#define ICC_BPR1_EL1 SYSREG(3,0,12,12,3)
+#define ICC_CTLR_EL1 SYSREG(3,0,12,12,4)
+#define ICC_DIR_EL1 SYSREG(3,0,12,11,1)
+#define ICC_EOIR0_EL1 SYSREG(3,0,12,8,1)
+#define ICC_EOIR1_EL1 SYSREG(3,0,12,12,1)
+#define ICC_HPPIR0_EL1 SYSREG(3,0,12,8,2)
+#define ICC_HPPIR1_EL1 SYSREG(3,0,12,12,2)
+#define ICC_IAR0_EL1 SYSREG(3,0,12,8,0)
+#define ICC_IAR1_EL1 SYSREG(3,0,12,12,0)
+#define ICC_IGRPEN0_EL1 SYSREG(3,0,12,12,6)
+#define ICC_IGRPEN1_EL1 SYSREG(3,0,12,12,7)
+#define ICC_NMIAR1_EL1 SYSREG(3,0,12,9,5)
+#define ICC_PMR_EL1 SYSREG(3,0,4,6,0)
+#define ICC_RPR_EL1 SYSREG(3,0,12,11,3)
+#define ICC_SGI0R_EL1 SYSREG(3,0,12,11,7)
+#define ICC_SGI1R_EL1 SYSREG(3,0,12,11,5)
+#define ICC_SRE_EL1 SYSREG(3,0,12,12,5)
+
+/* l.s redefines this for the assembler */
+#define SYSREG(op0,op1,Cn,Cm,op2) ((op0)<<19|(op1)<<16|(Cn)<<12|(Cm)<<8|(op2)<<5)
+
+#define OSHLD (0<<2 | 1)
+#define OSHST (0<<2 | 2)
+#define OSH (0<<2 | 3)
+#define NSHLD (1<<2 | 1)
+#define NSHST (1<<2 | 2)
+#define NSH (1<<2 | 3)
+#define ISHLD (2<<2 | 1)
+#define ISHST (2<<2 | 2)
+#define ISH (2<<2 | 3)
+#define LD (3<<2 | 1)
+#define ST (3<<2 | 2)
+#define SY (3<<2 | 3)
--- /dev/null
+++ b/sys/src/9/imx8/trap.c
@@ -1,0 +1,716 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "../port/systab.h"
+
+#include <tos.h>
+#include "ureg.h"
+#include "sysreg.h"
+
+int (*buserror)(Ureg*);
+
+/* SPSR bits user can modify */
+#define USPSRMASK (0xFULL<<28)
+
+static void
+setupvector(u32int *v, void (*t)(void), void (*f)(void))
+{
+ int i;
+
+ for(i = 0; i < 0x80/4; i++){
+ v[i] = ((u32int*)t)[i];
+ if(v[i] == 0x14000000){
+ v[i] |= ((u32int*)f - &v[i]) & 0x3ffffff;
+ return;
+ }
+ }
+ panic("bug in vector code");
+}
+
+void
+trapinit(void)
+{
+ extern void vsys(void);
+ extern void vtrap(void);
+ extern void virq(void);
+ extern void vfiq(void);
+ extern void vserr(void);
+
+ extern void vsys0(void);
+ extern void vtrap0(void);
+ extern void vtrap1(void);
+
+ static u32int *v;
+
+ intrcpushutdown();
+ if(v == nil){
+ /* disable everything */
+ intrsoff();
+
+ v = mallocalign(0x80*4*4, 1<<11, 0, 0);
+ if(v == nil)
+ panic("no memory for vector table");
+
+ setupvector(&v[0x000/4], vtrap, vtrap0);
+ setupvector(&v[0x080/4], virq, vtrap0);
+ setupvector(&v[0x100/4], vfiq, vtrap0);
+ setupvector(&v[0x180/4], vserr, vtrap0);
+
+ setupvector(&v[0x200/4], vtrap, vtrap1);
+ setupvector(&v[0x280/4], virq, vtrap1);
+ setupvector(&v[0x300/4], vfiq, vtrap1);
+ setupvector(&v[0x380/4], vserr, vtrap1);
+
+ setupvector(&v[0x400/4], vsys, vsys0);
+ setupvector(&v[0x480/4], virq, vtrap0);
+ setupvector(&v[0x500/4], vfiq, vtrap0);
+ setupvector(&v[0x580/4], vserr, vtrap0);
+
+ setupvector(&v[0x600/4], vtrap, vtrap0);
+ setupvector(&v[0x680/4], virq, vtrap0);
+ setupvector(&v[0x700/4], vfiq, vtrap0);
+ setupvector(&v[0x780/4], vserr, vtrap0);
+
+ cacheduwbse(v, 0x80*4*4);
+ }
+ cacheiinvse(v, 0x80*4*4);
+ syswr(VBAR_EL1, (uintptr)v);
+ splx(0x3<<6); // unmask serr and debug
+}
+
+static char *traps[64] = {
+ [0x00] "sys: trap: unknown",
+ [0x01] "sys: trap: WFI or WFE instruction execution",
+ [0x0E] "sys: trap: illegal execution state",
+ [0x18] "sys: trap: illegal MSR/MRS access",
+ [0x22] "sys: trap: misaligned pc",
+ [0x26] "sys: trap: stack pointer misaligned",
+ [0x30] "sys: trap: breakpoint",
+ [0x32] "sys: trap: software step",
+ [0x34] "sys: trap: watchpoint",
+ [0x3C] "sys: trap: BRK instruction",
+};
+
+void
+trap(Ureg *ureg)
+{
+ u32int type, intr;
+ int user;
+
+ intr = ureg->type >> 32;
+ if(intr == 2){
+ fiq(ureg);
+ return;
+ }
+ splflo();
+ user = kenter(ureg);
+ type = (u32int)ureg->type >> 26;
+ switch(type){
+ case 0x20: // instruction abort from lower level
+ case 0x21: // instruction abort from same level
+ case 0x24: // data abort from lower level
+ case 0x25: // data abort from same level
+ faultarm64(ureg);
+ break;
+ case 0x07: // SIMD/FP
+ case 0x2C: // FPU exception (A64 only)
+ mathtrap(ureg);
+ break;
+ case 0x00: // unknown
+ if(intr == 1){
+ if(irq(ureg) && up != nil && up->delaysched)
+ sched();
+ break;
+ }
+ if(intr == 3){
+ case 0x2F: // SError interrupt
+ if(buserror != nil && (*buserror)(ureg))
+ break;
+ dumpregs(ureg);
+ panic("SError interrupt");
+ break;
+ }
+ /* wet floor */
+ case 0x01: // WFI or WFE instruction execution
+ case 0x03: // MCR or MRC access to CP15 (A32 only)
+ case 0x04: // MCRR or MRC access to CP15 (A32 only)
+ case 0x05: // MCR or MRC access to CP14 (A32 only)
+ case 0x06: // LDC or STD access to CP14 (A32 only)
+ case 0x08: // MCR or MRC to CP10 (A32 only)
+ case 0x0C: // MRC access to CP14 (A32 only)
+ case 0x0E: // Illegal Execution State
+ case 0x11: // SVC instruction execution (A32 only)
+ case 0x12: // HVC instruction execution (A32 only)
+ case 0x13: // SMC instruction execution (A32 only)
+ case 0x15: // SVC instruction execution (A64 only)
+ case 0x16: // HVC instruction execution (A64 only)
+ case 0x17: // SMC instruction execution (A64 only)
+ case 0x18: // MSR/MRS (A64)
+ case 0x22: // misaligned pc
+ case 0x26: // stack pointer misaligned
+ case 0x28: // FPU exception (A32 only)
+ case 0x30: // breakpoint from lower level
+ case 0x31: // breakpoint from same level
+ case 0x32: // software step from lower level
+ case 0x33: // software step from same level
+ case 0x34: // watchpoint execution from lower level
+ case 0x35: // watchpoint exception from same level
+ case 0x38: // breapoint (A32 only)
+ case 0x3A: // vector catch exception (A32 only)
+ case 0x3C: // BRK instruction (A64 only)
+ default:
+ if(!userureg(ureg)){
+ dumpregs(ureg);
+ panic("unhandled trap");
+ }
+ if(traps[type] == nil) type = 0; // unknown
+ postnote(up, 1, traps[type], NDebug);
+ break;
+ }
+ splhi();
+ if(user){
+ if(up->procctl || up->nnote)
+ notify(ureg);
+ kexit(ureg);
+ }
+}
+
+void
+syscall(Ureg *ureg)
+{
+ vlong startns, stopns;
+ uintptr sp, ret;
+ ulong scallnr;
+ int i, s;
+ char *e;
+
+ if(!kenter(ureg))
+ panic("syscall from kernel");
+
+ m->syscall++;
+ up->insyscall = 1;
+ up->pc = ureg->pc;
+
+ sp = ureg->sp;
+ up->scallnr = scallnr = ureg->r0;
+
+ spllo();
+
+ up->nerrlab = 0;
+ startns = 0;
+ ret = -1;
+ if(!waserror()){
+ if(sp < USTKTOP - BY2PG || sp > USTKTOP - sizeof(Sargs) - BY2WD){
+ validaddr(sp, sizeof(Sargs)+BY2WD, 0);
+ evenaddr(sp);
+ }
+ up->s = *((Sargs*) (sp + BY2WD));
+
+ if(up->procctl == Proc_tracesyscall){
+ syscallfmt(scallnr, ureg->pc, (va_list) up->s.args);
+ s = splhi();
+ up->procctl = Proc_stopme;
+ procctl();
+ splx(s);
+ startns = todget(nil);
+ }
+
+ if(scallnr >= nsyscall || systab[scallnr] == nil){
+ pprint("bad sys call number %lud pc %#p", scallnr, ureg->pc);
+ postnote(up, 1, "sys: bad sys call", NDebug);
+ error(Ebadarg);
+ }
+ up->psstate = sysctab[scallnr];
+ ret = systab[scallnr]((va_list)up->s.args);
+ poperror();
+ }else{
+ e = up->syserrstr;
+ up->syserrstr = up->errstr;
+ up->errstr = e;
+ }
+ if(up->nerrlab){
+ print("bad errstack [%lud]: %d extra\n", scallnr, up->nerrlab);
+ for(i = 0; i < NERR; i++)
+ print("sp=%#p pc=%#p\n", up->errlab[i].sp, up->errlab[i].pc);
+ panic("error stack");
+ }
+ ureg->r0 = ret;
+ if(up->procctl == Proc_tracesyscall){
+ stopns = todget(nil);
+ sysretfmt(scallnr, (va_list) up->s.args, ret, startns, stopns);
+ s = splhi();
+ up->procctl = Proc_stopme;
+ procctl();
+ splx(s);
+ }
+
+ up->insyscall = 0;
+ up->psstate = 0;
+ if(scallnr == NOTED){
+ noted(ureg, *((ulong*) up->s.args));
+ /*
+ * normally, syscall() returns to forkret()
+ * not restoring general registers when going
+ * to userspace. to completely restore the
+ * interrupted context, we have to return thru
+ * noteret(). we override return pc to jump to
+ * to it when returning form syscall()
+ */
+ returnto(noteret);
+ }
+
+ if(scallnr != RFORK && (up->procctl || up->nnote)){
+ splhi();
+ notify(ureg);
+ }
+ if(up->delaysched)
+ sched();
+ kexit(ureg);
+}
+
+int
+notify(Ureg *ureg)
+{
+ int l;
+ uintptr s, sp;
+ Note *n;
+
+ if(up->procctl)
+ procctl();
+ if(up->nnote == 0)
+ return 0;
+ if(up->fpstate == FPactive){
+ fpsave(up->fpsave);
+ up->fpstate = FPinactive;
+ }
+ up->fpstate |= FPillegal;
+
+ s = spllo();
+ qlock(&up->debug);
+ up->notepending = 0;
+ n = &up->note[0];
+ if(strncmp(n->msg, "sys:", 4) == 0){
+ l = strlen(n->msg);
+ if(l > ERRMAX-23) /* " pc=0x0123456789abcdef\0" */
+ l = ERRMAX-23;
+ sprint(n->msg+l, " pc=%#p", ureg->pc);
+ }
+
+ if(n->flag!=NUser && (up->notified || up->notify==0)){
+ qunlock(&up->debug);
+ if(n->flag == NDebug)
+ pprint("suicide: %s\n", n->msg);
+ 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 = ureg->sp;
+ sp -= 256; /* debugging: preserve context causing problem */
+ sp -= sizeof(Ureg);
+ sp = STACKALIGN(sp);
+
+ if(!okaddr((uintptr)up->notify, 1, 0)
+ || !okaddr(sp-ERRMAX-4*BY2WD, sizeof(Ureg)+ERRMAX+4*BY2WD, 1)
+ || ((uintptr) up->notify & 3) != 0
+ || (sp & 7) != 0){
+ qunlock(&up->debug);
+ pprint("suicide: bad address in notify: handler=%#p sp=%#p\n",
+ up->notify, sp);
+ pexit("Suicide", 0);
+ }
+
+ memmove((Ureg*)sp, ureg, sizeof(Ureg));
+ *(Ureg**)(sp-BY2WD) = up->ureg; /* word under Ureg is old up->ureg */
+ up->ureg = (void*)sp;
+ sp -= BY2WD+ERRMAX;
+ memmove((char*)sp, up->note[0].msg, ERRMAX);
+ sp -= 3*BY2WD;
+ *(uintptr*)(sp+2*BY2WD) = sp+3*BY2WD;
+ *(uintptr*)(sp+1*BY2WD) = (uintptr)up->ureg;
+ ureg->r0 = (uintptr) up->ureg;
+ ureg->sp = sp;
+ ureg->pc = (uintptr) up->notify;
+ ureg->link = 0;
+ 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;
+}
+
+void
+noted(Ureg *ureg, ulong arg0)
+{
+ Ureg *nureg;
+ uintptr oureg, sp;
+
+ qlock(&up->debug);
+ if(arg0 != NRSTR && !up->notified){
+ qunlock(&up->debug);
+ pprint("call to noted() when not notified\n");
+ pexit("Suicide", 0);
+ }
+ up->notified = 0;
+
+ nureg = up->ureg;
+ up->fpstate &= ~FPillegal;
+
+ oureg = (uintptr) nureg;
+ if(!okaddr(oureg - BY2WD, BY2WD + sizeof(Ureg), 0) || (oureg & 7) != 0){
+ qunlock(&up->debug);
+ pprint("bad ureg in noted or call to noted when not notified\n");
+ pexit("Suicide", 0);
+ }
+
+ nureg->psr = (nureg->psr & USPSRMASK) | (ureg->psr & ~USPSRMASK);
+ memmove(ureg, nureg, sizeof(Ureg));
+
+ switch(arg0){
+ case NCONT: case NRSTR:
+ if(!okaddr(nureg->pc, BY2WD, 0) || !okaddr(nureg->sp, BY2WD, 0) ||
+ (nureg->pc & 3) != 0 || (nureg->sp & 7) != 0){
+ qunlock(&up->debug);
+ pprint("suicide: trap in noted\n");
+ pexit("Suicide", 0);
+ }
+ up->ureg = (Ureg *) (*(uintptr*) (oureg - BY2WD));
+ qunlock(&up->debug);
+ break;
+
+ case NSAVE:
+ if(!okaddr(nureg->pc, BY2WD, 0) || !okaddr(nureg->sp, BY2WD, 0) ||
+ (nureg->pc & 3) != 0 || (nureg->sp & 7) != 0){
+ qunlock(&up->debug);
+ pprint("suicide: trap in noted\n");
+ pexit("Suicide", 0);
+ }
+ qunlock(&up->debug);
+ sp = oureg - 4 * BY2WD - ERRMAX;
+ splhi();
+ ureg->sp = sp;
+ ureg->r0 = (uintptr) oureg;
+ ((uintptr *) sp)[1] = oureg;
+ ((uintptr *) sp)[0] = 0;
+ break;
+
+ default:
+ up->lastnote.flag = NDebug;
+
+ case NDFLT:
+ qunlock(&up->debug);
+ if(up->lastnote.flag == NDebug)
+ pprint("suicide: %s\n", up->lastnote.msg);
+ pexit(up->lastnote.msg, up->lastnote.flag != NDebug);
+ }
+}
+
+void
+faultarm64(Ureg *ureg)
+{
+ extern void checkpages(void);
+ char buf[ERRMAX];
+ int read, insyscall;
+ uintptr addr;
+
+ insyscall = up->insyscall;
+ up->insyscall = 1;
+
+ if(!userureg(ureg)){
+ extern void _peekinst(void);
+
+ if(ureg->pc == (uintptr)_peekinst){
+ ureg->pc = ureg->link;
+ goto out;
+ }
+
+ if(waserror()){
+ if(up->nerrlab == 0){
+ pprint("suicide: sys: %s\n", up->errstr);
+ pexit(up->errstr, 1);
+ }
+ up->insyscall = insyscall;
+ nexterror();
+ }
+ }
+
+ addr = getfar();
+ read = (ureg->type & (1<<6)) == 0;
+
+ switch((u32int)ureg->type & 0x3F){
+ case 4: case 5: case 6: case 7: // Tanslation fault.
+ case 8: case 9: case 10: case 11: // Access flag fault.
+ case 12: case 13: case 14: case 15: // Permission fault.
+ case 48: // tlb conflict fault.
+ if(fault(addr, ureg->pc, read) == 0)
+ break;
+
+ /* wet floor */
+ case 0: case 1: case 2: case 3: // Address size fault.
+ case 16: // synchronous external abort.
+ case 24: // synchronous parity error on a memory access.
+ case 20: case 21: case 22: case 23: // synchronous external abort on a table walk.
+ case 28: case 29: case 30: case 31: // synchronous parity error on table walk.
+ case 33: // alignment fault.
+ case 52: // implementation defined, lockdown abort.
+ case 53: // implementation defined, unsuppoted exclusive.
+ case 61: // first level domain fault
+ case 62: // second level domain fault
+ default:
+ if(!userureg(ureg)){
+ dumpregs(ureg);
+ panic("fault: %s addr=%#p", read ? "read" : "write", addr);
+ }
+ checkpages();
+ sprint(buf, "sys: trap: fault %s addr=%#p", read ? "read" : "write", addr);
+ postnote(up, 1, buf, NDebug);
+ }
+
+ if(!userureg(ureg))
+ poperror();
+
+out:
+ up->insyscall = insyscall;
+}
+
+int
+userureg(Ureg* ureg)
+{
+ return (ureg->psr & 15) == 0;
+}
+
+uintptr
+userpc(void)
+{
+ Ureg *ur = up->dbgreg;
+ return ur->pc;
+}
+
+uintptr
+dbgpc(Proc *)
+{
+ Ureg *ur = up->dbgreg;
+ if(ur == nil)
+ return 0;
+ return ur->pc;
+}
+
+void
+procfork(Proc *p)
+{
+ int s;
+
+ s = splhi();
+ switch(up->fpstate & ~FPillegal){
+ case FPactive:
+ fpsave(up->fpsave);
+ up->fpstate = FPinactive;
+ case FPinactive:
+ memmove(p->fpsave, up->fpsave, sizeof(FPsave));
+ p->fpstate = FPinactive;
+ }
+ splx(s);
+
+ p->tpidr = up->tpidr;
+}
+
+void
+procsetup(Proc *p)
+{
+ p->fpstate = FPinit;
+ fpoff();
+
+ p->tpidr = 0;
+ syswr(TPIDR_EL0, p->tpidr);
+}
+
+void
+procsave(Proc *p)
+{
+ if(p->fpstate == FPactive){
+ if(p->state == Moribund)
+ fpclear();
+ else
+ fpsave(p->fpsave);
+ p->fpstate = FPinactive;
+ }
+
+ if(p->kp == 0)
+ p->tpidr = sysrd(TPIDR_EL0);
+
+ putasid(p); // release asid
+}
+
+void
+procrestore(Proc *p)
+{
+ if(p->kp == 0)
+ syswr(TPIDR_EL0, p->tpidr);
+}
+
+void
+kprocchild(Proc *p, void (*entry)(void))
+{
+ p->sched.pc = (uintptr) entry;
+ p->sched.sp = (uintptr) p->kstack + KSTACK - 16;
+ *(void**)p->sched.sp = kprocchild; /* fake */
+}
+
+void
+forkchild(Proc *p, Ureg *ureg)
+{
+ Ureg *cureg;
+
+ p->sched.pc = (uintptr) forkret;
+ p->sched.sp = (uintptr) p->kstack + KSTACK - TRAPFRAMESIZE;
+
+ cureg = (Ureg*) (p->sched.sp + 16);
+ memmove(cureg, ureg, sizeof(Ureg));
+ cureg->r0 = 0;
+}
+
+uintptr
+execregs(uintptr entry, ulong ssize, ulong nargs)
+{
+ uintptr *sp;
+ Ureg *ureg;
+
+ sp = (uintptr*)(USTKTOP - ssize);
+ *--sp = nargs;
+
+ ureg = up->dbgreg;
+ ureg->sp = (uintptr)sp;
+ ureg->pc = entry;
+ ureg->link = 0;
+ return USTKTOP-sizeof(Tos);
+}
+
+void
+evenaddr(uintptr addr)
+{
+ if(addr & 3){
+ postnote(up, 1, "sys: odd address", NDebug);
+ error(Ebadarg);
+ }
+}
+
+void
+callwithureg(void (*f) (Ureg *))
+{
+ Ureg u;
+
+ u.pc = getcallerpc(&f);
+ u.sp = (uintptr) &f;
+ f(&u);
+}
+
+void
+setkernur(Ureg *ureg, Proc *p)
+{
+ ureg->pc = p->sched.pc;
+ ureg->sp = p->sched.sp;
+ ureg->link = (uintptr)sched;
+}
+
+void
+setupwatchpts(Proc*, Watchpt*, int)
+{
+}
+
+void
+setregisters(Ureg* ureg, char* pureg, char* uva, int n)
+{
+ ulong v;
+
+ v = ureg->psr;
+ memmove(pureg, uva, n);
+ ureg->psr = (ureg->psr & USPSRMASK) | (v & ~USPSRMASK);
+}
+
+static void
+dumpstackwithureg(Ureg *ureg)
+{
+ uintptr v, estack, sp;
+ char *s;
+ int i;
+
+ if((s = getconf("*nodumpstack")) != nil && strcmp(s, "0") != 0){
+ iprint("dumpstack disabled\n");
+ return;
+ }
+ iprint("ktrace /kernel/path %#p %#p %#p # pc, sp, link\n",
+ ureg->pc, ureg->sp, ureg->link);
+ delay(2000);
+
+ sp = ureg->sp;
+ if(sp < KZERO || (sp & 7) != 0)
+ sp = (uintptr)&ureg;
+
+ estack = (uintptr)m+MACHSIZE;
+ if(up != nil && sp <= (uintptr)up->kstack+KSTACK)
+ estack = (uintptr)up->kstack+KSTACK;
+
+ if(sp > estack){
+ if(up != nil)
+ iprint("&up->kstack %#p sp %#p\n", up->kstack, sp);
+ else
+ iprint("&m %#p sp %#p\n", m, sp);
+ return;
+ }
+
+ i = 0;
+ for(; sp < estack; sp += sizeof(uintptr)){
+ v = *(uintptr*)sp;
+ if(KTZERO < v && v < (uintptr)etext && (v & 3) == 0){
+ iprint("%#8.8lux=%#8.8lux ", (ulong)sp, (ulong)v);
+ i++;
+ }
+ if(i == 4){
+ i = 0;
+ iprint("\n");
+ }
+ }
+ if(i)
+ iprint("\n");
+}
+
+void
+dumpstack(void)
+{
+ callwithureg(dumpstackwithureg);
+}
+
+void
+dumpregs(Ureg *ureg)
+{
+ u64int *r;
+ int i, x;
+
+ x = splhi();
+ if(up != nil)
+ iprint("cpu%d: dumpregs ureg %#p process %lud: %s\n", m->machno, ureg,
+ up->pid, up->text);
+ else
+ iprint("cpu%d: dumpregs ureg %#p\n", m->machno, ureg);
+ r = &ureg->r0;
+ for(i = 0; i < 30; i += 3)
+ iprint("R%d %.16llux R%d %.16llux R%d %.16llux\n", i, r[i], i+1, r[i+1], i+2, r[i+2]);
+ iprint("PC %#p SP %#p LR %#p PSR %llux TYPE %llux\n",
+ ureg->pc, ureg->sp, ureg->link,
+ ureg->psr, ureg->type);
+ splx(x);
+}
--- /dev/null
+++ b/sys/src/9/imx8/uartimx.c
@@ -1,0 +1,383 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "../port/error.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+enum {
+ URXD = 0x00/4, /* UART Receiver Register */
+ RX_CHARRDY = 1<<15,
+ RX_ERR = 1<<14,
+ RX_OVRRUN = 1<<13,
+ RX_FRMERR = 1<<12,
+ RX_BRK = 1<<11,
+ RX_PRERR = 1<<10,
+ RX_DATA = 0xFF,
+
+ UTXD = 0x40/4, /* UART Transmitter Register */
+ TX_DATA = 0xFF,
+
+ UCR1 = 0x80/4, /* UART Control Register 1 */
+ CR1_ADEN = 1<<15, /* Automatic Baud Rate Detection Interrupt Enable */
+ CR1_ADNR = 1<<14, /* Automatic Detection of Baud Rate */
+ CR1_TRDYEN = 1<<13, /* Transmitter Ready Interrupt Enable */
+ CR1_IDEN = 1<<12, /* Idle Condition Detected Interrupt Enable */
+
+ CR1_ICD_SHIFT = 10, /* Idle Condition Detect Mask */
+ CR1_ICD_MASK = 3<<CR1_ICD_SHIFT,
+
+ CR1_RRDYEN = 1<<9, /* Receiver Ready Interrupt Enable */
+ CR1_RXDMAEN = 1<<8, /* Receive Ready DMA Enable */
+ CR1_IREN = 1<<7, /* Infrared Interface Enable */
+ CR1_TXMPTYEN = 1<<6, /* Transmitter Empty Interrupt Enable */
+ CR1_RTSDEN = 1<<5, /* RTS Delta Interrupt Enable */
+ CR1_SNDBRK = 1<<4, /* Send BREAK */
+ CR1_TXDMAEN = 1<<3, /* Transmitter Ready DMA Enable */
+ CR1_ATDMAEN = 1<<2, /* Aging DMA Timer Enable */
+ CR1_DOZE = 1<<1, /* DOZE */
+ CR1_UARTEN = 1<<0, /* Uart Enable */
+
+ UCR2 = 0x84/4, /* UART Control Register 2 */
+ CR2_ESCI = 1<<15, /* Escape Sequence Interrupt Enable */
+ CR2_IRTS = 1<<14, /* Ignore RTS Pin */
+ CR2_CTSC = 1<<13, /* CTS Pin Control */
+ CR2_CTS = 1<<12, /* Clear to Send */
+ CR2_ESCEN = 1<<11, /* Escape Enable */
+
+ CR2_RTEC_RAISING= 0<<9,
+ CR2_RTEC_FALLING= 1<<9,
+ CR2_RTEC_ANY = 2<<9,
+ CR2_RTEC_MASK = 3<<9,
+
+ CR2_PREN = 1<<8, /* Parity Enable */
+ CR2_PREVEN = 0<<7, /* Parity Even */
+ CR2_PRODD = 1<<7, /* Parity Odd */
+ CR2_STPB = 1<<6, /* Stop */
+ CR2_WS8 = 1<<5, /* Word Size */
+ CR2_WS7 = 0<<5,
+ CR2_RTSEN = 1<<4, /* Request to Send Interrupt Enable */
+ CR2_ATEN = 1<<3, /* Aging Timer Enable */
+ CR2_TXEN = 1<<2, /* Transmitter Enable */
+ CR2_RXEN = 1<<1, /* Receiver Enable */
+ CR2_SRST = 1<<0, /* Software Reset */
+
+ UCR3 = 0x88/4, /* UART Control Register 3 */
+ CR3_PARERREN = 1<<12, /* Parity Error Interrupt Enable */
+ CR3_FRAERREN = 1<<11, /* Frame Error Interrupt Enable */
+ CR3_ADNIMP = 1<<7, /* Autobaud Detection Not Improved */
+ CR3_RXDSEN = 1<<6, /* Receive Status Interrupt Enable */
+ CR3_AIRINTEN = 1<<5, /* Asynchronous IR WAKE Interrupt Enable */
+ CR3_AWAKEN = 1<<4, /* Asynchronous WAKE Interrupt Enable */
+ CR3_RXDMUXSEL = 1<<2, /* RXD Muxed Input Selected */
+ CR3_INVT = 1<<1, /* Invert TXD output in RS-232/RS-485 mode */
+ CR3_ACIEN = 1<<0, /* Autobaud Counter Interrupt Enable */
+
+ UCR4 = 0x8C/4, /* UART Control Register 4 */
+ CR4_CTSTL_SHIFT = 10, /* CTS Trigger Level */
+ CR4_CTSTL_MASK = 0x3F<<CR4_CTSTL_SHIFT,
+
+ CR4_INVR = 1<<9, /* Invert RXD Input in RS-232/RS-485 Mode */
+ CR4_ENIRI = 1<<8, /* Serial Infrared Interrupt Enable */
+ CR4_WKEN = 1<<7, /* WAKE Interrupt Enable */
+ CR4_IDDMAEN = 1<<6, /* DMA IDLE Condition Detected Interrupt Enable */
+ CR4_IRSC = 1<<5, /* IR Special Case */
+ CR4_LPBYP = 1<<4, /* Low Power Bypass */
+ CR4_TCEN = 1<<3, /* Transmit Complete Interrupt Enable */
+ CR4_BKEN = 1<<2, /* BREAK Condition Detected Interrupt Enable */
+ CR4_OREN = 1<<1, /* Receiver Overrun Interrupt Enable */
+ CR4_DREN = 1<<0, /* Receive Data Interrupt Enable */
+
+ UFCR = 0x90/4, /* UART FIFO Control Register */
+ FCR_TXTL_SHIFT = 10, /* Transmitter Trigger Level */
+ FCR_TXTL_MASK = 0x3F<<FCR_TXTL_SHIFT,
+
+ FCR_RFDIV_SHIFT = 7, /* Reference Frequency Divider */
+ FCR_RFDIV_MASK = 0x7<<FCR_RFDIV_SHIFT,
+
+ FCR_DCE = 0<<6, /* DCE/DTE mode select */
+ FCR_DTE = 1<<6,
+
+ FCR_RXTL_SHIFT = 0, /* Receive Trigger Level */
+ FCR_RXTL_MASK = 0x3F<<FCR_RXTL_SHIFT,
+
+ USR1 = 0x94/4, /* UART Status Register 1 */
+ SR1_PARITYERR = 1<<15, /* Parity Error Interrupt Flag */
+ SR1_RTSS = 1<<14, /* RTS_B Pin Status */
+ SR1_TRDY = 1<<13, /* Transmitter Ready Interrupt / DMA Flag */
+ SR1_RTSD = 1<<12, /* RTS Delta */
+ SR1_ESCF = 1<<11, /* Escape Sequence Interrupt Flag */
+ SR1_FRAMEERR = 1<<10, /* Frame Error Interrupt Flag */
+ SR1_RRDY = 1<<9, /* Receiver Ready Interrupt / DMA Flag */
+ SR1_AGTIM = 1<<8, /* Aging Timer Interrupt Flag */
+ SR1_DTRD = 1<<7,
+ SR1_RXDS = 1<<6, /* Receiver IDLE Interrupt Flag */
+ SR1_AIRINT = 1<<5, /* Asynchronous IR WAKE Interrupt Flag */
+ SR1_AWAKE = 1<<4, /* Asynchronous WAKE Interrupt Flag */
+ SR1_SAD = 1<<3, /* RS-485 Slave Address Detected Interrupt Flag */
+
+ USR2 = 0x98/4, /* UART Status Register 2 */
+ SR2_ADET = 1<<15, /* Automatic Baud Rate Detected Complete */
+ SR2_TXFE = 1<<14, /* Transmit Buffer FIFO Empty */
+ SR2_DTRF = 1<<13,
+ SR2_IDLE = 1<<12, /* Idle Condition */
+ SR2_ACST = 1<<11, /* Autobaud Counter Stopped */
+ SR2_RIDELT = 1<<10,
+ SR2_RIIN = 1<<9,
+ SR2_IRINT = 1<<8, /* Serial Infrared Interrupt Flag */
+ SR2_WAKE = 1<<7, /* Wake */
+ SR2_DCDDELT = 1<<6,
+ SR2_DCDIN = 1<<5,
+ SR2_RTSF = 1<<4, /* RTS Edge Triggered Interrupt Flag */
+ SR2_TXDC = 1<<3, /* Transmitter Complete */
+ SR2_BRCD = 1<<2, /* BREAK Condition Detected */
+ SR2_ORE = 1<<1, /* Overrun Error */
+ SR2_RDR = 1<<0, /* Receive Data Ready */
+
+ UESC = 0x9C/4, /* UART Escape Character Register */
+ UTIM = 0xA0/4, /* UART Escape Timer Register */
+ UBIR = 0xA4/4, /* UART BRM Incremental Modulator Register */
+ UBMR = 0xA8/4, /* UART BRM Modulator Register */
+ UBRC = 0xAC/4, /* UART Baud Rate Count Register */
+ ONEMS = 0xB0/4, /* UART One-Millisecond Register */
+ UTS = 0xB5/4, /* UART Test Register */
+ UMCR = 0xB8/4, /* UART RS-485 Mode Control Register */
+};
+
+extern PhysUart imxphysuart;
+
+static Uart uart1 = {
+ .regs = (u32int*)(VIRTIO + 0x860000ULL),
+ .name = "uart1",
+ .baud = 115200,
+ .freq = 25*Mhz,
+ .phys = &imxphysuart,
+};
+
+static Uart*
+pnp(void)
+{
+ return &uart1;
+}
+
+static void
+kick(Uart *u)
+{
+ u32int *regs = (u32int*)u->regs;
+
+ if(u->blocked)
+ return;
+ while(regs[USR1] & SR1_TRDY){
+ if(u->op >= u->oe && uartstageoutput(u) == 0)
+ break;
+ regs[UTXD] = *(u->op++) & TX_DATA;
+ }
+}
+
+static void
+config(Uart *u)
+{
+ u32int cr2, *regs = u->regs;
+
+ /* enable uart */
+ regs[UCR1] = CR1_UARTEN;
+
+ cr2 = CR2_SRST | CR2_IRTS | CR2_RXEN | CR2_TXEN;
+ switch(u->parity){
+ case 'e': cr2 |= CR2_PREN | CR2_PREVEN; break;
+ case 'o': cr2 |= CR2_PREN | CR2_PRODD; break;
+ }
+ cr2 |= u->bits == 7 ? CR2_WS7 : CR2_WS8;
+ if(u->stop == 2) cr2 |= CR2_STPB;
+ regs[UCR2] = cr2;
+ regs[UCR3] = 0x7<<8 | CR3_RXDMUXSEL;
+ regs[UCR4] = 31<<CR4_CTSTL_SHIFT;
+
+ /* baud = clock / (16 * (ubmr+1)/(ubir+1)) */
+ regs[UFCR] = (6 - 1)<<FCR_RFDIV_SHIFT | 32<<FCR_TXTL_SHIFT | 32<<FCR_RXTL_SHIFT;
+ regs[UBIR] = ((16*u->baud) / 1600)-1;
+ regs[UBMR] = (u->freq / 1600)-1;
+
+ regs[UCR1] = CR1_UARTEN | CR1_TRDYEN | CR1_RRDYEN;
+}
+
+static int
+bits(Uart *u, int n)
+{
+ switch(n){
+ case 8:
+ break;
+ case 7:
+ break;
+ default:
+ return -1;
+ }
+ u->bits = n;
+ config(u);
+ return 0;
+}
+
+static int
+stop(Uart *u, int n)
+{
+ switch(n){
+ case 1:
+ break;
+ case 2:
+ break;
+ default:
+ return -1;
+ }
+ u->stop = n;
+ config(u);
+ return 0;
+}
+
+static int
+parity(Uart *u, int n)
+{
+ switch(n){
+ case 'n':
+ break;
+ case 'e':
+ break;
+ case 'o':
+ break;
+ default:
+ return -1;
+ }
+ u->parity = n;
+ config(u);
+ return 0;
+}
+
+static int
+baud(Uart *u, int n)
+{
+ if(u->freq == 0 || n <= 0)
+ return -1;
+ u->baud = n;
+ config(u);
+ return 0;
+}
+
+static void
+rts(Uart*, int)
+{
+}
+
+static void
+dobreak(Uart*, int)
+{
+}
+
+static long
+status(Uart *uart, void *buf, long n, long offset)
+{
+ char *p;
+
+ p = malloc(READSTR);
+ if(p == nil)
+ error(Enomem);
+ snprint(p, READSTR,
+ "b%d\n"
+ "dev(%d) type(%d) framing(%d) overruns(%d) "
+ "berr(%d) serr(%d)\n",
+
+ uart->baud,
+ uart->dev,
+ uart->type,
+ uart->ferr,
+ uart->oerr,
+ uart->berr,
+ uart->serr
+ );
+ n = readstr(offset, buf, n, p);
+ free(p);
+
+ return n;
+}
+
+static void
+interrupt(Ureg*, void *arg)
+{
+ Uart *uart = arg;
+ u32int v, *regs = (u32int*)uart->regs;
+
+ while((v = regs[URXD]) & RX_CHARRDY)
+ uartrecv(uart, v & RX_DATA);
+
+ uartkick(uart);
+}
+
+static void
+disable(Uart *u)
+{
+ u32int *regs = u->regs;
+ regs[UCR1] = 0;
+}
+
+static void
+enable(Uart *u, int ie)
+{
+ disable(u);
+ if(ie) intrenable(IRQuart1, interrupt, u, BUSUNKNOWN, u->name);
+ config(u);
+}
+
+static void
+donothing(Uart*, int)
+{
+}
+
+static void
+putc(Uart *u, int c)
+{
+ u32int *regs = u->regs;
+
+ while((regs[USR1] & SR1_TRDY) == 0)
+ ;
+ regs[UTXD] = c & TX_DATA;
+}
+
+static int
+getc(Uart *u)
+{
+ u32int c, *regs = (u32int*)u->regs;
+
+ do
+ c = regs[URXD];
+ while((c & RX_CHARRDY) == 0);
+ return c & RX_DATA;
+}
+
+void
+uartconsinit(void)
+{
+ consuart = &uart1;
+ consuart->console = 1;
+ uartctl(consuart, "l8 pn s1");
+ uartputs(kmesg.buf, kmesg.n);
+}
+
+PhysUart imxphysuart = {
+ .name = "imx",
+ .pnp = pnp,
+ .enable = enable,
+ .disable = disable,
+ .kick = kick,
+ .dobreak = dobreak,
+ .baud = baud,
+ .bits = bits,
+ .stop = stop,
+ .parity = parity,
+ .modemctl = donothing,
+ .rts = rts,
+ .dtr = donothing,
+ .status = status,
+ .fifo = donothing,
+ .getc = getc,
+ .putc = putc,
+};
+