Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/efi/efi.git

This commit is contained in:
Stephen Rothwell 2025-01-14 15:29:39 +11:00
commit ec454ce37f
25 changed files with 680 additions and 721 deletions

View File

@ -250,6 +250,9 @@ static inline u32 efi64_convert_status(efi_status_t status)
#define __efi64_argmap_allocate_pool(type, size, buffer) \
((type), (size), efi64_zero_upper(buffer))
#define __efi64_argmap_locate_handle_buffer(type, proto, key, num, buf) \
((type), (proto), (key), efi64_zero_upper(num), efi64_zero_upper(buf))
#define __efi64_argmap_create_event(type, tpl, f, c, event) \
((type), (tpl), (f), (c), efi64_zero_upper(event))

View File

@ -54,13 +54,11 @@
#include <asm/uv/uv.h>
static unsigned long efi_systab_phys __initdata;
static unsigned long uga_phys = EFI_INVALID_TABLE_ADDR;
static unsigned long efi_runtime, efi_nr_tables;
unsigned long efi_fw_vendor, efi_config_table;
static const efi_config_table_type_t arch_tables[] __initconst = {
{UGA_IO_PROTOCOL_GUID, &uga_phys, "UGA" },
#ifdef CONFIG_X86_UV
{UV_SYSTEM_TABLE_GUID, &uv_systab_phys, "UVsystab" },
#endif
@ -72,7 +70,6 @@ static const unsigned long * const efi_tables[] = {
&efi.acpi20,
&efi.smbios,
&efi.smbios3,
&uga_phys,
#ifdef CONFIG_X86_UV
&uv_systab_phys,
#endif
@ -891,13 +888,6 @@ bool efi_is_table_address(unsigned long phys_addr)
return false;
}
char *efi_systab_show_arch(char *str)
{
if (uga_phys != EFI_INVALID_TABLE_ADDR)
str += sprintf(str, "UGA=0x%lx\n", uga_phys);
return str;
}
#define EFI_FIELD(var) efi_ ## var
#define EFI_ATTR_SHOW(name) \

View File

@ -148,9 +148,6 @@ static ssize_t systab_show(struct kobject *kobj,
if (efi.smbios != EFI_INVALID_TABLE_ADDR)
str += sprintf(str, "SMBIOS=0x%lx\n", efi.smbios);
if (IS_ENABLED(CONFIG_X86))
str = efi_systab_show_arch(str);
return str - buf;
}

View File

@ -89,7 +89,12 @@ lib-$(CONFIG_LOONGARCH) += loongarch.o loongarch-stub.o
CFLAGS_arm32-stub.o := -DTEXT_OFFSET=$(TEXT_OFFSET)
zboot-obj-$(CONFIG_RISCV) := lib-clz_ctz.o lib-ashldi3.o
zboot-obj-y := zboot-decompress-gzip.o
CFLAGS_zboot-decompress-gzip.o += -I$(srctree)/lib/zlib_inflate
zboot-obj-$(CONFIG_KERNEL_ZSTD) := zboot-decompress-zstd.o lib-xxhash.o
CFLAGS_zboot-decompress-zstd.o += -I$(srctree)/lib/zstd
zboot-obj-$(CONFIG_RISCV) += lib-clz_ctz.o lib-ashldi3.o
lib-$(CONFIG_EFI_ZBOOT) += zboot.o $(zboot-obj-y)
lib-$(CONFIG_UNACCEPTED_MEMORY) += unaccepted_memory.o bitmap.o find.o

View File

@ -47,9 +47,10 @@ bool __pure __efi_soft_reserve_enabled(void)
*/
efi_status_t efi_parse_options(char const *cmdline)
{
size_t len;
char *buf __free(efi_pool) = NULL;
efi_status_t status;
char *str, *buf;
size_t len;
char *str;
if (!cmdline)
return EFI_SUCCESS;
@ -102,7 +103,6 @@ efi_status_t efi_parse_options(char const *cmdline)
efi_parse_option_graphics(val + strlen("efifb:"));
}
}
efi_bs_call(free_pool, buf);
return EFI_SUCCESS;
}
@ -250,7 +250,7 @@ static efi_status_t efi_measure_tagged_event(unsigned long load_addr,
u64, const union efistub_event *);
struct { u32 hash_log_extend_event; } mixed_mode;
} method;
struct efistub_measured_event *evt;
struct efistub_measured_event *evt __free(efi_pool) = NULL;
int size = struct_size(evt, tagged_event.tagged_event_data,
events[event].event_data_len);
efi_guid_t tcg2_guid = EFI_TCG2_PROTOCOL_GUID;
@ -312,7 +312,6 @@ static efi_status_t efi_measure_tagged_event(unsigned long load_addr,
status = efi_fn_call(&method, hash_log_extend_event, protocol, 0,
load_addr, load_size, &evt->event_data);
efi_bs_call(free_pool, evt);
if (status == EFI_SUCCESS)
return EFI_SUCCESS;

View File

@ -10,6 +10,7 @@
*/
#include <linux/efi.h>
#include <linux/screen_info.h>
#include <asm/efi.h>
#include "efistub.h"
@ -53,25 +54,16 @@ void __weak free_screen_info(struct screen_info *si)
static struct screen_info *setup_graphics(void)
{
efi_guid_t gop_proto = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
efi_status_t status;
unsigned long size;
void **gop_handle = NULL;
struct screen_info *si = NULL;
struct screen_info *si, tmp = {};
size = 0;
status = efi_bs_call(locate_handle, EFI_LOCATE_BY_PROTOCOL,
&gop_proto, NULL, &size, gop_handle);
if (status == EFI_BUFFER_TOO_SMALL) {
si = alloc_screen_info();
if (!si)
return NULL;
status = efi_setup_gop(si, &gop_proto, size);
if (status != EFI_SUCCESS) {
free_screen_info(si);
return NULL;
}
}
if (efi_setup_gop(&tmp) != EFI_SUCCESS)
return NULL;
si = alloc_screen_info();
if (!si)
return NULL;
*si = tmp;
return si;
}
@ -112,8 +104,8 @@ static u32 get_supported_rt_services(void)
efi_status_t efi_handle_cmdline(efi_loaded_image_t *image, char **cmdline_ptr)
{
char *cmdline __free(efi_pool) = NULL;
efi_status_t status;
char *cmdline;
/*
* Get the command line from EFI, using the LOADED_IMAGE
@ -128,25 +120,24 @@ efi_status_t efi_handle_cmdline(efi_loaded_image_t *image, char **cmdline_ptr)
if (!IS_ENABLED(CONFIG_CMDLINE_FORCE)) {
status = efi_parse_options(cmdline);
if (status != EFI_SUCCESS)
goto fail_free_cmdline;
if (status != EFI_SUCCESS) {
efi_err("Failed to parse EFI load options\n");
return status;
}
}
if (IS_ENABLED(CONFIG_CMDLINE_EXTEND) ||
IS_ENABLED(CONFIG_CMDLINE_FORCE) ||
cmdline[0] == 0) {
status = efi_parse_options(CONFIG_CMDLINE);
if (status != EFI_SUCCESS)
goto fail_free_cmdline;
if (status != EFI_SUCCESS) {
efi_err("Failed to parse built-in command line\n");
return status;
}
}
*cmdline_ptr = cmdline;
*cmdline_ptr = no_free_ptr(cmdline);
return EFI_SUCCESS;
fail_free_cmdline:
efi_err("Failed to parse options\n");
efi_bs_call(free_pool, cmdline);
return status;
}
efi_status_t efi_stub_common(efi_handle_t handle,

View File

@ -4,6 +4,7 @@
#define _DRIVERS_FIRMWARE_EFI_EFISTUB_H
#include <linux/compiler.h>
#include <linux/cleanup.h>
#include <linux/efi.h>
#include <linux/kernel.h>
#include <linux/kern_levels.h>
@ -122,11 +123,10 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
#define efi_get_handle_num(size) \
((size) / (efi_is_native() ? sizeof(efi_handle_t) : sizeof(u32)))
#define for_each_efi_handle(handle, array, size, i) \
for (i = 0; \
i < efi_get_handle_num(size) && \
((handle = efi_get_handle_at((array), i)) || true); \
i++)
#define for_each_efi_handle(handle, array, num) \
for (int __i = 0; __i < (num) && \
((handle = efi_get_handle_at((array), __i)) || true); \
__i++)
static inline
void efi_set_u64_split(u64 data, u32 *lo, u32 *hi)
@ -171,7 +171,7 @@ void efi_set_u64_split(u64 data, u32 *lo, u32 *hi)
* the EFI memory map. Other related structures, e.g. x86 e820ext, need
* to factor in this headroom requirement as well.
*/
#define EFI_MMAP_NR_SLACK_SLOTS 8
#define EFI_MMAP_NR_SLACK_SLOTS 32
typedef struct efi_generic_dev_path efi_device_path_protocol_t;
@ -314,7 +314,9 @@ union efi_boot_services {
void *close_protocol;
void *open_protocol_information;
void *protocols_per_handle;
void *locate_handle_buffer;
efi_status_t (__efiapi *locate_handle_buffer)(int, efi_guid_t *,
void *, unsigned long *,
efi_handle_t **);
efi_status_t (__efiapi *locate_protocol)(efi_guid_t *, void *,
void **);
efi_status_t (__efiapi *install_multiple_protocol_interfaces)(efi_handle_t *, ...);
@ -1053,6 +1055,7 @@ void efi_puts(const char *str);
__printf(1, 2) int efi_printk(char const *fmt, ...);
void efi_free(unsigned long size, unsigned long addr);
DEFINE_FREE(efi_pool, void *, if (_T) efi_bs_call(free_pool, _T));
void efi_apply_loadoptions_quirk(const void **load_options, u32 *load_options_size);
@ -1082,8 +1085,7 @@ efi_status_t efi_parse_options(char const *cmdline);
void efi_parse_option_graphics(char *option);
efi_status_t efi_setup_gop(struct screen_info *si, efi_guid_t *proto,
unsigned long size);
efi_status_t efi_setup_gop(struct screen_info *si);
efi_status_t handle_cmdline_files(efi_loaded_image_t *image,
const efi_char16_t *optstr,
@ -1232,4 +1234,7 @@ void process_unaccepted_memory(u64 start, u64 end);
void accept_memory(phys_addr_t start, unsigned long size);
void arch_accept_memory(phys_addr_t start, phys_addr_t end);
efi_status_t efi_zboot_decompress_init(unsigned long *alloc_size);
efi_status_t efi_zboot_decompress(u8 *out, unsigned long outlen);
#endif

View File

@ -133,13 +133,11 @@ void efi_parse_option_graphics(char *option)
static u32 choose_mode_modenum(efi_graphics_output_protocol_t *gop)
{
efi_status_t status;
efi_graphics_output_mode_info_t *info __free(efi_pool) = NULL;
efi_graphics_output_protocol_mode_t *mode;
efi_graphics_output_mode_info_t *info;
unsigned long info_size;
u32 max_mode, cur_mode;
efi_status_t status;
int pf;
mode = efi_table_attr(gop, mode);
@ -154,17 +152,13 @@ static u32 choose_mode_modenum(efi_graphics_output_protocol_t *gop)
return cur_mode;
}
status = efi_call_proto(gop, query_mode, cmdline.mode,
&info_size, &info);
status = efi_call_proto(gop, query_mode, cmdline.mode, &info_size, &info);
if (status != EFI_SUCCESS) {
efi_err("Couldn't get mode information\n");
return cur_mode;
}
pf = info->pixel_format;
efi_bs_call(free_pool, info);
if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX) {
efi_err("Invalid PixelFormat\n");
return cur_mode;
@ -173,6 +167,28 @@ static u32 choose_mode_modenum(efi_graphics_output_protocol_t *gop)
return cmdline.mode;
}
static u32 choose_mode(efi_graphics_output_protocol_t *gop,
bool (*match)(const efi_graphics_output_mode_info_t *, u32, void *),
void *ctx)
{
efi_graphics_output_protocol_mode_t *mode = efi_table_attr(gop, mode);
u32 max_mode = efi_table_attr(mode, max_mode);
for (u32 m = 0; m < max_mode; m++) {
efi_graphics_output_mode_info_t *info __free(efi_pool) = NULL;
unsigned long info_size;
efi_status_t status;
status = efi_call_proto(gop, query_mode, m, &info_size, &info);
if (status != EFI_SUCCESS)
continue;
if (match(info, m, ctx))
return m;
}
return (unsigned long)ctx;
}
static u8 pixel_bpp(int pixel_format, efi_pixel_bitmask_t pixel_info)
{
if (pixel_format == PIXEL_BIT_MASK) {
@ -185,192 +201,117 @@ static u8 pixel_bpp(int pixel_format, efi_pixel_bitmask_t pixel_info)
return 32;
}
static bool match_res(const efi_graphics_output_mode_info_t *info, u32 mode, void *ctx)
{
efi_pixel_bitmask_t pi = info->pixel_information;
int pf = info->pixel_format;
if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX)
return false;
return cmdline.res.width == info->horizontal_resolution &&
cmdline.res.height == info->vertical_resolution &&
(cmdline.res.format < 0 || cmdline.res.format == pf) &&
(!cmdline.res.depth || cmdline.res.depth == pixel_bpp(pf, pi));
}
static u32 choose_mode_res(efi_graphics_output_protocol_t *gop)
{
efi_status_t status;
efi_graphics_output_protocol_mode_t *mode = efi_table_attr(gop, mode);
unsigned long cur_mode = efi_table_attr(mode, mode);
efi_graphics_output_protocol_mode_t *mode;
efi_graphics_output_mode_info_t *info;
unsigned long info_size;
u32 max_mode, cur_mode;
int pf;
efi_pixel_bitmask_t pi;
u32 m, w, h;
mode = efi_table_attr(gop, mode);
cur_mode = efi_table_attr(mode, mode);
info = efi_table_attr(mode, info);
pf = info->pixel_format;
pi = info->pixel_information;
w = info->horizontal_resolution;
h = info->vertical_resolution;
if (w == cmdline.res.width && h == cmdline.res.height &&
(cmdline.res.format < 0 || cmdline.res.format == pf) &&
(!cmdline.res.depth || cmdline.res.depth == pixel_bpp(pf, pi)))
if (match_res(efi_table_attr(mode, info), cur_mode, NULL))
return cur_mode;
max_mode = efi_table_attr(mode, max_mode);
return choose_mode(gop, match_res, (void *)cur_mode);
}
for (m = 0; m < max_mode; m++) {
if (m == cur_mode)
continue;
struct match {
u32 mode;
u32 area;
u8 depth;
};
status = efi_call_proto(gop, query_mode, m,
&info_size, &info);
if (status != EFI_SUCCESS)
continue;
static bool match_auto(const efi_graphics_output_mode_info_t *info, u32 mode, void *ctx)
{
u32 area = info->horizontal_resolution * info->vertical_resolution;
efi_pixel_bitmask_t pi = info->pixel_information;
int pf = info->pixel_format;
u8 depth = pixel_bpp(pf, pi);
struct match *m = ctx;
pf = info->pixel_format;
pi = info->pixel_information;
w = info->horizontal_resolution;
h = info->vertical_resolution;
if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX)
return false;
efi_bs_call(free_pool, info);
if (area > m->area || (area == m->area && depth > m->depth))
*m = (struct match){ mode, area, depth };
if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX)
continue;
if (w == cmdline.res.width && h == cmdline.res.height &&
(cmdline.res.format < 0 || cmdline.res.format == pf) &&
(!cmdline.res.depth || cmdline.res.depth == pixel_bpp(pf, pi)))
return m;
}
efi_err("Couldn't find requested mode\n");
return cur_mode;
return false;
}
static u32 choose_mode_auto(efi_graphics_output_protocol_t *gop)
{
efi_status_t status;
struct match match = {};
efi_graphics_output_protocol_mode_t *mode;
efi_graphics_output_mode_info_t *info;
unsigned long info_size;
choose_mode(gop, match_auto, &match);
u32 max_mode, cur_mode, best_mode, area;
u8 depth;
int pf;
efi_pixel_bitmask_t pi;
u32 m, w, h, a;
u8 d;
return match.mode;
}
mode = efi_table_attr(gop, mode);
static bool match_list(const efi_graphics_output_mode_info_t *info, u32 mode, void *ctx)
{
efi_pixel_bitmask_t pi = info->pixel_information;
u32 cur_mode = (unsigned long)ctx;
int pf = info->pixel_format;
const char *dstr;
u8 depth = 0;
bool valid;
cur_mode = efi_table_attr(mode, mode);
max_mode = efi_table_attr(mode, max_mode);
valid = !(pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX);
info = efi_table_attr(mode, info);
pf = info->pixel_format;
pi = info->pixel_information;
w = info->horizontal_resolution;
h = info->vertical_resolution;
best_mode = cur_mode;
area = w * h;
depth = pixel_bpp(pf, pi);
for (m = 0; m < max_mode; m++) {
if (m == cur_mode)
continue;
status = efi_call_proto(gop, query_mode, m,
&info_size, &info);
if (status != EFI_SUCCESS)
continue;
pf = info->pixel_format;
pi = info->pixel_information;
w = info->horizontal_resolution;
h = info->vertical_resolution;
efi_bs_call(free_pool, info);
if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX)
continue;
a = w * h;
if (a < area)
continue;
d = pixel_bpp(pf, pi);
if (a > area || d > depth) {
best_mode = m;
area = a;
depth = d;
}
switch (pf) {
case PIXEL_RGB_RESERVED_8BIT_PER_COLOR:
dstr = "rgb";
break;
case PIXEL_BGR_RESERVED_8BIT_PER_COLOR:
dstr = "bgr";
break;
case PIXEL_BIT_MASK:
dstr = "";
depth = pixel_bpp(pf, pi);
break;
case PIXEL_BLT_ONLY:
dstr = "blt";
break;
default:
dstr = "xxx";
break;
}
return best_mode;
efi_printk("Mode %3u %c%c: Resolution %ux%u-%s%.0hhu\n",
mode,
(mode == cur_mode) ? '*' : ' ',
!valid ? '-' : ' ',
info->horizontal_resolution,
info->vertical_resolution,
dstr, depth);
return false;
}
static u32 choose_mode_list(efi_graphics_output_protocol_t *gop)
{
efi_status_t status;
efi_graphics_output_protocol_mode_t *mode;
efi_graphics_output_mode_info_t *info;
unsigned long info_size;
u32 max_mode, cur_mode;
int pf;
efi_pixel_bitmask_t pi;
u32 m, w, h;
u8 d;
const char *dstr;
bool valid;
efi_graphics_output_protocol_mode_t *mode = efi_table_attr(gop, mode);
unsigned long cur_mode = efi_table_attr(mode, mode);
u32 max_mode = efi_table_attr(mode, max_mode);
efi_input_key_t key;
mode = efi_table_attr(gop, mode);
cur_mode = efi_table_attr(mode, mode);
max_mode = efi_table_attr(mode, max_mode);
efi_status_t status;
efi_printk("Available graphics modes are 0-%u\n", max_mode-1);
efi_puts(" * = current mode\n"
" - = unusable mode\n");
for (m = 0; m < max_mode; m++) {
status = efi_call_proto(gop, query_mode, m,
&info_size, &info);
if (status != EFI_SUCCESS)
continue;
pf = info->pixel_format;
pi = info->pixel_information;
w = info->horizontal_resolution;
h = info->vertical_resolution;
efi_bs_call(free_pool, info);
valid = !(pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX);
d = 0;
switch (pf) {
case PIXEL_RGB_RESERVED_8BIT_PER_COLOR:
dstr = "rgb";
break;
case PIXEL_BGR_RESERVED_8BIT_PER_COLOR:
dstr = "bgr";
break;
case PIXEL_BIT_MASK:
dstr = "";
d = pixel_bpp(pf, pi);
break;
case PIXEL_BLT_ONLY:
dstr = "blt";
break;
default:
dstr = "xxx";
break;
}
efi_printk("Mode %3u %c%c: Resolution %ux%u-%s%.0hhu\n",
m,
m == cur_mode ? '*' : ' ',
!valid ? '-' : ' ',
w, h, dstr, d);
}
choose_mode(gop, match_list, (void *)cur_mode);
efi_puts("\nPress any key to continue (or wait 10 seconds)\n");
status = efi_wait_for_key(10 * EFI_USEC_PER_SEC, &key);
@ -461,26 +402,25 @@ setup_pixel_info(struct screen_info *si, u32 pixels_per_scan_line,
}
}
static efi_graphics_output_protocol_t *
find_gop(efi_guid_t *proto, unsigned long size, void **handles)
static efi_graphics_output_protocol_t *find_gop(unsigned long num,
const efi_handle_t handles[])
{
efi_graphics_output_protocol_t *first_gop;
efi_handle_t h;
int i;
first_gop = NULL;
for_each_efi_handle(h, handles, size, i) {
for_each_efi_handle(h, handles, num) {
efi_status_t status;
efi_graphics_output_protocol_t *gop;
efi_graphics_output_protocol_mode_t *mode;
efi_graphics_output_mode_info_t *info;
efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID;
void *dummy = NULL;
status = efi_bs_call(handle_protocol, h, proto, (void **)&gop);
status = efi_bs_call(handle_protocol, h,
&EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID,
(void **)&gop);
if (status != EFI_SUCCESS)
continue;
@ -500,7 +440,8 @@ find_gop(efi_guid_t *proto, unsigned long size, void **handles)
* Once we've found a GOP supporting ConOut,
* don't bother looking any further.
*/
status = efi_bs_call(handle_protocol, h, &conout_proto, &dummy);
status = efi_bs_call(handle_protocol, h,
&EFI_CONSOLE_OUT_DEVICE_GUID, &dummy);
if (status == EFI_SUCCESS)
return gop;
@ -511,16 +452,22 @@ find_gop(efi_guid_t *proto, unsigned long size, void **handles)
return first_gop;
}
static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto,
unsigned long size, void **handles)
efi_status_t efi_setup_gop(struct screen_info *si)
{
efi_graphics_output_protocol_t *gop;
efi_handle_t *handles __free(efi_pool) = NULL;
efi_graphics_output_protocol_mode_t *mode;
efi_graphics_output_mode_info_t *info;
efi_graphics_output_protocol_t *gop;
efi_status_t status;
unsigned long num;
gop = find_gop(proto, size, handles);
status = efi_bs_call(locate_handle_buffer, EFI_LOCATE_BY_PROTOCOL,
&EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID, NULL, &num,
&handles);
if (status != EFI_SUCCESS)
return status;
/* Did we find any GOPs? */
gop = find_gop(num, handles);
if (!gop)
return EFI_NOT_FOUND;
@ -552,29 +499,3 @@ static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto,
return EFI_SUCCESS;
}
/*
* See if we have Graphics Output Protocol
*/
efi_status_t efi_setup_gop(struct screen_info *si, efi_guid_t *proto,
unsigned long size)
{
efi_status_t status;
void **gop_handle = NULL;
status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, size,
(void **)&gop_handle);
if (status != EFI_SUCCESS)
return status;
status = efi_bs_call(locate_handle, EFI_LOCATE_BY_PROTOCOL, proto, NULL,
&size, gop_handle);
if (status != EFI_SUCCESS)
goto free_handle;
status = setup_gop(si, proto, size, gop_handle);
free_handle:
efi_bs_call(free_pool, gop_handle);
return status;
}

View File

@ -57,7 +57,7 @@ u32 efi_kaslr_get_phys_seed(efi_handle_t image_handle)
*/
static bool check_image_region(u64 base, u64 size)
{
struct efi_boot_memmap *map;
struct efi_boot_memmap *map __free(efi_pool) = NULL;
efi_status_t status;
bool ret = false;
int map_offset;
@ -80,8 +80,6 @@ static bool check_image_region(u64 base, u64 size)
}
}
efi_bs_call(free_pool, map);
return ret;
}

