target-arm queue:

* Support M profile derived exceptions on exception entry and exit
  * Implement AArch64 v8.2 crypto insns (SHA-512, SHA-3, SM3, SM4)
  * Implement working i.MX6 SD controller
  * Various devices preparatory to i.MX7 support
  * Preparatory patches for SVE emulation
  * v8M: Fix bug in implementation of 'TT' insn
  * Give useful error if user tries to use userspace GICv3 with KVM
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABCAAGBQJafX+DAAoJEDwlJe0UNgzegGgP/2OepsVnpg++NkBpJdCq0wjC
 DFaGLpDqaXabv0E5b56flkqBsK6mO2vXoIiWOLF3f25G5pBf5zu7XOEYXWuOIBEP
 Yr/vl4/yWRZfiNKl6aPC5HwHKN8SWMV+Mp/GtKz0PtHLY+oGl3fBRBu8fbeoOyj9
 3x+HB0c5fpAtWrRoS+XrUJfQPdkkZ4c5lDvpxec57cqOzJ8qYZEcFCi7WP3Rglox
 sQJEls5IXkOVGx35o7k0rIw0qzcV8iCGBsuA+4QAonxE5B0ip6aolPAaq+gf/mLP
 StX2UJG9JO5tOhaNBexHE/QwLNu2pver8mghIyluirLvisB6kx+dEoa0Zj/7J7or
 LD9O6pqpSPCCKLSw0xPOl2FqjXzbIYcjgddxrOGvUQgAhdagB2EeRHiCdZ7hQXvm
 Pg4gAsIAZURPrbj7LaXgDCzM0IP6bZjNR2U+C0ekJDBRJb/NTLcvlmx3MQo3s4ot
 s/t6FP728tnmMk7ib8w95oo2oSMiPUZBfj2IMYogXgY+pup0OunKwOQL93Lmj4By
 LJwawcsa/9ctyzF/XvBUHO/n4l0zZi3wLmtVojbxkxtHiFmvv5gQMlyHYM08aTJ1
 Vu/V0tiX6+oXoNrJg5e8TAchsS5PmO54Sj/ywMm5Q8FWfBE19KTRl5BatxbMYahU
 gQQt64esxQQFxxjdSrZY
 =bokK
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20180209' into staging

target-arm queue:
 * Support M profile derived exceptions on exception entry and exit
 * Implement AArch64 v8.2 crypto insns (SHA-512, SHA-3, SM3, SM4)
 * Implement working i.MX6 SD controller
 * Various devices preparatory to i.MX7 support
 * Preparatory patches for SVE emulation
 * v8M: Fix bug in implementation of 'TT' insn
 * Give useful error if user tries to use userspace GICv3 with KVM

# gpg: Signature made Fri 09 Feb 2018 11:01:23 GMT
# gpg:                using RSA key 3C2525ED14360CDE
# gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>"
# gpg:                 aka "Peter Maydell <pmaydell@gmail.com>"
# gpg:                 aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>"
# Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83  15CF 3C25 25ED 1436 0CDE

* remotes/pmaydell/tags/pull-target-arm-20180209: (30 commits)
  hw/core/generic-loader: Allow PC to be set on command line
  target/arm/translate.c: Fix missing 'break' for TT insns
  target/arm/kvm: gic: Prevent creating userspace GICv3 with KVM
  target/arm: Add SVE state to TB->FLAGS
  target/arm: Add ZCR_ELx
  target/arm: Add SVE to migration state
  target/arm: Add predicate registers for SVE
  target/arm: Expand vector registers for SVE
  hw/arm: Move virt's PSCI DT fixup code to arm/boot.c
  usb: Add basic code to emulate Chipidea USB IP
  i.MX: Add implementation of i.MX7 GPR IP block
  i.MX: Add i.MX7 GPT variant
  i.MX: Add code to emulate GPCv2 IP block
  i.MX: Add code to emulate i.MX7 SNVS IP-block
  i.MX: Add code to emulate i.MX2 watchdog IP block
  i.MX: Add code to emulate i.MX7 CCM, PMU and ANALOG IP blocks
  hw: i.MX: Convert i.MX6 to use TYPE_IMX_USDHC
  sdhci: Add i.MX specific subtype of SDHCI
  target/arm: enable user-mode SHA-3, SM3, SM4 and SHA-512 instruction support
  target/arm: implement SM4 instructions
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2018-02-09 13:27:40 +00:00
commit f31cd9e4e2
38 changed files with 2928 additions and 187 deletions

View file

@ -385,6 +385,69 @@ static void set_kernel_args_old(const struct arm_boot_info *info)
}
}
static void fdt_add_psci_node(void *fdt)
{
uint32_t cpu_suspend_fn;
uint32_t cpu_off_fn;
uint32_t cpu_on_fn;
uint32_t migrate_fn;
ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(0));
const char *psci_method;
int64_t psci_conduit;
psci_conduit = object_property_get_int(OBJECT(armcpu),
"psci-conduit",
&error_abort);
switch (psci_conduit) {
case QEMU_PSCI_CONDUIT_DISABLED:
return;
case QEMU_PSCI_CONDUIT_HVC:
psci_method = "hvc";
break;
case QEMU_PSCI_CONDUIT_SMC:
psci_method = "smc";
break;
default:
g_assert_not_reached();
}
qemu_fdt_add_subnode(fdt, "/psci");
if (armcpu->psci_version == 2) {
const char comp[] = "arm,psci-0.2\0arm,psci";
qemu_fdt_setprop(fdt, "/psci", "compatible", comp, sizeof(comp));
cpu_off_fn = QEMU_PSCI_0_2_FN_CPU_OFF;
if (arm_feature(&armcpu->env, ARM_FEATURE_AARCH64)) {
cpu_suspend_fn = QEMU_PSCI_0_2_FN64_CPU_SUSPEND;
cpu_on_fn = QEMU_PSCI_0_2_FN64_CPU_ON;
migrate_fn = QEMU_PSCI_0_2_FN64_MIGRATE;
} else {
cpu_suspend_fn = QEMU_PSCI_0_2_FN_CPU_SUSPEND;
cpu_on_fn = QEMU_PSCI_0_2_FN_CPU_ON;
migrate_fn = QEMU_PSCI_0_2_FN_MIGRATE;
}
} else {
qemu_fdt_setprop_string(fdt, "/psci", "compatible", "arm,psci");
cpu_suspend_fn = QEMU_PSCI_0_1_FN_CPU_SUSPEND;
cpu_off_fn = QEMU_PSCI_0_1_FN_CPU_OFF;
cpu_on_fn = QEMU_PSCI_0_1_FN_CPU_ON;
migrate_fn = QEMU_PSCI_0_1_FN_MIGRATE;
}
/* We adopt the PSCI spec's nomenclature, and use 'conduit' to refer
* to the instruction that should be used to invoke PSCI functions.
* However, the device tree binding uses 'method' instead, so that is
* what we should use here.
*/
qemu_fdt_setprop_string(fdt, "/psci", "method", psci_method);
qemu_fdt_setprop_cell(fdt, "/psci", "cpu_suspend", cpu_suspend_fn);
qemu_fdt_setprop_cell(fdt, "/psci", "cpu_off", cpu_off_fn);
qemu_fdt_setprop_cell(fdt, "/psci", "cpu_on", cpu_on_fn);
qemu_fdt_setprop_cell(fdt, "/psci", "migrate", migrate_fn);
}
/**
* load_dtb() - load a device tree binary image into memory
* @addr: the address to load the image at
@ -541,6 +604,8 @@ static int load_dtb(hwaddr addr, const struct arm_boot_info *binfo,
}
}
fdt_add_psci_node(fdt);
if (binfo->modify_dtb) {
binfo->modify_dtb(binfo, fdt);
}

View file

@ -93,7 +93,7 @@ static void fsl_imx6_init(Object *obj)
}
for (i = 0; i < FSL_IMX6_NUM_ESDHCS; i++) {
object_initialize(&s->esdhc[i], sizeof(s->esdhc[i]), TYPE_SYSBUS_SDHCI);
object_initialize(&s->esdhc[i], sizeof(s->esdhc[i]), TYPE_IMX_USDHC);
qdev_set_parent_bus(DEVICE(&s->esdhc[i]), sysbus_get_default());
snprintf(name, NAME_SIZE, "sdhc%d", i + 1);
object_property_add_child(obj, name, OBJECT(&s->esdhc[i]), NULL);

View file

@ -244,66 +244,6 @@ static void create_fdt(VirtMachineState *vms)
}
}
static void fdt_add_psci_node(const VirtMachineState *vms)
{
uint32_t cpu_suspend_fn;
uint32_t cpu_off_fn;
uint32_t cpu_on_fn;
uint32_t migrate_fn;
void *fdt = vms->fdt;
ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(0));
const char *psci_method;
switch (vms->psci_conduit) {
case QEMU_PSCI_CONDUIT_DISABLED:
return;
case QEMU_PSCI_CONDUIT_HVC:
psci_method = "hvc";
break;
case QEMU_PSCI_CONDUIT_SMC:
psci_method = "smc";
break;
default:
g_assert_not_reached();
}
qemu_fdt_add_subnode(fdt, "/psci");
if (armcpu->psci_version == 2) {
const char comp[] = "arm,psci-0.2\0arm,psci";
qemu_fdt_setprop(fdt, "/psci", "compatible", comp, sizeof(comp));
cpu_off_fn = QEMU_PSCI_0_2_FN_CPU_OFF;
if (arm_feature(&armcpu->env, ARM_FEATURE_AARCH64)) {
cpu_suspend_fn = QEMU_PSCI_0_2_FN64_CPU_SUSPEND;
cpu_on_fn = QEMU_PSCI_0_2_FN64_CPU_ON;
migrate_fn = QEMU_PSCI_0_2_FN64_MIGRATE;
} else {
cpu_suspend_fn = QEMU_PSCI_0_2_FN_CPU_SUSPEND;
cpu_on_fn = QEMU_PSCI_0_2_FN_CPU_ON;
migrate_fn = QEMU_PSCI_0_2_FN_MIGRATE;
}
} else {
qemu_fdt_setprop_string(fdt, "/psci", "compatible", "arm,psci");
cpu_suspend_fn = QEMU_PSCI_0_1_FN_CPU_SUSPEND;
cpu_off_fn = QEMU_PSCI_0_1_FN_CPU_OFF;
cpu_on_fn = QEMU_PSCI_0_1_FN_CPU_ON;
migrate_fn = QEMU_PSCI_0_1_FN_MIGRATE;
}
/* We adopt the PSCI spec's nomenclature, and use 'conduit' to refer
* to the instruction that should be used to invoke PSCI functions.
* However, the device tree binding uses 'method' instead, so that is
* what we should use here.
*/
qemu_fdt_setprop_string(fdt, "/psci", "method", psci_method);
qemu_fdt_setprop_cell(fdt, "/psci", "cpu_suspend", cpu_suspend_fn);
qemu_fdt_setprop_cell(fdt, "/psci", "cpu_off", cpu_off_fn);
qemu_fdt_setprop_cell(fdt, "/psci", "cpu_on", cpu_on_fn);
qemu_fdt_setprop_cell(fdt, "/psci", "migrate", migrate_fn);
}
static void fdt_add_timer_nodes(const VirtMachineState *vms)
{
/* On real hardware these interrupts are level-triggered.
@ -1409,7 +1349,6 @@ static void machvirt_init(MachineState *machine)
}
fdt_add_timer_nodes(vms);
fdt_add_cpu_nodes(vms);
fdt_add_psci_node(vms);
memory_region_allocate_system_memory(ram, NULL, "mach-virt.ram",
machine->ram_size);

View file

@ -105,7 +105,7 @@ static void generic_loader_realize(DeviceState *dev, Error **errp)
error_setg(errp, "data can not be specified when setting a "
"program counter");
return;
} else if (!s->cpu_num) {
} else if (s->cpu_num == CPU_NONE) {
error_setg(errp, "cpu_num must be specified when setting a "
"program counter");
return;

View file

@ -6,7 +6,7 @@ common-obj-$(CONFIG_XILINX) += xilinx_intc.o
common-obj-$(CONFIG_XLNX_ZYNQMP) += xlnx-pmu-iomod-intc.o
common-obj-$(CONFIG_XLNX_ZYNQMP) += xlnx-zynqmp-ipi.o
common-obj-$(CONFIG_ETRAXFS) += etraxfs_pic.o
common-obj-$(CONFIG_IMX) += imx_avic.o
common-obj-$(CONFIG_IMX) += imx_avic.o imx_gpcv2.o
common-obj-$(CONFIG_LM32) += lm32_pic.o
common-obj-$(CONFIG_REALVIEW) += realview_gic.o
common-obj-$(CONFIG_SLAVIO) += slavio_intctl.o

View file

@ -503,8 +503,25 @@ static void armv7m_nvic_clear_pending(void *opaque, int irq, bool secure)
}
}
void armv7m_nvic_set_pending(void *opaque, int irq, bool secure)
static void do_armv7m_nvic_set_pending(void *opaque, int irq, bool secure,
bool derived)
{
/* Pend an exception, including possibly escalating it to HardFault.
*
* This function handles both "normal" pending of interrupts and
* exceptions, and also derived exceptions (ones which occur as
* a result of trying to take some other exception).
*
* If derived == true, the caller guarantees that we are part way through
* trying to take an exception (but have not yet called
* armv7m_nvic_acknowledge_irq() to make it active), and so:
* - s->vectpending is the "original exception" we were trying to take
* - irq is the "derived exception"
* - nvic_exec_prio(s) gives the priority before exception entry
* Here we handle the prioritization logic which the pseudocode puts
* in the DerivedLateArrival() function.
*/
NVICState *s = (NVICState *)opaque;
bool banked = exc_is_banked(irq);
VecInfo *vec;
@ -514,7 +531,44 @@ void armv7m_nvic_set_pending(void *opaque, int irq, bool secure)
vec = (banked && secure) ? &s->sec_vectors[irq] : &s->vectors[irq];
trace_nvic_set_pending(irq, secure, vec->enabled, vec->prio);
trace_nvic_set_pending(irq, secure, derived, vec->enabled, vec->prio);
if (derived) {
/* Derived exceptions are always synchronous. */
assert(irq >= ARMV7M_EXCP_HARD && irq < ARMV7M_EXCP_PENDSV);
if (irq == ARMV7M_EXCP_DEBUG &&
exc_group_prio(s, vec->prio, secure) >= nvic_exec_prio(s)) {
/* DebugMonitorFault, but its priority is lower than the
* preempted exception priority: just ignore it.
*/
return;
}
if (irq == ARMV7M_EXCP_HARD && vec->prio >= s->vectpending_prio) {
/* If this is a terminal exception (one which means we cannot
* take the original exception, like a failure to read its
* vector table entry), then we must take the derived exception.
* If the derived exception can't take priority over the
* original exception, then we go into Lockup.
*
* For QEMU, we rely on the fact that a derived exception is
* terminal if and only if it's reported to us as HardFault,
* which saves having to have an extra argument is_terminal
* that we'd only use in one place.
*/
cpu_abort(&s->cpu->parent_obj,
"Lockup: can't take terminal derived exception "
"(original exception priority %d)\n",
s->vectpending_prio);
}
/* We now continue with the same code as for a normal pending
* exception, which will cause us to pend the derived exception.
* We'll then take either the original or the derived exception
* based on which is higher priority by the usual mechanism
* for selecting the highest priority pending interrupt.
*/
}
if (irq >= ARMV7M_EXCP_HARD && irq < ARMV7M_EXCP_PENDSV) {
/* If a synchronous exception is pending then it may be
@ -585,25 +639,31 @@ void armv7m_nvic_set_pending(void *opaque, int irq, bool secure)
}
}
void armv7m_nvic_set_pending(void *opaque, int irq, bool secure)
{
do_armv7m_nvic_set_pending(opaque, irq, secure, false);
}
void armv7m_nvic_set_pending_derived(void *opaque, int irq, bool secure)
{
do_armv7m_nvic_set_pending(opaque, irq, secure, true);
}
/* Make pending IRQ active. */
bool armv7m_nvic_acknowledge_irq(void *opaque)
void armv7m_nvic_acknowledge_irq(void *opaque)
{
NVICState *s = (NVICState *)opaque;
CPUARMState *env = &s->cpu->env;
const int pending = s->vectpending;
const int running = nvic_exec_prio(s);
VecInfo *vec;
bool targets_secure;
assert(pending > ARMV7M_EXCP_RESET && pending < s->num_irq);
if (s->vectpending_is_s_banked) {
vec = &s->sec_vectors[pending];
targets_secure = true;
} else {
vec = &s->vectors[pending];
targets_secure = !exc_is_banked(s->vectpending) &&
exc_targets_secure(s, s->vectpending);
}
assert(vec->enabled);
@ -611,7 +671,7 @@ bool armv7m_nvic_acknowledge_irq(void *opaque)
assert(s->vectpending_prio < running);
trace_nvic_acknowledge_irq(pending, s->vectpending_prio, targets_secure);
trace_nvic_acknowledge_irq(pending, s->vectpending_prio);
vec->active = 1;
vec->pending = 0;
@ -619,8 +679,28 @@ bool armv7m_nvic_acknowledge_irq(void *opaque)
write_v7m_exception(env, s->vectpending);
nvic_irq_update(s);
}
return targets_secure;
void armv7m_nvic_get_pending_irq_info(void *opaque,
int *pirq, bool *ptargets_secure)
{
NVICState *s = (NVICState *)opaque;
const int pending = s->vectpending;
bool targets_secure;
assert(pending > ARMV7M_EXCP_RESET && pending < s->num_irq);
if (s->vectpending_is_s_banked) {
targets_secure = true;
} else {
targets_secure = !exc_is_banked(pending) &&
exc_targets_secure(s, pending);
}
trace_nvic_get_pending_irq_info(pending, targets_secure);
*ptargets_secure = targets_secure;
*pirq = pending;
}
int armv7m_nvic_complete_irq(void *opaque, int irq, bool secure)

125
hw/intc/imx_gpcv2.c Normal file
View file

@ -0,0 +1,125 @@
/*
* Copyright (c) 2018, Impinj, Inc.
*
* i.MX7 GPCv2 block emulation code
*
* Author: Andrey Smirnov <andrew.smirnov@gmail.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "hw/intc/imx_gpcv2.h"
#include "qemu/log.h"
#define GPC_PU_PGC_SW_PUP_REQ 0x0f8
#define GPC_PU_PGC_SW_PDN_REQ 0x104
#define USB_HSIC_PHY_SW_Pxx_REQ BIT(4)
#define USB_OTG2_PHY_SW_Pxx_REQ BIT(3)
#define USB_OTG1_PHY_SW_Pxx_REQ BIT(2)
#define PCIE_PHY_SW_Pxx_REQ BIT(1)
#define MIPI_PHY_SW_Pxx_REQ BIT(0)
static void imx_gpcv2_reset(DeviceState *dev)
{
IMXGPCv2State *s = IMX_GPCV2(dev);
memset(s->regs, 0, sizeof(s->regs));
}
static uint64_t imx_gpcv2_read(void *opaque, hwaddr offset,
unsigned size)
{
IMXGPCv2State *s = opaque;
return s->regs[offset / sizeof(uint32_t)];
}
static void imx_gpcv2_write(void *opaque, hwaddr offset,
uint64_t value, unsigned size)
{
IMXGPCv2State *s = opaque;
const size_t idx = offset / sizeof(uint32_t);
s->regs[idx] = value;
/*
* Real HW will clear those bits once as a way to indicate that
* power up request is complete
*/
if (offset == GPC_PU_PGC_SW_PUP_REQ ||
offset == GPC_PU_PGC_SW_PDN_REQ) {
s->regs[idx] &= ~(USB_HSIC_PHY_SW_Pxx_REQ |
USB_OTG2_PHY_SW_Pxx_REQ |
USB_OTG1_PHY_SW_Pxx_REQ |
PCIE_PHY_SW_Pxx_REQ |
MIPI_PHY_SW_Pxx_REQ);
}
}
static const struct MemoryRegionOps imx_gpcv2_ops = {
.read = imx_gpcv2_read,
.write = imx_gpcv2_write,
.endianness = DEVICE_NATIVE_ENDIAN,
.impl = {
/*
* Our device would not work correctly if the guest was doing
* unaligned access. This might not be a limitation on the real
* device but in practice there is no reason for a guest to access
* this device unaligned.
*/
.min_access_size = 4,
.max_access_size = 4,
.unaligned = false,
},
};
static void imx_gpcv2_init(Object *obj)
{
SysBusDevice *sd = SYS_BUS_DEVICE(obj);
IMXGPCv2State *s = IMX_GPCV2(obj);
memory_region_init_io(&s->iomem,
obj,
&imx_gpcv2_ops,
s,
TYPE_IMX_GPCV2 ".iomem",
sizeof(s->regs));
sysbus_init_mmio(sd, &s->iomem);
}
static const VMStateDescription vmstate_imx_gpcv2 = {
.name = TYPE_IMX_GPCV2,
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32_ARRAY(regs, IMXGPCv2State, GPC_NUM),
VMSTATE_END_OF_LIST()
},
};
static void imx_gpcv2_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->reset = imx_gpcv2_reset;
dc->vmsd = &vmstate_imx_gpcv2;
dc->desc = "i.MX GPCv2 Module";
}
static const TypeInfo imx_gpcv2_info = {
.name = TYPE_IMX_GPCV2,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(IMXGPCv2State),
.instance_init = imx_gpcv2_init,
.class_init = imx_gpcv2_class_init,
};
static void imx_gpcv2_register_type(void)
{
type_register_static(&imx_gpcv2_info);
}
type_init(imx_gpcv2_register_type)

