[PATCH v3] arm64: move efi_reboot to restart handler

From: Krzysztof Adamski
Date: Mon Feb 07 2022 - 10:30:52 EST


On EFI enabled arm64 systems, efi_reboot was called before
do_kernel_restart, completely omitting the reset_handlers functionality.
By registering efi_reboot as part of the chain with elevated priority,
we make it run before the default handler but still allow plugging in
other handlers.

This is useful in two scenarios:
- Abusing the restart handler as a notification mechanism similar to
reboot notifiers, but working in wider spectrum of cases, (notably
also at emergency_restart) and just before reset. This is useful for
things like in-kernel pwrseq_emmc, or any other place where it is
beneficial for an *external* component to know about the restart, but
not performing the reset itself.
- Providing higher priority reset handler, where resetting the SoC is
not enough, and reset of the whole board should be orchestrated by
some external component, that the firmware is not aware of (like an
FPGA or some PMIC).

This change moves the conditional efi_reboot() call from arm64 specific
machine_restart() function to an arm efi initialization code where it is
registered as a restart handler with a very high (but not the highest)
priority, leaving a small window of opportunity for some code to be run
prior to the actual reset, like on other architectures.

The restart handlers mechanism is proven to work reliably as we depend
on it heavily on other platforms so there is almost no cost for this
change but it adds flexibility and unifies our infrastructure with other
ports (where one can depend on restart handlers working).

Signed-off-by: Krzysztof Adamski <krzysztof.adamski@xxxxxxxxx>
---

While previous attempts got mixed feedback, my explanations did not seen
counterarguments so I am trying another round:

Changes in v3:
- Bump the priority much higher, to almost maximal value
- Add a comment discouraging from registering higher prio handlers
- Update the commit message to contain some more justifications

Changes in v2:
- Register the handler in EFI code, instead of arm64 setup.c
- Remove the contdition from the handler - it should be run in all
cases when it is registered
- Bump the priority to 130 to make it completly obious this should be
run before PSCI (which has priority of 129)

arch/arm64/kernel/process.c | 7 -------
drivers/firmware/efi/arm-runtime.c | 25 +++++++++++++++++++++++++
2 files changed, 25 insertions(+), 7 deletions(-)

diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
index 5369e649fa79..b86ef77bb0c8 100644
--- a/arch/arm64/kernel/process.c
+++ b/arch/arm64/kernel/process.c
@@ -130,13 +130,6 @@ void machine_restart(char *cmd)
local_irq_disable();
smp_send_stop();

- /*
- * UpdateCapsule() depends on the system being reset via
- * ResetSystem().
- */
- if (efi_enabled(EFI_RUNTIME_SERVICES))
- efi_reboot(reboot_mode, NULL);
-
/* Now call the architecture specific reboot code. */
do_kernel_restart(cmd);

diff --git a/drivers/firmware/efi/arm-runtime.c b/drivers/firmware/efi/arm-runtime.c
index 3359ae2adf24..b29f47783bbc 100644
--- a/drivers/firmware/efi/arm-runtime.c
+++ b/drivers/firmware/efi/arm-runtime.c
@@ -80,6 +80,28 @@ static bool __init efi_virtmap_init(void)
return true;
}

+static int efi_restart(struct notifier_block *nb, unsigned long action,
+ void *data)
+{
+ /*
+ * UpdateCapsule() depends on the system being reset via
+ * ResetSystem().
+ */
+ efi_reboot(reboot_mode, NULL);
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block efi_restart_nb = {
+ .notifier_call = efi_restart,
+ /**
+ * If you are running UEFI based system, you most certainly should let
+ * efi_reboot() do a reset for you. If you think you know better, we
+ * leave you a window of opportunity here by not using maximal priority.
+ */
+ .priority = 251,
+};
+
/*
* Enable the UEFI Runtime Services if all prerequisites are in place, i.e.,
* non-early mapping of the UEFI system table and virtual mappings for all
@@ -148,6 +170,9 @@ static int __init arm_enable_runtime_services(void)
efi_native_runtime_setup();
set_bit(EFI_RUNTIME_SERVICES, &efi.flags);

+ if (IS_ENABLED(CONFIG_ARM64))
+ register_restart_handler(&efi_restart_nb);
+
return 0;
}
early_initcall(arm_enable_runtime_services);
--
2.34.1