Re: [PATCH] efibc: EFI Bootloader Control

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Matt Fleming <matt@xxxxxxxxxxxxxxxxxxx> writes:

> (Sorry for the delay)
No worries.

> On Fri, 01 Apr, at 03:09:51PM, Compostella, Jeremy wrote:
>> This module installs a reboot hook, such that if reboot() is invoked
>> with a string argument NNN, "NNN" is copied to the
>> "LoaderEntryOneShot" EFI variable, to be read by the bootloader. If
>> the string matches one of the boot labels defined in its
>> configuration, the bootloader will boot once to that label.  The
>> "LoaderEntryRebootReason" EFI variable is set with the reboot reason:
>> "reboot", "shutdown", "kernel_panic" or "watchdog".  The bootloader
>> reads this reboot reason and takes particular action according to its
>> policy.
>  
> This commit message is missing some info. In particular, I'd like see
> to which boot loaders support this protocol and why this feature
> should be a kernel driver and cannot be done with the userspace EFI
> variable API.
>
> Yes we've already discussed these bits on the mailing list, but
> recording it for posterity in the commit log is very worthwhile.
Done.

>> Signed-off-by: Jeremy Compostella <jeremy.compostella@xxxxxxxxx>
>> ---
>>  arch/x86/Kconfig               |  16 ++++
>>  arch/x86/platform/efi/Makefile |   1 +
>>  arch/x86/platform/efi/efibc.c  | 164 +++++++++++++++++++++++++++++++++++++++++
>>  3 files changed, 181 insertions(+)
>>  create mode 100644 arch/x86/platform/efi/efibc.c
>  
> Hehe, I suspect I was a little unclear in my previous comments about
> moving this out of drivers/firmware/efi.
>
> arch/x86/platform/efi is not the right place for this kind of stuff
> either, since it contains core x86 EFI support.
>
> That directory is no longer named appropriately, as EFI should not be
> considered a feature of specific platforms - it's the standard
> firmware technology nowadays on x86. I've been meaning to rename it.
I moved it to driver/firmware/efi/.

> But let's ignore the issue of finding the right home for now. I've got
> some reservations about the panic notifier that I've outlined below.
>
>> diff --git a/arch/x86/platform/efi/Makefile b/arch/x86/platform/efi/Makefile
>> index 066619b..05cf769 100644
>> --- a/arch/x86/platform/efi/Makefile
>> +++ b/arch/x86/platform/efi/Makefile
>> @@ -4,3 +4,4 @@ obj-$(CONFIG_EFI) 		+= quirks.o efi.o efi_$(BITS).o efi_stub_$(BITS).o
>>  obj-$(CONFIG_ACPI_BGRT) += efi-bgrt.o
>>  obj-$(CONFIG_EARLY_PRINTK_EFI)	+= early_printk.o
>>  obj-$(CONFIG_EFI_MIXED)		+= efi_thunk_$(BITS).o
>> +obj-$(CONFIG_EFI_BOOTLOADER_CONTROL)	+= efibc.o
>> diff --git a/arch/x86/platform/efi/efibc.c b/arch/x86/platform/efi/efibc.c
>> new file mode 100644
>> index 0000000..8ca8b43
>> --- /dev/null
>> +++ b/arch/x86/platform/efi/efibc.c
>> @@ -0,0 +1,164 @@
>> +/*
>> + * efibc: control EFI bootloaders which obey LoaderEntryOneShot var
>> + * Copyright (c) 2013-2016, Intel Corporation.
>> + *
>> + * This program is free software; you can redistribute it and/or modify it
>> + * under the terms and conditions of the GNU General Public License,
>> + * version 2, as published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope it will be useful, but WITHOUT
>> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
>> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
>> + * more details.
>> + */
>> +
>> +#include <linux/efi.h>
>> +#include <linux/module.h>
>> +#include <linux/moduleparam.h>
>> +#include <linux/reboot.h>
>> +#include <linux/kexec.h>
>> +#include <linux/slab.h>
>> +#include <linux/nls.h>
>> +
>> +#define MODULE_NAME "efibc"
>> +
>> +static const efi_guid_t LOADER_GUID =
>> +	EFI_GUID(0x4a67b082, 0x0a4c, 0x41cf,
>> +		 0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f);
>
> Please move this guid to include/linux/efi.h.
Done.  But I'm not sure if EFI_LOADER_GUID constant name is
appropriate.  However, I'd like to avoid naming it after Kernelflinger
or Gummiboot.

>> +
>> +static const char REBOOT_TARGET[] = "LoaderEntryOneShot";
>> +static const char REBOOT_REASON[] = "LoaderEntryRebootReason";
>> +
>> +static const char * const WDT_SOURCE_PREFIX[] = {
>> +	"Kernel Watchdog", "Watchdog", "softlockup", "Software Watchdog"
>> +};
>
> Please use lowercase for names, unless you're using #define.
> Additionally, because REBOOT_* are only used in one place you could
> just use the strings directly instead of making them file-global.
Done.

