On Tue, 11 Oct 2022 at 04:43, Richard Henderson
<[email protected]> wrote:
>
> Perform the atomic update for hardware management of the access flag.
>
> Signed-off-by: Richard Henderson <[email protected]>
> ---
> v4: Raise permission fault if pte read-only and atomic update reqd.
> Split out dirty bit portion.
> Prepare for a single update for AF + DB.
> +static uint64_t arm_casq_ptw(CPUARMState *env, uint64_t old_val,
> + uint64_t new_val, S1Translate *ptw,
> + ARMMMUFaultInfo *fi)
> +{
> + uint64_t cur_val;
> + void *host = ptw->out_host;
> +
> + if (unlikely(!host)) {
> + fi->type = ARMFault_UnsuppAtomicUpdate;
> + fi->s1ptw = true;
> + return 0;
> + }
> +
> + /*
> + * Raising a stage2 Protection fault for an atomic update to a read-only
> + * page is delayed until it is certain that there is a change to make.
> + */
> + if (unlikely(!ptw->out_rw)) {
> + int flags;
> + void *discard;
> +
> + env->tlb_fi = fi;
> + flags = probe_access_flags(env, ptw->out_virt, MMU_DATA_STORE,
> + arm_to_core_mmu_idx(ptw->in_ptw_idx),
> + true, &discard, 0);
> + env->tlb_fi = NULL;
> +
> + if (unlikely(flags & TLB_INVALID_MASK)) {
> + assert(fi->type != ARMFault_None);
> + fi->s2addr = ptw->out_virt;
> + fi->stage2 = true;
> + fi->s1ptw = true;
> + fi->s1ns = ptw->in_secure;
Shouldn't there be a ! here ? LHS is true-for-NS and RHS is true-for-S, I think.
> + return 0;
> + }
> +
> + /* In case CAS mismatches and we loop, remember writability. */
> + ptw->out_rw = true;
> + }
> +
> +#ifdef CONFIG_ATOMIC64
> + if (ptw->out_be) {
> + old_val = cpu_to_be64(old_val);
> + new_val = cpu_to_be64(new_val);
> + cur_val = qatomic_cmpxchg__nocheck((uint64_t *)host, old_val,
> new_val);
> + cur_val = be64_to_cpu(cur_val);
> + } else {
> + old_val = cpu_to_le64(old_val);
> + new_val = cpu_to_le64(new_val);
> + cur_val = qatomic_cmpxchg__nocheck((uint64_t *)host, old_val,
> new_val);
> + cur_val = le64_to_cpu(cur_val);
> + }
> +#else
> + /*
> + * We can't support the full 64-bit atomic cmpxchg on the host.
> + * Because this is only used for FEAT_HAFDBS, which is only for AA64,
> + * we know that TCG_OVERSIZED_GUEST is set, which means that we are
> + * running in round-robin mode and could only race with dma i/o.
> + */
> +#ifndef TCG_OVERSIZED_GUEST
> +# error "Unexpected configuration"
> +#endif
> + bool locked = qemu_mutex_iothread_locked();
> + if (!locked) {
> + qemu_mutex_lock_iothread();
> + }
> + if (ptw->out_be) {
> + cur_val = ldq_be_p(host);
> + if (cur_val == old_val) {
> + stq_be_p(host, new_val);
> + }
> + } else {
> + cur_val = ldq_le_p(host);
> + if (cur_val == old_val) {
> + stq_le_p(host, new_val);
> + }
> + }
> + if (!locked) {
> + qemu_mutex_unlock_iothread();
> + }
> +#endif
> +
> + return cur_val;
> +}
> @@ -1286,7 +1389,9 @@ static bool get_phys_addr_lpae(CPUARMState *env,
> S1Translate *ptw,
> if (fi->type != ARMFault_None) {
> goto do_fault;
> }
> + new_descriptor = descriptor;
>
> + restart_atomic_update:
> if (!(descriptor & 1) || (!(descriptor & 2) && (level == 3))) {
> /* Invalid, or the Reserved level 3 encoding */
> goto do_translation_fault;
> @@ -1362,10 +1467,18 @@ static bool get_phys_addr_lpae(CPUARMState *env,
> S1Translate *ptw,
> * Here descaddr is the final physical address, and attributes
> * are all in attrs.
> */
> - if ((attrs & (1 << 10)) == 0) {
> - /* Access flag */
> - fi->type = ARMFault_AccessFlag;
> - goto do_fault;
> + if (!(attrs & (1 << 10)) && !ptw->in_debug) {
> + /*
> + * Access flag.
> + * If HA is enabled, prepare to update the descriptor below.
> + * Otherwise, pass the access fault on to software.
> + */
> + if (param.ha) {
> + new_descriptor |= 1 << 10; /* AF */
> + } else {
> + fi->type = ARMFault_AccessFlag;
> + goto do_fault;
> + }
> }
>
> ap = extract32(attrs, 6, 2);
> @@ -1381,6 +1494,18 @@ static bool get_phys_addr_lpae(CPUARMState *env,
> S1Translate *ptw,
> result->f.prot = get_S1prot(env, mmu_idx, aarch64, ap, ns, xn, pxn);
> }
>
> + /* If FEAT_HAFDBS has made changes, update the PTE. */
> + if (new_descriptor != descriptor) {
> + new_descriptor = arm_casq_ptw(env, descriptor, new_descriptor, ptw,
> fi);
> + if (fi->type != ARMFault_None) {
> + goto do_fault;
> + }
> + if (new_descriptor != descriptor) {
I think we could probably usefully add a comment here:
/*
* I_YZSVV says that if the in-memory descriptor has changed, then we
* must use the information in that new value (which might include a
* different output address, different attributes, or generate a fault),
* Restart the handling of the descriptor value from scratch.
*/
Otherwise
Reviewed-by: Peter Maydell <[email protected]>
thanks
-- PMM