View file

@ -177,10 +177,11 @@ nvic_set_prio(int irq, bool secure, uint8_t prio) "NVIC set irq %d secure-bank %
nvic_irq_update(int vectpending, int pendprio, int exception_prio, int level) "NVIC vectpending %d pending prio %d exception_prio %d: setting irq line to %d"
nvic_escalate_prio(int irq, int irqprio, int runprio) "NVIC escalating irq %d to HardFault: insufficient priority %d >= %d"
nvic_escalate_disabled(int irq) "NVIC escalating irq %d to HardFault: disabled"
nvic_set_pending(int irq, bool secure, int en, int prio) "NVIC set pending irq %d secure-bank %d (enabled: %d priority %d)"
nvic_set_pending(int irq, bool secure, bool derived, int en, int prio) "NVIC set pending irq %d secure-bank %d derived %d (enabled: %d priority %d)"
nvic_clear_pending(int irq, bool secure, int en, int prio) "NVIC clear pending irq %d secure-bank %d (enabled: %d priority %d)"
nvic_set_pending_level(int irq) "NVIC set pending: irq %d higher prio than vectpending: setting irq line to 1"
nvic_acknowledge_irq(int irq, int prio, bool targets_secure) "NVIC acknowledge IRQ: %d now active (prio %d targets_secure %d)"
nvic_acknowledge_irq(int irq, int prio) "NVIC acknowledge IRQ: %d now active (prio %d)"
nvic_get_pending_irq_info(int irq, bool secure) "NVIC next IRQ %d: targets_secure: %d"
nvic_complete_irq(int irq, bool secure) "NVIC complete IRQ %d (secure %d)"
nvic_set_irq_level(int irq, int level) "NVIC external irq %d level set to %d"
nvic_sysreg_read(uint64_t addr, uint32_t value, unsigned size) "NVIC sysreg read addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u"

View file

@ -33,6 +33,10 @@ obj-$(CONFIG_IMX) += imx31_ccm.o
obj-$(CONFIG_IMX) += imx25_ccm.o
obj-$(CONFIG_IMX) += imx6_ccm.o
obj-$(CONFIG_IMX) += imx6_src.o
obj-$(CONFIG_IMX) += imx7_ccm.o
obj-$(CONFIG_IMX) += imx2_wdt.o
obj-$(CONFIG_IMX) += imx7_snvs.o
obj-$(CONFIG_IMX) += imx7_gpr.o
obj-$(CONFIG_MILKYMIST) += milkymist-hpdmc.o
obj-$(CONFIG_MILKYMIST) += milkymist-pfpu.o
obj-$(CONFIG_MAINSTONE) += mst_fpga.o

89
hw/misc/imx2_wdt.c Normal file
View file

@ -0,0 +1,89 @@
/*
* Copyright (c) 2018, Impinj, Inc.
*
* i.MX2 Watchdog IP block
*
* Author: Andrey Smirnov <andrew.smirnov@gmail.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "qemu/bitops.h"
#include "sysemu/watchdog.h"
#include "hw/misc/imx2_wdt.h"
#define IMX2_WDT_WCR_WDA BIT(5) /* -> External Reset WDOG_B */
#define IMX2_WDT_WCR_SRS BIT(4) /* -> Software Reset Signal */
static uint64_t imx2_wdt_read(void *opaque, hwaddr addr,
unsigned int size)
{
return 0;
}
static void imx2_wdt_write(void *opaque, hwaddr addr,
uint64_t value, unsigned int size)
{
if (addr == IMX2_WDT_WCR &&
(value & (IMX2_WDT_WCR_WDA | IMX2_WDT_WCR_SRS))) {
watchdog_perform_action();
}
}
static const MemoryRegionOps imx2_wdt_ops = {
.read = imx2_wdt_read,
.write = imx2_wdt_write,
.endianness = DEVICE_NATIVE_ENDIAN,
.impl = {
/*
* Our device would not work correctly if the guest was doing
* unaligned access. This might not be a limitation on the
* real device but in practice there is no reason for a guest
* to access this device unaligned.
*/
.min_access_size = 4,
.max_access_size = 4,
.unaligned = false,
},
};
static void imx2_wdt_realize(DeviceState *dev, Error **errp)
{
IMX2WdtState *s = IMX2_WDT(dev);
memory_region_init_io(&s->mmio, OBJECT(dev),
&imx2_wdt_ops, s,
TYPE_IMX2_WDT".mmio",
IMX2_WDT_REG_NUM * sizeof(uint16_t));
sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio);
}
static void imx2_wdt_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = imx2_wdt_realize;
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
}
static const TypeInfo imx2_wdt_info = {
.name = TYPE_IMX2_WDT,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(IMX2WdtState),
.class_init = imx2_wdt_class_init,
};
static WatchdogTimerModel model = {
.wdt_name = "imx2-watchdog",
.wdt_description = "i.MX2 Watchdog",
};
static void imx2_wdt_register_type(void)
{
watchdog_add_model(&model);
type_register_static(&imx2_wdt_info);
}
type_init(imx2_wdt_register_type)

277
hw/misc/imx7_ccm.c Normal file
View file

@ -0,0 +1,277 @@
/*
* Copyright (c) 2018, Impinj, Inc.
*
* i.MX7 CCM, PMU and ANALOG IP blocks emulation code
*
* Author: Andrey Smirnov <andrew.smirnov@gmail.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "qemu/log.h"
#include "hw/misc/imx7_ccm.h"
static void imx7_analog_reset(DeviceState *dev)
{
IMX7AnalogState *s = IMX7_ANALOG(dev);
memset(s->pmu, 0, sizeof(s->pmu));
memset(s->analog, 0, sizeof(s->analog));
s->analog[ANALOG_PLL_ARM] = 0x00002042;
s->analog[ANALOG_PLL_DDR] = 0x0060302c;
s->analog[ANALOG_PLL_DDR_SS] = 0x00000000;
s->analog[ANALOG_PLL_DDR_NUM] = 0x06aaac4d;
s->analog[ANALOG_PLL_DDR_DENOM] = 0x100003ec;
s->analog[ANALOG_PLL_480] = 0x00002000;
s->analog[ANALOG_PLL_480A] = 0x52605a56;
s->analog[ANALOG_PLL_480B] = 0x52525216;
s->analog[ANALOG_PLL_ENET] = 0x00001fc0;
s->analog[ANALOG_PLL_AUDIO] = 0x0001301b;
s->analog[ANALOG_PLL_AUDIO_SS] = 0x00000000;
s->analog[ANALOG_PLL_AUDIO_NUM] = 0x05f5e100;
s->analog[ANALOG_PLL_AUDIO_DENOM] = 0x2964619c;
s->analog[ANALOG_PLL_VIDEO] = 0x0008201b;
s->analog[ANALOG_PLL_VIDEO_SS] = 0x00000000;
s->analog[ANALOG_PLL_VIDEO_NUM] = 0x0000f699;
s->analog[ANALOG_PLL_VIDEO_DENOM] = 0x000f4240;
s->analog[ANALOG_PLL_MISC0] = 0x00000000;
/* all PLLs need to be locked */
s->analog[ANALOG_PLL_ARM] |= ANALOG_PLL_LOCK;
s->analog[ANALOG_PLL_DDR] |= ANALOG_PLL_LOCK;
s->analog[ANALOG_PLL_480] |= ANALOG_PLL_LOCK;
s->analog[ANALOG_PLL_480A] |= ANALOG_PLL_LOCK;
s->analog[ANALOG_PLL_480B] |= ANALOG_PLL_LOCK;
s->analog[ANALOG_PLL_ENET] |= ANALOG_PLL_LOCK;
s->analog[ANALOG_PLL_AUDIO] |= ANALOG_PLL_LOCK;
s->analog[ANALOG_PLL_VIDEO] |= ANALOG_PLL_LOCK;
s->analog[ANALOG_PLL_MISC0] |= ANALOG_PLL_LOCK;
/*
* Since I couldn't find any info about this in the reference
* manual the value of this register is based strictly on matching
* what Linux kernel expects it to be.
*/
s->analog[ANALOG_DIGPROG] = 0x720000;
/*
* Set revision to be 1.0 (Arbitrary choice, no particular
* reason).
*/
s->analog[ANALOG_DIGPROG] |= 0x000010;
}
static void imx7_ccm_reset(DeviceState *dev)
{
IMX7CCMState *s = IMX7_CCM(dev);
memset(s->ccm, 0, sizeof(s->ccm));
}
#define CCM_INDEX(offset) (((offset) & ~(hwaddr)0xF) / sizeof(uint32_t))
#define CCM_BITOP(offset) ((offset) & (hwaddr)0xF)
enum {
CCM_BITOP_NONE = 0x00,
CCM_BITOP_SET = 0x04,
CCM_BITOP_CLR = 0x08,
CCM_BITOP_TOG = 0x0C,
};
static uint64_t imx7_set_clr_tog_read(void *opaque, hwaddr offset,
unsigned size)
{
const uint32_t *mmio = opaque;
return mmio[CCM_INDEX(offset)];
}
static void imx7_set_clr_tog_write(void *opaque, hwaddr offset,
uint64_t value, unsigned size)
{
const uint8_t bitop = CCM_BITOP(offset);
const uint32_t index = CCM_INDEX(offset);
uint32_t *mmio = opaque;
switch (bitop) {
case CCM_BITOP_NONE:
mmio[index] = value;
break;
case CCM_BITOP_SET:
mmio[index] |= value;
break;
case CCM_BITOP_CLR:
mmio[index] &= ~value;
break;
case CCM_BITOP_TOG:
mmio[index] ^= value;
break;
};
}
static const struct MemoryRegionOps imx7_set_clr_tog_ops = {
.read = imx7_set_clr_tog_read,
.write = imx7_set_clr_tog_write,
.endianness = DEVICE_NATIVE_ENDIAN,
.impl = {
/*
* Our device would not work correctly if the guest was doing
* unaligned access. This might not be a limitation on the real
* device but in practice there is no reason for a guest to access
* this device unaligned.
*/
.min_access_size = 4,
.max_access_size = 4,
.unaligned = false,
},
};
static const struct MemoryRegionOps imx7_digprog_ops = {
.read = imx7_set_clr_tog_read,
.endianness = DEVICE_NATIVE_ENDIAN,
.impl = {
.min_access_size = 4,
.max_access_size = 4,
.unaligned = false,
},
};
static void imx7_ccm_init(Object *obj)
{
SysBusDevice *sd = SYS_BUS_DEVICE(obj);
IMX7CCMState *s = IMX7_CCM(obj);
memory_region_init_io(&s->iomem,
obj,
&imx7_set_clr_tog_ops,
s->ccm,
TYPE_IMX7_CCM ".ccm",
sizeof(s->ccm));
sysbus_init_mmio(sd, &s->iomem);
}
static void imx7_analog_init(Object *obj)
{
SysBusDevice *sd = SYS_BUS_DEVICE(obj);
IMX7AnalogState *s = IMX7_ANALOG(obj);
memory_region_init(&s->mmio.container, obj, TYPE_IMX7_ANALOG,
0x10000);
memory_region_init_io(&s->mmio.analog,
obj,
&imx7_set_clr_tog_ops,
s->analog,
TYPE_IMX7_ANALOG,
sizeof(s->analog));
memory_region_add_subregion(&s->mmio.container,
0x60, &s->mmio.analog);
memory_region_init_io(&s->mmio.pmu,
obj,
&imx7_set_clr_tog_ops,
s->pmu,
TYPE_IMX7_ANALOG ".pmu",
sizeof(s->pmu));
memory_region_add_subregion(&s->mmio.container,
0x200, &s->mmio.pmu);
memory_region_init_io(&s->mmio.digprog,
obj,
&imx7_digprog_ops,
&s->analog[ANALOG_DIGPROG],
TYPE_IMX7_ANALOG ".digprog",
sizeof(uint32_t));
memory_region_add_subregion_overlap(&s->mmio.container,
0x800, &s->mmio.digprog, 10);
sysbus_init_mmio(sd, &s->mmio.container);
}
static const VMStateDescription vmstate_imx7_ccm = {
.name = TYPE_IMX7_CCM,
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32_ARRAY(ccm, IMX7CCMState, CCM_MAX),
VMSTATE_END_OF_LIST()
},
};
static uint32_t imx7_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
{
/*
* This function is "consumed" by GPT emulation code, however on
* i.MX7 each GPT block can have their own clock root. This means
* that this functions needs somehow to know requester's identity
* and the way to pass it: be it via additional IMXClk constants
* or by adding another argument to this method needs to be
* figured out
*/
qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Not implemented\n",
TYPE_IMX7_CCM, __func__);
return 0;
}
static void imx7_ccm_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
IMXCCMClass *ccm = IMX_CCM_CLASS(klass);
dc->reset = imx7_ccm_reset;
dc->vmsd = &vmstate_imx7_ccm;
dc->desc = "i.MX7 Clock Control Module";
ccm->get_clock_frequency = imx7_ccm_get_clock_frequency;
}
static const TypeInfo imx7_ccm_info = {
.name = TYPE_IMX7_CCM,
.parent = TYPE_IMX_CCM,
.instance_size = sizeof(IMX7CCMState),
.instance_init = imx7_ccm_init,
.class_init = imx7_ccm_class_init,
};
static const VMStateDescription vmstate_imx7_analog = {
.name = TYPE_IMX7_ANALOG,
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32_ARRAY(analog, IMX7AnalogState, ANALOG_MAX),
VMSTATE_UINT32_ARRAY(pmu, IMX7AnalogState, PMU_MAX),
VMSTATE_END_OF_LIST()
},
};
static void imx7_analog_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->reset = imx7_analog_reset;
dc->vmsd = &vmstate_imx7_analog;
dc->desc = "i.MX7 Analog Module";
}
static const TypeInfo imx7_analog_info = {
.name = TYPE_IMX7_ANALOG,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(IMX7AnalogState),
.instance_init = imx7_analog_init,
.class_init = imx7_analog_class_init,
};
static void imx7_ccm_register_type(void)
{
type_register_static(&imx7_ccm_info);
type_register_static(&imx7_analog_info);
}
type_init(imx7_ccm_register_type)

124
hw/misc/imx7_gpr.c Normal file
View file

@ -0,0 +1,124 @@
/*
* Copyright (c) 2018, Impinj, Inc.
*
* i.MX7 GPR IP block emulation code
*
* Author: Andrey Smirnov <andrew.smirnov@gmail.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
* Bare minimum emulation code needed to support being able to shut
* down linux guest gracefully.
*/
#include "qemu/osdep.h"
#include "hw/misc/imx7_gpr.h"
#include "qemu/log.h"
#include "sysemu/sysemu.h"
#include "trace.h"
enum IMX7GPRRegisters {
IOMUXC_GPR0 = 0x00,
IOMUXC_GPR1 = 0x04,
IOMUXC_GPR2 = 0x08,
IOMUXC_GPR3 = 0x0c,
IOMUXC_GPR4 = 0x10,
IOMUXC_GPR5 = 0x14,
IOMUXC_GPR6 = 0x18,
IOMUXC_GPR7 = 0x1c,
IOMUXC_GPR8 = 0x20,
IOMUXC_GPR9 = 0x24,
IOMUXC_GPR10 = 0x28,
IOMUXC_GPR11 = 0x2c,
IOMUXC_GPR12 = 0x30,
IOMUXC_GPR13 = 0x34,
IOMUXC_GPR14 = 0x38,
IOMUXC_GPR15 = 0x3c,
IOMUXC_GPR16 = 0x40,
IOMUXC_GPR17 = 0x44,
IOMUXC_GPR18 = 0x48,
IOMUXC_GPR19 = 0x4c,
IOMUXC_GPR20 = 0x50,
IOMUXC_GPR21 = 0x54,
IOMUXC_GPR22 = 0x58,
};
#define IMX7D_GPR1_IRQ_MASK BIT(12)
#define IMX7D_GPR1_ENET1_TX_CLK_SEL_MASK BIT(13)
#define IMX7D_GPR1_ENET2_TX_CLK_SEL_MASK BIT(14)
#define IMX7D_GPR1_ENET_TX_CLK_SEL_MASK (0x3 << 13)
#define IMX7D_GPR1_ENET1_CLK_DIR_MASK BIT(17)
#define IMX7D_GPR1_ENET2_CLK_DIR_MASK BIT(18)
#define IMX7D_GPR1_ENET_CLK_DIR_MASK (0x3 << 17)
#define IMX7D_GPR5_CSI_MUX_CONTROL_MIPI BIT(4)
#define IMX7D_GPR12_PCIE_PHY_REFCLK_SEL BIT(5)
#define IMX7D_GPR22_PCIE_PHY_PLL_LOCKED BIT(31)
static uint64_t imx7_gpr_read(void *opaque, hwaddr offset, unsigned size)
{
trace_imx7_gpr_read(offset);
if (offset == IOMUXC_GPR22) {
return IMX7D_GPR22_PCIE_PHY_PLL_LOCKED;
}
return 0;
}
static void imx7_gpr_write(void *opaque, hwaddr offset,
uint64_t v, unsigned size)
{
trace_imx7_gpr_write(offset, v);
}
static const struct MemoryRegionOps imx7_gpr_ops = {
.read = imx7_gpr_read,
.write = imx7_gpr_write,
.endianness = DEVICE_NATIVE_ENDIAN,
.impl = {
/*
* Our device would not work correctly if the guest was doing
* unaligned access. This might not be a limitation on the
* real device but in practice there is no reason for a guest
* to access this device unaligned.
*/
.min_access_size = 4,
.max_access_size = 4,
.unaligned = false,
},
};
static void imx7_gpr_init(Object *obj)
{
SysBusDevice *sd = SYS_BUS_DEVICE(obj);
IMX7GPRState *s = IMX7_GPR(obj);
memory_region_init_io(&s->mmio, obj, &imx7_gpr_ops, s,
TYPE_IMX7_GPR, 64 * 1024);
sysbus_init_mmio(sd, &s->mmio);
}
static void imx7_gpr_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->desc = "i.MX7 General Purpose Registers Module";
}
static const TypeInfo imx7_gpr_info = {
.name = TYPE_IMX7_GPR,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(IMX7GPRState),
.instance_init = imx7_gpr_init,
.class_init = imx7_gpr_class_init,
};
static void imx7_gpr_register_type(void)
{
type_register_static(&imx7_gpr_info);
}
type_init(imx7_gpr_register_type)