>> +static void efibc_str_to_str16(const char *str, wchar_t *str16)
>> +{
>
> s/wchar_t/efi_char16_t/
>
>> +	size_t size = strlen(str) + 1;
>> +
>> +	utf8s_to_utf16s(str, size, UTF16_LITTLE_ENDIAN, str16,
>> +			size * sizeof(*str16) / sizeof(*str));
>> +	str16[size - 1] = '\0';
>
> 	for (i = 0; i < strlen(str); i++)
> 		str16[i] = str[i];
>
> 	str16[i] = '\0';
>
> right? Are there some scenarios where utf8s_to_utf16s() works and the
> above loop doesn't? We have to do this conversion in the efivarfs file
> system too, so does that need updating?
Done.  I do not see any scenario where ASCII is not enough.

>> +static struct efivar_entry *efibc_get_var_entry(const char *name)
>> +{
>> +	wchar_t name16[strlen(name) + 1];
>> +	struct efivar_entry *entry;
>> +
>> +	efibc_str_to_str16(name, name16);
>> +
>> +	efivar_entry_iter_begin();
>> +	entry = efivar_entry_find(name16, LOADER_GUID,
>> +				  &efivar_sysfs_list, false);
>
> Hmm... there's an interesting consequence of using efivar_sysfs_list
> here, and that is that if someone creates this variable manually via
> the efivarfs file system you won't see it on efivar_sysfs_list.
>
> Of course, when you restart the machine you will see it then, so it's
> probably not a big deal for this driver that only cares about reboot;
> I just wanted to make you aware.
I removed this function (not needed anymore).

>> +	efivar_entry_iter_end();
>> +
>> +	return entry;
>> +}
>> +
>> +static void efibc_set_variable(const char *name, const char *value)
>> +{
>> +	u32 attributes = EFI_VARIABLE_NON_VOLATILE
>> +		| EFI_VARIABLE_BOOTSERVICE_ACCESS
>> +		| EFI_VARIABLE_RUNTIME_ACCESS;
>> +	wchar_t name16[strlen(name) + 1];
>> +	wchar_t value16[strlen(value) + 1];
>> +	int ret;
>
> Please put the attribute value on a separate line from the
> declaration.
Done.

>> +
>> +	efibc_str_to_str16(name, name16);
>> +	efibc_str_to_str16(value, value16);
>> +
>> +	ret = efivar_entry_set_safe(name16, LOADER_GUID, attributes, true,
>> +				    sizeof(value16), value16);
>
> If it is always OK to block when calling SetVariable() you don't want
> to be using efivar_entry_set_safe(). The regular efivar_entry_set()
> will suffice.
>
> The *_safe() version is special because it will handle being called in
> a context where it is not allowed to block.


> Oh but I just realised you call this from a panic handler, so it's not
> safe to always block.
>
> In which case, yes, using efivar_entry_set_safe() is correct but you
> cannot always pass 'true' for the @block parameter.
>
>> +	if (ret)
>> +		pr_err(MODULE_NAME ": failed to set %s EFI variable: 0x%x\n",
>> +		       name, ret);
>> +}
>
> You can redefine pr_fmt to avoid having to specify MODULE_NAME here,
> e.g.
>
> #define pr_fmt(fmt)	"efifbc: "
Done.

>> +
>> +static int efibc_reboot_notifier_call(struct notifier_block *notifier,
>> +				      unsigned long what, void *data)
>> +{
>> +	char *reason = what == SYS_RESTART ? "reboot" : "shutdown";
>> +
>
> Let's rename 'what' to 'event' to provide an idea of the kind of data
> it contains. This line needs breaking down too.
>
> How about,
>
> 	const char *reason = "shutdown";
>
> 	if (event == SYS_RESTART)
> 		reason = "reboot";
>
> Which makes it a little clearer that we treat the SYS_RESTART event as
> being special and lump all other events under "shutdown".
Done.

>> +}
>> +
>> +static bool is_watchdog_source(const char *str)
>> +{
>> +	size_t i;
>> +
>> +	for (i = 0; i < ARRAY_SIZE(WDT_SOURCE_PREFIX); i++)
>> +		if (!strncmp(str, WDT_SOURCE_PREFIX[i],
>> +			     strlen(WDT_SOURCE_PREFIX[i])))
>> +			return true;
>> +
>> +	return false;
>> +}
>> +
>> +static int efibc_panic_notifier_call(struct notifier_block *notifier,
>> +				     unsigned long what, void *data)
>> +{
>> +	char *reason;
>> +
>> +	/* If the reboot reason has already been supplied, keep it. */
>> +	if (efibc_get_var_entry(REBOOT_REASON))
>> +		return NOTIFY_DONE;
>
> I'm not sure this is the best idea. If, in the process of doing a
> normal reboot, we panic, wouldn't the user rather see the panic
> reason and not the reboot reason?
Not needed anymore.

>> +
>> +	if (data && is_watchdog_source((char *)data))
>> +		reason = "watchdog";
>> +	else
>> +		reason = "kernel_panic";
>> +
>> +	efibc_set_variable(REBOOT_REASON, reason);
>
> Checking the panic string prefix for known watchdog strings is a bit
> hacky. For example, this won't work with the hard lockup detector.
> What happens if someone adds a new watchdog driver?
>
> On second thought, I'm not at all sure of the value of this panic
> notifier. As I understood it, the proposition of this driver was that
> it allows the user to pass information via the kernel to the boot
> loader upon reboot. This data is strictly opaque to the kernel, and
> I'm OK with that.
>
> However, with this panic handler, you're doing something different.
> You're passing kernel strings directly (albeit translated to
> "watchdog" or "kernel_panic") to the boot loader. You're creating an
> ABI between the kernel and boot loaders.
>
> Besides which, we already have code to store information in EFI
> variables on panic; the efi-pstore driver.
>
> Sorry, but NAK on this part of the patch.
Agreed.  I removed all the panic handler related code.  Though, I'd
like to keep the LoaderEntryRebootReason filled with "shutdown" or
"reboot" if that's okay with you.

Thanks,

Jérémy
-- 
One Emacs to rule them all
--
To unsubscribe from this list: send the line "unsubscribe linux-efi" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux