efirt: When present, attempt to use EFI runtime services to shutdown

PR:		maybe related to 233998 (inconclusive at this time)
Submitted by:	byuu <byuu AT tutanota.com> (previous version)
Reviewed by:	imp
Differential Revision:	https://reviews.freebsd.org/D18506
This commit is contained in:
Conrad Meyer 2018-12-15 05:46:04 +00:00
parent 8763f2174d
commit 26649bb5e8
6 changed files with 75 additions and 11 deletions

View File

@ -26,7 +26,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd August 12, 2018
.Dd December 11, 2018
.Dt EFIRT 9
.Os
.Sh NAME
@ -54,7 +54,7 @@
.Ft int
.Fn efi_get_time_capabilities "struct efi_tmcap *tmcap"
.Ft int
.Fn efi_reset_system "void"
.Fn efi_reset_system "enum efi_reset type"
.Ft int
.Fn efi_set_time "struct efi_tm *tm"
.Ft int
@ -123,7 +123,20 @@ if the time could not be retrieved due to a hardware error.
.Pp
The
.Fn efi_reset_system
function requests a warm reset and reboot of the system.
function requests a reset of the system.
The
.Fa type
argument may be one of the
.Vt enum efi_reset
values:
.Bl -tag -width ".Dv EFI_RESET_SHUTDOWN"
.It Dv EFI_RESET_COLD
Perform a cold reset of the system, and reboot.
.It Dv EFI_RESET_WARM
Perform a warm reset of the system, and reboot.
.It Dv EFI_RESET_SHUTDOWN
Power off the system.
.El
.Pp
The
.Fn efi_set_time
@ -239,7 +252,7 @@ The variable could not be saved due to a hardware error.
.It Dv EROFS
The variable in question is read-only or may not be deleted.
.It Dv EDOOFUS
The varialbe could not be set due to an authentication failure.
The variable could not be set due to an authentication failure.
.It Dv ENOENT
The variable trying to be updated or deleted was not found.
.El

View File

@ -691,6 +691,7 @@ MEM_STATIC U32 ZSTD_window_update(ZSTD_window_t* window,
/* debug functions */
#if 0
MEM_STATIC double ZSTD_fWeight(U32 rawStat)
{
U32 const fp_accuracy = 8;
@ -714,6 +715,7 @@ MEM_STATIC void ZSTD_debugTable(const U32* table, U32 max)
u, table[u], ZSTD_fWeight(sum) - ZSTD_fWeight(table[u]) );
}
}
#endif
#if defined (__cplusplus)
}

View File

@ -53,10 +53,12 @@ MEM_STATIC U32 ZSTD_fracWeight(U32 rawStat)
}
/* debugging function, @return price in bytes */
#if 0
MEM_STATIC double ZSTD_fCost(U32 price)
{
return (double)price / (BITCOST_MULTIPLIER*8);
}
#endif
static void ZSTD_setBasePrices(optState_t* optPtr, int optLevel)
{
@ -852,8 +854,10 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms,
for ( ; pos <= end ; pos++ ) {
U32 const matchPrice = ZSTD_getMatchPrice(offset, pos, optStatePtr, optLevel);
U32 const sequencePrice = literalsPrice + matchPrice;
#if 0
DEBUGLOG(7, "rPos:%u => set initial price : %.2f",
pos, ZSTD_fCost(sequencePrice));
#endif
opt[pos].mlen = pos;
opt[pos].off = offset;
opt[pos].litlen = litlen;
@ -879,18 +883,22 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms,
- ZSTD_litLengthPrice(litlen-1, optStatePtr, optLevel);
assert(price < 1000000000); /* overflow check */
if (price <= opt[cur].price) {
#if 0
DEBUGLOG(7, "cPos:%zi==rPos:%u : better price (%.2f<=%.2f) using literal (ll==%u) (hist:%u,%u,%u)",
inr-istart, cur, ZSTD_fCost(price), ZSTD_fCost(opt[cur].price), litlen,
opt[cur-1].rep[0], opt[cur-1].rep[1], opt[cur-1].rep[2]);
#endif
opt[cur].mlen = 0;
opt[cur].off = 0;
opt[cur].litlen = litlen;
opt[cur].price = price;
memcpy(opt[cur].rep, opt[cur-1].rep, sizeof(opt[cur].rep));
} else {
#if 0
DEBUGLOG(7, "cPos:%zi==rPos:%u : literal would cost more (%.2f>%.2f) (hist:%u,%u,%u)",
inr-istart, cur, ZSTD_fCost(price), ZSTD_fCost(opt[cur].price),
opt[cur].rep[0], opt[cur].rep[1], opt[cur].rep[2]);
#endif
}
}
@ -947,8 +955,10 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms,
int const price = basePrice + ZSTD_getMatchPrice(offset, mlen, optStatePtr, optLevel);
if ((pos > last_pos) || (price < opt[pos].price)) {
#if 0
DEBUGLOG(7, "rPos:%u (ml=%2u) => new better price (%.2f<%.2f)",
pos, mlen, ZSTD_fCost(price), ZSTD_fCost(opt[pos].price));
#endif
while (last_pos < pos) { opt[last_pos+1].price = ZSTD_MAX_PRICE; last_pos++; } /* fill empty positions */
opt[pos].mlen = mlen;
opt[pos].off = offset;
@ -957,8 +967,10 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms,
ZSTD_STATIC_ASSERT(sizeof(opt[pos].rep) == sizeof(repHistory));
memcpy(opt[pos].rep, &repHistory, sizeof(repHistory));
} else {
#if 0
DEBUGLOG(7, "rPos:%u (ml=%2u) => new price is worse (%.2f>=%.2f)",
pos, mlen, ZSTD_fCost(price), ZSTD_fCost(opt[pos].price));
#endif
if (optLevel==0) break; /* early update abort; gets ~+10% speed for about -0.01 ratio loss */
}
} } }

