mirror of
https://github.com/torvalds/linux.git
synced 2024-12-12 06:02:38 +00:00
efi/x86: Add a quirk to support command line arguments on Dell EFI firmware
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. This was verified with firmware revision 2.15.0 on a Dell Precision T3620 by Jacobo Pantoja. 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. Signed-off-by: Arvind Sankar <nivedita@alum.mit.edu> Reported-by: Jacobo Pantoja <jacobopantoja@gmail.com> Link: https://lore.kernel.org/linux-efi/20200907170021.GA2284449@rani.riverdale.lan/ Link: https://lore.kernel.org/r/20200914213535.933454-2-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
This commit is contained in:
parent
c1df5e0c57
commit
4a568ce29d
@ -230,6 +230,102 @@ 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;
|
||||
|
||||
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;
|
||||
|
||||
efi_warn_once(FW_BUG "LoadOptions is an EFI_LOAD_OPTION descriptor\n");
|
||||
efi_warn_once(FW_BUG "Using OptionalData as a workaround\n");
|
||||
|
||||
*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.
|
||||
@ -239,12 +335,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--) {
|
||||
|
@ -708,6 +708,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)(
|
||||
@ -750,6 +779,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);
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user