83
hw/misc/imx7_snvs.c Normal file
View file

@ -0,0 +1,83 @@
/*
* IMX7 Secure Non-Volatile Storage
*
* Copyright (c) 2018, Impinj, Inc.
*
* Author: Andrey Smirnov <andrew.smirnov@gmail.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
* Bare minimum emulation code needed to support being able to shut
* down linux guest gracefully.
*/
#include "qemu/osdep.h"
#include "hw/misc/imx7_snvs.h"
#include "qemu/log.h"
#include "sysemu/sysemu.h"
static uint64_t imx7_snvs_read(void *opaque, hwaddr offset, unsigned size)
{
return 0;
}
static void imx7_snvs_write(void *opaque, hwaddr offset,
uint64_t v, unsigned size)
{
const uint32_t value = v;
const uint32_t mask = SNVS_LPCR_TOP | SNVS_LPCR_DP_EN;
if (offset == SNVS_LPCR && ((value & mask) == mask)) {
qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
}
}
static const struct MemoryRegionOps imx7_snvs_ops = {
.read = imx7_snvs_read,
.write = imx7_snvs_write,
.endianness = DEVICE_NATIVE_ENDIAN,
.impl = {
/*
* Our device would not work correctly if the guest was doing
* unaligned access. This might not be a limitation on the real
* device but in practice there is no reason for a guest to access
* this device unaligned.
*/
.min_access_size = 4,
.max_access_size = 4,
.unaligned = false,
},
};
static void imx7_snvs_init(Object *obj)
{
SysBusDevice *sd = SYS_BUS_DEVICE(obj);
IMX7SNVSState *s = IMX7_SNVS(obj);
memory_region_init_io(&s->mmio, obj, &imx7_snvs_ops, s,
TYPE_IMX7_SNVS, 0x1000);
sysbus_init_mmio(sd, &s->mmio);
}
static void imx7_snvs_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->desc = "i.MX7 Secure Non-Volatile Storage Module";
}
static const TypeInfo imx7_snvs_info = {
.name = TYPE_IMX7_SNVS,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(IMX7SNVSState),
.instance_init = imx7_snvs_init,
.class_init = imx7_snvs_class_init,
};
static void imx7_snvs_register_type(void)
{
type_register_static(&imx7_snvs_info);
}
type_init(imx7_snvs_register_type)

View file

@ -66,3 +66,7 @@ mps2_scc_cfg_read(unsigned function, unsigned device, uint32_t value) "MPS2 SCC
msf2_sysreg_write(uint64_t offset, uint32_t val, uint32_t prev) "msf2-sysreg write: addr 0x%08" HWADDR_PRIx " data 0x%" PRIx32 " prev 0x%" PRIx32
msf2_sysreg_read(uint64_t offset, uint32_t val) "msf2-sysreg read: addr 0x%08" HWADDR_PRIx " data 0x%08" PRIx32
msf2_sysreg_write_pll_status(void) "Invalid write to read only PLL status register"
#hw/misc/imx7_gpr.c
imx7_gpr_read(uint64_t offset) "addr 0x%08" HWADDR_PRIx
imx7_gpr_write(uint64_t offset, uint64_t value) "addr 0x%08" HWADDR_PRIx "value 0x%08" HWADDR_PRIx

View file

@ -84,12 +84,18 @@
/* R/W Host control Register 0x0 */
#define SDHC_HOSTCTL 0x28
#define SDHC_CTRL_LED 0x01
#define SDHC_CTRL_DMA_CHECK_MASK 0x18
#define SDHC_CTRL_SDMA 0x00
#define SDHC_CTRL_ADMA1_32 0x08
#define SDHC_CTRL_ADMA2_32 0x10
#define SDHC_CTRL_ADMA2_64 0x18
#define SDHC_DMA_TYPE(x) ((x) & SDHC_CTRL_DMA_CHECK_MASK)
#define SDHC_CTRL_4BITBUS 0x02
#define SDHC_CTRL_8BITBUS 0x20
#define SDHC_CTRL_CDTEST_INS 0x40
#define SDHC_CTRL_CDTEST_EN 0x80
/* R/W Power Control Register 0x0 */
#define SDHC_PWRCON 0x29
@ -226,4 +232,21 @@ enum {
sdhc_gap_write = 2 /* SDHC stopped at block gap during write operation */
};
extern const VMStateDescription sdhci_vmstate;
#define ESDHC_MIX_CTRL 0x48
#define ESDHC_VENDOR_SPEC 0xc0
#define ESDHC_DLL_CTRL 0x60
#define ESDHC_TUNING_CTRL 0xcc
#define ESDHC_TUNE_CTRL_STATUS 0x68
#define ESDHC_WTMK_LVL 0x44
/* Undocumented register used by guests working around erratum ERR004536 */
#define ESDHC_UNDOCUMENTED_REG27 0x6c
#define ESDHC_CTRL_4BITBUS (0x1 << 1)
#define ESDHC_CTRL_8BITBUS (0x2 << 1)
#endif

View file

@ -244,7 +244,8 @@ static void sdhci_send_command(SDHCIState *s)
}
}
if ((s->norintstsen & SDHC_NISEN_TRSCMP) &&
if (!(s->quirks & SDHCI_QUIRK_NO_BUSY_IRQ) &&
(s->norintstsen & SDHC_NISEN_TRSCMP) &&
(s->cmdreg & SDHC_CMD_RESPONSE) == SDHC_CMD_RSP_WITH_BUSY) {
s->norintsts |= SDHC_NIS_TRSCMP;
}
@ -1189,6 +1190,8 @@ static void sdhci_initfn(SDHCIState *s)
s->insert_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sdhci_raise_insertion_irq, s);
s->transfer_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sdhci_data_transfer, s);
s->io_ops = &sdhci_mmio_ops;
}
static void sdhci_uninitfn(SDHCIState *s)
@ -1396,6 +1399,10 @@ static void sdhci_sysbus_realize(DeviceState *dev, Error ** errp)
}
sysbus_init_irq(sbd, &s->irq);
memory_region_init_io(&s->iomem, OBJECT(s), s->io_ops, s, "sdhci",
SDHC_REGISTERS_MAP_SIZE);
sysbus_init_mmio(sbd, &s->iomem);
}
@ -1447,11 +1454,232 @@ static const TypeInfo sdhci_bus_info = {
.class_init = sdhci_bus_class_init,
};
static uint64_t usdhc_read(void *opaque, hwaddr offset, unsigned size)
{
SDHCIState *s = SYSBUS_SDHCI(opaque);
uint32_t ret;
uint16_t hostctl;
switch (offset) {
default:
return sdhci_read(opaque, offset, size);
case SDHC_HOSTCTL:
/*
* For a detailed explanation on the following bit
* manipulation code see comments in a similar part of
* usdhc_write()
*/
hostctl = SDHC_DMA_TYPE(s->hostctl) << (8 - 3);
if (s->hostctl & SDHC_CTRL_8BITBUS) {
hostctl |= ESDHC_CTRL_8BITBUS;
}
if (s->hostctl & SDHC_CTRL_4BITBUS) {
hostctl |= ESDHC_CTRL_4BITBUS;
}
ret = hostctl;
ret |= (uint32_t)s->blkgap << 16;
ret |= (uint32_t)s->wakcon << 24;
break;
case ESDHC_DLL_CTRL:
case ESDHC_TUNE_CTRL_STATUS:
case ESDHC_UNDOCUMENTED_REG27:
case ESDHC_TUNING_CTRL:
case ESDHC_VENDOR_SPEC:
case ESDHC_MIX_CTRL:
case ESDHC_WTMK_LVL:
ret = 0;
break;
}
return ret;
}
static void
usdhc_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
{
SDHCIState *s = SYSBUS_SDHCI(opaque);
uint8_t hostctl;
uint32_t value = (uint32_t)val;
switch (offset) {
case ESDHC_DLL_CTRL:
case ESDHC_TUNE_CTRL_STATUS:
case ESDHC_UNDOCUMENTED_REG27:
case ESDHC_TUNING_CTRL:
case ESDHC_WTMK_LVL:
case ESDHC_VENDOR_SPEC:
break;
case SDHC_HOSTCTL:
/*
* Here's What ESDHCI has at offset 0x28 (SDHC_HOSTCTL)
*
* 7 6 5 4 3 2 1 0
* |-----------+--------+--------+-----------+----------+---------|
* | Card | Card | Endian | DATA3 | Data | Led |
* | Detect | Detect | Mode | as Card | Transfer | Control |
* | Signal | Test | | Detection | Width | |
* | Selection | Level | | Pin | | |
* |-----------+--------+--------+-----------+----------+---------|
*
* and 0x29
*
* 15 10 9 8
* |----------+------|
* | Reserved | DMA |
* | | Sel. |
* | | |
* |----------+------|
*
* and here's what SDCHI spec expects those offsets to be:
*
* 0x28 (Host Control Register)
*
* 7 6 5 4 3 2 1 0
* |--------+--------+----------+------+--------+----------+---------|
* | Card | Card | Extended | DMA | High | Data | LED |
* | Detect | Detect | Data | Sel. | Speed | Transfer | Control |
* | Signal | Test | Transfer | | Enable | Width | |
* | Sel. | Level | Width | | | | |
* |--------+--------+----------+------+--------+----------+---------|
*
* and 0x29 (Power Control Register)
*
* |----------------------------------|
* | Power Control Register |
* | |
* | Description omitted, |
* | since it has no analog in ESDHCI |
* | |
* |----------------------------------|
*
* Since offsets 0x2A and 0x2B should be compatible between
* both IP specs we only need to reconcile least 16-bit of the
* word we've been given.
*/
/*
* First, save bits 7 6 and 0 since they are identical
*/
hostctl = value & (SDHC_CTRL_LED |
SDHC_CTRL_CDTEST_INS |
SDHC_CTRL_CDTEST_EN);
/*
* Second, split "Data Transfer Width" from bits 2 and 1 in to
* bits 5 and 1
*/
if (value & ESDHC_CTRL_8BITBUS) {
hostctl |= SDHC_CTRL_8BITBUS;
}
if (value & ESDHC_CTRL_4BITBUS) {
hostctl |= ESDHC_CTRL_4BITBUS;
}
/*
* Third, move DMA select from bits 9 and 8 to bits 4 and 3
*/
hostctl |= SDHC_DMA_TYPE(value >> (8 - 3));
/*
* Now place the corrected value into low 16-bit of the value
* we are going to give standard SDHCI write function
*
* NOTE: This transformation should be the inverse of what can
* be found in drivers/mmc/host/sdhci-esdhc-imx.c in Linux
* kernel
*/
value &= ~UINT16_MAX;
value |= hostctl;
value |= (uint16_t)s->pwrcon << 8;
sdhci_write(opaque, offset, value, size);
break;
case ESDHC_MIX_CTRL:
/*
* So, when SD/MMC stack in Linux tries to write to "Transfer
* Mode Register", ESDHC i.MX quirk code will translate it
* into a write to ESDHC_MIX_CTRL, so we do the opposite in
* order to get where we started
*
* Note that Auto CMD23 Enable bit is located in a wrong place
* on i.MX, but since it is not used by QEMU we do not care.
*
* We don't want to call sdhci_write(.., SDHC_TRNMOD, ...)
* here becuase it will result in a call to
* sdhci_send_command(s) which we don't want.
*
*/
s->trnmod = value & UINT16_MAX;
break;
case SDHC_TRNMOD:
/*
* Similar to above, but this time a write to "Command
* Register" will be translated into a 4-byte write to
* "Transfer Mode register" where lower 16-bit of value would
* be set to zero. So what we do is fill those bits with
* cached value from s->trnmod and let the SDHCI
* infrastructure handle the rest
*/
sdhci_write(opaque, offset, val | s->trnmod, size);
break;
case SDHC_BLKSIZE:
/*
* ESDHCI does not implement "Host SDMA Buffer Boundary", and
* Linux driver will try to zero this field out which will
* break the rest of SDHCI emulation.
*
* Linux defaults to maximum possible setting (512K boundary)
* and it seems to be the only option that i.MX IP implements,
* so we artificially set it to that value.
*/
val |= 0x7 << 12;
/* FALLTHROUGH */
default:
sdhci_write(opaque, offset, val, size);
break;
}
}
static const MemoryRegionOps usdhc_mmio_ops = {
.read = usdhc_read,
.write = usdhc_write,
.valid = {
.min_access_size = 1,
.max_access_size = 4,
.unaligned = false
},
.endianness = DEVICE_LITTLE_ENDIAN,
};
static void imx_usdhc_init(Object *obj)
{
SDHCIState *s = SYSBUS_SDHCI(obj);
s->io_ops = &usdhc_mmio_ops;
s->quirks = SDHCI_QUIRK_NO_BUSY_IRQ;
}
static const TypeInfo imx_usdhc_info = {
.name = TYPE_IMX_USDHC,
.parent = TYPE_SYSBUS_SDHCI,
.instance_init = imx_usdhc_init,
};
static void sdhci_register_types(void)
{
type_register_static(&sdhci_pci_info);
type_register_static(&sdhci_sysbus_info);
type_register_static(&sdhci_bus_info);
type_register_static(&imx_usdhc_info);
}
type_init(sdhci_register_types)

View file

@ -113,6 +113,17 @@ static const IMXClk imx6_gpt_clocks[] = {
CLK_HIGH, /* 111 reference clock */
};
static const IMXClk imx7_gpt_clocks[] = {
CLK_NONE, /* 000 No clock source */
CLK_IPG, /* 001 ipg_clk, 532MHz*/
CLK_IPG_HIGH, /* 010 ipg_clk_highfreq */
CLK_EXT, /* 011 External clock */
CLK_32k, /* 100 ipg_clk_32k */
CLK_HIGH, /* 101 reference clock */
CLK_NONE, /* 110 not defined */
CLK_NONE, /* 111 not defined */
};
static void imx_gpt_set_freq(IMXGPTState *s)
{
uint32_t clksrc = extract32(s->cr, GPT_CR_CLKSRC_SHIFT, 3);
@ -512,6 +523,13 @@ static void imx6_gpt_init(Object *obj)
s->clocks = imx6_gpt_clocks;
}
static void imx7_gpt_init(Object *obj)
{
IMXGPTState *s = IMX_GPT(obj);
s->clocks = imx7_gpt_clocks;
}
static const TypeInfo imx25_gpt_info = {
.name = TYPE_IMX25_GPT,
.parent = TYPE_SYS_BUS_DEVICE,
@ -532,11 +550,18 @@ static const TypeInfo imx6_gpt_info = {
.instance_init = imx6_gpt_init,
};
static const TypeInfo imx7_gpt_info = {
.name = TYPE_IMX7_GPT,
.parent = TYPE_IMX25_GPT,
.instance_init = imx7_gpt_init,
};
static void imx_gpt_register_types(void)
{
type_register_static(&imx25_gpt_info);
type_register_static(&imx31_gpt_info);
type_register_static(&imx6_gpt_info);
type_register_static(&imx7_gpt_info);
}
type_init(imx_gpt_register_types)

View file

@ -12,6 +12,7 @@ common-obj-$(CONFIG_USB_XHCI_NEC) += hcd-xhci-nec.o
common-obj-$(CONFIG_USB_MUSB) += hcd-musb.o
obj-$(CONFIG_TUSB6010) += tusb6010.o
obj-$(CONFIG_IMX) += chipidea.o
# emulated usb devices
common-obj-$(CONFIG_USB) += dev-hub.o

176
hw/usb/chipidea.c Normal file
View file

@ -0,0 +1,176 @@
/*
* Copyright (c) 2018, Impinj, Inc.
*
* Chipidea USB block emulation code
*
* Author: Andrey Smirnov <andrew.smirnov@gmail.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "hw/usb/hcd-ehci.h"
#include "hw/usb/chipidea.h"
#include "qemu/log.h"
enum {
CHIPIDEA_USBx_DCIVERSION = 0x000,
CHIPIDEA_USBx_DCCPARAMS = 0x004,
CHIPIDEA_USBx_DCCPARAMS_HC = BIT(8),
};
static uint64_t chipidea_read(void *opaque, hwaddr offset,
unsigned size)
{
return 0;
}
static void chipidea_write(void *opaque, hwaddr offset,
uint64_t value, unsigned size)
{
}
static const struct MemoryRegionOps chipidea_ops = {
.read = chipidea_read,
.write = chipidea_write,
.endianness = DEVICE_NATIVE_ENDIAN,
.impl = {
/*
* Our device would not work correctly if the guest was doing
* unaligned access. This might not be a limitation on the
* real device but in practice there is no reason for a guest
* to access this device unaligned.
*/
.min_access_size = 4,
.max_access_size = 4,
.unaligned = false,
},
};
static uint64_t chipidea_dc_read(void *opaque, hwaddr offset,
unsigned size)
{
switch (offset) {
case CHIPIDEA_USBx_DCIVERSION:
return 0x1;
case CHIPIDEA_USBx_DCCPARAMS:
/*
* Real hardware (at least i.MX7) will also report the
* controller as "Device Capable" (and 8 supported endpoints),
* but there doesn't seem to be much point in doing so, since
* we don't emulate that part.
*/
return CHIPIDEA_USBx_DCCPARAMS_HC;
}
return 0;
}
static void chipidea_dc_write(void *opaque, hwaddr offset,
uint64_t value, unsigned size)
{
}
static const struct MemoryRegionOps chipidea_dc_ops = {
.read = chipidea_dc_read,
.write = chipidea_dc_write,
.endianness = DEVICE_NATIVE_ENDIAN,
.impl = {
/*
* Our device would not work correctly if the guest was doing
* unaligned access. This might not be a limitation on the real
* device but in practice there is no reason for a guest to access
* this device unaligned.
*/
.min_access_size = 4,
.max_access_size = 4,
.unaligned = false,
},
};
static void chipidea_init(Object *obj)
{
EHCIState *ehci = &SYS_BUS_EHCI(obj)->ehci;
ChipideaState *ci = CHIPIDEA(obj);
int i;
for (i = 0; i < ARRAY_SIZE(ci->iomem); i++) {
const struct {
const char *name;
hwaddr offset;
uint64_t size;
const struct MemoryRegionOps *ops;
} regions[ARRAY_SIZE(ci->iomem)] = {
/*
* Registers located between offsets 0x000 and 0xFC
*/
{
.name = TYPE_CHIPIDEA ".misc",
.offset = 0x000,
.size = 0x100,
.ops = &chipidea_ops,
},
/*
* Registers located between offsets 0x1A4 and 0x1DC
*/
{
.name = TYPE_CHIPIDEA ".endpoints",
.offset = 0x1A4,
.size = 0x1DC - 0x1A4 + 4,
.ops = &chipidea_ops,
},
/*
* USB_x_DCIVERSION and USB_x_DCCPARAMS
*/
{
.name = TYPE_CHIPIDEA ".dc",
.offset = 0x120,
.size = 8,
.ops = &chipidea_dc_ops,
},
};
memory_region_init_io(&ci->iomem[i],
obj,
regions[i].ops,
ci,
regions[i].name,
regions[i].size);
memory_region_add_subregion(&ehci->mem,
regions[i].offset,
&ci->iomem[i]);
}
}
static void chipidea_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(klass);
/*
* Offsets used were taken from i.MX7Dual Applications Processor
* Reference Manual, Rev 0.1, p. 3177, Table 11-59
*/
sec->capsbase = 0x100;
sec->opregbase = 0x140;
sec->portnr = 1;
set_bit(DEVICE_CATEGORY_USB, dc->categories);
dc->desc = "Chipidea USB Module";
}
static const TypeInfo chipidea_info = {
.name = TYPE_CHIPIDEA,
.parent = TYPE_SYS_BUS_EHCI,
.instance_size = sizeof(ChipideaState),
.instance_init = chipidea_init,
.class_init = chipidea_class_init,
};
static void chipidea_register_type(void)
{
type_register_static(&chipidea_info);
}
type_init(chipidea_register_type)

