Index: sys/dev/efidev/efirt.c =================================================================== --- sys/dev/efidev/efirt.c +++ sys/dev/efidev/efirt.c @@ -34,6 +34,7 @@ #include #include +#include #include #include #include @@ -41,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -95,6 +97,7 @@ static int efi_enter(void); static void efi_leave(void); +static int _efi_reset_system(enum efi_reset); static int efi_status_to_errno(efi_status status) @@ -106,6 +109,10 @@ } static struct mtx efi_lock; +static SYSCTL_NODE(_hw, OID_AUTO, efi, CTLFLAG_RWTUN, NULL, "EFI"); +static bool efi_poweroff = true; +SYSCTL_BOOL(_hw_efi, OID_AUTO, poweroff, CTLFLAG_RWTUN, &efi_poweroff, 0, + "If true, use EFI runtime services to shutdown in preference to ACPI"); static bool efi_is_in_map(struct efi_md *map, int ndesc, int descsz, vm_offset_t addr) @@ -126,6 +133,19 @@ return (false); } +static void +efi_shutdown_final(void *dummy __unused, int howto) +{ + + /* + * On some systems, ACPI S5 is missing or does not function properly. + * When present, shutdown via EFI Runtime Services instead, unless + * disabled. + */ + if ((howto & RB_POWEROFF) != 0 && efi_poweroff) + (void)_efi_reset_system(EFI_RESET_SHUTDOWN); +} + static int efi_init(void) { @@ -214,6 +234,12 @@ } #endif + /* + * We use SHUTDOWN_PRI_LAST - 1 to trigger after IPMI, but before ACPI. + */ + EVENTHANDLER_REGISTER(shutdown_final, efi_shutdown_final, NULL, + SHUTDOWN_PRI_LAST - 1); + return (0); } @@ -410,8 +436,8 @@ return (error); } -int -efi_reset_system(void) +static int +_efi_reset_system(enum efi_reset type) { struct efirt_callinfo ec; @@ -420,7 +446,7 @@ bzero(&ec, sizeof(ec)); ec.ec_name = "rt_reset"; ec.ec_argcnt = 4; - ec.ec_arg1 = (uintptr_t)EFI_RESET_WARM; + ec.ec_arg1 = (uintptr_t)type; ec.ec_arg2 = (uintptr_t)0; ec.ec_arg3 = (uintptr_t)0; ec.ec_arg4 = (uintptr_t)NULL; @@ -428,6 +454,13 @@ return (efi_call(&ec)); } +int +efi_reset_system(void) +{ + + return (_efi_reset_system(EFI_RESET_WARM)); +} + static int efi_set_time_locked(struct efi_tm *tm) { Index: sys/dev/ipmi/ipmi.c =================================================================== --- sys/dev/ipmi/ipmi.c +++ sys/dev/ipmi/ipmi.c @@ -938,14 +938,14 @@ } else if (!on) (void)ipmi_set_watchdog(sc, 0); /* - * Power cycle the system off using IPMI. We use last - 1 since we don't + * Power cycle the system off using IPMI. We use last - 2 since we don't * handle all the other kinds of reboots. We'll let others handle them. * We only try to do this if the BMC supports the Chassis device. */ if (sc->ipmi_dev_support & IPMI_ADS_CHASSIS) { device_printf(dev, "Establishing power cycle handler\n"); sc->ipmi_power_cycle_tag = EVENTHANDLER_REGISTER(shutdown_final, - ipmi_power_cycle, sc, SHUTDOWN_PRI_LAST - 1); + ipmi_power_cycle, sc, SHUTDOWN_PRI_LAST - 2); } } Index: sys/sys/efi.h =================================================================== --- sys/sys/efi.h +++ sys/sys/efi.h @@ -43,7 +43,8 @@ enum efi_reset { EFI_RESET_COLD, - EFI_RESET_WARM + EFI_RESET_WARM, + EFI_RESET_SHUTDOWN, }; typedef uint16_t efi_char;