Message ID | 20230516120646.31195-1-kirill.shutemov@linux.intel.com |
---|---|
State | Superseded |
Headers | show |
Series | None | expand |
On Tue, 16 May 2023 at 14:07, Kirill A. Shutemov <kirill.shutemov@linux.intel.com> wrote: > > efi_config_parse_tables() reserves memory that holds unaccepted memory > configuration table so it won't be reused by page allocator. > > Core-mm requires few helpers to support unaccepted memory: > > - accept_memory() checks the range of addresses against the bitmap and > accept memory if needed. > > - range_contains_unaccepted_memory() checks if anything within the > range requires acceptance. > > Architectural code has to provide efi_get_unaccepted_table() that > returns pointer to the unaccepted memory configuration table. > > arch_accept_memory() handles arch-specific part of memory acceptance. > > Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com> > Reviewed-by: Ard Biesheuvel <ardb@kernel.org> > v11.1: > - Add missing memblock_reserve() for the unaccepted memory > configuration table. > > --- > drivers/firmware/efi/Makefile | 1 + > drivers/firmware/efi/efi.c | 25 ++++++ > drivers/firmware/efi/unaccepted_memory.c | 103 +++++++++++++++++++++++ > include/linux/efi.h | 1 + > 4 files changed, 130 insertions(+) > create mode 100644 drivers/firmware/efi/unaccepted_memory.c > > diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile > index b51f2a4c821e..e489fefd23da 100644 > --- a/drivers/firmware/efi/Makefile > +++ b/drivers/firmware/efi/Makefile > @@ -41,3 +41,4 @@ obj-$(CONFIG_EFI_CAPSULE_LOADER) += capsule-loader.o > obj-$(CONFIG_EFI_EARLYCON) += earlycon.o > obj-$(CONFIG_UEFI_CPER_ARM) += cper-arm.o > obj-$(CONFIG_UEFI_CPER_X86) += cper-x86.o > +obj-$(CONFIG_UNACCEPTED_MEMORY) += unaccepted_memory.o > diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c > index 7dce06e419c5..d817e7afd266 100644 > --- a/drivers/firmware/efi/efi.c > +++ b/drivers/firmware/efi/efi.c > @@ -50,6 +50,9 @@ struct efi __read_mostly efi = { > #ifdef CONFIG_EFI_COCO_SECRET > .coco_secret = EFI_INVALID_TABLE_ADDR, > #endif > +#ifdef CONFIG_UNACCEPTED_MEMORY > + .unaccepted = EFI_INVALID_TABLE_ADDR, > +#endif > }; > EXPORT_SYMBOL(efi); > > @@ -605,6 +608,9 @@ static const efi_config_table_type_t common_tables[] __initconst = { > #ifdef CONFIG_EFI_COCO_SECRET > {LINUX_EFI_COCO_SECRET_AREA_GUID, &efi.coco_secret, "CocoSecret" }, > #endif > +#ifdef CONFIG_UNACCEPTED_MEMORY > + {LINUX_EFI_UNACCEPTED_MEM_TABLE_GUID, &efi.unaccepted, "Unaccepted" }, > +#endif > #ifdef CONFIG_EFI_GENERIC_STUB > {LINUX_EFI_SCREEN_INFO_TABLE_GUID, &screen_info_table }, > #endif > @@ -759,6 +765,25 @@ int __init efi_config_parse_tables(const efi_config_table_t *config_tables, > } > } > > + if (IS_ENABLED(CONFIG_UNACCEPTED_MEMORY) && > + efi.unaccepted != EFI_INVALID_TABLE_ADDR) { > + struct efi_unaccepted_memory *unaccepted; > + > + unaccepted = early_memremap(efi.unaccepted, sizeof(*unaccepted)); > + if (unaccepted) { > + unsigned long size; > + > + if (unaccepted->version == 1) { > + size = sizeof(*unaccepted) + unaccepted->size; > + memblock_reserve(efi.unaccepted, size); > + } else { > + efi.unaccepted = EFI_INVALID_TABLE_ADDR; > + } > + > + early_memunmap(unaccepted, sizeof(*unaccepted)); > + } > + } > + > return 0; > } > > diff --git a/drivers/firmware/efi/unaccepted_memory.c b/drivers/firmware/efi/unaccepted_memory.c > new file mode 100644 > index 000000000000..bb91c41f76fb > --- /dev/null > +++ b/drivers/firmware/efi/unaccepted_memory.c > @@ -0,0 +1,103 @@ > +// SPDX-License-Identifier: GPL-2.0-only > + > +#include <linux/efi.h> > +#include <linux/memblock.h> > +#include <linux/spinlock.h> > +#include <asm/unaccepted_memory.h> > + > +/* Protects unaccepted memory bitmap */ > +static DEFINE_SPINLOCK(unaccepted_memory_lock); > + > +void accept_memory(phys_addr_t start, phys_addr_t end) > +{ > + struct efi_unaccepted_memory *unaccepted; > + unsigned long range_start, range_end; > + unsigned long flags; > + u64 unit_size; > + > + if (efi.unaccepted == EFI_INVALID_TABLE_ADDR) > + return; > + > + unaccepted = efi_get_unaccepted_table(); > + if (!unaccepted) > + return; > + > + unit_size = unaccepted->unit_size; > + > + /* > + * Only care for the part of the range that is represented > + * in the bitmap. > + */ > + if (start < unaccepted->phys_base) > + start = unaccepted->phys_base; > + if (end < unaccepted->phys_base) > + return; > + > + /* Translate to offsets from the beginning of the bitmap */ > + start -= unaccepted->phys_base; > + end -= unaccepted->phys_base; > + > + /* Make sure not to overrun the bitmap */ > + if (end > unaccepted->size * unit_size * BITS_PER_BYTE) > + end = unaccepted->size * unit_size * BITS_PER_BYTE; > + > + range_start = start / unit_size; > + > + spin_lock_irqsave(&unaccepted_memory_lock, flags); > + for_each_set_bitrange_from(range_start, range_end, unaccepted->bitmap, > + DIV_ROUND_UP(end, unit_size)) { > + unsigned long phys_start, phys_end; > + unsigned long len = range_end - range_start; > + > + phys_start = range_start * unit_size + unaccepted->phys_base; > + phys_end = range_end * unit_size + unaccepted->phys_base; > + > + arch_accept_memory(phys_start, phys_end); > + bitmap_clear(unaccepted->bitmap, range_start, len); > + } > + spin_unlock_irqrestore(&unaccepted_memory_lock, flags); > +} > + > +bool range_contains_unaccepted_memory(phys_addr_t start, phys_addr_t end) > +{ > + struct efi_unaccepted_memory *unaccepted; > + unsigned long flags; > + bool ret = false; > + u64 unit_size; > + > + unaccepted = efi_get_unaccepted_table(); > + if (!unaccepted) > + return false; > + > + unit_size = unaccepted->unit_size; > + > + /* > + * Only care for the part of the range that is represented > + * in the bitmap. > + */ > + if (start < unaccepted->phys_base) > + start = unaccepted->phys_base; > + if (end < unaccepted->phys_base) > + return false; > + > + /* Translate to offsets from the beginning of the bitmap */ > + start -= unaccepted->phys_base; > + end -= unaccepted->phys_base; > + > + /* Make sure not to overrun the bitmap */ > + if (end > unaccepted->size * unit_size * BITS_PER_BYTE) > + end = unaccepted->size * unit_size * BITS_PER_BYTE; > + > + spin_lock_irqsave(&unaccepted_memory_lock, flags); > + while (start < end) { > + if (test_bit(start / unit_size, unaccepted->bitmap)) { > + ret = true; > + break; > + } > + > + start += unit_size; > + } > + spin_unlock_irqrestore(&unaccepted_memory_lock, flags); > + > + return ret; > +} > diff --git a/include/linux/efi.h b/include/linux/efi.h > index 29cc622910da..9864f9c00da2 100644 > --- a/include/linux/efi.h > +++ b/include/linux/efi.h > @@ -646,6 +646,7 @@ extern struct efi { > unsigned long tpm_final_log; /* TPM2 Final Events Log table */ > unsigned long mokvar_table; /* MOK variable config table */ > unsigned long coco_secret; /* Confidential computing secret table */ > + unsigned long unaccepted; /* Unaccepted memory table */ > > efi_get_time_t *get_time; > efi_set_time_t *set_time; > -- > 2.39.3 >
On 5/16/23 07:06, Kirill A. Shutemov wrote: > efi_config_parse_tables() reserves memory that holds unaccepted memory > configuration table so it won't be reused by page allocator. > > Core-mm requires few helpers to support unaccepted memory: > > - accept_memory() checks the range of addresses against the bitmap and > accept memory if needed. > > - range_contains_unaccepted_memory() checks if anything within the > range requires acceptance. > > Architectural code has to provide efi_get_unaccepted_table() that > returns pointer to the unaccepted memory configuration table. > > arch_accept_memory() handles arch-specific part of memory acceptance. > > Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com> Can you also add the efi.unaccepted table to the efi_tables array in arch/x86/platform/efi/efi.c? With that... Reviewed-by: Tom Lendacky <thomas.lendacky@amd.com> > > v11.1: > - Add missing memblock_reserve() for the unaccepted memory > configuration table. > > --- > drivers/firmware/efi/Makefile | 1 + > drivers/firmware/efi/efi.c | 25 ++++++ > drivers/firmware/efi/unaccepted_memory.c | 103 +++++++++++++++++++++++ > include/linux/efi.h | 1 + > 4 files changed, 130 insertions(+) > create mode 100644 drivers/firmware/efi/unaccepted_memory.c > > diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile > index b51f2a4c821e..e489fefd23da 100644 > --- a/drivers/firmware/efi/Makefile > +++ b/drivers/firmware/efi/Makefile > @@ -41,3 +41,4 @@ obj-$(CONFIG_EFI_CAPSULE_LOADER) += capsule-loader.o > obj-$(CONFIG_EFI_EARLYCON) += earlycon.o > obj-$(CONFIG_UEFI_CPER_ARM) += cper-arm.o > obj-$(CONFIG_UEFI_CPER_X86) += cper-x86.o > +obj-$(CONFIG_UNACCEPTED_MEMORY) += unaccepted_memory.o > diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c > index 7dce06e419c5..d817e7afd266 100644 > --- a/drivers/firmware/efi/efi.c > +++ b/drivers/firmware/efi/efi.c > @@ -50,6 +50,9 @@ struct efi __read_mostly efi = { > #ifdef CONFIG_EFI_COCO_SECRET > .coco_secret = EFI_INVALID_TABLE_ADDR, > #endif > +#ifdef CONFIG_UNACCEPTED_MEMORY > + .unaccepted = EFI_INVALID_TABLE_ADDR, > +#endif > }; > EXPORT_SYMBOL(efi); > > @@ -605,6 +608,9 @@ static const efi_config_table_type_t common_tables[] __initconst = { > #ifdef CONFIG_EFI_COCO_SECRET > {LINUX_EFI_COCO_SECRET_AREA_GUID, &efi.coco_secret, "CocoSecret" }, > #endif > +#ifdef CONFIG_UNACCEPTED_MEMORY > + {LINUX_EFI_UNACCEPTED_MEM_TABLE_GUID, &efi.unaccepted, "Unaccepted" }, > +#endif > #ifdef CONFIG_EFI_GENERIC_STUB > {LINUX_EFI_SCREEN_INFO_TABLE_GUID, &screen_info_table }, > #endif > @@ -759,6 +765,25 @@ int __init efi_config_parse_tables(const efi_config_table_t *config_tables, > } > } > > + if (IS_ENABLED(CONFIG_UNACCEPTED_MEMORY) && > + efi.unaccepted != EFI_INVALID_TABLE_ADDR) { > + struct efi_unaccepted_memory *unaccepted; > + > + unaccepted = early_memremap(efi.unaccepted, sizeof(*unaccepted)); > + if (unaccepted) { > + unsigned long size; > + > + if (unaccepted->version == 1) { > + size = sizeof(*unaccepted) + unaccepted->size; > + memblock_reserve(efi.unaccepted, size); > + } else { > + efi.unaccepted = EFI_INVALID_TABLE_ADDR; > + } > + > + early_memunmap(unaccepted, sizeof(*unaccepted)); > + } > + } > + > return 0; > } > > diff --git a/drivers/firmware/efi/unaccepted_memory.c b/drivers/firmware/efi/unaccepted_memory.c > new file mode 100644 > index 000000000000..bb91c41f76fb > --- /dev/null > +++ b/drivers/firmware/efi/unaccepted_memory.c > @@ -0,0 +1,103 @@ > +// SPDX-License-Identifier: GPL-2.0-only > + > +#include <linux/efi.h> > +#include <linux/memblock.h> > +#include <linux/spinlock.h> > +#include <asm/unaccepted_memory.h> > + > +/* Protects unaccepted memory bitmap */ > +static DEFINE_SPINLOCK(unaccepted_memory_lock); > + > +void accept_memory(phys_addr_t start, phys_addr_t end) > +{ > + struct efi_unaccepted_memory *unaccepted; > + unsigned long range_start, range_end; > + unsigned long flags; > + u64 unit_size; > + > + if (efi.unaccepted == EFI_INVALID_TABLE_ADDR) > + return; > + > + unaccepted = efi_get_unaccepted_table(); > + if (!unaccepted) > + return; > + > + unit_size = unaccepted->unit_size; > + > + /* > + * Only care for the part of the range that is represented > + * in the bitmap. > + */ > + if (start < unaccepted->phys_base) > + start = unaccepted->phys_base; > + if (end < unaccepted->phys_base) > + return; > + > + /* Translate to offsets from the beginning of the bitmap */ > + start -= unaccepted->phys_base; > + end -= unaccepted->phys_base; > + > + /* Make sure not to overrun the bitmap */ > + if (end > unaccepted->size * unit_size * BITS_PER_BYTE) > + end = unaccepted->size * unit_size * BITS_PER_BYTE; > + > + range_start = start / unit_size; > + > + spin_lock_irqsave(&unaccepted_memory_lock, flags); > + for_each_set_bitrange_from(range_start, range_end, unaccepted->bitmap, > + DIV_ROUND_UP(end, unit_size)) { > + unsigned long phys_start, phys_end; > + unsigned long len = range_end - range_start; > + > + phys_start = range_start * unit_size + unaccepted->phys_base; > + phys_end = range_end * unit_size + unaccepted->phys_base; > + > + arch_accept_memory(phys_start, phys_end); > + bitmap_clear(unaccepted->bitmap, range_start, len); > + } > + spin_unlock_irqrestore(&unaccepted_memory_lock, flags); > +} > + > +bool range_contains_unaccepted_memory(phys_addr_t start, phys_addr_t end) > +{ > + struct efi_unaccepted_memory *unaccepted; > + unsigned long flags; > + bool ret = false; > + u64 unit_size; > + > + unaccepted = efi_get_unaccepted_table(); > + if (!unaccepted) > + return false; > + > + unit_size = unaccepted->unit_size; > + > + /* > + * Only care for the part of the range that is represented > + * in the bitmap. > + */ > + if (start < unaccepted->phys_base) > + start = unaccepted->phys_base; > + if (end < unaccepted->phys_base) > + return false; > + > + /* Translate to offsets from the beginning of the bitmap */ > + start -= unaccepted->phys_base; > + end -= unaccepted->phys_base; > + > + /* Make sure not to overrun the bitmap */ > + if (end > unaccepted->size * unit_size * BITS_PER_BYTE) > + end = unaccepted->size * unit_size * BITS_PER_BYTE; > + > + spin_lock_irqsave(&unaccepted_memory_lock, flags); > + while (start < end) { > + if (test_bit(start / unit_size, unaccepted->bitmap)) { > + ret = true; > + break; > + } > + > + start += unit_size; > + } > + spin_unlock_irqrestore(&unaccepted_memory_lock, flags); > + > + return ret; > +} > diff --git a/include/linux/efi.h b/include/linux/efi.h > index 29cc622910da..9864f9c00da2 100644 > --- a/include/linux/efi.h > +++ b/include/linux/efi.h > @@ -646,6 +646,7 @@ extern struct efi { > unsigned long tpm_final_log; /* TPM2 Final Events Log table */ > unsigned long mokvar_table; /* MOK variable config table */ > unsigned long coco_secret; /* Confidential computing secret table */ > + unsigned long unaccepted; /* Unaccepted memory table */ > > efi_get_time_t *get_time; > efi_set_time_t *set_time;
diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile index b51f2a4c821e..e489fefd23da 100644 --- a/drivers/firmware/efi/Makefile +++ b/drivers/firmware/efi/Makefile @@ -41,3 +41,4 @@ obj-$(CONFIG_EFI_CAPSULE_LOADER) += capsule-loader.o obj-$(CONFIG_EFI_EARLYCON) += earlycon.o obj-$(CONFIG_UEFI_CPER_ARM) += cper-arm.o obj-$(CONFIG_UEFI_CPER_X86) += cper-x86.o +obj-$(CONFIG_UNACCEPTED_MEMORY) += unaccepted_memory.o diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 7dce06e419c5..d817e7afd266 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -50,6 +50,9 @@ struct efi __read_mostly efi = { #ifdef CONFIG_EFI_COCO_SECRET .coco_secret = EFI_INVALID_TABLE_ADDR, #endif +#ifdef CONFIG_UNACCEPTED_MEMORY + .unaccepted = EFI_INVALID_TABLE_ADDR, +#endif }; EXPORT_SYMBOL(efi); @@ -605,6 +608,9 @@ static const efi_config_table_type_t common_tables[] __initconst = { #ifdef CONFIG_EFI_COCO_SECRET {LINUX_EFI_COCO_SECRET_AREA_GUID, &efi.coco_secret, "CocoSecret" }, #endif +#ifdef CONFIG_UNACCEPTED_MEMORY + {LINUX_EFI_UNACCEPTED_MEM_TABLE_GUID, &efi.unaccepted, "Unaccepted" }, +#endif #ifdef CONFIG_EFI_GENERIC_STUB {LINUX_EFI_SCREEN_INFO_TABLE_GUID, &screen_info_table }, #endif @@ -759,6 +765,25 @@ int __init efi_config_parse_tables(const efi_config_table_t *config_tables, } } + if (IS_ENABLED(CONFIG_UNACCEPTED_MEMORY) && + efi.unaccepted != EFI_INVALID_TABLE_ADDR) { + struct efi_unaccepted_memory *unaccepted; + + unaccepted = early_memremap(efi.unaccepted, sizeof(*unaccepted)); + if (unaccepted) { + unsigned long size; + + if (unaccepted->version == 1) { + size = sizeof(*unaccepted) + unaccepted->size; + memblock_reserve(efi.unaccepted, size); + } else { + efi.unaccepted = EFI_INVALID_TABLE_ADDR; + } + + early_memunmap(unaccepted, sizeof(*unaccepted)); + } + } + return 0; } diff --git a/drivers/firmware/efi/unaccepted_memory.c b/drivers/firmware/efi/unaccepted_memory.c new file mode 100644 index 000000000000..bb91c41f76fb --- /dev/null +++ b/drivers/firmware/efi/unaccepted_memory.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <linux/efi.h> +#include <linux/memblock.h> +#include <linux/spinlock.h> +#include <asm/unaccepted_memory.h> + +/* Protects unaccepted memory bitmap */ +static DEFINE_SPINLOCK(unaccepted_memory_lock); + +void accept_memory(phys_addr_t start, phys_addr_t end) +{ + struct efi_unaccepted_memory *unaccepted; + unsigned long range_start, range_end; + unsigned long flags; + u64 unit_size; + + if (efi.unaccepted == EFI_INVALID_TABLE_ADDR) + return; + + unaccepted = efi_get_unaccepted_table(); + if (!unaccepted) + return; + + unit_size = unaccepted->unit_size; + + /* + * Only care for the part of the range that is represented + * in the bitmap. + */ + if (start < unaccepted->phys_base) + start = unaccepted->phys_base; + if (end < unaccepted->phys_base) + return; + + /* Translate to offsets from the beginning of the bitmap */ + start -= unaccepted->phys_base; + end -= unaccepted->phys_base; + + /* Make sure not to overrun the bitmap */ + if (end > unaccepted->size * unit_size * BITS_PER_BYTE) + end = unaccepted->size * unit_size * BITS_PER_BYTE; + + range_start = start / unit_size; + + spin_lock_irqsave(&unaccepted_memory_lock, flags); + for_each_set_bitrange_from(range_start, range_end, unaccepted->bitmap, + DIV_ROUND_UP(end, unit_size)) { + unsigned long phys_start, phys_end; + unsigned long len = range_end - range_start; + + phys_start = range_start * unit_size + unaccepted->phys_base; + phys_end = range_end * unit_size + unaccepted->phys_base; + + arch_accept_memory(phys_start, phys_end); + bitmap_clear(unaccepted->bitmap, range_start, len); + } + spin_unlock_irqrestore(&unaccepted_memory_lock, flags); +} + +bool range_contains_unaccepted_memory(phys_addr_t start, phys_addr_t end) +{ + struct efi_unaccepted_memory *unaccepted; + unsigned long flags; + bool ret = false; + u64 unit_size; + + unaccepted = efi_get_unaccepted_table(); + if (!unaccepted) + return false; + + unit_size = unaccepted->unit_size; + + /* + * Only care for the part of the range that is represented + * in the bitmap. + */ + if (start < unaccepted->phys_base) + start = unaccepted->phys_base; + if (end < unaccepted->phys_base) + return false; + + /* Translate to offsets from the beginning of the bitmap */ + start -= unaccepted->phys_base; + end -= unaccepted->phys_base; + + /* Make sure not to overrun the bitmap */ + if (end > unaccepted->size * unit_size * BITS_PER_BYTE) + end = unaccepted->size * unit_size * BITS_PER_BYTE; + + spin_lock_irqsave(&unaccepted_memory_lock, flags); + while (start < end) { + if (test_bit(start / unit_size, unaccepted->bitmap)) { + ret = true; + break; + } + + start += unit_size; + } + spin_unlock_irqrestore(&unaccepted_memory_lock, flags); + + return ret; +} diff --git a/include/linux/efi.h b/include/linux/efi.h index 29cc622910da..9864f9c00da2 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -646,6 +646,7 @@ extern struct efi { unsigned long tpm_final_log; /* TPM2 Final Events Log table */ unsigned long mokvar_table; /* MOK variable config table */ unsigned long coco_secret; /* Confidential computing secret table */ + unsigned long unaccepted; /* Unaccepted memory table */ efi_get_time_t *get_time; efi_set_time_t *set_time;
efi_config_parse_tables() reserves memory that holds unaccepted memory configuration table so it won't be reused by page allocator. Core-mm requires few helpers to support unaccepted memory: - accept_memory() checks the range of addresses against the bitmap and accept memory if needed. - range_contains_unaccepted_memory() checks if anything within the range requires acceptance. Architectural code has to provide efi_get_unaccepted_table() that returns pointer to the unaccepted memory configuration table. arch_accept_memory() handles arch-specific part of memory acceptance. Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com> v11.1: - Add missing memblock_reserve() for the unaccepted memory configuration table. --- drivers/firmware/efi/Makefile | 1 + drivers/firmware/efi/efi.c | 25 ++++++ drivers/firmware/efi/unaccepted_memory.c | 103 +++++++++++++++++++++++ include/linux/efi.h | 1 + 4 files changed, 130 insertions(+) create mode 100644 drivers/firmware/efi/unaccepted_memory.c