Skip to content

Commit 3e251af

Browse files
dwmw2oupton
authored andcommitted
arm64: Use SYSTEM_OFF2 PSCI call to power off for hibernate
The PSCI v1.3 specification adds support for a SYSTEM_OFF2 function which is analogous to ACPI S4 state. This will allow hosting environments to determine that a guest is hibernated rather than just powered off, and handle that state appropriately on subsequent launches. Since commit 60c0d45 ("efi/arm64: use UEFI for system reset and poweroff") the EFI shutdown method is deliberately preferred over PSCI or other methods. So register a SYS_OFF_MODE_POWER_OFF handler which *only* handles the hibernation, leaving the original PSCI SYSTEM_OFF as a last resort via the legacy pm_power_off function pointer. The hibernation code already exports a system_entering_hibernation() function which is be used by the higher-priority handler to check for hibernation. That existing function just returns the value of a static boolean variable from hibernate.c, which was previously only set in the hibernation_platform_enter() code path. Set the same flag in the simpler code path around the call to kernel_power_off() too. An alternative way to hook SYSTEM_OFF2 into the hibernation code would be to register a platform_hibernation_ops structure with an ->enter() method which makes the new SYSTEM_OFF2 call. But that would have the unwanted side-effect of making hibernation take a completely different code path in hibernation_platform_enter(), invoking a lot of special dpm callbacks. Another option might be to add a new SYS_OFF_MODE_HIBERNATE mode, with fallback to SYS_OFF_MODE_POWER_OFF. Or to use the sys_off_data to indicate whether the power off is for hibernation. But this version works and is relatively simple. Signed-off-by: David Woodhouse <[email protected]> Acked-by: Rafael J. Wysocki <[email protected]> Acked-by: Catalin Marinas <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Oliver Upton <[email protected]>
1 parent 94f985c commit 3e251af

File tree

2 files changed

+49
-1
lines changed

2 files changed

+49
-1
lines changed

drivers/firmware/psci/psci.c

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ struct psci_0_1_function_ids get_psci_0_1_function_ids(void)
7878

7979
static u32 psci_cpu_suspend_feature;
8080
static bool psci_system_reset2_supported;
81+
static bool psci_system_off2_hibernate_supported;
8182

8283
static inline bool psci_has_ext_power_state(void)
8384
{
@@ -333,6 +334,36 @@ static void psci_sys_poweroff(void)
333334
invoke_psci_fn(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0);
334335
}
335336

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+
336367
static int psci_features(u32 psci_func_id)
337368
{
338369
return invoke_psci_fn(PSCI_1_0_FN_PSCI_FEATURES,
@@ -364,6 +395,7 @@ static const struct {
364395
PSCI_ID_NATIVE(1_1, SYSTEM_RESET2),
365396
PSCI_ID(1_1, MEM_PROTECT),
366397
PSCI_ID_NATIVE(1_1, MEM_PROTECT_CHECK_RANGE),
398+
PSCI_ID_NATIVE(1_3, SYSTEM_OFF2),
367399
};
368400

369401
static int psci_debugfs_read(struct seq_file *s, void *data)
@@ -525,6 +557,18 @@ static void __init psci_init_system_reset2(void)
525557
psci_system_reset2_supported = true;
526558
}
527559

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+
528572
static void __init psci_init_system_suspend(void)
529573
{
530574
int ret;
@@ -655,6 +699,7 @@ static int __init psci_probe(void)
655699
psci_init_cpu_suspend();
656700
psci_init_system_suspend();
657701
psci_init_system_reset2();
702+
psci_init_system_off2();
658703
kvm_init_hyp_services();
659704
}
660705

kernel/power/hibernate.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -685,8 +685,11 @@ static void power_down(void)
685685
}
686686
fallthrough;
687687
case HIBERNATION_SHUTDOWN:
688-
if (kernel_can_power_off())
688+
if (kernel_can_power_off()) {
689+
entering_platform_hibernation = true;
689690
kernel_power_off();
691+
entering_platform_hibernation = false;
692+
}
690693
break;
691694
}
692695
kernel_halt();

0 commit comments

Comments
 (0)