First batch of EFI fixes for v6.3:

- Set the NX compat flag for arm64 and zboot, to ensure compatibility
   with EFI firmware that complies with tightening requirements imposed
   across the ecosystem.
 
 - Improve identification of Ampere Altra systems based on SMBIOS data.
 
 - Fix some issues related to the EFI framebuffer that were introduced
   as a result from some refactoring related to zboot and the merge with
   sysfb.
 
 - Makefile tweak to avoid rebuilding vmlinuz unnecessarily.
 
 - Fix efi_random_alloc() return value on out of memory condition.
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYIAB0WIQQQm/3uucuRGn1Dmh0wbglWLn0tXAUCZBxfeAAKCRAwbglWLn0t
 XJzfAQCiRPMpm5YomKDLdAtjXfwEbyevlYN/gDInAdX5ETzPqgD/WDSEmj3cqh+V
 Es3u5P/7ICC/qgCleq87qpUk0IPwEwo=
 =u0Zg
 -----END PGP SIGNATURE-----

Merge tag 'efi-fixes-for-v6.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/efi/efi

Pull EFI fixes from Ard Biesheuvel:

 - Set the NX compat flag for arm64 and zboot, to ensure compatibility
   with EFI firmware that complies with tightening requirements imposed
   across the ecosystem.

 - Improve identification of Ampere Altra systems based on SMBIOS data.

 - Fix some issues related to the EFI framebuffer that were introduced
   as a result from some refactoring related to zboot and the merge with
   sysfb.

 - Makefile tweak to avoid rebuilding vmlinuz unnecessarily.

 - Fix efi_random_alloc() return value on out of memory condition.

* tag 'efi-fixes-for-v6.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/efi/efi:
  efi/libstub: randomalloc: Return EFI_OUT_OF_RESOURCES on failure
  efi/libstub: Use relocated version of kernel's struct screen_info
  efi/libstub: zboot: Add compressed image to make targets
  efi: sysfb_efi: Add quirk for Lenovo Yoga Book X91F/L
  efi: sysfb_efi: Fix DMI quirks not working for simpledrm
  efi/libstub: smbios: Drop unused 'recsize' parameter
  arm64: efi: Use SMBIOS processor version to key off Ampere quirk
  efi/libstub: smbios: Use length member instead of record struct size
  efi: earlycon: Reprobe after parsing config tables
  arm64: efi: Set NX compat flag in PE/COFF header
  efi/libstub: arm64: Remap relocated image with strict permissions
  efi/libstub: zboot: Mark zboot EFI application as NX compatible
This commit is contained in:
Linus Torvalds 2023-03-24 10:07:38 -07:00
commit 877c20b104
19 changed files with 147 additions and 40 deletions

View File

@ -66,7 +66,7 @@
.long .Lefi_header_end - .L_head // SizeOfHeaders
.long 0 // CheckSum
.short IMAGE_SUBSYSTEM_EFI_APPLICATION // Subsystem
.short 0 // DllCharacteristics
.short IMAGE_DLL_CHARACTERISTICS_NX_COMPAT // DllCharacteristics
.quad 0 // SizeOfStackReserve
.quad 0 // SizeOfStackCommit
.quad 0 // SizeOfHeapReserve

View File

@ -215,6 +215,14 @@ efi_earlycon_write(struct console *con, const char *str, unsigned int num)
}
}
static bool __initdata fb_probed;
void __init efi_earlycon_reprobe(void)
{
if (fb_probed)
setup_earlycon("efifb");
}
static int __init efi_earlycon_setup(struct earlycon_device *device,
const char *opt)
{
@ -222,15 +230,17 @@ static int __init efi_earlycon_setup(struct earlycon_device *device,
u16 xres, yres;
u32 i;
if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI)
fb_wb = opt && !strcmp(opt, "ram");
if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI) {
fb_probed = true;
return -ENODEV;
}
fb_base = screen_info.lfb_base;
if (screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE)
fb_base |= (u64)screen_info.ext_lfb_base << 32;
fb_wb = opt && !strcmp(opt, "ram");
si = &screen_info;
xres = si->lfb_width;
yres = si->lfb_height;

View File