View File

@ -34,6 +34,7 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/efi.h>
#include <sys/eventhandler.h>
#include <sys/kernel.h>
#include <sys/linker.h>
#include <sys/lock.h>
@ -41,6 +42,7 @@ __FBSDID("$FreeBSD$");
#include <sys/mutex.h>
#include <sys/clock.h>
#include <sys/proc.h>
#include <sys/reboot.h>
#include <sys/rwlock.h>
#include <sys/sched.h>
#include <sys/sysctl.h>
@ -57,6 +59,7 @@ __FBSDID("$FreeBSD$");
#include <vm/vm_map.h>
static struct efi_systbl *efi_systbl;
static eventhandler_tag efi_shutdown_tag;
/*
* The following pointers point to tables in the EFI runtime service data pages.
* Care should be taken to make sure that we've properly entered the EFI runtime
@ -106,6 +109,10 @@ efi_status_to_errno(efi_status status)
}
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 power off 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 @@ efi_is_in_map(struct efi_md *map, int ndesc, int descsz, vm_offset_t addr)
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 @@ efi_init(void)
}
#endif
/*
* We use SHUTDOWN_PRI_LAST - 1 to trigger after IPMI, but before ACPI.
*/
efi_shutdown_tag = EVENTHANDLER_REGISTER(shutdown_final,
efi_shutdown_final, NULL, SHUTDOWN_PRI_LAST - 1);
return (0);
}
@ -224,6 +250,8 @@ efi_uninit(void)
/* Most likely disabled by tunable */
if (efi_runtime == NULL)
return;
if (efi_shutdown_tag != NULL)
EVENTHANDLER_DEREGISTER(shutdown_final, efi_shutdown_tag);
efi_destroy_1t1_map();
efi_systbl = NULL;
@ -411,16 +439,24 @@ efi_get_time_capabilities(struct efi_tmcap *tmcap)
}
int
efi_reset_system(void)
efi_reset_system(enum efi_reset type)
{
struct efirt_callinfo ec;
switch (type) {
case EFI_RESET_COLD:
case EFI_RESET_WARM:
case EFI_RESET_SHUTDOWN:
break;
default:
return (EINVAL);
}
if (efi_runtime == NULL)
return (ENXIO);
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;

View File

@ -938,14 +938,14 @@ ipmi_startup(void *arg)
} 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);
}
}

View File

@ -42,8 +42,9 @@
{0xeb9d2d32,0x2d88,0x11d3,0x9a,0x16,{0x00,0x90,0x27,0x3f,0xc1,0x4d}}
enum efi_reset {
EFI_RESET_COLD,
EFI_RESET_WARM
EFI_RESET_COLD = 0,
EFI_RESET_WARM = 1,
EFI_RESET_SHUTDOWN = 2,
};
typedef uint16_t efi_char;
@ -184,7 +185,7 @@ int efi_rt_ok(void);
int efi_get_table(struct uuid *uuid, void **ptr);
int efi_get_time(struct efi_tm *tm);
int efi_get_time_capabilities(struct efi_tmcap *tmcap);
int efi_reset_system(void);
int efi_reset_system(enum efi_reset type);
int efi_set_time(struct efi_tm *tm);
int efi_var_get(uint16_t *name, struct uuid *vendor, uint32_t *attrib,
size_t *datasize, void *data);