diff --git a/sys/arm/arm/generic_timer.c b/sys/arm/arm/generic_timer.c
index 685398117396..a82f8756b806 100644
--- a/sys/arm/arm/generic_timer.c
+++ b/sys/arm/arm/generic_timer.c
@@ -1,920 +1,920 @@
/*-
* SPDX-License-Identifier: BSD-3-Clause
*
* Copyright (c) 2011 The FreeBSD Foundation
* Copyright (c) 2013 Ruslan Bukin
* All rights reserved.
*
* Based on mpcore_timer.c developed by Ben Gray
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the company nor the name of the author may be used to
* endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/**
* Cortex-A7, Cortex-A15, ARMv8 and later Generic Timer
*/
#include "opt_acpi.h"
#include "opt_platform.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#if defined(__aarch64__)
#include
#include
#include
#endif
#ifdef FDT
#include
#include
#include
#endif
#ifdef DEV_ACPI
#include
#include
#endif
#define GT_PHYS_SECURE 0
#define GT_PHYS_NONSECURE 1
#define GT_VIRT 2
#define GT_HYP_PHYS 3
#define GT_HYP_VIRT 4
#define GT_IRQ_COUNT 5
#define GT_CTRL_ENABLE (1 << 0)
#define GT_CTRL_INT_MASK (1 << 1)
#define GT_CTRL_INT_STAT (1 << 2)
#define GT_REG_CTRL 0
#define GT_REG_TVAL 1
#define GT_CNTKCTL_PL0PTEN (1 << 9) /* PL0 Physical timer reg access */
#define GT_CNTKCTL_PL0VTEN (1 << 8) /* PL0 Virtual timer reg access */
#define GT_CNTKCTL_EVNTI (0xf << 4) /* Virtual counter event bits */
#define GT_CNTKCTL_EVNTDIR (1 << 3) /* Virtual counter event transition */
#define GT_CNTKCTL_EVNTEN (1 << 2) /* Enables virtual counter events */
#define GT_CNTKCTL_PL0VCTEN (1 << 1) /* PL0 CNTVCT and CNTFRQ access */
#define GT_CNTKCTL_PL0PCTEN (1 << 0) /* PL0 CNTPCT and CNTFRQ access */
#if defined(__aarch64__)
static bool __read_mostly enable_wfxt = false;
#endif
struct arm_tmr_softc;
struct arm_tmr_irq {
struct resource *res;
void *ihl;
int rid;
int idx;
};
struct arm_tmr_softc {
struct arm_tmr_irq irqs[GT_IRQ_COUNT];
uint64_t (*get_cntxct)(bool);
uint32_t clkfreq;
int irq_count;
struct eventtimer et;
bool physical_sys;
bool physical_user;
};
static struct arm_tmr_softc *arm_tmr_sc = NULL;
static const struct arm_tmr_irq_defs {
int idx;
const char *name;
int flags;
} arm_tmr_irq_defs[] = {
{
.idx = GT_PHYS_SECURE,
.name = "sec-phys",
.flags = RF_ACTIVE | RF_OPTIONAL,
},
{
.idx = GT_PHYS_NONSECURE,
.name = "phys",
.flags = RF_ACTIVE,
},
{
.idx = GT_VIRT,
.name = "virt",
.flags = RF_ACTIVE,
},
{
.idx = GT_HYP_PHYS,
.name = "hyp-phys",
.flags = RF_ACTIVE | RF_OPTIONAL,
},
{
.idx = GT_HYP_VIRT,
.name = "hyp-virt",
.flags = RF_ACTIVE | RF_OPTIONAL,
},
};
static int arm_tmr_attach(device_t);
static uint32_t arm_tmr_fill_vdso_timehands(struct vdso_timehands *vdso_th,
struct timecounter *tc);
static void arm_tmr_do_delay(int usec, void *);
static timecounter_get_t arm_tmr_get_timecount;
static struct timecounter arm_tmr_timecount = {
.tc_name = "ARM MPCore Timecounter",
.tc_get_timecount = arm_tmr_get_timecount,
.tc_poll_pps = NULL,
.tc_counter_mask = ~0u,
.tc_frequency = 0,
.tc_quality = 1000,
.tc_fill_vdso_timehands = arm_tmr_fill_vdso_timehands,
};
#ifdef __arm__
#define get_el0(x) cp15_## x ##_get()
#define get_el1(x) cp15_## x ##_get()
#define set_el0(x, val) cp15_## x ##_set(val)
#define set_el1(x, val) cp15_## x ##_set(val)
#define HAS_PHYS true
#define IN_VHE false
#else /* __aarch64__ */
#define get_el0(x) READ_SPECIALREG(x ##_el0)
#define get_el1(x) READ_SPECIALREG(x ##_el1)
#define set_el0(x, val) WRITE_SPECIALREG(x ##_el0, val)
#define set_el1(x, val) WRITE_SPECIALREG(x ##_el1, val)
#define HAS_PHYS has_hyp()
#define IN_VHE in_vhe()
#endif
static int
get_freq(void)
{
return (get_el0(cntfrq));
}
#ifdef FDT
static uint64_t
get_cntxct_a64_unstable(bool physical)
{
uint64_t val;
isb();
if (physical) {
do {
val = get_el0(cntpct);
}
while (((val + 1) & 0x7FF) <= 1);
}
else {
do {
val = get_el0(cntvct);
}
while (((val + 1) & 0x7FF) <= 1);
}
return (val);
}
#endif
static uint64_t
get_cntxct(bool physical)
{
uint64_t val;
isb();
if (physical)
val = get_el0(cntpct);
else
val = get_el0(cntvct);
return (val);
}
static int
set_ctrl(uint32_t val, bool physical)
{
if (physical)
set_el0(cntp_ctl, val);
else
set_el0(cntv_ctl, val);
isb();
return (0);
}
static int
set_tval(uint32_t val, bool physical)
{
if (physical)
set_el0(cntp_tval, val);
else
set_el0(cntv_tval, val);
isb();
return (0);
}
static int
get_ctrl(bool physical)
{
uint32_t val;
if (physical)
val = get_el0(cntp_ctl);
else
val = get_el0(cntv_ctl);
return (val);
}
static void
setup_user_access(void *arg __unused)
{
uint32_t cntkctl;
cntkctl = get_el1(cntkctl);
cntkctl &= ~(GT_CNTKCTL_PL0PTEN | GT_CNTKCTL_PL0VTEN |
GT_CNTKCTL_EVNTEN | GT_CNTKCTL_PL0PCTEN);
/* Always enable the virtual timer */
cntkctl |= GT_CNTKCTL_PL0VCTEN;
/* Enable the physical timer if supported */
if (arm_tmr_sc->physical_user) {
cntkctl |= GT_CNTKCTL_PL0PCTEN;
}
set_el1(cntkctl, cntkctl);
isb();
}
#ifdef __aarch64__
static int
cntpct_handler(vm_offset_t va, uint32_t insn, struct trapframe *frame,
uint32_t esr)
{
uint64_t val;
int reg;
if ((insn & MRS_MASK) != MRS_VALUE)
return (0);
if (MRS_SPECIAL(insn) != MRS_SPECIAL(CNTPCT_EL0))
return (0);
reg = MRS_REGISTER(insn);
val = READ_SPECIALREG(cntvct_el0);
if (reg < nitems(frame->tf_x)) {
frame->tf_x[reg] = val;
} else if (reg == 30) {
frame->tf_lr = val;
}
/*
* We will handle this instruction, move to the next so we
* don't trap here again.
*/
frame->tf_elr += INSN_SIZE;
return (1);
}
#endif
static void
tmr_setup_user_access(void *arg __unused)
{
#ifdef __aarch64__
int emulate;
#endif
if (arm_tmr_sc != NULL) {
smp_rendezvous(NULL, setup_user_access, NULL, NULL);
#ifdef __aarch64__
if (TUNABLE_INT_FETCH("hw.emulate_phys_counter", &emulate) &&
emulate != 0) {
- install_undef_handler(true, cntpct_handler);
+ install_undef_handler(cntpct_handler);
}
#endif
}
}
SYSINIT(tmr_ua, SI_SUB_SMP, SI_ORDER_ANY, tmr_setup_user_access, NULL);
static unsigned
arm_tmr_get_timecount(struct timecounter *tc)
{
return (arm_tmr_sc->get_cntxct(arm_tmr_sc->physical_sys));
}
static int
arm_tmr_start(struct eventtimer *et, sbintime_t first,
sbintime_t period __unused)
{
struct arm_tmr_softc *sc;
int counts, ctrl;
sc = (struct arm_tmr_softc *)et->et_priv;
if (first != 0) {
counts = ((uint32_t)et->et_frequency * first) >> 32;
ctrl = get_ctrl(sc->physical_sys);
ctrl &= ~GT_CTRL_INT_MASK;
ctrl |= GT_CTRL_ENABLE;
set_tval(counts, sc->physical_sys);
set_ctrl(ctrl, sc->physical_sys);
return (0);
}
return (EINVAL);
}
static void
arm_tmr_disable(bool physical)
{
int ctrl;
ctrl = get_ctrl(physical);
ctrl &= ~GT_CTRL_ENABLE;
set_ctrl(ctrl, physical);
}
static int
arm_tmr_stop(struct eventtimer *et)
{
struct arm_tmr_softc *sc;
sc = (struct arm_tmr_softc *)et->et_priv;
arm_tmr_disable(sc->physical_sys);
return (0);
}
static int
arm_tmr_intr(void *arg)
{
struct arm_tmr_softc *sc;
int ctrl;
sc = (struct arm_tmr_softc *)arg;
ctrl = get_ctrl(sc->physical_sys);
if (ctrl & GT_CTRL_INT_STAT) {
ctrl |= GT_CTRL_INT_MASK;
set_ctrl(ctrl, sc->physical_sys);
}
if (sc->et.et_active)
sc->et.et_event_cb(&sc->et, sc->et.et_arg);
return (FILTER_HANDLED);
}
static int
arm_tmr_attach_irq(device_t dev, struct arm_tmr_softc *sc,
const struct arm_tmr_irq_defs *irq_def, int rid, int flags)
{
struct arm_tmr_irq *irq;
irq = &sc->irqs[sc->irq_count];
irq->res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
&rid, flags);
if (irq->res == NULL) {
if (bootverbose || (flags & RF_OPTIONAL) == 0) {
device_printf(dev,
"could not allocate irq for %s interrupt '%s'\n",
(flags & RF_OPTIONAL) != 0 ? "optional" :
"required", irq_def->name);
}
if ((flags & RF_OPTIONAL) == 0)
return (ENXIO);
} else {
if (bootverbose)
device_printf(dev, "allocated irq for '%s'\n",
irq_def->name);
irq->rid = rid;
irq->idx = irq_def->idx;
sc->irq_count++;
}
return (0);
}
#ifdef FDT
static int
arm_tmr_fdt_probe(device_t dev)
{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (ofw_bus_is_compatible(dev, "arm,armv8-timer")) {
device_set_desc(dev, "ARMv8 Generic Timer");
return (BUS_PROBE_DEFAULT);
} else if (ofw_bus_is_compatible(dev, "arm,armv7-timer")) {
device_set_desc(dev, "ARMv7 Generic Timer");
return (BUS_PROBE_DEFAULT);
}
return (ENXIO);
}
static int
arm_tmr_fdt_attach(device_t dev)
{
struct arm_tmr_softc *sc;
const struct arm_tmr_irq_defs *irq_def;
size_t i;
phandle_t node;
int error, rid;
bool has_names;
sc = device_get_softc(dev);
node = ofw_bus_get_node(dev);
has_names = OF_hasprop(node, "interrupt-names");
for (i = 0; i < nitems(arm_tmr_irq_defs); i++) {
int flags;
/*
* If we don't have names to go off of, we assume that they're
* in the "usual" order with sec-phys first and allocate by idx.
*/
irq_def = &arm_tmr_irq_defs[i];
rid = irq_def->idx;
flags = irq_def->flags;
if (has_names) {
error = ofw_bus_find_string_index(node,
"interrupt-names", irq_def->name, &rid);
/*
* If we have names, missing a name means we don't
* have it.
*/
if (error != 0) {
/*
* Could be noisy on a lot of platforms for no
* good cause.
*/
if (bootverbose || (flags & RF_OPTIONAL) == 0) {
device_printf(dev,
"could not find irq for %s interrupt '%s'\n",
(flags & RF_OPTIONAL) != 0 ?
"optional" : "required",
irq_def->name);
}
if ((flags & RF_OPTIONAL) == 0)
goto out;
continue;
}
/*
* Warn about failing to activate if we did actually
* have the name present.
*/
flags &= ~RF_OPTIONAL;
}
error = arm_tmr_attach_irq(dev, sc, irq_def, rid, flags);
if (error != 0)
goto out;
}
error = arm_tmr_attach(dev);
out:
if (error != 0) {
for (i = 0; i < sc->irq_count; i++) {
bus_release_resource(dev, SYS_RES_IRQ, sc->irqs[i].rid,
sc->irqs[i].res);
}
}
return (error);
}
#endif
#ifdef DEV_ACPI
static void
arm_tmr_acpi_add_irq(device_t parent, device_t dev, int rid, u_int irq)
{
BUS_SET_RESOURCE(parent, dev, SYS_RES_IRQ, rid, irq, 1);
}
static void
arm_tmr_acpi_identify(driver_t *driver, device_t parent)
{
ACPI_TABLE_GTDT *gtdt;
vm_paddr_t physaddr;
device_t dev;
physaddr = acpi_find_table(ACPI_SIG_GTDT);
if (physaddr == 0)
return;
gtdt = acpi_map_table(physaddr, ACPI_SIG_GTDT);
if (gtdt == NULL) {
device_printf(parent, "gic: Unable to map the GTDT\n");
return;
}
dev = BUS_ADD_CHILD(parent, BUS_PASS_TIMER + BUS_PASS_ORDER_MIDDLE,
"generic_timer", -1);
if (dev == NULL) {
device_printf(parent, "add gic child failed\n");
goto out;
}
arm_tmr_acpi_add_irq(parent, dev, GT_PHYS_SECURE,
gtdt->SecureEl1Interrupt);
arm_tmr_acpi_add_irq(parent, dev, GT_PHYS_NONSECURE,
gtdt->NonSecureEl1Interrupt);
arm_tmr_acpi_add_irq(parent, dev, GT_VIRT,
gtdt->VirtualTimerInterrupt);
arm_tmr_acpi_add_irq(parent, dev, GT_HYP_PHYS,
gtdt->NonSecureEl2Interrupt);
out:
acpi_unmap_table(gtdt);
}
static int
arm_tmr_acpi_probe(device_t dev)
{
device_set_desc(dev, "ARM Generic Timer");
return (BUS_PROBE_NOWILDCARD);
}
static int
arm_tmr_acpi_attach(device_t dev)
{
const struct arm_tmr_irq_defs *irq_def;
struct arm_tmr_softc *sc;
int error;
sc = device_get_softc(dev);
for (int i = 0; i < nitems(arm_tmr_irq_defs); i++) {
irq_def = &arm_tmr_irq_defs[i];
error = arm_tmr_attach_irq(dev, sc, irq_def, irq_def->idx,
irq_def->flags);
if (error != 0)
goto out;
}
error = arm_tmr_attach(dev);
out:
if (error != 0) {
for (int i = 0; i < sc->irq_count; i++) {
bus_release_resource(dev, SYS_RES_IRQ,
sc->irqs[i].rid, sc->irqs[i].res);
}
}
return (error);
}
#endif
static int
arm_tmr_attach(device_t dev)
{
struct arm_tmr_softc *sc;
#ifdef INVARIANTS
const struct arm_tmr_irq_defs *irq_def;
#endif
#ifdef FDT
phandle_t node;
pcell_t clock;
#endif
#ifdef __aarch64__
int user_phys;
#endif
int error;
int i, first_timer, last_timer;
sc = device_get_softc(dev);
if (arm_tmr_sc)
return (ENXIO);
sc->get_cntxct = &get_cntxct;
#ifdef FDT
/* Get the base clock frequency */
node = ofw_bus_get_node(dev);
if (node > 0) {
error = OF_getencprop(node, "clock-frequency", &clock,
sizeof(clock));
if (error > 0)
sc->clkfreq = clock;
if (OF_hasprop(node, "allwinner,sun50i-a64-unstable-timer")) {
sc->get_cntxct = &get_cntxct_a64_unstable;
if (bootverbose)
device_printf(dev,
"Enabling allwinner unstable timer workaround\n");
}
}
#endif
if (sc->clkfreq == 0) {
/* Try to get clock frequency from timer */
sc->clkfreq = get_freq();
}
if (sc->clkfreq == 0) {
device_printf(dev, "No clock frequency specified\n");
return (ENXIO);
}
#ifdef INVARIANTS
/* Confirm that non-optional irqs were allocated before coming in. */
for (i = 0; i < nitems(arm_tmr_irq_defs); i++) {
int j;
irq_def = &arm_tmr_irq_defs[i];
/* Skip optional interrupts */
if ((irq_def->flags & RF_OPTIONAL) != 0)
continue;
for (j = 0; j < sc->irq_count; j++) {
if (sc->irqs[j].idx == irq_def->idx)
break;
}
KASSERT(j < sc->irq_count, ("%s: Missing required interrupt %s",
__func__, irq_def->name));
}
#endif
#ifdef __aarch64__
if (IN_VHE) {
/*
* The kernel is running at EL2. The EL0 timer registers are
* re-mapped to the EL2 version. Because of this we need to
* use the EL2 interrupt.
*/
sc->physical_sys = true;
first_timer = GT_HYP_PHYS;
last_timer = GT_HYP_PHYS;
} else if (!HAS_PHYS) {
/*
* Use the virtual timer when we can't use the hypervisor.
* A hypervisor guest may change the virtual timer registers
* while executing so any use of the virtual timer interrupt
* needs to be coordinated with the virtual machine manager.
*/
sc->physical_sys = false;
first_timer = GT_VIRT;
last_timer = GT_VIRT;
} else
#endif
/* Otherwise set up the secure and non-secure physical timers. */
{
sc->physical_sys = true;
first_timer = GT_PHYS_SECURE;
last_timer = GT_PHYS_NONSECURE;
}
#ifdef __aarch64__
/*
* The virtual timer is always available on arm and arm64, tell
* userspace to use it.
*/
sc->physical_user = false;
/* Allow use of the physical counter in userspace when available */
if (TUNABLE_INT_FETCH("hw.userspace_allow_phys_counter", &user_phys) &&
user_phys != 0)
sc->physical_user = sc->physical_sys;
#else
/*
* The virtual timer depends on setting cntvoff from the hypervisor
* privilege level/el2, however this is only set on arm64.
*/
sc->physical_user = true;
#endif
arm_tmr_sc = sc;
/* Setup secure, non-secure and virtual IRQs handler */
for (i = 0; i < sc->irq_count; i++) {
/* Only enable IRQs on timers we expect to use */
if (sc->irqs[i].idx < first_timer ||
sc->irqs[i].idx > last_timer)
continue;
error = bus_setup_intr(dev, sc->irqs[i].res, INTR_TYPE_CLK,
arm_tmr_intr, NULL, sc, &sc->irqs[i].ihl);
if (error) {
device_printf(dev, "Unable to alloc int resource.\n");
for (int j = 0; j < i; j++)
bus_teardown_intr(dev, sc->irqs[j].res,
&sc->irqs[j].ihl);
return (ENXIO);
}
}
/* Disable the timers until we are ready */
arm_tmr_disable(false);
if (HAS_PHYS)
arm_tmr_disable(true);
arm_tmr_timecount.tc_frequency = sc->clkfreq;
tc_init(&arm_tmr_timecount);
sc->et.et_name = "ARM MPCore Eventtimer";
sc->et.et_flags = ET_FLAGS_ONESHOT | ET_FLAGS_PERCPU;
sc->et.et_quality = 1000;
sc->et.et_frequency = sc->clkfreq;
sc->et.et_min_period = (0x00000010LLU << 32) / sc->et.et_frequency;
sc->et.et_max_period = (0xfffffffeLLU << 32) / sc->et.et_frequency;
sc->et.et_start = arm_tmr_start;
sc->et.et_stop = arm_tmr_stop;
sc->et.et_priv = sc;
et_register(&sc->et);
#if defined(__arm__)
arm_set_delay(arm_tmr_do_delay, sc);
#endif
return (0);
}
#ifdef FDT
static device_method_t arm_tmr_fdt_methods[] = {
DEVMETHOD(device_probe, arm_tmr_fdt_probe),
DEVMETHOD(device_attach, arm_tmr_fdt_attach),
{ 0, 0 }
};
static DEFINE_CLASS_0(generic_timer, arm_tmr_fdt_driver, arm_tmr_fdt_methods,
sizeof(struct arm_tmr_softc));
EARLY_DRIVER_MODULE(timer, simplebus, arm_tmr_fdt_driver, 0, 0,
BUS_PASS_TIMER + BUS_PASS_ORDER_MIDDLE);
EARLY_DRIVER_MODULE(timer, ofwbus, arm_tmr_fdt_driver, 0, 0,
BUS_PASS_TIMER + BUS_PASS_ORDER_MIDDLE);
#endif
#ifdef DEV_ACPI
static device_method_t arm_tmr_acpi_methods[] = {
DEVMETHOD(device_identify, arm_tmr_acpi_identify),
DEVMETHOD(device_probe, arm_tmr_acpi_probe),
DEVMETHOD(device_attach, arm_tmr_acpi_attach),
{ 0, 0 }
};
static DEFINE_CLASS_0(generic_timer, arm_tmr_acpi_driver, arm_tmr_acpi_methods,
sizeof(struct arm_tmr_softc));
EARLY_DRIVER_MODULE(timer, acpi, arm_tmr_acpi_driver, 0, 0,
BUS_PASS_TIMER + BUS_PASS_ORDER_MIDDLE);
#endif
static int64_t
arm_tmr_get_counts(int usec)
{
int64_t counts, counts_per_usec;
/* Get the number of times to count */
counts_per_usec = ((arm_tmr_timecount.tc_frequency / 1000000) + 1);
/*
* Clamp the timeout at a maximum value (about 32 seconds with
* a 66MHz clock). *Nobody* should be delay()ing for anywhere
* near that length of time and if they are, they should be hung
* out to dry.
*/
if (usec >= (0x80000000U / counts_per_usec))
counts = (0x80000000U / counts_per_usec) - 1;
else
counts = usec * counts_per_usec;
return counts;
}
static void
arm_tmr_do_delay(int usec, void *arg)
{
struct arm_tmr_softc *sc = arg;
int64_t counts;
uint64_t first;
#if defined(__aarch64__)
int64_t end;
#endif
counts = arm_tmr_get_counts(usec);
first = sc->get_cntxct(sc->physical_sys);
#if defined(__aarch64__)
end = first + counts;
#endif
while ((sc->get_cntxct(sc->physical_sys) - first) < counts) {
#if defined(__aarch64__)
if (enable_wfxt)
wfet(end);
#endif
}
}
#if defined(__aarch64__)
void
DELAY(int usec)
{
int32_t counts;
TSENTER();
/*
* We have two options for a delay: using the timer, or using the wfet
* instruction. However, both of these are dependent on timers being
* setup, and if they're not just use a loop for the meantime.
*/
if (arm_tmr_sc != NULL) {
arm_tmr_do_delay(usec, arm_tmr_sc);
} else {
for (; usec > 0; usec--)
for (counts = 200; counts > 0; counts--)
/* Prevent the compiler from optimizing out the loop */
cpufunc_nullop();
}
TSEXIT();
}
static bool
wfxt_check(const struct cpu_feat *feat __unused, u_int midr __unused)
{
uint64_t id_aa64isar2;
if (!get_kernel_reg(ID_AA64ISAR2_EL1, &id_aa64isar2))
return (false);
return (ID_AA64ISAR2_WFxT_VAL(id_aa64isar2) != ID_AA64ISAR2_WFxT_NONE);
}
static void
wfxt_enable(const struct cpu_feat *feat __unused,
cpu_feat_errata errata_status __unused, u_int *errata_list __unused,
u_int errata_count __unused)
{
/* will be called if wfxt_check returns true */
enable_wfxt = true;
}
static struct cpu_feat feat_wfxt = {
.feat_name = "FEAT_WFXT",
.feat_check = wfxt_check,
.feat_enable = wfxt_enable,
.feat_flags = CPU_FEAT_AFTER_DEV | CPU_FEAT_SYSTEM,
};
DATA_SET(cpu_feat_set, feat_wfxt);
#endif
static uint32_t
arm_tmr_fill_vdso_timehands(struct vdso_timehands *vdso_th,
struct timecounter *tc)
{
vdso_th->th_algo = VDSO_TH_ALGO_ARM_GENTIM;
vdso_th->th_physical = arm_tmr_sc->physical_user;
bzero(vdso_th->th_res, sizeof(vdso_th->th_res));
return (1);
}
diff --git a/sys/arm64/arm64/identcpu.c b/sys/arm64/arm64/identcpu.c
index 654a1b63d165..663c1a335114 100644
--- a/sys/arm64/arm64/identcpu.c
+++ b/sys/arm64/arm64/identcpu.c
@@ -1,3340 +1,3340 @@
/*-
* Copyright (c) 2014 Andrew Turner
* Copyright (c) 2014 The FreeBSD Foundation
* All rights reserved.
*
* Portions of this software were developed by Semihalf
* under sponsorship of the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
static MALLOC_DEFINE(M_IDENTCPU, "CPU ID", "arm64 CPU identification memory");
struct cpu_desc;
static void print_cpu_midr(struct sbuf *sb, u_int cpu);
static void print_cpu_features(u_int cpu, struct cpu_desc *desc,
struct cpu_desc *prev_desc);
static void print_cpu_caches(struct sbuf *sb, struct cpu_desc *desc);
#ifdef COMPAT_FREEBSD32
static u_long parse_cpu_features_hwcap32(void);
#endif
const char machine[] = "arm64";
#ifdef SCTL_MASK32
extern int adaptive_machine_arch;
#endif
static SYSCTL_NODE(_machdep, OID_AUTO, cache, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
"Cache management tuning");
static int allow_dic = 1;
SYSCTL_INT(_machdep_cache, OID_AUTO, allow_dic, CTLFLAG_RDTUN, &allow_dic, 0,
"Allow optimizations based on the DIC cache bit");
static int allow_idc = 1;
SYSCTL_INT(_machdep_cache, OID_AUTO, allow_idc, CTLFLAG_RDTUN, &allow_idc, 0,
"Allow optimizations based on the IDC cache bit");
static bool emulate_ctr = false;
static void check_cpu_regs(u_int cpu, struct cpu_desc *desc,
struct cpu_desc *prev_desc);
static uint64_t update_special_reg_field(uint64_t user_reg, u_int type,
uint64_t value, u_int width, u_int shift, bool sign);
/*
* The default implementation of I-cache sync assumes we have an
* aliasing cache until we know otherwise.
*/
void (*arm64_icache_sync_range)(void *, vm_size_t) =
&arm64_aliasing_icache_sync_range;
static int
sysctl_hw_machine(SYSCTL_HANDLER_ARGS)
{
#ifdef SCTL_MASK32
static const char machine32[] = "arm";
#endif
int error;
#ifdef SCTL_MASK32
if ((req->flags & SCTL_MASK32) != 0 && adaptive_machine_arch)
error = SYSCTL_OUT(req, machine32, sizeof(machine32));
else
#endif
error = SYSCTL_OUT(req, machine, sizeof(machine));
return (error);
}
SYSCTL_PROC(_hw, HW_MACHINE, machine, CTLTYPE_STRING | CTLFLAG_RD |
CTLFLAG_CAPRD | CTLFLAG_MPSAFE, NULL, 0, sysctl_hw_machine, "A",
"Machine class");
static char cpu_model[64];
SYSCTL_STRING(_hw, HW_MODEL, model, CTLFLAG_RD | CTLFLAG_CAPRD,
cpu_model, sizeof(cpu_model), "Machine model");
#define MAX_CACHES 8 /* Maximum number of caches supported
architecturally. */
/*
* Per-CPU affinity as provided in MPIDR_EL1
* Indexed by CPU number in logical order selected by the system.
* Relevant fields can be extracted using CPU_AFFn macros,
* Aff3.Aff2.Aff1.Aff0 construct a unique CPU address in the system.
*
* Fields used by us:
* Aff1 - Cluster number
* Aff0 - CPU number in Aff1 cluster
*/
uint64_t __cpu_affinity[MAXCPU];
static u_int cpu_aff_levels;
struct cpu_desc {
uint64_t mpidr;
uint64_t id_aa64afr0;
uint64_t id_aa64afr1;
uint64_t id_aa64dfr0;
uint64_t id_aa64dfr1;
uint64_t id_aa64isar0;
uint64_t id_aa64isar1;
uint64_t id_aa64isar2;
uint64_t id_aa64mmfr0;
uint64_t id_aa64mmfr1;
uint64_t id_aa64mmfr2;
uint64_t id_aa64mmfr3;
uint64_t id_aa64mmfr4;
uint64_t id_aa64pfr0;
uint64_t id_aa64pfr1;
uint64_t id_aa64pfr2;
uint64_t id_aa64zfr0;
uint64_t ctr;
#ifdef COMPAT_FREEBSD32
uint64_t id_isar5;
uint64_t mvfr0;
uint64_t mvfr1;
#endif
uint64_t clidr;
uint32_t ccsidr[MAX_CACHES][2]; /* 2 possible types. */
bool have_sve;
};
static struct cpu_desc cpu_desc0;
static struct cpu_desc *cpu_desc;
static struct cpu_desc kern_cpu_desc;
static struct cpu_desc user_cpu_desc;
static struct cpu_desc l_user_cpu_desc;
static struct cpu_desc *
get_cpu_desc(u_int cpu)
{
/* The cpu_desc for CPU 0 is used before the allocator is ready. */
if (cpu == 0)
return (&cpu_desc0);
MPASS(cpu_desc != NULL);
return (&cpu_desc[cpu - 1]);
}
struct cpu_parts {
u_int part_id;
const char *part_name;
};
#define CPU_PART_NONE { 0, NULL }
struct cpu_implementers {
u_int impl_id;
const char *impl_name;
/*
* Part number is implementation defined
* so each vendor will have its own set of values and names.
*/
const struct cpu_parts *cpu_parts;
};
#define CPU_IMPLEMENTER_NONE { 0, NULL, NULL }
/*
* Per-implementer table of (PartNum, CPU Name) pairs.
*/
/* ARM Ltd. */
static const struct cpu_parts cpu_parts_arm[] = {
{ CPU_PART_AEM_V8, "AEMv8" },
{ CPU_PART_FOUNDATION, "Foundation-Model" },
{ CPU_PART_CORTEX_A34, "Cortex-A34" },
{ CPU_PART_CORTEX_A35, "Cortex-A35" },
{ CPU_PART_CORTEX_A53, "Cortex-A53" },
{ CPU_PART_CORTEX_A55, "Cortex-A55" },
{ CPU_PART_CORTEX_A57, "Cortex-A57" },
{ CPU_PART_CORTEX_A65, "Cortex-A65" },
{ CPU_PART_CORTEX_A65AE, "Cortex-A65AE" },
{ CPU_PART_CORTEX_A72, "Cortex-A72" },
{ CPU_PART_CORTEX_A73, "Cortex-A73" },
{ CPU_PART_CORTEX_A75, "Cortex-A75" },
{ CPU_PART_CORTEX_A76, "Cortex-A76" },
{ CPU_PART_CORTEX_A76AE, "Cortex-A76AE" },
{ CPU_PART_CORTEX_A77, "Cortex-A77" },
{ CPU_PART_CORTEX_A78, "Cortex-A78" },
{ CPU_PART_CORTEX_A78C, "Cortex-A78C" },
{ CPU_PART_CORTEX_A510, "Cortex-A510" },
{ CPU_PART_CORTEX_A710, "Cortex-A710" },
{ CPU_PART_CORTEX_A715, "Cortex-A715" },
{ CPU_PART_CORTEX_X1, "Cortex-X1" },
{ CPU_PART_CORTEX_X1C, "Cortex-X1C" },
{ CPU_PART_CORTEX_X2, "Cortex-X2" },
{ CPU_PART_CORTEX_X3, "Cortex-X3" },
{ CPU_PART_NEOVERSE_E1, "Neoverse-E1" },
{ CPU_PART_NEOVERSE_N1, "Neoverse-N1" },
{ CPU_PART_NEOVERSE_N2, "Neoverse-N2" },
{ CPU_PART_NEOVERSE_V1, "Neoverse-V1" },
{ CPU_PART_NEOVERSE_V2, "Neoverse-V2" },
CPU_PART_NONE,
};
/* Cavium */
static const struct cpu_parts cpu_parts_cavium[] = {
{ CPU_PART_THUNDERX, "ThunderX" },
{ CPU_PART_THUNDERX2, "ThunderX2" },
CPU_PART_NONE,
};
/* APM / Ampere */
static const struct cpu_parts cpu_parts_apm[] = {
{ CPU_PART_EMAG8180, "eMAG 8180" },
CPU_PART_NONE,
};
/* Qualcomm */
static const struct cpu_parts cpu_parts_qcom[] = {
{ CPU_PART_KRYO400_GOLD, "Kryo 400 Gold" },
{ CPU_PART_KRYO400_SILVER, "Kryo 400 Silver" },
CPU_PART_NONE,
};
/* Apple */
static const struct cpu_parts cpu_parts_apple[] = {
{ CPU_PART_M1_ICESTORM, "M1 Icestorm" },
{ CPU_PART_M1_FIRESTORM, "M1 Firestorm" },
{ CPU_PART_M1_ICESTORM_PRO, "M1 Pro Icestorm" },
{ CPU_PART_M1_FIRESTORM_PRO, "M1 Pro Firestorm" },
{ CPU_PART_M1_ICESTORM_MAX, "M1 Max Icestorm" },
{ CPU_PART_M1_FIRESTORM_MAX, "M1 Max Firestorm" },
{ CPU_PART_M2_BLIZZARD, "M2 Blizzard" },
{ CPU_PART_M2_AVALANCHE, "M2 Avalanche" },
{ CPU_PART_M2_BLIZZARD_PRO, "M2 Pro Blizzard" },
{ CPU_PART_M2_AVALANCHE_PRO, "M2 Pro Avalanche" },
{ CPU_PART_M2_BLIZZARD_MAX, "M2 Max Blizzard" },
{ CPU_PART_M2_AVALANCHE_MAX, "M2 Max Avalanche" },
CPU_PART_NONE,
};
/* Unknown */
static const struct cpu_parts cpu_parts_none[] = {
CPU_PART_NONE,
};
/*
* Implementers table.
*/
const struct cpu_implementers cpu_implementers[] = {
{ CPU_IMPL_AMPERE, "Ampere", cpu_parts_none },
{ CPU_IMPL_APPLE, "Apple", cpu_parts_apple },
{ CPU_IMPL_APM, "APM", cpu_parts_apm },
{ CPU_IMPL_ARM, "ARM", cpu_parts_arm },
{ CPU_IMPL_BROADCOM, "Broadcom", cpu_parts_none },
{ CPU_IMPL_CAVIUM, "Cavium", cpu_parts_cavium },
{ CPU_IMPL_DEC, "DEC", cpu_parts_none },
{ CPU_IMPL_FREESCALE, "Freescale", cpu_parts_none },
{ CPU_IMPL_FUJITSU, "Fujitsu", cpu_parts_none },
{ CPU_IMPL_INFINEON, "IFX", cpu_parts_none },
{ CPU_IMPL_INTEL, "Intel", cpu_parts_none },
{ CPU_IMPL_MARVELL, "Marvell", cpu_parts_none },
{ CPU_IMPL_NVIDIA, "NVIDIA", cpu_parts_none },
{ CPU_IMPL_QUALCOMM, "Qualcomm", cpu_parts_qcom },
CPU_IMPLEMENTER_NONE,
};
#define MRS_TYPE_MASK 0xf
#define MRS_INVALID 0
#define MRS_EXACT 1
#define MRS_EXACT_IF_DIFFERENT 2
#define MRS_LOWER 3
#define MRS_HIGHER_OR_ZERO 4
#define MRS_HIGHER 5
#define MRS_SAFE_SHIFT 4
#define MRS_SAFE_MASK (0xfu << MRS_SAFE_SHIFT)
#define MRS_SAFE(x) (((x) << MRS_SAFE_SHIFT) & MRS_SAFE_MASK)
#define MRS_SAFE_VAL(x) (((x) & MRS_SAFE_MASK) >> MRS_SAFE_SHIFT)
#define MRS_FREEBSD (1u << 8)
#define MRS_LINUX (1u << 9)
#define MRS_USERSPACE (MRS_FREEBSD | MRS_LINUX)
struct mrs_field_value {
uint64_t value;
const char *desc;
};
#define MRS_FIELD_VALUE(_value, _desc) \
{ \
.value = (_value), \
.desc = (_desc), \
}
#define MRS_FIELD_VALUE_NONE_IMPL(_reg, _field, _none, _impl) \
MRS_FIELD_VALUE(_reg ## _ ## _field ## _ ## _none, ""), \
MRS_FIELD_VALUE(_reg ## _ ## _field ## _ ## _impl, #_field)
#define MRS_FIELD_VALUE_COUNT(_reg, _field, _desc) \
MRS_FIELD_VALUE(0ul << _reg ## _ ## _field ## _SHIFT, "1 " _desc), \
MRS_FIELD_VALUE(1ul << _reg ## _ ## _field ## _SHIFT, "2 " _desc "s"), \
MRS_FIELD_VALUE(2ul << _reg ## _ ## _field ## _SHIFT, "3 " _desc "s"), \
MRS_FIELD_VALUE(3ul << _reg ## _ ## _field ## _SHIFT, "4 " _desc "s"), \
MRS_FIELD_VALUE(4ul << _reg ## _ ## _field ## _SHIFT, "5 " _desc "s"), \
MRS_FIELD_VALUE(5ul << _reg ## _ ## _field ## _SHIFT, "6 " _desc "s"), \
MRS_FIELD_VALUE(6ul << _reg ## _ ## _field ## _SHIFT, "7 " _desc "s"), \
MRS_FIELD_VALUE(7ul << _reg ## _ ## _field ## _SHIFT, "8 " _desc "s"), \
MRS_FIELD_VALUE(8ul << _reg ## _ ## _field ## _SHIFT, "9 " _desc "s"), \
MRS_FIELD_VALUE(9ul << _reg ## _ ## _field ## _SHIFT, "10 "_desc "s"), \
MRS_FIELD_VALUE(10ul<< _reg ## _ ## _field ## _SHIFT, "11 "_desc "s"), \
MRS_FIELD_VALUE(11ul<< _reg ## _ ## _field ## _SHIFT, "12 "_desc "s"), \
MRS_FIELD_VALUE(12ul<< _reg ## _ ## _field ## _SHIFT, "13 "_desc "s"), \
MRS_FIELD_VALUE(13ul<< _reg ## _ ## _field ## _SHIFT, "14 "_desc "s"), \
MRS_FIELD_VALUE(14ul<< _reg ## _ ## _field ## _SHIFT, "15 "_desc "s"), \
MRS_FIELD_VALUE(15ul<< _reg ## _ ## _field ## _SHIFT, "16 "_desc "s")
/*
* Used for printing I/D cache line sizes & CWG/ERG, as 0 is a special case
* in some cases the decoded string needs to be passed in.
*/
#define MRS_FIELD_VALUE_CACHE(_reg, _field, _0desc, _desc) \
MRS_FIELD_VALUE(0ul << _reg ## _ ## _field ## _SHIFT, _0desc), \
MRS_FIELD_VALUE(1ul << _reg ## _ ## _field ## _SHIFT, "8 " _desc), \
MRS_FIELD_VALUE(2ul << _reg ## _ ## _field ## _SHIFT, "16 " _desc), \
MRS_FIELD_VALUE(3ul << _reg ## _ ## _field ## _SHIFT, "32 " _desc), \
MRS_FIELD_VALUE(4ul << _reg ## _ ## _field ## _SHIFT, "64 " _desc), \
MRS_FIELD_VALUE(5ul << _reg ## _ ## _field ## _SHIFT, "128 " _desc), \
MRS_FIELD_VALUE(6ul << _reg ## _ ## _field ## _SHIFT, "256 " _desc), \
MRS_FIELD_VALUE(7ul << _reg ## _ ## _field ## _SHIFT, "512 " _desc), \
MRS_FIELD_VALUE(8ul << _reg ## _ ## _field ## _SHIFT, "1k " _desc), \
MRS_FIELD_VALUE(9ul << _reg ## _ ## _field ## _SHIFT, "2k " _desc), \
MRS_FIELD_VALUE(10ul<< _reg ## _ ## _field ## _SHIFT, "4k " _desc), \
MRS_FIELD_VALUE(11ul<< _reg ## _ ## _field ## _SHIFT, "8k " _desc), \
MRS_FIELD_VALUE(12ul<< _reg ## _ ## _field ## _SHIFT, "16k " _desc), \
MRS_FIELD_VALUE(13ul<< _reg ## _ ## _field ## _SHIFT, "32k " _desc), \
MRS_FIELD_VALUE(14ul<< _reg ## _ ## _field ## _SHIFT, "64k " _desc), \
MRS_FIELD_VALUE(15ul<< _reg ## _ ## _field ## _SHIFT, "128k "_desc)
#define MRS_FIELD_VALUE_END { .desc = NULL }
struct mrs_field_hwcap {
uint64_t min;
u_long hwcap_val;
u_int hwcap_id;
};
#define MRS_HWCAP(_hwcap_id, _val, _min) \
{ \
.hwcap_id = (_hwcap_id), \
.hwcap_val = (_val), \
.min = (_min), \
}
#define MRS_HWCAP_END { .hwcap_id = 0 }
struct mrs_field {
const char *name;
const struct mrs_field_value *values;
const struct mrs_field_hwcap *hwcaps;
uint64_t mask;
bool sign;
u_int type;
u_int width;
u_int shift;
};
#define MRS_FIELD_RES1(_width, _shift) \
{ \
.sign = false, \
.type = MRS_EXACT | MRS_SAFE((1u << (_width)) - 1) | \
MRS_USERSPACE, \
.width = (_width), \
.shift = (_shift), \
}
#define MRS_FIELD_HWCAP(_register, _name, _sign, _type, _visibility, \
_values, _hwcap) \
{ \
.name = #_name, \
.sign = (_sign), \
.type = ((_type) | (_visibility)), \
.width = _register ## _ ## _name ## _WIDTH, \
.shift = _register ## _ ## _name ## _SHIFT, \
.mask = _register ## _ ## _name ## _MASK, \
.values = (_values), \
.hwcaps = (_hwcap), \
}
#define MRS_FIELD(_register, _name, _sign, _type, _visibility, _values) \
MRS_FIELD_HWCAP(_register, _name, _sign, _type, _visibility, \
_values, NULL)
#define MRS_FIELD_END { .type = MRS_INVALID, }
/* CTR_EL0 */
static const struct mrs_field_value ctr_dic[] = {
MRS_FIELD_VALUE_NONE_IMPL(CTR, DIC, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value ctr_idc[] = {
MRS_FIELD_VALUE_NONE_IMPL(CTR, IDC, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value ctr_cwg[] = {
MRS_FIELD_VALUE_CACHE(CTR, CWG, "Unknown CWG",
"byte CWG"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value ctr_erg[] = {
MRS_FIELD_VALUE_CACHE(CTR, ERG, "Unknown ERG",
"byte ERG"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value ctr_dline[] = {
MRS_FIELD_VALUE_CACHE(CTR, DLINE, "4 byte D-cacheline",
"byte D-cacheline"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value ctr_l1ip[] = {
MRS_FIELD_VALUE(CTR_L1IP_VIPT, "VIPT I-cache"),
MRS_FIELD_VALUE(CTR_L1IP_PIPT, "PIPT I-cache"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value ctr_iline[] = {
MRS_FIELD_VALUE_CACHE(CTR, ILINE, "4 byte I-cacheline",
"byte I-cacheline"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field ctr_fields[] = {
/* Bit 31 is RES1 */
MRS_FIELD_RES1(1, 31),
MRS_FIELD(CTR, DIC, false, MRS_LOWER, MRS_USERSPACE, ctr_dic),
MRS_FIELD(CTR, IDC, false, MRS_LOWER, MRS_USERSPACE, ctr_idc),
MRS_FIELD(CTR, CWG, false, MRS_HIGHER_OR_ZERO, MRS_USERSPACE, ctr_cwg),
MRS_FIELD(CTR, ERG, false, MRS_HIGHER_OR_ZERO, MRS_USERSPACE, ctr_erg),
MRS_FIELD(CTR, DLINE, false, MRS_LOWER, MRS_USERSPACE, ctr_dline),
/* If the ICache types are different report the safe option */
MRS_FIELD(CTR, L1IP, false, MRS_EXACT_IF_DIFFERENT |
MRS_SAFE(CTR_L1IP_VIPT >> CTR_L1IP_SHIFT), MRS_USERSPACE,
ctr_l1ip),
MRS_FIELD(CTR, ILINE, false, MRS_LOWER, MRS_USERSPACE, ctr_iline),
MRS_FIELD_END,
};
/* ID_AA64AFR0_EL1 */
static const struct mrs_field id_aa64afr0_fields[] = {
MRS_FIELD_END,
};
/* ID_AA64AFR1_EL1 */
static const struct mrs_field id_aa64afr1_fields[] = {
MRS_FIELD_END,
};
/* ID_AA64DFR0_EL1 */
static const struct mrs_field_value id_aa64dfr0_hpmn0[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64DFR0, HPMN0, CONSTR, DEFINED),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64dfr0_brbe[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64DFR0, BRBE, NONE, IMPL),
MRS_FIELD_VALUE(ID_AA64DFR0_BRBE_EL3, "BRBE EL3"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64dfr0_mtpmu[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64DFR0, MTPMU, NONE, IMPL),
MRS_FIELD_VALUE(ID_AA64DFR0_MTPMU_NONE_MT_RES0, "MTPMU res0"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64dfr0_tracebuffer[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64DFR0, TraceBuffer, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64dfr0_tracefilt[] = {
MRS_FIELD_VALUE(ID_AA64DFR0_TraceFilt_NONE, ""),
MRS_FIELD_VALUE(ID_AA64DFR0_TraceFilt_8_4, "Trace v8.4"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64dfr0_doublelock[] = {
MRS_FIELD_VALUE(ID_AA64DFR0_DoubleLock_IMPL, "DoubleLock"),
MRS_FIELD_VALUE(ID_AA64DFR0_DoubleLock_NONE, ""),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64dfr0_pmsver[] = {
MRS_FIELD_VALUE(ID_AA64DFR0_PMSVer_NONE, ""),
MRS_FIELD_VALUE(ID_AA64DFR0_PMSVer_SPE, "SPE"),
MRS_FIELD_VALUE(ID_AA64DFR0_PMSVer_SPE_1_1, "SPEv1p1"),
MRS_FIELD_VALUE(ID_AA64DFR0_PMSVer_SPE_1_2, "SPEv1p2"),
MRS_FIELD_VALUE(ID_AA64DFR0_PMSVer_SPE_1_3, "SPEv1p3"),
MRS_FIELD_VALUE(ID_AA64DFR0_PMSVer_SPE_1_4, "SPEv1p4"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64dfr0_ctx_cmps[] = {
MRS_FIELD_VALUE_COUNT(ID_AA64DFR0, CTX_CMPs, "CTX BKPT"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64dfr0_wrps[] = {
MRS_FIELD_VALUE_COUNT(ID_AA64DFR0, WRPs, "Watchpoint"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64dfr0_pmss[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64DFR0, PMSS, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64dfr0_brps[] = {
MRS_FIELD_VALUE_COUNT(ID_AA64DFR0, BRPs, "Breakpoint"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64dfr0_pmuver[] = {
MRS_FIELD_VALUE(ID_AA64DFR0_PMUVer_NONE, ""),
MRS_FIELD_VALUE(ID_AA64DFR0_PMUVer_3, "PMUv3"),
MRS_FIELD_VALUE(ID_AA64DFR0_PMUVer_3_1, "PMUv3p1"),
MRS_FIELD_VALUE(ID_AA64DFR0_PMUVer_3_4, "PMUv3p4"),
MRS_FIELD_VALUE(ID_AA64DFR0_PMUVer_3_5, "PMUv3p5"),
MRS_FIELD_VALUE(ID_AA64DFR0_PMUVer_3_7, "PMUv3p7"),
MRS_FIELD_VALUE(ID_AA64DFR0_PMUVer_3_8, "PMUv3p8"),
MRS_FIELD_VALUE(ID_AA64DFR0_PMUVer_3_9, "PMUv3p9"),
MRS_FIELD_VALUE(ID_AA64DFR0_PMUVer_IMPL, "IMPL PMU"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64dfr0_tracever[] = {
MRS_FIELD_VALUE(ID_AA64DFR0_TraceVer_NONE, ""),
MRS_FIELD_VALUE(ID_AA64DFR0_TraceVer_IMPL, "Trace"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64dfr0_debugver[] = {
MRS_FIELD_VALUE(ID_AA64DFR0_DebugVer_8, "Debugv8"),
MRS_FIELD_VALUE(ID_AA64DFR0_DebugVer_8_VHE, "Debugv8_VHE"),
MRS_FIELD_VALUE(ID_AA64DFR0_DebugVer_8_2, "Debugv8p2"),
MRS_FIELD_VALUE(ID_AA64DFR0_DebugVer_8_4, "Debugv8p4"),
MRS_FIELD_VALUE(ID_AA64DFR0_DebugVer_8_8, "Debugv8p8"),
MRS_FIELD_VALUE(ID_AA64DFR0_DebugVer_8_9, "Debugv8p9"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field id_aa64dfr0_fields[] = {
MRS_FIELD(ID_AA64DFR0, HPMN0, false, MRS_LOWER, 0, id_aa64dfr0_hpmn0),
MRS_FIELD(ID_AA64DFR0, BRBE, false, MRS_LOWER, 0, id_aa64dfr0_brbe),
MRS_FIELD(ID_AA64DFR0, MTPMU, true, MRS_LOWER, 0, id_aa64dfr0_mtpmu),
MRS_FIELD(ID_AA64DFR0, TraceBuffer, false, MRS_LOWER, 0,
id_aa64dfr0_tracebuffer),
MRS_FIELD(ID_AA64DFR0, TraceFilt, false, MRS_LOWER, 0,
id_aa64dfr0_tracefilt),
MRS_FIELD(ID_AA64DFR0, DoubleLock, false, MRS_LOWER, 0,
id_aa64dfr0_doublelock),
MRS_FIELD(ID_AA64DFR0, PMSVer, false, MRS_LOWER, 0, id_aa64dfr0_pmsver),
MRS_FIELD(ID_AA64DFR0, CTX_CMPs, false, MRS_LOWER, 0,
id_aa64dfr0_ctx_cmps),
MRS_FIELD(ID_AA64DFR0, WRPs, false, MRS_LOWER, MRS_USERSPACE,
id_aa64dfr0_wrps),
MRS_FIELD(ID_AA64DFR0, PMSS, false, MRS_LOWER, 0, id_aa64dfr0_pmss),
MRS_FIELD(ID_AA64DFR0, BRPs, false, MRS_LOWER, MRS_USERSPACE,
id_aa64dfr0_brps),
MRS_FIELD(ID_AA64DFR0, PMUVer, false, MRS_LOWER, 0, id_aa64dfr0_pmuver),
MRS_FIELD(ID_AA64DFR0, TraceVer, false, MRS_LOWER, 0,
id_aa64dfr0_tracever),
MRS_FIELD(ID_AA64DFR0, DebugVer, false, MRS_LOWER | MRS_SAFE(0x6), 0,
id_aa64dfr0_debugver),
MRS_FIELD_END,
};
/* ID_AA64DFR1_EL1 */
static const struct mrs_field_value id_aa64dfr1_dpfzs[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64DFR1, DPFZS, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64dfr1_pmicntr[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64DFR1, PMICNTR, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64dfr1_spmu[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64DFR1, SPMU, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field id_aa64dfr1_fields[] = {
MRS_FIELD(ID_AA64DFR1, DPFZS, false, MRS_LOWER, 0, id_aa64dfr1_dpfzs),
MRS_FIELD(ID_AA64DFR1, PMICNTR, false, MRS_LOWER, 0, id_aa64dfr1_pmicntr),
MRS_FIELD(ID_AA64DFR1, SPMU, false, MRS_LOWER, 0, id_aa64dfr1_spmu),
MRS_FIELD_END,
};
/* ID_AA64ISAR0_EL1 */
static const struct mrs_field_value id_aa64isar0_rndr[] = {
MRS_FIELD_VALUE(ID_AA64ISAR0_RNDR_NONE, ""),
MRS_FIELD_VALUE(ID_AA64ISAR0_RNDR_IMPL, "RNG"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_aa64isar0_rndr_caps[] = {
MRS_HWCAP(2, HWCAP2_RNG, ID_AA64ISAR0_RNDR_IMPL),
MRS_HWCAP_END
};
static const struct mrs_field_value id_aa64isar0_tlb[] = {
MRS_FIELD_VALUE(ID_AA64ISAR0_TLB_NONE, ""),
MRS_FIELD_VALUE(ID_AA64ISAR0_TLB_TLBIOS, "TLBI-OS"),
MRS_FIELD_VALUE(ID_AA64ISAR0_TLB_TLBIOSR, "TLBI-OSR"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64isar0_ts[] = {
MRS_FIELD_VALUE(ID_AA64ISAR0_TS_NONE, ""),
MRS_FIELD_VALUE(ID_AA64ISAR0_TS_CondM_8_4, "CondM-8.4"),
MRS_FIELD_VALUE(ID_AA64ISAR0_TS_CondM_8_5, "CondM-8.5"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_aa64isar0_ts_caps[] = {
MRS_HWCAP(1, HWCAP_FLAGM, ID_AA64ISAR0_TS_CondM_8_4),
MRS_HWCAP(2, HWCAP2_FLAGM2, ID_AA64ISAR0_TS_CondM_8_5),
MRS_HWCAP_END
};
static const struct mrs_field_value id_aa64isar0_fhm[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64ISAR0, FHM, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_aa64isar0_fhm_caps[] = {
MRS_HWCAP(1, HWCAP_ASIMDFHM, ID_AA64ISAR0_FHM_IMPL),
MRS_HWCAP_END
};
static const struct mrs_field_value id_aa64isar0_dp[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64ISAR0, DP, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_aa64isar0_dp_caps[] = {
MRS_HWCAP(1, HWCAP_ASIMDDP, ID_AA64ISAR0_DP_IMPL),
MRS_HWCAP_END
};
static const struct mrs_field_value id_aa64isar0_sm4[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64ISAR0, SM4, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_aa64isar0_sm4_caps[] = {
MRS_HWCAP(1, HWCAP_SM4, ID_AA64ISAR0_SM4_IMPL),
MRS_HWCAP_END
};
static const struct mrs_field_value id_aa64isar0_sm3[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64ISAR0, SM3, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_aa64isar0_sm3_caps[] = {
MRS_HWCAP(1, HWCAP_SM3, ID_AA64ISAR0_SM3_IMPL),
MRS_HWCAP_END
};
static const struct mrs_field_value id_aa64isar0_sha3[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64ISAR0, SHA3, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_aa64isar0_sha3_caps[] = {
MRS_HWCAP(1, HWCAP_SHA3, ID_AA64ISAR0_SHA3_IMPL),
MRS_HWCAP_END
};
static const struct mrs_field_value id_aa64isar0_rdm[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64ISAR0, RDM, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_aa64isar0_rdm_caps[] = {
MRS_HWCAP(1, HWCAP_ASIMDRDM, ID_AA64ISAR0_RDM_IMPL),
MRS_HWCAP_END
};
static const struct mrs_field_value id_aa64isar0_tme[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64ISAR0, TME, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64isar0_atomic[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64ISAR0, Atomic, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_aa64isar0_atomic_caps[] = {
MRS_HWCAP(1, HWCAP_ATOMICS, ID_AA64ISAR0_Atomic_IMPL),
MRS_HWCAP_END
};
static const struct mrs_field_value id_aa64isar0_crc32[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64ISAR0, CRC32, NONE, BASE),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_aa64isar0_crc32_caps[] = {
MRS_HWCAP(1, HWCAP_CRC32, ID_AA64ISAR0_CRC32_BASE),
MRS_HWCAP_END
};
static const struct mrs_field_value id_aa64isar0_sha2[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64ISAR0, SHA2, NONE, BASE),
MRS_FIELD_VALUE(ID_AA64ISAR0_SHA2_512, "SHA2+SHA512"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_aa64isar0_sha2_caps[] = {
MRS_HWCAP(1, HWCAP_SHA2, ID_AA64ISAR0_SHA2_BASE),
MRS_HWCAP(1, HWCAP_SHA512, ID_AA64ISAR0_SHA2_512),
MRS_HWCAP_END
};
static const struct mrs_field_value id_aa64isar0_sha1[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64ISAR0, SHA1, NONE, BASE),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_aa64isar0_sha1_caps[] = {
MRS_HWCAP(1, HWCAP_SHA1, ID_AA64ISAR0_SHA1_BASE),
MRS_HWCAP_END
};
static const struct mrs_field_value id_aa64isar0_aes[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64ISAR0, AES, NONE, BASE),
MRS_FIELD_VALUE(ID_AA64ISAR0_AES_PMULL, "AES+PMULL"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_aa64isar0_aes_caps[] = {
MRS_HWCAP(1, HWCAP_AES, ID_AA64ISAR0_AES_BASE),
MRS_HWCAP(1, HWCAP_PMULL, ID_AA64ISAR0_AES_PMULL),
MRS_HWCAP_END
};
static const struct mrs_field id_aa64isar0_fields[] = {
MRS_FIELD_HWCAP(ID_AA64ISAR0, RNDR, false, MRS_LOWER, MRS_USERSPACE,
id_aa64isar0_rndr, id_aa64isar0_rndr_caps),
MRS_FIELD(ID_AA64ISAR0, TLB, false, MRS_LOWER, 0, id_aa64isar0_tlb),
MRS_FIELD_HWCAP(ID_AA64ISAR0, TS, false, MRS_LOWER, MRS_USERSPACE,
id_aa64isar0_ts, id_aa64isar0_ts_caps),
MRS_FIELD_HWCAP(ID_AA64ISAR0, FHM, false, MRS_LOWER, MRS_USERSPACE,
id_aa64isar0_fhm, id_aa64isar0_fhm_caps),
MRS_FIELD_HWCAP(ID_AA64ISAR0, DP, false, MRS_LOWER, MRS_USERSPACE,
id_aa64isar0_dp, id_aa64isar0_dp_caps),
MRS_FIELD_HWCAP(ID_AA64ISAR0, SM4, false, MRS_LOWER, MRS_USERSPACE,
id_aa64isar0_sm4, id_aa64isar0_sm4_caps),
MRS_FIELD_HWCAP(ID_AA64ISAR0, SM3, false, MRS_LOWER, MRS_USERSPACE,
id_aa64isar0_sm3, id_aa64isar0_sm3_caps),
MRS_FIELD_HWCAP(ID_AA64ISAR0, SHA3, false, MRS_LOWER, MRS_USERSPACE,
id_aa64isar0_sha3, id_aa64isar0_sha3_caps),
MRS_FIELD_HWCAP(ID_AA64ISAR0, RDM, false, MRS_LOWER, MRS_USERSPACE,
id_aa64isar0_rdm, id_aa64isar0_rdm_caps),
MRS_FIELD(ID_AA64ISAR0, TME, false, MRS_LOWER, 0, id_aa64isar0_tme),
MRS_FIELD_HWCAP(ID_AA64ISAR0, Atomic, false, MRS_LOWER, MRS_USERSPACE,
id_aa64isar0_atomic, id_aa64isar0_atomic_caps),
MRS_FIELD_HWCAP(ID_AA64ISAR0, CRC32, false, MRS_LOWER, MRS_USERSPACE,
id_aa64isar0_crc32, id_aa64isar0_crc32_caps),
MRS_FIELD_HWCAP(ID_AA64ISAR0, SHA2, false, MRS_LOWER, MRS_USERSPACE,
id_aa64isar0_sha2, id_aa64isar0_sha2_caps),
MRS_FIELD_HWCAP(ID_AA64ISAR0, SHA1, false, MRS_LOWER, MRS_USERSPACE,
id_aa64isar0_sha1, id_aa64isar0_sha1_caps),
MRS_FIELD_HWCAP(ID_AA64ISAR0, AES, false, MRS_LOWER, MRS_USERSPACE,
id_aa64isar0_aes, id_aa64isar0_aes_caps),
MRS_FIELD_END,
};
/* ID_AA64ISAR1_EL1 */
static const struct mrs_field_value id_aa64isar1_ls64[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64ISAR1, LS64, NONE, IMPL),
MRS_FIELD_VALUE(ID_AA64ISAR1_LS64_V, "LS64v"),
MRS_FIELD_VALUE(ID_AA64ISAR1_LS64_ACCDATA, "LS64+ACCDATA"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64isar1_xs[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64ISAR1, XS, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64isar1_i8mm[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64ISAR1, I8MM, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_aa64isar1_i8mm_caps[] = {
MRS_HWCAP(2, HWCAP2_I8MM, ID_AA64ISAR1_I8MM_IMPL),
MRS_HWCAP_END
};
static const struct mrs_field_value id_aa64isar1_dgh[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64ISAR1, DGH, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_aa64isar1_dgh_caps[] = {
MRS_HWCAP(2, HWCAP2_DGH, ID_AA64ISAR1_DGH_IMPL),
MRS_HWCAP_END
};
static const struct mrs_field_value id_aa64isar1_bf16[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64ISAR1, BF16, NONE, IMPL),
MRS_FIELD_VALUE(ID_AA64ISAR1_BF16_EBF, "EBF16"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_aa64isar1_bf16_caps[] = {
MRS_HWCAP(2, HWCAP2_BF16, ID_AA64ISAR1_BF16_IMPL),
MRS_HWCAP_END
};
static const struct mrs_field_value id_aa64isar1_specres[] = {
MRS_FIELD_VALUE(ID_AA64ISAR1_SPECRES_NONE, ""),
MRS_FIELD_VALUE(ID_AA64ISAR1_SPECRES_8_5, "PredInv v8.5"),
MRS_FIELD_VALUE(ID_AA64ISAR1_SPECRES_8_9, "PredInv v8.9"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64isar1_sb[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64ISAR1, SB, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_aa64isar1_sb_caps[] = {
MRS_HWCAP(1, HWCAP_SB, ID_AA64ISAR1_SB_IMPL),
MRS_HWCAP_END
};
static const struct mrs_field_value id_aa64isar1_frintts[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64ISAR1, FRINTTS, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_aa64isar1_frintts_caps[] = {
MRS_HWCAP(2, HWCAP2_FRINT, ID_AA64ISAR1_FRINTTS_IMPL),
MRS_HWCAP_END
};
static const struct mrs_field_value id_aa64isar1_gpi[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64ISAR1, GPI, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_aa64isar1_gpi_caps[] = {
MRS_HWCAP(1, HWCAP_PACG, ID_AA64ISAR1_GPI_IMPL),
MRS_HWCAP_END
};
static const struct mrs_field_value id_aa64isar1_gpa[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64ISAR1, GPA, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_aa64isar1_gpa_caps[] = {
MRS_HWCAP(1, HWCAP_PACG, ID_AA64ISAR1_GPA_IMPL),
MRS_HWCAP_END
};
static const struct mrs_field_value id_aa64isar1_lrcpc[] = {
MRS_FIELD_VALUE(ID_AA64ISAR1_LRCPC_NONE, ""),
MRS_FIELD_VALUE(ID_AA64ISAR1_LRCPC_RCPC_8_3, "RCPC-8.3"),
MRS_FIELD_VALUE(ID_AA64ISAR1_LRCPC_RCPC_8_4, "RCPC-8.4"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_aa64isar1_lrcpc_caps[] = {
MRS_HWCAP(1, HWCAP_LRCPC, ID_AA64ISAR1_LRCPC_RCPC_8_3),
MRS_HWCAP(1, HWCAP_ILRCPC, ID_AA64ISAR1_LRCPC_RCPC_8_4),
MRS_HWCAP_END
};
static const struct mrs_field_value id_aa64isar1_fcma[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64ISAR1, FCMA, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_aa64isar1_fcma_caps[] = {
MRS_HWCAP(1, HWCAP_FCMA, ID_AA64ISAR1_FCMA_IMPL),
MRS_HWCAP_END
};
static const struct mrs_field_value id_aa64isar1_jscvt[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64ISAR1, JSCVT, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_aa64isar1_jscvt_caps[] = {
MRS_HWCAP(1, HWCAP_JSCVT, ID_AA64ISAR1_JSCVT_IMPL),
MRS_HWCAP_END
};
static const struct mrs_field_value id_aa64isar1_api[] = {
MRS_FIELD_VALUE(ID_AA64ISAR1_API_NONE, ""),
MRS_FIELD_VALUE(ID_AA64ISAR1_API_PAC, "API PAC"),
MRS_FIELD_VALUE(ID_AA64ISAR1_API_EPAC, "API EPAC"),
MRS_FIELD_VALUE(ID_AA64ISAR1_API_EPAC2, "Impl PAuth+EPAC2"),
MRS_FIELD_VALUE(ID_AA64ISAR1_API_FPAC, "Impl PAuth+FPAC"),
MRS_FIELD_VALUE(ID_AA64ISAR1_API_FPAC_COMBINED,
"Impl PAuth+FPAC+Combined"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_aa64isar1_api_caps[] = {
MRS_HWCAP(1, HWCAP_PACA, ID_AA64ISAR1_API_PAC),
MRS_HWCAP_END
};
static const struct mrs_field_value id_aa64isar1_apa[] = {
MRS_FIELD_VALUE(ID_AA64ISAR1_APA_NONE, ""),
MRS_FIELD_VALUE(ID_AA64ISAR1_APA_PAC, "APA PAC"),
MRS_FIELD_VALUE(ID_AA64ISAR1_APA_EPAC, "APA EPAC"),
MRS_FIELD_VALUE(ID_AA64ISAR1_APA_EPAC2, "APA EPAC2"),
MRS_FIELD_VALUE(ID_AA64ISAR1_APA_FPAC, "APA FPAC"),
MRS_FIELD_VALUE(ID_AA64ISAR1_APA_FPAC_COMBINED,
"APA FPAC+Combined"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_aa64isar1_apa_caps[] = {
MRS_HWCAP(1, HWCAP_PACA, ID_AA64ISAR1_APA_PAC),
MRS_HWCAP_END
};
static const struct mrs_field_value id_aa64isar1_dpb[] = {
MRS_FIELD_VALUE(ID_AA64ISAR1_DPB_NONE, ""),
MRS_FIELD_VALUE(ID_AA64ISAR1_DPB_DCCVAP, "DCPoP"),
MRS_FIELD_VALUE(ID_AA64ISAR1_DPB_DCCVADP, "DCCVADP"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_aa64isar1_dpb_caps[] = {
MRS_HWCAP(1, HWCAP_DCPOP, ID_AA64ISAR1_DPB_DCCVAP),
MRS_HWCAP(2, HWCAP2_DCPODP, ID_AA64ISAR1_DPB_DCCVADP),
MRS_HWCAP_END
};
static const struct mrs_field id_aa64isar1_fields[] = {
MRS_FIELD(ID_AA64ISAR1, LS64, false, MRS_LOWER, 0, id_aa64isar1_ls64),
MRS_FIELD(ID_AA64ISAR1, XS, false, MRS_LOWER, 0, id_aa64isar1_xs),
MRS_FIELD_HWCAP(ID_AA64ISAR1, I8MM, false, MRS_LOWER, MRS_USERSPACE,
id_aa64isar1_i8mm, id_aa64isar1_i8mm_caps),
MRS_FIELD_HWCAP(ID_AA64ISAR1, DGH, false, MRS_LOWER, MRS_USERSPACE,
id_aa64isar1_dgh, id_aa64isar1_dgh_caps),
MRS_FIELD_HWCAP(ID_AA64ISAR1, BF16, false, MRS_LOWER, MRS_USERSPACE,
id_aa64isar1_bf16, id_aa64isar1_bf16_caps),
MRS_FIELD(ID_AA64ISAR1, SPECRES, false, MRS_LOWER, 0,
id_aa64isar1_specres),
MRS_FIELD_HWCAP(ID_AA64ISAR1, SB, false, MRS_LOWER, MRS_USERSPACE,
id_aa64isar1_sb, id_aa64isar1_sb_caps),
MRS_FIELD_HWCAP(ID_AA64ISAR1, FRINTTS, false, MRS_LOWER, MRS_USERSPACE,
id_aa64isar1_frintts, id_aa64isar1_frintts_caps),
MRS_FIELD_HWCAP(ID_AA64ISAR1, GPI, false, MRS_LOWER, 0,
id_aa64isar1_gpi, id_aa64isar1_gpi_caps),
MRS_FIELD_HWCAP(ID_AA64ISAR1, GPA, false, MRS_LOWER, 0,
id_aa64isar1_gpa, id_aa64isar1_gpa_caps),
MRS_FIELD_HWCAP(ID_AA64ISAR1, LRCPC, false, MRS_LOWER, MRS_USERSPACE,
id_aa64isar1_lrcpc, id_aa64isar1_lrcpc_caps),
MRS_FIELD_HWCAP(ID_AA64ISAR1, FCMA, false, MRS_LOWER, MRS_USERSPACE,
id_aa64isar1_fcma, id_aa64isar1_fcma_caps),
MRS_FIELD_HWCAP(ID_AA64ISAR1, JSCVT, false, MRS_LOWER, MRS_USERSPACE,
id_aa64isar1_jscvt, id_aa64isar1_jscvt_caps),
MRS_FIELD_HWCAP(ID_AA64ISAR1, API, false, MRS_LOWER, 0,
id_aa64isar1_api, id_aa64isar1_api_caps),
MRS_FIELD_HWCAP(ID_AA64ISAR1, APA, false, MRS_LOWER, 0,
id_aa64isar1_apa, id_aa64isar1_apa_caps),
MRS_FIELD_HWCAP(ID_AA64ISAR1, DPB, false, MRS_LOWER, MRS_USERSPACE,
id_aa64isar1_dpb, id_aa64isar1_dpb_caps),
MRS_FIELD_END,
};
/* ID_AA64ISAR2_EL1 */
static const struct mrs_field_value id_aa64isar2_ats1a[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64ISAR2, ATS1A, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64isar2_cssc[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64ISAR2, CSSC, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64isar2_rprfm[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64ISAR2, RPRFM, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64isar2_prfmslc[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64ISAR2, PRFMSLC, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64isar2_clrbhb[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64ISAR2, CLRBHB, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64isar2_pac_frac[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64ISAR2, PAC_frac, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64isar2_bc[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64ISAR2, BC, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64isar2_mops[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64ISAR2, MOPS, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64isar2_apa3[] = {
MRS_FIELD_VALUE(ID_AA64ISAR2_APA3_NONE, ""),
MRS_FIELD_VALUE(ID_AA64ISAR2_APA3_PAC, "APA3 PAC"),
MRS_FIELD_VALUE(ID_AA64ISAR2_APA3_EPAC, "APA3 EPAC"),
MRS_FIELD_VALUE(ID_AA64ISAR2_APA3_EPAC2, "APA3 EPAC2"),
MRS_FIELD_VALUE(ID_AA64ISAR2_APA3_FPAC, "APA3 FPAC"),
MRS_FIELD_VALUE(ID_AA64ISAR2_APA3_FPAC_COMBINED,
"APA3 FPAC+Combined"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_aa64isar2_apa3_caps[] = {
MRS_HWCAP(1, HWCAP_PACA, ID_AA64ISAR2_APA3_PAC),
MRS_HWCAP_END
};
static const struct mrs_field_value id_aa64isar2_gpa3[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64ISAR2, GPA3, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_aa64isar2_gpa3_caps[] = {
MRS_HWCAP(1, HWCAP_PACG, ID_AA64ISAR2_GPA3_IMPL),
MRS_HWCAP_END
};
static const struct mrs_field_value id_aa64isar2_rpres[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64ISAR2, RPRES, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_aa64isar2_rpres_caps[] = {
MRS_HWCAP(2, HWCAP2_RPRES, ID_AA64ISAR2_RPRES_IMPL),
MRS_HWCAP_END
};
static const struct mrs_field_value id_aa64isar2_wfxt[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64ISAR2, WFxT, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_aa64isar2_wfxt_caps[] = {
MRS_HWCAP(2, HWCAP2_WFXT, ID_AA64ISAR2_WFxT_IMPL),
MRS_HWCAP_END
};
static const struct mrs_field id_aa64isar2_fields[] = {
MRS_FIELD(ID_AA64ISAR2, ATS1A, false, MRS_LOWER, 0, id_aa64isar2_ats1a),
MRS_FIELD(ID_AA64ISAR2, CSSC, false, MRS_LOWER, 0, id_aa64isar2_cssc),
MRS_FIELD(ID_AA64ISAR2, RPRFM, false, MRS_LOWER, 0, id_aa64isar2_rprfm),
MRS_FIELD(ID_AA64ISAR2, PRFMSLC, false, MRS_LOWER, 0, id_aa64isar2_prfmslc),
MRS_FIELD(ID_AA64ISAR2, CLRBHB, false, MRS_LOWER, 0, id_aa64isar2_clrbhb),
MRS_FIELD(ID_AA64ISAR2, PAC_frac, false, MRS_LOWER, 0,
id_aa64isar2_pac_frac),
MRS_FIELD(ID_AA64ISAR2, BC, false, MRS_LOWER, 0, id_aa64isar2_bc),
MRS_FIELD(ID_AA64ISAR2, MOPS, false, MRS_LOWER, 0, id_aa64isar2_mops),
MRS_FIELD_HWCAP(ID_AA64ISAR2, APA3, false, MRS_LOWER, 0,
id_aa64isar2_apa3, id_aa64isar2_apa3_caps),
MRS_FIELD_HWCAP(ID_AA64ISAR2, GPA3, false, MRS_LOWER, 0,
id_aa64isar2_gpa3, id_aa64isar2_gpa3_caps),
MRS_FIELD_HWCAP(ID_AA64ISAR2, RPRES, false, MRS_LOWER, MRS_USERSPACE,
id_aa64isar2_rpres, id_aa64isar2_rpres_caps),
MRS_FIELD_HWCAP(ID_AA64ISAR2, WFxT, false, MRS_LOWER, 0,
id_aa64isar2_wfxt, id_aa64isar2_wfxt_caps),
MRS_FIELD_END,
};
/* ID_AA64MMFR0_EL1 */
static const struct mrs_field_value id_aa64mmfr0_ecv[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64MMFR0, ECV, NONE, IMPL),
MRS_FIELD_VALUE(ID_AA64MMFR0_ECV_CNTHCTL, "ECV+CNTHCTL"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr0_fgt[] = {
MRS_FIELD_VALUE(ID_AA64MMFR0_FGT_NONE, ""),
MRS_FIELD_VALUE(ID_AA64MMFR0_FGT_8_6, "FGT v8.6"),
MRS_FIELD_VALUE(ID_AA64MMFR0_FGT_8_9, "FGT v8.9"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr0_exs[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64MMFR0, ExS, ALL, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr0_tgran4_2[] = {
MRS_FIELD_VALUE(ID_AA64MMFR0_TGran4_2_TGran4, ""),
MRS_FIELD_VALUE(ID_AA64MMFR0_TGran4_2_NONE, "No S2 TGran4"),
MRS_FIELD_VALUE(ID_AA64MMFR0_TGran4_2_IMPL, "S2 TGran4"),
MRS_FIELD_VALUE(ID_AA64MMFR0_TGran4_2_LPA2, "S2 TGran4+LPA2"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr0_tgran64_2[] = {
MRS_FIELD_VALUE(ID_AA64MMFR0_TGran64_2_TGran64, ""),
MRS_FIELD_VALUE(ID_AA64MMFR0_TGran64_2_NONE, "No S2 TGran64"),
MRS_FIELD_VALUE(ID_AA64MMFR0_TGran64_2_IMPL, "S2 TGran64"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr0_tgran16_2[] = {
MRS_FIELD_VALUE(ID_AA64MMFR0_TGran16_2_TGran16, ""),
MRS_FIELD_VALUE(ID_AA64MMFR0_TGran16_2_NONE, "No S2 TGran16"),
MRS_FIELD_VALUE(ID_AA64MMFR0_TGran16_2_IMPL, "S2 TGran16"),
MRS_FIELD_VALUE(ID_AA64MMFR0_TGran16_2_LPA2, "S2 TGran16+LPA2"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr0_tgran4[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64MMFR0, TGran4, NONE, IMPL),
MRS_FIELD_VALUE(ID_AA64MMFR0_TGran4_LPA2, "TGran4+LPA2"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr0_tgran64[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64MMFR0, TGran64, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr0_tgran16[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64MMFR0, TGran16, NONE, IMPL),
MRS_FIELD_VALUE(ID_AA64MMFR0_TGran16_LPA2, "TGran16+LPA2"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr0_bigendel0[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64MMFR0, BigEndEL0, FIXED, MIXED),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr0_snsmem[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64MMFR0, SNSMem, NONE, DISTINCT),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr0_bigend[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64MMFR0, BigEnd, FIXED, MIXED),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr0_asidbits[] = {
MRS_FIELD_VALUE(ID_AA64MMFR0_ASIDBits_8, "8bit ASID"),
MRS_FIELD_VALUE(ID_AA64MMFR0_ASIDBits_16, "16bit ASID"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr0_parange[] = {
MRS_FIELD_VALUE(ID_AA64MMFR0_PARange_4G, "4GB PA"),
MRS_FIELD_VALUE(ID_AA64MMFR0_PARange_64G, "64GB PA"),
MRS_FIELD_VALUE(ID_AA64MMFR0_PARange_1T, "1TB PA"),
MRS_FIELD_VALUE(ID_AA64MMFR0_PARange_4T, "4TB PA"),
MRS_FIELD_VALUE(ID_AA64MMFR0_PARange_16T, "16TB PA"),
MRS_FIELD_VALUE(ID_AA64MMFR0_PARange_256T, "256TB PA"),
MRS_FIELD_VALUE(ID_AA64MMFR0_PARange_4P, "4PB PA"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field id_aa64mmfr0_fields[] = {
MRS_FIELD(ID_AA64MMFR0, ECV, false, MRS_LOWER, 0, id_aa64mmfr0_ecv),
MRS_FIELD(ID_AA64MMFR0, FGT, false, MRS_LOWER, 0, id_aa64mmfr0_fgt),
MRS_FIELD(ID_AA64MMFR0, ExS, false, MRS_LOWER, 0, id_aa64mmfr0_exs),
MRS_FIELD(ID_AA64MMFR0, TGran4_2, false, MRS_LOWER, 0,
id_aa64mmfr0_tgran4_2),
MRS_FIELD(ID_AA64MMFR0, TGran64_2, false, MRS_LOWER, 0,
id_aa64mmfr0_tgran64_2),
MRS_FIELD(ID_AA64MMFR0, TGran16_2, false, MRS_LOWER, 0,
id_aa64mmfr0_tgran16_2),
MRS_FIELD(ID_AA64MMFR0, TGran4, false, MRS_LOWER, 0,
id_aa64mmfr0_tgran4),
MRS_FIELD(ID_AA64MMFR0, TGran64, false, MRS_LOWER, 0,
id_aa64mmfr0_tgran64),
MRS_FIELD(ID_AA64MMFR0, TGran16, false, MRS_LOWER, 0,
id_aa64mmfr0_tgran16),
MRS_FIELD(ID_AA64MMFR0, BigEndEL0, false, MRS_LOWER, 0,
id_aa64mmfr0_bigendel0),
MRS_FIELD(ID_AA64MMFR0, SNSMem, false, MRS_LOWER, 0,
id_aa64mmfr0_snsmem),
MRS_FIELD(ID_AA64MMFR0, BigEnd, false, MRS_LOWER, 0,
id_aa64mmfr0_bigend),
MRS_FIELD(ID_AA64MMFR0, ASIDBits, false, MRS_LOWER, 0,
id_aa64mmfr0_asidbits),
MRS_FIELD(ID_AA64MMFR0, PARange, false, MRS_LOWER, 0,
id_aa64mmfr0_parange),
MRS_FIELD_END,
};
/* ID_AA64MMFR1_EL1 */
static const struct mrs_field_value id_aa64mmfr1_ecbhb[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64MMFR1, ECBHB, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr1_cmovw[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64MMFR1, CMOVW, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr1_tidcp1[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64MMFR1, TIDCP1, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr1_ntlbpa[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64MMFR1, nTLBPA, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr1_afp[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64MMFR1, AFP, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_aa64mmfr1_afp_caps[] = {
MRS_HWCAP(2, HWCAP2_AFP, ID_AA64MMFR1_AFP_IMPL),
MRS_HWCAP_END
};
static const struct mrs_field_value id_aa64mmfr1_hcx[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64MMFR1, HCX, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr1_ets[] = {
MRS_FIELD_VALUE(ID_AA64MMFR1_ETS_NONE, ""),
MRS_FIELD_VALUE(ID_AA64MMFR1_ETS_NONE2, ""),
MRS_FIELD_VALUE(ID_AA64MMFR1_ETS_IMPL, "ETS2"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr1_twed[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64MMFR1, TWED, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr1_xnx[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64MMFR1, XNX, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr1_specsei[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64MMFR1, SpecSEI, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr1_pan[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64MMFR1, PAN, NONE, IMPL),
MRS_FIELD_VALUE(ID_AA64MMFR1_PAN_ATS1E1, "PAN+ATS1E1"),
MRS_FIELD_VALUE(ID_AA64MMFR1_PAN_EPAN, "EPAN"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr1_lo[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64MMFR1, LO, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr1_hpds[] = {
MRS_FIELD_VALUE(ID_AA64MMFR1_HPDS_NONE, ""),
MRS_FIELD_VALUE(ID_AA64MMFR1_HPDS_HPD, "HPD"),
MRS_FIELD_VALUE(ID_AA64MMFR1_HPDS_TTPBHA, "HPD+TTPBHA"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr1_vh[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64MMFR1, VH, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr1_vmidbits[] = {
MRS_FIELD_VALUE(ID_AA64MMFR1_VMIDBits_8, "8bit VMID"),
MRS_FIELD_VALUE(ID_AA64MMFR1_VMIDBits_16, "16bit VMID"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr1_hafdbs[] = {
MRS_FIELD_VALUE(ID_AA64MMFR1_HAFDBS_NONE, ""),
MRS_FIELD_VALUE(ID_AA64MMFR1_HAFDBS_AF, "HAF"),
MRS_FIELD_VALUE(ID_AA64MMFR1_HAFDBS_AF_DBS, "HAF+DS"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field id_aa64mmfr1_fields[] = {
MRS_FIELD(ID_AA64MMFR1, ECBHB, false, MRS_LOWER, 0, id_aa64mmfr1_ecbhb),
MRS_FIELD(ID_AA64MMFR1, CMOVW, false, MRS_LOWER, 0, id_aa64mmfr1_cmovw),
MRS_FIELD(ID_AA64MMFR1, TIDCP1, false, MRS_LOWER, 0,
id_aa64mmfr1_tidcp1),
MRS_FIELD(ID_AA64MMFR1, nTLBPA, false, MRS_LOWER, 0,
id_aa64mmfr1_ntlbpa),
MRS_FIELD_HWCAP(ID_AA64MMFR1, AFP, false, MRS_LOWER, 0,
id_aa64mmfr1_afp, id_aa64mmfr1_afp_caps),
MRS_FIELD(ID_AA64MMFR1, HCX, false, MRS_LOWER, 0, id_aa64mmfr1_hcx),
MRS_FIELD(ID_AA64MMFR1, ETS, false, MRS_LOWER, 0, id_aa64mmfr1_ets),
MRS_FIELD(ID_AA64MMFR1, TWED, false, MRS_LOWER, 0, id_aa64mmfr1_twed),
MRS_FIELD(ID_AA64MMFR1, XNX, false, MRS_LOWER, 0, id_aa64mmfr1_xnx),
/*
* SpecSEI != 0 indicates the CPU might generate an external abort
* under speculation, while 0 indicates it can't happen. It's safer
* to incorrectly indicate it might happen when it can't rather than
* say it can't happen when it could. As such use the largest value
* found in the system.
*/
MRS_FIELD(ID_AA64MMFR1, SpecSEI, false, MRS_HIGHER, 0,
id_aa64mmfr1_specsei),
MRS_FIELD(ID_AA64MMFR1, PAN, false, MRS_LOWER, 0, id_aa64mmfr1_pan),
MRS_FIELD(ID_AA64MMFR1, LO, false, MRS_LOWER, 0, id_aa64mmfr1_lo),
MRS_FIELD(ID_AA64MMFR1, HPDS, false, MRS_LOWER, 0, id_aa64mmfr1_hpds),
MRS_FIELD(ID_AA64MMFR1, VH, false, MRS_LOWER, 0, id_aa64mmfr1_vh),
MRS_FIELD(ID_AA64MMFR1, VMIDBits, false, MRS_LOWER, 0,
id_aa64mmfr1_vmidbits),
MRS_FIELD(ID_AA64MMFR1, HAFDBS, false, MRS_LOWER, 0, id_aa64mmfr1_hafdbs),
MRS_FIELD_END,
};
/* ID_AA64MMFR2_EL1 */
static const struct mrs_field_value id_aa64mmfr2_e0pd[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64MMFR2, E0PD, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr2_evt[] = {
MRS_FIELD_VALUE(ID_AA64MMFR2_EVT_NONE, ""),
MRS_FIELD_VALUE(ID_AA64MMFR2_EVT_8_2, "EVT-8.2"),
MRS_FIELD_VALUE(ID_AA64MMFR2_EVT_8_5, "EVT-8.5"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr2_bbm[] = {
MRS_FIELD_VALUE(ID_AA64MMFR2_BBM_LEVEL0, ""),
MRS_FIELD_VALUE(ID_AA64MMFR2_BBM_LEVEL1, "BBM level 1"),
MRS_FIELD_VALUE(ID_AA64MMFR2_BBM_LEVEL2, "BBM level 2"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr2_ttl[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64MMFR2, TTL, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr2_fwb[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64MMFR2, FWB, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr2_ids[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64MMFR2, IDS, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr2_at[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64MMFR2, AT, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_aa64mmfr2_at_caps[] = {
MRS_HWCAP(1, HWCAP_USCAT, ID_AA64MMFR2_AT_IMPL),
MRS_HWCAP_END
};
static const struct mrs_field_value id_aa64mmfr2_st[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64MMFR2, ST, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr2_nv[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64MMFR2, NV, NONE, 8_3),
MRS_FIELD_VALUE(ID_AA64MMFR2_NV_8_4, "NV v8.4"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr2_ccidx[] = {
MRS_FIELD_VALUE(ID_AA64MMFR2_CCIDX_32, "32bit CCIDX"),
MRS_FIELD_VALUE(ID_AA64MMFR2_CCIDX_64, "64bit CCIDX"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr2_varange[] = {
MRS_FIELD_VALUE(ID_AA64MMFR2_VARange_48, "48bit VA"),
MRS_FIELD_VALUE(ID_AA64MMFR2_VARange_52, "52bit VA"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr2_iesb[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64MMFR2, IESB, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr2_lsm[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64MMFR2, LSM, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr2_uao[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64MMFR2, UAO, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr2_cnp[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64MMFR2, CnP, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field id_aa64mmfr2_fields[] = {
MRS_FIELD(ID_AA64MMFR2, E0PD, false, MRS_LOWER, 0, id_aa64mmfr2_e0pd),
MRS_FIELD(ID_AA64MMFR2, EVT, false, MRS_LOWER, 0, id_aa64mmfr2_evt),
MRS_FIELD(ID_AA64MMFR2, BBM, false, MRS_LOWER, 0, id_aa64mmfr2_bbm),
MRS_FIELD(ID_AA64MMFR2, TTL, false, MRS_LOWER, 0, id_aa64mmfr2_ttl),
MRS_FIELD(ID_AA64MMFR2, FWB, false, MRS_LOWER, 0, id_aa64mmfr2_fwb),
MRS_FIELD(ID_AA64MMFR2, IDS, false, MRS_LOWER, 0, id_aa64mmfr2_ids),
MRS_FIELD_HWCAP(ID_AA64MMFR2, AT, false, MRS_LOWER, MRS_USERSPACE,
id_aa64mmfr2_at, id_aa64mmfr2_at_caps),
MRS_FIELD(ID_AA64MMFR2, ST, false, MRS_LOWER, 0, id_aa64mmfr2_st),
MRS_FIELD(ID_AA64MMFR2, NV, false, MRS_LOWER, 0, id_aa64mmfr2_nv),
MRS_FIELD(ID_AA64MMFR2, CCIDX, false, MRS_LOWER, 0, id_aa64mmfr2_ccidx),
MRS_FIELD(ID_AA64MMFR2, VARange, false, MRS_LOWER, 0,
id_aa64mmfr2_varange),
MRS_FIELD(ID_AA64MMFR2, IESB, false, MRS_LOWER, 0, id_aa64mmfr2_iesb),
MRS_FIELD(ID_AA64MMFR2, LSM, false, MRS_LOWER, 0, id_aa64mmfr2_lsm),
MRS_FIELD(ID_AA64MMFR2, UAO, false, MRS_LOWER, 0, id_aa64mmfr2_uao),
MRS_FIELD(ID_AA64MMFR2, CnP, false, MRS_LOWER, 0, id_aa64mmfr2_cnp),
MRS_FIELD_END,
};
/* ID_AA64MMFR2_EL1 */
static const struct mrs_field_value id_aa64mmfr3_spec_fpacc[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64MMFR3, Spec_FPACC, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr3_aderr[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64MMFR3, ADERR, NONE, SOME),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr3_sderr[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64MMFR3, SDERR, NONE, ALL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr3_anerr[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64MMFR3, ANERR, NONE, SOME),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr3_snerr[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64MMFR3, SNERR, NONE, ALL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr3_mec[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64MMFR3, MEC, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr3_aie[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64MMFR3, AIE, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr3_s2poe[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64MMFR3, S2POE, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr3_s1poe[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64MMFR3, S1POE, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr3_s2pie[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64MMFR3, S2PIE, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr3_s1pie[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64MMFR3, S1PIE, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr3_sctlrx[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64MMFR3, SCTLRX, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64mmfr3_tcrx[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64MMFR3, TCRX, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field id_aa64mmfr3_fields[] = {
MRS_FIELD(ID_AA64MMFR3, Spec_FPACC, false, MRS_LOWER, 0,
id_aa64mmfr3_spec_fpacc),
MRS_FIELD(ID_AA64MMFR3, ADERR, false, MRS_LOWER, 0, id_aa64mmfr3_aderr),
MRS_FIELD(ID_AA64MMFR3, SDERR, false, MRS_LOWER, 0, id_aa64mmfr3_sderr),
MRS_FIELD(ID_AA64MMFR3, ANERR, false, MRS_LOWER, 0, id_aa64mmfr3_anerr),
MRS_FIELD(ID_AA64MMFR3, SNERR, false, MRS_LOWER, 0, id_aa64mmfr3_snerr),
MRS_FIELD(ID_AA64MMFR3, MEC, false, MRS_LOWER, 0, id_aa64mmfr3_mec),
MRS_FIELD(ID_AA64MMFR3, AIE, false, MRS_LOWER, 0, id_aa64mmfr3_aie),
MRS_FIELD(ID_AA64MMFR3, S2POE, false, MRS_LOWER, 0, id_aa64mmfr3_s2poe),
MRS_FIELD(ID_AA64MMFR3, S1POE, false, MRS_LOWER, 0, id_aa64mmfr3_s1poe),
MRS_FIELD(ID_AA64MMFR3, S2PIE, false, MRS_LOWER, 0, id_aa64mmfr3_s2pie),
MRS_FIELD(ID_AA64MMFR3, S1PIE, false, MRS_LOWER, 0, id_aa64mmfr3_s1pie),
MRS_FIELD(ID_AA64MMFR3, SCTLRX, false, MRS_LOWER, 0,
id_aa64mmfr3_sctlrx),
MRS_FIELD(ID_AA64MMFR3, TCRX, false, MRS_LOWER, 0, id_aa64mmfr3_tcrx),
MRS_FIELD_END,
};
/* ID_AA64MMFR4_EL1 */
static const struct mrs_field id_aa64mmfr4_fields[] = {
MRS_FIELD_END,
};
/* ID_AA64PFR0_EL1 */
static const struct mrs_field_value id_aa64pfr0_csv3[] = {
MRS_FIELD_VALUE(ID_AA64PFR0_CSV3_NONE, ""),
MRS_FIELD_VALUE(ID_AA64PFR0_CSV3_ISOLATED, "CSV3"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64pfr0_csv2[] = {
MRS_FIELD_VALUE(ID_AA64PFR0_CSV2_NONE, ""),
MRS_FIELD_VALUE(ID_AA64PFR0_CSV2_ISOLATED, "CSV2"),
MRS_FIELD_VALUE(ID_AA64PFR0_CSV2_SCXTNUM, "CSV2_2"),
MRS_FIELD_VALUE(ID_AA64PFR0_CSV2_3, "CSV2_3"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64pfr0_rme[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64PFR0, RME, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64pfr0_dit[] = {
MRS_FIELD_VALUE(ID_AA64PFR0_DIT_NONE, ""),
MRS_FIELD_VALUE(ID_AA64PFR0_DIT_PSTATE, "PSTATE.DIT"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_aa64pfr0_dit_caps[] = {
MRS_HWCAP(1, HWCAP_DIT, ID_AA64PFR0_DIT_PSTATE),
MRS_HWCAP_END
};
static const struct mrs_field_value id_aa64pfr0_amu[] = {
MRS_FIELD_VALUE(ID_AA64PFR0_AMU_NONE, ""),
MRS_FIELD_VALUE(ID_AA64PFR0_AMU_V1, "AMUv1"),
MRS_FIELD_VALUE(ID_AA64PFR0_AMU_V1_1, "AMUv1p1"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64pfr0_mpam[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64PFR0, MPAM, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64pfr0_sel2[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64PFR0, SEL2, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64pfr0_sve[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64PFR0, SVE, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_aa64pfr0_sve_caps[] = {
MRS_HWCAP(1, HWCAP_SVE, ID_AA64PFR0_SVE_IMPL),
MRS_HWCAP_END
};
static const struct mrs_field_value id_aa64pfr0_ras[] = {
MRS_FIELD_VALUE(ID_AA64PFR0_RAS_NONE, ""),
MRS_FIELD_VALUE(ID_AA64PFR0_RAS_IMPL, "RAS"),
MRS_FIELD_VALUE(ID_AA64PFR0_RAS_8_4, "RAS v8.4"),
MRS_FIELD_VALUE(ID_AA64PFR0_RAS_8_9, "RAS v8.9"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64pfr0_gic[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64PFR0, GIC, CPUIF_NONE, CPUIF_EN),
MRS_FIELD_VALUE(ID_AA64PFR0_GIC_CPUIF_NONE, ""),
MRS_FIELD_VALUE(ID_AA64PFR0_GIC_CPUIF_EN, "GIC"),
MRS_FIELD_VALUE(ID_AA64PFR0_GIC_CPUIF_4_1, "GIC 4.1"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64pfr0_advsimd[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64PFR0, AdvSIMD, NONE, IMPL),
MRS_FIELD_VALUE(ID_AA64PFR0_AdvSIMD_HP, "AdvSIMD+HP"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_aa64pfr0_advsimd_caps[] = {
MRS_HWCAP(1, HWCAP_ASIMD, ID_AA64PFR0_AdvSIMD_IMPL),
MRS_HWCAP(1, HWCAP_ASIMDHP, ID_AA64PFR0_AdvSIMD_HP),
MRS_HWCAP_END
};
static const struct mrs_field_value id_aa64pfr0_fp[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64PFR0, FP, NONE, IMPL),
MRS_FIELD_VALUE(ID_AA64PFR0_FP_HP, "FP+HP"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_aa64pfr0_fp_caps[] = {
MRS_HWCAP(1, HWCAP_FP, ID_AA64PFR0_FP_IMPL),
MRS_HWCAP(1, HWCAP_FPHP, ID_AA64PFR0_FP_HP),
MRS_HWCAP_END
};
static const struct mrs_field_value id_aa64pfr0_el3[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64PFR0, EL3, NONE, 64),
MRS_FIELD_VALUE(ID_AA64PFR0_EL3_64_32, "EL3 32"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64pfr0_el2[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64PFR0, EL2, NONE, 64),
MRS_FIELD_VALUE(ID_AA64PFR0_EL2_64_32, "EL2 32"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64pfr0_el1[] = {
MRS_FIELD_VALUE(ID_AA64PFR0_EL1_64, "EL1"),
MRS_FIELD_VALUE(ID_AA64PFR0_EL1_64_32, "EL1 32"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64pfr0_el0[] = {
MRS_FIELD_VALUE(ID_AA64PFR0_EL0_64, "EL0"),
MRS_FIELD_VALUE(ID_AA64PFR0_EL0_64_32, "EL0 32"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field id_aa64pfr0_fields[] = {
MRS_FIELD(ID_AA64PFR0, CSV3, false, MRS_LOWER, 0, id_aa64pfr0_csv3),
MRS_FIELD(ID_AA64PFR0, CSV2, false, MRS_LOWER, 0, id_aa64pfr0_csv2),
MRS_FIELD(ID_AA64PFR0, RME, false, MRS_LOWER, 0, id_aa64pfr0_rme),
MRS_FIELD_HWCAP(ID_AA64PFR0, DIT, false, MRS_LOWER, MRS_USERSPACE,
id_aa64pfr0_dit, id_aa64pfr0_dit_caps),
MRS_FIELD(ID_AA64PFR0, AMU, false, MRS_LOWER, 0, id_aa64pfr0_amu),
MRS_FIELD(ID_AA64PFR0, MPAM, false, MRS_LOWER, 0, id_aa64pfr0_mpam),
MRS_FIELD(ID_AA64PFR0, SEL2, false, MRS_LOWER, 0, id_aa64pfr0_sel2),
MRS_FIELD_HWCAP(ID_AA64PFR0, SVE, false, MRS_LOWER,
MRS_FREEBSD, id_aa64pfr0_sve, id_aa64pfr0_sve_caps),
MRS_FIELD(ID_AA64PFR0, RAS, false, MRS_LOWER, 0, id_aa64pfr0_ras),
MRS_FIELD(ID_AA64PFR0, GIC, false, MRS_LOWER, 0, id_aa64pfr0_gic),
MRS_FIELD_HWCAP(ID_AA64PFR0, AdvSIMD, true, MRS_LOWER, MRS_USERSPACE,
id_aa64pfr0_advsimd, id_aa64pfr0_advsimd_caps),
MRS_FIELD_HWCAP(ID_AA64PFR0, FP, true, MRS_LOWER, MRS_USERSPACE,
id_aa64pfr0_fp, id_aa64pfr0_fp_caps),
MRS_FIELD(ID_AA64PFR0, EL3, false, MRS_LOWER, 0, id_aa64pfr0_el3),
MRS_FIELD(ID_AA64PFR0, EL2, false, MRS_LOWER, 0, id_aa64pfr0_el2),
MRS_FIELD(ID_AA64PFR0, EL1, false, MRS_LOWER, MRS_USERSPACE,
id_aa64pfr0_el1),
MRS_FIELD(ID_AA64PFR0, EL0, false, MRS_LOWER, MRS_USERSPACE,
id_aa64pfr0_el0),
MRS_FIELD_END,
};
/* ID_AA64PFR1_EL1 */
static const struct mrs_field_value id_aa64pfr1_pfar[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64PFR1, PFAR, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64pfr1_df2[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64PFR1, DF2, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64pfr1_mtex[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64PFR1, MTEX, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64pfr1_the[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64PFR1, THE, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64pfr1_mtefrac[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64PFR1, MTE_frac, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64pfr1_nmi[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64PFR1, NMI, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64pfr1_csv2_frac[] = {
MRS_FIELD_VALUE(ID_AA64PFR1_CSV2_frac_p0, ""),
MRS_FIELD_VALUE(ID_AA64PFR1_CSV2_frac_p1, "CSV2 p1"),
MRS_FIELD_VALUE(ID_AA64PFR1_CSV2_frac_p2, "CSV2 p2"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64pfr1_rndr_trap[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64PFR1, RNDR_trap, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64pfr1_sme[] = {
MRS_FIELD_VALUE(ID_AA64PFR1_SME_NONE, ""),
MRS_FIELD_VALUE(ID_AA64PFR1_SME_SME, "SME"),
MRS_FIELD_VALUE(ID_AA64PFR1_SME_SME2, "SME2"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64pfr1_mpam_frac[] = {
MRS_FIELD_VALUE(ID_AA64PFR1_MPAM_frac_p0, ""),
MRS_FIELD_VALUE(ID_AA64PFR1_MPAM_frac_p1, "MPAM p1"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64pfr1_ras_frac[] = {
MRS_FIELD_VALUE(ID_AA64PFR1_RAS_frac_p0, ""),
MRS_FIELD_VALUE(ID_AA64PFR1_RAS_frac_p1, "RAS p1"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64pfr1_mte[] = {
MRS_FIELD_VALUE(ID_AA64PFR1_MTE_NONE, ""),
MRS_FIELD_VALUE(ID_AA64PFR1_MTE_MTE, "MTE"),
MRS_FIELD_VALUE(ID_AA64PFR1_MTE_MTE2, "MTE2"),
MRS_FIELD_VALUE(ID_AA64PFR1_MTE_MTE3, "MTE3"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_aa64pfr1_ssbs[] = {
MRS_FIELD_VALUE(ID_AA64PFR1_SSBS_NONE, ""),
MRS_FIELD_VALUE(ID_AA64PFR1_SSBS_PSTATE, "PSTATE.SSBS"),
MRS_FIELD_VALUE(ID_AA64PFR1_SSBS_PSTATE_MSR, "PSTATE.SSBS MSR"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_aa64pfr1_ssbs_caps[] = {
MRS_HWCAP(1, HWCAP_SSBS, ID_AA64PFR1_SSBS_PSTATE),
MRS_HWCAP_END
};
static const struct mrs_field_value id_aa64pfr1_bt[] = {
MRS_FIELD_VALUE(ID_AA64PFR1_BT_NONE, ""),
MRS_FIELD_VALUE(ID_AA64PFR1_BT_IMPL, "BTI"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_aa64pfr1_bt_caps[] = {
MRS_HWCAP(2, HWCAP2_BTI, ID_AA64PFR1_BT_IMPL),
MRS_HWCAP_END
};
static const struct mrs_field id_aa64pfr1_fields[] = {
MRS_FIELD(ID_AA64PFR1, PFAR, false, MRS_LOWER, 0, id_aa64pfr1_pfar),
MRS_FIELD(ID_AA64PFR1, DF2, false, MRS_LOWER, 0, id_aa64pfr1_df2),
MRS_FIELD(ID_AA64PFR1, MTEX, false, MRS_LOWER, 0, id_aa64pfr1_mtex),
MRS_FIELD(ID_AA64PFR1, THE, false, MRS_LOWER, 0, id_aa64pfr1_the),
MRS_FIELD(ID_AA64PFR1, MTE_frac, false, MRS_LOWER, 0, id_aa64pfr1_mtefrac),
MRS_FIELD(ID_AA64PFR1, NMI, false, MRS_LOWER, 0, id_aa64pfr1_nmi),
MRS_FIELD(ID_AA64PFR1, CSV2_frac, false, MRS_LOWER, 0,
id_aa64pfr1_csv2_frac),
MRS_FIELD(ID_AA64PFR1, RNDR_trap, false, MRS_LOWER, 0,
id_aa64pfr1_rndr_trap),
MRS_FIELD(ID_AA64PFR1, SME, false, MRS_LOWER, 0, id_aa64pfr1_sme),
MRS_FIELD(ID_AA64PFR1, MPAM_frac, false, MRS_LOWER, 0,
id_aa64pfr1_mpam_frac),
MRS_FIELD(ID_AA64PFR1, RAS_frac, false, MRS_LOWER, 0,
id_aa64pfr1_ras_frac),
MRS_FIELD(ID_AA64PFR1, MTE, false, MRS_LOWER, 0, id_aa64pfr1_mte),
MRS_FIELD_HWCAP(ID_AA64PFR1, SSBS, false, MRS_LOWER, MRS_USERSPACE,
id_aa64pfr1_ssbs, id_aa64pfr1_ssbs_caps),
MRS_FIELD_HWCAP(ID_AA64PFR1, BT, false, MRS_LOWER,
MRS_FREEBSD, id_aa64pfr1_bt, id_aa64pfr1_bt_caps),
MRS_FIELD_END,
};
/* ID_AA64PFR2_EL1 */
static const struct mrs_field id_aa64pfr2_fields[] = {
MRS_FIELD_END,
};
/* ID_AA64ZFR0_EL1 */
static const struct mrs_field_value id_aa64zfr0_f64mm[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64ZFR0, F64MM, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_aa64zfr0_f64mm_caps[] = {
MRS_HWCAP(2, HWCAP2_SVEF64MM, ID_AA64ZFR0_F64MM_IMPL),
MRS_HWCAP_END,
};
static const struct mrs_field_value id_aa64zfr0_f32mm[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64ZFR0, F32MM, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_aa64zfr0_f32mm_caps[] = {
MRS_HWCAP(2, HWCAP2_SVEF32MM, ID_AA64ZFR0_F32MM_IMPL),
MRS_HWCAP_END,
};
static const struct mrs_field_value id_aa64zfr0_i8mm[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64ZFR0, I8MM, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_aa64zfr0_i8mm_caps[] = {
MRS_HWCAP(2, HWCAP2_SVEI8MM, ID_AA64ZFR0_I8MM_IMPL),
MRS_HWCAP_END,
};
static const struct mrs_field_value id_aa64zfr0_sm4[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64ZFR0, SM4, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_aa64zfr0_sm4_caps[] = {
MRS_HWCAP(2, HWCAP2_SVESM4, ID_AA64ZFR0_SM4_IMPL),
MRS_HWCAP_END,
};
static const struct mrs_field_value id_aa64zfr0_sha3[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64ZFR0, SHA3, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_aa64zfr0_sha3_caps[] = {
MRS_HWCAP(2, HWCAP2_SVESHA3, ID_AA64ZFR0_SHA3_IMPL),
MRS_HWCAP_END,
};
static const struct mrs_field_value id_aa64zfr0_bf16[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64ZFR0, BF16, NONE, BASE),
MRS_FIELD_VALUE(ID_AA64ZFR0_BF16_EBF, "BF16+EBF"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_aa64zfr0_bf16_caps[] = {
MRS_HWCAP(2, HWCAP2_SVEBF16, ID_AA64ZFR0_BF16_BASE),
MRS_HWCAP(2, HWCAP2_SVE_EBF16, ID_AA64ZFR0_BF16_EBF),
MRS_HWCAP_END,
};
static const struct mrs_field_value id_aa64zfr0_bitperm[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64ZFR0, BitPerm, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_aa64zfr0_bitperm_caps[] = {
MRS_HWCAP(2, HWCAP2_SVEBITPERM, ID_AA64ZFR0_BitPerm_IMPL),
MRS_HWCAP_END,
};
static const struct mrs_field_value id_aa64zfr0_aes[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_AA64ZFR0, AES, NONE, BASE),
MRS_FIELD_VALUE(ID_AA64ZFR0_AES_PMULL, "AES+PMULL"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_aa64zfr0_aes_caps[] = {
MRS_HWCAP(2, HWCAP2_SVEAES, ID_AA64ZFR0_AES_BASE),
MRS_HWCAP(2, HWCAP2_SVEPMULL, ID_AA64ZFR0_AES_PMULL),
MRS_HWCAP_END,
};
static const struct mrs_field_value id_aa64zfr0_svever[] = {
MRS_FIELD_VALUE(ID_AA64ZFR0_SVEver_SVE1, "SVE1"),
MRS_FIELD_VALUE(ID_AA64ZFR0_SVEver_SVE2, "SVE2"),
MRS_FIELD_VALUE(ID_AA64ZFR0_SVEver_SVE2P1, "SVE2P1"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_aa64zfr0_svever_caps[] = {
MRS_HWCAP(2, HWCAP2_SVE2, ID_AA64ZFR0_SVEver_SVE2),
MRS_HWCAP(2, HWCAP2_SVE2P1, ID_AA64ZFR0_SVEver_SVE2P1),
MRS_HWCAP_END,
};
static const struct mrs_field id_aa64zfr0_fields[] = {
MRS_FIELD_HWCAP(ID_AA64ZFR0, F64MM, false, MRS_LOWER, MRS_USERSPACE,
id_aa64zfr0_f64mm, id_aa64zfr0_f64mm_caps),
MRS_FIELD_HWCAP(ID_AA64ZFR0, F32MM, false, MRS_LOWER, MRS_USERSPACE,
id_aa64zfr0_f32mm, id_aa64zfr0_f32mm_caps),
MRS_FIELD_HWCAP(ID_AA64ZFR0, I8MM, false, MRS_LOWER, MRS_USERSPACE,
id_aa64zfr0_i8mm, id_aa64zfr0_i8mm_caps),
MRS_FIELD_HWCAP(ID_AA64ZFR0, SM4, false, MRS_LOWER, MRS_USERSPACE,
id_aa64zfr0_sm4, id_aa64zfr0_sm4_caps),
MRS_FIELD_HWCAP(ID_AA64ZFR0, SHA3, false, MRS_LOWER, MRS_USERSPACE,
id_aa64zfr0_sha3, id_aa64zfr0_sha3_caps),
MRS_FIELD_HWCAP(ID_AA64ZFR0, BF16, false, MRS_LOWER, MRS_USERSPACE,
id_aa64zfr0_bf16, id_aa64zfr0_bf16_caps),
MRS_FIELD_HWCAP(ID_AA64ZFR0, BitPerm, false, MRS_LOWER, MRS_USERSPACE,
id_aa64zfr0_bitperm, id_aa64zfr0_bitperm_caps),
MRS_FIELD_HWCAP(ID_AA64ZFR0, AES, false, MRS_LOWER, MRS_USERSPACE,
id_aa64zfr0_aes, id_aa64zfr0_aes_caps),
MRS_FIELD_HWCAP(ID_AA64ZFR0, SVEver, false, MRS_LOWER, MRS_USERSPACE,
id_aa64zfr0_svever, id_aa64zfr0_svever_caps),
MRS_FIELD_END,
};
#ifdef COMPAT_FREEBSD32
/* ID_ISAR5_EL1 */
static const struct mrs_field_value id_isar5_vcma[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_ISAR5, VCMA, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_isar5_rdm[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_ISAR5, RDM, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value id_isar5_crc32[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_ISAR5, CRC32, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_isar5_crc32_caps[] = {
MRS_HWCAP(2, HWCAP32_2_CRC32, ID_ISAR5_CRC32_IMPL),
MRS_HWCAP_END
};
static const struct mrs_field_value id_isar5_sha2[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_ISAR5, SHA2, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_isar5_sha2_caps[] = {
MRS_HWCAP(2, HWCAP32_2_SHA2, ID_ISAR5_SHA2_IMPL),
MRS_HWCAP_END
};
static const struct mrs_field_value id_isar5_sha1[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_ISAR5, SHA1, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_isar5_sha1_caps[] = {
MRS_HWCAP(2, HWCAP32_2_SHA1, ID_ISAR5_SHA1_IMPL),
MRS_HWCAP_END
};
static const struct mrs_field_value id_isar5_aes[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_ISAR5, AES, NONE, BASE),
MRS_FIELD_VALUE(ID_ISAR5_AES_VMULL, "AES+VMULL"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap id_isar5_aes_caps[] = {
MRS_HWCAP(2, HWCAP32_2_AES, ID_ISAR5_AES_BASE),
MRS_HWCAP(2, HWCAP32_2_PMULL, ID_ISAR5_AES_VMULL),
MRS_HWCAP_END
};
static const struct mrs_field_value id_isar5_sevl[] = {
MRS_FIELD_VALUE_NONE_IMPL(ID_ISAR5, SEVL, NOP, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field id_isar5_fields[] = {
MRS_FIELD(ID_ISAR5, VCMA, false, MRS_LOWER,MRS_USERSPACE,
id_isar5_vcma),
MRS_FIELD(ID_ISAR5, RDM, false, MRS_LOWER, MRS_USERSPACE, id_isar5_rdm),
MRS_FIELD_HWCAP(ID_ISAR5, CRC32, false, MRS_LOWER, MRS_USERSPACE,
id_isar5_crc32, id_isar5_crc32_caps),
MRS_FIELD_HWCAP(ID_ISAR5, SHA2, false, MRS_LOWER, MRS_USERSPACE,
id_isar5_sha2, id_isar5_sha2_caps),
MRS_FIELD_HWCAP(ID_ISAR5, SHA1, false, MRS_LOWER, MRS_USERSPACE,
id_isar5_sha1, id_isar5_sha1_caps),
MRS_FIELD_HWCAP(ID_ISAR5, AES, false, MRS_LOWER, MRS_USERSPACE,
id_isar5_aes, id_isar5_aes_caps),
MRS_FIELD(ID_ISAR5, SEVL, false, MRS_LOWER, MRS_USERSPACE,
id_isar5_sevl),
MRS_FIELD_END,
};
/* MVFR0 */
static const struct mrs_field_value mvfr0_fpround[] = {
MRS_FIELD_VALUE_NONE_IMPL(MVFR0, FPRound, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value mvfr0_fpsqrt[] = {
MRS_FIELD_VALUE_NONE_IMPL(MVFR0, FPSqrt, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value mvfr0_fpdivide[] = {
MRS_FIELD_VALUE_NONE_IMPL(MVFR0, FPDivide, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value mvfr0_fptrap[] = {
MRS_FIELD_VALUE_NONE_IMPL(MVFR0, FPTrap, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value mvfr0_fpdp[] = {
MRS_FIELD_VALUE(MVFR0_FPDP_NONE, ""),
MRS_FIELD_VALUE(MVFR0_FPDP_VFP_v2, "DP VFPv2"),
MRS_FIELD_VALUE(MVFR0_FPDP_VFP_v3_v4, "DP VFPv3+v4"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap mvfr0_fpdp_caps[] = {
MRS_HWCAP(1, HWCAP32_VFP, MVFR0_FPDP_VFP_v2),
MRS_HWCAP(1, HWCAP32_VFPv3, MVFR0_FPDP_VFP_v3_v4),
MRS_HWCAP_END
};
static const struct mrs_field_value mvfr0_fpsp[] = {
MRS_FIELD_VALUE(MVFR0_FPSP_NONE, ""),
MRS_FIELD_VALUE(MVFR0_FPSP_VFP_v2, "SP VFPv2"),
MRS_FIELD_VALUE(MVFR0_FPSP_VFP_v3_v4, "SP VFPv3+v4"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value mvfr0_simdreg[] = {
MRS_FIELD_VALUE(MVFR0_SIMDReg_NONE, ""),
MRS_FIELD_VALUE(MVFR0_SIMDReg_FP, "FP 16x64"),
MRS_FIELD_VALUE(MVFR0_SIMDReg_AdvSIMD, "AdvSIMD"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field mvfr0_fields[] = {
MRS_FIELD(MVFR0, FPRound, false, MRS_LOWER, MRS_USERSPACE,
mvfr0_fpround),
MRS_FIELD(MVFR0, FPSqrt, false, MRS_LOWER, MRS_USERSPACE,
mvfr0_fpsqrt),
MRS_FIELD(MVFR0, FPDivide, false, MRS_LOWER, MRS_USERSPACE,
mvfr0_fpdivide),
MRS_FIELD(MVFR0, FPTrap, false, MRS_LOWER, MRS_USERSPACE,
mvfr0_fptrap),
MRS_FIELD_HWCAP(MVFR0, FPDP, false, MRS_LOWER, MRS_USERSPACE,
mvfr0_fpdp, mvfr0_fpdp_caps),
MRS_FIELD(MVFR0, FPSP, false, MRS_LOWER, MRS_USERSPACE, mvfr0_fpsp),
MRS_FIELD(MVFR0, SIMDReg, false, MRS_LOWER, MRS_USERSPACE,
mvfr0_simdreg),
MRS_FIELD_END,
};
/* MVFR1 */
static const struct mrs_field_value mvfr1_simdfmac[] = {
MRS_FIELD_VALUE_NONE_IMPL(MVFR1, SIMDFMAC, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap mvfr1_simdfmac_caps[] = {
MRS_HWCAP(1, HWCAP32_VFPv4, MVFR1_SIMDFMAC_IMPL),
MRS_HWCAP_END
};
static const struct mrs_field_value mvfr1_fphp[] = {
MRS_FIELD_VALUE(MVFR1_FPHP_NONE, ""),
MRS_FIELD_VALUE(MVFR1_FPHP_CONV_SP, "FPHP SP Conv"),
MRS_FIELD_VALUE(MVFR1_FPHP_CONV_DP, "FPHP DP Conv"),
MRS_FIELD_VALUE(MVFR1_FPHP_ARITH, "FPHP Arith"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value mvfr1_simdhp[] = {
MRS_FIELD_VALUE(MVFR1_SIMDHP_NONE, ""),
MRS_FIELD_VALUE(MVFR1_SIMDHP_CONV_SP, "SIMDHP SP Conv"),
MRS_FIELD_VALUE(MVFR1_SIMDHP_ARITH, "SIMDHP Arith"),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value mvfr1_simdsp[] = {
MRS_FIELD_VALUE_NONE_IMPL(MVFR1, SIMDSP, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value mvfr1_simdint[] = {
MRS_FIELD_VALUE_NONE_IMPL(MVFR1, SIMDInt, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value mvfr1_simdls[] = {
MRS_FIELD_VALUE_NONE_IMPL(MVFR1, SIMDLS, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_hwcap mvfr1_simdls_caps[] = {
MRS_HWCAP(1, HWCAP32_VFPv4, MVFR1_SIMDFMAC_IMPL),
MRS_HWCAP_END
};
static const struct mrs_field_value mvfr1_fpdnan[] = {
MRS_FIELD_VALUE_NONE_IMPL(MVFR1, FPDNaN, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field_value mvfr1_fpftz[] = {
MRS_FIELD_VALUE_NONE_IMPL(MVFR1, FPFtZ, NONE, IMPL),
MRS_FIELD_VALUE_END,
};
static const struct mrs_field mvfr1_fields[] = {
MRS_FIELD_HWCAP(MVFR1, SIMDFMAC, false, MRS_LOWER, MRS_USERSPACE,
mvfr1_simdfmac, mvfr1_simdfmac_caps),
MRS_FIELD(MVFR1, FPHP, false, MRS_LOWER, MRS_USERSPACE, mvfr1_fphp),
MRS_FIELD(MVFR1, SIMDHP, false, MRS_LOWER, MRS_USERSPACE, mvfr1_simdhp),
MRS_FIELD(MVFR1, SIMDSP, false, MRS_LOWER, MRS_USERSPACE, mvfr1_simdsp),
MRS_FIELD(MVFR1, SIMDInt, false, MRS_LOWER, MRS_USERSPACE,
mvfr1_simdint),
MRS_FIELD_HWCAP(MVFR1, SIMDLS, false, MRS_LOWER, MRS_USERSPACE,
mvfr1_simdls, mvfr1_simdls_caps),
MRS_FIELD(MVFR1, FPDNaN, false, MRS_LOWER, MRS_USERSPACE,
mvfr1_fpdnan),
MRS_FIELD(MVFR1, FPFtZ, false, MRS_LOWER, MRS_USERSPACE,
mvfr1_fpftz),
MRS_FIELD_END,
};
#endif /* COMPAT_FREEBSD32 */
struct mrs_user_reg {
u_int reg;
u_int CRm;
u_int Op2;
bool is64bit;
size_t offset;
const struct mrs_field *fields;
};
#define USER_REG(name, field_name, _is64bit) \
{ \
.reg = name, \
.CRm = name##_CRm, \
.Op2 = name##_op2, \
.offset = __offsetof(struct cpu_desc, field_name), \
.fields = field_name##_fields, \
.is64bit = _is64bit, \
}
static const struct mrs_user_reg user_regs[] = {
USER_REG(ID_AA64AFR0_EL1, id_aa64afr0, true),
USER_REG(ID_AA64AFR1_EL1, id_aa64afr1, true),
USER_REG(ID_AA64DFR0_EL1, id_aa64dfr0, true),
USER_REG(ID_AA64DFR1_EL1, id_aa64dfr1, true),
USER_REG(ID_AA64ISAR0_EL1, id_aa64isar0, true),
USER_REG(ID_AA64ISAR1_EL1, id_aa64isar1, true),
USER_REG(ID_AA64ISAR2_EL1, id_aa64isar2, true),
USER_REG(ID_AA64MMFR0_EL1, id_aa64mmfr0, true),
USER_REG(ID_AA64MMFR1_EL1, id_aa64mmfr1, true),
USER_REG(ID_AA64MMFR2_EL1, id_aa64mmfr2, true),
USER_REG(ID_AA64MMFR3_EL1, id_aa64mmfr3, true),
USER_REG(ID_AA64MMFR4_EL1, id_aa64mmfr4, true),
USER_REG(ID_AA64PFR0_EL1, id_aa64pfr0, true),
USER_REG(ID_AA64PFR1_EL1, id_aa64pfr1, true),
USER_REG(ID_AA64PFR2_EL1, id_aa64pfr2, true),
USER_REG(ID_AA64ZFR0_EL1, id_aa64zfr0, true),
USER_REG(CTR_EL0, ctr, true),
#ifdef COMPAT_FREEBSD32
USER_REG(ID_ISAR5_EL1, id_isar5, false),
USER_REG(MVFR0_EL1, mvfr0, false),
USER_REG(MVFR1_EL1, mvfr1, false),
#endif /* COMPAT_FREEBSD32 */
};
#define CPU_DESC_FIELD(desc, idx) \
*(uint64_t *)((char *)&(desc) + user_regs[(idx)].offset)
static bool
user_ctr_has_neoverse_n1_1542419(uint32_t midr, uint64_t ctr)
{
/* Skip non-Neoverse-N1 */
if (!CPU_MATCH(CPU_IMPL_MASK | CPU_PART_MASK, CPU_IMPL_ARM,
CPU_PART_NEOVERSE_N1, 0, 0))
return (false);
switch (CPU_VAR(midr)) {
default:
break;
case 4:
/* Fixed in r4p1 */
if (CPU_REV(midr) > 0)
break;
/* FALLTHROUGH */
case 3:
/* If DIC is enabled (coherent icache) then we are affected */
return (CTR_DIC_VAL(ctr) != 0);
}
return (false);
}
static bool
user_ctr_check(const struct cpu_feat *feat __unused, u_int midr __unused)
{
if (emulate_ctr)
return (true);
if (user_ctr_has_neoverse_n1_1542419(midr, READ_SPECIALREG(ctr_el0)))
return (true);
return (false);
}
static bool
user_ctr_has_errata(const struct cpu_feat *feat __unused, u_int midr,
u_int **errata_list, u_int *errata_count)
{
if (user_ctr_has_neoverse_n1_1542419(midr, READ_SPECIALREG(ctr_el0))) {
static u_int errata_id = 1542419;
*errata_list = &errata_id;
*errata_count = 1;
return (true);
}
return (false);
}
static void
user_ctr_enable(const struct cpu_feat *feat __unused,
cpu_feat_errata errata_status, u_int *errata_list, u_int errata_count)
{
MPASS(emulate_ctr || errata_status != ERRATA_NONE);
/*
* The Errata Management Firmware Interface may incorrectly mark
* this as firmware mitigated. We should ignore that as there is
* a kernel component to the mitigation.
*/
if (errata_status != ERRATA_NONE && PCPU_GET(cpuid) == 0 &&
cpu_feat_has_erratum(errata_list, errata_count, 1542419)) {
/* Clear fields we will change */
user_cpu_desc.ctr &= ~(CTR_DIC_MASK | CTR_ILINE_WIDTH);
/*
* Set DIC to none so userspace will execute an 'ic ivau'
* instruction that can be trapped by EL3.
*/
user_cpu_desc.ctr |= CTR_DIC_NONE;
/*
* Set the i-cache line size to be page size to reduce the
* number of times userspace needs to execute the 'ic ivau'
* instruction. The ctr_el0.IminLine is log2 the number of
* 4-byte words the instruction covers. As PAGE_SHIFT is log2
* of the number of bytes in a page we need to subtract 2.
*/
user_cpu_desc.ctr |= (PAGE_SHIFT - 2) << CTR_ILINE_SHIFT;
l_user_cpu_desc.ctr = user_cpu_desc.ctr;
}
WRITE_SPECIALREG(sctlr_el1,
READ_SPECIALREG(sctlr_el1) & ~SCTLR_UCT);
isb();
}
static struct cpu_feat user_ctr = {
.feat_name = "Trap CTR_EL0",
.feat_check = user_ctr_check,
.feat_has_errata = user_ctr_has_errata,
.feat_enable = user_ctr_enable,
.feat_flags = CPU_FEAT_AFTER_DEV | CPU_FEAT_PER_CPU,
};
DATA_SET(cpu_feat_set, user_ctr);
static int
user_ctr_handler(vm_offset_t va, uint32_t insn, struct trapframe *frame,
uint32_t esr)
{
uint64_t value;
int reg;
if ((insn & MRS_MASK) != MRS_VALUE)
return (0);
/* Check if this is the ctr_el0 register */
/* TODO: Add macros to armreg.h */
if (mrs_Op0(insn) != 3 || mrs_Op1(insn) != 3 || mrs_CRn(insn) != 0 ||
mrs_CRm(insn) != 0 || mrs_Op2(insn) != 1)
return (0);
if (SV_CURPROC_ABI() == SV_ABI_FREEBSD)
value = user_cpu_desc.ctr;
else
value = l_user_cpu_desc.ctr;
/*
* 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 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(user_regs); i++) {
if (user_regs[i].CRm == CRm && user_regs[i].Op2 == Op2) {
if (SV_CURPROC_ABI() == SV_ABI_FREEBSD)
value = CPU_DESC_FIELD(user_cpu_desc, i);
else
value = CPU_DESC_FIELD(l_user_cpu_desc, i);
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);
}
/*
* Compares two field values that may be signed or unsigned.
* Returns:
* < 0 when a is less than b
* = 0 when a equals b
* > 0 when a is greater than b
*/
static int
mrs_field_cmp(uint64_t a, uint64_t b, u_int shift, int width, bool sign)
{
uint64_t mask;
KASSERT(width > 0 && width < 64, ("%s: Invalid width %d", __func__,
width));
mask = (1ul << width) - 1;
/* Move the field to the lower bits */
a = (a >> shift) & mask;
b = (b >> shift) & mask;
if (sign) {
/*
* The field is signed. Toggle the upper bit so the comparison
* works on unsigned values as this makes positive numbers,
* i.e. those with a 0 bit, larger than negative numbers,
* i.e. those with a 1 bit, in an unsigned comparison.
*/
a ^= 1ul << (width - 1);
b ^= 1ul << (width - 1);
}
return (a - b);
}
bool
extract_user_id_field(u_int reg, u_int field_shift, uint8_t *val)
{
uint64_t value;
int i;
for (i = 0; i < nitems(user_regs); i++) {
if (user_regs[i].reg == reg) {
value = CPU_DESC_FIELD(user_cpu_desc, i);
*val = value >> field_shift;
return (true);
}
}
return (false);
}
bool
get_kernel_reg(u_int reg, uint64_t *val)
{
int i;
for (i = 0; i < nitems(user_regs); i++) {
if (user_regs[i].reg == reg) {
*val = CPU_DESC_FIELD(kern_cpu_desc, i);
return (true);
}
}
return (false);
}
/*
* Fetch the specified register's value, ensuring that individual field values
* do not exceed those in the mask.
*/
bool
get_kernel_reg_masked(u_int reg, uint64_t *valp, uint64_t mask)
{
const struct mrs_field *fields;
uint64_t val;
for (int i = 0; i < nitems(user_regs); i++) {
if (user_regs[i].reg == reg) {
val = CPU_DESC_FIELD(kern_cpu_desc, i);
fields = user_regs[i].fields;
for (int j = 0; fields[j].type != 0; j++) {
mask = update_special_reg_field(mask,
fields[j].type, val, fields[j].width,
fields[j].shift, fields[j].sign);
}
*valp = mask;
return (true);
}
}
return (false);
}
static uint64_t
update_special_reg_field(uint64_t user_reg, u_int type, uint64_t value,
u_int width, u_int shift, bool sign)
{
uint64_t cur, mask, new_val;
mask = ((1ul << width) - 1) << shift;
cur = user_reg & mask;
new_val = value & mask;
switch (type & MRS_TYPE_MASK) {
case MRS_EXACT_IF_DIFFERENT:
if (mrs_field_cmp(new_val, cur, shift, width, sign) == 0)
break;
/* FALLTHROUGH */
case MRS_EXACT:
cur = (uint64_t)MRS_SAFE_VAL(type) << shift;
break;
case MRS_LOWER:
if (mrs_field_cmp(new_val, cur, shift, width, sign) < 0)
cur = new_val;
break;
case MRS_HIGHER_OR_ZERO:
if (cur == 0 || new_val == 0) {
cur = 0;
break;
}
/* FALLTHROUGH */
case MRS_HIGHER:
if (mrs_field_cmp(new_val, cur, shift, width, sign) > 0)
cur = new_val;
break;
default:
panic("Invalid field type: %d", type);
}
user_reg &= ~mask;
user_reg |= cur;
return (user_reg);
}
void
update_special_regs(u_int cpu)
{
struct cpu_desc *desc;
const struct mrs_field *fields;
uint64_t l_user_reg, user_reg, kern_reg, value;
int i, j;
if (cpu == 0) {
/* Create a user visible cpu description with safe values */
memset(&user_cpu_desc, 0, sizeof(user_cpu_desc));
/* Safe values for these registers */
user_cpu_desc.id_aa64pfr0 = ID_AA64PFR0_AdvSIMD_NONE |
ID_AA64PFR0_FP_NONE | ID_AA64PFR0_EL1_64 |
ID_AA64PFR0_EL0_64;
user_cpu_desc.id_aa64dfr0 = ID_AA64DFR0_DebugVer_8;
/* Create the Linux user visible cpu description */
memcpy(&l_user_cpu_desc, &user_cpu_desc, sizeof(user_cpu_desc));
}
desc = get_cpu_desc(cpu);
for (i = 0; i < nitems(user_regs); i++) {
value = CPU_DESC_FIELD(*desc, i);
if (cpu == 0) {
kern_reg = value;
user_reg = value;
l_user_reg = value;
} else {
kern_reg = CPU_DESC_FIELD(kern_cpu_desc, i);
user_reg = CPU_DESC_FIELD(user_cpu_desc, i);
l_user_reg = CPU_DESC_FIELD(l_user_cpu_desc, i);
}
fields = user_regs[i].fields;
for (j = 0; fields[j].type != 0; j++) {
u_int type;
/* Update the FreeBSD userspace ID register view */
type = ((fields[j].type & MRS_FREEBSD) != 0) ?
fields[j].type :
(MRS_EXACT | (fields[j].type & MRS_SAFE_MASK));
user_reg = update_special_reg_field(user_reg,
type, value, fields[j].width, fields[j].shift,
fields[j].sign);
/* Update the Linux userspace ID register view */
type = ((fields[j].type & MRS_LINUX) != 0) ?
fields[j].type :
(MRS_EXACT | (fields[j].type & MRS_SAFE_MASK));
l_user_reg = update_special_reg_field(l_user_reg,
type, value, fields[j].width, fields[j].shift,
fields[j].sign);
/* Update the kernel ID register view */
kern_reg = update_special_reg_field(kern_reg,
fields[j].type, value, fields[j].width,
fields[j].shift, fields[j].sign);
}
CPU_DESC_FIELD(kern_cpu_desc, i) = kern_reg;
CPU_DESC_FIELD(user_cpu_desc, i) = user_reg;
CPU_DESC_FIELD(l_user_cpu_desc, i) = l_user_reg;
}
}
void
cpu_desc_init(void)
{
if (mp_ncpus == 1)
return;
/*
* Allocate memory for the non-boot CPUs to store their registers.
* As this is indexed by CPU ID we need to allocate space for CPUs
* 1 to mp_maxid. Because of this mp_maxid is already the correct
* number of elements.
*/
cpu_desc = mallocarray(mp_maxid, sizeof(*cpu_desc), M_IDENTCPU,
M_ZERO | M_WAITOK);
}
/* HWCAP */
bool __read_frequently lse_supported = false;
bool __read_frequently icache_aliasing = false;
bool __read_frequently icache_vmid = false;
int64_t dcache_line_size; /* The minimum D cache line size */
int64_t icache_line_size; /* The minimum I cache line size */
int64_t idcache_line_size; /* The minimum cache line size */
/*
* Find the values to export to userspace as AT_HWCAP and AT_HWCAP2.
*/
static void
parse_cpu_features(bool is64bit, struct cpu_desc *cpu_desc, u_long *hwcap,
u_long *hwcap2)
{
const struct mrs_field_hwcap *hwcaps;
const struct mrs_field *fields;
uint64_t min, reg;
u_long *cur_hwcap;
int i, j, k;
for (i = 0; i < nitems(user_regs); i++) {
if (user_regs[i].is64bit != is64bit)
continue;
reg = CPU_DESC_FIELD(*cpu_desc, i);
fields = user_regs[i].fields;
for (j = 0; fields[j].type != 0; j++) {
hwcaps = fields[j].hwcaps;
if (hwcaps == NULL)
continue;
for (k = 0; hwcaps[k].hwcap_id != 0; k++) {
KASSERT(hwcaps[k].hwcap_id == 1 ||
hwcaps[k].hwcap_id == 2,
("%s: Invalid HWCAP ID %d", __func__,
hwcaps[k].hwcap_id));
cur_hwcap = hwcaps[k].hwcap_id == 1 ?
hwcap : hwcap2;
min = hwcaps[k].min;
/*
* If the field is greater than the minimum
* value we can set the hwcap;
*/
if (mrs_field_cmp(reg, min, fields[j].shift,
4, fields[j].sign) >= 0) {
*cur_hwcap |= hwcaps[k].hwcap_val;
}
}
}
}
}
static void
identify_cpu_sysinit(void *dummy __unused)
{
struct cpu_desc *desc, *prev_desc;
int cpu;
bool dic, idc;
dic = (allow_dic != 0);
idc = (allow_idc != 0);
prev_desc = NULL;
CPU_FOREACH(cpu) {
desc = get_cpu_desc(cpu);
if (cpu != 0) {
check_cpu_regs(cpu, desc, prev_desc);
update_special_regs(cpu);
}
if (CTR_DIC_VAL(desc->ctr) == 0)
dic = false;
if (CTR_IDC_VAL(desc->ctr) == 0)
idc = false;
prev_desc = desc;
}
/* Find the values to export to userspace as AT_HWCAP and AT_HWCAP2 */
parse_cpu_features(true, &user_cpu_desc, &elf_hwcap, &elf_hwcap2);
parse_cpu_features(true, &l_user_cpu_desc, &linux_elf_hwcap,
&linux_elf_hwcap2);
#ifdef COMPAT_FREEBSD32
parse_cpu_features(false, &user_cpu_desc, &elf32_hwcap, &elf32_hwcap2);
#endif
/* We export the CPUID registers */
elf_hwcap |= HWCAP_CPUID;
linux_elf_hwcap |= HWCAP_CPUID;
#ifdef COMPAT_FREEBSD32
/* Set the default caps and any that need to check multiple fields */
elf32_hwcap |= parse_cpu_features_hwcap32();
#endif
if (dic && idc) {
arm64_icache_sync_range = &arm64_dic_idc_icache_sync_range;
if (bootverbose)
printf("Enabling DIC & IDC ICache sync\n");
} else if (idc) {
arm64_icache_sync_range = &arm64_idc_aliasing_icache_sync_range;
if (bootverbose)
printf("Enabling IDC ICache sync\n");
}
if ((elf_hwcap & HWCAP_ATOMICS) != 0) {
lse_supported = true;
if (bootverbose)
printf("Enabling LSE atomics in the kernel\n");
}
#ifdef LSE_ATOMICS
if (!lse_supported)
panic("CPU does not support LSE atomic instructions");
#endif
- install_undef_handler(true, user_ctr_handler);
- install_undef_handler(true, user_mrs_handler);
+ install_undef_handler(user_ctr_handler);
+ install_undef_handler(user_mrs_handler);
}
SYSINIT(identify_cpu, SI_SUB_CPU, SI_ORDER_MIDDLE, identify_cpu_sysinit, NULL);
static void
cpu_features_sysinit(void *dummy __unused)
{
struct sbuf sb;
struct cpu_desc *desc, *prev_desc;
u_int cpu;
prev_desc = NULL;
CPU_FOREACH(cpu) {
desc = get_cpu_desc(cpu);
print_cpu_features(cpu, desc, prev_desc);
prev_desc = desc;
}
/* Fill in cpu_model for the hw.model sysctl */
sbuf_new(&sb, cpu_model, sizeof(cpu_model), SBUF_FIXEDLEN);
print_cpu_midr(&sb, 0);
sbuf_finish(&sb);
sbuf_delete(&sb);
free(cpu_desc, M_IDENTCPU);
}
/* Log features before APs are released and start printing to the dmesg. */
SYSINIT(cpu_features, SI_SUB_SMP - 1, SI_ORDER_ANY, cpu_features_sysinit, NULL);
static void
tcr_set_e0pd1(void *arg __unused)
{
uint64_t tcr;
tcr = READ_SPECIALREG(tcr_el1);
tcr |= TCR_E0PD1;
WRITE_SPECIALREG(tcr_el1, tcr);
isb();
}
/* Enable support for more recent architecture features */
static void
cpu_feat_support(void *arg __unused)
{
/*
* If FEAT_E0PD is supported use it to cause faults without a page
* table walk if userspace tries to access kernel memory.
*/
if (ID_AA64MMFR2_E0PD_VAL(kern_cpu_desc.id_aa64mmfr2) !=
ID_AA64MMFR2_E0PD_NONE)
smp_rendezvous(NULL, tcr_set_e0pd1, NULL, NULL);
}
SYSINIT(cpu_feat_support, SI_SUB_SMP, SI_ORDER_ANY, cpu_feat_support, NULL);
#ifdef COMPAT_FREEBSD32
static u_long
parse_cpu_features_hwcap32(void)
{
u_long hwcap = HWCAP32_DEFAULT;
if ((MVFR1_SIMDLS_VAL(user_cpu_desc.mvfr1) >=
MVFR1_SIMDLS_IMPL) &&
(MVFR1_SIMDInt_VAL(user_cpu_desc.mvfr1) >=
MVFR1_SIMDInt_IMPL) &&
(MVFR1_SIMDSP_VAL(user_cpu_desc.mvfr1) >=
MVFR1_SIMDSP_IMPL))
hwcap |= HWCAP32_NEON;
return (hwcap);
}
#endif /* COMPAT_FREEBSD32 */
static void
print_register(struct sbuf *sb, const char *reg_name, uint64_t reg,
void (*print_fields)(struct sbuf *, uint64_t, const void *),
const void *arg)
{
sbuf_printf(sb, "%29s = <", reg_name);
print_fields(sb, reg, arg);
sbuf_finish(sb);
printf("%s>\n", sbuf_data(sb));
sbuf_clear(sb);
}
static void
print_id_fields(struct sbuf *sb, uint64_t reg, const void *arg)
{
const struct mrs_field *fields = arg;
const struct mrs_field_value *fv;
int field, i, j, printed;
#define SEP_STR ((printed++) == 0) ? "" : ","
printed = 0;
for (i = 0; fields[i].type != 0; i++) {
fv = fields[i].values;
if (fv == NULL)
goto next;
field = (reg & fields[i].mask) >> fields[i].shift;
for (j = 0; fv[j].desc != NULL; j++) {
if ((fv[j].value >> fields[i].shift) != field)
continue;
if (fv[j].desc[0] != '\0')
sbuf_printf(sb, "%s%s", SEP_STR, fv[j].desc);
break;
}
if (fv[j].desc == NULL)
sbuf_printf(sb, "%sUnknown %s(%x)", SEP_STR,
fields[i].name, field);
next:
reg &= ~(((1ul << fields[i].width) - 1) << fields[i].shift);
}
if (reg != 0)
sbuf_printf(sb, "%s%#lx", SEP_STR, reg);
#undef SEP_STR
}
static void
print_id_register(struct sbuf *sb, const char *reg_name, uint64_t reg,
const struct mrs_field *fields)
{
print_register(sb, reg_name, reg, print_id_fields, fields);
}
static void
print_cpu_midr(struct sbuf *sb, u_int cpu)
{
const struct cpu_parts *cpu_partsp;
const char *cpu_impl_name;
const char *cpu_part_name;
u_int midr;
u_int impl_id;
u_int part_id;
midr = pcpu_find(cpu)->pc_midr;
cpu_impl_name = NULL;
cpu_partsp = NULL;
impl_id = CPU_IMPL(midr);
for (int i = 0; cpu_implementers[i].impl_name != NULL; i++) {
if (impl_id == cpu_implementers[i].impl_id) {
cpu_impl_name = cpu_implementers[i].impl_name;
cpu_partsp = cpu_implementers[i].cpu_parts;
break;
}
}
/* Unknown implementer, so unknown part */
if (cpu_impl_name == NULL) {
sbuf_printf(sb, "Unknown Implementer (midr: %08x)", midr);
return;
}
KASSERT(cpu_partsp != NULL, ("%s: No parts table for implementer %s",
__func__, cpu_impl_name));
cpu_part_name = NULL;
part_id = CPU_PART(midr);
for (int i = 0; cpu_partsp[i].part_name != NULL; i++) {
if (part_id == cpu_partsp[i].part_id) {
cpu_part_name = cpu_partsp[i].part_name;
break;
}
}
/* Known Implementer, Unknown part */
if (cpu_part_name == NULL) {
sbuf_printf(sb, "%s Unknown CPU r%dp%d (midr: %08x)",
cpu_impl_name, CPU_VAR(midr), CPU_REV(midr), midr);
return;
}
sbuf_printf(sb, "%s %s r%dp%d", cpu_impl_name,
cpu_part_name, CPU_VAR(midr), CPU_REV(midr));
}
static void
print_cpu_cache(struct cpu_desc *desc, struct sbuf *sb, uint64_t ccs,
bool icache, bool unified)
{
size_t cache_size;
size_t line_size;
/* LineSize is Log2(S) - 4. */
line_size = 1 << ((ccs & CCSIDR_LineSize_MASK) + 4);
/*
* Calculate cache size (sets * ways * line size). There are different
* formats depending on the FEAT_CCIDX bit in ID_AA64MMFR2 feature
* register.
*/
if ((desc->id_aa64mmfr2 & ID_AA64MMFR2_CCIDX_64))
cache_size = (CCSIDR_NSETS_64(ccs) + 1) *
(CCSIDR_ASSOC_64(ccs) + 1);
else
cache_size = (CCSIDR_NSETS(ccs) + 1) * (CCSIDR_ASSOC(ccs) + 1);
cache_size *= line_size;
sbuf_printf(sb, "%zuKB (%s)", cache_size / 1024,
icache ? "instruction" : unified ? "unified" : "data");
}
static void
print_cpu_caches(struct sbuf *sb, struct cpu_desc *desc)
{
/* Print out each cache combination */
uint64_t clidr;
int i = 1;
clidr = desc->clidr;
for (i = 0; (clidr & CLIDR_CTYPE_MASK) != 0; i++, clidr >>= 3) {
int j = 0;
int ctype_m = (clidr & CLIDR_CTYPE_MASK);
sbuf_printf(sb, " L%d cache: ", i + 1);
if ((clidr & CLIDR_CTYPE_IO)) {
print_cpu_cache(desc, sb, desc->ccsidr[i][j++], true,
false);
/* If there's more, add to the line. */
if ((ctype_m & ~CLIDR_CTYPE_IO) != 0)
sbuf_printf(sb, ", ");
}
if ((ctype_m & ~CLIDR_CTYPE_IO) != 0) {
print_cpu_cache(desc, sb, desc->ccsidr[i][j], false,
(clidr & CLIDR_CTYPE_UNIFIED));
}
sbuf_printf(sb, "\n");
}
sbuf_finish(sb);
printf("%s", sbuf_data(sb));
}
static void
print_cpu_features(u_int cpu, struct cpu_desc *desc,
struct cpu_desc *prev_desc)
{
struct sbuf *sb;
sb = sbuf_new_auto();
sbuf_printf(sb, "CPU%3u: ", cpu);
print_cpu_midr(sb, cpu);
sbuf_cat(sb, " affinity:");
switch(cpu_aff_levels) {
default:
case 4:
sbuf_printf(sb, " %2d", CPU_AFF3(desc->mpidr));
/* FALLTHROUGH */
case 3:
sbuf_printf(sb, " %2d", CPU_AFF2(desc->mpidr));
/* FALLTHROUGH */
case 2:
sbuf_printf(sb, " %2d", CPU_AFF1(desc->mpidr));
/* FALLTHROUGH */
case 1:
case 0: /* On UP this will be zero */
sbuf_printf(sb, " %2d", CPU_AFF0(desc->mpidr));
break;
}
sbuf_finish(sb);
printf("%s\n", sbuf_data(sb));
sbuf_clear(sb);
/*
* There is a hardware errata where, if one CPU is performing a TLB
* invalidation while another is performing a store-exclusive the
* store-exclusive may return the wrong status. A workaround seems
* to be to use an IPI to invalidate on each CPU, however given the
* limited number of affected units (pass 1.1 is the evaluation
* hardware revision), and the lack of information from Cavium
* this has not been implemented.
*
* At the time of writing this the only information is from:
* https://lkml.org/lkml/2016/8/4/722
*/
/*
* XXX: CPU_MATCH_ERRATA_CAVIUM_THUNDERX_1_1 on its own also
* triggers on pass 2.0+.
*/
if (cpu == 0 && CPU_VAR(PCPU_GET(midr)) == 0 &&
CPU_MATCH_ERRATA_CAVIUM_THUNDERX_1_1)
printf("WARNING: ThunderX Pass 1.1 detected.\nThis has known "
"hardware bugs that may cause the incorrect operation of "
"atomic operations.\n");
#define SHOULD_PRINT_REG(_reg) \
(prev_desc == NULL || desc->_reg != prev_desc->_reg)
/* Cache Type Register */
if (SHOULD_PRINT_REG(ctr))
print_id_register(sb, "Cache Type", desc->ctr, ctr_fields);
/* AArch64 Instruction Set Attribute Register 0 */
if (SHOULD_PRINT_REG(id_aa64isar0))
print_id_register(sb, "Instruction Set Attributes 0",
desc->id_aa64isar0, id_aa64isar0_fields);
/* AArch64 Instruction Set Attribute Register 1 */
if (SHOULD_PRINT_REG(id_aa64isar1))
print_id_register(sb, "Instruction Set Attributes 1",
desc->id_aa64isar1, id_aa64isar1_fields);
/* AArch64 Instruction Set Attribute Register 2 */
if (SHOULD_PRINT_REG(id_aa64isar2))
print_id_register(sb, "Instruction Set Attributes 2",
desc->id_aa64isar2, id_aa64isar2_fields);
/* AArch64 Processor Feature Register 0 */
if (SHOULD_PRINT_REG(id_aa64pfr0))
print_id_register(sb, "Processor Features 0",
desc->id_aa64pfr0, id_aa64pfr0_fields);
/* AArch64 Processor Feature Register 1 */
if (SHOULD_PRINT_REG(id_aa64pfr1))
print_id_register(sb, "Processor Features 1",
desc->id_aa64pfr1, id_aa64pfr1_fields);
/* AArch64 Processor Feature Register 2 */
if (SHOULD_PRINT_REG(id_aa64pfr2))
print_id_register(sb, "Processor Features 2",
desc->id_aa64pfr2, id_aa64pfr2_fields);
/* AArch64 Memory Model Feature Register 0 */
if (SHOULD_PRINT_REG(id_aa64mmfr0))
print_id_register(sb, "Memory Model Features 0",
desc->id_aa64mmfr0, id_aa64mmfr0_fields);
/* AArch64 Memory Model Feature Register 1 */
if (SHOULD_PRINT_REG(id_aa64mmfr1))
print_id_register(sb, "Memory Model Features 1",
desc->id_aa64mmfr1, id_aa64mmfr1_fields);
/* AArch64 Memory Model Feature Register 2 */
if (SHOULD_PRINT_REG(id_aa64mmfr2))
print_id_register(sb, "Memory Model Features 2",
desc->id_aa64mmfr2, id_aa64mmfr2_fields);
/* AArch64 Memory Model Feature Register 3 */
if (SHOULD_PRINT_REG(id_aa64mmfr3))
print_id_register(sb, "Memory Model Features 3",
desc->id_aa64mmfr3, id_aa64mmfr3_fields);
/* AArch64 Memory Model Feature Register 4 */
if (SHOULD_PRINT_REG(id_aa64mmfr4))
print_id_register(sb, "Memory Model Features 4",
desc->id_aa64mmfr4, id_aa64mmfr4_fields);
/* AArch64 Debug Feature Register 0 */
if (SHOULD_PRINT_REG(id_aa64dfr0))
print_id_register(sb, "Debug Features 0",
desc->id_aa64dfr0, id_aa64dfr0_fields);
/* AArch64 Memory Model Feature Register 1 */
if (SHOULD_PRINT_REG(id_aa64dfr1))
print_id_register(sb, "Debug Features 1",
desc->id_aa64dfr1, id_aa64dfr1_fields);
/* AArch64 Auxiliary Feature Register 0 */
if (SHOULD_PRINT_REG(id_aa64afr0))
print_id_register(sb, "Auxiliary Features 0",
desc->id_aa64afr0, id_aa64afr0_fields);
/* AArch64 Auxiliary Feature Register 1 */
if (SHOULD_PRINT_REG(id_aa64afr1))
print_id_register(sb, "Auxiliary Features 1",
desc->id_aa64afr1, id_aa64afr1_fields);
/* AArch64 SVE Feature Register 0 */
if (desc->have_sve) {
if (SHOULD_PRINT_REG(id_aa64zfr0) ||
!prev_desc->have_sve) {
print_id_register(sb, "SVE Features 0",
desc->id_aa64zfr0, id_aa64zfr0_fields);
}
}
#ifdef COMPAT_FREEBSD32
/* AArch32 Instruction Set Attribute Register 5 */
if (SHOULD_PRINT_REG(id_isar5))
print_id_register(sb, "AArch32 Instruction Set Attributes 5",
desc->id_isar5, id_isar5_fields);
/* AArch32 Media and VFP Feature Register 0 */
if (SHOULD_PRINT_REG(mvfr0))
print_id_register(sb, "AArch32 Media and VFP Features 0",
desc->mvfr0, mvfr0_fields);
/* AArch32 Media and VFP Feature Register 1 */
if (SHOULD_PRINT_REG(mvfr1))
print_id_register(sb, "AArch32 Media and VFP Features 1",
desc->mvfr1, mvfr1_fields);
#endif
if (bootverbose)
print_cpu_caches(sb, desc);
sbuf_delete(sb);
sb = NULL;
#undef SHOULD_PRINT_REG
#undef SEP_STR
}
void
identify_cache(uint64_t ctr)
{
/* Identify the L1 cache type */
switch (CTR_L1IP_VAL(ctr)) {
case CTR_L1IP_PIPT:
break;
default:
case CTR_L1IP_VIPT:
icache_aliasing = true;
break;
}
if (dcache_line_size == 0) {
KASSERT(icache_line_size == 0, ("%s: i-cacheline size set: %ld",
__func__, icache_line_size));
/* Get the D cache line size */
dcache_line_size = CTR_DLINE_SIZE(ctr);
/* And the same for the I cache */
icache_line_size = CTR_ILINE_SIZE(ctr);
idcache_line_size = MIN(dcache_line_size, icache_line_size);
}
if (dcache_line_size != CTR_DLINE_SIZE(ctr)) {
printf("WARNING: D-cacheline size mismatch %ld != %d\n",
dcache_line_size, CTR_DLINE_SIZE(ctr));
}
if (icache_line_size != CTR_ILINE_SIZE(ctr)) {
printf("WARNING: I-cacheline size mismatch %ld != %d\n",
icache_line_size, CTR_ILINE_SIZE(ctr));
}
}
void
identify_cpu(u_int cpu)
{
struct cpu_desc *desc;
uint64_t clidr;
desc = get_cpu_desc(cpu);
/* Save affinity for current CPU */
desc->mpidr = get_mpidr();
CPU_AFFINITY(cpu) = desc->mpidr & CPU_AFF_MASK;
desc->ctr = READ_SPECIALREG(ctr_el0);
desc->id_aa64dfr0 = READ_SPECIALREG(ID_AA64DFR0_EL1_REG);
desc->id_aa64dfr1 = READ_SPECIALREG(ID_AA64DFR1_EL1_REG);
desc->id_aa64isar0 = READ_SPECIALREG(ID_AA64ISAR0_EL1_REG);
desc->id_aa64isar1 = READ_SPECIALREG(ID_AA64ISAR1_EL1_REG);
desc->id_aa64isar2 = READ_SPECIALREG(ID_AA64ISAR2_EL1_REG);
desc->id_aa64mmfr0 = READ_SPECIALREG(ID_AA64MMFR0_EL1_REG);
desc->id_aa64mmfr1 = READ_SPECIALREG(ID_AA64MMFR1_EL1_REG);
desc->id_aa64mmfr2 = READ_SPECIALREG(ID_AA64MMFR2_EL1_REG);
desc->id_aa64mmfr3 = READ_SPECIALREG(ID_AA64MMFR3_EL1_REG);
desc->id_aa64mmfr4 = READ_SPECIALREG(ID_AA64MMFR4_EL1_REG);
desc->id_aa64pfr0 = READ_SPECIALREG(ID_AA64PFR0_EL1_REG);
desc->id_aa64pfr1 = READ_SPECIALREG(ID_AA64PFR1_EL1_REG);
desc->id_aa64pfr2 = READ_SPECIALREG(ID_AA64PFR2_EL1_REG);
/*
* ID_AA64ZFR0_EL1 is only valid when at least one of:
* - ID_AA64PFR0_EL1.SVE is non-zero
* - ID_AA64PFR1_EL1.SME is non-zero
* In other cases it is zero, but still safe to read
*/
desc->have_sve =
(ID_AA64PFR0_SVE_VAL(desc->id_aa64pfr0) != 0);
desc->id_aa64zfr0 = READ_SPECIALREG(ID_AA64ZFR0_EL1_REG);
desc->clidr = READ_SPECIALREG(clidr_el1);
clidr = desc->clidr;
for (int i = 0; (clidr & CLIDR_CTYPE_MASK) != 0; i++, clidr >>= 3) {
int j = 0;
if ((clidr & CLIDR_CTYPE_IO)) {
WRITE_SPECIALREG(csselr_el1,
CSSELR_Level(i) | CSSELR_InD);
desc->ccsidr[i][j++] =
READ_SPECIALREG(ccsidr_el1);
}
if ((clidr & ~CLIDR_CTYPE_IO) == 0)
continue;
WRITE_SPECIALREG(csselr_el1, CSSELR_Level(i));
desc->ccsidr[i][j] = READ_SPECIALREG(ccsidr_el1);
}
#ifdef COMPAT_FREEBSD32
/* Only read aarch32 SRs if EL0-32 is available */
if (ID_AA64PFR0_EL0_VAL(desc->id_aa64pfr0) == ID_AA64PFR0_EL0_64_32) {
desc->id_isar5 = READ_SPECIALREG(id_isar5_el1);
desc->mvfr0 = READ_SPECIALREG(mvfr0_el1);
desc->mvfr1 = READ_SPECIALREG(mvfr1_el1);
}
#endif
}
static void
check_cpu_regs(u_int cpu, struct cpu_desc *desc, struct cpu_desc *prev_desc)
{
switch (cpu_aff_levels) {
case 0:
if (CPU_AFF0(desc->mpidr) != CPU_AFF0(prev_desc->mpidr))
cpu_aff_levels = 1;
/* FALLTHROUGH */
case 1:
if (CPU_AFF1(desc->mpidr) != CPU_AFF1(prev_desc->mpidr))
cpu_aff_levels = 2;
/* FALLTHROUGH */
case 2:
if (CPU_AFF2(desc->mpidr) != CPU_AFF2(prev_desc->mpidr))
cpu_aff_levels = 3;
/* FALLTHROUGH */
case 3:
if (CPU_AFF3(desc->mpidr) != CPU_AFF3(prev_desc->mpidr))
cpu_aff_levels = 4;
break;
}
if (desc->ctr != prev_desc->ctr) {
/*
* If the cache is different on different cores we should
* emulate for userspace to provide a uniform value
*/
emulate_ctr = true;
/*
* If the cache type register is different we may
* have a different l1 cache type.
*/
identify_cache(desc->ctr);
}
}
diff --git a/sys/arm64/arm64/trap.c b/sys/arm64/arm64/trap.c
index fdcc38cd9a31..bed58095201a 100644
--- a/sys/arm64/arm64/trap.c
+++ b/sys/arm64/arm64/trap.c
@@ -1,792 +1,790 @@
/*-
* Copyright (c) 2014 Andrew Turner
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include "opt_ddb.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef KDB
#include
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef KDTRACE_HOOKS
#include
#endif
#ifdef VFP
#include
#endif
#ifdef KDB
#include
#endif
#ifdef DDB
#include
#include
#endif
/* Called from exception.S */
void do_el1h_sync(struct thread *, struct trapframe *);
void do_el0_sync(struct thread *, struct trapframe *);
void do_el0_error(struct trapframe *);
void do_serror(struct trapframe *);
void unhandled_exception(struct trapframe *);
static void print_gp_register(const char *name, uint64_t value);
static void print_registers(struct trapframe *frame);
int (*dtrace_invop_jump_addr)(struct trapframe *);
u_long cnt_efirt_faults;
int print_efirt_faults;
typedef void (abort_handler)(struct thread *, struct trapframe *, uint64_t,
uint64_t, int);
static abort_handler align_abort;
static abort_handler data_abort;
static abort_handler external_abort;
static abort_handler *abort_handlers[] = {
[ISS_DATA_DFSC_TF_L0] = data_abort,
[ISS_DATA_DFSC_TF_L1] = data_abort,
[ISS_DATA_DFSC_TF_L2] = data_abort,
[ISS_DATA_DFSC_TF_L3] = data_abort,
[ISS_DATA_DFSC_AFF_L1] = data_abort,
[ISS_DATA_DFSC_AFF_L2] = data_abort,
[ISS_DATA_DFSC_AFF_L3] = data_abort,
[ISS_DATA_DFSC_PF_L1] = data_abort,
[ISS_DATA_DFSC_PF_L2] = data_abort,
[ISS_DATA_DFSC_PF_L3] = data_abort,
[ISS_DATA_DFSC_ALIGN] = align_abort,
[ISS_DATA_DFSC_EXT] = external_abort,
[ISS_DATA_DFSC_EXT_L0] = external_abort,
[ISS_DATA_DFSC_EXT_L1] = external_abort,
[ISS_DATA_DFSC_EXT_L2] = external_abort,
[ISS_DATA_DFSC_EXT_L3] = external_abort,
[ISS_DATA_DFSC_ECC] = external_abort,
[ISS_DATA_DFSC_ECC_L0] = external_abort,
[ISS_DATA_DFSC_ECC_L1] = external_abort,
[ISS_DATA_DFSC_ECC_L2] = external_abort,
[ISS_DATA_DFSC_ECC_L3] = external_abort,
};
static __inline void
call_trapsignal(struct thread *td, int sig, int code, void *addr, int trapno)
{
ksiginfo_t ksi;
ksiginfo_init_trap(&ksi);
ksi.ksi_signo = sig;
ksi.ksi_code = code;
ksi.ksi_addr = addr;
ksi.ksi_trapno = trapno;
trapsignal(td, &ksi);
}
int
cpu_fetch_syscall_args(struct thread *td)
{
struct proc *p;
syscallarg_t *ap, *dst_ap;
struct syscall_args *sa;
p = td->td_proc;
sa = &td->td_sa;
ap = td->td_frame->tf_x;
dst_ap = &sa->args[0];
sa->code = td->td_frame->tf_x[8];
sa->original_code = sa->code;
if (__predict_false(sa->code == SYS_syscall || sa->code == SYS___syscall)) {
sa->code = *ap++;
} else {
*dst_ap++ = *ap++;
}
if (__predict_false(sa->code >= p->p_sysent->sv_size))
sa->callp = &nosys_sysent;
else
sa->callp = &p->p_sysent->sv_table[sa->code];
KASSERT(sa->callp->sy_narg <= nitems(sa->args),
("Syscall %d takes too many arguments", sa->code));
memcpy(dst_ap, ap, (nitems(sa->args) - 1) * sizeof(*dst_ap));
td->td_retval[0] = 0;
td->td_retval[1] = 0;
return (0);
}
#include "../../kern/subr_syscall.c"
/*
* Test for fault generated by given access instruction in
* bus_peek_ or bus_poke_ bus function.
*/
extern uint32_t generic_bs_peek_1f, generic_bs_peek_2f;
extern uint32_t generic_bs_peek_4f, generic_bs_peek_8f;
extern uint32_t generic_bs_poke_1f, generic_bs_poke_2f;
extern uint32_t generic_bs_poke_4f, generic_bs_poke_8f;
static bool
test_bs_fault(void *addr)
{
return (addr == &generic_bs_peek_1f ||
addr == &generic_bs_peek_2f ||
addr == &generic_bs_peek_4f ||
addr == &generic_bs_peek_8f ||
addr == &generic_bs_poke_1f ||
addr == &generic_bs_poke_2f ||
addr == &generic_bs_poke_4f ||
addr == &generic_bs_poke_8f);
}
static void
svc_handler(struct thread *td, struct trapframe *frame)
{
if ((frame->tf_esr & ESR_ELx_ISS_MASK) == 0) {
syscallenter(td);
syscallret(td);
} else {
call_trapsignal(td, SIGILL, ILL_ILLOPN, (void *)frame->tf_elr,
ESR_ELx_EXCEPTION(frame->tf_esr));
userret(td, frame);
}
}
static void
align_abort(struct thread *td, struct trapframe *frame, uint64_t esr,
uint64_t far, int lower)
{
if (!lower) {
print_registers(frame);
print_gp_register("far", far);
printf(" esr: 0x%.16lx\n", esr);
panic("Misaligned access from kernel space!");
}
call_trapsignal(td, SIGBUS, BUS_ADRALN, (void *)frame->tf_elr,
ESR_ELx_EXCEPTION(frame->tf_esr));
userret(td, frame);
}
static void
external_abort(struct thread *td, struct trapframe *frame, uint64_t esr,
uint64_t far, int lower)
{
if (lower) {
call_trapsignal(td, SIGBUS, BUS_OBJERR, (void *)far,
ESR_ELx_EXCEPTION(frame->tf_esr));
userret(td, frame);
return;
}
/*
* Try to handle synchronous external aborts caused by
* bus_space_peek() and/or bus_space_poke() functions.
*/
if (test_bs_fault((void *)frame->tf_elr)) {
frame->tf_elr = (uint64_t)generic_bs_fault;
return;
}
print_registers(frame);
print_gp_register("far", far);
panic("Unhandled external data abort");
}
/*
* It is unsafe to access the stack canary value stored in "td" until
* kernel map translation faults are handled, see the pmap_klookup() call below.
* Thus, stack-smashing detection with per-thread canaries must be disabled in
* this function.
*/
static void NO_PERTHREAD_SSP
data_abort(struct thread *td, struct trapframe *frame, uint64_t esr,
uint64_t far, int lower)
{
struct vm_map *map;
struct pcb *pcb;
vm_prot_t ftype;
int error, sig, ucode;
#ifdef KDB
bool handled;
#endif
/*
* According to the ARMv8-A rev. A.g, B2.10.5 "Load-Exclusive
* and Store-Exclusive instruction usage restrictions", state
* of the exclusive monitors after data abort exception is unknown.
*/
clrex();
#ifdef KDB
if (kdb_active) {
kdb_reenter();
return;
}
#endif
if (lower) {
map = &td->td_proc->p_vmspace->vm_map;
} else if (!ADDR_IS_CANONICAL(far)) {
/* We received a TBI/PAC/etc. fault from the kernel */
error = KERN_INVALID_ADDRESS;
pcb = td->td_pcb;
goto bad_far;
} else if (ADDR_IS_KERNEL(far)) {
/*
* Handle a special case: the data abort was caused by accessing
* a thread structure while its mapping was being promoted or
* demoted, as a consequence of the break-before-make rule. It
* is not safe to enable interrupts or dereference "td" before
* this case is handled.
*
* In principle, if pmap_klookup() fails, there is no need to
* call pmap_fault() below, but avoiding that call is not worth
* the effort.
*/
if (ESR_ELx_EXCEPTION(esr) == EXCP_DATA_ABORT) {
switch (esr & ISS_DATA_DFSC_MASK) {
case ISS_DATA_DFSC_TF_L0:
case ISS_DATA_DFSC_TF_L1:
case ISS_DATA_DFSC_TF_L2:
case ISS_DATA_DFSC_TF_L3:
if (pmap_klookup(far, NULL))
return;
break;
}
}
if (td->td_md.md_spinlock_count == 0 &&
(frame->tf_spsr & PSR_DAIF_INTR) != PSR_DAIF_INTR) {
MPASS((frame->tf_spsr & PSR_DAIF_INTR) == 0);
intr_enable();
}
map = kernel_map;
} else {
if (td->td_md.md_spinlock_count == 0 &&
(frame->tf_spsr & PSR_DAIF_INTR) != PSR_DAIF_INTR) {
MPASS((frame->tf_spsr & PSR_DAIF_INTR) == 0);
intr_enable();
}
map = &td->td_proc->p_vmspace->vm_map;
if (map == NULL)
map = kernel_map;
}
pcb = td->td_pcb;
/*
* Try to handle translation, access flag, and permission faults.
* Translation faults may occur as a result of the required
* break-before-make sequence used when promoting or demoting
* superpages. Such faults must not occur while holding the pmap lock,
* or pmap_fault() will recurse on that lock.
*/
if ((lower || map == kernel_map || pcb->pcb_onfault != 0) &&
pmap_fault(map->pmap, esr, far) == KERN_SUCCESS)
return;
#ifdef INVARIANTS
if (td->td_md.md_spinlock_count != 0) {
print_registers(frame);
print_gp_register("far", far);
printf(" esr: 0x%.16lx\n", esr);
panic("data abort with spinlock held (spinlock count %d != 0)",
td->td_md.md_spinlock_count);
}
#endif
if ((td->td_pflags & TDP_NOFAULTING) == 0 &&
(td->td_critnest != 0 || WITNESS_CHECK(WARN_SLEEPOK |
WARN_GIANTOK, NULL, "Kernel page fault") != 0)) {
print_registers(frame);
print_gp_register("far", far);
printf(" esr: 0x%.16lx\n", esr);
panic("data abort in critical section or under mutex");
}
switch (ESR_ELx_EXCEPTION(esr)) {
case EXCP_INSN_ABORT:
case EXCP_INSN_ABORT_L:
ftype = VM_PROT_EXECUTE;
break;
default:
/*
* If the exception was because of a read or cache operation
* pass a read fault type into the vm code. Cache operations
* need read permission but will set the WnR flag when the
* memory is unmapped.
*/
if ((esr & ISS_DATA_WnR) == 0 || (esr & ISS_DATA_CM) != 0)
ftype = VM_PROT_READ;
else
ftype = VM_PROT_WRITE;
break;
}
/* Fault in the page. */
error = vm_fault_trap(map, far, ftype, VM_FAULT_NORMAL, &sig, &ucode);
if (error != KERN_SUCCESS) {
if (lower) {
call_trapsignal(td, sig, ucode, (void *)far,
ESR_ELx_EXCEPTION(esr));
} else {
bad_far:
if (td->td_intr_nesting_level == 0 &&
pcb->pcb_onfault != 0) {
frame->tf_elr = pcb->pcb_onfault;
return;
}
printf("Fatal data abort:\n");
print_registers(frame);
print_gp_register("far", far);
printf(" esr: 0x%.16lx\n", esr);
#ifdef KDB
if (debugger_on_trap) {
kdb_why = KDB_WHY_TRAP;
handled = kdb_trap(ESR_ELx_EXCEPTION(esr), 0,
frame);
kdb_why = KDB_WHY_UNSET;
if (handled)
return;
}
#endif
panic("vm_fault failed: 0x%lx error %d",
frame->tf_elr, error);
}
}
if (lower)
userret(td, frame);
}
static void
print_gp_register(const char *name, uint64_t value)
{
#if defined(DDB)
c_db_sym_t sym;
const char *sym_name;
db_expr_t sym_value;
db_expr_t offset;
#endif
printf(" %s: 0x%.16lx", name, value);
#if defined(DDB)
/* If this looks like a kernel address try to find the symbol */
if (value >= VM_MIN_KERNEL_ADDRESS) {
sym = db_search_symbol(value, DB_STGY_ANY, &offset);
if (sym != C_DB_SYM_NULL) {
db_symbol_values(sym, &sym_name, &sym_value);
printf(" (%s + 0x%lx)", sym_name, offset);
}
}
#endif
printf("\n");
}
static void
print_registers(struct trapframe *frame)
{
char name[4];
u_int reg;
for (reg = 0; reg < nitems(frame->tf_x); reg++) {
snprintf(name, sizeof(name), "%sx%d", (reg < 10) ? " " : "",
reg);
print_gp_register(name, frame->tf_x[reg]);
}
printf(" sp: 0x%.16lx\n", frame->tf_sp);
print_gp_register(" lr", frame->tf_lr);
print_gp_register("elr", frame->tf_elr);
printf("spsr: 0x%.16lx\n", frame->tf_spsr);
}
#ifdef VFP
static void
fpe_trap(struct thread *td, void *addr, uint32_t exception)
{
int code;
code = FPE_FLTIDO;
if ((exception & ISS_FP_TFV) != 0) {
if ((exception & ISS_FP_IOF) != 0)
code = FPE_FLTINV;
else if ((exception & ISS_FP_DZF) != 0)
code = FPE_FLTDIV;
else if ((exception & ISS_FP_OFF) != 0)
code = FPE_FLTOVF;
else if ((exception & ISS_FP_UFF) != 0)
code = FPE_FLTUND;
else if ((exception & ISS_FP_IXF) != 0)
code = FPE_FLTRES;
}
call_trapsignal(td, SIGFPE, code, addr, exception);
}
#endif
/*
* See the comment above data_abort().
*/
void NO_PERTHREAD_SSP
do_el1h_sync(struct thread *td, struct trapframe *frame)
{
uint32_t exception;
uint64_t esr, far;
int dfsc;
kasan_mark(frame, sizeof(*frame), sizeof(*frame), 0);
kmsan_mark(frame, sizeof(*frame), KMSAN_STATE_INITED);
far = frame->tf_far;
/* Read the esr register to get the exception details */
esr = frame->tf_esr;
exception = ESR_ELx_EXCEPTION(esr);
#ifdef KDTRACE_HOOKS
if (dtrace_trap_func != NULL && (*dtrace_trap_func)(frame, exception))
return;
#endif
CTR4(KTR_TRAP, "%s: exception=%lu, elr=0x%lx, esr=0x%lx",
__func__, exception, frame->tf_elr, esr);
/*
* Enable debug exceptions if we aren't already handling one. They will
* be masked again in the exception handler's epilogue.
*/
switch (exception) {
case EXCP_BRK:
case EXCP_BRKPT_EL1:
case EXCP_WATCHPT_EL1:
case EXCP_SOFTSTP_EL1:
break;
default:
dbg_enable();
break;
}
switch (exception) {
case EXCP_FP_SIMD:
case EXCP_TRAP_FP:
#ifdef VFP
if ((td->td_pcb->pcb_fpflags & PCB_FP_KERN) != 0) {
vfp_restore_state();
} else
#endif
{
print_registers(frame);
printf(" esr: 0x%.16lx\n", esr);
panic("VFP exception in the kernel");
}
break;
case EXCP_INSN_ABORT:
case EXCP_DATA_ABORT:
dfsc = esr & ISS_DATA_DFSC_MASK;
if (dfsc < nitems(abort_handlers) &&
abort_handlers[dfsc] != NULL) {
abort_handlers[dfsc](td, frame, esr, far, 0);
} else {
print_registers(frame);
print_gp_register("far", far);
printf(" esr: 0x%.16lx\n", esr);
panic("Unhandled EL1 %s abort: 0x%x",
exception == EXCP_INSN_ABORT ? "instruction" :
"data", dfsc);
}
break;
case EXCP_BRK:
#ifdef KDTRACE_HOOKS
if ((esr & ESR_ELx_ISS_MASK) == 0x40d /* BRK_IMM16_VAL */ &&
dtrace_invop_jump_addr != NULL &&
dtrace_invop_jump_addr(frame) == 0)
break;
#endif
#ifdef KDB
kdb_trap(exception, 0, frame);
#else
panic("No debugger in kernel.");
#endif
break;
case EXCP_BRKPT_EL1:
case EXCP_WATCHPT_EL1:
case EXCP_SOFTSTP_EL1:
#ifdef KDB
kdb_trap(exception, 0, frame);
#else
panic("No debugger in kernel.");
#endif
break;
case EXCP_FPAC:
/* We can see this if the authentication on PAC fails */
print_registers(frame);
print_gp_register("far", far);
panic("FPAC kernel exception");
break;
case EXCP_UNKNOWN:
- if (undef_insn(1, frame))
- break;
print_registers(frame);
print_gp_register("far", far);
panic("Undefined instruction: %08x",
*(uint32_t *)frame->tf_elr);
break;
case EXCP_BTI:
print_registers(frame);
print_gp_register("far", far);
panic("Branch Target exception");
break;
default:
print_registers(frame);
print_gp_register("far", far);
panic("Unknown kernel exception 0x%x esr_el1 0x%lx", exception,
esr);
}
}
void
do_el0_sync(struct thread *td, struct trapframe *frame)
{
pcpu_bp_harden bp_harden;
uint32_t exception;
uint64_t esr, far;
int dfsc;
/* Check we have a sane environment when entering from userland */
KASSERT((uintptr_t)get_pcpu() >= VM_MIN_KERNEL_ADDRESS,
("Invalid pcpu address from userland: %p (tpidr 0x%lx)",
get_pcpu(), READ_SPECIALREG(tpidr_el1)));
kasan_mark(frame, sizeof(*frame), sizeof(*frame), 0);
kmsan_mark(frame, sizeof(*frame), KMSAN_STATE_INITED);
far = frame->tf_far;
esr = frame->tf_esr;
exception = ESR_ELx_EXCEPTION(esr);
if (exception == EXCP_INSN_ABORT_L && far > VM_MAXUSER_ADDRESS) {
/*
* Userspace may be trying to train the branch predictor to
* attack the kernel. If we are on a CPU affected by this
* call the handler to clear the branch predictor state.
*/
bp_harden = PCPU_GET(bp_harden);
if (bp_harden != NULL)
bp_harden();
}
intr_enable();
CTR4(KTR_TRAP, "%s: exception=%lu, elr=0x%lx, esr=0x%lx",
__func__, exception, frame->tf_elr, esr);
switch (exception) {
case EXCP_FP_SIMD:
#ifdef VFP
vfp_restore_state();
#else
panic("VFP exception in userland");
#endif
break;
case EXCP_TRAP_FP:
#ifdef VFP
fpe_trap(td, (void *)frame->tf_elr, esr);
userret(td, frame);
#else
panic("VFP exception in userland");
#endif
break;
case EXCP_SVE:
/* Returns true if this thread can use SVE */
if (!sve_restore_state(td))
call_trapsignal(td, SIGILL, ILL_ILLTRP,
(void *)frame->tf_elr, exception);
userret(td, frame);
break;
case EXCP_SVC32:
case EXCP_SVC64:
svc_handler(td, frame);
break;
case EXCP_INSN_ABORT_L:
case EXCP_DATA_ABORT_L:
case EXCP_DATA_ABORT:
dfsc = esr & ISS_DATA_DFSC_MASK;
if (dfsc < nitems(abort_handlers) &&
abort_handlers[dfsc] != NULL)
abort_handlers[dfsc](td, frame, esr, far, 1);
else {
print_registers(frame);
print_gp_register("far", far);
printf(" esr: 0x%.16lx\n", esr);
panic("Unhandled EL0 %s abort: 0x%x",
exception == EXCP_INSN_ABORT_L ? "instruction" :
"data", dfsc);
}
break;
case EXCP_UNKNOWN:
- if (!undef_insn(0, frame))
+ if (!undef_insn(frame))
call_trapsignal(td, SIGILL, ILL_ILLTRP, (void *)far,
exception);
userret(td, frame);
break;
case EXCP_FPAC:
call_trapsignal(td, SIGILL, ILL_ILLOPN, (void *)frame->tf_elr,
exception);
userret(td, frame);
break;
case EXCP_SP_ALIGN:
call_trapsignal(td, SIGBUS, BUS_ADRALN, (void *)frame->tf_sp,
exception);
userret(td, frame);
break;
case EXCP_PC_ALIGN:
call_trapsignal(td, SIGBUS, BUS_ADRALN, (void *)frame->tf_elr,
exception);
userret(td, frame);
break;
case EXCP_BRKPT_EL0:
case EXCP_BRK:
#ifdef COMPAT_FREEBSD32
case EXCP_BRKPT_32:
#endif /* COMPAT_FREEBSD32 */
call_trapsignal(td, SIGTRAP, TRAP_BRKPT, (void *)frame->tf_elr,
exception);
userret(td, frame);
break;
case EXCP_WATCHPT_EL0:
call_trapsignal(td, SIGTRAP, TRAP_TRACE, (void *)far,
exception);
userret(td, frame);
break;
case EXCP_MSR:
/*
* The CPU can raise EXCP_MSR when userspace executes an mrs
* instruction to access a special register userspace doesn't
* have access to.
*/
- if (!undef_insn(0, frame))
+ if (!undef_insn(frame))
call_trapsignal(td, SIGILL, ILL_PRVOPC,
(void *)frame->tf_elr, exception);
userret(td, frame);
break;
case EXCP_SOFTSTP_EL0:
PROC_LOCK(td->td_proc);
if ((td->td_dbgflags & TDB_STEP) != 0) {
td->td_frame->tf_spsr &= ~PSR_SS;
td->td_pcb->pcb_flags &= ~PCB_SINGLE_STEP;
WRITE_SPECIALREG(mdscr_el1,
READ_SPECIALREG(mdscr_el1) & ~MDSCR_SS);
}
PROC_UNLOCK(td->td_proc);
call_trapsignal(td, SIGTRAP, TRAP_TRACE,
(void *)frame->tf_elr, exception);
userret(td, frame);
break;
case EXCP_BTI:
call_trapsignal(td, SIGILL, ILL_ILLOPC, (void *)frame->tf_elr,
exception);
userret(td, frame);
break;
default:
call_trapsignal(td, SIGBUS, BUS_OBJERR, (void *)frame->tf_elr,
exception);
userret(td, frame);
break;
}
KASSERT(
(td->td_pcb->pcb_fpflags & ~(PCB_FP_USERMASK|PCB_FP_SVEVALID)) == 0,
("Kernel VFP flags set while entering userspace"));
KASSERT(
td->td_pcb->pcb_fpusaved == &td->td_pcb->pcb_fpustate,
("Kernel VFP state in use when entering userspace"));
}
/*
* TODO: We will need to handle these later when we support ARMv8.2 RAS.
*/
void
do_serror(struct trapframe *frame)
{
uint64_t esr, far;
kasan_mark(frame, sizeof(*frame), sizeof(*frame), 0);
kmsan_mark(frame, sizeof(*frame), KMSAN_STATE_INITED);
far = frame->tf_far;
esr = frame->tf_esr;
print_registers(frame);
print_gp_register("far", far);
printf(" esr: 0x%.16lx\n", esr);
panic("Unhandled System Error");
}
void
unhandled_exception(struct trapframe *frame)
{
uint64_t esr, far;
kasan_mark(frame, sizeof(*frame), sizeof(*frame), 0);
kmsan_mark(frame, sizeof(*frame), KMSAN_STATE_INITED);
far = frame->tf_far;
esr = frame->tf_esr;
print_registers(frame);
print_gp_register("far", far);
printf(" esr: 0x%.16lx\n", esr);
panic("Unhandled exception");
}
diff --git a/sys/arm64/arm64/undefined.c b/sys/arm64/arm64/undefined.c
index 1f44114af544..71c9b689aa78 100644
--- a/sys/arm64/arm64/undefined.c
+++ b/sys/arm64/arm64/undefined.c
@@ -1,308 +1,299 @@
/*-
* Copyright (c) 2017 Andrew Turner
* All rights reserved.
*
* This software was developed by SRI International and the University of
* Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237
* ("CTSRD"), as part of the DARPA CRASH research programme.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define _MD_WANT_SWAPWORD
#include
#include
#include
#include
#include
#include
/* Low bit masked off */
#define INSN_COND(insn) ((insn >> 28) & ~0x1)
#define INSN_COND_INVERTED(insn) ((insn >> 28) & 0x1)
#define INSN_COND_EQ 0x00 /* NE */
#define INSN_COND_CS 0x02 /* CC */
#define INSN_COND_MI 0x04 /* PL */
#define INSN_COND_VS 0x06 /* VC */
#define INSN_COND_HI 0x08 /* LS */
#define INSN_COND_GE 0x0a /* LT */
#define INSN_COND_GT 0x0c /* LE */
#define INSN_COND_AL 0x0e /* Always */
MALLOC_DEFINE(M_UNDEF, "undefhandler", "Undefined instruction handler data");
#ifdef COMPAT_FREEBSD32
#ifndef EMUL_SWP
#define EMUL_SWP 0
#endif
SYSCTL_DECL(_compat_arm);
static bool compat32_emul_swp = EMUL_SWP;
SYSCTL_BOOL(_compat_arm, OID_AUTO, emul_swp,
CTLFLAG_RWTUN | CTLFLAG_MPSAFE, &compat32_emul_swp, 0,
"Enable SWP/SWPB emulation");
#endif
struct undef_handler {
LIST_ENTRY(undef_handler) uh_link;
undef_handler_t uh_handler;
};
/*
- * Create two undefined instruction handler lists, one for userspace, one for
- * the kernel. This allows us to handle instructions that will trap
+ * Create the undefined instruction handler lists.
+ * This allows us to handle instructions that will trap.
*/
-LIST_HEAD(, undef_handler) undef_handlers[2];
+LIST_HEAD(, undef_handler) undef_handlers =
+ LIST_HEAD_INITIALIZER(undef_handlers);
static bool
arm_cond_match(uint32_t insn, struct trapframe *frame)
{
uint64_t spsr;
uint32_t cond;
bool invert;
bool match;
/*
* Generally based on the function of the same name in NetBSD, though
* condition bits left in their original position rather than shifting
* over the low bit that indicates inversion for quicker sanity checking
* against spec.
*/
spsr = frame->tf_spsr;
cond = INSN_COND(insn);
invert = INSN_COND_INVERTED(insn);
switch (cond) {
case INSN_COND_EQ:
match = (spsr & PSR_Z) != 0;
break;
case INSN_COND_CS:
match = (spsr & PSR_C) != 0;
break;
case INSN_COND_MI:
match = (spsr & PSR_N) != 0;
break;
case INSN_COND_VS:
match = (spsr & PSR_V) != 0;
break;
case INSN_COND_HI:
match = (spsr & (PSR_C | PSR_Z)) == PSR_C;
break;
case INSN_COND_GE:
match = (!(spsr & PSR_N) == !(spsr & PSR_V));
break;
case INSN_COND_GT:
match = !(spsr & PSR_Z) && (!(spsr & PSR_N) == !(spsr & PSR_V));
break;
case INSN_COND_AL:
match = true;
break;
default:
__assert_unreachable();
}
return (match != invert);
}
#ifdef COMPAT_FREEBSD32
/* arm32 GDB breakpoints */
#define GDB_BREAKPOINT 0xe6000011
#define GDB5_BREAKPOINT 0xe7ffdefe
static int
gdb_trapper(vm_offset_t va, uint32_t insn, struct trapframe *frame,
uint32_t esr)
{
struct thread *td = curthread;
if (insn == GDB_BREAKPOINT || insn == GDB5_BREAKPOINT) {
if (SV_PROC_FLAG(td->td_proc, SV_ILP32) &&
va < VM_MAXUSER_ADDRESS) {
ksiginfo_t ksi;
ksiginfo_init_trap(&ksi);
ksi.ksi_signo = SIGTRAP;
ksi.ksi_code = TRAP_BRKPT;
ksi.ksi_addr = (void *)va;
trapsignal(td, &ksi);
return 1;
}
}
return 0;
}
static int
swp_emulate(vm_offset_t va, uint32_t insn, struct trapframe *frame,
uint32_t esr)
{
ksiginfo_t ksi;
struct thread *td;
vm_offset_t vaddr;
uint64_t *regs;
uint32_t val;
int attempts, error, Rn, Rd, Rm;
bool is_swpb;
td = curthread;
/*
* swp, swpb only; there are no Thumb swp/swpb instructions so we can
* safely bail out if we're in Thumb mode.
*/
if (!compat32_emul_swp || !SV_PROC_FLAG(td->td_proc, SV_ILP32) ||
(frame->tf_spsr & PSR_T) != 0)
return (0);
else if ((insn & 0x0fb00ff0) != 0x01000090)
return (0);
else if (!arm_cond_match(insn, frame))
goto next; /* Handled, but does nothing */
Rn = (insn & 0xf0000) >> 16;
Rd = (insn & 0xf000) >> 12;
Rm = (insn & 0xf);
regs = frame->tf_x;
vaddr = regs[Rn] & 0xffffffff;
val = regs[Rm];
/* Enforce alignment for swp. */
is_swpb = (insn & 0x00400000) != 0;
if (!is_swpb && (vaddr & 3) != 0)
goto fault;
attempts = 0;
do {
if (is_swpb) {
uint8_t bval;
bval = val;
error = swapueword8((void *)vaddr, &bval);
val = bval;
} else {
error = swapueword32((void *)vaddr, &val);
}
if (error == -1)
goto fault;
/*
* Avoid potential DoS, e.g., on CPUs that don't implement
* global monitors.
*/
if (error != 0 && (++attempts % 5) == 0)
maybe_yield();
} while (error != 0);
regs[Rd] = val;
next:
/* No thumb SWP/SWPB */
frame->tf_elr += 4; //INSN_SIZE;
return (1);
fault:
ksiginfo_init_trap(&ksi);
ksi.ksi_signo = SIGSEGV;
ksi.ksi_code = SEGV_MAPERR;
ksi.ksi_addr = (void *)va;
trapsignal(td, &ksi);
return (1);
}
#endif
void
undef_init(void)
{
-
- LIST_INIT(&undef_handlers[0]);
- LIST_INIT(&undef_handlers[1]);
-
#ifdef COMPAT_FREEBSD32
- install_undef_handler(true, gdb_trapper);
- install_undef_handler(true, swp_emulate);
+ install_undef_handler(gdb_trapper);
+ install_undef_handler(swp_emulate);
#endif
}
void *
-install_undef_handler(bool user, undef_handler_t func)
+install_undef_handler(undef_handler_t func)
{
struct undef_handler *uh;
uh = malloc(sizeof(*uh), M_UNDEF, M_WAITOK);
uh->uh_handler = func;
- LIST_INSERT_HEAD(&undef_handlers[user ? 0 : 1], uh, uh_link);
+ LIST_INSERT_HEAD(&undef_handlers, uh, uh_link);
return (uh);
}
void
remove_undef_handler(void *handle)
{
struct undef_handler *uh;
uh = handle;
LIST_REMOVE(uh, uh_link);
free(handle, M_UNDEF);
}
int
-undef_insn(u_int el, struct trapframe *frame)
+undef_insn(struct trapframe *frame)
{
struct undef_handler *uh;
uint32_t insn;
int ret;
- KASSERT(el < 2, ("Invalid exception level %u", el));
-
- if (el == 0) {
- ret = fueword32((uint32_t *)frame->tf_elr, &insn);
- /* Raise a SIGILL if we are unable to read the instruction */
- if (ret != 0)
- return (0);
- } else {
- insn = *(uint32_t *)frame->tf_elr;
- }
+ ret = fueword32((uint32_t *)frame->tf_elr, &insn);
+ /* Raise a SIGILL if we are unable to read the instruction */
+ if (ret != 0)
+ return (0);
- LIST_FOREACH(uh, &undef_handlers[el], uh_link) {
+ LIST_FOREACH(uh, &undef_handlers, uh_link) {
ret = uh->uh_handler(frame->tf_elr, insn, frame, frame->tf_esr);
if (ret)
return (1);
}
return (0);
}
diff --git a/sys/arm64/include/undefined.h b/sys/arm64/include/undefined.h
index db5d0523e711..1370a648b17c 100644
--- a/sys/arm64/include/undefined.h
+++ b/sys/arm64/include/undefined.h
@@ -1,66 +1,66 @@
/*-
* Copyright (c) 2017 Andrew Turner
* All rights reserved.
*
* This software was developed by SRI International and the University of
* Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237
* ("CTSRD"), as part of the DARPA CRASH research programme.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef _MACHINE__UNDEFINED_H_
#define _MACHINE__UNDEFINED_H_
#ifdef _KERNEL
typedef int (*undef_handler_t)(vm_offset_t, uint32_t, struct trapframe *,
uint32_t);
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 *install_undef_handler(undef_handler_t);
void remove_undef_handler(void *);
-int undef_insn(u_int, struct trapframe *);
+int undef_insn(struct trapframe *);
#endif /* _KERNEL */
#endif