View file

@ -0,0 +1,22 @@
#ifndef IMX_GPCV2_H
#define IMX_GPCV2_H
#include "hw/sysbus.h"
enum IMXGPCv2Registers {
GPC_NUM = 0xE00 / sizeof(uint32_t),
};
typedef struct IMXGPCv2State {
/*< private >*/
SysBusDevice parent_obj;
/*< public >*/
MemoryRegion iomem;
uint32_t regs[GPC_NUM];
} IMXGPCv2State;
#define TYPE_IMX_GPCV2 "imx-gpcv2"
#define IMX_GPCV2(obj) OBJECT_CHECK(IMXGPCv2State, (obj), TYPE_IMX_GPCV2)
#endif /* IMX_GPCV2_H */

View file

@ -0,0 +1,33 @@
/*
* Copyright (c) 2017, Impinj, Inc.
*
* i.MX2 Watchdog IP block
*
* Author: Andrey Smirnov <andrew.smirnov@gmail.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#ifndef IMX2_WDT_H
#define IMX2_WDT_H
#include "hw/sysbus.h"
#define TYPE_IMX2_WDT "imx2.wdt"
#define IMX2_WDT(obj) OBJECT_CHECK(IMX2WdtState, (obj), TYPE_IMX2_WDT)
enum IMX2WdtRegisters {
IMX2_WDT_WCR = 0x0000,
IMX2_WDT_REG_NUM = 0x0008 / sizeof(uint16_t) + 1,
};
typedef struct IMX2WdtState {
/* <private> */
SysBusDevice parent_obj;
MemoryRegion mmio;
} IMX2WdtState;
#endif /* IMX7_SNVS_H */

139
include/hw/misc/imx7_ccm.h Normal file
View file

@ -0,0 +1,139 @@
/*
* Copyright (c) 2017, Impinj, Inc.
*
* i.MX7 CCM, PMU and ANALOG IP blocks emulation code
*
* Author: Andrey Smirnov <andrew.smirnov@gmail.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#ifndef IMX7_CCM_H
#define IMX7_CCM_H
#include "hw/misc/imx_ccm.h"
#include "qemu/bitops.h"
enum IMX7AnalogRegisters {
ANALOG_PLL_ARM,
ANALOG_PLL_ARM_SET,
ANALOG_PLL_ARM_CLR,
ANALOG_PLL_ARM_TOG,
ANALOG_PLL_DDR,
ANALOG_PLL_DDR_SET,
ANALOG_PLL_DDR_CLR,
ANALOG_PLL_DDR_TOG,
ANALOG_PLL_DDR_SS,
ANALOG_PLL_DDR_SS_SET,
ANALOG_PLL_DDR_SS_CLR,
ANALOG_PLL_DDR_SS_TOG,
ANALOG_PLL_DDR_NUM,
ANALOG_PLL_DDR_NUM_SET,
ANALOG_PLL_DDR_NUM_CLR,
ANALOG_PLL_DDR_NUM_TOG,
ANALOG_PLL_DDR_DENOM,
ANALOG_PLL_DDR_DENOM_SET,
ANALOG_PLL_DDR_DENOM_CLR,
ANALOG_PLL_DDR_DENOM_TOG,
ANALOG_PLL_480,
ANALOG_PLL_480_SET,
ANALOG_PLL_480_CLR,
ANALOG_PLL_480_TOG,
ANALOG_PLL_480A,
ANALOG_PLL_480A_SET,
ANALOG_PLL_480A_CLR,
ANALOG_PLL_480A_TOG,
ANALOG_PLL_480B,
ANALOG_PLL_480B_SET,
ANALOG_PLL_480B_CLR,
ANALOG_PLL_480B_TOG,
ANALOG_PLL_ENET,
ANALOG_PLL_ENET_SET,
ANALOG_PLL_ENET_CLR,
ANALOG_PLL_ENET_TOG,
ANALOG_PLL_AUDIO,
ANALOG_PLL_AUDIO_SET,
ANALOG_PLL_AUDIO_CLR,
ANALOG_PLL_AUDIO_TOG,
ANALOG_PLL_AUDIO_SS,
ANALOG_PLL_AUDIO_SS_SET,
ANALOG_PLL_AUDIO_SS_CLR,
ANALOG_PLL_AUDIO_SS_TOG,
ANALOG_PLL_AUDIO_NUM,
ANALOG_PLL_AUDIO_NUM_SET,
ANALOG_PLL_AUDIO_NUM_CLR,
ANALOG_PLL_AUDIO_NUM_TOG,
ANALOG_PLL_AUDIO_DENOM,
ANALOG_PLL_AUDIO_DENOM_SET,
ANALOG_PLL_AUDIO_DENOM_CLR,
ANALOG_PLL_AUDIO_DENOM_TOG,
ANALOG_PLL_VIDEO,
ANALOG_PLL_VIDEO_SET,
ANALOG_PLL_VIDEO_CLR,
ANALOG_PLL_VIDEO_TOG,
ANALOG_PLL_VIDEO_SS,
ANALOG_PLL_VIDEO_SS_SET,
ANALOG_PLL_VIDEO_SS_CLR,
ANALOG_PLL_VIDEO_SS_TOG,
ANALOG_PLL_VIDEO_NUM,
ANALOG_PLL_VIDEO_NUM_SET,
ANALOG_PLL_VIDEO_NUM_CLR,
ANALOG_PLL_VIDEO_NUM_TOG,
ANALOG_PLL_VIDEO_DENOM,
ANALOG_PLL_VIDEO_DENOM_SET,
ANALOG_PLL_VIDEO_DENOM_CLR,
ANALOG_PLL_VIDEO_DENOM_TOG,
ANALOG_PLL_MISC0,
ANALOG_PLL_MISC0_SET,
ANALOG_PLL_MISC0_CLR,
ANALOG_PLL_MISC0_TOG,
ANALOG_DIGPROG = 0x800 / sizeof(uint32_t),
ANALOG_MAX,
ANALOG_PLL_LOCK = BIT(31)
};
enum IMX7CCMRegisters {
CCM_MAX = 0xBE00 / sizeof(uint32_t) + 1,
};
enum IMX7PMURegisters {
PMU_MAX = 0x140 / sizeof(uint32_t),
};
#define TYPE_IMX7_CCM "imx7.ccm"
#define IMX7_CCM(obj) OBJECT_CHECK(IMX7CCMState, (obj), TYPE_IMX7_CCM)
typedef struct IMX7CCMState {
/* <private> */
IMXCCMState parent_obj;
/* <public> */
MemoryRegion iomem;
uint32_t ccm[CCM_MAX];
} IMX7CCMState;
#define TYPE_IMX7_ANALOG "imx7.analog"
#define IMX7_ANALOG(obj) OBJECT_CHECK(IMX7AnalogState, (obj), TYPE_IMX7_ANALOG)
typedef struct IMX7AnalogState {
/* <private> */
IMXCCMState parent_obj;
/* <public> */
struct {
MemoryRegion container;
MemoryRegion analog;
MemoryRegion digprog;
MemoryRegion pmu;
} mmio;
uint32_t analog[ANALOG_MAX];
uint32_t pmu[PMU_MAX];
} IMX7AnalogState;
#endif /* IMX7_CCM_H */

View file

@ -0,0 +1,28 @@
/*
* Copyright (c) 2017, Impinj, Inc.
*
* i.MX7 GPR IP block emulation code
*
* Author: Andrey Smirnov <andrew.smirnov@gmail.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#ifndef IMX7_GPR_H
#define IMX7_GPR_H
#include "qemu/bitops.h"
#include "hw/sysbus.h"
#define TYPE_IMX7_GPR "imx7.gpr"
#define IMX7_GPR(obj) OBJECT_CHECK(IMX7GPRState, (obj), TYPE_IMX7_GPR)
typedef struct IMX7GPRState {
/* <private> */
SysBusDevice parent_obj;
MemoryRegion mmio;
} IMX7GPRState;
#endif /* IMX7_GPR_H */

View file

@ -0,0 +1,35 @@
/*
* Copyright (c) 2017, Impinj, Inc.
*
* i.MX7 SNVS block emulation code
*
* Author: Andrey Smirnov <andrew.smirnov@gmail.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#ifndef IMX7_SNVS_H
#define IMX7_SNVS_H
#include "qemu/bitops.h"
#include "hw/sysbus.h"
enum IMX7SNVSRegisters {
SNVS_LPCR = 0x38,
SNVS_LPCR_TOP = BIT(6),
SNVS_LPCR_DP_EN = BIT(5)
};
#define TYPE_IMX7_SNVS "imx7.snvs"
#define IMX7_SNVS(obj) OBJECT_CHECK(IMX7SNVSState, (obj), TYPE_IMX7_SNVS)
typedef struct IMX7SNVSState {
/* <private> */
SysBusDevice parent_obj;
MemoryRegion mmio;
} IMX7SNVSState;
#endif /* IMX7_SNVS_H */

View file

