diff options
Diffstat (limited to 'target')
-rw-r--r-- | target/arm/cpu.c | 14 | ||||
-rw-r--r-- | target/arm/cpu.h | 2 | ||||
-rw-r--r-- | target/arm/internals.h | 5 | ||||
-rw-r--r-- | target/arm/kvm.c | 75 | ||||
-rw-r--r-- | target/arm/kvm_arm.h | 35 | ||||
-rw-r--r-- | target/arm/tcg/helper-a64.c | 2 | ||||
-rw-r--r-- | target/arm/vfp_helper.c | 56 |
7 files changed, 120 insertions, 69 deletions
diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 1320fd8c8f..5b751439bd 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -2390,14 +2390,22 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) #ifndef CONFIG_USER_ONLY /* - * If we do not have tag-memory provided by the machine, - * reduce MTE support to instructions enabled at EL0. + * If we run with TCG and do not have tag-memory provided by + * the machine, then reduce MTE support to instructions enabled at EL0. * This matches Cortex-A710 BROADCASTMTE input being LOW. */ - if (cpu->tag_memory == NULL) { + if (tcg_enabled() && cpu->tag_memory == NULL) { cpu->isar.id_aa64pfr1 = FIELD_DP64(cpu->isar.id_aa64pfr1, ID_AA64PFR1, MTE, 1); } + + /* + * If MTE is supported by the host, however it should not be + * enabled on the guest (i.e mte=off), clear guest's MTE bits." + */ + if (kvm_enabled() && !cpu->kvm_mte) { + FIELD_DP64(cpu->isar.id_aa64pfr1, ID_AA64PFR1, MTE, 0); + } #endif } diff --git a/target/arm/cpu.h b/target/arm/cpu.h index f065756c5c..8fc8b6398f 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -922,6 +922,8 @@ struct ArchCPU { /* CPU has memory protection unit */ bool has_mpu; + /* CPU has MTE enabled in KVM mode */ + bool kvm_mte; /* PMSAv7 MPU number of supported regions */ uint32_t pmsav7_dregion; /* PMSAv8 MPU number of supported hyp regions */ diff --git a/target/arm/internals.h b/target/arm/internals.h index 299a96a81a..fd8f7c82aa 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -963,6 +963,7 @@ static inline uint32_t regime_el(CPUARMState *env, ARMMMUIdx mmu_idx) static inline bool regime_is_user(CPUARMState *env, ARMMMUIdx mmu_idx) { switch (mmu_idx) { + case ARMMMUIdx_E10_0: case ARMMMUIdx_E20_0: case ARMMMUIdx_Stage1_E0: case ARMMMUIdx_MUser: @@ -972,10 +973,6 @@ static inline bool regime_is_user(CPUARMState *env, ARMMMUIdx mmu_idx) return true; default: return false; - case ARMMMUIdx_E10_0: - case ARMMMUIdx_E10_1: - case ARMMMUIdx_E10_1_PAN: - g_assert_not_reached(); } } diff --git a/target/arm/kvm.c b/target/arm/kvm.c index f1f1b5b375..7b6812c0de 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -39,8 +39,10 @@ #include "hw/acpi/acpi.h" #include "hw/acpi/ghes.h" #include "target/arm/gtimer.h" +#include "migration/blocker.h" const KVMCapabilityInfo kvm_arch_required_capabilities[] = { + KVM_CAP_INFO(DEVICE_CTRL), KVM_CAP_LAST_INFO }; @@ -119,6 +121,21 @@ bool kvm_arm_create_scratch_host_vcpu(const uint32_t *cpus_to_try, if (vmfd < 0) { goto err; } + + /* + * The MTE capability must be enabled by the VMM before creating + * any VCPUs in order to allow the MTE bits of the ID_AA64PFR1 + * register to be probed correctly, as they are masked if MTE + * is not enabled. + */ + if (kvm_arm_mte_supported()) { + KVMState kvm_state; + + kvm_state.fd = kvmfd; + kvm_state.vmfd = vmfd; + kvm_vm_enable_cap(&kvm_state, KVM_CAP_ARM_MTE, 0); + } + cpufd = ioctl(vmfd, KVM_CREATE_VCPU, 0); if (cpufd < 0) { goto err; @@ -675,19 +692,11 @@ static void kvm_arm_set_device_addr(KVMDevice *kd) { struct kvm_device_attr *attr = &kd->kdattr; int ret; + uint64_t addr = kd->kda.addr; - /* If the device control API is available and we have a device fd on the - * KVMDevice struct, let's use the newer API - */ - if (kd->dev_fd >= 0) { - uint64_t addr = kd->kda.addr; - - addr |= kd->kda_addr_ormask; - attr->addr = (uintptr_t)&addr; - ret = kvm_device_ioctl(kd->dev_fd, KVM_SET_DEVICE_ATTR, attr); - } else { - ret = kvm_vm_ioctl(kvm_state, KVM_ARM_SET_DEVICE_ADDR, &kd->kda); - } + addr |= kd->kda_addr_ormask; + attr->addr = (uintptr_t)&addr; + ret = kvm_device_ioctl(kd->dev_fd, KVM_SET_DEVICE_ATTR, attr); if (ret < 0) { fprintf(stderr, "Failed to set device address: %s\n", @@ -1793,6 +1802,11 @@ bool kvm_arm_sve_supported(void) return kvm_check_extension(kvm_state, KVM_CAP_ARM_SVE); } +bool kvm_arm_mte_supported(void) +{ + return kvm_check_extension(kvm_state, KVM_CAP_ARM_MTE); +} + QEMU_BUILD_BUG_ON(KVM_ARM64_SVE_VQ_MIN != 1); uint32_t kvm_arm_sve_get_vls(ARMCPU *cpu) @@ -2417,3 +2431,40 @@ int kvm_arch_remove_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp) } return 0; } + +void kvm_arm_enable_mte(Object *cpuobj, Error **errp) +{ + static bool tried_to_enable; + static bool succeeded_to_enable; + Error *mte_migration_blocker = NULL; + ARMCPU *cpu = ARM_CPU(cpuobj); + int ret; + + if (!tried_to_enable) { + /* + * MTE on KVM is enabled on a per-VM basis (and retrying doesn't make + * sense), and we only want a single migration blocker as well. + */ + tried_to_enable = true; + + ret = kvm_vm_enable_cap(kvm_state, KVM_CAP_ARM_MTE, 0); + if (ret) { + error_setg_errno(errp, -ret, "Failed to enable KVM_CAP_ARM_MTE"); + return; + } + + /* TODO: Add migration support with MTE enabled */ + error_setg(&mte_migration_blocker, + "Live migration disabled due to MTE enabled"); + if (migrate_add_blocker(&mte_migration_blocker, errp)) { + error_free(mte_migration_blocker); + return; + } + + succeeded_to_enable = true; + } + + if (succeeded_to_enable) { + cpu->kvm_mte = true; + } +} diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h index cfaa0d9bc7..2e6b49bf13 100644 --- a/target/arm/kvm_arm.h +++ b/target/arm/kvm_arm.h @@ -22,17 +22,15 @@ * @devid: the KVM device ID * @group: device control API group for setting addresses * @attr: device control API address type - * @dev_fd: device control device file descriptor (or -1 if not supported) + * @dev_fd: device control device file descriptor * @addr_ormask: value to be OR'ed with resolved address * - * Remember the memory region @mr, and when it is mapped by the - * machine model, tell the kernel that base address using the - * KVM_ARM_SET_DEVICE_ADDRESS ioctl or the newer device control API. @devid - * should be the ID of the device as defined by KVM_ARM_SET_DEVICE_ADDRESS or - * the arm-vgic device in the device control API. - * The machine model may map - * and unmap the device multiple times; the kernel will only be told the final - * address at the point where machine init is complete. + * Remember the memory region @mr, and when it is mapped by the machine + * model, tell the kernel that base address using the device control API. + * @devid should be the ID of the device as defined by the arm-vgic device + * in the device control API. The machine model may map and unmap the device + * multiple times; the kernel will only be told the final address at the + * point where machine init is complete. */ void kvm_arm_register_device(MemoryRegion *mr, uint64_t devid, uint64_t group, uint64_t attr, int dev_fd, uint64_t addr_ormask); @@ -189,6 +187,13 @@ bool kvm_arm_pmu_supported(void); bool kvm_arm_sve_supported(void); /** + * kvm_arm_mte_supported: + * + * Returns: true if KVM can enable MTE, and false otherwise. + */ +bool kvm_arm_mte_supported(void); + +/** * kvm_arm_get_max_vm_ipa_size: * @ms: Machine state handle * @fixed_ipa: True when the IPA limit is fixed at 40. This is the case @@ -214,6 +219,8 @@ void kvm_arm_pvtime_init(ARMCPU *cpu, uint64_t ipa); int kvm_arm_set_irq(int cpu, int irqtype, int irq, int level); +void kvm_arm_enable_mte(Object *cpuobj, Error **errp); + #else /* @@ -235,6 +242,11 @@ static inline bool kvm_arm_sve_supported(void) return false; } +static inline bool kvm_arm_mte_supported(void) +{ + return false; +} + /* * These functions should never actually be called without KVM support. */ @@ -283,6 +295,11 @@ static inline uint32_t kvm_arm_sve_get_vls(ARMCPU *cpu) g_assert_not_reached(); } +static inline void kvm_arm_enable_mte(Object *cpuobj, Error **errp) +{ + g_assert_not_reached(); +} + #endif #endif diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c index 56b431faf5..8f42a28d07 100644 --- a/target/arm/tcg/helper-a64.c +++ b/target/arm/tcg/helper-a64.c @@ -1348,7 +1348,7 @@ static void do_setm(CPUARMState *env, uint32_t syndrome, uint32_t mtedesc, /* Do the actual memset: we leave the last partial page to SETE */ stagesetsize = setsize & TARGET_PAGE_MASK; while (stagesetsize > 0) { - step = stepfn(env, toaddr, setsize, data, memidx, &mtedesc, ra); + step = stepfn(env, toaddr, stagesetsize, data, memidx, &mtedesc, ra); toaddr += step; setsize -= step; stagesetsize -= step; diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index 203d37303b..62638d2b1f 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -59,32 +59,6 @@ static inline int vfp_exceptbits_from_host(int host_bits) return target_bits; } -/* Convert vfp exception flags to target form. */ -static inline int vfp_exceptbits_to_host(int target_bits) -{ - int host_bits = 0; - - if (target_bits & 1) { - host_bits |= float_flag_invalid; - } - if (target_bits & 2) { - host_bits |= float_flag_divbyzero; - } - if (target_bits & 4) { - host_bits |= float_flag_overflow; - } - if (target_bits & 8) { - host_bits |= float_flag_underflow; - } - if (target_bits & 0x10) { - host_bits |= float_flag_inexact; - } - if (target_bits & 0x80) { - host_bits |= float_flag_input_denormal; - } - return host_bits; -} - static uint32_t vfp_get_fpsr_from_host(CPUARMState *env) { uint32_t i; @@ -99,15 +73,14 @@ static uint32_t vfp_get_fpsr_from_host(CPUARMState *env) return vfp_exceptbits_from_host(i); } -static void vfp_set_fpsr_to_host(CPUARMState *env, uint32_t val) +static void vfp_clear_float_status_exc_flags(CPUARMState *env) { /* - * The exception flags are ORed together when we read fpscr so we - * only need to preserve the current state in one of our - * float_status values. + * Clear out all the exception-flag information in the float_status + * values. The caller should have arranged for env->vfp.fpsr to + * be the architecturally up-to-date exception flag information first. */ - int i = vfp_exceptbits_to_host(val); - set_float_exception_flags(i, &env->vfp.fp_status); + set_float_exception_flags(0, &env->vfp.fp_status); set_float_exception_flags(0, &env->vfp.fp_status_f16); set_float_exception_flags(0, &env->vfp.standard_fp_status); set_float_exception_flags(0, &env->vfp.standard_fp_status_f16); @@ -164,7 +137,7 @@ static uint32_t vfp_get_fpsr_from_host(CPUARMState *env) return 0; } -static void vfp_set_fpsr_to_host(CPUARMState *env, uint32_t val) +static void vfp_clear_float_status_exc_flags(CPUARMState *env) { } @@ -216,8 +189,6 @@ void vfp_set_fpsr(CPUARMState *env, uint32_t val) { ARMCPU *cpu = env_archcpu(env); - vfp_set_fpsr_to_host(env, val); - if (arm_feature(env, ARM_FEATURE_NEON) || cpu_isar_feature(aa32_mve, cpu)) { /* @@ -231,13 +202,18 @@ void vfp_set_fpsr(CPUARMState *env, uint32_t val) } /* - * The only FPSR bits we keep in vfp.fpsr are NZCV: - * the exception flags IOC|DZC|OFC|UFC|IXC|IDC are stored in - * fp_status, and QC is in vfp.qc[]. Store the NZCV bits there, - * and zero any of the other FPSR bits. + * NZCV lives only in env->vfp.fpsr. The cumulative exception flags + * IOC|DZC|OFC|UFC|IXC|IDC also live in env->vfp.fpsr, with possible + * extra pending exception information that hasn't yet been folded in + * living in the float_status values (for TCG). + * Since this FPSR write gives us the up to date values of the exception + * flags, we want to store into vfp.fpsr the NZCV and CEXC bits, zeroing + * anything else. We also need to clear out the float_status exception + * information so that the next vfp_get_fpsr does not fold in stale data. */ - val &= FPSR_NZCV_MASK; + val &= FPSR_NZCV_MASK | FPSR_CEXC_MASK; env->vfp.fpsr = val; + vfp_clear_float_status_exc_flags(env); } static void vfp_set_fpcr_masked(CPUARMState *env, uint32_t val, uint32_t mask) |