Move the undefined instruction handler to identcpu.c so we have access

to the registers from boot.

Approved by:	re (kib)
Sponsored by:	ABT Systems Ltd
Differential Revision:	https://reviews.freebsd.org/D17301
This commit is contained in:
Andrew Turner 2018-09-27 13:50:57 +00:00
parent 3d31df21e7
commit c7637c4d19
3 changed files with 132 additions and 130 deletions

View File

@ -43,6 +43,7 @@ __FBSDID("$FreeBSD$");
#include <machine/atomic.h>
#include <machine/cpu.h>
#include <machine/cpufunc.h>
#include <machine/undefined.h>
static int ident_lock;
@ -162,6 +163,98 @@ const struct cpu_implementers cpu_implementers[] = {
CPU_IMPLEMENTER_NONE,
};
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);
}
static void
identify_cpu_sysinit(void *dummy __unused)
{
@ -170,6 +263,8 @@ identify_cpu_sysinit(void *dummy __unused)
CPU_FOREACH(cpu) {
print_cpu_features(cpu);
}
install_undef_handler(true, user_mrs_handler);
}
SYSINIT(idenrity_cpu, SI_SUB_SMP, SI_ORDER_ANY, identify_cpu_sysinit, NULL);

View File

@ -53,135 +53,6 @@ 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.
@ -219,7 +90,6 @@ 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);
}

View File

@ -36,6 +36,43 @@
typedef int (*undef_handler_t)(vm_offset_t, uint32_t, struct trapframe *,
uint32_t);
#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)
void undef_init(void);
void *install_undef_handler(bool, undef_handler_t);
void remove_undef_handler(void *);