diff --git a/MAINTAINERS b/MAINTAINERS index ba52b8c77b..1032406c56 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -442,8 +442,9 @@ ARM Machines ------------ Allwinner-a10 M: Beniamino Galvani +M: Peter Maydell L: qemu-arm@nongnu.org -S: Maintained +S: Odd Fixes F: hw/*/allwinner* F: include/hw/*/allwinner* F: hw/arm/cubieboard.c @@ -502,40 +503,46 @@ F: tests/test-arm-mptimer.c Exynos M: Igor Mitsyanko +M: Peter Maydell L: qemu-arm@nongnu.org -S: Maintained +S: Odd Fixes F: hw/*/exynos* F: include/hw/arm/exynos4210.h Calxeda Highbank M: Rob Herring +M: Peter Maydell L: qemu-arm@nongnu.org -S: Maintained +S: Odd Fixes F: hw/arm/highbank.c F: hw/net/xgmac.c Canon DIGIC M: Antony Pavlov +M: Peter Maydell L: qemu-arm@nongnu.org -S: Maintained +S: Odd Fixes F: include/hw/arm/digic.h F: hw/*/digic* Gumstix -M: Philippe Mathieu-Daudé +M: Peter Maydell +R: Philippe Mathieu-Daudé L: qemu-devel@nongnu.org L: qemu-arm@nongnu.org S: Odd Fixes F: hw/arm/gumstix.c -i.MX31 +i.MX31 (kzm) M: Peter Chubb +M: Peter Maydell L: qemu-arm@nongnu.org -S: Odd fixes -F: hw/*/imx* -F: include/hw/*/imx* +S: Odd Fixes F: hw/arm/kzm.c -F: include/hw/arm/fsl-imx31.h +F: hw/*/imx_* +F: hw/*/*imx31* +F: include/hw/*/imx_* +F: include/hw/*/*imx31* Integrator CP M: Peter Maydell @@ -544,6 +551,28 @@ S: Maintained F: hw/arm/integratorcp.c F: hw/misc/arm_integrator_debug.c +MCIMX6UL EVK / i.MX6ul +M: Peter Maydell +R: Jean-Christophe Dubois +L: qemu-arm@nongnu.org +S: Odd Fixes +F: hw/arm/mcimx6ul-evk.c +F: hw/arm/fsl-imx6ul.c +F: hw/misc/imx6ul_ccm.c +F: include/hw/arm/fsl-imx6ul.h +F: include/hw/misc/imx6ul_ccm.h + +MCIMX7D SABRE / i.MX7 +M: Peter Maydell +R: Andrey Smirnov +L: qemu-arm@nongnu.org +S: Odd Fixes +F: hw/arm/mcimx7d-sabre.c +F: hw/arm/fsl-imx7.c +F: include/hw/arm/fsl-imx7.h +F: hw/pci-host/designware.c +F: include/hw/pci-host/designware.h + MPS2 M: Peter Maydell L: qemu-arm@nongnu.org @@ -561,22 +590,36 @@ F: include/hw/misc/iotkit-sysinfo.h Musicpal M: Jan Kiszka +M: Peter Maydell L: qemu-arm@nongnu.org -S: Maintained +S: Odd Fixes F: hw/arm/musicpal.c nSeries M: Andrzej Zaborowski +M: Peter Maydell L: qemu-arm@nongnu.org -S: Maintained +S: Odd Fixes F: hw/arm/nseries.c Palm M: Andrzej Zaborowski +M: Peter Maydell L: qemu-arm@nongnu.org -S: Maintained +S: Odd Fixes F: hw/arm/palm.c +Raspberry Pi +M: Peter Maydell +R: Andrew Baumann +R: Philippe Mathieu-Daudé +L: qemu-arm@nongnu.org +S: Odd Fixes +F: hw/arm/raspi_platform.h +F: hw/*/bcm283* +F: include/hw/arm/raspi* +F: include/hw/*/bcm283* + Real View M: Peter Maydell L: qemu-arm@nongnu.org @@ -588,8 +631,9 @@ F: include/hw/intc/realview_gic.h PXA2XX M: Andrzej Zaborowski +M: Peter Maydell L: qemu-arm@nongnu.org -S: Maintained +S: Odd Fixes F: hw/arm/mainstone.c F: hw/arm/spitz.c F: hw/arm/tosa.c @@ -598,6 +642,19 @@ F: hw/*/pxa2xx* F: hw/misc/mst_fpga.c F: include/hw/arm/pxa.h +SABRELITE / i.MX6 +M: Peter Maydell +R: Jean-Christophe Dubois +L: qemu-arm@nongnu.org +S: Odd Fixes +F: hw/arm/sabrelite.c +F: hw/arm/fsl-imx6.c +F: hw/misc/imx6_src.c +F: hw/ssi/imx_spi.c +F: include/hw/arm/fsl-imx6.h +F: include/hw/misc/imx6_src.h +F: include/hw/ssi/imx_spi.h + Sharp SL-5500 (Collie) PDA M: Peter Maydell L: qemu-arm@nongnu.org @@ -611,6 +668,12 @@ L: qemu-arm@nongnu.org S: Maintained F: hw/*/stellaris* +Versatile Express +M: Peter Maydell +L: qemu-arm@nongnu.org +S: Maintained +F: hw/arm/vexpress.c + Versatile PB M: Peter Maydell L: qemu-arm@nongnu.org @@ -618,9 +681,17 @@ S: Maintained F: hw/*/versatile* F: hw/misc/arm_sysctl.c +Virt +M: Peter Maydell +L: qemu-arm@nongnu.org +S: Maintained +F: hw/arm/virt* +F: include/hw/arm/virt.h + Xilinx Zynq M: Edgar E. Iglesias M: Alistair Francis +M: Peter Maydell L: qemu-arm@nongnu.org S: Maintained F: hw/*/xilinx_* @@ -632,6 +703,7 @@ X: hw/ssi/xilinx_* Xilinx ZynqMP M: Alistair Francis M: Edgar E. Iglesias +M: Peter Maydell L: qemu-arm@nongnu.org S: Maintained F: hw/*/xlnx*.c @@ -645,6 +717,7 @@ F: hw/arm/virt-acpi-build.c STM32F205 M: Alistair Francis +M: Peter Maydell S: Maintained F: hw/arm/stm32f205_soc.c F: hw/misc/stm32f2xx_syscfg.c @@ -656,11 +729,13 @@ F: include/hw/*/stm32*.h Netduino 2 M: Alistair Francis +M: Peter Maydell S: Maintained F: hw/arm/netduino2.c SmartFusion2 M: Subbaraya Sundeep +M: Peter Maydell S: Maintained F: hw/arm/msf2-soc.c F: hw/misc/msf2-sysreg.c @@ -673,11 +748,13 @@ F: include/hw/ssi/mss-spi.h Emcraft M2S-FG484 M: Subbaraya Sundeep +M: Peter Maydell S: Maintained F: hw/arm/msf2-som.c ASPEED BMCs M: Cédric Le Goater +M: Peter Maydell R: Andrew Jeffery R: Joel Stanley L: qemu-arm@nongnu.org @@ -689,6 +766,7 @@ F: include/hw/net/ftgmac100.h NRF51 M: Joel Stanley +M: Peter Maydell L: qemu-arm@nongnu.org S: Maintained F: hw/arm/nrf51_soc.c diff --git a/hw/block/onenand.c b/hw/block/onenand.c index 0cb8d7fa13..2b48609776 100644 --- a/hw/block/onenand.c +++ b/hw/block/onenand.c @@ -28,6 +28,7 @@ #include "exec/memory.h" #include "hw/sysbus.h" #include "qemu/error-report.h" +#include "qemu/log.h" /* 11 for 2kB-page OneNAND ("2nd generation") and 10 for 1kB-page chips */ #define PAGE_SHIFT 11 @@ -594,8 +595,8 @@ static void onenand_command(OneNANDState *s) default: s->status |= ONEN_ERR_CMD; s->intstatus |= ONEN_INT; - fprintf(stderr, "%s: unknown OneNAND command %x\n", - __func__, s->command); + qemu_log_mask(LOG_GUEST_ERROR, "unknown OneNAND command %x\n", + s->command); } onenand_intr_update(s); @@ -608,7 +609,7 @@ static uint64_t onenand_read(void *opaque, hwaddr addr, int offset = addr >> s->shift; switch (offset) { - case 0x0000 ... 0xc000: + case 0x0000 ... 0xbffe: return lduw_le_p(s->boot[0] + addr); case 0xf000: /* Manufacturer ID */ @@ -657,12 +658,13 @@ static uint64_t onenand_read(void *opaque, hwaddr addr, case 0xff02: /* ECC Result of spare area data */ case 0xff03: /* ECC Result of main area data */ case 0xff04: /* ECC Result of spare area data */ - hw_error("%s: implement ECC\n", __func__); + qemu_log_mask(LOG_UNIMP, + "onenand: ECC result registers unimplemented\n"); return 0x0000; } - fprintf(stderr, "%s: unknown OneNAND register %x\n", - __func__, offset); + qemu_log_mask(LOG_GUEST_ERROR, "read of unknown OneNAND register 0x%x\n", + offset); return 0; } @@ -706,8 +708,9 @@ static void onenand_write(void *opaque, hwaddr addr, break; default: - fprintf(stderr, "%s: unknown OneNAND boot command %"PRIx64"\n", - __func__, value); + qemu_log_mask(LOG_GUEST_ERROR, + "unknown OneNAND boot command %" PRIx64 "\n", + value); } break; @@ -757,8 +760,9 @@ static void onenand_write(void *opaque, hwaddr addr, break; default: - fprintf(stderr, "%s: unknown OneNAND register %x\n", - __func__, offset); + qemu_log_mask(LOG_GUEST_ERROR, + "write to unknown OneNAND register 0x%x\n", + offset); } } diff --git a/hw/char/stm32f2xx_usart.c b/hw/char/stm32f2xx_usart.c index 032b5fda13..f3363a2952 100644 --- a/hw/char/stm32f2xx_usart.c +++ b/hw/char/stm32f2xx_usart.c @@ -202,7 +202,7 @@ static void stm32f2xx_usart_init(Object *obj) sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); memory_region_init_io(&s->mmio, obj, &stm32f2xx_usart_ops, s, - TYPE_STM32F2XX_USART, 0x2000); + TYPE_STM32F2XX_USART, 0x400); sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); } diff --git a/hw/timer/stm32f2xx_timer.c b/hw/timer/stm32f2xx_timer.c index 58fc7b1188..ae744d1642 100644 --- a/hw/timer/stm32f2xx_timer.c +++ b/hw/timer/stm32f2xx_timer.c @@ -308,7 +308,7 @@ static void stm32f2xx_timer_init(Object *obj) sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); memory_region_init_io(&s->iomem, obj, &stm32f2xx_timer_ops, s, - "stm32f2xx_timer", 0x4000); + "stm32f2xx_timer", 0x400); sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem); s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, stm32f2xx_timer_interrupt, s); diff --git a/target/arm/kvm.c b/target/arm/kvm.c index 09a86e2820..44dd0ce6ce 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -158,6 +158,7 @@ void kvm_arm_set_cpu_features_from_host(ARMCPU *cpu) cpu->kvm_target = arm_host_cpu_features.target; cpu->dtb_compatible = arm_host_cpu_features.dtb_compatible; + cpu->isar = arm_host_cpu_features.isar; env->features = arm_host_cpu_features.features; } diff --git a/target/arm/kvm32.c b/target/arm/kvm32.c index cb3fb73a96..bd51eb43c8 100644 --- a/target/arm/kvm32.c +++ b/target/arm/kvm32.c @@ -28,6 +28,14 @@ static inline void set_feature(uint64_t *features, int feature) *features |= 1ULL << feature; } +static int read_sys_reg32(int fd, uint32_t *pret, uint64_t id) +{ + struct kvm_one_reg idreg = { .id = id, .addr = (uintptr_t)pret }; + + assert((id & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U32); + return ioctl(fd, KVM_GET_ONE_REG, &idreg); +} + bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) { /* Identify the feature bits corresponding to the host CPU, and @@ -35,9 +43,10 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) * we have to create a scratch VM, create a single CPU inside it, * and then query that CPU for the relevant ID registers. */ - int i, ret, fdarray[3]; - uint32_t midr, id_pfr0, mvfr1; + int err = 0, fdarray[3]; + uint32_t midr, id_pfr0; uint64_t features = 0; + /* Old kernels may not know about the PREFERRED_TARGET ioctl: however * we know these will only support creating one kind of guest CPU, * which is its preferred CPU type. @@ -47,23 +56,6 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) QEMU_KVM_ARM_TARGET_NONE }; struct kvm_vcpu_init init; - struct kvm_one_reg idregs[] = { - { - .id = KVM_REG_ARM | KVM_REG_SIZE_U32 - | ENCODE_CP_REG(15, 0, 0, 0, 0, 0, 0), - .addr = (uintptr_t)&midr, - }, - { - .id = KVM_REG_ARM | KVM_REG_SIZE_U32 - | ENCODE_CP_REG(15, 0, 0, 0, 1, 0, 0), - .addr = (uintptr_t)&id_pfr0, - }, - { - .id = KVM_REG_ARM | KVM_REG_SIZE_U32 - | KVM_REG_ARM_VFP | KVM_REG_ARM_VFP_MVFR1, - .addr = (uintptr_t)&mvfr1, - }, - }; if (!kvm_arm_create_scratch_host_vcpu(cpus_to_try, fdarray, &init)) { return false; @@ -77,16 +69,45 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) */ ahcf->dtb_compatible = "arm,arm-v7"; - for (i = 0; i < ARRAY_SIZE(idregs); i++) { - ret = ioctl(fdarray[2], KVM_GET_ONE_REG, &idregs[i]); - if (ret) { - break; - } + err |= read_sys_reg32(fdarray[2], &midr, ARM_CP15_REG32(0, 0, 0, 0)); + err |= read_sys_reg32(fdarray[2], &id_pfr0, ARM_CP15_REG32(0, 0, 1, 0)); + + err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar0, + ARM_CP15_REG32(0, 0, 2, 0)); + err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar1, + ARM_CP15_REG32(0, 0, 2, 1)); + err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar2, + ARM_CP15_REG32(0, 0, 2, 2)); + err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar3, + ARM_CP15_REG32(0, 0, 2, 3)); + err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar4, + ARM_CP15_REG32(0, 0, 2, 4)); + err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar5, + ARM_CP15_REG32(0, 0, 2, 5)); + if (read_sys_reg32(fdarray[2], &ahcf->isar.id_isar6, + ARM_CP15_REG32(0, 0, 2, 7))) { + /* + * Older kernels don't support reading ID_ISAR6. This register was + * only introduced in ARMv8, so we can assume that it is zero on a + * CPU that a kernel this old is running on. + */ + ahcf->isar.id_isar6 = 0; } + err |= read_sys_reg32(fdarray[2], &ahcf->isar.mvfr0, + KVM_REG_ARM | KVM_REG_SIZE_U32 | + KVM_REG_ARM_VFP | KVM_REG_ARM_VFP_MVFR0); + err |= read_sys_reg32(fdarray[2], &ahcf->isar.mvfr1, + KVM_REG_ARM | KVM_REG_SIZE_U32 | + KVM_REG_ARM_VFP | KVM_REG_ARM_VFP_MVFR1); + /* + * FIXME: There is not yet a way to read MVFR2. + * Fortunately there is not yet anything in there that affects migration. + */ + kvm_arm_destroy_scratch_host_vcpu(fdarray); - if (ret) { + if (err < 0) { return false; } @@ -104,13 +125,13 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) if (extract32(id_pfr0, 12, 4) == 1) { set_feature(&features, ARM_FEATURE_THUMB2EE); } - if (extract32(mvfr1, 20, 4) == 1) { + if (extract32(ahcf->isar.mvfr1, 20, 4) == 1) { set_feature(&features, ARM_FEATURE_VFP_FP16); } - if (extract32(mvfr1, 12, 4) == 1) { + if (extract32(ahcf->isar.mvfr1, 12, 4) == 1) { set_feature(&features, ARM_FEATURE_NEON); } - if (extract32(mvfr1, 28, 4) == 1) { + if (extract32(ahcf->isar.mvfr1, 28, 4) == 1) { /* FMAC support implies VFPv4 */ set_feature(&features, ARM_FEATURE_VFP4); } diff --git a/target/arm/kvm64.c b/target/arm/kvm64.c index 46fbe6d8ff..0a502091e7 100644 --- a/target/arm/kvm64.c +++ b/target/arm/kvm64.c @@ -456,17 +456,40 @@ static inline void unset_feature(uint64_t *features, int feature) *features &= ~(1ULL << feature); } +static int read_sys_reg32(int fd, uint32_t *pret, uint64_t id) +{ + uint64_t ret; + struct kvm_one_reg idreg = { .id = id, .addr = (uintptr_t)&ret }; + int err; + + assert((id & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U64); + err = ioctl(fd, KVM_GET_ONE_REG, &idreg); + if (err < 0) { + return -1; + } + *pret = ret; + return 0; +} + +static int read_sys_reg64(int fd, uint64_t *pret, uint64_t id) +{ + struct kvm_one_reg idreg = { .id = id, .addr = (uintptr_t)pret }; + + assert((id & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U64); + return ioctl(fd, KVM_GET_ONE_REG, &idreg); +} + bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) { /* Identify the feature bits corresponding to the host CPU, and * fill out the ARMHostCPUClass fields accordingly. To do this * we have to create a scratch VM, create a single CPU inside it, * and then query that CPU for the relevant ID registers. - * For AArch64 we currently don't care about ID registers at - * all; we just want to know the CPU type. */ int fdarray[3]; uint64_t features = 0; + int err; + /* Old kernels may not know about the PREFERRED_TARGET ioctl: however * we know these will only support creating one kind of guest CPU, * which is its preferred CPU type. Fortunately these old kernels @@ -487,8 +510,71 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) ahcf->target = init.target; ahcf->dtb_compatible = "arm,arm-v8"; + err = read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64pfr0, + ARM64_SYS_REG(3, 0, 0, 4, 0)); + if (unlikely(err < 0)) { + /* + * Before v4.15, the kernel only exposed a limited number of system + * registers, not including any of the interesting AArch64 ID regs. + * For the most part we could leave these fields as zero with minimal + * effect, since this does not affect the values seen by the guest. + * + * However, it could cause problems down the line for QEMU, + * so provide a minimal v8.0 default. + * + * ??? Could read MIDR and use knowledge from cpu64.c. + * ??? Could map a page of memory into our temp guest and + * run the tiniest of hand-crafted kernels to extract + * the values seen by the guest. + * ??? Either of these sounds like too much effort just + * to work around running a modern host kernel. + */ + ahcf->isar.id_aa64pfr0 = 0x00000011; /* EL1&0, AArch64 only */ + err = 0; + } else { + err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64pfr1, + ARM64_SYS_REG(3, 0, 0, 4, 1)); + err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64isar0, + ARM64_SYS_REG(3, 0, 0, 6, 0)); + err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64isar1, + ARM64_SYS_REG(3, 0, 0, 6, 1)); + + /* + * Note that if AArch32 support is not present in the host, + * the AArch32 sysregs are present to be read, but will + * return UNKNOWN values. This is neither better nor worse + * than skipping the reads and leaving 0, as we must avoid + * considering the values in every case. + */ + err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar0, + ARM64_SYS_REG(3, 0, 0, 2, 0)); + err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar1, + ARM64_SYS_REG(3, 0, 0, 2, 1)); + err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar2, + ARM64_SYS_REG(3, 0, 0, 2, 2)); + err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar3, + ARM64_SYS_REG(3, 0, 0, 2, 3)); + err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar4, + ARM64_SYS_REG(3, 0, 0, 2, 4)); + err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar5, + ARM64_SYS_REG(3, 0, 0, 2, 5)); + err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar6, + ARM64_SYS_REG(3, 0, 0, 2, 7)); + + err |= read_sys_reg32(fdarray[2], &ahcf->isar.mvfr0, + ARM64_SYS_REG(3, 0, 0, 3, 0)); + err |= read_sys_reg32(fdarray[2], &ahcf->isar.mvfr1, + ARM64_SYS_REG(3, 0, 0, 3, 1)); + err |= read_sys_reg32(fdarray[2], &ahcf->isar.mvfr2, + ARM64_SYS_REG(3, 0, 0, 3, 2)); + } + kvm_arm_destroy_scratch_host_vcpu(fdarray); + if (err < 0) { + return false; + } + /* We can assume any KVM supporting CPU is at least a v8 * with VFPv4+Neon; this in turn implies most of the other * feature bits. diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h index 21c0129da2..6393455b1d 100644 --- a/target/arm/kvm_arm.h +++ b/target/arm/kvm_arm.h @@ -183,6 +183,7 @@ void kvm_arm_destroy_scratch_host_vcpu(int *fdarray); * by asking the host kernel) */ typedef struct ARMHostCPUFeatures { + ARMISARegisters isar; uint64_t features; uint32_t target; const char *dtb_compatible; diff --git a/target/arm/op_helper.c b/target/arm/op_helper.c index eb6fb82fb8..0d6e89e474 100644 --- a/target/arm/op_helper.c +++ b/target/arm/op_helper.c @@ -939,7 +939,38 @@ void HELPER(pre_smc)(CPUARMState *env, uint32_t syndrome) ARMCPU *cpu = arm_env_get_cpu(env); int cur_el = arm_current_el(env); bool secure = arm_is_secure(env); - bool smd = env->cp15.scr_el3 & SCR_SMD; + bool smd_flag = env->cp15.scr_el3 & SCR_SMD; + + /* + * SMC behaviour is summarized in the following table. + * This helper handles the "Trap to EL2" and "Undef insn" cases. + * The "Trap to EL3" and "PSCI call" cases are handled in the exception + * helper. + * + * -> ARM_FEATURE_EL3 and !SMD + * HCR_TSC && NS EL1 !HCR_TSC || !NS EL1 + * + * Conduit SMC, valid call Trap to EL2 PSCI Call + * Conduit SMC, inval call Trap to EL2 Trap to EL3 + * Conduit not SMC Trap to EL2 Trap to EL3 + * + * + * -> ARM_FEATURE_EL3 and SMD + * HCR_TSC && NS EL1 !HCR_TSC || !NS EL1 + * + * Conduit SMC, valid call Trap to EL2 PSCI Call + * Conduit SMC, inval call Trap to EL2 Undef insn + * Conduit not SMC Trap to EL2 Undef insn + * + * + * -> !ARM_FEATURE_EL3 + * HCR_TSC && NS EL1 !HCR_TSC || !NS EL1 + * + * Conduit SMC, valid call Trap to EL2 PSCI Call + * Conduit SMC, inval call Trap to EL2 Undef insn + * Conduit not SMC Undef insn Undef insn + */ + /* On ARMv8 with EL3 AArch64, SMD applies to both S and NS state. * On ARMv8 with EL3 AArch32, or ARMv7 with the Virtualization * extensions, SMD only applies to NS state. @@ -947,7 +978,8 @@ void HELPER(pre_smc)(CPUARMState *env, uint32_t syndrome) * doesn't exist, but we forbid the guest to set it to 1 in scr_write(), * so we need not special case this here. */ - bool undef = arm_feature(env, ARM_FEATURE_AARCH64) ? smd : smd && !secure; + bool smd = arm_feature(env, ARM_FEATURE_AARCH64) ? smd_flag + : smd_flag && !secure; if (!arm_feature(env, ARM_FEATURE_EL3) && cpu->psci_conduit != QEMU_PSCI_CONDUIT_SMC) { @@ -957,21 +989,27 @@ void HELPER(pre_smc)(CPUARMState *env, uint32_t syndrome) * to forbid its EL1 from making PSCI calls into QEMU's * "firmware" via HCR.TSC, so for these purposes treat * PSCI-via-SMC as implying an EL3. + * This handles the very last line of the previous table. */ - undef = true; - } else if (!secure && cur_el == 1 && (env->cp15.hcr_el2 & HCR_TSC)) { + raise_exception(env, EXCP_UDEF, syn_uncategorized(), + exception_target_el(env)); + } + + if (!secure && cur_el == 1 && (env->cp15.hcr_el2 & HCR_TSC)) { /* In NS EL1, HCR controlled routing to EL2 has priority over SMD. * We also want an EL2 guest to be able to forbid its EL1 from * making PSCI calls into QEMU's "firmware" via HCR.TSC. + * This handles all the "Trap to EL2" cases of the previous table. */ raise_exception(env, EXCP_HYP_TRAP, syndrome, 2); } - /* If PSCI is enabled and this looks like a valid PSCI call then - * suppress the UNDEF -- we'll catch the SMC exception and - * implement the PSCI call behaviour there. + /* Catch the two remaining "Undef insn" cases of the previous table: + * - PSCI conduit is SMC but we don't have a valid PCSI call, + * - We don't have EL3 or SMD is set. */ - if (undef && !arm_is_psci_call(cpu, EXCP_SMC)) { + if (!arm_is_psci_call(cpu, EXCP_SMC) && + (smd || !arm_feature(env, ARM_FEATURE_EL3))) { raise_exception(env, EXCP_UDEF, syn_uncategorized(), exception_target_el(env)); }