qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [RFC PATCH 1/1] target/arm: kvm: Handle DABT with no valid ISS


From: Peter Maydell
Subject: Re: [RFC PATCH 1/1] target/arm: kvm: Handle DABT with no valid ISS
Date: Mon, 6 Jan 2020 17:14:53 +0000

On Fri, 20 Dec 2019 at 20:27, Beata Michalska
<address@hidden> wrote:
>
> On ARMv7 & ARMv8 some load/store instructions might trigger a data abort
> exception with no valid ISS info to be decoded. The lack of decode info
> makes it at least tricky to emulate those instruction which is one of the
> (many) reasons why KVM will not even try to do so.
>
> Add suport for handling those by requesting KVM to inject external
> dabt into the quest.
>
> Signed-off-by: Beata Michalska <address@hidden>
> ---
>  accel/kvm/kvm-all.c    | 15 +++++++
>  accel/stubs/kvm-stub.c |  4 ++
>  include/sysemu/kvm.h   |  1 +
>  target/arm/cpu.h       |  3 +-
>  target/arm/kvm.c       | 95 ++++++++++++++++++++++++++++++++++++++++++
>  target/arm/kvm32.c     |  3 ++
>  target/arm/kvm64.c     |  3 ++
>  target/arm/kvm_arm.h   | 19 +++++++++
>  8 files changed, 142 insertions(+), 1 deletion(-)
>
> diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c
> index ca00daa2f5..a3ee038142 100644
> --- a/accel/kvm/kvm-all.c
> +++ b/accel/kvm/kvm-all.c
> @@ -2174,6 +2174,14 @@ static void do_kvm_cpu_synchronize_state(CPUState 
> *cpu, run_on_cpu_data arg)
>      }
>  }
>
> +static void do_kvm_cpu_synchronize_state_force(CPUState *cpu,
> +                                               run_on_cpu_data arg)
> +{
> +    kvm_arch_get_registers(cpu);
> +    cpu->vcpu_dirty = true;
> +}

Why is this functionality special such that it needs a non-standard
way of synchronizing state with KVM that nothing else does ?

> diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h
> index 9fe233b9bf..0cacc61d8a 100644
> --- a/include/sysemu/kvm.h
> +++ b/include/sysemu/kvm.h
> @@ -483,6 +483,7 @@ void kvm_cpu_synchronize_state(CPUState *cpu);
>  void kvm_cpu_synchronize_post_reset(CPUState *cpu);
>  void kvm_cpu_synchronize_post_init(CPUState *cpu);
>  void kvm_cpu_synchronize_pre_loadvm(CPUState *cpu);
> +void kvm_cpu_synchronize_state_force(CPUState *cpu);
>
>  void kvm_init_cpu_signals(CPUState *cpu);
>
> diff --git a/target/arm/cpu.h b/target/arm/cpu.h
> index 5f70e9e043..e11b5e7438 100644
> --- a/target/arm/cpu.h
> +++ b/target/arm/cpu.h
> @@ -558,7 +558,8 @@ typedef struct CPUARMState {
>          uint8_t has_esr;
>          uint64_t esr;
>      } serror;
> -
> +    /* Status field for pending extarnal dabt */

"external" (I think you have the same typo later in the patch too)

> +    uint8_t ext_dabt_pending;
>      /* State of our input IRQ/FIQ/VIRQ/VFIQ lines */
>      uint32_t irq_line_state;


> @@ -701,6 +719,11 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run 
> *run)
>              ret = EXCP_DEBUG;
>          } /* otherwise return to guest */
>          break;
> +    case KVM_EXIT_ARM_NISV:
> +        /* External DAB with no valid iss to decode */

"DABT"

> +        ret = kvm_arm_handle_dabt_nisv(cs, run->arm_nisv.esr_iss,
> +                                 run->arm_nisv.fault_ipa);

(indentation looks odd here?)

