logind,systemctl: add reboot to EFI firmware support

This commit is contained in:
Jan Janssen 2015-04-03 18:03:06 +02:00 committed by Lennart Poettering
parent 3d1137ac2b
commit 5bdf22430e
9 changed files with 224 additions and 1 deletions

View File

@ -526,6 +526,18 @@
</listitem>
</varlistentry>
<varlistentry>
<term><option>--firmware-setup</option></term>
<listitem>
<para>When used with the <command>reboot</command> command,
indicate to the system's firmware to boot into setup
mode. Note that this is currently only supported on some EFI
systems and only if the system was booted in EFI
mode.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--plain</option></term>

View File

@ -92,7 +92,7 @@ _systemctl () {
local -A OPTS=(
[STANDALONE]='--all -a --reverse --after --before --defaults --fail --ignore-dependencies --failed --force -f --full -l --global
--help -h --no-ask-password --no-block --no-legend --no-pager --no-reload --no-wall
--quiet -q --privileged -P --system --user --version --runtime --recursive -r'
--quiet -q --privileged -P --system --user --version --runtime --recursive -r --firmware-setup'
[ARG]='--host -H --kill-who --property -p --signal -s --type -t --state --root'
)

View File

@ -384,5 +384,6 @@ _arguments -s \
{-P,--privileged}'[Acquire privileges before execution]' \
{-n+,--lines=}'[Journal entries to show]:number of entries' \
{-o+,--output=}'[Change journal output mode]:modes:_sd_outputmodes' \
'--firmware-setup[Tell the firmware to show the setup menu on next boot]' \
'--plain[When used with list-dependencies, print output as a list]' \
'*::systemctl command:_systemctl_command'

View File

@ -38,6 +38,7 @@
#include "bus-common-errors.h"
#include "udev-util.h"
#include "selinux-util.h"
#include "efivars.h"
#include "logind.h"
int manager_get_session_from_creds(Manager *m, sd_bus_message *message, const char *name, sd_bus_error *error, Session **ret) {
@ -1850,6 +1851,104 @@ static int method_can_hybrid_sleep(sd_bus *bus, sd_bus_message *message, void *u
error);
}
static int property_get_reboot_to_firmware_setup(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
int r;
assert(bus);
assert(reply);
assert(userdata);
r = efi_get_reboot_to_firmware();
if (r < 0 && r != -EOPNOTSUPP)
return r;
return sd_bus_message_append(reply, "b", r > 0);
}
static int method_set_reboot_to_firmware_setup(
sd_bus *bus,
sd_bus_message *message,
void *userdata,
sd_bus_error *error) {
int b, r;
int interactive;
Manager *m = userdata;
assert(bus);
assert(message);
assert(m);
r = sd_bus_message_read(message, "bb", &b, &interactive);
if (r < 0)
return r;
r = bus_verify_polkit_async(message,
CAP_SYS_ADMIN,
"org.freedesktop.login1.set-reboot-to-firmware-setup",
interactive,
UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
return r;
if (r == 0)
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
r = efi_set_reboot_to_firmware(b);
if (r < 0)
return r;
return sd_bus_reply_method_return(message, NULL);
}
static int method_can_reboot_to_firmware_setup(
sd_bus *bus,
sd_bus_message *message,
void *userdata,
sd_bus_error *error) {
int r;
bool challenge;
const char *result;
Manager *m = userdata;
assert(bus);
assert(message);
assert(m);
r = efi_reboot_to_firmware_supported();
if (r == -EOPNOTSUPP)
return sd_bus_reply_method_return(message, "s", "na");
else if (r < 0)
return r;
r = bus_test_polkit(message,
CAP_SYS_ADMIN,
"org.freedesktop.login1.set-reboot-to-firmware-setup",
UID_INVALID,
&challenge,
error);
if (r < 0)
return r;
if (r > 0)
result = "yes";
else if (challenge)
result = "challenge";
else
result = "no";
return sd_bus_reply_method_return(message, "s", result);
}
static int method_inhibit(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
const char *who, *why, *what, *mode;
@ -1970,6 +2069,7 @@ const sd_bus_vtable manager_vtable[] = {
SD_BUS_PROPERTY("KillOnlyUsers", "as", NULL, offsetof(Manager, kill_only_users), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("KillExcludeUsers", "as", NULL, offsetof(Manager, kill_exclude_users), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("KillUserProcesses", "b", NULL, offsetof(Manager, kill_user_processes), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RebootToFirmwareSetup", "b", property_get_reboot_to_firmware_setup, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
@ -2023,6 +2123,8 @@ const sd_bus_vtable manager_vtable[] = {
SD_BUS_METHOD("CanHibernate", NULL, "s", method_can_hibernate, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CanHybridSleep", NULL, "s", method_can_hybrid_sleep, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Inhibit", "ssss", "h", method_inhibit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CanRebootToFirmwareSetup", NULL, "s", method_can_reboot_to_firmware_setup, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetRebootToFirmwareSetup", "bb", NULL, method_set_reboot_to_firmware_setup, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_SIGNAL("SessionNew", "so", 0),
SD_BUS_SIGNAL("SessionRemoved", "so", 0),

View File

@ -128,6 +128,14 @@
send_interface="org.freedesktop.login1.Manager"
send_member="CanHybridSleep"/>
<allow send_destination="org.freedesktop.login1"
send_interface="org.freedesktop.login1.Manager"
send_member="CanRebootToFirmwareSetup"/>
<allow send_destination="org.freedesktop.login1"
send_interface="org.freedesktop.login1.Manager"
send_member="SetRebootToFirmwareSetup"/>
<allow send_destination="org.freedesktop.login1"
send_interface="org.freedesktop.login1.Manager"
send_member="AttachDevice"/>

View File

@ -290,4 +290,14 @@
</defaults>
</action>
<action id="org.freedesktop.login1.set-reboot-to-firmware-setup">
<_description>Allow indication to the firmware to boot to setup interface</_description>
<_message>Authentication is required to indicate to the firmware to boot to setup interface.</_message>
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
<allow_active>auth_admin_keep</allow_active>
</defaults>
</action>
</policyconfig>

View File

@ -25,6 +25,7 @@
#include "util.h"
#include "utf8.h"
#include "virt.h"
#include "efivars.h"
#ifdef ENABLE_EFI
@ -37,6 +38,7 @@
#define MBR_TYPE_EFI_PARTITION_TABLE_HEADER 0x02
#define END_DEVICE_PATH_TYPE 0x7f
#define END_ENTIRE_DEVICE_PATH_SUBTYPE 0xff
#define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001
struct boot_option {
uint32_t attr;
@ -93,6 +95,76 @@ int is_efi_secure_boot_setup_mode(void) {
return read_flag("SetupMode");
}
int efi_reboot_to_firmware_supported(void) {
int r;
size_t s;
uint64_t b;
_cleanup_free_ void *v = NULL;
if (!is_efi_boot() || detect_container(NULL) > 0)
return -EOPNOTSUPP;
r = efi_get_variable(EFI_VENDOR_GLOBAL, "OsIndicationsSupported", NULL, &v, &s);
if (r < 0)
return r;
else if (s != sizeof(uint64_t))
return -EINVAL;
b = *(uint64_t *)v;
b &= EFI_OS_INDICATIONS_BOOT_TO_FW_UI;
return b > 0 ? 0 : -EOPNOTSUPP;
}
static int get_os_indications(uint64_t *os_indication) {
int r;
size_t s;
_cleanup_free_ void *v = NULL;
r = efi_reboot_to_firmware_supported();
if (r < 0)
return r;
r = efi_get_variable(EFI_VENDOR_GLOBAL, "OsIndications", NULL, &v, &s);
if (r < 0)
return r;
else if (s != sizeof(uint64_t))
return -EINVAL;
*os_indication = *(uint64_t *)v;
return 0;
}
int efi_get_reboot_to_firmware(void) {
int r;
uint64_t b;
r = get_os_indications(&b);
if (r < 0)
return r;
return !!(b & EFI_OS_INDICATIONS_BOOT_TO_FW_UI);
}
int efi_set_reboot_to_firmware(bool value) {
int r;
uint64_t b, b_new;
r = get_os_indications(&b);
if (r < 0)
return r;
if (value)
b_new = b | EFI_OS_INDICATIONS_BOOT_TO_FW_UI;
else
b_new = b & ~EFI_OS_INDICATIONS_BOOT_TO_FW_UI;
/* Avoid writing to efi vars store if we can due to firmware bugs. */
if (b != b_new)
return efi_set_variable(EFI_VENDOR_GLOBAL, "OsIndications", &b_new, sizeof(uint64_t));
return 0;
}
int efi_get_variable(
sd_id128_t vendor,
const char *name,

View File

@ -35,6 +35,9 @@
bool is_efi_boot(void);
int is_efi_secure_boot(void);
int is_efi_secure_boot_setup_mode(void);
int efi_reboot_to_firmware_supported(void);
int efi_get_reboot_to_firmware(void);
int efi_set_reboot_to_firmware(bool value);
int efi_get_variable(sd_id128_t vendor, const char *name, uint32_t *attribute, void **value, size_t *size);
int efi_set_variable(sd_id128_t vendor, const char *name, const void *value, size_t size);

View File

@ -68,6 +68,7 @@
#include "bus-common-errors.h"
#include "mkdir.h"
#include "dropin.h"
#include "efivars.h"
static char **arg_types = NULL;
static char **arg_states = NULL;
@ -132,6 +133,7 @@ static char *arg_host = NULL;
static unsigned arg_lines = 10;
static OutputMode arg_output = OUTPUT_SHORT;
static bool arg_plain = false;
static bool arg_firmware_setup = false;
static bool original_stdout_is_tty;
@ -2928,6 +2930,12 @@ static int start_special(sd_bus *bus, char **args) {
return -EPERM;
}
if (arg_firmware_setup) {
r = efi_set_reboot_to_firmware(true);
if (r < 0)
return log_error_errno(r, "Cannot indicate to EFI to boot into setup mode: %m");
}
if (a == ACTION_REBOOT && args[1]) {
r = update_reboot_param_file(args[1]);
if (r < 0)
@ -5971,6 +5979,7 @@ static void systemctl_help(void) {
" -o --output=STRING Change journal output mode (short, short-iso,\n"
" short-precise, short-monotonic, verbose,\n"
" export, json, json-pretty, json-sse, cat)\n"
" --firmware-setup Tell the firmware to show the setup menu on next boot\n"
" --plain Print unit dependencies as a list instead of a tree\n\n"
"Unit Commands:\n"
" list-units [PATTERN...] List loaded units\n"
@ -6150,6 +6159,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
ARG_STATE,
ARG_JOB_MODE,
ARG_PRESET_MODE,
ARG_FIRMWARE_SETUP,
};
static const struct option options[] = {
@ -6192,6 +6202,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
{ "state", required_argument, NULL, ARG_STATE },
{ "recursive", no_argument, NULL, 'r' },
{ "preset-mode", required_argument, NULL, ARG_PRESET_MODE },
{ "firmware-setup", no_argument, NULL, ARG_FIRMWARE_SETUP },
{}
};
@ -6432,6 +6443,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
arg_plain = true;
break;
case ARG_FIRMWARE_SETUP:
arg_firmware_setup = true;
break;
case ARG_STATE: {
const char *word, *state;
size_t size;