For now just deal with the basic version probe we see during startup.
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
---
target/arm/kvm.c | 44 +++++++++++++++++++++++++++++++++++++++++
target/arm/trace-events | 1 +
2 files changed, 45 insertions(+)
diff --git a/target/arm/kvm.c b/target/arm/kvm.c
index 0a852af126..1280e2c1e8 100644
--- a/target/arm/kvm.c
+++ b/target/arm/kvm.c
@@ -1507,6 +1507,43 @@ static int kvm_arm_handle_sysreg_trap(ARMCPU *cpu,
return -1;
}
+/*
+ * The guest is making a hypercall or firmware call. We can handle a
+ * limited number of them (e.g. PSCI) but we can't emulate a true
+ * firmware. This is an abbreviated version of
+ * kvm_smccc_call_handler() in the kernel and the TCG only arm_handle_psci_call().
+ *
+ * In the SplitAccel case we would be transitioning to execute EL2+
+ * under TCG.
+ */
+static int kvm_arm_handle_hypercall(ARMCPU *cpu,
+ int esr_ec)
+{
+ CPUARMState *env = &cpu->env;
+ int32_t ret = 0;
+
+ trace_kvm_hypercall(esr_ec, env->xregs[0]);
+
+ switch (env->xregs[0]) {
+ case QEMU_PSCI_0_2_FN_PSCI_VERSION:
+ ret = QEMU_PSCI_VERSION_1_1;
+ break;
+ case QEMU_PSCI_0_2_FN_MIGRATE_INFO_TYPE:
+ ret = QEMU_PSCI_0_2_RET_TOS_MIGRATION_NOT_REQUIRED; /* No trusted OS */
+ break;
+ case QEMU_PSCI_1_0_FN_PSCI_FEATURES:
+ ret = QEMU_PSCI_RET_NOT_SUPPORTED;
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP, "%s: unhandled hypercall %"PRIx64"\n",
+ __func__, env->xregs[0]);
+ return -1;
+ }
+
+ env->xregs[0] = ret;
+ return 0;
+}
+
/**
* kvm_arm_handle_hard_trap:
* @cpu: ARMCPU
@@ -1538,6 +1575,13 @@ static int kvm_arm_handle_hard_trap(ARMCPU *cpu,
switch (esr_ec) {
case EC_SYSTEMREGISTERTRAP:
return kvm_arm_handle_sysreg_trap(cpu, esr_iss, elr);
+ case EC_AA32_SVC:
+ case EC_AA32_HVC:
+ case EC_AA32_SMC:
+ case EC_AA64_SVC:
+ case EC_AA64_HVC:
+ case EC_AA64_SMC:
+ return kvm_arm_handle_hypercall(cpu, esr_ec);
default:
qemu_log_mask(LOG_UNIMP, "%s: unhandled EC: %x/%x/%x/%d\n",
__func__, esr_ec, esr_iss, esr_iss2, esr_il);
diff --git a/target/arm/trace-events b/target/arm/trace-events
index 69bb4d370d..10cdba92a3 100644
--- a/target/arm/trace-events
+++ b/target/arm/trace-events
@@ -15,3 +15,4 @@ arm_gt_update_irq(int timer, int irqstate) "gt_update_irq: timer %d irqstate %d"
kvm_arm_fixup_msi_route(uint64_t iova, uint64_t gpa) "MSI iova = 0x%"PRIx64" is translated into 0x%"PRIx64
kvm_sysreg_read(const char *name, uint64_t val) "%s => 0x%" PRIx64
kvm_sysreg_write(const char *name, uint64_t val) "%s <= 0x%" PRIx64
+kvm_hypercall(int ec, uint64_t arg0) "%d: %"PRIx64
--
2.47.2
On 17/6/25 18:33, Alex Bennée wrote:
> For now just deal with the basic version probe we see during startup.
>
> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
> ---
> target/arm/kvm.c | 44 +++++++++++++++++++++++++++++++++++++++++
> target/arm/trace-events | 1 +
> 2 files changed, 45 insertions(+)
> +/*
> + * The guest is making a hypercall or firmware call. We can handle a
> + * limited number of them (e.g. PSCI) but we can't emulate a true
> + * firmware. This is an abbreviated version of
> + * kvm_smccc_call_handler() in the kernel and the TCG only arm_handle_psci_call().
> + *
> + * In the SplitAccel case we would be transitioning to execute EL2+
> + * under TCG.
> + */
> +static int kvm_arm_handle_hypercall(ARMCPU *cpu,
> + int esr_ec)
> +{
> + CPUARMState *env = &cpu->env;
> + int32_t ret = 0;
> +
> + trace_kvm_hypercall(esr_ec, env->xregs[0]);
> +
Should we make arm_is_psci_call() generic to be able to use it here?
> + switch (env->xregs[0]) {
> + case QEMU_PSCI_0_2_FN_PSCI_VERSION:
> + ret = QEMU_PSCI_VERSION_1_1;
> + break;
> + case QEMU_PSCI_0_2_FN_MIGRATE_INFO_TYPE:
> + ret = QEMU_PSCI_0_2_RET_TOS_MIGRATION_NOT_REQUIRED; /* No trusted OS */
> + break;
> + case QEMU_PSCI_1_0_FN_PSCI_FEATURES:
> + ret = QEMU_PSCI_RET_NOT_SUPPORTED;
> + break;
> + default:
> + qemu_log_mask(LOG_UNIMP, "%s: unhandled hypercall %"PRIx64"\n",
> + __func__, env->xregs[0]);
> + return -1;
> + }
> +
> + env->xregs[0] = ret;
> + return 0;
> +}
On 17/6/25 18:33, Alex Bennée wrote:
> For now just deal with the basic version probe we see during startup.
>
> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
> ---
> target/arm/kvm.c | 44 +++++++++++++++++++++++++++++++++++++++++
> target/arm/trace-events | 1 +
> 2 files changed, 45 insertions(+)
>
> diff --git a/target/arm/kvm.c b/target/arm/kvm.c
> index 0a852af126..1280e2c1e8 100644
> --- a/target/arm/kvm.c
> +++ b/target/arm/kvm.c
> @@ -1507,6 +1507,43 @@ static int kvm_arm_handle_sysreg_trap(ARMCPU *cpu,
> return -1;
> }
>
> +/*
> + * The guest is making a hypercall or firmware call. We can handle a
> + * limited number of them (e.g. PSCI) but we can't emulate a true
> + * firmware. This is an abbreviated version of
> + * kvm_smccc_call_handler() in the kernel and the TCG only arm_handle_psci_call().
> + *
> + * In the SplitAccel case we would be transitioning to execute EL2+
> + * under TCG.
> + */
> +static int kvm_arm_handle_hypercall(ARMCPU *cpu,
> + int esr_ec)
> +{
> + CPUARMState *env = &cpu->env;
> + int32_t ret = 0;
> +
> + trace_kvm_hypercall(esr_ec, env->xregs[0]);
> +
> + switch (env->xregs[0]) {
> + case QEMU_PSCI_0_2_FN_PSCI_VERSION:
> + ret = QEMU_PSCI_VERSION_1_1;
> + break;
> + case QEMU_PSCI_0_2_FN_MIGRATE_INFO_TYPE:
> + ret = QEMU_PSCI_0_2_RET_TOS_MIGRATION_NOT_REQUIRED; /* No trusted OS */
> + break;
> + case QEMU_PSCI_1_0_FN_PSCI_FEATURES:
> + ret = QEMU_PSCI_RET_NOT_SUPPORTED;
> + break;
> + default:
> + qemu_log_mask(LOG_UNIMP, "%s: unhandled hypercall %"PRIx64"\n",
> + __func__, env->xregs[0]);
> + return -1;
> + }
> +
> + env->xregs[0] = ret;
> + return 0;
> +}
> +
> /**
> * kvm_arm_handle_hard_trap:
> * @cpu: ARMCPU
> @@ -1538,6 +1575,13 @@ static int kvm_arm_handle_hard_trap(ARMCPU *cpu,
> switch (esr_ec) {
> case EC_SYSTEMREGISTERTRAP:
> return kvm_arm_handle_sysreg_trap(cpu, esr_iss, elr);
> + case EC_AA32_SVC:
> + case EC_AA32_HVC:
> + case EC_AA32_SMC:
> + case EC_AA64_SVC:
> + case EC_AA64_HVC:
> + case EC_AA64_SMC:
Should we increment $pc for SVC/SMC?
The instruction operation pseudocode [*] is:
preferred_exception_return = ThisInstrAddr(64);
[*]
https://developer.arm.com/documentation/ddi0602/2022-06/Shared-Pseudocode/AArch64-Exceptions?lang=en
> + return kvm_arm_handle_hypercall(cpu, esr_ec);
> default:
> qemu_log_mask(LOG_UNIMP, "%s: unhandled EC: %x/%x/%x/%d\n",
> __func__, esr_ec, esr_iss, esr_iss2, esr_il);
> diff --git a/target/arm/trace-events b/target/arm/trace-events
> index 69bb4d370d..10cdba92a3 100644
> --- a/target/arm/trace-events
> +++ b/target/arm/trace-events
> @@ -15,3 +15,4 @@ arm_gt_update_irq(int timer, int irqstate) "gt_update_irq: timer %d irqstate %d"
> kvm_arm_fixup_msi_route(uint64_t iova, uint64_t gpa) "MSI iova = 0x%"PRIx64" is translated into 0x%"PRIx64
> kvm_sysreg_read(const char *name, uint64_t val) "%s => 0x%" PRIx64
> kvm_sysreg_write(const char *name, uint64_t val) "%s <= 0x%" PRIx64
> +kvm_hypercall(int ec, uint64_t arg0) "%d: %"PRIx64
On Fri, Aug 22, 2025 at 10:13 AM Philippe Mathieu-Daudé
<philmd@linaro.org> wrote:
>
> On 17/6/25 18:33, Alex Bennée wrote:
> > For now just deal with the basic version probe we see during startup.
> >
> > Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
> > ---
> > target/arm/kvm.c | 44 +++++++++++++++++++++++++++++++++++++++++
> > target/arm/trace-events | 1 +
> > 2 files changed, 45 insertions(+)
> >
> > diff --git a/target/arm/kvm.c b/target/arm/kvm.c
> > index 0a852af126..1280e2c1e8 100644
> > --- a/target/arm/kvm.c
> > +++ b/target/arm/kvm.c
> > @@ -1507,6 +1507,43 @@ static int kvm_arm_handle_sysreg_trap(ARMCPU *cpu,
> > return -1;
> > }
> >
> > +/*
> > + * The guest is making a hypercall or firmware call. We can handle a
> > + * limited number of them (e.g. PSCI) but we can't emulate a true
> > + * firmware. This is an abbreviated version of
> > + * kvm_smccc_call_handler() in the kernel and the TCG only arm_handle_psci_call().
> > + *
> > + * In the SplitAccel case we would be transitioning to execute EL2+
> > + * under TCG.
> > + */
> > +static int kvm_arm_handle_hypercall(ARMCPU *cpu,
> > + int esr_ec)
> > +{
> > + CPUARMState *env = &cpu->env;
> > + int32_t ret = 0;
> > +
> > + trace_kvm_hypercall(esr_ec, env->xregs[0]);
> > +
> > + switch (env->xregs[0]) {
> > + case QEMU_PSCI_0_2_FN_PSCI_VERSION:
> > + ret = QEMU_PSCI_VERSION_1_1;
> > + break;
> > + case QEMU_PSCI_0_2_FN_MIGRATE_INFO_TYPE:
> > + ret = QEMU_PSCI_0_2_RET_TOS_MIGRATION_NOT_REQUIRED; /* No trusted OS */
> > + break;
> > + case QEMU_PSCI_1_0_FN_PSCI_FEATURES:
> > + ret = QEMU_PSCI_RET_NOT_SUPPORTED;
> > + break;
> > + default:
> > + qemu_log_mask(LOG_UNIMP, "%s: unhandled hypercall %"PRIx64"\n",
> > + __func__, env->xregs[0]);
> > + return -1;
> > + }
> > +
> > + env->xregs[0] = ret;
> > + return 0;
> > +}
> > +
> > /**
> > * kvm_arm_handle_hard_trap:
> > * @cpu: ARMCPU
> > @@ -1538,6 +1575,13 @@ static int kvm_arm_handle_hard_trap(ARMCPU *cpu,
> > switch (esr_ec) {
> > case EC_SYSTEMREGISTERTRAP:
> > return kvm_arm_handle_sysreg_trap(cpu, esr_iss, elr);
> > + case EC_AA32_SVC:
> > + case EC_AA32_HVC:
> > + case EC_AA32_SMC:
> > + case EC_AA64_SVC:
> > + case EC_AA64_HVC:
> > + case EC_AA64_SMC:
>
> Should we increment $pc for SVC/SMC?
> The instruction operation pseudocode [*] is:
>
> preferred_exception_return = ThisInstrAddr(64);
>
Here's what the trusted firmware handler does.
The exception return address is modified by the :
https://github.com/ARM-software/arm-trusted-firmware/blob/da6b3a181c03a492ee52182b0466d0b7cc4091dd/bl31/aarch64/runtime_exceptions.S#L456-L480
> * returns:
> * -1: unhandled trap, UNDEF injection into lower EL
> * 0: handled trap, return to the trapping instruction (repeating it)
> * 1: handled trap, return to the next instruction
An SMC-aware trap handler should do the same
> [*]
> https://developer.arm.com/documentation/ddi0602/2022-06/Shared-Pseudocode/AArch64-Exceptions?lang=en
>
> > + return kvm_arm_handle_hypercall(cpu, esr_ec);
> > default:
> > qemu_log_mask(LOG_UNIMP, "%s: unhandled EC: %x/%x/%x/%d\n",
> > __func__, esr_ec, esr_iss, esr_iss2, esr_il);
> > diff --git a/target/arm/trace-events b/target/arm/trace-events
> > index 69bb4d370d..10cdba92a3 100644
> > --- a/target/arm/trace-events
> > +++ b/target/arm/trace-events
> > @@ -15,3 +15,4 @@ arm_gt_update_irq(int timer, int irqstate) "gt_update_irq: timer %d irqstate %d"
> > kvm_arm_fixup_msi_route(uint64_t iova, uint64_t gpa) "MSI iova = 0x%"PRIx64" is translated into 0x%"PRIx64
> > kvm_sysreg_read(const char *name, uint64_t val) "%s => 0x%" PRIx64
> > kvm_sysreg_write(const char *name, uint64_t val) "%s <= 0x%" PRIx64
> > +kvm_hypercall(int ec, uint64_t arg0) "%d: %"PRIx64
>
>
© 2016 - 2025 Red Hat, Inc.