RE: [RFC PATCH 1/2] efi/x86: Add a quirk to support command line arguments on Dell EFI firmware

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

 



> -----Original Message-----
> From: Arvind Sankar <nivedita@xxxxxxxxxxxx>
> Sent: Saturday, September 12, 2020 12:51
> To: Jacobo Pantoja
> Cc: Limonciello, Mario; Ard Biesheuvel; Peter Jones; linux-efi
> Subject: [RFC PATCH 1/2] efi/x86: Add a quirk to support command line
> arguments on Dell EFI firmware
> 
> 
> [EXTERNAL EMAIL]
> 
> At least some versions of Dell EFI firmware pass the entire
> EFI_LOAD_OPTION descriptor, rather than just the OptionalData part, to
> the loaded image.
> 
> To handle this, add a quirk to check if the options look like a valid
> EFI_LOAD_OPTION descriptor, and if so, use the OptionalData part as the
> command line.

I think it would be useful to document in the commit message the specifics
of at least the failure reported by Jacobo (Precision T3620 FW 2.15.0).

> 
> Signed-off-by: Arvind Sankar <nivedita@xxxxxxxxxxxx>
> Reported-by: Jacobo Pantoja <jacobopantoja@xxxxxxxxx>
> Link: https://lore.kernel.org/linux-
> efi/20200907170021.GA2284449@xxxxxxxxxxxxxxxxxx/
> ---
>  .../firmware/efi/libstub/efi-stub-helper.c    | 99 ++++++++++++++++++-
>  drivers/firmware/efi/libstub/efistub.h        | 31 ++++++
>  drivers/firmware/efi/libstub/file.c           |  5 +-
>  3 files changed, 133 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c
> b/drivers/firmware/efi/libstub/efi-stub-helper.c
> index f735db55adc0..294958ff1ee6 100644
> --- a/drivers/firmware/efi/libstub/efi-stub-helper.c
> +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c
> @@ -238,6 +238,100 @@ efi_status_t efi_parse_options(char const *cmdline)
>  	return EFI_SUCCESS;
>  }
> 
> +/*
> + * The EFI_LOAD_OPTION descriptor has the following layout:
> + *	u32 Attributes;
> + *	u16 FilePathListLength;
> + *	u16 Description[];
> + *	efi_device_path_protocol_t FilePathList[];
> + *	u8 OptionalData[];
> + *
> + * This function validates and unpacks the variable-size data fields.
> + */
> +static
> +bool efi_load_option_unpack(efi_load_option_unpacked_t *dest,
> +			    const efi_load_option_t *src, size_t size)
> +{
> +
> +	const void *pos;
> +	u16 c;
> +	efi_device_path_protocol_t header;
> +	const efi_char16_t *description;
> +	const efi_device_path_protocol_t *file_path_list;

Should re-order to reverse xmas tree order.

> +
> +	if (size < offsetof(efi_load_option_t, variable_data))
> +		return false;
> +	pos = src->variable_data;
> +	size -= offsetof(efi_load_option_t, variable_data);
> +
> +	if ((src->attributes & ~EFI_LOAD_OPTION_MASK) != 0)
> +		return false;
> +
> +	/* Scan description. */
> +	description = pos;
> +	do {
> +		if (size < sizeof(c))
> +			return false;
> +		c = *(const u16 *)pos;
> +		pos += sizeof(c);
> +		size -= sizeof(c);
> +	} while (c != L'\0');
> +
> +	/* Scan file_path_list. */
> +	file_path_list = pos;
> +	do {
> +		if (size < sizeof(header))
> +			return false;
> +		header = *(const efi_device_path_protocol_t *)pos;
> +		if (header.length < sizeof(header))
> +			return false;
> +		if (size < header.length)
> +			return false;
> +		pos += header.length;
> +		size -= header.length;
> +	} while ((header.type != EFI_DEV_END_PATH && header.type !=
> EFI_DEV_END_PATH2) ||
> +		 (header.sub_type != EFI_DEV_END_ENTIRE));
> +	if (pos != (const void *)file_path_list + src->file_path_list_length)
> +		return false;
> +
> +	dest->attributes = src->attributes;
> +	dest->file_path_list_length = src->file_path_list_length;
> +	dest->description = description;
> +	dest->file_path_list = file_path_list;
> +	dest->optional_data_size = size;
> +	dest->optional_data = size ? pos : NULL;
> +
> +	return true;
> +}
> +
> +/*
> + * At least some versions of Dell firmware pass the entire contents of the
> + * Boot#### variable, i.e. the EFI_LOAD_OPTION descriptor, rather than just
> the
> + * OptionalData field.
> + *
> + * Detect this case and extract OptionalData.
> + */
> +void efi_apply_loadoptions_quirk(const void **load_options, int
> *load_options_size)
> +{
> +	const efi_load_option_t *load_option = *load_options;
> +	efi_load_option_unpacked_t load_option_unpacked;
> +
> +	if (!IS_ENABLED(CONFIG_X86))
> +		return;
> +	if (!load_option)
> +		return;
> +	if (*load_options_size < sizeof(*load_option))
> +		return;
> +	if ((load_option->attributes & ~EFI_LOAD_OPTION_BOOT_MASK) != 0)
> +		return;
> +
> +	if (!efi_load_option_unpack(&load_option_unpacked, load_option,
> *load_options_size))
> +		return;
> +

In case this was ever to be attributed to a cause for someone to fail to
boot, it may be useful to drop a pr_debug here that could be easily turned
on.

> +	*load_options = load_option_unpacked.optional_data;
> +	*load_options_size = load_option_unpacked.optional_data_size;
> +}
> +
>  /*
>   * Convert the unicode UEFI command line to ASCII to pass to kernel.
>   * Size of memory allocated return in *cmd_line_len.
> @@ -247,12 +341,15 @@ char *efi_convert_cmdline(efi_loaded_image_t *image, int
> *cmd_line_len)
>  {
>  	const u16 *s2;
>  	unsigned long cmdline_addr = 0;
> -	int options_chars = efi_table_attr(image, load_options_size) / 2;
> +	int options_chars = efi_table_attr(image, load_options_size);
>  	const u16 *options = efi_table_attr(image, load_options);
>  	int options_bytes = 0, safe_options_bytes = 0;  /* UTF-8 bytes */
>  	bool in_quote = false;
>  	efi_status_t status;
> 
> +	efi_apply_loadoptions_quirk((const void **)&options, &options_chars);
> +	options_chars /= sizeof(*options);
> +
>  	if (options) {
>  		s2 = options;
>  		while (options_bytes < COMMAND_LINE_SIZE && options_chars--) {
> diff --git a/drivers/firmware/efi/libstub/efistub.h
> b/drivers/firmware/efi/libstub/efistub.h
> index 85050f5a1b28..589d07acb22d 100644
> --- a/drivers/firmware/efi/libstub/efistub.h
> +++ b/drivers/firmware/efi/libstub/efistub.h
> @@ -688,6 +688,35 @@ union efi_load_file_protocol {
>  	} mixed_mode;
>  };
> 
> +typedef struct {
> +	u32 attributes;
> +	u16 file_path_list_length;
> +	u8 variable_data[];
> +	// efi_char16_t description[];
> +	// efi_device_path_protocol_t file_path_list[];
> +	// u8 optional_data[];
> +} __packed efi_load_option_t;
> +
> +#define EFI_LOAD_OPTION_ACTIVE		0x0001U
> +#define EFI_LOAD_OPTION_FORCE_RECONNECT	0x0002U
> +#define EFI_LOAD_OPTION_HIDDEN		0x0008U
> +#define EFI_LOAD_OPTION_CATEGORY	0x1f00U
> +#define   EFI_LOAD_OPTION_CATEGORY_BOOT	0x0000U
> +#define   EFI_LOAD_OPTION_CATEGORY_APP	0x0100U
> +
> +#define EFI_LOAD_OPTION_BOOT_MASK \
> +	(EFI_LOAD_OPTION_ACTIVE|EFI_LOAD_OPTION_HIDDEN|EFI_LOAD_OPTION_CATEGORY)
> +#define EFI_LOAD_OPTION_MASK
> (EFI_LOAD_OPTION_FORCE_RECONNECT|EFI_LOAD_OPTION_BOOT_MASK)
> +
> +typedef struct {
> +	u32 attributes;
> +	u16 file_path_list_length;
> +	const efi_char16_t *description;
> +	const efi_device_path_protocol_t *file_path_list;
> +	size_t optional_data_size;
> +	const void *optional_data;
> +} efi_load_option_unpacked_t;
> +
>  void efi_pci_disable_bridge_busmaster(void);
> 
>  typedef efi_status_t (*efi_exit_boot_map_processing)(
> @@ -730,6 +759,8 @@ __printf(1, 2) int efi_printk(char const *fmt, ...);
> 
>  void efi_free(unsigned long size, unsigned long addr);
> 
> +void efi_apply_loadoptions_quirk(const void **load_options, int
> *load_options_size);
> +
>  char *efi_convert_cmdline(efi_loaded_image_t *image, int *cmd_line_len);
> 
>  efi_status_t efi_get_memory_map(struct efi_boot_memmap *map);
> diff --git a/drivers/firmware/efi/libstub/file.c
> b/drivers/firmware/efi/libstub/file.c
> index 630caa6b1f4c..4e81c6077188 100644
> --- a/drivers/firmware/efi/libstub/file.c
> +++ b/drivers/firmware/efi/libstub/file.c
> @@ -136,7 +136,7 @@ efi_status_t handle_cmdline_files(efi_loaded_image_t
> *image,
>  				  unsigned long *load_size)
>  {
>  	const efi_char16_t *cmdline = image->load_options;
> -	int cmdline_len = image->load_options_size / 2;
> +	int cmdline_len = image->load_options_size;
>  	unsigned long efi_chunk_size = ULONG_MAX;
>  	efi_file_protocol_t *volume = NULL;
>  	efi_file_protocol_t *file;
> @@ -148,6 +148,9 @@ efi_status_t handle_cmdline_files(efi_loaded_image_t
> *image,
>  	if (!load_addr || !load_size)
>  		return EFI_INVALID_PARAMETER;
> 
> +	efi_apply_loadoptions_quirk((const void **)&cmdline, &cmdline_len);
> +	cmdline_len /= sizeof(*cmdline);
> +
>  	if (IS_ENABLED(CONFIG_X86) && !efi_nochunk)
>  		efi_chunk_size = EFI_READ_CHUNK_SIZE;
> 
> --
> 2.26.2





[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