@ -44,6 +44,7 @@ typedef struct SDHCIState {
AddressSpace sysbus_dma_as;
AddressSpace *dma_as;
MemoryRegion *dma_mr;
const MemoryRegionOps *io_ops;
QEMUTimer *insert_timer; /* timer for 'changing' sd card. */
QEMUTimer *transfer_timer;
@ -91,8 +92,18 @@ typedef struct SDHCIState {
/* Configurable properties */
bool pending_insert_quirk; /* Quirk for Raspberry Pi card insert int */
uint32_t quirks;
} SDHCIState;
/*
* Controller does not provide transfer-complete interrupt when not
* busy.
*
* NOTE: This definition is taken out of Linux kernel and so the
* original bit number is preserved
*/
#define SDHCI_QUIRK_NO_BUSY_IRQ BIT(14)
#define TYPE_PCI_SDHCI "sdhci-pci"
#define PCI_SDHCI(obj) OBJECT_CHECK(SDHCIState, (obj), TYPE_PCI_SDHCI)
@ -100,4 +111,6 @@ typedef struct SDHCIState {
#define SYSBUS_SDHCI(obj) \
OBJECT_CHECK(SDHCIState, (obj), TYPE_SYSBUS_SDHCI)
#define TYPE_IMX_USDHC "imx-usdhc"
#endif /* SDHCI_H */

View file

@ -77,6 +77,7 @@
#define TYPE_IMX25_GPT "imx25.gpt"
#define TYPE_IMX31_GPT "imx31.gpt"
#define TYPE_IMX6_GPT "imx6.gpt"
#define TYPE_IMX7_GPT "imx7.gpt"
#define TYPE_IMX_GPT TYPE_IMX25_GPT

16
include/hw/usb/chipidea.h Normal file
View file

@ -0,0 +1,16 @@
#ifndef CHIPIDEA_H
#define CHIPIDEA_H
#include "hw/usb/hcd-ehci.h"
typedef struct ChipideaState {
/*< private >*/
EHCISysBusState parent_obj;
MemoryRegion iomem[3];
} ChipideaState;
#define TYPE_CHIPIDEA "usb-chipidea"
#define CHIPIDEA(obj) OBJECT_CHECK(ChipideaState, (obj), TYPE_CHIPIDEA)
#endif /* CHIPIDEA_H */

View file

@ -512,6 +512,21 @@ enum {
ARM_HWCAP_A64_SHA1 = 1 << 5,
ARM_HWCAP_A64_SHA2 = 1 << 6,
ARM_HWCAP_A64_CRC32 = 1 << 7,
ARM_HWCAP_A64_ATOMICS = 1 << 8,
ARM_HWCAP_A64_FPHP = 1 << 9,
ARM_HWCAP_A64_ASIMDHP = 1 << 10,
ARM_HWCAP_A64_CPUID = 1 << 11,
ARM_HWCAP_A64_ASIMDRDM = 1 << 12,
ARM_HWCAP_A64_JSCVT = 1 << 13,
ARM_HWCAP_A64_FCMA = 1 << 14,
ARM_HWCAP_A64_LRCPC = 1 << 15,
ARM_HWCAP_A64_DCPOP = 1 << 16,
ARM_HWCAP_A64_SHA3 = 1 << 17,
ARM_HWCAP_A64_SM3 = 1 << 18,
ARM_HWCAP_A64_SM4 = 1 << 19,
ARM_HWCAP_A64_ASIMDDP = 1 << 20,
ARM_HWCAP_A64_SHA512 = 1 << 21,
ARM_HWCAP_A64_SVE = 1 << 22,
};
#define ELF_HWCAP get_elf_hwcap()
@ -532,6 +547,10 @@ static uint32_t get_elf_hwcap(void)
GET_FEATURE(ARM_FEATURE_V8_SHA1, ARM_HWCAP_A64_SHA1);
GET_FEATURE(ARM_FEATURE_V8_SHA256, ARM_HWCAP_A64_SHA2);
GET_FEATURE(ARM_FEATURE_CRC, ARM_HWCAP_A64_CRC32);
GET_FEATURE(ARM_FEATURE_V8_SHA3, ARM_HWCAP_A64_SHA3);
GET_FEATURE(ARM_FEATURE_V8_SM3, ARM_HWCAP_A64_SM3);
GET_FEATURE(ARM_FEATURE_V8_SM4, ARM_HWCAP_A64_SM4);
GET_FEATURE(ARM_FEATURE_V8_SHA512, ARM_HWCAP_A64_SHA512);
#undef GET_FEATURE
return hwcaps;

View file

@ -153,6 +153,49 @@ typedef struct {
uint32_t base_mask;
} TCR;
/* Define a maximum sized vector register.
* For 32-bit, this is a 128-bit NEON/AdvSIMD register.
* For 64-bit, this is a 2048-bit SVE register.
*
* Note that the mapping between S, D, and Q views of the register bank
* differs between AArch64 and AArch32.
* In AArch32:
* Qn = regs[n].d[1]:regs[n].d[0]
* Dn = regs[n / 2].d[n & 1]
* Sn = regs[n / 4].d[n % 4 / 2],
* bits 31..0 for even n, and bits 63..32 for odd n
* (and regs[16] to regs[31] are inaccessible)
* In AArch64:
* Zn = regs[n].d[*]
* Qn = regs[n].d[1]:regs[n].d[0]
* Dn = regs[n].d[0]
* Sn = regs[n].d[0] bits 31..0
*
* This corresponds to the architecturally defined mapping between
* the two execution states, and means we do not need to explicitly
* map these registers when changing states.
*
* Align the data for use with TCG host vector operations.
*/
#ifdef TARGET_AARCH64
# define ARM_MAX_VQ 16
#else
# define ARM_MAX_VQ 1
#endif
typedef struct ARMVectorReg {
uint64_t d[2 * ARM_MAX_VQ] QEMU_ALIGNED(16);
} ARMVectorReg;
/* In AArch32 mode, predicate registers do not exist at all. */
#ifdef TARGET_AARCH64
typedef struct ARMPredicateReg {
uint64_t p[2 * ARM_MAX_VQ / 8] QEMU_ALIGNED(16);
} ARMPredicateReg;
#endif
typedef struct CPUARMState {
/* Regs for current mode. */
uint32_t regs[16];
@ -477,22 +520,12 @@ typedef struct CPUARMState {
/* VFP coprocessor state. */
struct {
/* VFP/Neon register state. Note that the mapping between S, D and Q
* views of the register bank differs between AArch64 and AArch32:
* In AArch32:
* Qn = regs[2n+1]:regs[2n]
* Dn = regs[n]
* Sn = regs[n/2] bits 31..0 for even n, and bits 63..32 for odd n
* (and regs[32] to regs[63] are inaccessible)
* In AArch64:
* Qn = regs[2n+1]:regs[2n]
* Dn = regs[2n]
* Sn = regs[2n] bits 31..0
* This corresponds to the architecturally defined mapping between
* the two execution states, and means we do not need to explicitly
* map these registers when changing states.
*/
uint64_t regs[64] QEMU_ALIGNED(16);
ARMVectorReg zregs[32];
#ifdef TARGET_AARCH64
/* Store FFR as pregs[16] to make it easier to treat as any other. */
ARMPredicateReg pregs[17];
#endif
uint32_t xregs[16];
/* We store these fpcsr fields separately for convenience. */
@ -516,6 +549,9 @@ typedef struct CPUARMState {
*/
float_status fp_status;
float_status standard_fp_status;
/* ZCR_EL[1-3] */
uint64_t zcr_el[4];
} vfp;
uint64_t exclusive_addr;
uint64_t exclusive_val;
@ -890,6 +926,8 @@ void pmccntr_sync(CPUARMState *env);
#define CPTR_TCPAC (1U << 31)
#define CPTR_TTA (1U << 20)
#define CPTR_TFP (1U << 10)
#define CPTR_TZ (1U << 8) /* CPTR_EL2 */
#define CPTR_EZ (1U << 8) /* CPTR_EL3 */
#define MDCR_EPMAD (1U << 21)
#define MDCR_EDAD (1U << 20)
@ -1341,6 +1379,10 @@ enum arm_features {
ARM_FEATURE_M_SECURITY, /* M profile Security Extension */
ARM_FEATURE_JAZELLE, /* has (trivial) Jazelle implementation */
ARM_FEATURE_SVE, /* has Scalable Vector Extension */
ARM_FEATURE_V8_SHA512, /* implements SHA512 part of v8 Crypto Extensions */
ARM_FEATURE_V8_SHA3, /* implements SHA3 part of v8 Crypto Extensions */
ARM_FEATURE_V8_SM3, /* implements SM3 part of v8 Crypto Extensions */
ARM_FEATURE_V8_SM4, /* implements SM4 part of v8 Crypto Extensions */
};
static inline int arm_feature(CPUARMState *env, int feature)
@ -1505,6 +1547,34 @@ static inline bool armv7m_nvic_can_take_pending_exception(void *opaque)
* of architecturally banked exceptions.
*/
void armv7m_nvic_set_pending(void *opaque, int irq, bool secure);
/**
* armv7m_nvic_set_pending_derived: mark this derived exception as pending
* @opaque: the NVIC
* @irq: the exception number to mark pending
* @secure: false for non-banked exceptions or for the nonsecure
* version of a banked exception, true for the secure version of a banked
* exception.
*
* Similar to armv7m_nvic_set_pending(), but specifically for derived
* exceptions (exceptions generated in the course of trying to take
* a different exception).
*/
void armv7m_nvic_set_pending_derived(void *opaque, int irq, bool secure);
/**
* armv7m_nvic_get_pending_irq_info: return highest priority pending
* exception, and whether it targets Secure state
* @opaque: the NVIC
* @pirq: set to pending exception number
* @ptargets_secure: set to whether pending exception targets Secure
*
* This function writes the number of the highest priority pending
* exception (the one which would be made active by
* armv7m_nvic_acknowledge_irq()) to @pirq, and sets @ptargets_secure
* to true if the current highest priority pending exception should
* be taken to Secure state, false for NS.
*/
void armv7m_nvic_get_pending_irq_info(void *opaque, int *pirq,
bool *ptargets_secure);
/**
* armv7m_nvic_acknowledge_irq: make highest priority pending exception active
* @opaque: the NVIC
@ -1512,10 +1582,8 @@ void armv7m_nvic_set_pending(void *opaque, int irq, bool secure);
* Move the current highest priority pending exception from the pending
* state to the active state, and update v7m.exception to indicate that
* it is the exception currently being handled.
*
* Returns: true if exception should be taken to Secure state, false for NS
*/
bool armv7m_nvic_acknowledge_irq(void *opaque);
void armv7m_nvic_acknowledge_irq(void *opaque);
/**
* armv7m_nvic_complete_irq: complete specified interrupt or exception
* @opaque: the NVIC
@ -2610,6 +2678,10 @@ static inline bool arm_cpu_data_is_big_endian(CPUARMState *env)
#define ARM_TBFLAG_TBI0_MASK (0x1ull << ARM_TBFLAG_TBI0_SHIFT)
#define ARM_TBFLAG_TBI1_SHIFT 1 /* TBI1 for EL0/1 */
#define ARM_TBFLAG_TBI1_MASK (0x1ull << ARM_TBFLAG_TBI1_SHIFT)
#define ARM_TBFLAG_SVEEXC_EL_SHIFT 2
#define ARM_TBFLAG_SVEEXC_EL_MASK (0x3 << ARM_TBFLAG_SVEEXC_EL_SHIFT)
#define ARM_TBFLAG_ZCR_LEN_SHIFT 4
#define ARM_TBFLAG_ZCR_LEN_MASK (0xf << ARM_TBFLAG_ZCR_LEN_SHIFT)
/* some convenience accessor macros */
#define ARM_TBFLAG_AARCH64_STATE(F) \
@ -2646,6 +2718,10 @@ static inline bool arm_cpu_data_is_big_endian(CPUARMState *env)
(((F) & ARM_TBFLAG_TBI0_MASK) >> ARM_TBFLAG_TBI0_SHIFT)
#define ARM_TBFLAG_TBI1(F) \
(((F) & ARM_TBFLAG_TBI1_MASK) >> ARM_TBFLAG_TBI1_SHIFT)
#define ARM_TBFLAG_SVEEXC_EL(F) \
(((F) & ARM_TBFLAG_SVEEXC_EL_MASK) >> ARM_TBFLAG_SVEEXC_EL_SHIFT)
#define ARM_TBFLAG_ZCR_LEN(F) \
(((F) & ARM_TBFLAG_ZCR_LEN_MASK) >> ARM_TBFLAG_ZCR_LEN_SHIFT)
static inline bool bswap_code(bool sctlr_b)
{
@ -2769,7 +2845,7 @@ static inline void *arm_get_el_change_hook_opaque(ARMCPU *cpu)
*/
static inline uint64_t *aa32_vfp_dreg(CPUARMState *env, unsigned regno)
{
return &env->vfp.regs[regno];
return &env->vfp.zregs[regno >> 1].d[regno & 1];
}
/**
@ -2778,7 +2854,7 @@ static inline uint64_t *aa32_vfp_dreg(CPUARMState *env, unsigned regno)
*/
static inline uint64_t *aa32_vfp_qreg(CPUARMState *env, unsigned regno)
{
return &env->vfp.regs[2 * regno];
return &env->vfp.zregs[regno].d[0];
}
/**
@ -2787,7 +2863,7 @@ static inline uint64_t *aa32_vfp_qreg(CPUARMState *env, unsigned regno)
*/
static inline uint64_t *aa64_vfp_qreg(CPUARMState *env, unsigned regno)
{
return &env->vfp.regs[2 * regno];
return &env->vfp.zregs[regno].d[0];
}
#endif

View file

@ -224,6 +224,10 @@ static void aarch64_any_initfn(Object *obj)
set_feature(&cpu->env, ARM_FEATURE_V8_AES);
set_feature(&cpu->env, ARM_FEATURE_V8_SHA1);
set_feature(&cpu->env, ARM_FEATURE_V8_SHA256);
set_feature(&cpu->env, ARM_FEATURE_V8_SHA512);
set_feature(&cpu->env, ARM_FEATURE_V8_SHA3);
set_feature(&cpu->env, ARM_FEATURE_V8_SM3);
set_feature(&cpu->env, ARM_FEATURE_V8_SM4);
set_feature(&cpu->env, ARM_FEATURE_V8_PMULL);
set_feature(&cpu->env, ARM_FEATURE_CRC);
cpu->ctr = 0x80038003; /* 32 byte I and D cacheline size, VIPT icache */

View file

@ -1,7 +1,7 @@
/*
* crypto_helper.c - emulate v8 Crypto Extensions instructions
*
* Copyright (C) 2013 - 2014 Linaro Ltd <ard.biesheuvel@linaro.org>
* Copyright (C) 2013 - 2018 Linaro Ltd <ard.biesheuvel@linaro.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -419,3 +419,278 @@ void HELPER(crypto_sha256su1)(void *vd, void *vn, void *vm)
rd[0] = d.l[0];
rd[1] = d.l[1];
}
/*
* The SHA-512 logical functions (same as above but using 64-bit operands)
*/
static uint64_t cho512(uint64_t x, uint64_t y, uint64_t z)
{
return (x & (y ^ z)) ^ z;
}
static uint64_t maj512(uint64_t x, uint64_t y, uint64_t z)
{
return (x & y) | ((x | y) & z);
}
static uint64_t S0_512(uint64_t x)
{
return ror64(x, 28) ^ ror64(x, 34) ^ ror64(x, 39);
}
static uint64_t S1_512(uint64_t x)
{
return ror64(x, 14) ^ ror64(x, 18) ^ ror64(x, 41);
}
static uint64_t s0_512(uint64_t x)
{
return ror64(x, 1) ^ ror64(x, 8) ^ (x >> 7);
}
static uint64_t s1_512(uint64_t x)
{
return ror64(x, 19) ^ ror64(x, 61) ^ (x >> 6);
}
void HELPER(crypto_sha512h)(void *vd, void *vn, void *vm)
{
uint64_t *rd = vd;
uint64_t *rn = vn;
uint64_t *rm = vm;
uint64_t d0 = rd[0];
uint64_t d1 = rd[1];
d1 += S1_512(rm[1]) + cho512(rm[1], rn[0], rn[1]);
d0 += S1_512(d1 + rm[0]) + cho512(d1 + rm[0], rm[1], rn[0]);
rd[0] = d0;
rd[1] = d1;
}
void HELPER(crypto_sha512h2)(void *vd, void *vn, void *vm)
{
uint64_t *rd = vd;
uint64_t *rn = vn;
uint64_t *rm = vm;
uint64_t d0 = rd[0];
uint64_t d1 = rd[1];
d1 += S0_512(rm[0]) + maj512(rn[0], rm[1], rm[0]);
d0 += S0_512(d1) + maj512(d1, rm[0], rm[1]);
rd[0] = d0;
rd[1] = d1;
}
void HELPER(crypto_sha512su0)(void *vd, void *vn)
{
uint64_t *rd = vd;
uint64_t *rn = vn;
uint64_t d0 = rd[0];
uint64_t d1 = rd[1];
d0 += s0_512(rd[1]);
d1 += s0_512(rn[0]);
rd[0] = d0;
rd[1] = d1;
}
void HELPER(crypto_sha512su1)(void *vd, void *vn, void *vm)
{
uint64_t *rd = vd;
uint64_t *rn = vn;
uint64_t *rm = vm;
rd[0] += s1_512(rn[0]) + rm[0];
rd[1] += s1_512(rn[1]) + rm[1];
}
void HELPER(crypto_sm3partw1)(void *vd, void *vn, void *vm)
{
uint64_t *rd = vd;
uint64_t *rn = vn;
uint64_t *rm = vm;
union CRYPTO_STATE d = { .l = { rd[0], rd[1] } };
union CRYPTO_STATE n = { .l = { rn[0], rn[1] } };
union CRYPTO_STATE m = { .l = { rm[0], rm[1] } };
uint32_t t;
t = CR_ST_WORD(d, 0) ^ CR_ST_WORD(n, 0) ^ ror32(CR_ST_WORD(m, 1), 17);
CR_ST_WORD(d, 0) = t ^ ror32(t, 17) ^ ror32(t, 9);
t = CR_ST_WORD(d, 1) ^ CR_ST_WORD(n, 1) ^ ror32(CR_ST_WORD(m, 2), 17);
CR_ST_WORD(d, 1) = t ^ ror32(t, 17) ^ ror32(t, 9);
t = CR_ST_WORD(d, 2) ^ CR_ST_WORD(n, 2) ^ ror32(CR_ST_WORD(m, 3), 17);
CR_ST_WORD(d, 2) = t ^ ror32(t, 17) ^ ror32(t, 9);
t = CR_ST_WORD(d, 3) ^ CR_ST_WORD(n, 3) ^ ror32(CR_ST_WORD(d, 0), 17);
CR_ST_WORD(d, 3) = t ^ ror32(t, 17) ^ ror32(t, 9);
rd[0] = d.l[0];
rd[1] = d.l[1];
}
void HELPER(crypto_sm3partw2)(void *vd, void *vn, void *vm)
{
uint64_t *rd = vd;
uint64_t *rn = vn;
uint64_t *rm = vm;
union CRYPTO_STATE d = { .l = { rd[0], rd[1] } };
union CRYPTO_STATE n = { .l = { rn[0], rn[1] } };
union CRYPTO_STATE m = { .l = { rm[0], rm[1] } };
uint32_t t = CR_ST_WORD(n, 0) ^ ror32(CR_ST_WORD(m, 0), 25);
CR_ST_WORD(d, 0) ^= t;
CR_ST_WORD(d, 1) ^= CR_ST_WORD(n, 1) ^ ror32(CR_ST_WORD(m, 1), 25);
CR_ST_WORD(d, 2) ^= CR_ST_WORD(n, 2) ^ ror32(CR_ST_WORD(m, 2), 25);
CR_ST_WORD(d, 3) ^= CR_ST_WORD(n, 3) ^ ror32(CR_ST_WORD(m, 3), 25) ^
ror32(t, 17) ^ ror32(t, 2) ^ ror32(t, 26);
rd[0] = d.l[0];
rd[1] = d.l[1];
}
void HELPER(crypto_sm3tt)(void *vd, void *vn, void *vm, uint32_t imm2,
uint32_t opcode)
{
uint64_t *rd = vd;
uint64_t *rn = vn;
uint64_t *rm = vm;
union CRYPTO_STATE d = { .l = { rd[0], rd[1] } };
union CRYPTO_STATE n = { .l = { rn[0], rn[1] } };
union CRYPTO_STATE m = { .l = { rm[0], rm[1] } };
uint32_t t;
assert(imm2 < 4);
if (opcode == 0 || opcode == 2) {
/* SM3TT1A, SM3TT2A */
t = par(CR_ST_WORD(d, 3), CR_ST_WORD(d, 2), CR_ST_WORD(d, 1));
} else if (opcode == 1) {
/* SM3TT1B */
t = maj(CR_ST_WORD(d, 3), CR_ST_WORD(d, 2), CR_ST_WORD(d, 1));
} else if (opcode == 3) {
/* SM3TT2B */
t = cho(CR_ST_WORD(d, 3), CR_ST_WORD(d, 2), CR_ST_WORD(d, 1));
} else {
g_assert_not_reached();
}
t += CR_ST_WORD(d, 0) + CR_ST_WORD(m, imm2);
CR_ST_WORD(d, 0) = CR_ST_WORD(d, 1);
if (opcode < 2) {
/* SM3TT1A, SM3TT1B */
t += CR_ST_WORD(n, 3) ^ ror32(CR_ST_WORD(d, 3), 20);
CR_ST_WORD(d, 1) = ror32(CR_ST_WORD(d, 2), 23);
} else {
/* SM3TT2A, SM3TT2B */
t += CR_ST_WORD(n, 3);
t ^= rol32(t, 9) ^ rol32(t, 17);
CR_ST_WORD(d, 1) = ror32(CR_ST_WORD(d, 2), 13);
}
CR_ST_WORD(d, 2) = CR_ST_WORD(d, 3);
CR_ST_WORD(d, 3) = t;
rd[0] = d.l[0];
rd[1] = d.l[1];
}
static uint8_t const sm4_sbox[] = {
0xd6, 0x90, 0xe9, 0xfe, 0xcc, 0xe1, 0x3d, 0xb7,
0x16, 0xb6, 0x14, 0xc2, 0x28, 0xfb, 0x2c, 0x05,
0x2b, 0x67, 0x9a, 0x76, 0x2a, 0xbe, 0x04, 0xc3,
0xaa, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99,
0x9c, 0x42, 0x50, 0xf4, 0x91, 0xef, 0x98, 0x7a,
0x33, 0x54, 0x0b, 0x43, 0xed, 0xcf, 0xac, 0x62,
0xe4, 0xb3, 0x1c, 0xa9, 0xc9, 0x08, 0xe8, 0x95,
0x80, 0xdf, 0x94, 0xfa, 0x75, 0x8f, 0x3f, 0xa6,
0x47, 0x07, 0xa7, 0xfc, 0xf3, 0x73, 0x17, 0xba,
0x83, 0x59, 0x3c, 0x19, 0xe6, 0x85, 0x4f, 0xa8,
0x68, 0x6b, 0x81, 0xb2, 0x71, 0x64, 0xda, 0x8b,
0xf8, 0xeb, 0x0f, 0x4b, 0x70, 0x56, 0x9d, 0x35,
0x1e, 0x24, 0x0e, 0x5e, 0x63, 0x58, 0xd1, 0xa2,
0x25, 0x22, 0x7c, 0x3b, 0x01, 0x21, 0x78, 0x87,
0xd4, 0x00, 0x46, 0x57, 0x9f, 0xd3, 0x27, 0x52,
0x4c, 0x36, 0x02, 0xe7, 0xa0, 0xc4, 0xc8, 0x9e,
0xea, 0xbf, 0x8a, 0xd2, 0x40, 0xc7, 0x38, 0xb5,
0xa3, 0xf7, 0xf2, 0xce, 0xf9, 0x61, 0x15, 0xa1,
0xe0, 0xae, 0x5d, 0xa4, 0x9b, 0x34, 0x1a, 0x55,
0xad, 0x93, 0x32, 0x30, 0xf5, 0x8c, 0xb1, 0xe3,
0x1d, 0xf6, 0xe2, 0x2e, 0x82, 0x66, 0xca, 0x60,
0xc0, 0x29, 0x23, 0xab, 0x0d, 0x53, 0x4e, 0x6f,
0xd5, 0xdb, 0x37, 0x45, 0xde, 0xfd, 0x8e, 0x2f,
0x03, 0xff, 0x6a, 0x72, 0x6d, 0x6c, 0x5b, 0x51,
0x8d, 0x1b, 0xaf, 0x92, 0xbb, 0xdd, 0xbc, 0x7f,
0x11, 0xd9, 0x5c, 0x41, 0x1f, 0x10, 0x5a, 0xd8,
0x0a, 0xc1, 0x31, 0x88, 0xa5, 0xcd, 0x7b, 0xbd,
0x2d, 0x74, 0xd0, 0x12, 0xb8, 0xe5, 0xb4, 0xb0,
0x89, 0x69, 0x97, 0x4a, 0x0c, 0x96, 0x77, 0x7e,
0x65, 0xb9, 0xf1, 0x09, 0xc5, 0x6e, 0xc6, 0x84,
0x18, 0xf0, 0x7d, 0xec, 0x3a, 0xdc, 0x4d, 0x20,
0x79, 0xee, 0x5f, 0x3e, 0xd7, 0xcb, 0x39, 0x48,
};
void HELPER(crypto_sm4e)(void *vd, void *vn)
{
uint64_t *rd = vd;
uint64_t *rn = vn;
union CRYPTO_STATE d = { .l = { rd[0], rd[1] } };
union CRYPTO_STATE n = { .l = { rn[0], rn[1] } };
uint32_t t, i;
for (i = 0; i < 4; i++) {
t = CR_ST_WORD(d, (i + 1) % 4) ^
CR_ST_WORD(d, (i + 2) % 4) ^
CR_ST_WORD(d, (i + 3) % 4) ^
CR_ST_WORD(n, i);
t = sm4_sbox[t & 0xff] |
sm4_sbox[(t >> 8) & 0xff] << 8 |
sm4_sbox[(t >> 16) & 0xff] << 16 |
sm4_sbox[(t >> 24) & 0xff] << 24;
CR_ST_WORD(d, i) ^= t ^ rol32(t, 2) ^ rol32(t, 10) ^ rol32(t, 18) ^
rol32(t, 24);
}
rd[0] = d.l[0];
rd[1] = d.l[1];
}
void HELPER(crypto_sm4ekey)(void *vd, void *vn, void* vm)
{
uint64_t *rd = vd;
uint64_t *rn = vn;
uint64_t *rm = vm;
union CRYPTO_STATE d;
union CRYPTO_STATE n = { .l = { rn[0], rn[1] } };
union CRYPTO_STATE m = { .l = { rm[0], rm[1] } };
uint32_t t, i;
d = n;
for (i = 0; i < 4; i++) {
t = CR_ST_WORD(d, (i + 1) % 4) ^
CR_ST_WORD(d, (i + 2) % 4) ^
CR_ST_WORD(d, (i + 3) % 4) ^
CR_ST_WORD(m, i);
t = sm4_sbox[t & 0xff] |
sm4_sbox[(t >> 8) & 0xff] << 8 |
sm4_sbox[(t >> 16) & 0xff] << 16 |
sm4_sbox[(t >> 24) & 0xff] << 24;
CR_ST_WORD(d, i) ^= t ^ rol32(t, 13) ^ rol32(t, 23);
}
rd[0] = d.l[0];
rd[1] = d.l[1];
}

View file

@ -4266,6 +4266,125 @@ static const ARMCPRegInfo debug_lpae_cp_reginfo[] = {
REGINFO_SENTINEL
};
/* Return the exception level to which SVE-disabled exceptions should
* be taken, or 0 if SVE is enabled.
*/
static int sve_exception_el(CPUARMState *env)
{
#ifndef CONFIG_USER_ONLY
unsigned current_el = arm_current_el(env);
/* The CPACR.ZEN controls traps to EL1:
* 0, 2 : trap EL0 and EL1 accesses
* 1 : trap only EL0 accesses
* 3 : trap no accesses
*/
switch (extract32(env->cp15.cpacr_el1, 16, 2)) {
default:
if (current_el <= 1) {
/* Trap to PL1, which might be EL1 or EL3 */
if (arm_is_secure(env) && !arm_el_is_aa64(env, 3)) {
return 3;
}
return 1;
}
break;
case 1:
if (current_el == 0) {
return 1;
}
break;
case 3:
break;
}
/* Similarly for CPACR.FPEN, after having checked ZEN. */
switch (extract32(env->cp15.cpacr_el1, 20, 2)) {
default:
if (current_el <= 1) {
if (arm_is_secure(env) && !arm_el_is_aa64(env, 3)) {
return 3;
}
return 1;
}
break;
case 1:
if (current_el == 0) {
return 1;
}
break;
case 3:
break;
}
/* CPTR_EL2. Check both TZ and TFP. */
if (current_el <= 2
&& (env->cp15.cptr_el[2] & (CPTR_TFP | CPTR_TZ))
&& !arm_is_secure_below_el3(env)) {
return 2;
}
/* CPTR_EL3. Check both EZ and TFP. */
if (!(env->cp15.cptr_el[3] & CPTR_EZ)
|| (env->cp15.cptr_el[3] & CPTR_TFP)) {
return 3;
}
#endif
return 0;
}
static CPAccessResult zcr_access(CPUARMState *env, const ARMCPRegInfo *ri,
bool isread)
{
switch (sve_exception_el(env)) {
case 3:
return CP_ACCESS_TRAP_EL3;
case 2:
return CP_ACCESS_TRAP_EL2;
case 1:
return CP_ACCESS_TRAP;
}
return CP_ACCESS_OK;
}
static void zcr_write(CPUARMState *env, const ARMCPRegInfo *ri,
uint64_t value)
{
/* Bits other than [3:0] are RAZ/WI. */
raw_write(env, ri, value & 0xf);
}
static const ARMCPRegInfo zcr_el1_reginfo = {
.name = "ZCR_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 1, .crm = 2, .opc2 = 0,
.access = PL1_RW, .accessfn = zcr_access, .type = ARM_CP_64BIT,
.fieldoffset = offsetof(CPUARMState, vfp.zcr_el[1]),
.writefn = zcr_write, .raw_writefn = raw_write
};
static const ARMCPRegInfo zcr_el2_reginfo = {
.name = "ZCR_EL2", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 4, .crn = 1, .crm = 2, .opc2 = 0,
.access = PL2_RW, .accessfn = zcr_access, .type = ARM_CP_64BIT,
.fieldoffset = offsetof(CPUARMState, vfp.zcr_el[2]),
.writefn = zcr_write, .raw_writefn = raw_write
};
static const ARMCPRegInfo zcr_no_el2_reginfo = {
.name = "ZCR_EL2", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 4, .crn = 1, .crm = 2, .opc2 = 0,
.access = PL2_RW, .type = ARM_CP_64BIT,
.readfn = arm_cp_read_zero, .writefn = arm_cp_write_ignore
};
static const ARMCPRegInfo zcr_el3_reginfo = {
.name = "ZCR_EL3", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 6, .crn = 1, .crm = 2, .opc2 = 0,
.access = PL3_RW, .accessfn = zcr_access, .type = ARM_CP_64BIT,
.fieldoffset = offsetof(CPUARMState, vfp.zcr_el[3]),
.writefn = zcr_write, .raw_writefn = raw_write
};
void hw_watchpoint_update(ARMCPU *cpu, int n)
{
CPUARMState *env = &cpu->env;
@ -5332,6 +5451,18 @@ void register_cp_regs_for_features(ARMCPU *cpu)
}
define_one_arm_cp_reg(cpu, &sctlr);
}
if (arm_feature(env, ARM_FEATURE_SVE)) {
define_one_arm_cp_reg(cpu, &zcr_el1_reginfo);
if (arm_feature(env, ARM_FEATURE_EL2)) {
define_one_arm_cp_reg(cpu, &zcr_el2_reginfo);
} else {
define_one_arm_cp_reg(cpu, &zcr_no_el2_reginfo);
}
if (arm_feature(env, ARM_FEATURE_EL3)) {
define_one_arm_cp_reg(cpu, &zcr_el3_reginfo);
}
}
}
void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu)
@ -6161,12 +6292,127 @@ uint32_t arm_phys_excp_target_el(CPUState *cs, uint32_t excp_idx,
return target_el;
}
static void v7m_push(CPUARMState *env, uint32_t val)
static bool v7m_stack_write(ARMCPU *cpu, uint32_t addr, uint32_t value,
ARMMMUIdx mmu_idx, bool ignfault)
{
CPUState *cs = CPU(arm_env_get_cpu(env));
CPUState *cs = CPU(cpu);
CPUARMState *env = &cpu->env;
MemTxAttrs attrs = {};
MemTxResult txres;
target_ulong page_size;
hwaddr physaddr;
int prot;
ARMMMUFaultInfo fi;
bool secure = mmu_idx & ARM_MMU_IDX_M_S;
int exc;
bool exc_secure;
env->regs[13] -= 4;
stl_phys(cs->as, env->regs[13], val);
if (get_phys_addr(env, addr, MMU_DATA_STORE, mmu_idx, &physaddr,
&attrs, &prot, &page_size, &fi, NULL)) {
/* MPU/SAU lookup failed */
if (fi.type == ARMFault_QEMU_SFault) {
qemu_log_mask(CPU_LOG_INT,
"...SecureFault with SFSR.AUVIOL during stacking\n");
env->v7m.sfsr |= R_V7M_SFSR_AUVIOL_MASK | R_V7M_SFSR_SFARVALID_MASK;
env->v7m.sfar = addr;
exc = ARMV7M_EXCP_SECURE;
exc_secure = false;
} else {
qemu_log_mask(CPU_LOG_INT, "...MemManageFault with CFSR.MSTKERR\n");
env->v7m.cfsr[secure] |= R_V7M_CFSR_MSTKERR_MASK;
exc = ARMV7M_EXCP_MEM;
exc_secure = secure;
}
goto pend_fault;
}
address_space_stl_le(arm_addressspace(cs, attrs), physaddr, value,
attrs, &txres);
if (txres != MEMTX_OK) {
/* BusFault trying to write the data */
qemu_log_mask(CPU_LOG_INT, "...BusFault with BFSR.STKERR\n");
env->v7m.cfsr[M_REG_NS] |= R_V7M_CFSR_STKERR_MASK;
exc = ARMV7M_EXCP_BUS;
exc_secure = false;
goto pend_fault;
}
return true;
pend_fault:
/* By pending the exception at this point we are making
* the IMPDEF choice "overridden exceptions pended" (see the
* MergeExcInfo() pseudocode). The other choice would be to not
* pend them now and then make a choice about which to throw away
* later if we have two derived exceptions.
* The only case when we must not pend the exception but instead
* throw it away is if we are doing the push of the callee registers
* and we've already generated a derived exception. Even in this
* case we will still update the fault status registers.
*/
if (!ignfault) {
armv7m_nvic_set_pending_derived(env->nvic, exc, exc_secure);
}
return false;
}
static bool v7m_stack_read(ARMCPU *cpu, uint32_t *dest, uint32_t addr,
ARMMMUIdx mmu_idx)
{
CPUState *cs = CPU(cpu);
CPUARMState *env = &cpu->env;
MemTxAttrs attrs = {};
MemTxResult txres;
target_ulong page_size;
hwaddr physaddr;
int prot;
ARMMMUFaultInfo fi;
bool secure = mmu_idx & ARM_MMU_IDX_M_S;
int exc;
bool exc_secure;
uint32_t value;
if (get_phys_addr(env, addr, MMU_DATA_LOAD, mmu_idx, &physaddr,
&attrs, &prot, &page_size, &fi, NULL)) {
/* MPU/SAU lookup failed */
if (fi.type == ARMFault_QEMU_SFault) {
qemu_log_mask(CPU_LOG_INT,
"...SecureFault with SFSR.AUVIOL during unstack\n");
env->v7m.sfsr |= R_V7M_SFSR_AUVIOL_MASK | R_V7M_SFSR_SFARVALID_MASK;
env->v7m.sfar = addr;
exc = ARMV7M_EXCP_SECURE;
exc_secure = false;
} else {
qemu_log_mask(CPU_LOG_INT,
"...MemManageFault with CFSR.MUNSTKERR\n");
env->v7m.cfsr[secure] |= R_V7M_CFSR_MUNSTKERR_MASK;
exc = ARMV7M_EXCP_MEM;
exc_secure = secure;
}
goto pend_fault;
}
value = address_space_ldl(arm_addressspace(cs, attrs), physaddr,
attrs, &txres);
if (txres != MEMTX_OK) {
/* BusFault trying to read the data */
qemu_log_mask(CPU_LOG_INT, "...BusFault with BFSR.UNSTKERR\n");
env->v7m.cfsr[M_REG_NS] |= R_V7M_CFSR_UNSTKERR_MASK;
exc = ARMV7M_EXCP_BUS;
exc_secure = false;
goto pend_fault;
}
*dest = value;
return true;
pend_fault:
/* By pending the exception at this point we are making
* the IMPDEF choice "overridden exceptions pended" (see the
* MergeExcInfo() pseudocode). The other choice would be to not
* pend them now and then make a choice about which to throw away
* later if we have two derived exceptions.
*/
armv7m_nvic_set_pending(env->nvic, exc, exc_secure);
return false;
}
/* Return true if we're using the process stack pointer (not the MSP) */
@ -6395,65 +6641,126 @@ static uint32_t *get_v7m_sp_ptr(CPUARMState *env, bool secure, bool threadmode,
}
}
static uint32_t arm_v7m_load_vector(ARMCPU *cpu, bool targets_secure)
static bool arm_v7m_load_vector(ARMCPU *cpu, int exc, bool targets_secure,
uint32_t *pvec)
{
CPUState *cs = CPU(cpu);
CPUARMState *env = &cpu->env;
MemTxResult result;
hwaddr vec = env->v7m.vecbase[targets_secure] + env->v7m.exception * 4;
uint32_t addr;
uint32_t addr = env->v7m.vecbase[targets_secure] + exc * 4;
uint32_t vector_entry;
MemTxAttrs attrs = {};
ARMMMUIdx mmu_idx;
bool exc_secure;
addr = address_space_ldl(cs->as, vec,
MEMTXATTRS_UNSPECIFIED, &result);
if (result != MEMTX_OK) {
/* Architecturally this should cause a HardFault setting HSFR.VECTTBL,
* which would then be immediately followed by our failing to load
* the entry vector for that HardFault, which is a Lockup case.
* Since we don't model Lockup, we just report this guest error
* via cpu_abort().
*/
cpu_abort(cs, "Failed to read from %s exception vector table "
"entry %08x\n", targets_secure ? "secure" : "nonsecure",
(unsigned)vec);
mmu_idx = arm_v7m_mmu_idx_for_secstate_and_priv(env, targets_secure, true);
/* We don't do a get_phys_addr() here because the rules for vector
* loads are special: they always use the default memory map, and
* the default memory map permits reads from all addresses.
* Since there's no easy way to pass through to pmsav8_mpu_lookup()
* that we want this special case which would always say "yes",
* we just do the SAU lookup here followed by a direct physical load.
*/
attrs.secure = targets_secure;
attrs.user = false;
if (arm_feature(env, ARM_FEATURE_M_SECURITY)) {
V8M_SAttributes sattrs = {};
v8m_security_lookup(env, addr, MMU_DATA_LOAD, mmu_idx, &sattrs);
if (sattrs.ns) {
attrs.secure = false;
} else if (!targets_secure) {
/* NS access to S memory */
goto load_fail;
}
}
return addr;
vector_entry = address_space_ldl(arm_addressspace(cs, attrs), addr,
attrs, &result);
if (result != MEMTX_OK) {
goto load_fail;
}
*pvec = vector_entry;
return true;
load_fail:
/* All vector table fetch fails are reported as HardFault, with
* HFSR.VECTTBL and .FORCED set. (FORCED is set because
* technically the underlying exception is a MemManage or BusFault
* that is escalated to HardFault.) This is a terminal exception,
* so we will either take the HardFault immediately or else enter
* lockup (the latter case is handled in armv7m_nvic_set_pending_derived()).
*/
exc_secure = targets_secure ||
!(cpu->env.v7m.aircr & R_V7M_AIRCR_BFHFNMINS_MASK);
env->v7m.hfsr |= R_V7M_HFSR_VECTTBL_MASK | R_V7M_HFSR_FORCED_MASK;
armv7m_nvic_set_pending_derived(env->nvic, ARMV7M_EXCP_HARD, exc_secure);
return false;
}
static void v7m_push_callee_stack(ARMCPU *cpu, uint32_t lr, bool dotailchain)
static bool v7m_push_callee_stack(ARMCPU *cpu, uint32_t lr, bool dotailchain,
bool ignore_faults)
{
/* For v8M, push the callee-saves register part of the stack frame.
* Compare the v8M pseudocode PushCalleeStack().
* In the tailchaining case this may not be the current stack.
*/
CPUARMState *env = &cpu->env;
CPUState *cs = CPU(cpu);
uint32_t *frame_sp_p;
uint32_t frameptr;
ARMMMUIdx mmu_idx;
bool stacked_ok;
if (dotailchain) {
frame_sp_p = get_v7m_sp_ptr(env, true,
lr & R_V7M_EXCRET_MODE_MASK,
bool mode = lr & R_V7M_EXCRET_MODE_MASK;
bool priv = !(env->v7m.control[M_REG_S] & R_V7M_CONTROL_NPRIV_MASK) ||
!mode;
mmu_idx = arm_v7m_mmu_idx_for_secstate_and_priv(env, M_REG_S, priv);
frame_sp_p = get_v7m_sp_ptr(env, M_REG_S, mode,
lr & R_V7M_EXCRET_SPSEL_MASK);
} else {
mmu_idx = core_to_arm_mmu_idx(env, cpu_mmu_index(env, false));
frame_sp_p = &env->regs[13];
}
frameptr = *frame_sp_p - 0x28;
stl_phys(cs->as, frameptr, 0xfefa125b);
stl_phys(cs->as, frameptr + 0x8, env->regs[4]);
stl_phys(cs->as, frameptr + 0xc, env->regs[5]);
stl_phys(cs->as, frameptr + 0x10, env->regs[6]);
stl_phys(cs->as, frameptr + 0x14, env->regs[7]);
stl_phys(cs->as, frameptr + 0x18, env->regs[8]);
stl_phys(cs->as, frameptr + 0x1c, env->regs[9]);
stl_phys(cs->as, frameptr + 0x20, env->regs[10]);
stl_phys(cs->as, frameptr + 0x24, env->regs[11]);
/* Write as much of the stack frame as we can. A write failure may
* cause us to pend a derived exception.
*/
stacked_ok =
v7m_stack_write(cpu, frameptr, 0xfefa125b, mmu_idx, ignore_faults) &&
v7m_stack_write(cpu, frameptr + 0x8, env->regs[4], mmu_idx,
ignore_faults) &&
v7m_stack_write(cpu, frameptr + 0xc, env->regs[5], mmu_idx,
ignore_faults) &&
v7m_stack_write(cpu, frameptr + 0x10, env->regs[6], mmu_idx,
ignore_faults) &&
v7m_stack_write(cpu, frameptr + 0x14, env->regs[7], mmu_idx,
ignore_faults) &&
v7m_stack_write(cpu, frameptr + 0x18, env->regs[8], mmu_idx,
ignore_faults) &&
v7m_stack_write(cpu, frameptr + 0x1c, env->regs[9], mmu_idx,
ignore_faults) &&
v7m_stack_write(cpu, frameptr + 0x20, env->regs[10], mmu_idx,
ignore_faults) &&
v7m_stack_write(cpu, frameptr + 0x24, env->regs[11], mmu_idx,
ignore_faults);
/* Update SP regardless of whether any of the stack accesses failed.
* When we implement v8M stack limit checking then this attempt to
* update SP might also fail and result in a derived exception.
*/
*frame_sp_p = frameptr;
return !stacked_ok;
}
static void v7m_exception_taken(ARMCPU *cpu, uint32_t lr, bool dotailchain)
static void v7m_exception_taken(ARMCPU *cpu, uint32_t lr, bool dotailchain,
bool ignore_stackfaults)
{
/* Do the "take the exception" parts of exception entry,
* but not the pushing of state to the stack. This is
@ -6462,8 +6769,10 @@ static void v7m_exception_taken(ARMCPU *cpu, uint32_t lr, bool dotailchain)
CPUARMState *env = &cpu->env;
uint32_t addr;
bool targets_secure;
int exc;
bool push_failed = false;
targets_secure = armv7m_nvic_acknowledge_irq(env->nvic);
armv7m_nvic_get_pending_irq_info(env->nvic, &exc, &targets_secure);
if (arm_feature(env, ARM_FEATURE_V8)) {
if (arm_feature(env, ARM_FEATURE_M_SECURITY) &&
@ -6489,7 +6798,8 @@ static void v7m_exception_taken(ARMCPU *cpu, uint32_t lr, bool dotailchain)
*/
if (lr & R_V7M_EXCRET_DCRS_MASK &&
!(dotailchain && (lr & R_V7M_EXCRET_ES_MASK))) {
v7m_push_callee_stack(cpu, lr, dotailchain);
push_failed = v7m_push_callee_stack(cpu, lr, dotailchain,
ignore_stackfaults);
}
lr |= R_V7M_EXCRET_DCRS_MASK;
}
@ -6531,6 +6841,27 @@ static void v7m_exception_taken(ARMCPU *cpu, uint32_t lr, bool dotailchain)
}
}
if (push_failed && !ignore_stackfaults) {
/* Derived exception on callee-saves register stacking:
* we might now want to take a different exception which
* targets a different security state, so try again from the top.
*/
v7m_exception_taken(cpu, lr, true, true);
return;
}
if (!arm_v7m_load_vector(cpu, exc, targets_secure, &addr)) {
/* Vector load failed: derived exception */
v7m_exception_taken(cpu, lr, true, true);
return;
}
/* Now we've done everything that might cause a derived exception
* we can go ahead and activate whichever exception we're going to
* take (which might now be the derived exception).
*/
armv7m_nvic_acknowledge_irq(env->nvic);
/* Switch to target security state -- must do this before writing SPSEL */
switch_v7m_security_state(env, targets_secure);
write_v7m_control_spsel(env, 0);
@ -6538,34 +6869,55 @@ static void v7m_exception_taken(ARMCPU *cpu, uint32_t lr, bool dotailchain)
/* Clear IT bits */
env->condexec_bits = 0;
env->regs[14] = lr;
addr = arm_v7m_load_vector(cpu, targets_secure);
env->regs[15] = addr & 0xfffffffe;
env->thumb = addr & 1;
}
static void v7m_push_stack(ARMCPU *cpu)
static bool v7m_push_stack(ARMCPU *cpu)
{
/* Do the "set up stack frame" part of exception entry,
* similar to pseudocode PushStack().
* Return true if we generate a derived exception (and so
* should ignore further stack faults trying to process
* that derived exception.)
*/
bool stacked_ok;
CPUARMState *env = &cpu->env;
uint32_t xpsr = xpsr_read(env);
uint32_t frameptr = env->regs[13];
ARMMMUIdx mmu_idx = core_to_arm_mmu_idx(env, cpu_mmu_index(env, false));
/* Align stack pointer if the guest wants that */
if ((env->regs[13] & 4) &&
if ((frameptr & 4) &&
(env->v7m.ccr[env->v7m.secure] & R_V7M_CCR_STKALIGN_MASK)) {
env->regs[13] -= 4;
frameptr -= 4;
xpsr |= XPSR_SPREALIGN;
}
/* Switch to the handler mode. */
v7m_push(env, xpsr);
v7m_push(env, env->regs[15]);
v7m_push(env, env->regs[14]);
v7m_push(env, env->regs[12]);
v7m_push(env, env->regs[3]);
v7m_push(env, env->regs[2]);
v7m_push(env, env->regs[1]);
v7m_push(env, env->regs[0]);
frameptr -= 0x20;
/* Write as much of the stack frame as we can. If we fail a stack
* write this will result in a derived exception being pended
* (which may be taken in preference to the one we started with
* if it has higher priority).
*/
stacked_ok =
v7m_stack_write(cpu, frameptr, env->regs[0], mmu_idx, false) &&
v7m_stack_write(cpu, frameptr + 4, env->regs[1], mmu_idx, false) &&
v7m_stack_write(cpu, frameptr + 8, env->regs[2], mmu_idx, false) &&
v7m_stack_write(cpu, frameptr + 12, env->regs[3], mmu_idx, false) &&
v7m_stack_write(cpu, frameptr + 16, env->regs[12], mmu_idx, false) &&
v7m_stack_write(cpu, frameptr + 20, env->regs[14], mmu_idx, false) &&
v7m_stack_write(cpu, frameptr + 24, env->regs[15], mmu_idx, false) &&
v7m_stack_write(cpu, frameptr + 28, xpsr, mmu_idx, false);
/* Update SP regardless of whether any of the stack accesses failed.
* When we implement v8M stack limit checking then this attempt to
* update SP might also fail and result in a derived exception.
*/
env->regs[13] = frameptr;
return !stacked_ok;
}
static void do_v7m_exception_exit(ARMCPU *cpu)
@ -6711,7 +7063,7 @@ static void do_v7m_exception_exit(ARMCPU *cpu)
if (sfault) {
env->v7m.sfsr |= R_V7M_SFSR_INVER_MASK;
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SECURE, false);
v7m_exception_taken(cpu, excret, true);
v7m_exception_taken(cpu, excret, true, false);
qemu_log_mask(CPU_LOG_INT, "...taking SecureFault on existing "
"stackframe: failed EXC_RETURN.ES validity check\n");
return;
@ -6723,7 +7075,7 @@ static void do_v7m_exception_exit(ARMCPU *cpu)
*/
env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_INVPC_MASK;
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, env->v7m.secure);
v7m_exception_taken(cpu, excret, true);
v7m_exception_taken(cpu, excret, true, false);
qemu_log_mask(CPU_LOG_INT, "...taking UsageFault on existing "
"stackframe: failed exception return integrity check\n");
return;
@ -6752,6 +7104,11 @@ static void do_v7m_exception_exit(ARMCPU *cpu)
!return_to_handler,
return_to_sp_process);
uint32_t frameptr = *frame_sp_p;
bool pop_ok = true;
ARMMMUIdx mmu_idx;
mmu_idx = arm_v7m_mmu_idx_for_secstate_and_priv(env, return_to_secure,
!return_to_handler);
if (!QEMU_IS_ALIGNED(frameptr, 8) &&
arm_feature(env, ARM_FEATURE_V8)) {
@ -6771,36 +7128,45 @@ static void do_v7m_exception_exit(ARMCPU *cpu)
/* Take a SecureFault on the current stack */
env->v7m.sfsr |= R_V7M_SFSR_INVIS_MASK;
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SECURE, false);
v7m_exception_taken(cpu, excret, true);
v7m_exception_taken(cpu, excret, true, false);
qemu_log_mask(CPU_LOG_INT, "...taking SecureFault on existing "
"stackframe: failed exception return integrity "
"signature check\n");
return;
}
env->regs[4] = ldl_phys(cs->as, frameptr + 0x8);
env->regs[5] = ldl_phys(cs->as, frameptr + 0xc);
env->regs[6] = ldl_phys(cs->as, frameptr + 0x10);
env->regs[7] = ldl_phys(cs->as, frameptr + 0x14);
env->regs[8] = ldl_phys(cs->as, frameptr + 0x18);
env->regs[9] = ldl_phys(cs->as, frameptr + 0x1c);
env->regs[10] = ldl_phys(cs->as, frameptr + 0x20);
env->regs[11] = ldl_phys(cs->as, frameptr + 0x24);
pop_ok =
v7m_stack_read(cpu, &env->regs[4], frameptr + 0x8, mmu_idx) &&
v7m_stack_read(cpu, &env->regs[4], frameptr + 0x8, mmu_idx) &&
v7m_stack_read(cpu, &env->regs[5], frameptr + 0xc, mmu_idx) &&
v7m_stack_read(cpu, &env->regs[6], frameptr + 0x10, mmu_idx) &&
v7m_stack_read(cpu, &env->regs[7], frameptr + 0x14, mmu_idx) &&
v7m_stack_read(cpu, &env->regs[8], frameptr + 0x18, mmu_idx) &&
v7m_stack_read(cpu, &env->regs[9], frameptr + 0x1c, mmu_idx) &&
v7m_stack_read(cpu, &env->regs[10], frameptr + 0x20, mmu_idx) &&
v7m_stack_read(cpu, &env->regs[11], frameptr + 0x24, mmu_idx);
frameptr += 0x28;
}
/* Pop registers. TODO: make these accesses use the correct
* attributes and address space (S/NS, priv/unpriv) and handle
* memory transaction failures.
*/
env->regs[0] = ldl_phys(cs->as, frameptr);
env->regs[1] = ldl_phys(cs->as, frameptr + 0x4);
env->regs[2] = ldl_phys(cs->as, frameptr + 0x8);
env->regs[3] = ldl_phys(cs->as, frameptr + 0xc);
env->regs[12] = ldl_phys(cs->as, frameptr + 0x10);
env->regs[14] = ldl_phys(cs->as, frameptr + 0x14);
env->regs[15] = ldl_phys(cs->as, frameptr + 0x18);
/* Pop registers */
pop_ok = pop_ok &&
v7m_stack_read(cpu, &env->regs[0], frameptr, mmu_idx) &&
v7m_stack_read(cpu, &env->regs[1], frameptr + 0x4, mmu_idx) &&
v7m_stack_read(cpu, &env->regs[2], frameptr + 0x8, mmu_idx) &&
v7m_stack_read(cpu, &env->regs[3], frameptr + 0xc, mmu_idx) &&
v7m_stack_read(cpu, &env->regs[12], frameptr + 0x10, mmu_idx) &&
v7m_stack_read(cpu, &env->regs[14], frameptr + 0x14, mmu_idx) &&
v7m_stack_read(cpu, &env->regs[15], frameptr + 0x18, mmu_idx) &&
v7m_stack_read(cpu, &xpsr, frameptr + 0x1c, mmu_idx);
if (!pop_ok) {
/* v7m_stack_read() pended a fault, so take it (as a tail
* chained exception on the same stack frame)
*/
v7m_exception_taken(cpu, excret, true, false);
return;
}
/* Returning from an exception with a PC with bit 0 set is defined
* behaviour on v8M (bit 0 is ignored), but for v7M it was specified
@ -6819,8 +7185,6 @@ static void do_v7m_exception_exit(ARMCPU *cpu)
}
}
xpsr = ldl_phys(cs->as, frameptr + 0x1c);
if (arm_feature(env, ARM_FEATURE_V8)) {
/* For v8M we have to check whether the xPSR exception field
* matches the EXCRET value for return to handler/thread
@ -6836,7 +7200,7 @@ static void do_v7m_exception_exit(ARMCPU *cpu)
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE,
env->v7m.secure);
env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_INVPC_MASK;
v7m_exception_taken(cpu, excret, true);
v7m_exception_taken(cpu, excret, true, false);
qemu_log_mask(CPU_LOG_INT, "...taking UsageFault on existing "
"stackframe: failed exception return integrity "
"check\n");
@ -6869,11 +7233,13 @@ static void do_v7m_exception_exit(ARMCPU *cpu)
/* Take an INVPC UsageFault by pushing the stack again;
* we know we're v7M so this is never a Secure UsageFault.
*/
bool ignore_stackfaults;
assert(!arm_feature(env, ARM_FEATURE_V8));
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, false);
env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_INVPC_MASK;
v7m_push_stack(cpu);
v7m_exception_taken(cpu, excret, false);
ignore_stackfaults = v7m_push_stack(cpu);
v7m_exception_taken(cpu, excret, false, ignore_stackfaults);
qemu_log_mask(CPU_LOG_INT, "...taking UsageFault on new stackframe: "
"failed exception return integrity check\n");
return;
@ -7114,6 +7480,7 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs)
ARMCPU *cpu = ARM_CPU(cs);
CPUARMState *env = &cpu->env;
uint32_t lr;
bool ignore_stackfaults;
arm_log_exception(cs->exception_index);
@ -7288,8 +7655,8 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs)
lr |= R_V7M_EXCRET_MODE_MASK;
}
v7m_push_stack(cpu);
v7m_exception_taken(cpu, lr, false);
ignore_stackfaults = v7m_push_stack(cpu);
v7m_exception_taken(cpu, lr, false, ignore_stackfaults);
qemu_log_mask(CPU_LOG_INT, "... as %d\n", env->v7m.exception);
}
@ -11692,14 +12059,37 @@ void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc,
target_ulong *cs_base, uint32_t *pflags)
{
ARMMMUIdx mmu_idx = core_to_arm_mmu_idx(env, cpu_mmu_index(env, false));
int fp_el = fp_exception_el(env);
uint32_t flags;
if (is_a64(env)) {
int sve_el = sve_exception_el(env);
uint32_t zcr_len;
*pc = env->pc;
flags = ARM_TBFLAG_AARCH64_STATE_MASK;
/* Get control bits for tagged addresses */
flags |= (arm_regime_tbi0(env, mmu_idx) << ARM_TBFLAG_TBI0_SHIFT);
flags |= (arm_regime_tbi1(env, mmu_idx) << ARM_TBFLAG_TBI1_SHIFT);
flags |= sve_el << ARM_TBFLAG_SVEEXC_EL_SHIFT;
/* If SVE is disabled, but FP is enabled,
then the effective len is 0. */
if (sve_el != 0 && fp_el == 0) {
zcr_len = 0;
} else {
int current_el = arm_current_el(env);
zcr_len = env->vfp.zcr_el[current_el <= 1 ? 1 : current_el];
zcr_len &= 0xf;
if (current_el < 2 && arm_feature(env, ARM_FEATURE_EL2)) {
zcr_len = MIN(zcr_len, 0xf & (uint32_t)env->vfp.zcr_el[2]);
}
if (current_el < 3 && arm_feature(env, ARM_FEATURE_EL3)) {
zcr_len = MIN(zcr_len, 0xf & (uint32_t)env->vfp.zcr_el[3]);
}
}
flags |= zcr_len << ARM_TBFLAG_ZCR_LEN_SHIFT;
} else {
*pc = env->regs[15];
flags = (env->thumb << ARM_TBFLAG_THUMB_SHIFT)
@ -11742,7 +12132,7 @@ void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc,
if (arm_cpu_data_is_big_endian(env)) {
flags |= ARM_TBFLAG_BE_DATA_MASK;
}
flags |= fp_exception_el(env) << ARM_TBFLAG_FPEXC_EL_SHIFT;
flags |= fp_el << ARM_TBFLAG_FPEXC_EL_SHIFT;
if (arm_v7m_is_handler_mode(env)) {
flags |= ARM_TBFLAG_HANDLER_MASK;

View file

@ -534,6 +534,18 @@ DEF_HELPER_FLAGS_3(crypto_sha256h2, TCG_CALL_NO_RWG, void, ptr, ptr, ptr)
DEF_HELPER_FLAGS_2(crypto_sha256su0, TCG_CALL_NO_RWG, void, ptr, ptr)
DEF_HELPER_FLAGS_3(crypto_sha256su1, TCG_CALL_NO_RWG, void, ptr, ptr, ptr)
DEF_HELPER_FLAGS_3(crypto_sha512h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr)
DEF_HELPER_FLAGS_3(crypto_sha512h2, TCG_CALL_NO_RWG, void, ptr, ptr, ptr)
DEF_HELPER_FLAGS_2(crypto_sha512su0, TCG_CALL_NO_RWG, void, ptr, ptr)
DEF_HELPER_FLAGS_3(crypto_sha512su1, TCG_CALL_NO_RWG, void, ptr, ptr, ptr)
DEF_HELPER_FLAGS_5(crypto_sm3tt, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32, i32)
DEF_HELPER_FLAGS_3(crypto_sm3partw1, TCG_CALL_NO_RWG, void, ptr, ptr, ptr)
DEF_HELPER_FLAGS_3(crypto_sm3partw2, TCG_CALL_NO_RWG, void, ptr, ptr, ptr)
DEF_HELPER_FLAGS_2(crypto_sm4e, TCG_CALL_NO_RWG, void, ptr, ptr)
DEF_HELPER_FLAGS_3(crypto_sm4ekey, TCG_CALL_NO_RWG, void, ptr, ptr, ptr)
DEF_HELPER_FLAGS_3(crc32, TCG_CALL_NO_RWG_SE, i32, i32, i32, i32)
DEF_HELPER_FLAGS_3(crc32c, TCG_CALL_NO_RWG_SE, i32, i32, i32, i32)
DEF_HELPER_2(dc_zva, void, env, i64)

View file

@ -234,6 +234,10 @@ static inline const char *gicv3_class_name(void)
exit(1);
#endif
} else {
if (kvm_enabled()) {
error_report("Userspace GICv3 is not supported with KVM");
exit(1);
}
return "arm-gicv3";
}
}

View file

@ -50,7 +50,40 @@ static const VMStateDescription vmstate_vfp = {
.minimum_version_id = 3,
.needed = vfp_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT64_ARRAY(env.vfp.regs, ARMCPU, 64),
/* For compatibility, store Qn out of Zn here. */
VMSTATE_UINT64_SUB_ARRAY(env.vfp.zregs[0].d, ARMCPU, 0, 2),
VMSTATE_UINT64_SUB_ARRAY(env.vfp.zregs[1].d, ARMCPU, 0, 2),
VMSTATE_UINT64_SUB_ARRAY(env.vfp.zregs[2].d, ARMCPU, 0, 2),
VMSTATE_UINT64_SUB_ARRAY(env.vfp.zregs[3].d, ARMCPU, 0, 2),
VMSTATE_UINT64_SUB_ARRAY(env.vfp.zregs[4].d, ARMCPU, 0, 2),
VMSTATE_UINT64_SUB_ARRAY(env.vfp.zregs[5].d, ARMCPU, 0, 2),
VMSTATE_UINT64_SUB_ARRAY(env.vfp.zregs[6].d, ARMCPU, 0, 2),
VMSTATE_UINT64_SUB_ARRAY(env.vfp.zregs[7].d, ARMCPU, 0, 2),
VMSTATE_UINT64_SUB_ARRAY(env.vfp.zregs[8].d, ARMCPU, 0, 2),
VMSTATE_UINT64_SUB_ARRAY(env.vfp.zregs[9].d, ARMCPU, 0, 2),
VMSTATE_UINT64_SUB_ARRAY(env.vfp.zregs[10].d, ARMCPU, 0, 2),
VMSTATE_UINT64_SUB_ARRAY(env.vfp.zregs[11].d, ARMCPU, 0, 2),
VMSTATE_UINT64_SUB_ARRAY(env.vfp.zregs[12].d, ARMCPU, 0, 2),
VMSTATE_UINT64_SUB_ARRAY(env.vfp.zregs[13].d, ARMCPU, 0, 2),
VMSTATE_UINT64_SUB_ARRAY(env.vfp.zregs[14].d, ARMCPU, 0, 2),
VMSTATE_UINT64_SUB_ARRAY(env.vfp.zregs[15].d, ARMCPU, 0, 2),
VMSTATE_UINT64_SUB_ARRAY(env.vfp.zregs[16].d, ARMCPU, 0, 2),
VMSTATE_UINT64_SUB_ARRAY(env.vfp.zregs[17].d, ARMCPU, 0, 2),
VMSTATE_UINT64_SUB_ARRAY(env.vfp.zregs[18].d, ARMCPU, 0, 2),
VMSTATE_UINT64_SUB_ARRAY(env.vfp.zregs[19].d, ARMCPU, 0, 2),
VMSTATE_UINT64_SUB_ARRAY(env.vfp.zregs[20].d, ARMCPU, 0, 2),
VMSTATE_UINT64_SUB_ARRAY(env.vfp.zregs[21].d, ARMCPU, 0, 2),
VMSTATE_UINT64_SUB_ARRAY(env.vfp.zregs[22].d, ARMCPU, 0, 2),
VMSTATE_UINT64_SUB_ARRAY(env.vfp.zregs[23].d, ARMCPU, 0, 2),
VMSTATE_UINT64_SUB_ARRAY(env.vfp.zregs[24].d, ARMCPU, 0, 2),
VMSTATE_UINT64_SUB_ARRAY(env.vfp.zregs[25].d, ARMCPU, 0, 2),
VMSTATE_UINT64_SUB_ARRAY(env.vfp.zregs[26].d, ARMCPU, 0, 2),
VMSTATE_UINT64_SUB_ARRAY(env.vfp.zregs[27].d, ARMCPU, 0, 2),
VMSTATE_UINT64_SUB_ARRAY(env.vfp.zregs[28].d, ARMCPU, 0, 2),
VMSTATE_UINT64_SUB_ARRAY(env.vfp.zregs[29].d, ARMCPU, 0, 2),
VMSTATE_UINT64_SUB_ARRAY(env.vfp.zregs[30].d, ARMCPU, 0, 2),
VMSTATE_UINT64_SUB_ARRAY(env.vfp.zregs[31].d, ARMCPU, 0, 2),
/* The xregs array is a little awkward because element 1 (FPSCR)
* requires a specific accessor, so we have to split it up in
* the vmstate:
@ -89,6 +122,56 @@ static const VMStateDescription vmstate_iwmmxt = {
}
};
#ifdef TARGET_AARCH64
/* The expression ARM_MAX_VQ - 2 is 0 for pure AArch32 build,
* and ARMPredicateReg is actively empty. This triggers errors
* in the expansion of the VMSTATE macros.
*/
static bool sve_needed(void *opaque)
{
ARMCPU *cpu = opaque;
CPUARMState *env = &cpu->env;
return arm_feature(env, ARM_FEATURE_SVE);
}
/* The first two words of each Zreg is stored in VFP state. */
static const VMStateDescription vmstate_zreg_hi_reg = {
.name = "cpu/sve/zreg_hi",
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT64_SUB_ARRAY(d, ARMVectorReg, 2, ARM_MAX_VQ - 2),
VMSTATE_END_OF_LIST()
}
};
static const VMStateDescription vmstate_preg_reg = {
.name = "cpu/sve/preg",
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT64_ARRAY(p, ARMPredicateReg, 2 * ARM_MAX_VQ / 8),
VMSTATE_END_OF_LIST()
}
};
static const VMStateDescription vmstate_sve = {
.name = "cpu/sve",
.version_id = 1,
.minimum_version_id = 1,
.needed = sve_needed,
.fields = (VMStateField[]) {
VMSTATE_STRUCT_ARRAY(env.vfp.zregs, ARMCPU, 32, 0,
vmstate_zreg_hi_reg, ARMVectorReg),
VMSTATE_STRUCT_ARRAY(env.vfp.pregs, ARMCPU, 17, 0,
vmstate_preg_reg, ARMPredicateReg),
VMSTATE_END_OF_LIST()
}
};
#endif /* AARCH64 */
static bool m_needed(void *opaque)
{
ARMCPU *cpu = opaque;
@ -553,6 +636,9 @@ const VMStateDescription vmstate_arm_cpu = {
&vmstate_pmsav7,
&vmstate_pmsav8,
&vmstate_m_security,
#ifdef TARGET_AARCH64
&vmstate_sve,
#endif
NULL
}
};

