@@ -487,9 +487,15 @@ static void fdt_add_psci_node(void *fdt)
}
qemu_fdt_add_subnode(fdt, "/psci");
- if (armcpu->psci_version == 2) {
- const char comp[] = "arm,psci-0.2\0arm,psci";
- qemu_fdt_setprop(fdt, "/psci", "compatible", comp, sizeof(comp));
+ if (armcpu->psci_version == QEMU_PSCI_VERSION_0_2 ||
+ armcpu->psci_version == QEMU_PSCI_VERSION_1_1) {
+ if (armcpu->psci_version == QEMU_PSCI_VERSION_0_2) {
+ const char comp[] = "arm,psci-0.2\0arm,psci";
+ qemu_fdt_setprop(fdt, "/psci", "compatible", comp, sizeof(comp));
+ } else {
+ const char comp[] = "arm,psci-1.0\0arm,psci-0.2\0arm,psci";
+ qemu_fdt_setprop(fdt, "/psci", "compatible", comp, sizeof(comp));
+ }
cpu_off_fn = QEMU_PSCI_0_2_FN_CPU_OFF;
if (arm_feature(&armcpu->env, ARM_FEATURE_AARCH64)) {
@@ -1110,11 +1110,12 @@ static void arm_cpu_initfn(Object *obj)
* picky DTB consumer will also provide a helpful error message.
*/
cpu->dtb_compatible = "qemu,unknown";
- cpu->psci_version = 1; /* By default assume PSCI v0.1 */
+ cpu->psci_version = QEMU_PSCI_VERSION_0_1; /* By default assume PSCI v0.1 */
cpu->kvm_target = QEMU_KVM_ARM_TARGET_NONE;
if (tcg_enabled() || hvf_enabled()) {
- cpu->psci_version = 2; /* TCG and HVF implement PSCI 0.2 */
+ /* TCG and HVF implement PSCI 1.1 */
+ cpu->psci_version = QEMU_PSCI_VERSION_1_1;
}
}
@@ -10195,8 +10195,8 @@ void arm_cpu_do_interrupt(CPUState *cs)
env->exception.syndrome);
}
- if (arm_is_psci_call(cpu, cs->exception_index)) {
- arm_handle_psci_call(cpu);
+ if (arm_is_smccc_call(cpu, cs->exception_index)) {
+ arm_handle_smccc_call(cpu);
qemu_log_mask(CPU_LOG_INT, "...handled as PSCI call\n");
return;
}
@@ -653,7 +653,7 @@ static bool hvf_handle_psci_call(CPUState *cpu)
switch (param[0]) {
case QEMU_PSCI_0_2_FN_PSCI_VERSION:
- ret = QEMU_PSCI_0_2_RET_VERSION_0_2;
+ 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 */
@@ -721,6 +721,31 @@ static bool hvf_handle_psci_call(CPUState *cpu)
case QEMU_PSCI_0_2_FN_MIGRATE:
ret = QEMU_PSCI_RET_NOT_SUPPORTED;
break;
+ case QEMU_PSCI_1_0_FN_PSCI_FEATURES:
+ switch (param[1]) {
+ case QEMU_PSCI_0_2_FN_PSCI_VERSION:
+ case QEMU_PSCI_0_2_FN_MIGRATE_INFO_TYPE:
+ case QEMU_PSCI_0_2_FN_AFFINITY_INFO:
+ case QEMU_PSCI_0_2_FN64_AFFINITY_INFO:
+ case QEMU_PSCI_0_2_FN_SYSTEM_RESET:
+ case QEMU_PSCI_0_2_FN_SYSTEM_OFF:
+ case QEMU_PSCI_0_1_FN_CPU_ON:
+ case QEMU_PSCI_0_2_FN_CPU_ON:
+ case QEMU_PSCI_0_2_FN64_CPU_ON:
+ case QEMU_PSCI_0_1_FN_CPU_OFF:
+ case QEMU_PSCI_0_2_FN_CPU_OFF:
+ case QEMU_PSCI_0_1_FN_CPU_SUSPEND:
+ case QEMU_PSCI_0_2_FN_CPU_SUSPEND:
+ case QEMU_PSCI_0_2_FN64_CPU_SUSPEND:
+ case QEMU_PSCI_0_1_FN_MIGRATE:
+ case QEMU_PSCI_0_2_FN_MIGRATE:
+ case QEMU_PSCI_1_0_FN_PSCI_FEATURES:
+ ret = 0;
+ break;
+ default:
+ ret = QEMU_PSCI_RET_NOT_SUPPORTED;
+ }
+ break;
default:
return false;
}
@@ -1208,8 +1233,7 @@ int hvf_vcpu_exec(CPUState *cpu)
if (arm_cpu->psci_conduit == QEMU_PSCI_CONDUIT_HVC) {
if (!hvf_handle_psci_call(cpu)) {
trace_hvf_unknown_hvc(env->xregs[0]);
- /* SMCCC 1.3 section 5.2 says every unknown SMCCC call returns -1 */
- env->xregs[0] = -1;
+ env->xregs[0] = SMCCC_RET_NOT_SUPPORTED;
}
} else {
trace_hvf_unknown_hvc(env->xregs[0]);
@@ -1223,8 +1247,7 @@ int hvf_vcpu_exec(CPUState *cpu)
if (!hvf_handle_psci_call(cpu)) {
trace_hvf_unknown_smc(env->xregs[0]);
- /* SMCCC 1.3 section 5.2 says every unknown SMCCC call returns -1 */
- env->xregs[0] = -1;
+ env->xregs[0] = SMCCC_RET_NOT_SUPPORTED;
}
} else {
trace_hvf_unknown_smc(env->xregs[0]);
@@ -306,20 +306,39 @@ vaddr arm_adjust_watchpoint_address(CPUState *cs, vaddr addr, int len);
/* Callback function for when a watchpoint or breakpoint triggers. */
void arm_debug_excp_handler(CPUState *cs);
+/* ARM DEN 0028B section 5.2 says every unknown SMCCC call returns -1 */
+#define SMCCC_RET_NOT_SUPPORTED -1
+
#if defined(CONFIG_USER_ONLY) || !defined(CONFIG_TCG)
-static inline bool arm_is_psci_call(ARMCPU *cpu, int excp_type)
+static inline bool arm_is_smccc_call(ARMCPU *cpu, int excp_type)
{
return false;
}
-static inline void arm_handle_psci_call(ARMCPU *cpu)
+static inline void arm_handle_smccc_call(ARMCPU *cpu)
{
g_assert_not_reached();
}
#else
/* Return true if the r0/x0 value indicates that this SMC/HVC is a PSCI call. */
-bool arm_is_psci_call(ARMCPU *cpu, int excp_type);
-/* Actually handle a PSCI call */
-void arm_handle_psci_call(ARMCPU *cpu);
+static inline bool arm_is_smccc_call(ARMCPU *cpu, int excp_type)
+{
+ /* Return true if the exception type matches the configured PSCI conduit.
+ * This is called before the SMC/HVC instruction is executed, to decide
+ * whether we should treat it as a SMCCC-compliant call or with the
+ * architecturally defined behaviour for an SMC or HVC (which might be
+ * UNDEF or trap to EL2 or to EL3).
+ */
+ switch (excp_type) {
+ case EXCP_HVC:
+ return cpu->psci_conduit == QEMU_PSCI_CONDUIT_HVC;
+ case EXCP_SMC:
+ return cpu->psci_conduit == QEMU_PSCI_CONDUIT_SMC;
+ default:
+ return false;
+ }
+}
+/* Actually handle a SMCCC-compliant call */
+void arm_handle_smccc_call(ARMCPU *cpu);
#endif
/**
@@ -77,6 +77,8 @@ MISMATCH_CHECK(QEMU_PSCI_0_1_FN_MIGRATE, KVM_PSCI_FN_MIGRATE);
#define QEMU_PSCI_0_2_FN64_AFFINITY_INFO QEMU_PSCI_0_2_FN64(4)
#define QEMU_PSCI_0_2_FN64_MIGRATE QEMU_PSCI_0_2_FN64(5)
+#define QEMU_PSCI_1_0_FN_PSCI_FEATURES QEMU_PSCI_0_2_FN(10)
+
MISMATCH_CHECK(QEMU_PSCI_0_2_FN_CPU_SUSPEND, PSCI_0_2_FN_CPU_SUSPEND);
MISMATCH_CHECK(QEMU_PSCI_0_2_FN_CPU_OFF, PSCI_0_2_FN_CPU_OFF);
MISMATCH_CHECK(QEMU_PSCI_0_2_FN_CPU_ON, PSCI_0_2_FN_CPU_ON);
@@ -84,14 +86,16 @@ MISMATCH_CHECK(QEMU_PSCI_0_2_FN_MIGRATE, PSCI_0_2_FN_MIGRATE);
MISMATCH_CHECK(QEMU_PSCI_0_2_FN64_CPU_SUSPEND, PSCI_0_2_FN64_CPU_SUSPEND);
MISMATCH_CHECK(QEMU_PSCI_0_2_FN64_CPU_ON, PSCI_0_2_FN64_CPU_ON);
MISMATCH_CHECK(QEMU_PSCI_0_2_FN64_MIGRATE, PSCI_0_2_FN64_MIGRATE);
+MISMATCH_CHECK(QEMU_PSCI_1_0_FN_PSCI_FEATURES, PSCI_1_0_FN_PSCI_FEATURES);
/* PSCI v0.2 return values used by TCG emulation of PSCI */
/* No Trusted OS migration to worry about when offlining CPUs */
#define QEMU_PSCI_0_2_RET_TOS_MIGRATION_NOT_REQUIRED 2
-/* We implement version 0.2 only */
-#define QEMU_PSCI_0_2_RET_VERSION_0_2 2
+#define QEMU_PSCI_VERSION_0_1 0x00001
+#define QEMU_PSCI_VERSION_0_2 0x00002
+#define QEMU_PSCI_VERSION_1_1 0x10001
MISMATCH_CHECK(QEMU_PSCI_0_2_RET_TOS_MIGRATION_NOT_REQUIRED, PSCI_0_2_TOS_MP);
MISMATCH_CHECK(QEMU_PSCI_0_2_RET_VERSION_0_2,
@@ -847,7 +847,7 @@ int kvm_arch_init_vcpu(CPUState *cs)
cpu->kvm_init_features[0] |= 1 << KVM_ARM_VCPU_POWER_OFF;
}
if (kvm_check_extension(cs->kvm_state, KVM_CAP_ARM_PSCI_0_2)) {
- cpu->psci_version = 2;
+ cpu->psci_version = QEMU_PSCI_VERSION_0_2;
cpu->kvm_init_features[0] |= 1 << KVM_ARM_VCPU_PSCI_0_2;
}
if (!arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) {
@@ -57,7 +57,7 @@ arm_softmmu_ss.add(files(
'arm-powerctl.c',
'machine.c',
'monitor.c',
- 'psci.c',
+ 'smccc.c',
))
subdir('hvf')
@@ -778,9 +778,9 @@ void HELPER(pre_hvc)(CPUARMState *env)
bool secure = false;
bool undef;
- if (arm_is_psci_call(cpu, EXCP_HVC)) {
- /* If PSCI is enabled and this looks like a valid PSCI call then
- * that overrides the architecturally mandated HVC behaviour.
+ if (arm_is_smccc_call(cpu, EXCP_HVC)) {
+ /* If SMCCC is supported then that overrides the architecturally
+ * mandated HVC behaviour.
*/
return;
}
@@ -826,24 +826,21 @@ void HELPER(pre_smc)(CPUARMState *env, uint32_t syndrome)
* -> 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 SMC Trap to EL2 SMCCC-compliant Call
* 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 SMC Trap to EL2 SMCCC-compliant Call
* 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 SMC Trap to EL2 SMCCC-compliant Call
* Conduit not SMC Undef insn Undef insn
*/
@@ -881,10 +878,10 @@ void HELPER(pre_smc)(CPUARMState *env, uint32_t syndrome)
}
/* Catch the two remaining "Undef insn" cases of the previous table:
- * - PSCI conduit is SMC but we don't have a valid PCSI call,
+ * - SMCCC conduit is SMC but we don't have a valid SMCCC-compliant call,
* - We don't have EL3 or SMD is set.
*/
- if (!arm_is_psci_call(cpu, EXCP_SMC) &&
+ if (!arm_is_smccc_call(cpu, EXCP_SMC) &&
(smd || !arm_feature(env, ARM_FEATURE_EL3))) {
raise_exception(env, EXCP_UDEF, syn_uncategorized(),
exception_target_el(env));
similarity index 76%
rename from target/arm/psci.c
rename to target/arm/smccc.c
@@ -25,61 +25,11 @@
#include "internals.h"
#include "arm-powerctl.h"
-bool arm_is_psci_call(ARMCPU *cpu, int excp_type)
-{
- /* Return true if the r0/x0 value indicates a PSCI call and
- * the exception type matches the configured PSCI conduit. This is
- * called before the SMC/HVC instruction is executed, to decide whether
- * we should treat it as a PSCI call or with the architecturally
- * defined behaviour for an SMC or HVC (which might be UNDEF or trap
- * to EL2 or to EL3).
- */
- CPUARMState *env = &cpu->env;
- uint64_t param = is_a64(env) ? env->xregs[0] : env->regs[0];
-
- switch (excp_type) {
- case EXCP_HVC:
- if (cpu->psci_conduit != QEMU_PSCI_CONDUIT_HVC) {
- return false;
- }
- break;
- case EXCP_SMC:
- if (cpu->psci_conduit != QEMU_PSCI_CONDUIT_SMC) {
- return false;
- }
- break;
- default:
- return false;
- }
-
- switch (param) {
- case QEMU_PSCI_0_2_FN_PSCI_VERSION:
- case QEMU_PSCI_0_2_FN_MIGRATE_INFO_TYPE:
- case QEMU_PSCI_0_2_FN_AFFINITY_INFO:
- case QEMU_PSCI_0_2_FN64_AFFINITY_INFO:
- case QEMU_PSCI_0_2_FN_SYSTEM_RESET:
- case QEMU_PSCI_0_2_FN_SYSTEM_OFF:
- case QEMU_PSCI_0_1_FN_CPU_ON:
- case QEMU_PSCI_0_2_FN_CPU_ON:
- case QEMU_PSCI_0_2_FN64_CPU_ON:
- case QEMU_PSCI_0_1_FN_CPU_OFF:
- case QEMU_PSCI_0_2_FN_CPU_OFF:
- case QEMU_PSCI_0_1_FN_CPU_SUSPEND:
- case QEMU_PSCI_0_2_FN_CPU_SUSPEND:
- case QEMU_PSCI_0_2_FN64_CPU_SUSPEND:
- case QEMU_PSCI_0_1_FN_MIGRATE:
- case QEMU_PSCI_0_2_FN_MIGRATE:
- return true;
- default:
- return false;
- }
-}
-
-void arm_handle_psci_call(ARMCPU *cpu)
+void arm_handle_smccc_call(ARMCPU *cpu)
{
/*
* This function partially implements the logic for dispatching Power State
- * Coordination Interface (PSCI) calls (as described in ARM DEN 0022B.b),
+ * Coordination Interface (PSCI) calls (as described in ARM DEN 0022D.b),
* to the extent required for bringing up and taking down secondary cores,
* and for handling reset and poweroff requests.
* Additional information about the calling convention used is available in
@@ -102,7 +52,7 @@ void arm_handle_psci_call(ARMCPU *cpu)
}
if ((param[0] & QEMU_PSCI_0_2_64BIT) && !is_a64(env)) {
- ret = QEMU_PSCI_RET_INVALID_PARAMS;
+ ret = SMCCC_RET_NOT_SUPPORTED;
goto err;
}
@@ -111,7 +61,7 @@ void arm_handle_psci_call(ARMCPU *cpu)
ARMCPU *target_cpu;
case QEMU_PSCI_0_2_FN_PSCI_VERSION:
- ret = QEMU_PSCI_0_2_RET_VERSION_0_2;
+ 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 */
@@ -196,8 +146,37 @@ void arm_handle_psci_call(ARMCPU *cpu)
case QEMU_PSCI_0_2_FN_MIGRATE:
ret = QEMU_PSCI_RET_NOT_SUPPORTED;
break;
+ case QEMU_PSCI_1_0_FN_PSCI_FEATURES:
+ switch (param[1]) {
+ case QEMU_PSCI_0_2_FN_PSCI_VERSION:
+ case QEMU_PSCI_0_2_FN_MIGRATE_INFO_TYPE:
+ case QEMU_PSCI_0_2_FN_AFFINITY_INFO:
+ case QEMU_PSCI_0_2_FN64_AFFINITY_INFO:
+ case QEMU_PSCI_0_2_FN_SYSTEM_RESET:
+ case QEMU_PSCI_0_2_FN_SYSTEM_OFF:
+ case QEMU_PSCI_0_1_FN_CPU_ON:
+ case QEMU_PSCI_0_2_FN_CPU_ON:
+ case QEMU_PSCI_0_2_FN64_CPU_ON:
+ case QEMU_PSCI_0_1_FN_CPU_OFF:
+ case QEMU_PSCI_0_2_FN_CPU_OFF:
+ case QEMU_PSCI_0_1_FN_CPU_SUSPEND:
+ case QEMU_PSCI_0_2_FN_CPU_SUSPEND:
+ case QEMU_PSCI_0_2_FN64_CPU_SUSPEND:
+ case QEMU_PSCI_0_1_FN_MIGRATE:
+ case QEMU_PSCI_0_2_FN_MIGRATE:
+ case QEMU_PSCI_1_0_FN_PSCI_FEATURES:
+ if (!(param[1] & QEMU_PSCI_0_2_64BIT) || is_a64(env)) {
+ ret = 0;
+ break;
+ }
+ /* fallthrough */
+ default:
+ ret = QEMU_PSCI_RET_NOT_SUPPORTED;
+ break;
+ }
+ break;
default:
- g_assert_not_reached();
+ ret = SMCCC_RET_NOT_SUPPORTED;
}
err:
Support the latest PSCI on TCG and HVF. It has optional functions and none of them are implemented. Unimplemented functions now return NOT_SUPPORTED, which automatically makes TCG compliant to SMC Calling Convention 1.0. HVF had already complied to SMCCC 1.0 for the compatibility with Windows and this change eliminates the inconsistency between TCG and HVF. Signed-off-by: Akihiko Odaki <akihiko.odaki@gmail.com> --- hw/arm/boot.c | 12 +++-- target/arm/cpu.c | 5 +- target/arm/helper.c | 4 +- target/arm/hvf/hvf.c | 33 +++++++++++-- target/arm/internals.h | 29 +++++++++-- target/arm/kvm-consts.h | 8 ++- target/arm/kvm64.c | 2 +- target/arm/meson.build | 2 +- target/arm/op_helper.c | 19 +++----- target/arm/{psci.c => smccc.c} | 89 +++++++++++++--------------------- 10 files changed, 116 insertions(+), 87 deletions(-) rename target/arm/{psci.c => smccc.c} (76%)