> +        break;
>      default:
>          qemu_log_mask(LOG_UNIMP, "%s: un-handled exit reason %d\n",
>                        __func__, run->exit_reason);
> @@ -835,3 +858,75 @@ int kvm_arch_msi_data_to_gsi(uint32_t data)
>  {
>      return (data - 32) & 0xffff;
>  }
> +
> +int kvm_arm_handle_dabt_nisv(CPUState *cs, uint64_t esr_iss,
> +                              uint64_t fault_ipa)
> +{
> +    ARMCPU *cpu = ARM_CPU(cs);
> +    CPUARMState *env = &cpu->env;
> +    hwaddr xlat, len;
> +    AddressSpace *as = cs->as ? cs->as : &address_space_memory;

I don't think you should need to test cs->as for NULL;
qemu_init_vcpu() ensures that one is always set up.

Probably the neatest way to get the AddressSpace* is to
call arm_addressspace(cs, MEMTXATTRS_UNSPECIFIED).


> +    int store_ins = ((esr_iss >> 6) & 1); /* WnR bit */

This should be a bool (matching the argument type for
address_space_translate), so you can just do
   bool is_write = esr_iss & (1 << 6);

> +    int ret;
> +
> +    /*
> +     * Note: The ioctl for SET_EVENTS will be triggered before next
> +     * KVM_RUN call though the vcpu regs need to be updated before hand
> +     * so to not to overwrite changes done by KVM upon injecting the abort.
> +     * This sadly requires running sync twice - shame ...
> +     */
> +    kvm_cpu_synchronize_state(cs);
> +   /*
> +    * ISS [23:14] is invalid so there is a limited info
> +    * on what has just happened so the only *useful* thing that can
> +    * be retrieved from ISS is WnR & DFSC (though in some cases WnR
> +    * might be less of a value as well)
> +    */
> +    /* Verify whether the memory being accessed is even recognisable */
> +    if (!address_space_translate(as, fault_ipa, &xlat, &len,
> +                                 store_ins, MEMTXATTRS_UNSPECIFIED)) {
> +        error_report("An attemp to access memory outside of the boundries"

"attempt"; "boundaries". But I think what we should actually report here
is:

"Guest attempted to access memory/device at guest physical address
0x... using an instruction that KVM could not emulate (no valid ISS).
Injecting a data abort exception into guest."

It doesn't seem to me worth distinguishing whether the guest
physical address happens to have anything mapped there or not;
we're going to inject a somewhat bogus dabt anyway.

After the initial error_report(), supplemental information like
the offending instruction should be printed with error_printf(),
because it isn't a separate new error.

> +                     "at 0x" TARGET_FMT_lx, (target_ulong) fault_ipa);
> +    } else {
> +        uint32_t ins;
> +        uint8_t ins_fetched;
> +
> +        /*
> +         * Get current PC before it will get updated to except vector entry

"exception"

> +         */
> +        target_ulong ins_addr = is_a64(env) ? env->pc
> +                                /* AArch32 mode vs T32 aka Thumb mode */
> +                                : env->regs[15] - (env->thumb ? 4 : 8);
> +
> +        /*
> +         * Get the faulting instruction
> +         * Instructions have a fixed length of 32 bits
> +         * and are always little-endian

...unless they're 16-bit Thumb. In an ideal world you should handle
the Thumb case by doing a 16-bit load, and then loading another 16-bit
value if the top 3 bits of the first part are 0b111.

At least we don't have to consider the possibility ofold-style
really-big-endian-instruction-ordering :-)

> +         */
> +        ins_fetched = !cpu_memory_rw_debug(cs, ins_addr, (uint8_t *) &ins,
> +                                           sizeof(uint32_t), store_ins) ;

stray space before semicolon

> +
> +        error_report("Data abort exception with no valid ISS generated by "
> +                     "memory access at 0x" TARGET_FMT_lx,
> +                     (target_ulong)fault_ipa);
> +        error_report(ins_fetched ? "%s: 0x%" PRIx32 " (encoded)" : "%s",
> +                     "Unable to emulate memory instruction",
> +                     (!ins_fetched ? 0 : ldl_le_p(&ins)));
> +
> +        error_report("Faulting instruction at 0x" TARGET_FMT_lx, ins_addr);
> +    }
> +    /*
> +     * Set pending ext dabt amd trigger SET_EVENTS so that
> +     * KVM can inject the abort
> +     */
> +    env->ext_dabt_pending = 1;
> +
> +    ret = kvm_put_vcpu_events(cpu);
> +
> +    /* Get the most up-tp-date state */

"to"

> +    if (!ret) {
> +        /* Hacky but the sync needs to be forced */
> +        kvm_cpu_synchronize_state_force(cs);
> +    }
> +    return ret;
> +}
> diff --git a/target/arm/kvm32.c b/target/arm/kvm32.c
> index 32bf8d6757..5539c3a3f2 100644
> --- a/target/arm/kvm32.c
> +++ b/target/arm/kvm32.c
> @@ -240,6 +240,9 @@ int kvm_arch_init_vcpu(CPUState *cs)
>      /* Check whether userspace can specify guest syndrome value */
>      kvm_arm_init_serror_injection(cs);
>
> +    /* Set status for supporting the extarnal dabt injection */
> +    kvm_arm_init_ext_dabt_injection(cs);
> +
>      return kvm_arm_init_cpreg_list(cpu);
>  }
>
> diff --git a/target/arm/kvm64.c b/target/arm/kvm64.c
> index 876184b8fe..3da4e2e70e 100644
> --- a/target/arm/kvm64.c
> +++ b/target/arm/kvm64.c
> @@ -792,6 +792,9 @@ int kvm_arch_init_vcpu(CPUState *cs)
>      /* Check whether user space can specify guest syndrome value */
>      kvm_arm_init_serror_injection(cs);
>
> +    /* Set status for supporting the extarnal dabt injection */
> +    kvm_arm_init_ext_dabt_injection(cs);
> +
>      return kvm_arm_init_cpreg_list(cpu);
>  }

thanks
-- PMM



reply via email to

[Prev in Thread] Current Thread [Next in Thread]