@ -72,6 +72,9 @@ static void __init init_screen_info(void)
if (memblock_is_map_memory(screen_info.lfb_base))
memblock_mark_nomap(screen_info.lfb_base,
screen_info.lfb_size);
if (IS_ENABLED(CONFIG_EFI_EARLYCON))
efi_earlycon_reprobe();
}
}

View File

@ -44,4 +44,4 @@ OBJCOPYFLAGS_vmlinuz.efi := -O binary
$(obj)/vmlinuz.efi: $(obj)/vmlinuz.efi.elf FORCE
$(call if_changed,objcopy)
targets += zboot-header.o vmlinuz.o vmlinuz.efi.elf vmlinuz.efi
targets += zboot-header.o vmlinuz vmlinuz.o vmlinuz.efi.elf vmlinuz.efi

View File

@ -85,8 +85,10 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
}
}
if (image->image_base != _text)
if (image->image_base != _text) {
efi_err("FIRMWARE BUG: efi_loaded_image_t::image_base has bogus value\n");
image->image_base = _text;
}
if (!IS_ALIGNED((u64)_text, SEGMENT_ALIGN))
efi_err("FIRMWARE BUG: kernel image not aligned on %dk boundary\n",
@ -139,6 +141,7 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
*image_addr = *reserve_addr;
memcpy((void *)*image_addr, _text, kernel_size);
caches_clean_inval_pou(*image_addr, *image_addr + kernel_codesize);
efi_remap_image(*image_addr, *reserve_size, kernel_codesize);
return EFI_SUCCESS;
}

View File

@ -16,20 +16,43 @@
static bool system_needs_vamap(void)
{
const u8 *type1_family = efi_get_smbios_string(1, family);
const struct efi_smbios_type4_record *record;
const u32 __aligned(1) *socid;
const u8 *version;
/*
* Ampere eMAG, Altra, and Altra Max machines crash in SetTime() if
* SetVirtualAddressMap() has not been called prior.
* SetVirtualAddressMap() has not been called prior. Most Altra systems
* can be identified by the SMCCC soc ID, which is conveniently exposed
* via the type 4 SMBIOS records. Otherwise, test the processor version
* field. eMAG systems all appear to have the processor version field
* set to "eMAG".
*/
if (!type1_family || (
strcmp(type1_family, "eMAG") &&
strcmp(type1_family, "Altra") &&
strcmp(type1_family, "Altra Max")))
record = (struct efi_smbios_type4_record *)efi_get_smbios_record(4);
if (!record)
return false;
efi_warn("Working around broken SetVirtualAddressMap()\n");
return true;
socid = (u32 *)record->processor_id;
switch (*socid & 0xffff000f) {
static char const altra[] = "Ampere(TM) Altra(TM) Processor";
static char const emag[] = "eMAG";
default:
version = efi_get_smbios_string(&record->header, 4,
processor_version);
if (!version || (strncmp(version, altra, sizeof(altra) - 1) &&
strncmp(version, emag, sizeof(emag) - 1)))
break;
fallthrough;
case 0x0a160001: // Altra
case 0x0a160002: // Altra Max
efi_warn("Working around broken SetVirtualAddressMap()\n");
return true;
}
return false;
}
efi_status_t check_platform_features(void)

View File

