Support reading from the arm64 ID registers from userspace.

Trap reads to the arm64 ID registers and write a safe value into them. This
will allow us to put more useful values in these later and have userland
check them to find what features the hardware supports.

These are currently safe defaults, but will later be populated with better
values from the hardware.

Sponsored by:	DARPA, AFRL
Differential Revision:	https://reviews.freebsd.org/D16533
This commit is contained in:
Andrew Turner 2018-08-14 11:00:54 +00:00
parent 98f04e431e
commit 398810619c

View File

@ -53,6 +53,135 @@ struct undef_handler {
*/
LIST_HEAD(, undef_handler) undef_handlers[2];
#define MRS_MASK 0xfff00000
#define MRS_VALUE 0xd5300000
#define MRS_SPECIAL(insn) ((insn) & 0x000fffe0)
#define MRS_REGISTER(insn) ((insn) & 0x0000001f)
#define MRS_Op0_SHIFT 19
#define MRS_Op0_MASK 0x00080000
#define MRS_Op1_SHIFT 16
#define MRS_Op1_MASK 0x00070000
#define MRS_CRn_SHIFT 12
#define MRS_CRn_MASK 0x0000f000
#define MRS_CRm_SHIFT 8
#define MRS_CRm_MASK 0x00000f00
#define MRS_Op2_SHIFT 5
#define MRS_Op2_MASK 0x000000e0
#define MRS_Rt_SHIFT 0
#define MRS_Rt_MASK 0x0000001f
static inline int
mrs_Op0(uint32_t insn)
{
/* op0 is encoded without the top bit in a mrs instruction */
return (2 | ((insn & MRS_Op0_MASK) >> MRS_Op0_SHIFT));
}
#define MRS_GET(op) \
static inline int \
mrs_##op(uint32_t insn) \
{ \
\
return ((insn & MRS_##op##_MASK) >> MRS_##op##_SHIFT); \
}
MRS_GET(Op1)
MRS_GET(CRn)
MRS_GET(CRm)
MRS_GET(Op2)
struct mrs_safe_value {
u_int CRm;
u_int Op2;
uint64_t value;
};
static struct mrs_safe_value safe_values[] = {
{ /* id_aa64pfr0_el1 */
.CRm = 4,
.Op2 = 0,
.value = ID_AA64PFR0_ADV_SIMD_NONE | ID_AA64PFR0_FP_NONE |
ID_AA64PFR0_EL1_64 | ID_AA64PFR0_EL0_64,
},
{ /* id_aa64dfr0_el1 */
.CRm = 5,
.Op2 = 0,
.value = ID_AA64DFR0_DEBUG_VER_8,
},
};
static int
user_mrs_handler(vm_offset_t va, uint32_t insn, struct trapframe *frame,
uint32_t esr)
{
uint64_t value;
int CRm, Op2, i, reg;
if ((insn & MRS_MASK) != MRS_VALUE)
return (0);
/*
* We only emulate Op0 == 3, Op1 == 0, CRn == 0, CRm == {0, 4-7}.
* These are in the EL1 CPU identification space.
* CRm == 0 holds MIDR_EL1, MPIDR_EL1, and REVID_EL1.
* CRm == {4-7} holds the ID_AA64 registers.
*
* For full details see the ARMv8 ARM (ARM DDI 0487C.a)
* Table D9-2 System instruction encodings for non-Debug System
* register accesses.
*/
if (mrs_Op0(insn) != 3 || mrs_Op1(insn) != 0 || mrs_CRn(insn) != 0)
return (0);
CRm = mrs_CRm(insn);
if (CRm > 7 || (CRm < 4 && CRm != 0))
return (0);
Op2 = mrs_Op2(insn);
value = 0;
for (i = 0; i < nitems(safe_values); i++) {
if (safe_values[i].CRm == CRm && safe_values[i].Op2 == Op2) {
value = safe_values[i].value;
break;
}
}
if (CRm == 0) {
switch (Op2) {
case 0:
value = READ_SPECIALREG(midr_el1);
break;
case 5:
value = READ_SPECIALREG(mpidr_el1);
break;
case 6:
value = READ_SPECIALREG(revidr_el1);
break;
default:
return (0);
}
}
/*
* We will handle this instruction, move to the next so we
* don't trap here again.
*/
frame->tf_elr += INSN_SIZE;
reg = MRS_REGISTER(insn);
/* If reg is 31 then write to xzr, i.e. do nothing */
if (reg == 31)
return (1);
if (reg < nitems(frame->tf_x))
frame->tf_x[reg] = value;
else if (reg == 30)
frame->tf_lr = value;
return (1);
}
/*
* Work around a bug in QEMU prior to 2.5.1 where reading unknown ID
* registers would raise an exception when they should return 0.
@ -63,9 +192,6 @@ id_aa64mmfr2_handler(vm_offset_t va, uint32_t insn, struct trapframe *frame,
{
int reg;
#define MRS_MASK 0xfff00000
#define MRS_VALUE 0xd5300000
#define MRS_REGISTER(insn) ((insn) & 0x1f)
#define MRS_ID_AA64MMFR2_EL0_MASK (MRS_MASK | 0x000fffe0)
#define MRS_ID_AA64MMFR2_EL0_VALUE (MRS_VALUE | 0x00080740)
@ -93,6 +219,7 @@ undef_init(void)
LIST_INIT(&undef_handlers[0]);
LIST_INIT(&undef_handlers[1]);
install_undef_handler(true, user_mrs_handler);
install_undef_handler(false, id_aa64mmfr2_handler);
}