* kvm-arm64/psci-1.3: : PSCI v1.3 support, courtesy of David Woodhouse : : Bump KVM's PSCI implementation up to v1.3, with the added bonus of : implementing the SYSTEM_OFF2 call. Like other system-scoped PSCI calls, : this gets relayed to userspace for further processing with a new : KVM_SYSTEM_EVENT_SHUTDOWN flag. : : As an added bonus, implement client-side support for hibernation with : the SYSTEM_OFF2 call. arm64: Use SYSTEM_OFF2 PSCI call to power off for hibernate KVM: arm64: nvhe: Pass through PSCI v1.3 SYSTEM_OFF2 call KVM: selftests: Add test for PSCI SYSTEM_OFF2 KVM: arm64: Add support for PSCI v1.2 and v1.3 KVM: arm64: Add PSCI v1.3 SYSTEM_OFF2 function for hibernation firmware/psci: Add definitions for PSCI v1.3 specification
Unified · Split
| @@ -6855,6 +6855,10 @@ the first `ndata` items (possibly zero) of the data array are valid. · 6854 unmodified lines | |||
| 6855 | the guest issued a SYSTEM_RESET2 call according to v1.1 of the PSCI | 6855 | the guest issued a SYSTEM_RESET2 call according to v1.1 of the PSCI |
| 6856 | specification. | 6856 | specification. |
| 6857 | 6857 | ||
| 6858 | - for arm64, data[0] is set to KVM_SYSTEM_EVENT_SHUTDOWN_FLAG_PSCI_OFF2 | ||
| 6859 | if the guest issued a SYSTEM_OFF2 call according to v1.3 of the PSCI | ||
| 6860 | specification. | ||
| 6861 | |||
| 6858 | - for RISC-V, data[0] is set to the value of the second argument of the | 6862 | - for RISC-V, data[0] is set to the value of the second argument of the |
| 6859 | ``sbi_system_reset`` call. | 6863 | ``sbi_system_reset`` call. |
| 6860 | 6864 | ||
| @@ -6888,6 +6892,12 @@ either: · 27 unmodified lines | |||
| 6888 | - Deny the guest request to suspend the VM. See ARM DEN0022D.b 5.19.2 | 6892 | - Deny the guest request to suspend the VM. See ARM DEN0022D.b 5.19.2 |
| 6889 | "Caller responsibilities" for possible return values. | 6893 | "Caller responsibilities" for possible return values. |
| 6890 | 6894 | ||
| 6895 | Hibernation using the PSCI SYSTEM_OFF2 call is enabled when PSCI v1.3 | ||
| 6896 | is enabled. If a guest invokes the PSCI SYSTEM_OFF2 function, KVM will | ||
| 6897 | exit to userspace with the KVM_SYSTEM_EVENT_SHUTDOWN event type and with | ||
| 6898 | data[0] set to KVM_SYSTEM_EVENT_SHUTDOWN_FLAG_PSCI_OFF2. The only | ||
| 6899 | supported hibernate type for the SYSTEM_OFF2 function is HIBERNATE_OFF. | ||
| 6900 | |||
| 6891 | :: | 6901 | :: |
| 6892 | 6902 | ||
| 6893 | /* KVM_EXIT_IOAPIC_EOI */ | 6903 | /* KVM_EXIT_IOAPIC_EOI */ |
| @@ -484,6 +484,12 @@ enum { · 483 unmodified lines | |||
| 484 | */ | 484 | */ |
| 485 | #define KVM_SYSTEM_EVENT_RESET_FLAG_PSCI_RESET2 (1ULL << 0) | 485 | #define KVM_SYSTEM_EVENT_RESET_FLAG_PSCI_RESET2 (1ULL << 0) |
| 486 | 486 | ||
| 487 | /* | ||
| 488 | * Shutdown caused by a PSCI v1.3 SYSTEM_OFF2 call. | ||
| 489 | * Valid only when the system event has a type of KVM_SYSTEM_EVENT_SHUTDOWN. | ||
| 490 | */ | ||
| 491 | #define KVM_SYSTEM_EVENT_SHUTDOWN_FLAG_PSCI_OFF2 (1ULL << 0) | ||
| 492 | |||
| 487 | /* run->fail_entry.hardware_entry_failure_reason codes. */ | 493 | /* run->fail_entry.hardware_entry_failure_reason codes. */ |
| 488 | #define KVM_EXIT_FAIL_ENTRY_CPU_UNSUPPORTED (1ULL << 0) | 494 | #define KVM_EXIT_FAIL_ENTRY_CPU_UNSUPPORTED (1ULL << 0) |
| 489 | 495 | ||
| @@ -265,6 +265,8 @@ static unsigned long psci_1_0_handler(u64 func_id, struct kvm_cpu_context *host_ · 264 unmodified lines | |||
| 265 | case PSCI_1_0_FN_PSCI_FEATURES: | 265 | case PSCI_1_0_FN_PSCI_FEATURES: |
| 266 | case PSCI_1_0_FN_SET_SUSPEND_MODE: | 266 | case PSCI_1_0_FN_SET_SUSPEND_MODE: |
| 267 | case PSCI_1_1_FN64_SYSTEM_RESET2: | 267 | case PSCI_1_1_FN64_SYSTEM_RESET2: |
| 268 | case PSCI_1_3_FN_SYSTEM_OFF2: | ||
| 269 | case PSCI_1_3_FN64_SYSTEM_OFF2: | ||
| 268 | return psci_forward(host_ctxt); | 270 | return psci_forward(host_ctxt); |
| 269 | case PSCI_1_0_FN64_SYSTEM_SUSPEND: | 271 | case PSCI_1_0_FN64_SYSTEM_SUSPEND: |
| 270 | return psci_system_suspend(func_id, host_ctxt); | 272 | return psci_system_suspend(func_id, host_ctxt); |
| @@ -575,6 +575,8 @@ int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) · 574 unmodified lines | |||
| 575 | case KVM_ARM_PSCI_0_2: | 575 | case KVM_ARM_PSCI_0_2: |
| 576 | case KVM_ARM_PSCI_1_0: | 576 | case KVM_ARM_PSCI_1_0: |
| 577 | case KVM_ARM_PSCI_1_1: | 577 | case KVM_ARM_PSCI_1_1: |
| 578 | case KVM_ARM_PSCI_1_2: | ||
| 579 | case KVM_ARM_PSCI_1_3: | ||
| 578 | if (!wants_02) | 580 | if (!wants_02) |
| 579 | return -EINVAL; | 581 | return -EINVAL; |
| 580 | vcpu->kvm->arch.psci_version = val; | 582 | vcpu->kvm->arch.psci_version = val; |
| @@ -194,6 +194,12 @@ static void kvm_psci_system_off(struct kvm_vcpu *vcpu) · 193 unmodified lines | |||
| 194 | kvm_prepare_system_event(vcpu, KVM_SYSTEM_EVENT_SHUTDOWN, 0); | 194 | kvm_prepare_system_event(vcpu, KVM_SYSTEM_EVENT_SHUTDOWN, 0); |
| 195 | } | 195 | } |
| 196 | 196 | ||
| 197 | static void kvm_psci_system_off2(struct kvm_vcpu *vcpu) | ||
| 198 | { | ||
| 199 | kvm_prepare_system_event(vcpu, KVM_SYSTEM_EVENT_SHUTDOWN, | ||
| 200 | KVM_SYSTEM_EVENT_SHUTDOWN_FLAG_PSCI_OFF2); | ||
| 201 | } | ||
| 202 | |||
| 197 | static void kvm_psci_system_reset(struct kvm_vcpu *vcpu) | 203 | static void kvm_psci_system_reset(struct kvm_vcpu *vcpu) |
| 198 | { | 204 | { |
| 199 | kvm_prepare_system_event(vcpu, KVM_SYSTEM_EVENT_RESET, 0); | 205 | kvm_prepare_system_event(vcpu, KVM_SYSTEM_EVENT_RESET, 0); |
| @@ -322,7 +328,7 @@ static int kvm_psci_1_x_call(struct kvm_vcpu *vcpu, u32 minor) · 122 unmodified lines | |||
| 322 | 328 | ||
| 323 | switch(psci_fn) { | 329 | switch(psci_fn) { |
| 324 | case PSCI_0_2_FN_PSCI_VERSION: | 330 | case PSCI_0_2_FN_PSCI_VERSION: |
| 325 | val = minor == 0 ? KVM_ARM_PSCI_1_0 : KVM_ARM_PSCI_1_1; | 331 | val = PSCI_VERSION(1, minor); |
| 326 | break; | 332 | break; |
| 327 | case PSCI_1_0_FN_PSCI_FEATURES: | 333 | case PSCI_1_0_FN_PSCI_FEATURES: |
| 328 | arg = smccc_get_arg1(vcpu); | 334 | arg = smccc_get_arg1(vcpu); |
| @@ -358,6 +364,11 @@ static int kvm_psci_1_x_call(struct kvm_vcpu *vcpu, u32 minor) · 29 unmodified lines | |||
| 358 | if (minor >= 1) | 364 | if (minor >= 1) |
| 359 | val = 0; | 365 | val = 0; |
| 360 | break; | 366 | break; |
| 367 | case PSCI_1_3_FN_SYSTEM_OFF2: | ||
| 368 | case PSCI_1_3_FN64_SYSTEM_OFF2: | ||
| 369 | if (minor >= 3) | ||
| 370 | val = PSCI_1_3_OFF_TYPE_HIBERNATE_OFF; | ||
| 371 | break; | ||
| 361 | } | 372 | } |
| 362 | break; | 373 | break; |
| 363 | case PSCI_1_0_FN_SYSTEM_SUSPEND: | 374 | case PSCI_1_0_FN_SYSTEM_SUSPEND: |
| @@ -392,6 +403,33 @@ static int kvm_psci_1_x_call(struct kvm_vcpu *vcpu, u32 minor) · 28 unmodified lines | |||
| 392 | break; | 403 | break; |
| 393 | } | 404 | } |
| 394 | break; | 405 | break; |
| 406 | case PSCI_1_3_FN_SYSTEM_OFF2: | ||
| 407 | kvm_psci_narrow_to_32bit(vcpu); | ||
| 408 | fallthrough; | ||
| 409 | case PSCI_1_3_FN64_SYSTEM_OFF2: | ||
| 410 | if (minor < 3) | ||
| 411 | break; | ||
| 412 | |||
| 413 | arg = smccc_get_arg1(vcpu); | ||
| 414 | /* | ||
| 415 | * SYSTEM_OFF2 defaults to HIBERNATE_OFF if arg1 is zero. arg2 | ||
| 416 | * must be zero. | ||
| 417 | */ | ||
| 418 | if ((arg && arg != PSCI_1_3_OFF_TYPE_HIBERNATE_OFF) || | ||
| 419 | smccc_get_arg2(vcpu) != 0) { | ||
| 420 | val = PSCI_RET_INVALID_PARAMS; | ||
| 421 | break; | ||
| 422 | } | ||
| 423 | kvm_psci_system_off2(vcpu); | ||
| 424 | /* | ||
| 425 | * We shouldn't be going back to the guest after receiving a | ||
| 426 | * SYSTEM_OFF2 request. Preload a return value of | ||
| 427 | * INTERNAL_FAILURE should userspace ignore the exit and resume | ||
| 428 | * the vCPU. | ||
| 429 | */ | ||
| 430 | val = PSCI_RET_INTERNAL_FAILURE; | ||
| 431 | ret = 0; | ||
| 432 | break; | ||
| 395 | default: | 433 | default: |
| 396 | return kvm_psci_0_2_call(vcpu); | 434 | return kvm_psci_0_2_call(vcpu); |
| 397 | } | 435 | } |
| @@ -449,6 +487,10 @@ int kvm_psci_call(struct kvm_vcpu *vcpu) · 51 unmodified lines | |||
| 449 | } | 487 | } |
| 450 | 488 | ||
| 451 | switch (version) { | 489 | switch (version) { |
| 490 | case KVM_ARM_PSCI_1_3: | ||
| 491 | return kvm_psci_1_x_call(vcpu, 3); | ||
| 492 | case KVM_ARM_PSCI_1_2: | ||
| 493 | return kvm_psci_1_x_call(vcpu, 2); | ||
| 452 | case KVM_ARM_PSCI_1_1: | 494 | case KVM_ARM_PSCI_1_1: |
| 453 | return kvm_psci_1_x_call(vcpu, 1); | 495 | return kvm_psci_1_x_call(vcpu, 1); |
| 454 | case KVM_ARM_PSCI_1_0: | 496 | case KVM_ARM_PSCI_1_0: |
| @@ -78,6 +78,7 @@ struct psci_0_1_function_ids get_psci_0_1_function_ids(void) · 77 unmodified lines | |||
| 78 | 78 | ||
| 79 | static u32 psci_cpu_suspend_feature; | 79 | static u32 psci_cpu_suspend_feature; |
| 80 | static bool psci_system_reset2_supported; | 80 | static bool psci_system_reset2_supported; |
| 81 | static bool psci_system_off2_hibernate_supported; | ||
| 81 | 82 | ||
| 82 | static inline bool psci_has_ext_power_state(void) | 83 | static inline bool psci_has_ext_power_state(void) |
| 83 | { | 84 | { |
| @@ -333,6 +334,36 @@ static void psci_sys_poweroff(void) · 249 unmodified lines | |||
| 333 | invoke_psci_fn(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0); | 334 | invoke_psci_fn(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0); |
| 334 | } | 335 | } |
| 335 | 336 | ||
| 337 | #ifdef CONFIG_HIBERNATION | ||
| 338 | static int psci_sys_hibernate(struct sys_off_data *data) | ||
| 339 | { | ||
| 340 | /* | ||
| 341 | * If no hibernate type is specified SYSTEM_OFF2 defaults to selecting | ||
| 342 | * HIBERNATE_OFF. | ||
| 343 | * | ||
| 344 | * There are hypervisors in the wild that do not align with the spec and | ||
| 345 | * reject calls that explicitly provide a hibernate type. For | ||
| 346 | * compatibility with these nonstandard implementations, pass 0 as the | ||
| 347 | * type. | ||
| 348 | */ | ||
| 349 | if (system_entering_hibernation()) | ||
| 350 | invoke_psci_fn(PSCI_FN_NATIVE(1_3, SYSTEM_OFF2), 0, 0, 0); | ||
| 351 | return NOTIFY_DONE; | ||
| 352 | } | ||
| 353 | |||
| 354 | static int __init psci_hibernate_init(void) | ||
| 355 | { | ||
| 356 | if (psci_system_off2_hibernate_supported) { | ||
| 357 | /* Higher priority than EFI shutdown, but only for hibernate */ | ||
| 358 | register_sys_off_handler(SYS_OFF_MODE_POWER_OFF, | ||
| 359 | SYS_OFF_PRIO_FIRMWARE + 2, | ||
| 360 | psci_sys_hibernate, NULL); | ||
| 361 | } | ||
| 362 | return 0; | ||
| 363 | } | ||
| 364 | subsys_initcall(psci_hibernate_init); | ||
| 365 | #endif | ||
| 366 | |||
| 336 | static int psci_features(u32 psci_func_id) | 367 | static int psci_features(u32 psci_func_id) |
| 337 | { | 368 | { |
| 338 | return invoke_psci_fn(PSCI_1_0_FN_PSCI_FEATURES, | 369 | return invoke_psci_fn(PSCI_1_0_FN_PSCI_FEATURES, |
| @@ -364,6 +395,7 @@ static const struct { · 25 unmodified lines | |||
| 364 | PSCI_ID_NATIVE(1_1, SYSTEM_RESET2), | 395 | PSCI_ID_NATIVE(1_1, SYSTEM_RESET2), |
| 365 | PSCI_ID(1_1, MEM_PROTECT), | 396 | PSCI_ID(1_1, MEM_PROTECT), |
| 366 | PSCI_ID_NATIVE(1_1, MEM_PROTECT_CHECK_RANGE), | 397 | PSCI_ID_NATIVE(1_1, MEM_PROTECT_CHECK_RANGE), |
| 398 | PSCI_ID_NATIVE(1_3, SYSTEM_OFF2), | ||
| 367 | }; | 399 | }; |
| 368 | 400 | ||
| 369 | static int psci_debugfs_read(struct seq_file *s, void *data) | 401 | static int psci_debugfs_read(struct seq_file *s, void *data) |
| @@ -525,6 +557,18 @@ static void __init psci_init_system_reset2(void) · 155 unmodified lines | |||
| 525 | psci_system_reset2_supported = true; | 557 | psci_system_reset2_supported = true; |
| 526 | } | 558 | } |
| 527 | 559 | ||
| 560 | static void __init psci_init_system_off2(void) | ||
| 561 | { | ||
| 562 | int ret; | ||
| 563 | |||
| 564 | ret = psci_features(PSCI_FN_NATIVE(1_3, SYSTEM_OFF2)); | ||
| 565 | if (ret < 0) | ||
| 566 | return; | ||
| 567 | |||
| 568 | if (ret & PSCI_1_3_OFF_TYPE_HIBERNATE_OFF) | ||
| 569 | psci_system_off2_hibernate_supported = true; | ||
| 570 | } | ||
| 571 | |||
| 528 | static void __init psci_init_system_suspend(void) | 572 | static void __init psci_init_system_suspend(void) |
| 529 | { | 573 | { |
| 530 | int ret; | 574 | int ret; |
| @@ -655,6 +699,7 @@ static int __init psci_probe(void) · 124 unmodified lines | |||
| 655 | psci_init_cpu_suspend(); | 699 | psci_init_cpu_suspend(); |
| 656 | psci_init_system_suspend(); | 700 | psci_init_system_suspend(); |
| 657 | psci_init_system_reset2(); | 701 | psci_init_system_reset2(); |
| 702 | psci_init_system_off2(); | ||
| 658 | kvm_init_hyp_services(); | 703 | kvm_init_hyp_services(); |
| 659 | } | 704 | } |
| 660 | 705 | ||
| @@ -14,8 +14,10 @@ · 13 unmodified lines | |||
| 14 | #define KVM_ARM_PSCI_0_2 PSCI_VERSION(0, 2) | 14 | #define KVM_ARM_PSCI_0_2 PSCI_VERSION(0, 2) |
| 15 | #define KVM_ARM_PSCI_1_0 PSCI_VERSION(1, 0) | 15 | #define KVM_ARM_PSCI_1_0 PSCI_VERSION(1, 0) |
| 16 | #define KVM_ARM_PSCI_1_1 PSCI_VERSION(1, 1) | 16 | #define KVM_ARM_PSCI_1_1 PSCI_VERSION(1, 1) |
| 17 | #define KVM_ARM_PSCI_1_2 PSCI_VERSION(1, 2) | ||
| 18 | #define KVM_ARM_PSCI_1_3 PSCI_VERSION(1, 3) | ||
| 17 | 19 | ||
| 18 | #define KVM_ARM_PSCI_LATEST KVM_ARM_PSCI_1_1 | 20 | #define KVM_ARM_PSCI_LATEST KVM_ARM_PSCI_1_3 |
| 19 | 21 | ||
| 20 | static inline int kvm_psci_version(struct kvm_vcpu *vcpu) | 22 | static inline int kvm_psci_version(struct kvm_vcpu *vcpu) |
| 21 | { | 23 | { |
| @@ -59,6 +59,7 @@ · 58 unmodified lines | |||
| 59 | #define PSCI_1_1_FN_SYSTEM_RESET2 PSCI_0_2_FN(18) | 59 | #define PSCI_1_1_FN_SYSTEM_RESET2 PSCI_0_2_FN(18) |
| 60 | #define PSCI_1_1_FN_MEM_PROTECT PSCI_0_2_FN(19) | 60 | #define PSCI_1_1_FN_MEM_PROTECT PSCI_0_2_FN(19) |
| 61 | #define PSCI_1_1_FN_MEM_PROTECT_CHECK_RANGE PSCI_0_2_FN(20) | 61 | #define PSCI_1_1_FN_MEM_PROTECT_CHECK_RANGE PSCI_0_2_FN(20) |
| 62 | #define PSCI_1_3_FN_SYSTEM_OFF2 PSCI_0_2_FN(21) | ||
| 62 | 63 | ||
| 63 | #define PSCI_1_0_FN64_CPU_DEFAULT_SUSPEND PSCI_0_2_FN64(12) | 64 | #define PSCI_1_0_FN64_CPU_DEFAULT_SUSPEND PSCI_0_2_FN64(12) |
| 64 | #define PSCI_1_0_FN64_NODE_HW_STATE PSCI_0_2_FN64(13) | 65 | #define PSCI_1_0_FN64_NODE_HW_STATE PSCI_0_2_FN64(13) |
| @@ -68,6 +69,7 @@ · 3 unmodified lines | |||
| 68 | 69 | ||
| 69 | #define PSCI_1_1_FN64_SYSTEM_RESET2 PSCI_0_2_FN64(18) | 70 | #define PSCI_1_1_FN64_SYSTEM_RESET2 PSCI_0_2_FN64(18) |
| 70 | #define PSCI_1_1_FN64_MEM_PROTECT_CHECK_RANGE PSCI_0_2_FN64(20) | 71 | #define PSCI_1_1_FN64_MEM_PROTECT_CHECK_RANGE PSCI_0_2_FN64(20) |
| 72 | #define PSCI_1_3_FN64_SYSTEM_OFF2 PSCI_0_2_FN64(21) | ||
| 71 | 73 | ||
| 72 | /* PSCI v0.2 power state encoding for CPU_SUSPEND function */ | 74 | /* PSCI v0.2 power state encoding for CPU_SUSPEND function */ |
| 73 | #define PSCI_0_2_POWER_STATE_ID_MASK 0xffff | 75 | #define PSCI_0_2_POWER_STATE_ID_MASK 0xffff |
| @@ -100,6 +102,9 @@ · 26 unmodified lines | |||
| 100 | #define PSCI_1_1_RESET_TYPE_SYSTEM_WARM_RESET 0 | 102 | #define PSCI_1_1_RESET_TYPE_SYSTEM_WARM_RESET 0 |
| 101 | #define PSCI_1_1_RESET_TYPE_VENDOR_START 0x80000000U | 103 | #define PSCI_1_1_RESET_TYPE_VENDOR_START 0x80000000U |
| 102 | 104 | ||
| 105 | /* PSCI v1.3 hibernate type for SYSTEM_OFF2 */ | ||
| 106 | #define PSCI_1_3_OFF_TYPE_HIBERNATE_OFF BIT(0) | ||
| 107 | |||
| 103 | /* PSCI version decoding (independent of PSCI version) */ | 108 | /* PSCI version decoding (independent of PSCI version) */ |
| 104 | #define PSCI_VERSION_MAJOR_SHIFT 16 | 109 | #define PSCI_VERSION_MAJOR_SHIFT 16 |
| 105 | #define PSCI_VERSION_MINOR_MASK \ | 110 | #define PSCI_VERSION_MINOR_MASK \ |
| @@ -685,8 +685,11 @@ static void power_down(void) · 684 unmodified lines | |||
| 685 | } | 685 | } |
| 686 | fallthrough; | 686 | fallthrough; |
| 687 | case HIBERNATION_SHUTDOWN: | 687 | case HIBERNATION_SHUTDOWN: |
| 688 | if (kernel_can_power_off()) | 688 | if (kernel_can_power_off()) { |
| 689 | entering_platform_hibernation = true; | ||
| 689 | kernel_power_off(); | 690 | kernel_power_off(); |
| 691 | entering_platform_hibernation = false; | ||
| 692 | } | ||
| 690 | break; | 693 | break; |
| 691 | } | 694 | } |
| 692 | kernel_halt(); | 695 | kernel_halt(); |
| @@ -54,6 +54,15 @@ static uint64_t psci_system_suspend(uint64_t entry_addr, uint64_t context_id) · 53 unmodified lines | |||
| 54 | return res.a0; | 54 | return res.a0; |
| 55 | } | 55 | } |
| 56 | 56 | ||
| 57 | static uint64_t psci_system_off2(uint64_t type, uint64_t cookie) | ||
| 58 | { | ||
| 59 | struct arm_smccc_res res; | ||
| 60 | |||
| 61 | smccc_hvc(PSCI_1_3_FN64_SYSTEM_OFF2, type, cookie, 0, 0, 0, 0, 0, &res); | ||
| 62 | |||
| 63 | return res.a0; | ||
| 64 | } | ||
| 65 | |||
| 57 | static uint64_t psci_features(uint32_t func_id) | 66 | static uint64_t psci_features(uint32_t func_id) |
| 58 | { | 67 | { |
| 59 | struct arm_smccc_res res; | 68 | struct arm_smccc_res res; |
| @@ -188,11 +197,94 @@ static void host_test_system_suspend(void) · 128 unmodified lines | |||
| 188 | kvm_vm_free(vm); | 197 | kvm_vm_free(vm); |
| 189 | } | 198 | } |
| 190 | 199 | ||
| 200 | static void guest_test_system_off2(void) | ||
| 201 | { | ||
| 202 | uint64_t ret; | ||
| 203 | |||
| 204 | /* assert that SYSTEM_OFF2 is discoverable */ | ||
| 205 | GUEST_ASSERT(psci_features(PSCI_1_3_FN_SYSTEM_OFF2) & | ||
| 206 | PSCI_1_3_OFF_TYPE_HIBERNATE_OFF); | ||
| 207 | GUEST_ASSERT(psci_features(PSCI_1_3_FN64_SYSTEM_OFF2) & | ||
| 208 | PSCI_1_3_OFF_TYPE_HIBERNATE_OFF); | ||
| 209 | |||
| 210 | /* With non-zero 'cookie' field, it should fail */ | ||
| 211 | ret = psci_system_off2(PSCI_1_3_OFF_TYPE_HIBERNATE_OFF, 1); | ||
| 212 | GUEST_ASSERT(ret == PSCI_RET_INVALID_PARAMS); | ||
| 213 | |||
| 214 | /* | ||
| 215 | * This would normally never return, so KVM sets the return value | ||
| 216 | * to PSCI_RET_INTERNAL_FAILURE. The test case *does* return, so | ||
| 217 | * that it can test both values for HIBERNATE_OFF. | ||
| 218 | */ | ||
| 219 | ret = psci_system_off2(PSCI_1_3_OFF_TYPE_HIBERNATE_OFF, 0); | ||
| 220 | GUEST_ASSERT(ret == PSCI_RET_INTERNAL_FAILURE); | ||
| 221 | |||
| 222 | /* | ||
| 223 | * Revision F.b of the PSCI v1.3 specification documents zero as an | ||
| 224 | * alias for HIBERNATE_OFF, since that's the value used in earlier | ||
| 225 | * revisions of the spec and some implementations in the field. | ||
| 226 | */ | ||
| 227 | ret = psci_system_off2(0, 1); | ||
| 228 | GUEST_ASSERT(ret == PSCI_RET_INVALID_PARAMS); | ||
| 229 | |||
| 230 | ret = psci_system_off2(0, 0); | ||
| 231 | GUEST_ASSERT(ret == PSCI_RET_INTERNAL_FAILURE); | ||
| 232 | |||
| 233 | GUEST_DONE(); | ||
| 234 | } | ||
| 235 | |||
| 236 | static void host_test_system_off2(void) | ||
| 237 | { | ||
| 238 | struct kvm_vcpu *source, *target; | ||
| 239 | struct kvm_mp_state mps; | ||
| 240 | uint64_t psci_version = 0; | ||
| 241 | int nr_shutdowns = 0; | ||
| 242 | struct kvm_run *run; | ||
| 243 | struct ucall uc; | ||
| 244 | |||
| 245 | setup_vm(guest_test_system_off2, &source, &target); | ||
| 246 | |||
| 247 | vcpu_get_reg(target, KVM_REG_ARM_PSCI_VERSION, &psci_version); | ||
| 248 | |||
| 249 | TEST_ASSERT(psci_version >= PSCI_VERSION(1, 3), | ||
| 250 | "Unexpected PSCI version %lu.%lu", | ||
| 251 | PSCI_VERSION_MAJOR(psci_version), | ||
| 252 | PSCI_VERSION_MINOR(psci_version)); | ||
| 253 | |||
| 254 | vcpu_power_off(target); | ||
| 255 | run = source->run; | ||
| 256 | |||
| 257 | enter_guest(source); | ||
| 258 | while (run->exit_reason == KVM_EXIT_SYSTEM_EVENT) { | ||
| 259 | TEST_ASSERT(run->system_event.type == KVM_SYSTEM_EVENT_SHUTDOWN, | ||
| 260 | "Unhandled system event: %u (expected: %u)", | ||
| 261 | run->system_event.type, KVM_SYSTEM_EVENT_SHUTDOWN); | ||
| 262 | TEST_ASSERT(run->system_event.ndata >= 1, | ||
| 263 | "Unexpected amount of system event data: %u (expected, >= 1)", | ||
| 264 | run->system_event.ndata); | ||
| 265 | TEST_ASSERT(run->system_event.data[0] & KVM_SYSTEM_EVENT_SHUTDOWN_FLAG_PSCI_OFF2, | ||
| 266 | "PSCI_OFF2 flag not set. Flags %llu (expected %llu)", | ||
| 267 | run->system_event.data[0], KVM_SYSTEM_EVENT_SHUTDOWN_FLAG_PSCI_OFF2); | ||
| 268 | |||
| 269 | nr_shutdowns++; | ||
| 270 | |||
| 271 | /* Restart the vCPU */ | ||
| 272 | mps.mp_state = KVM_MP_STATE_RUNNABLE; | ||
| 273 | vcpu_mp_state_set(source, &mps); | ||
| 274 | |||
| 275 | enter_guest(source); | ||
| 276 | } | ||
| 277 | |||
| 278 | TEST_ASSERT(get_ucall(source, &uc) == UCALL_DONE, "Guest did not exit cleanly"); | ||
| 279 | TEST_ASSERT(nr_shutdowns == 2, "Two shutdown events were expected, but saw %d", nr_shutdowns); | ||
| 280 | } | ||
| 281 | |||
| 191 | int main(void) | 282 | int main(void) |
| 192 | { | 283 | { |
| 193 | TEST_REQUIRE(kvm_has_cap(KVM_CAP_ARM_SYSTEM_SUSPEND)); | 284 | TEST_REQUIRE(kvm_has_cap(KVM_CAP_ARM_SYSTEM_SUSPEND)); |
| 194 | 285 | ||
| 195 | host_test_cpu_on(); | 286 | host_test_cpu_on(); |
| 196 | host_test_system_suspend(); | 287 | host_test_system_suspend(); |
| 288 | host_test_system_off2(); | ||
| 197 | return 0; | 289 | return 0; |
| 198 | } | 290 | } |