Commit 70e65afc authored by Linus Torvalds's avatar Linus Torvalds
Browse files

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

Pull EFI fixes from Ard Biesheuvel:
 "The boot_params pointer fix uses a somewhat ugly extern struct
  declaration but this will be cleaned up the next cycle.

   - don't try to print warnings to the console when it is no longer
     available

   - fix theoretical memory leak in SSDT override handling

   - make sure that the boot_params global variable is set before the
     KASLR code attempts to hash it for 'randomness'

   - avoid soft lockups in the memory acceptance code"

* tag 'efi-fixes-for-v6.6-3' of git://git.kernel.org/pub/scm/linux/kernel/git/efi/efi:
  efi/unaccepted: Fix soft lockups caused by parallel memory acceptance
  x86/boot: efistub: Assign global boot_params variable
  efi: fix memory leak in krealloc failure handling
  x86/efistub: Don't try to print after ExitBootService()
parents 1acfd2bd c03d21f0
Loading
Loading
Loading
Loading
+6 −2
Original line number Diff line number Diff line
@@ -273,9 +273,13 @@ static __init int efivar_ssdt_load(void)
		if (status == EFI_NOT_FOUND) {
			break;
		} else if (status == EFI_BUFFER_TOO_SMALL) {
			name = krealloc(name, name_size, GFP_KERNEL);
			if (!name)
			efi_char16_t *name_tmp =
				krealloc(name, name_size, GFP_KERNEL);
			if (!name_tmp) {
				kfree(name);
				return -ENOMEM;
			}
			name = name_tmp;
			continue;
		}

+3 −4
Original line number Diff line number Diff line
@@ -605,11 +605,8 @@ setup_e820(struct boot_params *params, struct setup_data *e820ext, u32 e820ext_s
			break;

		case EFI_UNACCEPTED_MEMORY:
			if (!IS_ENABLED(CONFIG_UNACCEPTED_MEMORY)) {
				efi_warn_once(
"The system has unaccepted memory,  but kernel does not support it\nConsider enabling CONFIG_UNACCEPTED_MEMORY\n");
			if (!IS_ENABLED(CONFIG_UNACCEPTED_MEMORY))
				continue;
			}
			e820_type = E820_TYPE_RAM;
			process_unaccepted_memory(d->phys_addr,
						  d->phys_addr + PAGE_SIZE * d->num_pages);
@@ -852,6 +849,8 @@ void __noreturn efi_stub_entry(efi_handle_t handle,
	unsigned long kernel_entry;
	efi_status_t status;

	boot_params_pointer = boot_params;

	efi_system_table = sys_table_arg;
	/* Check if we were booted by the EFI firmware */
	if (efi_system_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
+2 −0
Original line number Diff line number Diff line
@@ -2,6 +2,8 @@

#include <linux/efi.h>

extern struct boot_params *boot_params_pointer asm("boot_params");

extern void trampoline_32bit_src(void *, bool);
extern const u16 trampoline_ljmp_imm_offset;

+60 −4
Original line number Diff line number Diff line
@@ -5,9 +5,17 @@
#include <linux/spinlock.h>
#include <asm/unaccepted_memory.h>

/* Protects unaccepted memory bitmap */
/* Protects unaccepted memory bitmap and accepting_list */
static DEFINE_SPINLOCK(unaccepted_memory_lock);

struct accept_range {
	struct list_head list;
	unsigned long start;
	unsigned long end;
};

static LIST_HEAD(accepting_list);

/*
 * accept_memory() -- Consult bitmap and accept the memory if needed.
 *
@@ -24,6 +32,7 @@ void accept_memory(phys_addr_t start, phys_addr_t end)
{
	struct efi_unaccepted_memory *unaccepted;
	unsigned long range_start, range_end;
	struct accept_range range, *entry;
	unsigned long flags;
	u64 unit_size;

@@ -78,20 +87,67 @@ void accept_memory(phys_addr_t start, phys_addr_t end)
	if (end > unaccepted->size * unit_size * BITS_PER_BYTE)
		end = unaccepted->size * unit_size * BITS_PER_BYTE;

	range_start = start / unit_size;

	range.start = start / unit_size;
	range.end = DIV_ROUND_UP(end, unit_size);
retry:
	spin_lock_irqsave(&unaccepted_memory_lock, flags);

	/*
	 * Check if anybody works on accepting the same range of the memory.
	 *
	 * The check is done with unit_size granularity. It is crucial to catch
	 * all accept requests to the same unit_size block, even if they don't
	 * overlap on physical address level.
	 */
	list_for_each_entry(entry, &accepting_list, list) {
		if (entry->end < range.start)
			continue;
		if (entry->start >= range.end)
			continue;

		/*
		 * Somebody else accepting the range. Or at least part of it.
		 *
		 * Drop the lock and retry until it is complete.
		 */
		spin_unlock_irqrestore(&unaccepted_memory_lock, flags);
		goto retry;
	}

	/*
	 * Register that the range is about to be accepted.
	 * Make sure nobody else will accept it.
	 */
	list_add(&range.list, &accepting_list);

	range_start = range.start;
	for_each_set_bitrange_from(range_start, range_end, unaccepted->bitmap,
				   DIV_ROUND_UP(end, unit_size)) {
				   range.end) {
		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;

		/*
		 * Keep interrupts disabled until the accept operation is
		 * complete in order to prevent deadlocks.
		 *
		 * Enabling interrupts before calling arch_accept_memory()
		 * creates an opportunity for an interrupt handler to request
		 * acceptance for the same memory. The handler will continuously
		 * spin with interrupts disabled, preventing other task from
		 * making progress with the acceptance process.
		 */
		spin_unlock(&unaccepted_memory_lock);

		arch_accept_memory(phys_start, phys_end);

		spin_lock(&unaccepted_memory_lock);
		bitmap_clear(unaccepted->bitmap, range_start, len);
	}

	list_del(&range.list);
	spin_unlock_irqrestore(&unaccepted_memory_lock, flags);
}