View File

@ -20,10 +20,10 @@
efi_status_t efi_get_memory_map(struct efi_boot_memmap **map,
bool install_cfg_tbl)
{
struct efi_boot_memmap tmp, *m __free(efi_pool) = NULL;
int memtype = install_cfg_tbl ? EFI_ACPI_RECLAIM_MEMORY
: EFI_LOADER_DATA;
efi_guid_t tbl_guid = LINUX_EFI_BOOT_MEMMAP_GUID;
struct efi_boot_memmap *m, tmp;
efi_status_t status;
unsigned long size;
@ -48,24 +48,20 @@ efi_status_t efi_get_memory_map(struct efi_boot_memmap **map,
*/
status = efi_bs_call(install_configuration_table, &tbl_guid, m);
if (status != EFI_SUCCESS)
goto free_map;
return status;
}
m->buff_size = m->map_size = size;
status = efi_bs_call(get_memory_map, &m->map_size, m->map, &m->map_key,
&m->desc_size, &m->desc_ver);
if (status != EFI_SUCCESS)
goto uninstall_table;
if (status != EFI_SUCCESS) {
if (install_cfg_tbl)
efi_bs_call(install_configuration_table, &tbl_guid, NULL);
return status;
}
*map = m;
*map = no_free_ptr(m);
return EFI_SUCCESS;
uninstall_table:
if (install_cfg_tbl)
efi_bs_call(install_configuration_table, &tbl_guid, NULL);
free_map:
efi_bs_call(free_pool, m);
return status;
}
/**

View File

@ -16,37 +16,20 @@
void efi_pci_disable_bridge_busmaster(void)
{
efi_guid_t pci_proto = EFI_PCI_IO_PROTOCOL_GUID;
unsigned long pci_handle_size = 0;
efi_handle_t *pci_handle = NULL;
efi_handle_t *pci_handle __free(efi_pool) = NULL;
unsigned long pci_handle_num;
efi_handle_t handle;
efi_status_t status;
u16 class, command;
int i;
status = efi_bs_call(locate_handle, EFI_LOCATE_BY_PROTOCOL, &pci_proto,
NULL, &pci_handle_size, NULL);
if (status != EFI_BUFFER_TOO_SMALL) {
if (status != EFI_SUCCESS && status != EFI_NOT_FOUND)
efi_err("Failed to locate PCI I/O handles'\n");
status = efi_bs_call(locate_handle_buffer, EFI_LOCATE_BY_PROTOCOL,
&pci_proto, NULL, &pci_handle_num, &pci_handle);
if (status != EFI_SUCCESS) {
efi_err("Failed to locate PCI I/O handles\n");
return;
}
status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, pci_handle_size,
(void **)&pci_handle);
if (status != EFI_SUCCESS) {
efi_err("Failed to allocate memory for 'pci_handle'\n");
return;
}
status = efi_bs_call(locate_handle, EFI_LOCATE_BY_PROTOCOL, &pci_proto,
NULL, &pci_handle_size, pci_handle);
if (status != EFI_SUCCESS) {
efi_err("Failed to locate PCI I/O handles'\n");
goto free_handle;
}
for_each_efi_handle(handle, pci_handle, pci_handle_size, i) {
for_each_efi_handle(handle, pci_handle, pci_handle_num) {
efi_pci_io_protocol_t *pci;
unsigned long segment_nr, bus_nr, device_nr, func_nr;
@ -82,7 +65,7 @@ void efi_pci_disable_bridge_busmaster(void)
efi_bs_call(disconnect_controller, handle, NULL, NULL);
}
for_each_efi_handle(handle, pci_handle, pci_handle_size, i) {
for_each_efi_handle(handle, pci_handle, pci_handle_num) {
efi_pci_io_protocol_t *pci;
status = efi_bs_call(handle_protocol, handle, &pci_proto,
@ -108,7 +91,4 @@ void efi_pci_disable_bridge_busmaster(void)
if (status != EFI_SUCCESS)
efi_err("Failed to disable PCI busmastering\n");
}
free_handle:
efi_bs_call(free_pool, pci_handle);
}

View File

@ -59,9 +59,9 @@ efi_status_t efi_random_alloc(unsigned long size,
unsigned long alloc_min,
unsigned long alloc_max)
{
struct efi_boot_memmap *map __free(efi_pool) = NULL;
unsigned long total_slots = 0, target_slot;
unsigned long total_mirrored_slots = 0;
struct efi_boot_memmap *map;
efi_status_t status;
int map_offset;
@ -130,7 +130,5 @@ efi_status_t efi_random_alloc(unsigned long size,
break;
}
efi_bs_call(free_pool, map);
return status;
}

View File

@ -23,14 +23,14 @@
efi_status_t efi_low_alloc_above(unsigned long size, unsigned long align,
unsigned long *addr, unsigned long min)
{
struct efi_boot_memmap *map;
struct efi_boot_memmap *map __free(efi_pool) = NULL;
efi_status_t status;
unsigned long nr_pages;
int i;
status = efi_get_memory_map(&map, false);
if (status != EFI_SUCCESS)
goto fail;
return status;
/*
* Enforce minimum alignment that EFI or Linux requires when
@ -79,11 +79,9 @@ efi_status_t efi_low_alloc_above(unsigned long size, unsigned long align,
}
if (i == map->map_size / map->desc_size)
status = EFI_NOT_FOUND;
return EFI_NOT_FOUND;
efi_bs_call(free_pool, map);
fail:
return status;
return EFI_SUCCESS;
}
/**

View File

@ -42,7 +42,7 @@ union sev_memory_acceptance_protocol {
static efi_status_t
preserve_pci_rom_image(efi_pci_io_protocol_t *pci, struct pci_setup_rom **__rom)
{
struct pci_setup_rom *rom = NULL;
struct pci_setup_rom *rom __free(efi_pool) = NULL;
efi_status_t status;
unsigned long size;
uint64_t romsize;
@ -75,14 +75,13 @@ preserve_pci_rom_image(efi_pci_io_protocol_t *pci, struct pci_setup_rom **__rom)
rom->data.len = size - sizeof(struct setup_data);
rom->data.next = 0;
rom->pcilen = romsize;
*__rom = rom;
status = efi_call_proto(pci, pci.read, EfiPciIoWidthUint16,
PCI_VENDOR_ID, 1, &rom->vendor);
if (status != EFI_SUCCESS) {
efi_err("Failed to read rom->vendor\n");
goto free_struct;
return status;
}
status = efi_call_proto(pci, pci.read, EfiPciIoWidthUint16,
@ -90,21 +89,18 @@ preserve_pci_rom_image(efi_pci_io_protocol_t *pci, struct pci_setup_rom **__rom)
if (status != EFI_SUCCESS) {
efi_err("Failed to read rom->devid\n");
goto free_struct;
return status;
}
status = efi_call_proto(pci, get_location, &rom->segment, &rom->bus,
&rom->device, &rom->function);
if (status != EFI_SUCCESS)
goto free_struct;
return status;
memcpy(rom->romdata, romimage, romsize);
return status;
free_struct:
efi_bs_call(free_pool, rom);
return status;
*__rom = no_free_ptr(rom);
return EFI_SUCCESS;
}
/*
@ -119,38 +115,23 @@ free_struct:
static void setup_efi_pci(struct boot_params *params)
{
efi_status_t status;
void **pci_handle = NULL;
efi_handle_t *pci_handle __free(efi_pool) = NULL;
efi_guid_t pci_proto = EFI_PCI_IO_PROTOCOL_GUID;
unsigned long size = 0;
struct setup_data *data;
unsigned long num;
efi_handle_t h;
int i;
status = efi_bs_call(locate_handle, EFI_LOCATE_BY_PROTOCOL,
&pci_proto, NULL, &size, pci_handle);
if (status == EFI_BUFFER_TOO_SMALL) {
status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, size,
(void **)&pci_handle);
if (status != EFI_SUCCESS) {
efi_err("Failed to allocate memory for 'pci_handle'\n");
return;
}
status = efi_bs_call(locate_handle, EFI_LOCATE_BY_PROTOCOL,
&pci_proto, NULL, &size, pci_handle);
}
status = efi_bs_call(locate_handle_buffer, EFI_LOCATE_BY_PROTOCOL,
&pci_proto, NULL, &num, &pci_handle);
if (status != EFI_SUCCESS)
goto free_handle;
return;
data = (struct setup_data *)(unsigned long)params->hdr.setup_data;
while (data && data->next)
data = (struct setup_data *)(unsigned long)data->next;
for_each_efi_handle(h, pci_handle, size, i) {
for_each_efi_handle(h, pci_handle, num) {
efi_pci_io_protocol_t *pci = NULL;
struct pci_setup_rom *rom;
@ -170,9 +151,6 @@ static void setup_efi_pci(struct boot_params *params)
data = (struct setup_data *)rom;
}
free_handle:
efi_bs_call(free_pool, pci_handle);
}
static void retrieve_apple_device_properties(struct boot_params *boot_params)
@ -405,116 +383,13 @@ static void setup_quirks(struct boot_params *boot_params)
}
}
/*
* See if we have Universal Graphics Adapter (UGA) protocol
*/
static efi_status_t
setup_uga(struct screen_info *si, efi_guid_t *uga_proto, unsigned long size)
{
efi_status_t status;
u32 width, height;
void **uga_handle = NULL;
efi_uga_draw_protocol_t *uga = NULL, *first_uga;
efi_handle_t handle;
int i;
status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, size,
(void **)&uga_handle);
if (status != EFI_SUCCESS)
return status;
status = efi_bs_call(locate_handle, EFI_LOCATE_BY_PROTOCOL,
uga_proto, NULL, &size, uga_handle);
if (status != EFI_SUCCESS)
goto free_handle;
height = 0;
width = 0;
first_uga = NULL;
for_each_efi_handle(handle, uga_handle, size, i) {
efi_guid_t pciio_proto = EFI_PCI_IO_PROTOCOL_GUID;
u32 w, h, depth, refresh;
void *pciio;
status = efi_bs_call(handle_protocol, handle, uga_proto,
(void **)&uga);
if (status != EFI_SUCCESS)
continue;
pciio = NULL;
efi_bs_call(handle_protocol, handle, &pciio_proto, &pciio);
status = efi_call_proto(uga, get_mode, &w, &h, &depth, &refresh);
if (status == EFI_SUCCESS && (!first_uga || pciio)) {
width = w;
height = h;
/*
* Once we've found a UGA supporting PCIIO,
* don't bother looking any further.
*/
if (pciio)
break;
first_uga = uga;
}
}
if (!width && !height)
goto free_handle;
/* EFI framebuffer */
si->orig_video_isVGA = VIDEO_TYPE_EFI;
si->lfb_depth = 32;
si->lfb_width = width;
si->lfb_height = height;
si->red_size = 8;
si->red_pos = 16;
si->green_size = 8;
si->green_pos = 8;
si->blue_size = 8;
si->blue_pos = 0;
si->rsvd_size = 8;
si->rsvd_pos = 24;
free_handle:
efi_bs_call(free_pool, uga_handle);
return status;
}
static void setup_graphics(struct boot_params *boot_params)
{
efi_guid_t graphics_proto = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
struct screen_info *si;
efi_guid_t uga_proto = EFI_UGA_PROTOCOL_GUID;
efi_status_t status;
unsigned long size;
void **gop_handle = NULL;
void **uga_handle = NULL;
struct screen_info *si = memset(&boot_params->screen_info, 0, sizeof(*si));
si = &boot_params->screen_info;
memset(si, 0, sizeof(*si));
size = 0;
status = efi_bs_call(locate_handle, EFI_LOCATE_BY_PROTOCOL,
&graphics_proto, NULL, &size, gop_handle);
if (status == EFI_BUFFER_TOO_SMALL)
status = efi_setup_gop(si, &graphics_proto, size);
if (status != EFI_SUCCESS) {
size = 0;
status = efi_bs_call(locate_handle, EFI_LOCATE_BY_PROTOCOL,
&uga_proto, NULL, &size, uga_handle);
if (status == EFI_BUFFER_TOO_SMALL)
setup_uga(si, &uga_proto, size);
}
efi_setup_gop(si);
}
static void __noreturn efi_exit(efi_handle_t handle, efi_status_t status)
{
efi_bs_call(exit, handle, status, 0, NULL);
@ -737,7 +612,7 @@ static efi_status_t allocate_e820(struct boot_params *params,
struct setup_data **e820ext,
u32 *e820ext_size)
{
struct efi_boot_memmap *map;
struct efi_boot_memmap *map __free(efi_pool) = NULL;
efi_status_t status;
__u32 nr_desc;
@ -751,13 +626,14 @@ static efi_status_t allocate_e820(struct boot_params *params,
EFI_MMAP_NR_SLACK_SLOTS;
status = alloc_e820ext(nr_e820ext, e820ext, e820ext_size);
if (status != EFI_SUCCESS)
return status;
}
if (IS_ENABLED(CONFIG_UNACCEPTED_MEMORY) && status == EFI_SUCCESS)
status = allocate_unaccepted_bitmap(nr_desc, map);
if (IS_ENABLED(CONFIG_UNACCEPTED_MEMORY))
return allocate_unaccepted_bitmap(nr_desc, map);
efi_bs_call(free_pool, map);
return status;
return EFI_SUCCESS;
}
struct exit_boot_struct {

View File

@ -0,0 +1,68 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/efi.h>
#include <linux/zlib.h>
#include <asm/efi.h>
#include "efistub.h"
#include "inftrees.c"
#include "inffast.c"
#include "inflate.c"
extern unsigned char _gzdata_start[], _gzdata_end[];
extern u32 __aligned(1) payload_size;
static struct z_stream_s stream;
efi_status_t efi_zboot_decompress_init(unsigned long *alloc_size)
{
efi_status_t status;
int rc;
/* skip the 10 byte header, assume no recorded filename */
stream.next_in = _gzdata_start + 10;
stream.avail_in = _gzdata_end - stream.next_in;
status = efi_allocate_pages(zlib_inflate_workspacesize(),
(unsigned long *)&stream.workspace,
ULONG_MAX);
if (status != EFI_SUCCESS)
return status;
rc = zlib_inflateInit2(&stream, -MAX_WBITS);
if (rc != Z_OK) {
efi_err("failed to initialize GZIP decompressor: %d\n", rc);
status = EFI_LOAD_ERROR;
goto out;
}
*alloc_size = payload_size;
return EFI_SUCCESS;
out:
efi_free(zlib_inflate_workspacesize(), (unsigned long)stream.workspace);
return status;
}
efi_status_t efi_zboot_decompress(u8 *out, unsigned long outlen)
{
int rc;
stream.next_out = out;
stream.avail_out = outlen;
rc = zlib_inflate(&stream, 0);
zlib_inflateEnd(&stream);
efi_free(zlib_inflate_workspacesize(), (unsigned long)stream.workspace);
if (rc != Z_STREAM_END) {
efi_err("GZIP decompression failed with status %d\n", rc);
return EFI_LOAD_ERROR;
}
efi_cache_sync_image((unsigned long)out, outlen);
return EFI_SUCCESS;
}

View File

@ -0,0 +1,83 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/efi.h>
#include <linux/zstd.h>
#include <asm/efi.h>
#include "decompress_sources.h"
#include "efistub.h"
extern unsigned char _gzdata_start[], _gzdata_end[];
extern u32 __aligned(1) payload_size;
static ZSTD_inBuffer zstd_buf;
static ZSTD_DStream *dstream;
static size_t wksp_size;
static void *wksp;
efi_status_t efi_zboot_decompress_init(unsigned long *alloc_size)
{
zstd_frame_header header;
efi_status_t status;
size_t ret;
zstd_buf.src = _gzdata_start;
zstd_buf.pos = 0;
zstd_buf.size = _gzdata_end - _gzdata_start;
ret = zstd_get_frame_header(&header, zstd_buf.src, zstd_buf.size);
if (ret != 0) {
efi_err("ZSTD-compressed data has an incomplete frame header\n");
status = EFI_LOAD_ERROR;
goto out;
}
if (header.windowSize > (1 << ZSTD_WINDOWLOG_MAX)) {
efi_err("ZSTD-compressed data has too large a window size\n");
status = EFI_LOAD_ERROR;
goto out;
}
wksp_size = zstd_dstream_workspace_bound(header.windowSize);
status = efi_allocate_pages(wksp_size, (unsigned long *)&wksp, ULONG_MAX);
if (status != EFI_SUCCESS)
goto out;
dstream = zstd_init_dstream(header.windowSize, wksp, wksp_size);
if (!dstream) {
efi_err("Can't initialize ZSTD stream\n");
status = EFI_OUT_OF_RESOURCES;
goto out;
}
*alloc_size = payload_size;
return EFI_SUCCESS;
out:
efi_free(wksp_size, (unsigned long)wksp);
return status;
}
efi_status_t efi_zboot_decompress(u8 *out, unsigned long outlen)
{
ZSTD_outBuffer zstd_dec;
size_t ret;
int retval;
zstd_dec.dst = out;
zstd_dec.pos = 0;
zstd_dec.size = outlen;
ret = zstd_decompress_stream(dstream, &zstd_dec, &zstd_buf);
efi_free(wksp_size, (unsigned long)wksp);
retval = zstd_get_error_code(ret);
if (retval) {
efi_err("ZSTD-decompression failed with status %d\n", retval);
return EFI_LOAD_ERROR;
}
efi_cache_sync_image((unsigned long)out, outlen);
return EFI_SUCCESS;
}

View File

@ -7,36 +7,6 @@
#include "efistub.h"
static unsigned char zboot_heap[SZ_256K] __aligned(64);
static unsigned long free_mem_ptr, free_mem_end_ptr;
#define STATIC static
#if defined(CONFIG_KERNEL_GZIP)
#include "../../../../lib/decompress_inflate.c"
#elif defined(CONFIG_KERNEL_LZ4)
#include "../../../../lib/decompress_unlz4.c"
#elif defined(CONFIG_KERNEL_LZMA)
#include "../../../../lib/decompress_unlzma.c"
#elif defined(CONFIG_KERNEL_LZO)
#include "../../../../lib/decompress_unlzo.c"
#elif defined(CONFIG_KERNEL_XZ)
#undef memcpy
#define memcpy memcpy
#undef memmove
#define memmove memmove
#include "../../../../lib/decompress_unxz.c"
#elif defined(CONFIG_KERNEL_ZSTD)
#include "../../../../lib/decompress_unzstd.c"
#endif
extern char efi_zboot_header[];
extern char _gzdata_start[], _gzdata_end[];
static void error(char *x)
{
efi_err("EFI decompressor: %s\n", x);
}
static unsigned long alloc_preferred_address(unsigned long alloc_size)
{
#ifdef EFI_KIMG_PREFERRED_ADDRESS
@ -64,22 +34,17 @@ struct screen_info *alloc_screen_info(void)
asmlinkage efi_status_t __efiapi
efi_zboot_entry(efi_handle_t handle, efi_system_table_t *systab)
{
unsigned long compressed_size = _gzdata_end - _gzdata_start;
char *cmdline_ptr __free(efi_pool) = NULL;
unsigned long image_base, alloc_size;
efi_loaded_image_t *image;
efi_status_t status;
char *cmdline_ptr;
int ret;
WRITE_ONCE(efi_system_table, systab);
free_mem_ptr = (unsigned long)&zboot_heap;
free_mem_end_ptr = free_mem_ptr + sizeof(zboot_heap);
status = efi_bs_call(handle_protocol, handle,
&LOADED_IMAGE_PROTOCOL_GUID, (void **)&image);
if (status != EFI_SUCCESS) {
error("Failed to locate parent's loaded image protocol");
efi_err("Failed to locate parent's loaded image protocol\n");
return status;
}
@ -89,9 +54,9 @@ efi_zboot_entry(efi_handle_t handle, efi_system_table_t *systab)
efi_info("Decompressing Linux Kernel...\n");
// SizeOfImage from the compressee's PE/COFF header
alloc_size = round_up(get_unaligned_le32(_gzdata_end - 4),
EFI_ALLOC_ALIGN);
status = efi_zboot_decompress_init(&alloc_size);
if (status != EFI_SUCCESS)
return status;
// If the architecture has a preferred address for the image,
// try that first.
@ -122,26 +87,14 @@ efi_zboot_entry(efi_handle_t handle, efi_system_table_t *systab)
seed, EFI_LOADER_CODE, 0, EFI_ALLOC_LIMIT);
if (status != EFI_SUCCESS) {
efi_err("Failed to allocate memory\n");
goto free_cmdline;
return status;
}
}
// Decompress the payload into the newly allocated buffer.
ret = __decompress(_gzdata_start, compressed_size, NULL, NULL,
(void *)image_base, alloc_size, NULL, error);
if (ret < 0) {
error("Decompression failed");
status = EFI_DEVICE_ERROR;
goto free_image;
}
// Decompress the payload into the newly allocated buffer
status = efi_zboot_decompress((void *)image_base, alloc_size) ?:
efi_stub_common(handle, image, image_base, cmdline_ptr);
efi_cache_sync_image(image_base, alloc_size);
status = efi_stub_common(handle, image, image_base, cmdline_ptr);
free_image:
efi_free(alloc_size, image_base);
free_cmdline:
efi_bs_call(free_pool, cmdline_ptr);
return status;
}

View File

@ -17,6 +17,7 @@ SECTIONS
.rodata : ALIGN(8) {
__efistub__gzdata_start = .;
*(.gzdata)
__efistub_payload_size = . - 4;
__efistub__gzdata_end = .;
*(.rodata* .init.rodata* .srodata*)

View File

@ -91,6 +91,7 @@ void efifb_setup_from_dmi(struct screen_info *si, const char *opt)
_ret_; \
})
#ifdef CONFIG_EFI
static int __init efifb_set_system(const struct dmi_system_id *id)
{
struct efifb_dmi_info *info = id->driver_data;
@ -346,7 +347,6 @@ static const struct fwnode_operations efifb_fwnode_ops = {
.add_links = efifb_add_links,
};
#ifdef CONFIG_EFI
static struct fwnode_handle efifb_fwnode;
__init void sysfb_apply_efi_quirks(void)

View File

@ -36,28 +36,41 @@ static ssize_t efivarfs_file_write(struct file *file,
if (IS_ERR(data))
return PTR_ERR(data);
inode_lock(inode);
if (d_unhashed(file->f_path.dentry)) {
/*
* file got removed; don't allow a set. Caused by an
* unsuccessful create or successful delete write
* racing with us.
*/
bytes = -EIO;
goto out;
}
bytes = efivar_entry_set_get_size(var, attributes, &datasize,
data, &set);
if (!set && bytes) {
if (!set) {
if (bytes == -ENOENT)
bytes = -EIO;
goto out;
}
if (bytes == -ENOENT) {
drop_nlink(inode);
d_delete(file->f_path.dentry);
dput(file->f_path.dentry);
/*
* zero size signals to release that the write deleted
* the variable
*/
i_size_write(inode, 0);
} else {
inode_lock(inode);
i_size_write(inode, datasize + sizeof(attributes));
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
inode_unlock(inode);
}
bytes = count;
out:
inode_unlock(inode);
kfree(data);
return bytes;
@ -106,8 +119,37 @@ out_free:
return size;
}
static int efivarfs_file_release(struct inode *inode, struct file *file)
{
bool release;
struct efivar_entry *var = inode->i_private;
inode_lock(inode);
release = (--var->open_count == 0 && i_size_read(inode) == 0);
inode_unlock(inode);
if (release)
simple_recursive_removal(file->f_path.dentry, NULL);
return 0;
}
static int efivarfs_file_open(struct inode *inode, struct file *file)
{
struct efivar_entry *entry = inode->i_private;
file->private_data = entry;
inode_lock(inode);
entry->open_count++;
inode_unlock(inode);
return 0;
}
const struct file_operations efivarfs_file_operations = {
.open = simple_open,
.read = efivarfs_file_read,
.write = efivarfs_file_write,
.open = efivarfs_file_open,
.read = efivarfs_file_read,
.write = efivarfs_file_write,
.release = efivarfs_file_release,
};

View File

@ -77,7 +77,6 @@ static bool efivarfs_valid_name(const char *str, int len)
static int efivarfs_create(struct mnt_idmap *idmap, struct inode *dir,
struct dentry *dentry, umode_t mode, bool excl)
{
struct efivarfs_fs_info *info = dir->i_sb->s_fs_info;
struct inode *inode = NULL;
struct efivar_entry *var;
int namelen, i = 0, err = 0;
@ -119,10 +118,6 @@ static int efivarfs_create(struct mnt_idmap *idmap, struct inode *dir,
inode->i_private = var;
kmemleak_ignore(var);
err = efivar_entry_add(var, &info->efivarfs_list);
if (err)
goto out;
d_instantiate(dentry, inode);
dget(dentry);
out:

View File

@ -6,7 +6,6 @@
#ifndef EFIVAR_FS_INTERNAL_H
#define EFIVAR_FS_INTERNAL_H
#include <linux/list.h>
#include <linux/efi.h>
struct efivarfs_mount_opts {
@ -16,30 +15,24 @@ struct efivarfs_mount_opts {
struct efivarfs_fs_info {
struct efivarfs_mount_opts mount_opts;
struct list_head efivarfs_list;
struct super_block *sb;
struct notifier_block nb;
struct notifier_block pm_nb;
};
struct efi_variable {
efi_char16_t VariableName[EFI_VAR_NAME_LEN/sizeof(efi_char16_t)];
efi_guid_t VendorGuid;
__u32 Attributes;
};
struct efivar_entry {
struct efi_variable var;
struct list_head list;
struct kobject kobj;
unsigned long open_count;
};
int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *,
struct list_head *),
void *data, struct list_head *head);
int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),
void *data, bool duplicate_check);
int efivar_entry_add(struct efivar_entry *entry, struct list_head *head);
void __efivar_entry_add(struct efivar_entry *entry, struct list_head *head);
void efivar_entry_remove(struct efivar_entry *entry);
int efivar_entry_delete(struct efivar_entry *entry);
int efivar_entry_size(struct efivar_entry *entry, unsigned long *size);
@ -50,13 +43,14 @@ int efivar_entry_get(struct efivar_entry *entry, u32 *attributes,
int efivar_entry_set_get_size(struct efivar_entry *entry, u32 attributes,
unsigned long *size, void *data, bool *set);
int efivar_entry_iter(int (*func)(struct efivar_entry *, void *),
struct list_head *head, void *data);
bool efivar_validate(efi_guid_t vendor, efi_char16_t *var_name, u8 *data,
unsigned long data_size);
bool efivar_variable_is_removable(efi_guid_t vendor, const char *name,
size_t len);
char *efivar_get_utf8name(const efi_char16_t *name16, efi_guid_t *vendor);
bool efivarfs_variable_is_present(efi_char16_t *variable_name,
efi_guid_t *vendor, void *data);
extern const struct file_operations efivarfs_file_operations;
extern const struct inode_operations efivarfs_dir_inode_operations;

View File

@ -13,6 +13,7 @@
#include <linux/pagemap.h>
#include <linux/ucs2_string.h>
#include <linux/slab.h>
#include <linux/suspend.h>
#include <linux/magic.h>
#include <linux/statfs.h>
#include <linux/notifier.h>
@ -41,6 +42,9 @@ static int efivarfs_ops_notifier(struct notifier_block *nb, unsigned long event,
static void efivarfs_evict_inode(struct inode *inode)
{
struct efivar_entry *entry = inode->i_private;
kfree(entry);
clear_inode(inode);
}
@ -181,51 +185,60 @@ static struct dentry *efivarfs_alloc_dentry(struct dentry *parent, char *name)
return ERR_PTR(-ENOMEM);
}
static int efivarfs_callback(efi_char16_t *name16, efi_guid_t vendor,
unsigned long name_size, void *data,
struct list_head *list)
bool efivarfs_variable_is_present(efi_char16_t *variable_name,
efi_guid_t *vendor, void *data)
{
char *name = efivar_get_utf8name(variable_name, vendor);
struct super_block *sb = data;
struct dentry *dentry;
struct qstr qstr;
if (!name)
/*
* If the allocation failed there'll already be an
* error in the log (and likely a huge and growing
* number of them since they system will be under
* extreme memory pressure), so simply assume
* collision for safety but don't add to the log
* flood.
*/
return true;
qstr.name = name;
qstr.len = strlen(name);
dentry = d_hash_and_lookup(sb->s_root, &qstr);
kfree(name);
if (!IS_ERR_OR_NULL(dentry))
dput(dentry);
return dentry != NULL;
}
static int efivarfs_create_dentry(struct super_block *sb, efi_char16_t *name16,
unsigned long name_size, efi_guid_t *vendor,
char *name)
{
struct super_block *sb = (struct super_block *)data;
struct efivar_entry *entry;
struct inode *inode = NULL;
struct inode *inode;
struct dentry *dentry, *root = sb->s_root;
unsigned long size = 0;
char *name;
int len;
int err = -ENOMEM;
bool is_removable = false;
if (guid_equal(&vendor, &LINUX_EFI_RANDOM_SEED_TABLE_GUID))
return 0;
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
if (!entry)
return err;
memcpy(entry->var.VariableName, name16, name_size);
memcpy(&(entry->var.VendorGuid), &vendor, sizeof(efi_guid_t));
memcpy(&(entry->var.VendorGuid), vendor, sizeof(efi_guid_t));
len = ucs2_utf8size(entry->var.VariableName);
/* name, plus '-', plus GUID, plus NUL*/
name = kmalloc(len + 1 + EFI_VARIABLE_GUID_LEN + 1, GFP_KERNEL);
if (!name)
goto fail;
ucs2_as_utf8(name, entry->var.VariableName, len);
/* length of the variable name itself: remove GUID and separator */
len = strlen(name) - EFI_VARIABLE_GUID_LEN - 1;
if (efivar_variable_is_removable(entry->var.VendorGuid, name, len))
is_removable = true;
name[len] = '-';
efi_guid_to_str(&entry->var.VendorGuid, name + len + 1);
name[len + EFI_VARIABLE_GUID_LEN+1] = '\0';
/* replace invalid slashes like kobject_set_name_vargs does for /sys/firmware/efi/vars. */
strreplace(name, '/', '!');
inode = efivarfs_get_inode(sb, d_inode(root), S_IFREG | 0644, 0,
is_removable);
if (!inode)
@ -238,14 +251,13 @@ static int efivarfs_callback(efi_char16_t *name16, efi_guid_t vendor,
}
__efivar_entry_get(entry, NULL, &size, NULL);
__efivar_entry_add(entry, list);
/* copied by the above to local storage in the dentry. */
kfree(name);
inode_lock(inode);
inode->i_private = entry;
i_size_write(inode, size + sizeof(entry->var.Attributes));
i_size_write(inode, size + sizeof(__u32)); /* attributes + data */
inode_unlock(inode);
d_add(dentry, inode);
@ -255,16 +267,25 @@ fail_inode:
iput(inode);
fail_name:
kfree(name);
fail:
kfree(entry);
return err;
}
static int efivarfs_destroy(struct efivar_entry *entry, void *data)
static int efivarfs_callback(efi_char16_t *name16, efi_guid_t vendor,
unsigned long name_size, void *data)
{
efivar_entry_remove(entry);
kfree(entry);
return 0;
struct super_block *sb = (struct super_block *)data;
char *name;
if (guid_equal(&vendor, &LINUX_EFI_RANDOM_SEED_TABLE_GUID))
return 0;
name = efivar_get_utf8name(name16, &vendor);
if (!name)
return -ENOMEM;
return efivarfs_create_dentry(sb, name16, name_size, &vendor, name);
}
enum {
@ -336,7 +357,7 @@ static int efivarfs_fill_super(struct super_block *sb, struct fs_context *fc)
if (err)
return err;
return efivar_init(efivarfs_callback, sb, &sfi->efivarfs_list);
return efivar_init(efivarfs_callback, sb, true);
}
static int efivarfs_get_tree(struct fs_context *fc)
@ -360,6 +381,148 @@ static const struct fs_context_operations efivarfs_context_ops = {
.reconfigure = efivarfs_reconfigure,
};
struct efivarfs_ctx {
struct dir_context ctx;
struct super_block *sb;
struct dentry *dentry;
};
static bool efivarfs_actor(struct dir_context *ctx, const char *name, int len,
loff_t offset, u64 ino, unsigned mode)
{
unsigned long size;
struct efivarfs_ctx *ectx = container_of(ctx, struct efivarfs_ctx, ctx);
struct qstr qstr = { .name = name, .len = len };
struct dentry *dentry = d_hash_and_lookup(ectx->sb->s_root, &qstr);
struct inode *inode;
struct efivar_entry *entry;
int err;
if (IS_ERR_OR_NULL(dentry))
return true;
inode = d_inode(dentry);
entry = inode->i_private;
err = efivar_entry_size(entry, &size);
size += sizeof(__u32); /* attributes */
if (err)
size = 0;
inode_lock(inode);
i_size_write(inode, size);
inode_unlock(inode);
if (!size) {
ectx->dentry = dentry;
return false;
}
dput(dentry);
return true;
}
static int efivarfs_check_missing(efi_char16_t *name16, efi_guid_t vendor,
unsigned long name_size, void *data)
{
char *name;
struct super_block *sb = data;
struct dentry *dentry;
struct qstr qstr;
int err;
if (guid_equal(&vendor, &LINUX_EFI_RANDOM_SEED_TABLE_GUID))
return 0;
name = efivar_get_utf8name(name16, &vendor);
if (!name)
return -ENOMEM;
qstr.name = name;
qstr.len = strlen(name);
dentry = d_hash_and_lookup(sb->s_root, &qstr);
if (IS_ERR(dentry)) {
err = PTR_ERR(dentry);
goto out;
}
if (!dentry) {
/* found missing entry */
pr_info("efivarfs: creating variable %s\n", name);
return efivarfs_create_dentry(sb, name16, name_size, &vendor, name);
}
dput(dentry);
err = 0;
out:
kfree(name);
return err;
}
static int efivarfs_pm_notify(struct notifier_block *nb, unsigned long action,
void *ptr)
{
struct efivarfs_fs_info *sfi = container_of(nb, struct efivarfs_fs_info,
pm_nb);
struct path path = { .mnt = NULL, .dentry = sfi->sb->s_root, };
struct efivarfs_ctx ectx = {
.ctx = {
.actor = efivarfs_actor,
},
.sb = sfi->sb,
};
struct file *file;
static bool rescan_done = true;
if (action == PM_HIBERNATION_PREPARE) {
rescan_done = false;
return NOTIFY_OK;
} else if (action != PM_POST_HIBERNATION) {
return NOTIFY_DONE;
}
if (rescan_done)
return NOTIFY_DONE;
pr_info("efivarfs: resyncing variable state\n");
/* O_NOATIME is required to prevent oops on NULL mnt */
file = kernel_file_open(&path, O_RDONLY | O_DIRECTORY | O_NOATIME,
current_cred());
if (IS_ERR(file))
return NOTIFY_DONE;
rescan_done = true;
/*
* First loop over the directory and verify each entry exists,
* removing it if it doesn't
*/
file->f_pos = 2; /* skip . and .. */
do {
ectx.dentry = NULL;
iterate_dir(file, &ectx.ctx);
if (ectx.dentry) {
pr_info("efivarfs: removing variable %pd\n",
ectx.dentry);
simple_recursive_removal(ectx.dentry, NULL);
dput(ectx.dentry);
}
} while (ectx.dentry);
fput(file);
/*
* then loop over variables, creating them if there's no matching
* dentry
*/
efivar_init(efivarfs_check_missing, sfi->sb, false);
return NOTIFY_OK;
}
static int efivarfs_init_fs_context(struct fs_context *fc)
{
struct efivarfs_fs_info *sfi;
@ -371,13 +534,16 @@ static int efivarfs_init_fs_context(struct fs_context *fc)
if (!sfi)
return -ENOMEM;
INIT_LIST_HEAD(&sfi->efivarfs_list);
sfi->mount_opts.uid = GLOBAL_ROOT_UID;
sfi->mount_opts.gid = GLOBAL_ROOT_GID;
fc->s_fs_info = sfi;
fc->ops = &efivarfs_context_ops;
sfi->pm_nb.notifier_call = efivarfs_pm_notify;
sfi->pm_nb.priority = 0;
register_pm_notifier(&sfi->pm_nb);
return 0;
}
@ -387,9 +553,8 @@ static void efivarfs_kill_sb(struct super_block *sb)
blocking_notifier_chain_unregister(&efivar_ops_nh, &sfi->nb);
kill_litter_super(sb);
unregister_pm_notifier(&sfi->pm_nb);
/* Remove all entries and destroy */
efivar_entry_iter(efivarfs_destroy, &sfi->efivarfs_list, NULL);
kfree(sfi);
}

View File

@ -225,6 +225,31 @@ variable_matches(const char *var_name, size_t len, const char *match_name,
}
}
char *
efivar_get_utf8name(const efi_char16_t *name16, efi_guid_t *vendor)
{
int len = ucs2_utf8size(name16);
char *name;
/* name, plus '-', plus GUID, plus NUL*/
name = kmalloc(len + 1 + EFI_VARIABLE_GUID_LEN + 1, GFP_KERNEL);
if (!name)
return NULL;
ucs2_as_utf8(name, name16, len);
name[len] = '-';
efi_guid_to_str(vendor, name + len + 1);
name[len + EFI_VARIABLE_GUID_LEN+1] = '\0';
/* replace invalid slashes like kobject_set_name_vargs does for /sys/firmware/efi/vars. */
strreplace(name, '/', '!');
return name;
}
bool
efivar_validate(efi_guid_t vendor, efi_char16_t *var_name, u8 *data,
unsigned long data_size)
@ -288,28 +313,6 @@ efivar_variable_is_removable(efi_guid_t vendor, const char *var_name,
return found;
}
static bool variable_is_present(efi_char16_t *variable_name, efi_guid_t *vendor,
struct list_head *head)
{
struct efivar_entry *entry, *n;
unsigned long strsize1, strsize2;
bool found = false;
strsize1 = ucs2_strsize(variable_name, EFI_VAR_NAME_LEN);
list_for_each_entry_safe(entry, n, head, list) {
strsize2 = ucs2_strsize(entry->var.VariableName, EFI_VAR_NAME_LEN);
if (strsize1 == strsize2 &&
!memcmp(variable_name, &(entry->var.VariableName),
strsize2) &&
!efi_guidcmp(entry->var.VendorGuid,
*vendor)) {
found = true;
break;
}
}
return found;
}
/*
* Returns the size of variable_name, in bytes, including the
* terminating NULL character, or variable_name_size if no NULL
@ -361,16 +364,15 @@ static void dup_variable_bug(efi_char16_t *str16, efi_guid_t *vendor_guid,
* efivar_init - build the initial list of EFI variables
* @func: callback function to invoke for every variable
* @data: function-specific data to pass to @func
* @head: initialised head of variable list
* @duplicate_check: fail if a duplicate variable is found
*
* Get every EFI variable from the firmware and invoke @func. @func
* should call efivar_entry_add() to build the list of variables.
* should populate the initial dentry and inode tree.
*
* Returns 0 on success, or a kernel error code on failure.
*/
int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *,
struct list_head *),
void *data, struct list_head *head)
int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),
void *data, bool duplicate_check)
{
unsigned long variable_name_size = 512;
efi_char16_t *variable_name;
@ -414,14 +416,15 @@ int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *,
* we'll ever see a different variable name,
* and may end up looping here forever.
*/
if (variable_is_present(variable_name, &vendor_guid,
head)) {
if (duplicate_check &&
efivarfs_variable_is_present(variable_name,
&vendor_guid, data)) {
dup_variable_bug(variable_name, &vendor_guid,
variable_name_size);
status = EFI_NOT_FOUND;
} else {
err = func(variable_name, vendor_guid,
variable_name_size, data, head);
variable_name_size, data);
if (err)
status = EFI_NOT_FOUND;
}
@ -453,70 +456,12 @@ free:
}
/**
* efivar_entry_add - add entry to variable list
* @entry: entry to add to list
* @head: list head
*
* Returns 0 on success, or a kernel error code on failure.
*/
int efivar_entry_add(struct efivar_entry *entry, struct list_head *head)
{
int err;
err = efivar_lock();
if (err)
return err;
list_add(&entry->list, head);
efivar_unlock();
return 0;
}
/**
* __efivar_entry_add - add entry to variable list
* @entry: entry to add to list
* @head: list head
*/
void __efivar_entry_add(struct efivar_entry *entry, struct list_head *head)
{
list_add(&entry->list, head);
}
/**
* efivar_entry_remove - remove entry from variable list
* @entry: entry to remove from list
*
* Returns 0 on success, or a kernel error code on failure.
*/
void efivar_entry_remove(struct efivar_entry *entry)
{
list_del(&entry->list);
}
/*
* efivar_entry_list_del_unlock - remove entry from variable list
* @entry: entry to remove
*
* Remove @entry from the variable list and release the list lock.
*
* NOTE: slightly weird locking semantics here - we expect to be
* called with the efivars lock already held, and we release it before
* returning. This is because this function is usually called after
* set_variable() while the lock is still held.
*/
static void efivar_entry_list_del_unlock(struct efivar_entry *entry)
{
list_del(&entry->list);
efivar_unlock();
}
/**
* efivar_entry_delete - delete variable and remove entry from list
* efivar_entry_delete - delete variable
* @entry: entry containing variable to delete
*
* Delete the variable from the firmware and remove @entry from the
* variable list. It is the caller's responsibility to free @entry
* once we return.
* Delete the variable from the firmware. It is the caller's
* responsibility to free @entry (by deleting the dentry/inode) once
* we return.
*
* Returns 0 on success, -EINTR if we can't grab the semaphore,
* converted EFI status code if set_variable() fails.
@ -533,12 +478,10 @@ int efivar_entry_delete(struct efivar_entry *entry)
status = efivar_set_variable_locked(entry->var.VariableName,
&entry->var.VendorGuid,
0, 0, NULL, false);
if (!(status == EFI_SUCCESS || status == EFI_NOT_FOUND)) {
efivar_unlock();
efivar_unlock();
if (!(status == EFI_SUCCESS || status == EFI_NOT_FOUND))
return efi_status_to_err(status);
}
efivar_entry_list_del_unlock(entry);
return 0;
}
@ -632,7 +575,7 @@ int efivar_entry_get(struct efivar_entry *entry, u32 *attributes,
* get_variable() fail.
*
* If the EFI variable does not exist when calling set_variable()
* (EFI_NOT_FOUND), @entry is removed from the variable list.
* (EFI_NOT_FOUND).
*/
int efivar_entry_set_get_size(struct efivar_entry *entry, u32 attributes,
unsigned long *size, void *data, bool *set)
@ -648,9 +591,8 @@ int efivar_entry_set_get_size(struct efivar_entry *entry, u32 attributes,
return -EINVAL;
/*
* The lock here protects the get_variable call, the conditional
* set_variable call, and removal of the variable from the efivars
* list (in the case of an authenticated delete).
* The lock here protects the get_variable call and the
* conditional set_variable call
*/
err = efivar_lock();
if (err)
@ -676,10 +618,7 @@ int efivar_entry_set_get_size(struct efivar_entry *entry, u32 attributes,
&entry->var.VendorGuid,
NULL, size, NULL);
if (status == EFI_NOT_FOUND)
efivar_entry_list_del_unlock(entry);
else
efivar_unlock();
efivar_unlock();
if (status && status != EFI_BUFFER_TOO_SMALL)
return efi_status_to_err(status);
@ -691,37 +630,3 @@ out:
return err;
}
/**
* efivar_entry_iter - iterate over variable list
* @func: callback function
* @head: head of variable list
* @data: function-specific data to pass to callback
*
* Iterate over the list of EFI variables and call @func with every
* entry on the list. It is safe for @func to remove entries in the
* list via efivar_entry_delete() while iterating.
*
* Some notes for the callback function:
* - a non-zero return value indicates an error and terminates the loop
* - @func is called from atomic context
*/
int efivar_entry_iter(int (*func)(struct efivar_entry *, void *),
struct list_head *head, void *data)
{
struct efivar_entry *entry, *n;
int err = 0;
err = efivar_lock();
if (err)
return err;
list_for_each_entry_safe(entry, n, head, list) {
err = func(entry, data);
if (err)
break;
}
efivar_unlock();
return err;
}

