Skip to content

Commit ab57bc6

Browse files
committed
Merge tag 'efi-fixes-for-v6.1-3' of git://git.kernel.org/pub/scm/linux/kernel/git/efi/efi
Pull EFI fixes from Ard Biesheuvel: - Force the use of SetVirtualAddressMap() on Ampera Altra arm64 machines, which crash in SetTime() if no virtual remapping is used This is the first time we've added an SMBIOS based quirk on arm64, but fortunately, we can just call a EFI protocol to grab the type #1 SMBIOS record when running in the stub, so we don't need all the machinery we have in the kernel proper to parse SMBIOS data. - Drop a spurious warning on misaligned runtime regions when using 16k or 64k pages on arm64 * tag 'efi-fixes-for-v6.1-3' of git://git.kernel.org/pub/scm/linux/kernel/git/efi/efi: arm64: efi: Fix handling of misaligned runtime regions and drop warning arm64: efi: Force the use of SetVirtualAddressMap() on Altra machines
2 parents fef7fd4 + 9b9eaee commit ab57bc6

File tree

6 files changed

+128
-20
lines changed

6 files changed

+128
-20
lines changed

arch/arm64/kernel/efi.c

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@
1313

1414
#include <asm/efi.h>
1515

16+
static bool region_is_misaligned(const efi_memory_desc_t *md)
17+
{
18+
if (PAGE_SIZE == EFI_PAGE_SIZE)
19+
return false;
20+
return !PAGE_ALIGNED(md->phys_addr) ||
21+
!PAGE_ALIGNED(md->num_pages << EFI_PAGE_SHIFT);
22+
}
23+
1624
/*
1725
* Only regions of type EFI_RUNTIME_SERVICES_CODE need to be
1826
* executable, everything else can be mapped with the XN bits
@@ -26,14 +34,22 @@ static __init pteval_t create_mapping_protection(efi_memory_desc_t *md)
2634
if (type == EFI_MEMORY_MAPPED_IO)
2735
return PROT_DEVICE_nGnRE;
2836

29-
if (WARN_ONCE(!PAGE_ALIGNED(md->phys_addr),
30-
"UEFI Runtime regions are not aligned to 64 KB -- buggy firmware?"))
37+
if (region_is_misaligned(md)) {
38+
static bool __initdata code_is_misaligned;
39+
3140
/*
32-
* If the region is not aligned to the page size of the OS, we
33-
* can not use strict permissions, since that would also affect
34-
* the mapping attributes of the adjacent regions.
41+
* Regions that are not aligned to the OS page size cannot be
42+
* mapped with strict permissions, as those might interfere
43+
* with the permissions that are needed by the adjacent
44+
* region's mapping. However, if we haven't encountered any
45+
* misaligned runtime code regions so far, we can safely use
46+
* non-executable permissions for non-code regions.
3547
*/
36-
return pgprot_val(PAGE_KERNEL_EXEC);
48+
code_is_misaligned |= (type == EFI_RUNTIME_SERVICES_CODE);
49+
50+
return code_is_misaligned ? pgprot_val(PAGE_KERNEL_EXEC)
51+
: pgprot_val(PAGE_KERNEL);
52+
}
3753

3854
/* R-- */
3955
if ((attr & (EFI_MEMORY_XP | EFI_MEMORY_RO)) ==
@@ -64,19 +80,16 @@ int __init efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md)
6480
bool page_mappings_only = (md->type == EFI_RUNTIME_SERVICES_CODE ||
6581
md->type == EFI_RUNTIME_SERVICES_DATA);
6682

