plan9front/sys/src/9/bcm/coproc.c

164 lines
3.1 KiB
C

/*
* arm co-processors
* mainly to cope with arm hard-wiring register numbers into instructions.
*
* CP15 (system control) is the one that gets used the most in practice.
*
* 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"
#include "io.h"
#include "arm.h"
enum {
/* alternates: 0xe12fff1e BX (R14); last e is R14 */
/* 0xe28ef000 B 0(R14); second e is R14 (ken) */
Retinst = 0xe1a0f00e, /* MOV R14, R15 */
Opmask = MASK(3),
Regmask = MASK(4),
};
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] = Retinst;
ep[MAXMACH] = ie = ip + 2;
cachedwbse(ip, 2*sizeof(*ip));
Found:
iunlock(&lk);
cacheiinv();
ep[m->machno] = ie;
return ip;
}
static void*
setupcpop(ulong opcode, int cp, int op1, int crn, int crm,
int op2)
{
op1 &= Opmask;
op2 &= Opmask;
crn &= Regmask;
crm &= Regmask;
cp &= Regmask;
return mkinstr(opcode | op1 << 21 | crn << 16 | cp << 8 | op2 << 5 | crm);
}
ulong
cprd(int cp, int op1, int crn, int crm, int op2)
{
/*
* MRC. return value will be in R0, which is convenient.
* Rt will be R0.
*/
ulong (*fp)(void) = setupcpop(0xee100010, cp, op1, crn, crm, op2);
return fp();
}
void
cpwr(int cp, int op1, int crn, int crm, int op2, ulong val)
{
/* MCR, Rt is R0 */
void (*fp)(ulong) = setupcpop(0xee000010, cp, op1, crn, crm, op2);
fp(val);
}
ulong
cprdsc(int op1, int crn, int crm, int op2)
{
return cprd(CpSC, op1, crn, crm, op2);
}
void
cpwrsc(int op1, int crn, int crm, int op2, ulong val)
{
cpwr(CpSC, op1, crn, crm, op2, val);
}
/* floating point */
/* fp coproc control */
static void*
setupfpctlop(int opcode, int fpctlreg)
{
fpctlreg &= Nfpctlregs - 1;
return mkinstr(opcode | fpctlreg << 16 | 0 << 12 | CpFP << 8);
}
ulong
fprd(int fpreg)
{
/*
* VMRS. return value will be in R0, which is convenient.
* Rt will be R0.
*/
ulong (*fp)(void) = setupfpctlop(0xeef00010, fpreg);
return fp();
}
void
fpwr(int fpreg, ulong val)
{
/*
* fpu might be off and this VMSR might enable it
* VMSR, Rt is R0
*/
void (*fp)(ulong) = setupfpctlop(0xeee00010, fpreg);
fp(val);
}
/* fp register access; don't bother with single precision */
static void*
setupfpop(int opcode, int fpreg)
{
ulong wd = opcode | 0 << 16 | (fpreg & (16 - 1)) << 12;
if (fpreg >= 16)
wd |= 1 << 22; /* high bit of dfp reg # */
return mkinstr(wd);
}
ulong
fpsavereg(int fpreg, uvlong *fpp)
{
/*
* VSTR. pointer will be in R0, which is convenient.
* Rt will be R0.
*/
ulong (*fp)(uvlong *) = setupfpop(0xed000000 | CpDFP << 8, fpreg);
return fp(fpp);
}
void
fprestreg(int fpreg, uvlong val)
{
/* VLDR, Rt is R0 */
void (*fp)(uvlong *) = setupfpop(0xed100000 | CpDFP << 8, fpreg);
fp(&val);
}