View file

@ -525,8 +525,8 @@ static inline int vec_reg_offset(DisasContext *s, int regno,
{
int offs = 0;
#ifdef HOST_WORDS_BIGENDIAN
/* This is complicated slightly because vfp.regs[2n] is
* still the low half and vfp.regs[2n+1] the high half
/* This is complicated slightly because vfp.zregs[n].d[0] is
* still the low half and vfp.zregs[n].d[1] the high half
* of the 128 bit vector, even on big endian systems.
* Calculate the offset assuming a fully bigendian 128 bits,
* then XOR to account for the order of the two 64 bit halves.
@ -536,7 +536,7 @@ static inline int vec_reg_offset(DisasContext *s, int regno,
#else
offs += element * (1 << size);
#endif
offs += offsetof(CPUARMState, vfp.regs[regno * 2]);
offs += offsetof(CPUARMState, vfp.zregs[regno]);
assert_fp_access_checked(s);
return offs;
}
@ -545,7 +545,7 @@ static inline int vec_reg_offset(DisasContext *s, int regno,
static inline int vec_full_reg_offset(DisasContext *s, int regno)
{
assert_fp_access_checked(s);
return offsetof(CPUARMState, vfp.regs[regno * 2]);
return offsetof(CPUARMState, vfp.zregs[regno]);
}
/* Return a newly allocated pointer to the vector register. */
@ -11587,6 +11587,341 @@ static void disas_crypto_two_reg_sha(DisasContext *s, uint32_t insn)
tcg_temp_free_ptr(tcg_rn_ptr);
}
/* Crypto three-reg SHA512
* 31 21 20 16 15 14 13 12 11 10 9 5 4 0
* +-----------------------+------+---+---+-----+--------+------+------+
* | 1 1 0 0 1 1 1 0 0 1 1 | Rm | 1 | O | 0 0 | opcode | Rn | Rd |
* +-----------------------+------+---+---+-----+--------+------+------+
*/
static void disas_crypto_three_reg_sha512(DisasContext *s, uint32_t insn)
{
int opcode = extract32(insn, 10, 2);
int o = extract32(insn, 14, 1);
int rm = extract32(insn, 16, 5);
int rn = extract32(insn, 5, 5);
int rd = extract32(insn, 0, 5);
int feature;
CryptoThreeOpFn *genfn;
if (o == 0) {
switch (opcode) {
case 0: /* SHA512H */
feature = ARM_FEATURE_V8_SHA512;
genfn = gen_helper_crypto_sha512h;
break;
case 1: /* SHA512H2 */
feature = ARM_FEATURE_V8_SHA512;
genfn = gen_helper_crypto_sha512h2;
break;
case 2: /* SHA512SU1 */
feature = ARM_FEATURE_V8_SHA512;
genfn = gen_helper_crypto_sha512su1;
break;
case 3: /* RAX1 */
feature = ARM_FEATURE_V8_SHA3;
genfn = NULL;
break;
}
} else {
switch (opcode) {
case 0: /* SM3PARTW1 */
feature = ARM_FEATURE_V8_SM3;
genfn = gen_helper_crypto_sm3partw1;
break;
case 1: /* SM3PARTW2 */
feature = ARM_FEATURE_V8_SM3;
genfn = gen_helper_crypto_sm3partw2;
break;
case 2: /* SM4EKEY */
feature = ARM_FEATURE_V8_SM4;
genfn = gen_helper_crypto_sm4ekey;
break;
default:
unallocated_encoding(s);
return;
}
}
if (!arm_dc_feature(s, feature)) {
unallocated_encoding(s);
return;
}
if (!fp_access_check(s)) {
return;
}
if (genfn) {
TCGv_ptr tcg_rd_ptr, tcg_rn_ptr, tcg_rm_ptr;
tcg_rd_ptr = vec_full_reg_ptr(s, rd);
tcg_rn_ptr = vec_full_reg_ptr(s, rn);
tcg_rm_ptr = vec_full_reg_ptr(s, rm);
genfn(tcg_rd_ptr, tcg_rn_ptr, tcg_rm_ptr);
tcg_temp_free_ptr(tcg_rd_ptr);
tcg_temp_free_ptr(tcg_rn_ptr);
tcg_temp_free_ptr(tcg_rm_ptr);
} else {
TCGv_i64 tcg_op1, tcg_op2, tcg_res[2];
int pass;
tcg_op1 = tcg_temp_new_i64();
tcg_op2 = tcg_temp_new_i64();
tcg_res[0] = tcg_temp_new_i64();
tcg_res[1] = tcg_temp_new_i64();
for (pass = 0; pass < 2; pass++) {
read_vec_element(s, tcg_op1, rn, pass, MO_64);
read_vec_element(s, tcg_op2, rm, pass, MO_64);
tcg_gen_rotli_i64(tcg_res[pass], tcg_op2, 1);
tcg_gen_xor_i64(tcg_res[pass], tcg_res[pass], tcg_op1);
}
write_vec_element(s, tcg_res[0], rd, 0, MO_64);
write_vec_element(s, tcg_res[1], rd, 1, MO_64);
tcg_temp_free_i64(tcg_op1);
tcg_temp_free_i64(tcg_op2);
tcg_temp_free_i64(tcg_res[0]);
tcg_temp_free_i64(tcg_res[1]);
}
}
/* Crypto two-reg SHA512
* 31 12 11 10 9 5 4 0
* +-----------------------------------------+--------+------+------+
* | 1 1 0 0 1 1 1 0 1 1 0 0 0 0 0 0 1 0 0 0 | opcode | Rn | Rd |
* +-----------------------------------------+--------+------+------+
*/
static void disas_crypto_two_reg_sha512(DisasContext *s, uint32_t insn)
{
int opcode = extract32(insn, 10, 2);
int rn = extract32(insn, 5, 5);
int rd = extract32(insn, 0, 5);
TCGv_ptr tcg_rd_ptr, tcg_rn_ptr;
int feature;
CryptoTwoOpFn *genfn;
switch (opcode) {
case 0: /* SHA512SU0 */
feature = ARM_FEATURE_V8_SHA512;
genfn = gen_helper_crypto_sha512su0;
break;
case 1: /* SM4E */
feature = ARM_FEATURE_V8_SM4;
genfn = gen_helper_crypto_sm4e;
break;
default:
unallocated_encoding(s);
return;
}
if (!arm_dc_feature(s, feature)) {
unallocated_encoding(s);
return;
}
if (!fp_access_check(s)) {
return;
}
tcg_rd_ptr = vec_full_reg_ptr(s, rd);
tcg_rn_ptr = vec_full_reg_ptr(s, rn);
genfn(tcg_rd_ptr, tcg_rn_ptr);
tcg_temp_free_ptr(tcg_rd_ptr);
tcg_temp_free_ptr(tcg_rn_ptr);
}
/* Crypto four-register
* 31 23 22 21 20 16 15 14 10 9 5 4 0
* +-------------------+-----+------+---+------+------+------+
* | 1 1 0 0 1 1 1 0 0 | Op0 | Rm | 0 | Ra | Rn | Rd |
* +-------------------+-----+------+---+------+------+------+
*/
static void disas_crypto_four_reg(DisasContext *s, uint32_t insn)
{
int op0 = extract32(insn, 21, 2);
int rm = extract32(insn, 16, 5);
int ra = extract32(insn, 10, 5);
int rn = extract32(insn, 5, 5);
int rd = extract32(insn, 0, 5);
int feature;
switch (op0) {
case 0: /* EOR3 */
case 1: /* BCAX */
feature = ARM_FEATURE_V8_SHA3;
break;
case 2: /* SM3SS1 */
feature = ARM_FEATURE_V8_SM3;
break;
default:
unallocated_encoding(s);
return;
}
if (!arm_dc_feature(s, feature)) {
unallocated_encoding(s);
return;
}
if (!fp_access_check(s)) {
return;
}
if (op0 < 2) {
TCGv_i64 tcg_op1, tcg_op2, tcg_op3, tcg_res[2];
int pass;
tcg_op1 = tcg_temp_new_i64();
tcg_op2 = tcg_temp_new_i64();
tcg_op3 = tcg_temp_new_i64();
tcg_res[0] = tcg_temp_new_i64();
tcg_res[1] = tcg_temp_new_i64();
for (pass = 0; pass < 2; pass++) {
read_vec_element(s, tcg_op1, rn, pass, MO_64);
read_vec_element(s, tcg_op2, rm, pass, MO_64);
read_vec_element(s, tcg_op3, ra, pass, MO_64);
if (op0 == 0) {
/* EOR3 */
tcg_gen_xor_i64(tcg_res[pass], tcg_op2, tcg_op3);
} else {
/* BCAX */
tcg_gen_andc_i64(tcg_res[pass], tcg_op2, tcg_op3);
}
tcg_gen_xor_i64(tcg_res[pass], tcg_res[pass], tcg_op1);
}
write_vec_element(s, tcg_res[0], rd, 0, MO_64);
write_vec_element(s, tcg_res[1], rd, 1, MO_64);
tcg_temp_free_i64(tcg_op1);
tcg_temp_free_i64(tcg_op2);
tcg_temp_free_i64(tcg_op3);
tcg_temp_free_i64(tcg_res[0]);
tcg_temp_free_i64(tcg_res[1]);
} else {
TCGv_i32 tcg_op1, tcg_op2, tcg_op3, tcg_res, tcg_zero;
tcg_op1 = tcg_temp_new_i32();
tcg_op2 = tcg_temp_new_i32();
tcg_op3 = tcg_temp_new_i32();
tcg_res = tcg_temp_new_i32();
tcg_zero = tcg_const_i32(0);
read_vec_element_i32(s, tcg_op1, rn, 3, MO_32);
read_vec_element_i32(s, tcg_op2, rm, 3, MO_32);
read_vec_element_i32(s, tcg_op3, ra, 3, MO_32);
tcg_gen_rotri_i32(tcg_res, tcg_op1, 20);
tcg_gen_add_i32(tcg_res, tcg_res, tcg_op2);
tcg_gen_add_i32(tcg_res, tcg_res, tcg_op3);
tcg_gen_rotri_i32(tcg_res, tcg_res, 25);
write_vec_element_i32(s, tcg_zero, rd, 0, MO_32);
write_vec_element_i32(s, tcg_zero, rd, 1, MO_32);
write_vec_element_i32(s, tcg_zero, rd, 2, MO_32);
write_vec_element_i32(s, tcg_res, rd, 3, MO_32);
tcg_temp_free_i32(tcg_op1);
tcg_temp_free_i32(tcg_op2);
tcg_temp_free_i32(tcg_op3);
tcg_temp_free_i32(tcg_res);
tcg_temp_free_i32(tcg_zero);
}
}
/* Crypto XAR
* 31 21 20 16 15 10 9 5 4 0
* +-----------------------+------+--------+------+------+
* | 1 1 0 0 1 1 1 0 1 0 0 | Rm | imm6 | Rn | Rd |
* +-----------------------+------+--------+------+------+
*/
static void disas_crypto_xar(DisasContext *s, uint32_t insn)
{
int rm = extract32(insn, 16, 5);
int imm6 = extract32(insn, 10, 6);
int rn = extract32(insn, 5, 5);
int rd = extract32(insn, 0, 5);
TCGv_i64 tcg_op1, tcg_op2, tcg_res[2];
int pass;
if (!arm_dc_feature(s, ARM_FEATURE_V8_SHA3)) {
unallocated_encoding(s);
return;
}
if (!fp_access_check(s)) {
return;
}
tcg_op1 = tcg_temp_new_i64();
tcg_op2 = tcg_temp_new_i64();
tcg_res[0] = tcg_temp_new_i64();
tcg_res[1] = tcg_temp_new_i64();
for (pass = 0; pass < 2; pass++) {
read_vec_element(s, tcg_op1, rn, pass, MO_64);
read_vec_element(s, tcg_op2, rm, pass, MO_64);
tcg_gen_xor_i64(tcg_res[pass], tcg_op1, tcg_op2);
tcg_gen_rotri_i64(tcg_res[pass], tcg_res[pass], imm6);
}
write_vec_element(s, tcg_res[0], rd, 0, MO_64);
write_vec_element(s, tcg_res[1], rd, 1, MO_64);
tcg_temp_free_i64(tcg_op1);
tcg_temp_free_i64(tcg_op2);
tcg_temp_free_i64(tcg_res[0]);
tcg_temp_free_i64(tcg_res[1]);
}
/* Crypto three-reg imm2
* 31 21 20 16 15 14 13 12 11 10 9 5 4 0
* +-----------------------+------+-----+------+--------+------+------+
* | 1 1 0 0 1 1 1 0 0 1 0 | Rm | 1 0 | imm2 | opcode | Rn | Rd |
* +-----------------------+------+-----+------+--------+------+------+
*/
static void disas_crypto_three_reg_imm2(DisasContext *s, uint32_t insn)
{
int opcode = extract32(insn, 10, 2);
int imm2 = extract32(insn, 12, 2);
int rm = extract32(insn, 16, 5);
int rn = extract32(insn, 5, 5);
int rd = extract32(insn, 0, 5);
TCGv_ptr tcg_rd_ptr, tcg_rn_ptr, tcg_rm_ptr;
TCGv_i32 tcg_imm2, tcg_opcode;
if (!arm_dc_feature(s, ARM_FEATURE_V8_SM3)) {
unallocated_encoding(s);
return;
}
if (!fp_access_check(s)) {
return;
}
tcg_rd_ptr = vec_full_reg_ptr(s, rd);
tcg_rn_ptr = vec_full_reg_ptr(s, rn);
tcg_rm_ptr = vec_full_reg_ptr(s, rm);
tcg_imm2 = tcg_const_i32(imm2);
tcg_opcode = tcg_const_i32(opcode);
gen_helper_crypto_sm3tt(tcg_rd_ptr, tcg_rn_ptr, tcg_rm_ptr, tcg_imm2,
tcg_opcode);
tcg_temp_free_ptr(tcg_rd_ptr);
tcg_temp_free_ptr(tcg_rn_ptr);
tcg_temp_free_ptr(tcg_rm_ptr);
tcg_temp_free_i32(tcg_imm2);
tcg_temp_free_i32(tcg_opcode);
}
/* C3.6 Data processing - SIMD, inc Crypto
*
* As the decode gets a little complex we are using a table based
@ -11616,6 +11951,11 @@ static const AArch64DecodeTable data_proc_simd[] = {
{ 0x4e280800, 0xff3e0c00, disas_crypto_aes },
{ 0x5e000000, 0xff208c00, disas_crypto_three_reg_sha },
{ 0x5e280800, 0xff3e0c00, disas_crypto_two_reg_sha },
{ 0xce608000, 0xffe0b000, disas_crypto_three_reg_sha512 },
{ 0xcec08000, 0xfffff000, disas_crypto_two_reg_sha512 },
{ 0xce000000, 0xff808000, disas_crypto_four_reg },
{ 0xce800000, 0xffe00000, disas_crypto_xar },
{ 0xce408000, 0xffe0c000, disas_crypto_three_reg_imm2 },
{ 0x00000000, 0x00000000, NULL }
};
@ -11718,6 +12058,8 @@ static int aarch64_tr_init_disas_context(DisasContextBase *dcbase,
dc->user = (dc->current_el == 0);
#endif
dc->fp_excp_el = ARM_TBFLAG_FPEXC_EL(dc->base.tb->flags);
dc->sve_excp_el = ARM_TBFLAG_SVEEXC_EL(dc->base.tb->flags);
dc->sve_len = (ARM_TBFLAG_ZCR_LEN(dc->base.tb->flags) + 1) * 16;
dc->vec_len = 0;
dc->vec_stride = 0;
dc->cp_regs = arm_cpu->cp_regs;

View file

@ -1512,13 +1512,12 @@ static inline void gen_vfp_st(DisasContext *s, int dp, TCGv_i32 addr)
}
}
static inline long
vfp_reg_offset (int dp, int reg)
static inline long vfp_reg_offset(bool dp, unsigned reg)
{
if (dp) {
return offsetof(CPUARMState, vfp.regs[reg]);
return offsetof(CPUARMState, vfp.zregs[reg >> 1].d[reg & 1]);
} else {
long ofs = offsetof(CPUARMState, vfp.regs[reg >> 1]);
long ofs = offsetof(CPUARMState, vfp.zregs[reg >> 2].d[(reg >> 1) & 1]);
if (reg & 1) {
ofs += offsetof(CPU_DoubleU, l.upper);
} else {
@ -9926,6 +9925,7 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn)
tcg_temp_free_i32(addr);
tcg_temp_free_i32(op);
store_reg(s, rd, ttresp);
break;
}
goto illegal_op;
}

View file

@ -29,6 +29,8 @@ typedef struct DisasContext {
bool tbi1; /* TBI1 for EL0/1, not used for EL2/3 */
bool ns; /* Use non-secure CPREG bank on access */
int fp_excp_el; /* FP exception EL or 0 if enabled */
int sve_excp_el; /* SVE exception EL or 0 if enabled */
int sve_len; /* SVE vector length in bytes */
/* Flag indicating that exceptions from secure mode are routed to EL3. */
bool secure_routed_to_el3;
bool vfp_enabled; /* FP enabled via FPSCR.EN */