67-
if (!PAGE_ALIGNED(md->phys_addr) ||
68-
!PAGE_ALIGNED(md->num_pages << EFI_PAGE_SHIFT)) {
69-
/*
70-
* If the end address of this region is not aligned to page
71-
* size, the mapping is rounded up, and may end up sharing a
72-
* page frame with the next UEFI memory region. If we create
73-
* a block entry now, we may need to split it again when mapping
74-
* the next region, and support for that is going to be removed
75-
* from the MMU routines. So avoid block mappings altogether in
76-
* that case.
77-
*/
83+
/*
84+
* If this region is not aligned to the page size used by the OS, the
85+
* mapping will be rounded outwards, and may end up sharing a page
86+
* frame with an adjacent runtime memory region. Given that the page
87+
* table descriptor covering the shared page will be rewritten when the
88+
* adjacent region gets mapped, we must avoid block mappings here so we
89+
* don't have to worry about splitting them when that happens.
90+
*/
91+
if (region_is_misaligned(md))
7892
page_mappings_only = true;
79-
}
8093

8194
create_pgd_mapping(mm, md->phys_addr, md->virt_addr,
8295
md->num_pages << EFI_PAGE_SHIFT,
@@ -103,6 +116,9 @@ int __init efi_set_mapping_permissions(struct mm_struct *mm,
103116
BUG_ON(md->type != EFI_RUNTIME_SERVICES_CODE &&
104117
md->type != EFI_RUNTIME_SERVICES_DATA);
105118

119+
if (region_is_misaligned(md))
120+
return 0;
121+
106122
/*
107123
* Calling apply_to_page_range() is only safe on regions that are
108124
* guaranteed to be mapped down to pages. Since we are only called

drivers/firmware/efi/libstub/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ $(obj)/lib-%.o: $(srctree)/lib/%.c FORCE
8282
lib-$(CONFIG_EFI_GENERIC_STUB) += efi-stub.o string.o intrinsics.o systable.o
8383

8484
lib-$(CONFIG_ARM) += arm32-stub.o
85-
lib-$(CONFIG_ARM64) += arm64-stub.o
85+
lib-$(CONFIG_ARM64) += arm64-stub.o smbios.o
8686
lib-$(CONFIG_X86) += x86-stub.o
8787
lib-$(CONFIG_RISCV) += riscv-stub.o
8888
lib-$(CONFIG_LOONGARCH) += loongarch-stub.o

drivers/firmware/efi/libstub/arm64-stub.c

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,21 @@
1515

1616
#include "efistub.h"
1717

18+
static bool system_needs_vamap(void)
19+
{
20+
const u8 *type1_family = efi_get_smbios_string(1, family);
21+
22+
/*
23+
* Ampere Altra machines crash in SetTime() if SetVirtualAddressMap()
24+
* has not been called prior.
25+
*/
26+
if (!type1_family || strcmp(type1_family, "Altra"))
27+
return false;
28+
29+
efi_warn("Working around broken SetVirtualAddressMap()\n");
30+
return true;
31+
}
32+
1833
efi_status_t check_platform_features(void)
1934
{
2035
u64 tg;
@@ -24,7 +39,7 @@ efi_status_t check_platform_features(void)
2439
* UEFI runtime regions 1:1 and so calling SetVirtualAddressMap() is
2540
* unnecessary.
2641
*/
27-
if (VA_BITS_MIN >= 48)
42+
if (VA_BITS_MIN >= 48 && !system_needs_vamap())
2843
efi_novamap = true;
2944

3045
/* UEFI mandates support for 4 KB granularity, no need to check */

drivers/firmware/efi/libstub/efistub.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -975,4 +975,32 @@ efi_enable_reset_attack_mitigation(void) { }
975975

976976
void efi_retrieve_tpm2_eventlog(void);
977977

978+
struct efi_smbios_record {
979+
u8 type;
980+
u8 length;
981+
u16 handle;
982+
};
983+
984+
struct efi_smbios_type1_record {
985+
struct efi_smbios_record header;
986+
987+
u8 manufacturer;
988+
u8 product_name;
989+
u8 version;
990+
u8 serial_number;
991+
efi_guid_t uuid;
992+
u8 wakeup_type;
993+
u8 sku_number;
994+
u8 family;
995+
};
996+
997+
#define efi_get_smbios_string(__type, __name) ({ \
998+
int size = sizeof(struct efi_smbios_type ## __type ## _record); \
999+
int off = offsetof(struct efi_smbios_type ## __type ## _record, \
1000+
__name); \
1001+
__efi_get_smbios_string(__type, off, size); \
1002+
})
1003+
1004+
const u8 *__efi_get_smbios_string(u8 type, int offset, int recsize);
1005+
9781006
#endif

drivers/firmware/efi/libstub/smbios.c

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
// Copyright 2022 Google LLC
3+
// Author: Ard Biesheuvel <[email protected]>
4+
5+
#include <linux/efi.h>
6+
7+
#include "efistub.h"
8+
9+
typedef struct efi_smbios_protocol efi_smbios_protocol_t;
10+
11+
struct efi_smbios_protocol {
12+
efi_status_t (__efiapi *add)(efi_smbios_protocol_t *, efi_handle_t,
13+
u16 *, struct efi_smbios_record *);
14+
efi_status_t (__efiapi *update_string)(efi_smbios_protocol_t *, u16 *,
15+
unsigned long *, u8 *);
16+
efi_status_t (__efiapi *remove)(efi_smbios_protocol_t *, u16);
17+
efi_status_t (__efiapi *get_next)(efi_smbios_protocol_t *, u16 *, u8 *,
18+
struct efi_smbios_record **,
19+
efi_handle_t *);
20+
21+
u8 major_version;
22+
u8 minor_version;
23+
};
24+
25+
const u8 *__efi_get_smbios_string(u8 type, int offset, int recsize)
26+
{
27+
struct efi_smbios_record *record;
28+
efi_smbios_protocol_t *smbios;
29+
efi_status_t status;
30+
u16 handle = 0xfffe;
31+
const u8 *strtable;
32+
33+
status = efi_bs_call(locate_protocol, &EFI_SMBIOS_PROTOCOL_GUID, NULL,
34+
(void **)&smbios) ?:
35+
efi_call_proto(smbios, get_next, &handle, &type, &record, NULL);
36+
if (status != EFI_SUCCESS)
37+
return NULL;
38+
39+
strtable = (u8 *)record + recsize;
40+
for (int i = 1; i < ((u8 *)record)[offset]; i++) {
41+
int len = strlen(strtable);
42+
43+
if (!len)
44+
return NULL;
45+
strtable += len + 1;
46+
}
47+
return strtable;
48+
}

include/linux/efi.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,7 @@ void efi_native_runtime_setup(void);
389389
#define EFI_LOAD_FILE2_PROTOCOL_GUID EFI_GUID(0x4006c0c1, 0xfcb3, 0x403e, 0x99, 0x6d, 0x4a, 0x6c, 0x87, 0x24, 0xe0, 0x6d)
390390
#define EFI_RT_PROPERTIES_TABLE_GUID EFI_GUID(0xeb66918a, 0x7eef, 0x402a, 0x84, 0x2e, 0x93, 0x1d, 0x21, 0xc3, 0x8a, 0xe9)
391391
#define EFI_DXE_SERVICES_TABLE_GUID EFI_GUID(0x05ad34ba, 0x6f02, 0x4214, 0x95, 0x2e, 0x4d, 0xa0, 0x39, 0x8e, 0x2b, 0xb9)
392+
#define EFI_SMBIOS_PROTOCOL_GUID EFI_GUID(0x03583ff6, 0xcb36, 0x4940, 0x94, 0x7e, 0xb9, 0xb3, 0x9f, 0x4a, 0xfa, 0xf7)
392393

393394
#define EFI_IMAGE_SECURITY_DATABASE_GUID EFI_GUID(0xd719b2cb, 0x3d3a, 0x4596, 0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f)
394395
#define EFI_SHIM_LOCK_GUID EFI_GUID(0x605dab50, 0xe046, 0x4300, 0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23)

0 commit comments

Comments
 (0)