@ -5,6 +5,15 @@
#include "efistub.h"
static unsigned long screen_info_offset;
struct screen_info *alloc_screen_info(void)
{
if (IS_ENABLED(CONFIG_ARM))
return __alloc_screen_info();
return (void *)&screen_info + screen_info_offset;
}
/*
* EFI entry point for the generic EFI stub used by ARM, arm64, RISC-V and
* LoongArch. This is the entrypoint that is described in the PE/COFF header
@ -56,6 +65,8 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
return status;
}
screen_info_offset = image_addr - (unsigned long)image->image_base;
status = efi_stub_common(handle, image, image_addr, cmdline_ptr);
efi_free(image_size, image_addr);

View File

@ -47,11 +47,6 @@
static u64 virtmap_base = EFI_RT_VIRTUAL_BASE;
static bool flat_va_mapping = (EFI_RT_VIRTUAL_OFFSET != 0);
struct screen_info * __weak alloc_screen_info(void)
{
return &screen_info;
}
void __weak free_screen_info(struct screen_info *si)
{
}

View File

@ -1062,6 +1062,7 @@ efi_enable_reset_attack_mitigation(void) { }
void efi_retrieve_tpm2_eventlog(void);
struct screen_info *alloc_screen_info(void);
struct screen_info *__alloc_screen_info(void);
void free_screen_info(struct screen_info *si);
void efi_cache_sync_image(unsigned long image_base,
@ -1074,6 +1075,8 @@ struct efi_smbios_record {
u16 handle;
};
const struct efi_smbios_record *efi_get_smbios_record(u8 type);
struct efi_smbios_type1_record {
struct efi_smbios_record header;
@ -1087,14 +1090,46 @@ struct efi_smbios_type1_record {
u8 family;
};
#define efi_get_smbios_string(__type, __name) ({ \
int size = sizeof(struct efi_smbios_type ## __type ## _record); \
struct efi_smbios_type4_record {
struct efi_smbios_record header;
u8 socket;
u8 processor_type;
u8 processor_family;
u8 processor_manufacturer;
u8 processor_id[8];
u8 processor_version;
u8 voltage;
u16 external_clock;
u16 max_speed;
u16 current_speed;
u8 status;
u8 processor_upgrade;
u16 l1_cache_handle;
u16 l2_cache_handle;
u16 l3_cache_handle;
u8 serial_number;
u8 asset_tag;
u8 part_number;
u8 core_count;
u8 enabled_core_count;
u8 thread_count;
u16 processor_characteristics;
u16 processor_family2;
u16 core_count2;
u16 enabled_core_count2;
u16 thread_count2;
u16 thread_enabled;
};
#define efi_get_smbios_string(__record, __type, __name) ({ \
int off = offsetof(struct efi_smbios_type ## __type ## _record, \
__name); \
__efi_get_smbios_string(__type, off, size); \
__efi_get_smbios_string((__record), __type, off); \
})
const u8 *__efi_get_smbios_string(u8 type, int offset, int recsize);
const u8 *__efi_get_smbios_string(const struct efi_smbios_record *record,
u8 type, int offset);
void efi_remap_image(unsigned long image_base, unsigned alloc_size,
unsigned long code_size);

View File

@ -101,6 +101,7 @@ efi_status_t efi_random_alloc(unsigned long size,
* to calculate the randomly chosen address, and allocate it directly
* using EFI_ALLOCATE_ADDRESS.
*/
status = EFI_OUT_OF_RESOURCES;
for (map_offset = 0; map_offset < map->map_size; map_offset += map->desc_size) {
efi_memory_desc_t *md = (void *)map->map + map_offset;
efi_physical_addr_t target;

View File

@ -15,18 +15,11 @@
* early, but it only works if the EFI stub is part of the core kernel image
* itself. The zboot decompressor can only use the configuration table
* approach.
*
* In order to support both methods from the same build of the EFI stub
* library, provide this dummy global definition of struct screen_info. If it
* is required to satisfy a link dependency, it means we need to override the
* __weak alloc and free methods with the ones below, and those will be pulled
* in as well.
*/
struct screen_info screen_info;
static efi_guid_t screen_info_guid = LINUX_EFI_SCREEN_INFO_TABLE_GUID;
struct screen_info *alloc_screen_info(void)
struct screen_info *__alloc_screen_info(void)
{
struct screen_info *si;
efi_status_t status;

View File

@ -22,21 +22,30 @@ struct efi_smbios_protocol {
u8 minor_version;
};
const u8 *__efi_get_smbios_string(u8 type, int offset, int recsize)
const struct efi_smbios_record *efi_get_smbios_record(u8 type)
{
struct efi_smbios_record *record;
efi_smbios_protocol_t *smbios;
efi_status_t status;
u16 handle = 0xfffe;
const u8 *strtable;
status = efi_bs_call(locate_protocol, &EFI_SMBIOS_PROTOCOL_GUID, NULL,
(void **)&smbios) ?:
efi_call_proto(smbios, get_next, &handle, &type, &record, NULL);
if (status != EFI_SUCCESS)
return NULL;
return record;
}
strtable = (u8 *)record + recsize;
const u8 *__efi_get_smbios_string(const struct efi_smbios_record *record,
u8 type, int offset)
{
const u8 *strtable;
if (!record)
return NULL;
strtable = (u8 *)record + record->length;
for (int i = 1; i < ((u8 *)record)[offset]; i++) {
int len = strlen(strtable);

View File

@ -63,7 +63,7 @@ __efistub_efi_zboot_header:
.long .Lefi_header_end - .Ldoshdr
.long 0
.short IMAGE_SUBSYSTEM_EFI_APPLICATION
.short 0
.short IMAGE_DLL_CHARACTERISTICS_NX_COMPAT
#ifdef CONFIG_64BIT
.quad 0, 0, 0, 0
#else

View File

@ -57,6 +57,11 @@ void __weak efi_cache_sync_image(unsigned long image_base,
// executable code loaded into memory to be safe for execution.
}
struct screen_info *alloc_screen_info(void)
{
return __alloc_screen_info();
}
asmlinkage efi_status_t __efiapi
efi_zboot_entry(efi_handle_t handle, efi_system_table_t *systab)
{

View File

@ -272,6 +272,14 @@ static const struct dmi_system_id efifb_dmi_swap_width_height[] __initconst = {
"IdeaPad Duet 3 10IGL5"),
},
},
{
/* Lenovo Yoga Book X91F / X91L */
.matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"),
/* Non exact match to match F + L versions */
DMI_MATCH(DMI_PRODUCT_NAME, "Lenovo YB1-X91"),
},
},
{},
};
@ -341,7 +349,7 @@ static const struct fwnode_operations efifb_fwnode_ops = {
#ifdef CONFIG_EFI
static struct fwnode_handle efifb_fwnode;
__init void sysfb_apply_efi_quirks(struct platform_device *pd)
__init void sysfb_apply_efi_quirks(void)
{
if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI ||
!(screen_info.capabilities & VIDEO_CAPABILITY_SKIP_QUIRKS))
@ -355,7 +363,10 @@ __init void sysfb_apply_efi_quirks(struct platform_device *pd)
screen_info.lfb_height = temp;
screen_info.lfb_linelength = 4 * screen_info.lfb_width;
}
}
__init void sysfb_set_efifb_fwnode(struct platform_device *pd)
{
if (screen_info.orig_video_isVGA == VIDEO_TYPE_EFI && IS_ENABLED(CONFIG_PCI)) {
fwnode_init(&efifb_fwnode, &efifb_fwnode_ops);
pd->dev.fwnode = &efifb_fwnode;

View File

@ -81,6 +81,8 @@ static __init int sysfb_init(void)
if (disabled)
goto unlock_mutex;
sysfb_apply_efi_quirks();
/* try to create a simple-framebuffer device */
compatible = sysfb_parse_mode(si, &mode);
if (compatible) {
@ -107,7 +109,7 @@ static __init int sysfb_init(void)
goto unlock_mutex;
}
sysfb_apply_efi_quirks(pd);
sysfb_set_efifb_fwnode(pd);
ret = platform_device_add_data(pd, si, sizeof(*si));
if (ret)

View File

@ -141,7 +141,7 @@ __init struct platform_device *sysfb_create_simplefb(const struct screen_info *s
if (!pd)
return ERR_PTR(-ENOMEM);
sysfb_apply_efi_quirks(pd);
sysfb_set_efifb_fwnode(pd);
ret = platform_device_add_resources(pd, &res, 1);
if (ret)

View File

@ -693,6 +693,7 @@ efi_guid_to_str(efi_guid_t *guid, char *out)
}
extern void efi_init (void);
extern void efi_earlycon_reprobe(void);
#ifdef CONFIG_EFI
extern void efi_enter_virtual_mode (void); /* switch EFI to virtual mode, if possible */
#else

View File

@ -70,11 +70,16 @@ static inline void sysfb_disable(void)
#ifdef CONFIG_EFI
extern struct efifb_dmi_info efifb_dmi_list[];
void sysfb_apply_efi_quirks(struct platform_device *pd);
void sysfb_apply_efi_quirks(void);
void sysfb_set_efifb_fwnode(struct platform_device *pd);
#else /* CONFIG_EFI */
static inline void sysfb_apply_efi_quirks(struct platform_device *pd)
static inline void sysfb_apply_efi_quirks(void)
{
}
static inline void sysfb_set_efifb_fwnode(struct platform_device *pd)
{
}