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
This commit is contained in:
cinap_lenrek 2022-05-08 16:50:29 +00:00
parent 9126ee3eea
commit fff070f2cb
19 changed files with 4740 additions and 0 deletions

212
sys/src/9/imx8/cache.v8.s Normal file
View file

@ -0,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

114
sys/src/9/imx8/clock.c Normal file
View file

@ -0,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);
}

218
sys/src/9/imx8/dat.h Normal file
View file

@ -0,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 */
};

706
sys/src/9/imx8/etherimx.c Normal file
View file

@ -0,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);
}

139
sys/src/9/imx8/fns.h Normal file
View file

@ -0,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);

92
sys/src/9/imx8/fpu.c Normal file
View file

@ -0,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;
}
}

316
sys/src/9/imx8/gic.c Normal file
View file

@ -0,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*)
{
}

4
sys/src/9/imx8/init9.s Normal file
View file

@ -0,0 +1,4 @@
TEXT main(SB), 1, $8
MOV $setSB(SB), R28 /* load the SB */
MOV $boot(SB), R0
B startboot(SB)

31
sys/src/9/imx8/io.h Normal file
View file

@ -0,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)

681
sys/src/9/imx8/l.s Normal file
View file

@ -0,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

218
sys/src/9/imx8/main.c Normal file
View file

@ -0,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);
}

138
sys/src/9/imx8/mem.h Normal file
View file

@ -0,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))

94
sys/src/9/imx8/mkfile Normal file
View file

@ -0,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

492
sys/src/9/imx8/mmu.c Normal file
View file

@ -0,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);
}

39
sys/src/9/imx8/reform Normal file
View file

@ -0,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

58
sys/src/9/imx8/sysreg.c Normal file
View file

@ -0,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);
}

89
sys/src/9/imx8/sysreg.h Normal file
View file

@ -0,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)

716
sys/src/9/imx8/trap.c Normal file
View file

@ -0,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);
}

383
sys/src/9/imx8/uartimx.c Normal file
View file

@ -0,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,
};