View File

@ -363,7 +363,6 @@ void efi_native_runtime_setup(void);
#define ACPI_20_TABLE_GUID EFI_GUID(0x8868e871, 0xe4f1, 0x11d3, 0xbc, 0x22, 0x00, 0x80, 0xc7, 0x3c, 0x88, 0x81)
#define SMBIOS_TABLE_GUID EFI_GUID(0xeb9d2d31, 0x2d88, 0x11d3, 0x9a, 0x16, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d)
#define SMBIOS3_TABLE_GUID EFI_GUID(0xf2fd1544, 0x9794, 0x4a2c, 0x99, 0x2e, 0xe5, 0xbb, 0xcf, 0x20, 0xe3, 0x94)
#define UGA_IO_PROTOCOL_GUID EFI_GUID(0x61a4d49e, 0x6f68, 0x4f1b, 0xb9, 0x22, 0xa8, 0x6e, 0xed, 0x0b, 0x07, 0xa2)
#define EFI_GLOBAL_VARIABLE_GUID EFI_GUID(0x8be4df61, 0x93ca, 0x11d2, 0xaa, 0x0d, 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c)
#define UV_SYSTEM_TABLE_GUID EFI_GUID(0x3b13a7d4, 0x633e, 0x11dd, 0x93, 0xec, 0xda, 0x25, 0x56, 0xd8, 0x95, 0x93)
#define LINUX_EFI_CRASH_GUID EFI_GUID(0xcfc8fc79, 0xbe2e, 0x4ddc, 0x97, 0xf0, 0x9f, 0x98, 0xbf, 0xe2, 0x98, 0xa0)
@ -373,7 +372,6 @@ void efi_native_runtime_setup(void);
#define EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID EFI_GUID(0x8b843e20, 0x8132, 0x4852, 0x90, 0xcc, 0x55, 0x1a, 0x4e, 0x4a, 0x7f, 0x1c)
#define EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL_GUID EFI_GUID(0x05c99a21, 0xc70f, 0x4ad2, 0x8a, 0x5f, 0x35, 0xdf, 0x33, 0x43, 0xf5, 0x1e)
#define EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID EFI_GUID(0x9042a9de, 0x23dc, 0x4a38, 0x96, 0xfb, 0x7a, 0xde, 0xd0, 0x80, 0x51, 0x6a)
#define EFI_UGA_PROTOCOL_GUID EFI_GUID(0x982c298b, 0xf4fa, 0x41cb, 0xb8, 0x38, 0x77, 0xaa, 0x68, 0x8f, 0xb8, 0x39)
#define EFI_PCI_IO_PROTOCOL_GUID EFI_GUID(0x4cf5b200, 0x68b8, 0x4ca5, 0x9e, 0xec, 0xb2, 0x3e, 0x3f, 0x50, 0x02, 0x9a)
#define EFI_FILE_INFO_ID EFI_GUID(0x09576e92, 0x6d3f, 0x11d2, 0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b)
#define EFI_SYSTEM_RESOURCE_TABLE_GUID EFI_GUID(0xb122a263, 0x3661, 0x4f68, 0x99, 0x29, 0x78, 0xf8, 0xb0, 0xd6, 0x21, 0x80)
@ -1286,8 +1284,6 @@ struct linux_efi_memreserve {
void __init efi_arch_mem_reserve(phys_addr_t addr, u64 size);
char *efi_systab_show_arch(char *str);
/*
* The LINUX_EFI_MOK_VARIABLE_TABLE_GUID config table can be provided
* to the kernel by an EFI boot loader. The table contains a packed